aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.clang-format13
-rw-r--r--.github/workflows/check-whitespace.yml67
-rw-r--r--.github/workflows/coverity.yml2
-rw-r--r--.github/workflows/main.yml11
-rw-r--r--.gitignore1
-rw-r--r--.gitlab-ci.yml33
-rw-r--r--Documentation/BreakingChanges.txt135
-rw-r--r--Documentation/DecisionMaking.txt74
-rw-r--r--Documentation/Makefile6
-rw-r--r--Documentation/MyFirstContribution.txt9
-rw-r--r--Documentation/RelNotes/2.39.4.txt79
-rw-r--r--Documentation/RelNotes/2.39.5.txt26
-rw-r--r--Documentation/RelNotes/2.40.2.txt7
-rw-r--r--Documentation/RelNotes/2.40.3.txt26
-rw-r--r--Documentation/RelNotes/2.41.1.txt7
-rw-r--r--Documentation/RelNotes/2.41.2.txt26
-rw-r--r--Documentation/RelNotes/2.42.2.txt7
-rw-r--r--Documentation/RelNotes/2.42.3.txt26
-rw-r--r--Documentation/RelNotes/2.43.4.txt7
-rw-r--r--Documentation/RelNotes/2.43.5.txt26
-rw-r--r--Documentation/RelNotes/2.44.1.txt8
-rw-r--r--Documentation/RelNotes/2.44.2.txt26
-rw-r--r--Documentation/RelNotes/2.45.1.txt8
-rw-r--r--Documentation/RelNotes/2.45.2.txt26
-rw-r--r--Documentation/RelNotes/2.45.3.txt107
-rw-r--r--Documentation/RelNotes/2.46.0.txt453
-rw-r--r--Documentation/SubmittingPatches275
-rw-r--r--Documentation/config.txt9
-rw-r--r--Documentation/config/add.txt6
-rw-r--r--Documentation/config/advice.txt6
-rw-r--r--Documentation/config/alias.txt25
-rw-r--r--Documentation/config/attr.txt5
-rw-r--r--Documentation/config/bitmap-pseudo-merge.txt91
-rw-r--r--Documentation/config/commitgraph.txt29
-rw-r--r--Documentation/config/core.txt3
-rw-r--r--Documentation/config/diff.txt18
-rw-r--r--Documentation/config/http.txt31
-rw-r--r--Documentation/config/interactive.txt8
-rw-r--r--Documentation/config/promisor.txt3
-rw-r--r--Documentation/config/reftable.txt48
-rw-r--r--Documentation/config/remote.txt11
-rw-r--r--Documentation/config/safe.txt3
-rw-r--r--Documentation/diff-options.txt10
-rw-r--r--Documentation/git-am.txt8
-rw-r--r--Documentation/git-archive.txt10
-rw-r--r--Documentation/git-config.txt219
-rw-r--r--Documentation/git-credential.txt104
-rw-r--r--Documentation/git-for-each-repo.txt9
-rw-r--r--Documentation/git-format-patch.txt26
-rw-r--r--Documentation/git-gui.txt2
-rw-r--r--Documentation/git-ls-remote.txt12
-rw-r--r--Documentation/git-merge-tree.txt5
-rw-r--r--Documentation/git-refs.txt61
-rw-r--r--Documentation/git-rev-parse.txt15
-rw-r--r--Documentation/git-show-branch.txt2
-rw-r--r--Documentation/git-show-ref.txt18
-rw-r--r--Documentation/git-tag.txt16
-rw-r--r--Documentation/git-update-index.txt1
-rw-r--r--Documentation/git-update-ref.txt25
-rw-r--r--Documentation/git-upload-pack.txt31
-rw-r--r--Documentation/git.txt59
-rw-r--r--Documentation/gitattributes.txt7
-rw-r--r--Documentation/gitfaq.txt109
-rw-r--r--Documentation/gitformat-commit-graph.txt9
-rw-r--r--Documentation/githooks.txt14
-rw-r--r--Documentation/gitpacking.txt189
-rw-r--r--Documentation/glossary-content.txt3
-rwxr-xr-xDocumentation/lint-manpages.sh108
-rw-r--r--Documentation/technical/bitmap-format.txt141
-rwxr-xr-xGIT-VERSION-GEN2
-rw-r--r--Makefile126
l---------RelNotes2
-rw-r--r--add-interactive.c21
-rw-r--r--add-patch.c30
-rw-r--r--advice.c9
-rw-r--r--advice.h2
-rw-r--r--alias.c6
-rw-r--r--apply.c92
-rw-r--r--apply.h4
-rw-r--r--archive-tar.c3
-rw-r--r--archive-zip.c3
-rw-r--r--archive.c2
-rw-r--r--attr.c63
-rw-r--r--attr.h4
-rw-r--r--bisect.c27
-rw-r--r--blame.c12
-rw-r--r--bloom.c209
-rw-r--r--bloom.h38
-rw-r--r--branch.c9
-rw-r--r--builtin.h9
-rw-r--r--builtin/add.c54
-rw-r--r--builtin/am.c90
-rw-r--r--builtin/apply.c10
-rw-r--r--builtin/archive.c11
-rw-r--r--builtin/bisect.c47
-rw-r--r--builtin/blame.c24
-rw-r--r--builtin/branch.c51
-rw-r--r--builtin/bugreport.c2
-rw-r--r--builtin/bundle.c5
-rw-r--r--builtin/cat-file.c21
-rw-r--r--builtin/check-attr.c5
-rw-r--r--builtin/check-ignore.c11
-rw-r--r--builtin/checkout-index.c22
-rw-r--r--builtin/checkout.c136
-rw-r--r--builtin/clean.c7
-rw-r--r--builtin/clone.c113
-rw-r--r--builtin/commit-tree.c11
-rw-r--r--builtin/commit.c121
-rw-r--r--builtin/config.c1170
-rw-r--r--builtin/credential-cache--daemon.c22
-rw-r--r--builtin/credential-cache.c10
-rw-r--r--builtin/credential-store.c2
-rw-r--r--builtin/credential.c17
-rw-r--r--builtin/describe.c20
-rw-r--r--builtin/diagnose.c2
-rw-r--r--builtin/diff-tree.c3
-rw-r--r--builtin/diff.c15
-rw-r--r--builtin/difftool.c19
-rw-r--r--builtin/fast-export.c2
-rw-r--r--builtin/fast-import.c59
-rw-r--r--builtin/fetch-pack.c4
-rw-r--r--builtin/fetch.c40
-rw-r--r--builtin/fmt-merge-msg.c4
-rw-r--r--builtin/for-each-repo.c13
-rw-r--r--builtin/fsck.c11
-rw-r--r--builtin/gc.c12
-rw-r--r--builtin/grep.c4
-rw-r--r--builtin/hash-object.c3
-rw-r--r--builtin/index-pack.c11
-rw-r--r--builtin/init-db.c2
-rw-r--r--builtin/interpret-trailers.c12
-rw-r--r--builtin/log.c758
-rw-r--r--builtin/ls-remote.c15
-rw-r--r--builtin/ls-tree.c3
-rw-r--r--builtin/mailsplit.c4
-rw-r--r--builtin/merge-index.c17
-rw-r--r--builtin/merge-recursive.c6
-rw-r--r--builtin/merge-tree.c4
-rw-r--r--builtin/merge.c114
-rw-r--r--builtin/multi-pack-index.c16
-rw-r--r--builtin/mv.c336
-rw-r--r--builtin/name-rev.c5
-rw-r--r--builtin/notes.c28
-rw-r--r--builtin/pack-objects.c39
-rw-r--r--builtin/pack-redundant.c10
-rw-r--r--builtin/patch-id.c19
-rw-r--r--builtin/pull.c64
-rw-r--r--builtin/push.c49
-rw-r--r--builtin/read-tree.c15
-rw-r--r--builtin/rebase.c79
-rw-r--r--builtin/receive-pack.c33
-rw-r--r--builtin/reflog.c27
-rw-r--r--builtin/refs.c75
-rw-r--r--builtin/remote.c140
-rw-r--r--builtin/repack.c17
-rw-r--r--builtin/replace.c26
-rw-r--r--builtin/replay.c15
-rw-r--r--builtin/reset.c45
-rw-r--r--builtin/rev-list.c2
-rw-r--r--builtin/rev-parse.c91
-rw-r--r--builtin/revert.c2
-rw-r--r--builtin/rm.c42
-rw-r--r--builtin/send-pack.c2
-rw-r--r--builtin/shortlog.c7
-rw-r--r--builtin/show-branch.c22
-rw-r--r--builtin/show-ref.c37
-rw-r--r--builtin/sparse-checkout.c50
-rw-r--r--builtin/stash.c95
-rw-r--r--builtin/submodule--helper.c149
-rw-r--r--builtin/symbolic-ref.c13
-rw-r--r--builtin/tag.c52
-rw-r--r--builtin/unpack-objects.c9
-rw-r--r--builtin/update-index.c129
-rw-r--r--builtin/update-ref.c267
-rw-r--r--builtin/upload-pack.c2
-rw-r--r--builtin/var.c5
-rw-r--r--builtin/worktree.c43
-rw-r--r--builtin/write-tree.c6
-rw-r--r--bulk-checkin.c3
-rw-r--r--bundle-uri.c16
-rw-r--r--bundle.c36
-rw-r--r--bundle.h1
-rw-r--r--cache-tree.c7
-rw-r--r--checkout.c6
-rw-r--r--checkout.h8
-rw-r--r--chunk-format.c2
-rw-r--r--chunk-format.h2
-rwxr-xr-xci/check-directional-formatting.bash2
-rwxr-xr-xci/check-whitespace.sh95
-rwxr-xr-xci/install-dependencies.sh103
-rwxr-xr-xci/install-docker-dependencies.sh46
-rwxr-xr-xci/lib.sh25
-rwxr-xr-xci/run-build-and-minimal-fuzzers.sh3
-rwxr-xr-xci/run-build-and-tests.sh2
-rwxr-xr-xci/run-test-slice.sh2
-rwxr-xr-xci/test-documentation.sh1
-rw-r--r--color.c21
-rw-r--r--color.h3
-rw-r--r--combine-diff.c5
-rw-r--r--command-list.txt1
-rw-r--r--commit-graph.c112
-rw-r--r--commit-graph.h2
-rw-r--r--commit-reach.c6
-rw-r--r--commit.c49
-rw-r--r--commit.h17
-rw-r--r--common-main.c4
-rw-r--r--compat/basename.c16
-rw-r--r--compat/fsmonitor/fsm-ipc-darwin.c7
-rw-r--r--compat/mingw.c33
-rw-r--r--compat/regex/regcomp.c14
-rw-r--r--compat/regex/regex_internal.c4
-rw-r--r--compat/regex/regexec.c10
-rw-r--r--compat/sha1-chunked.c2
-rw-r--r--compat/win32/path-utils.c37
-rw-r--r--compat/win32/path-utils.h4
-rw-r--r--compat/win32/trace2_win32_process_info.c2
-rw-r--r--compat/winansi.c2
-rw-r--r--config.c96
-rw-r--r--config.h21
-rw-r--r--config.mak.dev3
-rw-r--r--config.mak.uname1
-rw-r--r--connect.c4
-rw-r--r--connected.c2
-rw-r--r--contrib/buildsystems/CMakeLists.txt6
-rw-r--r--contrib/coccinelle/refs.cocci103
-rw-r--r--contrib/completion/git-completion.bash73
-rw-r--r--contrib/completion/git-completion.zsh1
-rw-r--r--contrib/credential/osxkeychain/git-credential-osxkeychain.c14
-rw-r--r--convert.c32
-rw-r--r--convert.h4
-rw-r--r--copy.c3
-rw-r--r--credential.c150
-rw-r--r--credential.h126
-rw-r--r--csum-file.c9
-rw-r--r--csum-file.h2
-rw-r--r--date.c12
-rw-r--r--decorate.h2
-rw-r--r--delta-islands.c7
-rw-r--r--diagnose.c2
-rw-r--r--diff-lib.c13
-rw-r--r--diff.c113
-rw-r--r--diff.h2
-rw-r--r--diffcore-break.c3
-rw-r--r--diffcore-rename.c13
-rw-r--r--diffcore.h2
-rw-r--r--dir.c90
-rw-r--r--dir.h19
-rw-r--r--entry.c32
-rw-r--r--environment.c19
-rw-r--r--environment.h21
-rw-r--r--ewah/bitmap.c76
-rw-r--r--ewah/ewok.h8
-rw-r--r--fetch-pack.c38
-rw-r--r--fetch-pack.h5
-rw-r--r--fmt-merge-msg.c8
-rw-r--r--fsck.c11
-rw-r--r--fsck.h2
-rw-r--r--fsmonitor-ipc.c2
-rw-r--r--fsmonitor-settings.c5
-rw-r--r--git-compat-util.h8
-rw-r--r--git-gui/README.md4
-rwxr-xr-xgit-gui/git-gui.sh2
-rw-r--r--git-gui/po/fr.po2
-rw-r--r--git-gui/po/sv.po3267
-rwxr-xr-xgit-p4.py24
-rwxr-xr-xgit-send-email.perl15
-rw-r--r--git.c14
-rwxr-xr-xgitweb/gitweb.perl4
-rw-r--r--gpg-interface.c8
-rw-r--r--hash-ll.h310
-rw-r--r--hash-lookup.c5
-rw-r--r--hash.h366
-rw-r--r--help.c32
-rw-r--r--help.h2
-rw-r--r--hex.c8
-rw-r--r--hex.h28
-rw-r--r--hook.c20
-rw-r--r--hook.h1
-rw-r--r--http-backend.c17
-rw-r--r--http-fetch.c8
-rw-r--r--http-push.c5
-rw-r--r--http-walker.c6
-rw-r--r--http.c255
-rw-r--r--http.h5
-rw-r--r--ident.c4
-rw-r--r--imap-send.c145
-rw-r--r--json-writer.c5
-rw-r--r--line-log.c22
-rw-r--r--line-range.c2
-rw-r--r--list-objects-filter-options.c2
-rw-r--r--list-objects-filter.c4
-rw-r--r--list-objects.c2
-rw-r--r--log-tree.c101
-rw-r--r--loose.c2
-rw-r--r--loose.h2
-rw-r--r--ls-refs.c14
-rw-r--r--mailmap.c8
-rw-r--r--mailmap.h4
-rw-r--r--match-trees.c6
-rw-r--r--mem-pool.h1
-rw-r--r--merge-blobs.c2
-rw-r--r--merge-ll.c13
-rw-r--r--merge-ort-wrappers.c2
-rw-r--r--merge-ort-wrappers.h2
-rw-r--r--merge-ort.c183
-rw-r--r--merge-ort.h4
-rw-r--r--merge-recursive.c71
-rw-r--r--merge-recursive.h4
-rw-r--r--merge.c2
-rw-r--r--mergetools/vimdiff5
-rw-r--r--midx-write.c183
-rw-r--r--midx.c15
-rw-r--r--midx.h6
-rw-r--r--negotiator/default.c5
-rw-r--r--negotiator/skipping.c5
-rw-r--r--notes-cache.c8
-rw-r--r--notes-merge.c11
-rw-r--r--notes-utils.c18
-rw-r--r--notes-utils.h2
-rw-r--r--notes.c40
-rw-r--r--notes.h5
-rw-r--r--object-file-convert.c6
-rw-r--r--object-file.c45
-rw-r--r--object-name.c44
-rw-r--r--object-name.h2
-rw-r--r--object.c25
-rw-r--r--object.h41
-rw-r--r--oid-array.c2
-rw-r--r--oidmap.h2
-rw-r--r--oidset.c8
-rw-r--r--oidset.h4
-rw-r--r--oidtree.c4
-rw-r--r--oidtree.h2
-rw-r--r--oss-fuzz/fuzz-commit-graph.c8
-rw-r--r--pack-bitmap-write.c490
-rw-r--r--pack-bitmap.c387
-rw-r--r--pack-bitmap.h55
-rw-r--r--pack-check.c7
-rw-r--r--pack-revindex.c8
-rw-r--r--pack-write.c5
-rw-r--r--packfile.c20
-rw-r--r--packfile.h2
-rw-r--r--pager.c4
-rw-r--r--parallel-checkout.c8
-rw-r--r--parse-options-cb.c5
-rw-r--r--parse-options.h2
-rw-r--r--parse.c29
-rw-r--r--parse.h1
-rw-r--r--path.c58
-rw-r--r--path.h10
-rw-r--r--pathspec.c2
-rw-r--r--pathspec.h2
-rw-r--r--perl/Git.pm4
-rw-r--r--pretty.c22
-rw-r--r--progress.c2
-rw-r--r--promisor-remote.c15
-rw-r--r--promisor-remote.h2
-rw-r--r--protocol-caps.c5
-rw-r--r--pseudo-merge.c759
-rw-r--r--pseudo-merge.h216
-rw-r--r--range-diff.c2
-rw-r--r--range-diff.h6
-rw-r--r--reachable.c7
-rw-r--r--read-cache-ll.h2
-rw-r--r--read-cache.c98
-rw-r--r--rebase-interactive.c23
-rw-r--r--rebase-interactive.h9
-rw-r--r--ref-filter.c39
-rw-r--r--reflog-walk.c33
-rw-r--r--reflog.c24
-rw-r--r--refs.c943
-rw-r--r--refs.h407
-rw-r--r--refs/debug.c29
-rw-r--r--refs/files-backend.c408
-rw-r--r--refs/iterator.c6
-rw-r--r--refs/packed-backend.c91
-rw-r--r--refs/packed-backend.h6
-rw-r--r--refs/ref-cache.c7
-rw-r--r--refs/ref-cache.h2
-rw-r--r--refs/refs-internal.h107
-rw-r--r--refs/reftable-backend.c500
-rw-r--r--refspec.c15
-rw-r--r--refspec.h1
-rw-r--r--reftable/basics.c15
-rw-r--r--reftable/basics.h4
-rw-r--r--reftable/basics_test.c105
-rw-r--r--reftable/block.c84
-rw-r--r--reftable/block.h6
-rw-r--r--reftable/block_test.c2
-rw-r--r--reftable/constants.h1
-rw-r--r--reftable/dump.c14
-rw-r--r--reftable/error.c2
-rw-r--r--reftable/generic.c94
-rw-r--r--reftable/generic.h9
-rw-r--r--reftable/iter.c23
-rw-r--r--reftable/merged.c160
-rw-r--r--reftable/merged.h6
-rw-r--r--reftable/merged_test.c63
-rw-r--r--reftable/reader.c283
-rw-r--r--reftable/readwrite_test.c67
-rw-r--r--reftable/record.c6
-rw-r--r--reftable/record_test.c419
-rw-r--r--reftable/refname.c206
-rw-r--r--reftable/refname.h29
-rw-r--r--reftable/refname_test.c101
-rw-r--r--reftable/reftable-error.h3
-rw-r--r--reftable/reftable-generic.h8
-rw-r--r--reftable/reftable-iterator.h21
-rw-r--r--reftable/reftable-merged.h15
-rw-r--r--reftable/reftable-reader.h47
-rw-r--r--reftable/reftable-record.h2
-rw-r--r--reftable/reftable-stack.h20
-rw-r--r--reftable/reftable-tests.h1
-rw-r--r--reftable/reftable-writer.h14
-rw-r--r--reftable/stack.c171
-rw-r--r--reftable/stack.h5
-rw-r--r--reftable/stack_test.c232
-rw-r--r--reftable/system.h2
-rw-r--r--reftable/writer.c160
-rw-r--r--remote-curl.c90
-rw-r--r--remote.c172
-rw-r--r--remote.h25
-rw-r--r--replace-object.c12
-rw-r--r--repo-settings.c6
-rw-r--r--repository.c95
-rw-r--r--repository.h34
-rw-r--r--rerere.c5
-rw-r--r--reset.c31
-rw-r--r--reset.h2
-rw-r--r--resolve-undo.c5
-rw-r--r--resolve-undo.h2
-rw-r--r--revision.c118
-rw-r--r--run-command.c27
-rw-r--r--run-command.h5
-rw-r--r--scalar.c18
-rw-r--r--send-pack.c17
-rw-r--r--sequencer.c556
-rw-r--r--sequencer.h15
-rw-r--r--serve.c4
-rw-r--r--server-info.c31
-rw-r--r--setup.c209
-rw-r--r--setup.h23
-rw-r--r--shallow.c18
-rw-r--r--sparse-index.c244
-rw-r--r--split-index.c4
-rw-r--r--split-index.h2
-rw-r--r--strbuf.c13
-rw-r--r--strbuf.h5
-rw-r--r--streaming.c3
-rw-r--r--strvec.c22
-rw-r--r--strvec.h16
-rw-r--r--submodule-config.c6
-rw-r--r--submodule.c106
-rw-r--r--submodule.h5
-rw-r--r--t/.gitattributes2
-rw-r--r--t/Makefile30
-rw-r--r--t/README34
-rw-r--r--t/chainlint-cat.pl29
-rwxr-xr-xt/chainlint.pl53
-rw-r--r--t/chainlint/arithmetic-expansion.expect18
-rw-r--r--t/chainlint/arithmetic-expansion.test2
-rw-r--r--t/chainlint/bash-array.expect20
-rw-r--r--t/chainlint/bash-array.test2
-rw-r--r--t/chainlint/blank-line-before-esac.expect36
-rw-r--r--t/chainlint/blank-line-before-esac.test2
-rw-r--r--t/chainlint/blank-line.expect16
-rw-r--r--t/chainlint/blank-line.test2
-rw-r--r--t/chainlint/block-comment.expect16
-rw-r--r--t/chainlint/block-comment.test2
-rw-r--r--t/chainlint/block.expect46
-rw-r--r--t/chainlint/block.test2
-rw-r--r--t/chainlint/broken-chain.expect12
-rw-r--r--t/chainlint/broken-chain.test2
-rw-r--r--t/chainlint/case-comment.expect22
-rw-r--r--t/chainlint/case-comment.test2
-rw-r--r--t/chainlint/case.expect38
-rw-r--r--t/chainlint/case.test2
-rw-r--r--t/chainlint/chain-break-background.expect18
-rw-r--r--t/chainlint/chain-break-background.test2
-rw-r--r--t/chainlint/chain-break-continue.expect24
-rw-r--r--t/chainlint/chain-break-continue.test2
-rw-r--r--t/chainlint/chain-break-false.expect18
-rw-r--r--t/chainlint/chain-break-false.test2
-rw-r--r--t/chainlint/chain-break-return-exit.expect38
-rw-r--r--t/chainlint/chain-break-return-exit.test2
-rw-r--r--t/chainlint/chain-break-status.expect18
-rw-r--r--t/chainlint/chain-break-status.test2
-rw-r--r--t/chainlint/chained-block.expect18
-rw-r--r--t/chainlint/chained-block.test2
-rw-r--r--t/chainlint/chained-subshell.expect20
-rw-r--r--t/chainlint/chained-subshell.test2
-rw-r--r--t/chainlint/close-nested-and-parent-together.expect6
-rw-r--r--t/chainlint/close-nested-and-parent-together.test2
-rw-r--r--t/chainlint/close-subshell.expect52
-rw-r--r--t/chainlint/close-subshell.test2
-rw-r--r--t/chainlint/command-substitution-subsubshell.expect4
-rw-r--r--t/chainlint/command-substitution-subsubshell.test2
-rw-r--r--t/chainlint/command-substitution.expect18
-rw-r--r--t/chainlint/command-substitution.test2
-rw-r--r--t/chainlint/comment.expect16
-rw-r--r--t/chainlint/comment.test2
-rw-r--r--t/chainlint/complex-if-in-cuddled-loop.expect18
-rw-r--r--t/chainlint/complex-if-in-cuddled-loop.test2
-rw-r--r--t/chainlint/cuddled-if-then-else.expect12
-rw-r--r--t/chainlint/cuddled-if-then-else.test2
-rw-r--r--t/chainlint/cuddled-loop.expect8
-rw-r--r--t/chainlint/cuddled-loop.test2
-rw-r--r--t/chainlint/cuddled.expect34
-rw-r--r--t/chainlint/cuddled.test2
-rw-r--r--t/chainlint/double-here-doc.expect24
-rw-r--r--t/chainlint/double-here-doc.test2
-rw-r--r--t/chainlint/dqstring-line-splice.expect10
-rw-r--r--t/chainlint/dqstring-line-splice.test2
-rw-r--r--t/chainlint/dqstring-no-interpolate.expect24
-rw-r--r--t/chainlint/dqstring-no-interpolate.test2
-rw-r--r--t/chainlint/empty-here-doc.expect8
-rw-r--r--t/chainlint/empty-here-doc.test2
-rw-r--r--t/chainlint/exclamation.expect8
-rw-r--r--t/chainlint/exclamation.test2
-rw-r--r--t/chainlint/exit-loop.expect48
-rw-r--r--t/chainlint/exit-loop.test2
-rw-r--r--t/chainlint/exit-subshell.expect10
-rw-r--r--t/chainlint/exit-subshell.test2
-rw-r--r--t/chainlint/for-loop-abbreviated.expect10
-rw-r--r--t/chainlint/for-loop-abbreviated.test2
-rw-r--r--t/chainlint/for-loop.expect28
-rw-r--r--t/chainlint/for-loop.test2
-rw-r--r--t/chainlint/function.expect22
-rw-r--r--t/chainlint/function.test2
-rw-r--r--t/chainlint/here-doc-body-indent.expect2
-rw-r--r--t/chainlint/here-doc-body-indent.test4
-rw-r--r--t/chainlint/here-doc-body-pathological.expect7
-rw-r--r--t/chainlint/here-doc-body-pathological.test9
-rw-r--r--t/chainlint/here-doc-body.expect7
-rw-r--r--t/chainlint/here-doc-body.test9
-rw-r--r--t/chainlint/here-doc-close-subshell.expect8
-rw-r--r--t/chainlint/here-doc-close-subshell.test2
-rw-r--r--t/chainlint/here-doc-double.expect2
-rw-r--r--t/chainlint/here-doc-double.test10
-rw-r--r--t/chainlint/here-doc-indent-operator.expect22
-rw-r--r--t/chainlint/here-doc-indent-operator.test2
-rw-r--r--t/chainlint/here-doc-multi-line-command-subst.expect16
-rw-r--r--t/chainlint/here-doc-multi-line-command-subst.test2
-rw-r--r--t/chainlint/here-doc-multi-line-string.expect14
-rw-r--r--t/chainlint/here-doc-multi-line-string.test2
-rw-r--r--t/chainlint/here-doc.expect50
-rw-r--r--t/chainlint/here-doc.test2
-rw-r--r--t/chainlint/if-condition-split.expect14
-rw-r--r--t/chainlint/if-condition-split.test2
-rw-r--r--t/chainlint/if-in-loop.expect24
-rw-r--r--t/chainlint/if-in-loop.test2
-rw-r--r--t/chainlint/if-then-else.expect44
-rw-r--r--t/chainlint/if-then-else.test2
-rw-r--r--t/chainlint/incomplete-line.expect20
-rw-r--r--t/chainlint/incomplete-line.test2
-rw-r--r--t/chainlint/inline-comment.expect16
-rw-r--r--t/chainlint/inline-comment.test2
-rw-r--r--t/chainlint/loop-detect-failure.expect30
-rw-r--r--t/chainlint/loop-detect-failure.test2
-rw-r--r--t/chainlint/loop-detect-status.expect36
-rw-r--r--t/chainlint/loop-detect-status.test2
-rw-r--r--t/chainlint/loop-in-if.expect24
-rw-r--r--t/chainlint/loop-in-if.test2
-rw-r--r--t/chainlint/loop-upstream-pipe.expect20
-rw-r--r--t/chainlint/loop-upstream-pipe.test2
-rw-r--r--t/chainlint/multi-line-nested-command-substitution.expect36
-rw-r--r--t/chainlint/multi-line-nested-command-substitution.test2
-rw-r--r--t/chainlint/multi-line-string.expect28
-rw-r--r--t/chainlint/multi-line-string.test2
-rw-r--r--t/chainlint/negated-one-liner.expect10
-rw-r--r--t/chainlint/negated-one-liner.test2
-rw-r--r--t/chainlint/nested-cuddled-subshell.expect50
-rw-r--r--t/chainlint/nested-cuddled-subshell.test2
-rw-r--r--t/chainlint/nested-here-doc.expect60
-rw-r--r--t/chainlint/nested-here-doc.test2
-rw-r--r--t/chainlint/nested-loop-detect-failure.expect62
-rw-r--r--t/chainlint/nested-loop-detect-failure.test2
-rw-r--r--t/chainlint/nested-subshell-comment.expect22
-rw-r--r--t/chainlint/nested-subshell-comment.test2
-rw-r--r--t/chainlint/nested-subshell.expect26
-rw-r--r--t/chainlint/nested-subshell.test2
-rw-r--r--t/chainlint/not-heredoc.expect28
-rw-r--r--t/chainlint/not-heredoc.test2
-rw-r--r--t/chainlint/one-liner-for-loop.expect18
-rw-r--r--t/chainlint/one-liner-for-loop.test2
-rw-r--r--t/chainlint/one-liner.expect18
-rw-r--r--t/chainlint/one-liner.test2
-rw-r--r--t/chainlint/p4-filespec.expect8
-rw-r--r--t/chainlint/p4-filespec.test2
-rw-r--r--t/chainlint/pipe.expect20
-rw-r--r--t/chainlint/pipe.test2
-rw-r--r--t/chainlint/return-loop.expect10
-rw-r--r--t/chainlint/return-loop.test2
-rw-r--r--t/chainlint/semicolon.expect38
-rw-r--r--t/chainlint/semicolon.test2
-rw-r--r--t/chainlint/sqstring-in-sqstring.expect8
-rw-r--r--t/chainlint/sqstring-in-sqstring.test2
-rw-r--r--t/chainlint/subshell-here-doc.expect60
-rw-r--r--t/chainlint/subshell-here-doc.test2
-rw-r--r--t/chainlint/subshell-one-liner.expect38
-rw-r--r--t/chainlint/subshell-one-liner.test2
-rw-r--r--t/chainlint/t7900-subtree.expect44
-rw-r--r--t/chainlint/t7900-subtree.test2
-rw-r--r--t/chainlint/token-pasting.expect54
-rw-r--r--t/chainlint/token-pasting.test2
-rw-r--r--t/chainlint/unclosed-here-doc-indent.expect8
-rw-r--r--t/chainlint/unclosed-here-doc-indent.test2
-rw-r--r--t/chainlint/unclosed-here-doc.expect14
-rw-r--r--t/chainlint/unclosed-here-doc.test2
-rw-r--r--t/chainlint/while-loop.expect28
-rw-r--r--t/chainlint/while-loop.test2
-rw-r--r--t/helper/test-bitmap.c36
-rw-r--r--t/helper/test-bloom.c11
-rw-r--r--t/helper/test-bundle-uri.c2
-rw-r--r--t/helper/test-cache-tree.c19
-rw-r--r--t/helper/test-dump-cache-tree.c7
-rw-r--r--t/helper/test-dump-fsmonitor.c2
-rw-r--r--t/helper/test-dump-split-index.c13
-rw-r--r--t/helper/test-dump-untracked-cache.c5
-rw-r--r--t/helper/test-example-decorate.c78
-rw-r--r--t/helper/test-example-tap.c (renamed from t/unit-tests/t-basic.c)5
-rw-r--r--t/helper/test-find-pack.c2
-rw-r--r--t/helper/test-fsmonitor-client.c2
-rw-r--r--t/helper/test-hash-speed.c2
-rw-r--r--t/helper/test-hashmap.c3
-rw-r--r--t/helper/test-json-writer.c10
-rw-r--r--t/helper/test-lazy-init-name-hash.c41
-rw-r--r--t/helper/test-match-trees.c2
-rw-r--r--t/helper/test-oid-array.c4
-rw-r--r--t/helper/test-oidmap.c123
-rw-r--r--t/helper/test-oidtree.c54
-rw-r--r--t/helper/test-pack-mtimes.c2
-rw-r--r--t/helper/test-parse-options.c1
-rw-r--r--t/helper/test-partial-clone.c2
-rw-r--r--t/helper/test-path-utils.c1
-rw-r--r--t/helper/test-proc-receive.c9
-rw-r--r--t/helper/test-reach.c4
-rw-r--r--t/helper/test-read-cache.c13
-rw-r--r--t/helper/test-read-graph.c67
-rw-r--r--t/helper/test-read-midx.c2
-rw-r--r--t/helper/test-ref-store.c7
-rw-r--r--t/helper/test-reftable.c3
-rw-r--r--t/helper/test-regex.c4
-rw-r--r--t/helper/test-repository.c2
-rw-r--r--t/helper/test-revision-walking.c2
-rw-r--r--t/helper/test-rot13-filter.c5
-rw-r--r--t/helper/test-run-command.c28
-rw-r--r--t/helper/test-scrap-cache-tree.c9
-rw-r--r--t/helper/test-sha1.c2
-rw-r--r--t/helper/test-sha256.c2
-rw-r--r--t/helper/test-strcmp-offset.c23
-rw-r--r--t/helper/test-submodule-config.c4
-rw-r--r--t/helper/test-submodule-nested-repo-config.c2
-rw-r--r--t/helper/test-submodule.c2
-rw-r--r--t/helper/test-tool.c5
-rw-r--r--t/helper/test-tool.h5
-rw-r--r--t/helper/test-trace2.c2
-rw-r--r--t/helper/test-write-cache.c5
-rw-r--r--t/lib-bundle-uri-protocol.sh4
-rw-r--r--t/lib-chunk.sh3
-rw-r--r--t/lib-credential.sh123
-rw-r--r--t/lib-httpd/nph-custom-auth.sh17
-rwxr-xr-xt/perf/p5333-pseudo-merge-bitmaps.sh32
-rwxr-xr-xt/run-test.sh18
-rwxr-xr-xt/t0000-basic.sh28
-rwxr-xr-xt/t0001-init.sh101
-rwxr-xr-xt/t0003-attributes.sh35
-rwxr-xr-xt/t0006-date.sh51
-rwxr-xr-xt/t0007-git-var.sh2
-rwxr-xr-xt/t0008-ignores.sh8
-rwxr-xr-xt/t0014-alias.sh11
-rwxr-xr-xt/t0015-hash.sh56
-rwxr-xr-xt/t0016-oidmap.sh112
-rwxr-xr-xt/t0017-env-helper.sh9
-rwxr-xr-xt/t0018-advice.sh71
-rwxr-xr-xt/t0033-safe-directory.sh39
-rwxr-xr-xt/t0040-parse-options.sh17
-rwxr-xr-xt/t0064-oid-array.sh18
-rwxr-xr-xt/t0065-strcmp-offset.sh22
-rwxr-xr-xt/t0068-for-each-repo.sh16
-rwxr-xr-xt/t0069-oidtree.sh50
-rwxr-xr-xt/t0080-unit-test-output.sh24
-rwxr-xr-xt/t0095-bloom.sh8
-rwxr-xr-xt/t0211-trace2-perf.sh2
-rwxr-xr-xt/t0300-credentials.sh167
-rwxr-xr-xt/t0301-credential-cache.sh1
-rwxr-xr-xt/t0410-partial-clone.sh62
-rwxr-xr-xt/t0411-clone-from-partial.sh79
-rw-r--r--t/t0450/txt-help-mismatches1
-rwxr-xr-xt/t0600-reffiles-backend.sh80
-rwxr-xr-xt/t0601-reffiles-pack-refs.sh9
-rwxr-xr-xt/t0610-reftable-basics.sh49
-rwxr-xr-xt/t0611-reftable-httpd.sh1
-rwxr-xr-xt/t0612-reftable-jgit-compatibility.sh133
-rwxr-xr-xt/t0613-reftable-write-options.sh287
-rwxr-xr-xt/t1004-read-tree-m-u-wf.sh1
-rwxr-xr-xt/t1006-cat-file.sh30
-rwxr-xr-xt/t1007-hash-object.sh6
-rwxr-xr-xt/t1013-read-tree-submodule.sh1
-rwxr-xr-xt/t1015-read-index-unmerged.sh2
-rwxr-xr-xt/t1021-rerere-in-workdir.sh1
-rwxr-xr-xt/t1090-sparse-checkout-scope.sh1
-rwxr-xr-xt/t1091-sparse-checkout-builtin.sh1
-rwxr-xr-xt/t1092-sparse-checkout-compatibility.sh16
-rwxr-xr-xt/t1300-config.sh441
-rwxr-xr-xt/t1306-xdg-files.sh1
-rwxr-xr-xt/t1350-config-hooks-path.sh8
-rwxr-xr-xt/t1400-update-ref.sh432
-rwxr-xr-xt/t1403-show-ref.sh24
-rwxr-xr-xt/t1404-update-ref-errors.sh196
-rwxr-xr-xt/t1416-ref-transaction-hooks.sh77
-rwxr-xr-xt/t1460-refs-migrate.sh243
-rwxr-xr-xt/t1500-rev-parse.sh6
-rwxr-xr-xt/t1512-rev-parse-disambiguation.sh1
-rwxr-xr-xt/t1517-outside-repo.sh111
-rwxr-xr-xt/t1700-split-index.sh2
-rwxr-xr-xt/t2013-checkout-submodule.sh1
-rwxr-xr-xt/t2024-checkout-dwim.sh1
-rwxr-xr-xt/t2060-switch.sh1
-rwxr-xr-xt/t2405-worktree-submodule.sh1
-rwxr-xr-xt/t2500-untracked-overwriting.sh1
-rwxr-xr-xt/t3007-ls-files-recurse-submodules.sh1
-rwxr-xr-xt/t3203-branch-output.sh2
-rwxr-xr-xt/t3206-range-diff.sh14
-rwxr-xr-xt/t3301-notes.sh1
-rwxr-xr-xt/t3306-notes-prune.sh1
-rwxr-xr-xt/t3308-notes-merge.sh1
-rwxr-xr-xt/t3309-notes-merge-auto-resolve.sh1
-rwxr-xr-xt/t3400-rebase.sh1
-rwxr-xr-xt/t3401-rebase-and-am-rename.sh1
-rwxr-xr-xt/t3403-rebase-skip.sh1
-rwxr-xr-xt/t3404-rebase-interactive.sh45
-rwxr-xr-xt/t3406-rebase-message.sh1
-rwxr-xr-xt/t3407-rebase-abort.sh1
-rwxr-xr-xt/t3415-rebase-autosquash.sh1
-rwxr-xr-xt/t3417-rebase-whitespace-fix.sh1
-rwxr-xr-xt/t3418-rebase-continue.sh1
-rwxr-xr-xt/t3420-rebase-autostash.sh1
-rwxr-xr-xt/t3421-rebase-topology-linear.sh2
-rwxr-xr-xt/t3424-rebase-empty.sh1
-rwxr-xr-xt/t3426-rebase-submodule.sh1
-rwxr-xr-xt/t3428-rebase-signoff.sh89
-rwxr-xr-xt/t3430-rebase-merges.sh1
-rwxr-xr-xt/t3434-rebase-i18n.sh3
-rwxr-xr-xt/t3500-cherry.sh1
-rwxr-xr-xt/t3504-cherry-pick-rerere.sh1
-rwxr-xr-xt/t3505-cherry-pick-empty.sh1
-rwxr-xr-xt/t3508-cherry-pick-many-commits.sh1
-rwxr-xr-xt/t3509-cherry-pick-merge-df.sh1
-rwxr-xr-xt/t3512-cherry-pick-submodule.sh1
-rwxr-xr-xt/t3513-revert-submodule.sh1
-rwxr-xr-xt/t3600-rm.sh1
-rwxr-xr-xt/t3602-rm-sparse-checkout.sh1
-rwxr-xr-xt/t3701-add-interactive.sh71
-rwxr-xr-xt/t3900-i18n-commit.sh1
-rwxr-xr-xt/t3901-i18n-patch.sh1
-rwxr-xr-xt/t3903-stash.sh9
-rwxr-xr-xt/t3906-stash-submodule.sh1
-rwxr-xr-xt/t3907-stash-show-config.sh1
-rwxr-xr-xt/t3910-mac-os-precompose.sh39
-rwxr-xr-xt/t4001-diff-rename.sh4
-rwxr-xr-xt/t4014-format-patch.sh110
-rwxr-xr-xt/t4015-diff-whitespace.sh9
-rwxr-xr-xt/t4020-diff-external.sh66
-rwxr-xr-xt/t4026-color.sh26
-rwxr-xr-xt/t4041-diff-submodule-option.sh1
-rwxr-xr-xt/t4043-diff-rename-binary.sh1
-rwxr-xr-xt/t4046-diff-unmerged.sh24
-rwxr-xr-xt/t4059-diff-submodule-not-initialized.sh1
-rwxr-xr-xt/t4060-diff-submodule-option-diff-format.sh1
-rwxr-xr-xt/t4061-diff-indent.sh1
-rwxr-xr-xt/t4120-apply-popt.sh1
-rwxr-xr-xt/t4131-apply-fake-ancestor.sh1
-rwxr-xr-xt/t4137-apply-submodule.sh1
-rwxr-xr-xt/t4151-am-abort.sh1
-rwxr-xr-xt/t4153-am-resume-override-opts.sh15
-rwxr-xr-xt/t4202-log.sh26
-rwxr-xr-xt/t4204-patch-id.sh34
-rwxr-xr-xt/t4208-log-magic-pathspec.sh1
-rwxr-xr-xt/t4210-log-i18n.sh2
-rwxr-xr-xt/t4216-log-bloom.sh325
-rwxr-xr-xt/t4253-am-keep-cr-dos.sh1
-rwxr-xr-xt/t4255-am-submodule.sh1
-rwxr-xr-xt/t5001-archive-attr.sh3
-rwxr-xr-xt/t5150-request-pull.sh1
-rwxr-xr-xt/t5300-pack-object.sh4
-rwxr-xr-xt/t5305-include-tag.sh1
-rwxr-xr-xt/t5319-multi-pack-index.sh23
-rwxr-xr-xt/t5324-split-commit-graph.sh26
-rwxr-xr-xt/t5326-multi-pack-bitmaps.sh51
-rwxr-xr-xt/t5332-multi-pack-reuse.sh26
-rwxr-xr-xt/t5333-pseudo-merge-bitmaps.sh393
-rwxr-xr-xt/t5407-post-rewrite-hook.sh1
-rwxr-xr-xt/t5500-fetch-pack.sh12
-rwxr-xr-xt/t5505-remote.sh36
-rwxr-xr-xt/t5510-fetch.sh40
-rwxr-xr-xt/t5512-ls-remote.sh32
-rwxr-xr-xt/t5516-fetch-push.sh10
-rwxr-xr-xt/t5529-push-errors.sh17
-rwxr-xr-xt/t5550-http-fetch-dumb.sh21
-rwxr-xr-xt/t5551-http-fetch-smart.sh1
-rwxr-xr-xt/t5553-set-upstream.sh8
-rwxr-xr-xt/t5558-clone-bundle-uri.sh187
-rwxr-xr-xt/t5563-simple-http-auth.sh425
-rwxr-xr-xt/t5564-http-proxy.sh1
-rwxr-xr-xt/t5581-http-curl-verbose.sh1
-rwxr-xr-xt/t5601-clone.sh22
-rwxr-xr-xt/t5605-clone-local.sh3
-rwxr-xr-xt/t5607-clone-bundle.sh36
-rwxr-xr-xt/t5612-clone-refspec.sh1
-rwxr-xr-xt/t5801-remote-helpers.sh23
-rwxr-xr-xt/t5801/git-remote-nourl3
-rwxr-xr-xt/t5801/git-remote-testgit3
-rwxr-xr-xt/t6000-rev-list-misc.sh1
-rwxr-xr-xt/t6001-rev-list-graft.sh1
-rwxr-xr-xt/t6006-rev-list-format.sh1
-rwxr-xr-xt/t6013-rev-list-reverse-parents.sh1
-rwxr-xr-xt/t6017-rev-list-stdin.sh1
-rwxr-xr-xt/t6020-bundle-misc.sh1
-rwxr-xr-xt/t6041-bisect-submodule.sh1
-rwxr-xr-xt/t6112-rev-list-filters-objects.sh12
-rwxr-xr-xt/t6115-rev-list-du.sh2
-rwxr-xr-xt/t6120-describe.sh36
-rwxr-xr-xt/t6130-pathspec-noglob.sh2
-rwxr-xr-xt/t6400-merge-df.sh1
-rwxr-xr-xt/t6402-merge-rename.sh1
-rwxr-xr-xt/t6406-merge-attr.sh42
-rwxr-xr-xt/t6412-merge-large-rename.sh1
-rwxr-xr-xt/t6426-merge-skip-unneeded-updates.sh1
-rwxr-xr-xt/t6427-diff3-conflict-markers.sh1
-rwxr-xr-xt/t6429-merge-sequence-rename-caching.sh1
-rwxr-xr-xt/t6430-merge-recursive.sh1
-rwxr-xr-xt/t6432-merge-recursive-space-options.sh1
-rwxr-xr-xt/t6434-merge-recursive-rename-options.sh1
-rwxr-xr-xt/t6436-merge-overwrite.sh1
-rwxr-xr-xt/t6438-submodule-directory-file-conflicts.sh1
-rwxr-xr-xt/t6500-gc.sh2
-rwxr-xr-xt/t7001-mv.sh2
-rwxr-xr-xt/t7002-mv-sparse-checkout.sh1
-rwxr-xr-xt/t7004-tag.sh114
-rwxr-xr-xt/t7005-editor.sh1
-rwxr-xr-xt/t7006-pager.sh18
-rwxr-xr-xt/t7010-setup.sh1
-rwxr-xr-xt/t7012-skip-worktree-writing.sh1
-rwxr-xr-xt/t7102-reset.sh1
-rwxr-xr-xt/t7112-reset-submodule.sh1
-rwxr-xr-xt/t7201-co.sh1
-rwxr-xr-xt/t7400-submodule-basic.sh31
-rwxr-xr-xt/t7406-submodule-update.sh48
-rwxr-xr-xt/t7417-submodule-path-url.sh1
-rwxr-xr-xt/t7421-submodule-summary-add.sh1
-rwxr-xr-xt/t7423-submodule-symlinks.sh68
-rwxr-xr-xt/t7450-bad-git-dotfiles.sh34
-rwxr-xr-xt/t7501-commit-basic-functionality.sh1
-rwxr-xr-xt/t7505-prepare-commit-msg-hook.sh1
-rwxr-xr-xt/t7512-status-help.sh1
-rwxr-xr-xt/t7600-merge.sh11
-rwxr-xr-xt/t7606-merge-custom.sh1
-rwxr-xr-xt/t7611-merge-abort.sh1
-rwxr-xr-xt/t7900-maintenance.sh6
-rwxr-xr-xt/t8002-blame.sh1
-rwxr-xr-xt/t8003-blame-corner-cases.sh1
-rwxr-xr-xt/t8004-blame-with-conflicts.sh1
-rwxr-xr-xt/t8006-blame-textconv.sh2
-rwxr-xr-xt/t8008-blame-formats.sh2
-rwxr-xr-xt/t8009-blame-vs-topicbranches.sh2
-rwxr-xr-xt/t8011-blame-split-file.sh2
-rwxr-xr-xt/t8012-blame-colors.sh1
-rwxr-xr-xt/t8013-blame-ignore-revs.sh2
-rwxr-xr-xt/t8014-blame-ignore-fuzzy.sh2
-rwxr-xr-xt/t9001-send-email.sh45
-rwxr-xr-xt/t9004-example.sh12
-rwxr-xr-xt/t9118-git-svn-funky-branch-names.sh2
-rwxr-xr-xt/t9129-git-svn-i18n-commitencoding.sh1
-rwxr-xr-xt/t9139-git-svn-non-utf8-commitencoding.sh1
-rwxr-xr-xt/t9200-git-cvsexportcommit.sh1
-rwxr-xr-xt/t9210-scalar.sh38
-rwxr-xr-xt/t9401-git-cvsserver-crlf.sh1
-rwxr-xr-xt/t9500-gitweb-standalone-no-errors.sh1
-rwxr-xr-xt/t9502-gitweb-standalone-parse-output.sh1
-rwxr-xr-xt/t9600-cvsimport.sh1
-rwxr-xr-xt/t9601-cvsimport-vendor-branch.sh1
-rwxr-xr-xt/t9602-cvsimport-branches-tags.sh1
-rwxr-xr-xt/t9603-cvsimport-patchsets.sh2
-rwxr-xr-xt/t9604-cvsimport-timestamps.sh2
-rwxr-xr-xt/t9902-completion.sh81
-rw-r--r--t/test-lib-functions.sh46
-rw-r--r--t/test-lib.sh40
-rwxr-xr-xt/test-terminal.perl29
-rw-r--r--t/unit-tests/lib-oid.c52
-rw-r--r--t/unit-tests/lib-oid.h17
-rw-r--r--t/unit-tests/t-example-decorate.c82
-rw-r--r--t/unit-tests/t-hash.c84
-rw-r--r--t/unit-tests/t-oidmap.c181
-rw-r--r--t/unit-tests/t-oidtree.c122
-rw-r--r--t/unit-tests/t-reftable-basics.c160
-rw-r--r--t/unit-tests/t-reftable-record.c551
-rw-r--r--t/unit-tests/t-strbuf.c10
-rw-r--r--t/unit-tests/t-strcmp-offset.c35
-rw-r--r--t/unit-tests/t-strvec.c272
-rw-r--r--t/unit-tests/t-trailer.c315
-rw-r--r--t/unit-tests/test-lib.c13
-rw-r--r--t/unit-tests/test-lib.h13
-rw-r--r--tag.c6
-rw-r--r--tag.h2
-rw-r--r--tmp-objdir.c2
-rw-r--r--trace2.h1
-rw-r--r--trailer.c181
-rw-r--r--trailer.h102
-rw-r--r--transport-helper.c37
-rw-r--r--transport.c41
-rw-r--r--tree-diff.c1
-rw-r--r--tree-walk.c6
-rw-r--r--tree-walk.h2
-rw-r--r--tree.c2
-rw-r--r--unpack-trees.c5
-rw-r--r--upload-pack.c26
-rw-r--r--userdiff.c16
-rw-r--r--userdiff.h7
-rw-r--r--walker.c10
-rw-r--r--worktree.c33
-rw-r--r--worktree.h10
-rw-r--r--wt-status.c31
-rw-r--r--xdiff-interface.c2
-rw-r--r--xdiff-interface.h2
925 files changed, 25872 insertions, 11273 deletions
diff --git a/.clang-format b/.clang-format
index 3ed4fac753..6408251577 100644
--- a/.clang-format
+++ b/.clang-format
@@ -149,20 +149,25 @@ Cpp11BracedListStyle: false
# A list of macros that should be interpreted as foreach loops instead of as
# function calls. Taken from:
-# git grep -h '^#define [^[:space:]]*for_each[^[:space:]]*(' \
-# | sed "s,^#define \([^[:space:]]*for_each[^[:space:]]*\)(.*$, - '\1'," \
-# | sort | uniq
+# git grep -h '^#define [^[:space:]]*for_\?each[^[:space:]]*(' |
+# sed "s/^#define / - '/; s/(.*$/'/" | sort | uniq
ForEachMacros:
- - 'for_each_abbrev'
- 'for_each_builtin'
- 'for_each_string_list_item'
- 'for_each_ut'
- 'for_each_wanted_builtin'
+ - 'hashmap_for_each_entry'
+ - 'hashmap_for_each_entry_from'
+ - 'kh_foreach'
+ - 'kh_foreach_value'
- 'list_for_each'
- 'list_for_each_dir'
- 'list_for_each_prev'
- 'list_for_each_prev_safe'
- 'list_for_each_safe'
+ - 'strintmap_for_each_entry'
+ - 'strmap_for_each_entry'
+ - 'strset_for_each_entry'
# The maximum number of consecutive empty lines to keep.
MaxEmptyLinesToKeep: 1
diff --git a/.github/workflows/check-whitespace.yml b/.github/workflows/check-whitespace.yml
index a241a63428..d0a78fc426 100644
--- a/.github/workflows/check-whitespace.yml
+++ b/.github/workflows/check-whitespace.yml
@@ -26,66 +26,7 @@ jobs:
- name: git log --check
id: check_out
run: |
- baseSha=${{github.event.pull_request.base.sha}}
- problems=()
- commit=
- commitText=
- commitTextmd=
- goodparent=
- while read dash sha etc
- do
- case "${dash}" in
- "---")
- if test -z "${commit}"
- then
- goodparent=${sha}
- fi
- commit="${sha}"
- commitText="${sha} ${etc}"
- commitTextmd="[${sha}](https://github.com/${{ github.repository }}/commit/${sha}) ${etc}"
- ;;
- "")
- ;;
- *)
- if test -n "${commit}"
- then
- problems+=("1) --- ${commitTextmd}")
- echo ""
- echo "--- ${commitText}"
- commit=
- fi
- case "${dash}" in
- *:[1-9]*:) # contains file and line number information
- dashend=${dash#*:}
- problems+=("[${dash}](https://github.com/${{ github.repository }}/blob/${{github.event.pull_request.head.ref}}/${dash%%:*}#L${dashend%:}) ${sha} ${etc}")
- ;;
- *)
- problems+=("\`${dash} ${sha} ${etc}\`")
- ;;
- esac
- echo "${dash} ${sha} ${etc}"
- ;;
- esac
- done <<< $(git log --check --pretty=format:"---% h% s" ${baseSha}..)
-
- if test ${#problems[*]} -gt 0
- then
- if test -z "${commit}"
- then
- goodparent=${baseSha: 0:7}
- fi
- echo "🛑 Please review the Summary output for further information."
- echo "### :x: A whitespace issue was found in one or more of the commits." >$GITHUB_STEP_SUMMARY
- echo "" >>$GITHUB_STEP_SUMMARY
- echo "Run these commands to correct the problem:" >>$GITHUB_STEP_SUMMARY
- echo "1. \`git rebase --whitespace=fix ${goodparent}\`" >>$GITHUB_STEP_SUMMARY
- echo "1. \`git push --force\`" >>$GITHUB_STEP_SUMMARY
- echo " " >>$GITHUB_STEP_SUMMARY
- echo "Errors:" >>$GITHUB_STEP_SUMMARY
- for i in "${problems[@]}"
- do
- echo "${i}" >>$GITHUB_STEP_SUMMARY
- done
-
- exit 2
- fi
+ ./ci/check-whitespace.sh \
+ "${{github.event.pull_request.base.sha}}" \
+ "$GITHUB_STEP_SUMMARY" \
+ "https://github.com/${{github.repository}}"
diff --git a/.github/workflows/coverity.yml b/.github/workflows/coverity.yml
index 53cf12fe04..48341e81f4 100644
--- a/.github/workflows/coverity.yml
+++ b/.github/workflows/coverity.yml
@@ -45,7 +45,7 @@ jobs:
- run: ci/install-dependencies.sh
if: contains(matrix.os, 'ubuntu') || contains(matrix.os, 'macos')
env:
- runs_on_pool: ${{ matrix.os }}
+ distro: ${{ matrix.os }}
# The Coverity site says the tool is usually updated twice yearly, so the
# MD5 of download can be used to determine whether there's been an update.
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 3428773b09..13cc0fe807 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -284,8 +284,7 @@ jobs:
cc: clang
pool: macos-13
- jobname: osx-gcc
- cc: gcc
- cc_package: gcc-13
+ cc: gcc-13
pool: macos-13
- jobname: linux-gcc-default
cc: gcc
@@ -303,7 +302,7 @@ jobs:
CC: ${{matrix.vector.cc}}
CC_PACKAGE: ${{matrix.vector.cc_package}}
jobname: ${{matrix.vector.jobname}}
- runs_on_pool: ${{matrix.vector.pool}}
+ distro: ${{matrix.vector.pool}}
runs-on: ${{matrix.vector.pool}}
steps:
- uses: actions/checkout@v4
@@ -342,12 +341,16 @@ jobs:
vector:
- jobname: linux-musl
image: alpine
+ distro: alpine-latest
- jobname: linux32
image: daald/ubuntu32:xenial
+ distro: ubuntu32-16.04
- jobname: pedantic
image: fedora
+ distro: fedora-latest
env:
jobname: ${{matrix.vector.jobname}}
+ distro: ${{matrix.vector.distro}}
runs-on: ubuntu-latest
container: ${{matrix.vector.image}}
steps:
@@ -355,7 +358,7 @@ jobs:
if: matrix.vector.jobname != 'linux32'
- uses: actions/checkout@v1 # cannot be upgraded because Node.js Actions aren't supported in this container
if: matrix.vector.jobname == 'linux32'
- - run: ci/install-docker-dependencies.sh
+ - run: ci/install-dependencies.sh
- run: ci/run-build-and-tests.sh
- name: print test failures
if: failure() && env.FAILED_TEST_ARTIFACTS != ''
diff --git a/.gitignore b/.gitignore
index 612c0f6a0f..8caf3700c2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -126,6 +126,7 @@
/git-rebase
/git-receive-pack
/git-reflog
+/git-refs
/git-remote
/git-remote-http
/git-remote-https
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index c0fa2fe90b..37b991e080 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -9,8 +9,10 @@ workflow:
test:linux:
image: $image
+ variables:
+ CUSTOM_PATH: "/custom"
before_script:
- - ./ci/install-docker-dependencies.sh
+ - ./ci/install-dependencies.sh
script:
- useradd builder --create-home
- chown -R builder "${CI_PROJECT_DIR}"
@@ -93,12 +95,39 @@ test:osx:
- t/failed-test-artifacts
when: on_failure
+test:fuzz-smoke-tests:
+ image: ubuntu:latest
+ variables:
+ CC: clang
+ before_script:
+ - ./ci/install-dependencies.sh
+ script:
+ - ./ci/run-build-and-minimal-fuzzers.sh
+
static-analysis:
image: ubuntu:22.04
variables:
jobname: StaticAnalysis
before_script:
- - ./ci/install-docker-dependencies.sh
+ - ./ci/install-dependencies.sh
script:
- ./ci/run-static-analysis.sh
- ./ci/check-directional-formatting.bash
+
+check-whitespace:
+ image: ubuntu:latest
+ before_script:
+ - ./ci/install-dependencies.sh
+ script:
+ - ./ci/check-whitespace.sh "$CI_MERGE_REQUEST_TARGET_BRANCH_SHA"
+ rules:
+ - if: $CI_PIPELINE_SOURCE == 'merge_request_event'
+
+documentation:
+ image: ubuntu:latest
+ variables:
+ jobname: Documentation
+ before_script:
+ - ./ci/install-dependencies.sh
+ script:
+ - ./ci/test-documentation.sh
diff --git a/Documentation/BreakingChanges.txt b/Documentation/BreakingChanges.txt
new file mode 100644
index 0000000000..0532bfcf7f
--- /dev/null
+++ b/Documentation/BreakingChanges.txt
@@ -0,0 +1,135 @@
+= Upcoming breaking changes
+
+The Git project aims to ensure backwards compatibility to the best extent
+possible. Minor releases will not break backwards compatibility unless there is
+a very strong reason to do so, like for example a security vulnerability.
+
+Regardless of that, due to the age of the Git project, it is only natural to
+accumulate a backlog of backwards-incompatible changes that will eventually be
+required to keep the project aligned with a changing world. These changes fall
+into several categories:
+
+* Changes to long established defaults.
+* Concepts that have been replaced with a superior design.
+* Concepts, commands, configuration or options that have been lacking in major
+ ways and that cannot be fixed and which will thus be removed without any
+ replacement.
+
+Explicitly not included in this list are fixes to minor bugs that may cause a
+change in user-visible behavior.
+
+The Git project irregularly releases breaking versions that deliberately break
+backwards compatibility with older versions. This is done to ensure that Git
+remains relevant, safe and maintainable going forward. The release cadence of
+breaking versions is typically measured in multiple years. We had the following
+major breaking releases in the past:
+
+* Git 1.6.0, released in August 2008.
+* Git 2.0, released in May 2014.
+
+We use <major>.<minor> release numbers these days, starting from Git 2.0. For
+future releases, our plan is to increment <major> in the release number when we
+make the next breaking release. Before Git 2.0, the release numbers were
+1.<major>.<minor> with the intention to increment <major> for "usual" breaking
+releases, reserving the jump to Git 2.0 for really large backward-compatibility
+breaking changes.
+
+The intent of this document is to track upcoming deprecations for future
+breaking releases. Furthermore, this document also tracks what will _not_ be
+deprecated. This is done such that the outcome of discussions document both
+when the discussion favors deprecation, but also when it rejects a deprecation.
+
+Items should have a clear summary of the reasons why we do or do not want to
+make the described change that can be easily understood without having to read
+the mailing list discussions. If there are alternatives to the changed feature,
+those alternatives should be pointed out to our users.
+
+All items should be accompanied by references to relevant mailing list threads
+where the deprecation was discussed. These references use message-IDs, which
+can visited via
+
+ https://lore.kernel.org/git/$message_id/
+
+to see the message and its surrounding discussion. Such a reference is there to
+make it easier for you to find how the project reached consensus on the
+described item back then.
+
+This is a living document as the environment surrounding the project changes
+over time. If circumstances change, an earlier decision to deprecate or change
+something may need to be revisited from time to time. So do not take items on
+this list to mean "it is settled, do not waste our time bringing it up again".
+
+== Git 3.0
+
+The following subsections document upcoming breaking changes for Git 3.0. There
+is no planned release date for this breaking version yet.
+
+Proposed changes and removals only include items which are "ready" to be done.
+In other words, this is not supposed to be a wishlist of features that should
+be changed to or replaced in case the alternative was implemented already.
+
+=== Changes
+
+* The default hash function for new repositories will be changed from "sha1"
+ to "sha256". SHA-1 has been deprecated by NIST in 2011 and is nowadays
+ recommended against in FIPS 140-2 and similar certifications. Furthermore,
+ there are practical attacks on SHA-1 that weaken its cryptographic properties:
++
+ ** The SHAppening (2015). The first demonstration of a practical attack
+ against SHA-1 with 2^57 operations.
+ ** SHAttered (2017). Generation of two valid PDF files with 2^63 operations.
+ ** Birthday-Near-Collision (2019). This attack allows for chosen prefix
+ attacks with 2^68 operations.
+ ** Shambles (2020). This attack allows for chosen prefix attacks with 2^63
+ operations.
++
+While we have protections in place against known attacks, it is expected
+that more attacks against SHA-1 will be found by future research. Paired
+with the ever-growing capability of hardware, it is only a matter of time
+before SHA-1 will be considered broken completely. We want to be prepared
+and will thus change the default hash algorithm to "sha256" for newly
+initialized repositories.
++
+An important requirement for this change is that the ecosystem is ready to
+support the "sha256" object format. This includes popular Git libraries,
+applications and forges.
++
+There is no plan to deprecate the "sha1" object format at this point in time.
++
+Cf. <2f5de416-04ba-c23d-1e0b-83bb655829a7@zombino.com>,
+<20170223155046.e7nxivfwqqoprsqj@LykOS.localdomain>,
+<CA+EOSBncr=4a4d8n9xS4FNehyebpmX8JiUwCsXD47EQDE+DiUQ@mail.gmail.com>.
+
+=== Removals
+
+* Support for grafting commits has long been superseded by git-replace(1).
+ Grafts are inferior to replacement refs:
++
+ ** Grafts are a local-only mechanism and cannot be shared across
+ repositories.
+ ** Grafts can lead to hard-to-diagnose problems when transferring objects
+ between repositories.
++
+The grafting mechanism has been marked as outdated since e650d0643b (docs: mark
+info/grafts as outdated, 2014-03-05) and will be removed.
++
+Cf. <20140304174806.GA11561@sigill.intra.peff.net>.
+
+== Superseded features that will not be deprecated
+
+Some features have gained newer replacements that aim to improve the design in
+certain ways. The fact that there is a replacement does not automatically mean
+that the old way of doing things will eventually be removed. This section tracks
+those features with newer alternatives.
+
+* The features git-checkout(1) offers are covered by the pair of commands
+ git-restore(1) and git-switch(1). Because the use of git-checkout(1) is still
+ widespread, and it is not expected that this will change anytime soon, all
+ three commands will stay.
++
+This decision may get revisited in case we ever figure out that there are
+almost no users of any of the commands anymore.
++
+Cf. <xmqqttjazwwa.fsf@gitster.g>,
+<xmqqleeubork.fsf@gitster.g>,
+<112b6568912a6de6672bf5592c3a718e@manjaro.org>.
diff --git a/Documentation/DecisionMaking.txt b/Documentation/DecisionMaking.txt
new file mode 100644
index 0000000000..dbb4c1f569
--- /dev/null
+++ b/Documentation/DecisionMaking.txt
@@ -0,0 +1,74 @@
+Decision-Making Process in the Git Project
+==========================================
+
+Introduction
+------------
+This document describes the current decision-making process in the Git
+project. It is a descriptive rather than prescriptive doc; that is, we want to
+describe how things work in practice rather than explicitly recommending any
+particular process or changes to the current process.
+
+Here we document how the project makes decisions for discussions
+(with or without patches), in scale larger than an individual patch
+series (which is fully covered by the SubmittingPatches document).
+
+
+Larger Discussions (with patches)
+---------------------------------
+As with discussions on an individual patch series, starting a larger-scale
+discussion often begins by sending a patch or series to the list. This might
+take the form of an initial design doc, with implementation following in later
+iterations of the series (for example,
+link:https://lore.kernel.org/git/0169ce6fb9ccafc089b74ae406db0d1a8ff8ac65.1688165272.git.steadmon@google.com/[adding unit tests] or
+link:https://lore.kernel.org/git/20200420235310.94493-1-emilyshaffer@google.com/[config-based hooks]),
+or it might include a full implementation from the beginning.
+In either case, discussion progresses the same way for an individual patch series,
+until consensus is reached or the topic is dropped.
+
+
+Larger Discussions (without patches)
+------------------------------------
+Occasionally, larger discussions might occur without an associated patch series.
+These may be very large-scale technical decisions that are beyond the scope of
+even a single large patch series, or they may be more open-ended,
+policy-oriented discussions (examples:
+link:https://lore.kernel.org/git/ZZ77NQkSuiRxRDwt@nand.local/[introducing Rust]
+or link:https://lore.kernel.org/git/YHofmWcIAidkvJiD@google.com/[improving submodule UX]).
+In either case, discussion progresses as described above for general patch series.
+
+For larger discussions without a patch series or other concrete implementation,
+it may be hard to judge when consensus has been reached, as there are not any
+official guidelines. If discussion stalls at this point, it may be helpful to
+restart discussion with an RFC patch series (such as a partial, unfinished
+implementation or proof of concept) that can be more easily debated.
+
+When consensus is reached that it is a good idea, the original
+proposer is expected to coordinate the effort to make it happen,
+with help from others who were involved in the discussion, as
+needed.
+
+For decisions that require code changes, it is often the case that the original
+proposer will follow up with a patch series, although it is also common for
+other interested parties to provide an implementation (or parts of the
+implementation, for very large changes).
+
+For non-technical decisions such as community norms or processes, it is up to
+the community as a whole to implement and sustain agreed-upon changes.
+The project leadership committe (PLC) may help the implementation of
+policy decisions.
+
+
+Other Discussion Venues
+-----------------------
+Occasionally decision proposals are presented off-list, e.g. at the semi-regular
+Contributors' Summit. While higher-bandwidth face-to-face discussion is often
+useful for quickly reaching consensus among attendees, generally we expect to
+summarize the discussion in notes that can later be presented on-list. For an
+example, see the thread
+link:https://lore.kernel.org/git/AC2EB721-2979-43FD-922D-C5076A57F24B@jramsay.com.au/[Notes
+from Git Contributor Summit, Los Angeles (April 5, 2020)] by James Ramsay.
+
+We prefer that "official" discussion happens on the list so that the full
+community has opportunity to engage in discussion. This also means that the
+mailing list archives contain a more-or-less complete history of project
+discussions and decisions.
diff --git a/Documentation/Makefile b/Documentation/Makefile
index 3f2383a12c..dc65759cb1 100644
--- a/Documentation/Makefile
+++ b/Documentation/Makefile
@@ -51,6 +51,7 @@ MAN7_TXT += gitdiffcore.txt
MAN7_TXT += giteveryday.txt
MAN7_TXT += gitfaq.txt
MAN7_TXT += gitglossary.txt
+MAN7_TXT += gitpacking.txt
MAN7_TXT += gitnamespaces.txt
MAN7_TXT += gitremote-helpers.txt
MAN7_TXT += gitrevisions.txt
@@ -103,6 +104,7 @@ SP_ARTICLES += howto/coordinate-embargoed-releases
API_DOCS = $(patsubst %.txt,%,$(filter-out technical/api-index-skel.txt technical/api-index.txt, $(wildcard technical/api-*.txt)))
SP_ARTICLES += $(API_DOCS)
+TECH_DOCS += DecisionMaking
TECH_DOCS += ReviewingGuidelines
TECH_DOCS += MyFirstContribution
TECH_DOCS += MyFirstObjectWalk
@@ -484,12 +486,16 @@ $(LINT_DOCS_FSCK_MSGIDS): ../fsck.h fsck-msgids.txt
lint-docs-fsck-msgids: $(LINT_DOCS_FSCK_MSGIDS)
+lint-docs-manpages:
+ $(QUIET_GEN)./lint-manpages.sh
+
## Lint: list of targets above
.PHONY: lint-docs
lint-docs: lint-docs-fsck-msgids
lint-docs: lint-docs-gitlink
lint-docs: lint-docs-man-end-blurb
lint-docs: lint-docs-man-section-order
+lint-docs: lint-docs-manpages
ifeq ($(wildcard po/Makefile),po/Makefile)
doc-l10n install-l10n::
diff --git a/Documentation/MyFirstContribution.txt b/Documentation/MyFirstContribution.txt
index f06563e981..e41654c00a 100644
--- a/Documentation/MyFirstContribution.txt
+++ b/Documentation/MyFirstContribution.txt
@@ -1116,6 +1116,15 @@ $ git send-email --to=target@example.com psuh/*.patch
NOTE: Check `git help send-email` for some other options which you may find
valuable, such as changing the Reply-to address or adding more CC and BCC lines.
+:contrib-scripts: footnoteref:[contrib-scripts,Scripts under `contrib/` are +
+not part of the core `git` binary and must be called directly. Clone the Git +
+codebase and run `perl contrib/contacts/git-contacts`.]
+
+NOTE: If you're not sure whom to CC, running `contrib/contacts/git-contacts` can
+list potential reviewers. In addition, you can do `git send-email
+--cc-cmd='perl contrib/contacts/git-contacts' feature/*.patch`{contrib-scripts} to
+automatically pass this list of emails to `send-email`.
+
NOTE: When you are sending a real patch, it will go to git@vger.kernel.org - but
please don't send your patchset from the tutorial to the real mailing list! For
now, you can send it to yourself, to make sure you understand how it will look.
diff --git a/Documentation/RelNotes/2.39.4.txt b/Documentation/RelNotes/2.39.4.txt
new file mode 100644
index 0000000000..7f54521fea
--- /dev/null
+++ b/Documentation/RelNotes/2.39.4.txt
@@ -0,0 +1,79 @@
+Git v2.39.4 Release Notes
+=========================
+
+This addresses the security issues CVE-2024-32002, CVE-2024-32004,
+CVE-2024-32020 and CVE-2024-32021.
+
+This release also backports fixes necessary to let the CI builds pass
+successfully.
+
+Fixes since v2.39.3
+-------------------
+
+ * CVE-2024-32002:
+
+ Recursive clones on case-insensitive filesystems that support symbolic
+ links are susceptible to case confusion that can be exploited to
+ execute just-cloned code during the clone operation.
+
+ * CVE-2024-32004:
+
+ Repositories can be configured to execute arbitrary code during local
+ clones. To address this, the ownership checks introduced in v2.30.3
+ are now extended to cover cloning local repositories.
+
+ * CVE-2024-32020:
+
+ Local clones may end up hardlinking files into the target repository's
+ object database when source and target repository reside on the same
+ disk. If the source repository is owned by a different user, then
+ those hardlinked files may be rewritten at any point in time by the
+ untrusted user.
+
+ * CVE-2024-32021:
+
+ When cloning a local source repository that contains symlinks via the
+ filesystem, Git may create hardlinks to arbitrary user-readable files
+ on the same filesystem as the target repository in the objects/
+ directory.
+
+ * CVE-2024-32465:
+
+ It is supposed to be safe to clone untrusted repositories, even those
+ unpacked from zip archives or tarballs originating from untrusted
+ sources, but Git can be tricked to run arbitrary code as part of the
+ clone.
+
+ * Defense-in-depth: submodule: require the submodule path to contain
+ directories only.
+
+ * Defense-in-depth: clone: when symbolic links collide with directories, keep
+ the latter.
+
+ * Defense-in-depth: clone: prevent hooks from running during a clone.
+
+ * Defense-in-depth: core.hooksPath: add some protection while cloning.
+
+ * Defense-in-depth: fsck: warn about symlink pointing inside a gitdir.
+
+ * Various fix-ups on HTTP tests.
+
+ * Test update.
+
+ * HTTP Header redaction code has been adjusted for a newer version of
+ cURL library that shows its traces differently from earlier
+ versions.
+
+ * Fix was added to work around a regression in libcURL 8.7.0 (which has
+ already been fixed in their tip of the tree).
+
+ * Replace macos-12 used at GitHub CI with macos-13.
+
+ * ci(linux-asan/linux-ubsan): let's save some time
+
+ * Tests with LSan from time to time seem to emit harmless message that makes
+ our tests unnecessarily flakey; we work it around by filtering the
+ uninteresting output.
+
+ * Update GitHub Actions jobs to avoid warnings against using deprecated
+ version of Node.js.
diff --git a/Documentation/RelNotes/2.39.5.txt b/Documentation/RelNotes/2.39.5.txt
new file mode 100644
index 0000000000..97c0185de4
--- /dev/null
+++ b/Documentation/RelNotes/2.39.5.txt
@@ -0,0 +1,26 @@
+Git v2.39.5 Release Notes
+=========================
+
+In preparing security fixes for four CVEs, we made overly aggressive
+"defense in depth" changes that broke legitimate use cases like 'git
+lfs' and 'git annex.' This release is to revert these misguided, if
+well-intentioned, changes that were shipped in 2.39.4 and were not
+direct security fixes.
+
+Jeff King (5):
+ send-email: drop FakeTerm hack
+ send-email: avoid creating more than one Term::ReadLine object
+ ci: drop mention of BREW_INSTALL_PACKAGES variable
+ ci: avoid bare "gcc" for osx-gcc job
+ ci: stop installing "gcc-13" for osx-gcc
+
+Johannes Schindelin (6):
+ hook: plug a new memory leak
+ init: use the correct path of the templates directory again
+ Revert "core.hooksPath: add some protection while cloning"
+ tests: verify that `clone -c core.hooksPath=/dev/null` works again
+ clone: drop the protections where hooks aren't run
+ Revert "Add a helper function to compare file contents"
+
+Junio C Hamano (1):
+ Revert "fsck: warn about symlink pointing inside a gitdir"
diff --git a/Documentation/RelNotes/2.40.2.txt b/Documentation/RelNotes/2.40.2.txt
new file mode 100644
index 0000000000..646a2cc3eb
--- /dev/null
+++ b/Documentation/RelNotes/2.40.2.txt
@@ -0,0 +1,7 @@
+Git v2.40.2 Release Notes
+=========================
+
+This release merges up the fix that appears in v2.39.4 to address
+the security issues CVE-2024-32002, CVE-2024-32004, CVE-2024-32020,
+CVE-2024-32021 and CVE-2024-32465; see the release notes for that
+version for details.
diff --git a/Documentation/RelNotes/2.40.3.txt b/Documentation/RelNotes/2.40.3.txt
new file mode 100644
index 0000000000..6ca088ec86
--- /dev/null
+++ b/Documentation/RelNotes/2.40.3.txt
@@ -0,0 +1,26 @@
+Git v2.40.3 Release Notes
+=========================
+
+In preparing security fixes for four CVEs, we made overly aggressive
+"defense in depth" changes that broke legitimate use cases like 'git
+lfs' and 'git annex.' This release is to revert these misguided, if
+well-intentioned, changes that were shipped in 2.40.2 and were not
+direct security fixes.
+
+Jeff King (5):
+ send-email: drop FakeTerm hack
+ send-email: avoid creating more than one Term::ReadLine object
+ ci: drop mention of BREW_INSTALL_PACKAGES variable
+ ci: avoid bare "gcc" for osx-gcc job
+ ci: stop installing "gcc-13" for osx-gcc
+
+Johannes Schindelin (6):
+ hook: plug a new memory leak
+ init: use the correct path of the templates directory again
+ Revert "core.hooksPath: add some protection while cloning"
+ tests: verify that `clone -c core.hooksPath=/dev/null` works again
+ clone: drop the protections where hooks aren't run
+ Revert "Add a helper function to compare file contents"
+
+Junio C Hamano (1):
+ Revert "fsck: warn about symlink pointing inside a gitdir"
diff --git a/Documentation/RelNotes/2.41.1.txt b/Documentation/RelNotes/2.41.1.txt
new file mode 100644
index 0000000000..9fb4c218b2
--- /dev/null
+++ b/Documentation/RelNotes/2.41.1.txt
@@ -0,0 +1,7 @@
+Git v2.41.1 Release Notes
+=========================
+
+This release merges up the fix that appears in v2.39.4 and v2.40.2
+to address the security issues CVE-2024-32002, CVE-2024-32004,
+CVE-2024-32020, CVE-2024-32021 and CVE-2024-32465; see the release
+notes for these versions for details.
diff --git a/Documentation/RelNotes/2.41.2.txt b/Documentation/RelNotes/2.41.2.txt
new file mode 100644
index 0000000000..f94afde8c2
--- /dev/null
+++ b/Documentation/RelNotes/2.41.2.txt
@@ -0,0 +1,26 @@
+Git v2.41.2 Release Notes
+=========================
+
+In preparing security fixes for four CVEs, we made overly aggressive
+"defense in depth" changes that broke legitimate use cases like 'git
+lfs' and 'git annex.' This release is to revert these misguided, if
+well-intentioned, changes that were shipped in 2.41.1 and were not
+direct security fixes.
+
+Jeff King (5):
+ send-email: drop FakeTerm hack
+ send-email: avoid creating more than one Term::ReadLine object
+ ci: drop mention of BREW_INSTALL_PACKAGES variable
+ ci: avoid bare "gcc" for osx-gcc job
+ ci: stop installing "gcc-13" for osx-gcc
+
+Johannes Schindelin (6):
+ hook: plug a new memory leak
+ init: use the correct path of the templates directory again
+ Revert "core.hooksPath: add some protection while cloning"
+ tests: verify that `clone -c core.hooksPath=/dev/null` works again
+ clone: drop the protections where hooks aren't run
+ Revert "Add a helper function to compare file contents"
+
+Junio C Hamano (1):
+ Revert "fsck: warn about symlink pointing inside a gitdir"
diff --git a/Documentation/RelNotes/2.42.2.txt b/Documentation/RelNotes/2.42.2.txt
new file mode 100644
index 0000000000..dbf761a01d
--- /dev/null
+++ b/Documentation/RelNotes/2.42.2.txt
@@ -0,0 +1,7 @@
+Git v2.42.2 Release Notes
+=========================
+
+This release merges up the fix that appears in v2.39.4, v2.40.2
+and v2.41.1 to address the security issues CVE-2024-32002,
+CVE-2024-32004, CVE-2024-32020, CVE-2024-32021 and CVE-2024-32465;
+see the release notes for these versions for details.
diff --git a/Documentation/RelNotes/2.42.3.txt b/Documentation/RelNotes/2.42.3.txt
new file mode 100644
index 0000000000..bfe3ba5629
--- /dev/null
+++ b/Documentation/RelNotes/2.42.3.txt
@@ -0,0 +1,26 @@
+Git v2.42.3 Release Notes
+=========================
+
+In preparing security fixes for four CVEs, we made overly aggressive
+"defense in depth" changes that broke legitimate use cases like 'git
+lfs' and 'git annex.' This release is to revert these misguided, if
+well-intentioned, changes that were shipped in 2.42.2 and were not
+direct security fixes.
+
+Jeff King (5):
+ send-email: drop FakeTerm hack
+ send-email: avoid creating more than one Term::ReadLine object
+ ci: drop mention of BREW_INSTALL_PACKAGES variable
+ ci: avoid bare "gcc" for osx-gcc job
+ ci: stop installing "gcc-13" for osx-gcc
+
+Johannes Schindelin (6):
+ hook: plug a new memory leak
+ init: use the correct path of the templates directory again
+ Revert "core.hooksPath: add some protection while cloning"
+ tests: verify that `clone -c core.hooksPath=/dev/null` works again
+ clone: drop the protections where hooks aren't run
+ Revert "Add a helper function to compare file contents"
+
+Junio C Hamano (1):
+ Revert "fsck: warn about symlink pointing inside a gitdir"
diff --git a/Documentation/RelNotes/2.43.4.txt b/Documentation/RelNotes/2.43.4.txt
new file mode 100644
index 0000000000..0a842515ff
--- /dev/null
+++ b/Documentation/RelNotes/2.43.4.txt
@@ -0,0 +1,7 @@
+Git v2.43.4 Release Notes
+=========================
+
+This release merges up the fix that appears in v2.39.4, v2.40.2,
+v2.41.1 and v2.42.2 to address the security issues CVE-2024-32002,
+CVE-2024-32004, CVE-2024-32020, CVE-2024-32021 and CVE-2024-32465;
+see the release notes for these versions for details.
diff --git a/Documentation/RelNotes/2.43.5.txt b/Documentation/RelNotes/2.43.5.txt
new file mode 100644
index 0000000000..236b234b06
--- /dev/null
+++ b/Documentation/RelNotes/2.43.5.txt
@@ -0,0 +1,26 @@
+Git v2.43.5 Release Notes
+=========================
+
+In preparing security fixes for four CVEs, we made overly aggressive
+"defense in depth" changes that broke legitimate use cases like 'git
+lfs' and 'git annex.' This release is to revert these misguided, if
+well-intentioned, changes that were shipped in 2.43.4 and were not
+direct security fixes.
+
+Jeff King (5):
+ send-email: drop FakeTerm hack
+ send-email: avoid creating more than one Term::ReadLine object
+ ci: drop mention of BREW_INSTALL_PACKAGES variable
+ ci: avoid bare "gcc" for osx-gcc job
+ ci: stop installing "gcc-13" for osx-gcc
+
+Johannes Schindelin (6):
+ hook: plug a new memory leak
+ init: use the correct path of the templates directory again
+ Revert "core.hooksPath: add some protection while cloning"
+ tests: verify that `clone -c core.hooksPath=/dev/null` works again
+ clone: drop the protections where hooks aren't run
+ Revert "Add a helper function to compare file contents"
+
+Junio C Hamano (1):
+ Revert "fsck: warn about symlink pointing inside a gitdir"
diff --git a/Documentation/RelNotes/2.44.1.txt b/Documentation/RelNotes/2.44.1.txt
new file mode 100644
index 0000000000..b5135c3281
--- /dev/null
+++ b/Documentation/RelNotes/2.44.1.txt
@@ -0,0 +1,8 @@
+Git v2.44.1 Release Notes
+=========================
+
+This release merges up the fix that appears in v2.39.4, v2.40.2,
+v2.41.1, v2.42.2 and v2.43.4 to address the security issues
+CVE-2024-32002, CVE-2024-32004, CVE-2024-32020, CVE-2024-32021
+and CVE-2024-32465; see the release notes for these versions
+for details.
diff --git a/Documentation/RelNotes/2.44.2.txt b/Documentation/RelNotes/2.44.2.txt
new file mode 100644
index 0000000000..76700f0b73
--- /dev/null
+++ b/Documentation/RelNotes/2.44.2.txt
@@ -0,0 +1,26 @@
+Git v2.44.2 Release Notes
+=========================
+
+In preparing security fixes for four CVEs, we made overly aggressive
+"defense in depth" changes that broke legitimate use cases like 'git
+lfs' and 'git annex.' This release is to revert these misguided, if
+well-intentioned, changes that were shipped in 2.44.1 and were not
+direct security fixes.
+
+Jeff King (5):
+ send-email: drop FakeTerm hack
+ send-email: avoid creating more than one Term::ReadLine object
+ ci: drop mention of BREW_INSTALL_PACKAGES variable
+ ci: avoid bare "gcc" for osx-gcc job
+ ci: stop installing "gcc-13" for osx-gcc
+
+Johannes Schindelin (6):
+ hook: plug a new memory leak
+ init: use the correct path of the templates directory again
+ Revert "core.hooksPath: add some protection while cloning"
+ tests: verify that `clone -c core.hooksPath=/dev/null` works again
+ clone: drop the protections where hooks aren't run
+ Revert "Add a helper function to compare file contents"
+
+Junio C Hamano (1):
+ Revert "fsck: warn about symlink pointing inside a gitdir"
diff --git a/Documentation/RelNotes/2.45.1.txt b/Documentation/RelNotes/2.45.1.txt
new file mode 100644
index 0000000000..3b0d60cfa3
--- /dev/null
+++ b/Documentation/RelNotes/2.45.1.txt
@@ -0,0 +1,8 @@
+Git v2.45.1 Release Notes
+=========================
+
+This release merges up the fix that appears in v2.39.4,
+v2.40.2, v2.41.1, v2.42.2, v2.43.4 and v2.44.1 to address the
+security issues CVE-2024-32002, CVE-2024-32004, CVE-2024-32020,
+CVE-2024-32021 and CVE-2024-32465; see the release notes for
+these versions for details.
diff --git a/Documentation/RelNotes/2.45.2.txt b/Documentation/RelNotes/2.45.2.txt
new file mode 100644
index 0000000000..13429e6491
--- /dev/null
+++ b/Documentation/RelNotes/2.45.2.txt
@@ -0,0 +1,26 @@
+Git v2.45.2 Release Notes
+=========================
+
+In preparing security fixes for four CVEs, we made overly aggressive
+"defense in depth" changes that broke legitimate use cases like 'git
+lfs' and 'git annex.' This release is to revert these misguided, if
+well-intentioned, changes that were shipped in 2.45.1 and were not
+direct security fixes.
+
+Jeff King (5):
+ send-email: drop FakeTerm hack
+ send-email: avoid creating more than one Term::ReadLine object
+ ci: drop mention of BREW_INSTALL_PACKAGES variable
+ ci: avoid bare "gcc" for osx-gcc job
+ ci: stop installing "gcc-13" for osx-gcc
+
+Johannes Schindelin (6):
+ hook: plug a new memory leak
+ init: use the correct path of the templates directory again
+ Revert "core.hooksPath: add some protection while cloning"
+ tests: verify that `clone -c core.hooksPath=/dev/null` works again
+ clone: drop the protections where hooks aren't run
+ Revert "Add a helper function to compare file contents"
+
+Junio C Hamano (1):
+ Revert "fsck: warn about symlink pointing inside a gitdir"
diff --git a/Documentation/RelNotes/2.45.3.txt b/Documentation/RelNotes/2.45.3.txt
new file mode 100644
index 0000000000..2a1e9aa608
--- /dev/null
+++ b/Documentation/RelNotes/2.45.3.txt
@@ -0,0 +1,107 @@
+Git v2.45.3 Release Notes
+=========================
+
+This primarily is to backport various small fixes accumulated on the
+'master' front during the development towards Git 2.46, the next
+feature release.
+
+
+Fixes since v2.45.2
+-------------------
+
+ * Git-GUI has a new maintainer, Johannes Sixt.
+
+ * Tests that try to corrupt in-repository files in chunked format did
+ not work well on macOS due to its broken "mv", which has been
+ worked around.
+
+ * The maximum size of attribute files is enforced more consistently.
+
+ * Unbreak CI jobs so that we do not attempt to use Python 2 that has
+ been removed from the platform.
+
+ * Git 2.43 started using the tree of HEAD as the source of attributes
+ in a bare repository, which has severe performance implications.
+ For now, revert the change, without ripping out a more explicit
+ support for the attr.tree configuration variable.
+
+ * Windows CI running in GitHub Actions started complaining about the
+ order of arguments given to calloc(); the imported regex code uses
+ the wrong order almost consistently, which has been corrected.
+
+ * The SubmittingPatches document now refers folks to manpages
+ translation project.
+
+ * "git rebase --signoff" used to forget that it needs to add a
+ sign-off to the resulting commit when told to continue after a
+ conflict stops its operation.
+
+ * The procedure to build multi-pack-index got confused by the
+ replace-refs mechanism, which has been corrected by disabling the
+ latter.
+
+ * "git stash -S" did not handle binary files correctly, which has
+ been corrected.
+
+ * A scheduled "git maintenance" job is expected to work on all
+ repositories it knows about, but it stopped at the first one that
+ errored out. Now it keeps going.
+
+ * zsh can pretend to be a normal shell pretty well except for some
+ glitches that we tickle in some of our scripts. Work them around
+ so that "vimdiff" and our test suite works well enough with it.
+
+ * Command line completion support for zsh (in contrib/) has been
+ updated to stop exposing internal state to end-user shell
+ interaction.
+
+ * The documentation for "git diff --name-only" has been clarified
+ that it is about showing the names in the post-image tree.
+
+ * The chainlint script (invoked during "make test") did nothing when
+ it failed to detect the number of available CPUs. It now falls
+ back to 1 CPU to avoid the problem.
+
+ * "git init" in an already created directory, when the user
+ configuration has includeif.onbranch, started to fail recently,
+ which has been corrected.
+
+ * The safe.directory configuration knob has been updated to
+ optionally allow leading path matches.
+
+ * An overly large ".gitignore" files are now rejected silently.
+
+ * Fix for an embarrassing typo that prevented Python2 tests from running
+ anywhere.
+
+ * Varargs functions that are unannotated as printf-like or execl-like
+ have been annotated as such.
+
+ * The "-k" and "--rfc" options of "format-patch" will now error out
+ when used together, as one tells us not to add anything to the
+ title of the commit, and the other one tells us to add "RFC" in
+ addition to "PATCH".
+
+ * When the user adds to "git rebase -i" instruction to "pick" a merge
+ commit, the error experience is not pleasant. Such an error is now
+ caught earlier in the process that parses the todo list.
+
+ * We forgot to normalize the result of getcwd() to NFC on macOS where
+ all other paths are normalized, which has been corrected. This still
+ does not address the case where core.precomposeUnicode configuration
+ is not defined globally.
+
+ * Earlier we stopped using the tree of HEAD as the default source of
+ attributes in a bare repository, but failed to document it. This
+ has been corrected.
+
+ * An unused extern declaration for mingw has been removed to prevent
+ it from causing build failure.
+
+ * A helper function shared between two tests had a copy-paste bug,
+ which has been corrected.
+
+ * "git fetch-pack -k -k" without passing "--lock-pack" (which we
+ never do ourselves) did not work at all, which has been corrected.
+
+Also contains various documentation updates and code clean-ups.
diff --git a/Documentation/RelNotes/2.46.0.txt b/Documentation/RelNotes/2.46.0.txt
new file mode 100644
index 0000000000..d836233bb5
--- /dev/null
+++ b/Documentation/RelNotes/2.46.0.txt
@@ -0,0 +1,453 @@
+Git v2.46 Release Notes
+=======================
+
+Backward Compatibility Notes
+
+ (None at this moment)
+
+UI, Workflows & Features
+
+ * The "--rfc" option of "git format-patch" learned to take an
+ optional string value to be used in place of "RFC" to tweak the
+ "[PATCH]" on the subject header.
+
+ * The credential helper protocol, together with the HTTP layer, have
+ been enhanced to support authentication schemes different from
+ username & password pair, like Bearer and NTLM.
+
+ * Command line completion script (in contrib/) learned to complete
+ "git symbolic-ref" a bit better (you need to enable plumbing
+ commands to be completed with GIT_COMPLETION_SHOW_ALL_COMMANDS).
+
+ * When the user responds to a prompt given by "git add -p" with an
+ unsupported command, list of available commands were given, which
+ was too much if the user knew what they wanted to type but merely
+ made a typo. Now the user gets a much shorter error message.
+
+ * The color parsing code learned to handle 12-bit RGB colors, spelled
+ as "#RGB" (in addition to "#RRGGBB" that is already supported).
+
+ * The operation mode options (like "--get") the "git config" command
+ uses have been deprecated and replaced with subcommands (like "git
+ config get").
+
+ * "git tag" learned the "--trailer" option to futz with the trailers
+ in the same way as "git commit" does.
+
+ * A new global "--no-advice" option can be used to disable all advice
+ messages, which is meant to be used only in scripts.
+
+ * Updates to symbolic refs can now be made as a part of ref
+ transaction.
+
+ * The trailer API has been reshuffled a bit.
+
+ * Terminology to call various ref-like things are getting
+ straightened out.
+
+ * The command line completion script (in contrib/) has been adjusted
+ to the recent update to "git config" that adopted subcommand based
+ UI.
+
+ * The knobs to tweak how reftable files are written have been made
+ available as configuration variables.
+
+ * When "git push" notices that the commit at the tip of the ref on
+ the other side it is about to overwrite does not exist locally, it
+ used to first try fetching it if the local repository is a partial
+ clone. The command has been taught not to do so and immediately
+ fail instead.
+
+ * The promisor.quiet configuration knob can be set to true to make
+ lazy fetching from promisor remotes silent.
+
+ * The inter/range-diff output has been moved to the end of the patch
+ when format-patch adds it to a single patch, instead of writing it
+ before the patch text, to be consistent with what is done for a
+ cover letter for a multi-patch series.
+
+ * A new command has been added to migrate a repository that uses the
+ files backend for its ref storage to use the reftable backend, with
+ limitations.
+
+ * "git diff --exit-code --ext-diff" learned to take the exit status
+ of the external diff driver into account when deciding the exit
+ status of the overall "git diff" invocation when configured to do
+ so.
+
+ * "git update-ref --stdin" learned to handle transactional updates of
+ symbolic-refs.
+
+ * "git format-patch --interdiff" for multi-patch series learned to
+ turn on cover letters automatically (unless told never to enable
+ cover letter with "--no-cover-letter" and such).
+
+ * The "--heads" option of "ls-remote" and "show-ref" has been been
+ deprecated; "--branches" replaces "--heads".
+
+ * For over a year, setting add.interactive.useBuiltin configuration
+ variable did nothing but giving a "this does not do anything"
+ warning. The warning has been removed.
+
+ * The http transport can now be told to send request with
+ authentication material without first getting a 401 response.
+
+ * A handful of entries are added to the GitFAQ document.
+
+ * "git var GIT_SHELL_PATH" should report the path to the shell used
+ to spawn external commands, but it didn't do so on Windows, which
+ has been corrected.
+
+
+Performance, Internal Implementation, Development Support etc.
+
+ * Advertise "git contacts", a tool for newcomers to find people to
+ ask review for their patches, a bit more in our developer
+ documentation.
+
+ * In addition to building the objects needed, try to link the objects
+ that are used in fuzzer tests, to make sure at least they build
+ without bitrot, in Linux CI runs.
+
+ * Code to write out reftable has seen some optimization and
+ simplification.
+
+ * Tests to ensure interoperability between reftable written by jgit
+ and our code have been added and enabled in CI.
+
+ * The singleton index_state instance "the_index" has been eliminated
+ by always instantiating "the_repository" and replacing references
+ to "the_index" with references to its .index member.
+
+ * Git-GUI has a new maintainer, Johannes Sixt.
+
+ * The "test-tool" has been taught to run testsuite tests in parallel,
+ bypassing the need to use the "prove" tool.
+
+ * The "whitespace check" task that was enabled for GitHub Actions CI
+ has been ported to GitLab CI.
+
+ * The refs API lost functions that implicitly assumes to work on the
+ primary ref_store by forcing the callers to pass a ref_store as an
+ argument.
+
+ * Code clean-up to reduce inter-function communication inside
+ builtin/config.c done via the use of global variables.
+
+ * The pack bitmap code saw some clean-up to prepare for a follow-up topic.
+
+ * Preliminary code clean-up for "git send-email".
+
+ * The default "creation-factor" used by "git format-patch" has been
+ raised to make it more aggressively find matching commits.
+
+ * Before discovering the repository details, We used to assume SHA-1
+ as the "default" hash function, which has been corrected. Hopefully
+ this will smoke out codepaths that rely on such an unwarranted
+ assumptions.
+
+ * The project decision making policy has been documented.
+
+ * The strcmp-offset tests have been rewritten using the unit test
+ framework.
+
+ * "git add -p" learned to complain when an answer with more than one
+ letter is given to a prompt that expects a single letter answer.
+
+ * The alias-expanded command lines are logged to the trace output.
+
+ * A new test was added to ensure git commands that are designed to
+ run outside repositories do work.
+
+ * A few tests in reftable library have been rewritten using the
+ unit test framework.
+
+ * A pair of test helpers that essentially are unit tests on hash
+ algorithms have been rewritten using the unit-tests framework.
+
+ * A test helper that essentially is unit tests on the "decorate"
+ logic has been rewritten using the unit-tests framework.
+
+ * Many memory leaks in the sparse-checkout code paths have been
+ plugged.
+
+ * "make check-docs" noticed problems and reported to its output but
+ failed to signal its findings with its exit status, which has been
+ corrected.
+
+ * Building with "-Werror -Wwrite-strings" is now supported.
+
+ * To help developers, the build procedure now allows builders to use
+ CFLAGS_APPEND to specify additional CFLAGS.
+
+ * "oidtree" tests were rewritten to use the unit test framework.
+
+ * The structure of the document that records longer-term project
+ decisions to deprecate/remove/update various behaviour has been
+ outlined.
+
+ * The pseudo-merge reachability bitmap to help more efficient storage
+ of the reachability bitmap in a repository with too many refs has
+ been added.
+
+ * When "git merge" sees that the index cannot be refreshed (e.g. due
+ to another process doing the same in the background), it died but
+ after writing MERGE_HEAD etc. files, which was useless for the
+ purpose to recover from the failure.
+
+ * The output from "git cat-file --batch-check" and "--batch-command
+ (info)" should not be unbuffered, for which some tests have been
+ added.
+
+ * A CPP macro USE_THE_REPOSITORY_VARIABLE is introduced to help
+ transition the codebase to rely less on the availability of the
+ singleton the_repository instance.
+
+ * "git version --build-options" reports the version information of
+ OpenSSL and other libraries (if used) in the build.
+
+ * Memory ownership rules for the in-core representation of
+ remote.*.url configuration values have been straightened out, which
+ resulted in a few leak fixes and code clarification.
+
+ * When bundleURI interface fetches multiple bundles, Git failed to
+ take full advantage of all bundles and ended up slurping duplicated
+ objects, which has been corrected..
+
+ * The code to deal with modified paths that are out-of-cone in a
+ sparsely checked out working tree has been optimized.
+
+ * An existing test of oidmap API has been rewritten with the
+ unit-test framework.
+
+ * The "ort" merge backend saw one bugfix for a crash that happens
+ when inner merge gets killed, and assorted code clean-ups.
+
+ * A new warning message is issued when a command has to expand a
+ sparse index to handle working tree cruft that are outside of the
+ sparse checkout.
+
+ * The test framework learned to take the test body not as a single
+ string but as a here-document.
+
+ * "git push '' HEAD:there" used to hit a BUG(); it has been corrected
+ to die with "fatal: bad repository ''".
+
+ * What happens when http.cookieFile gets the special value "" has
+ been clarified in the documentation.
+
+
+Fixes since v2.45
+-----------------
+
+ * "git rebase --signoff" used to forget that it needs to add a
+ sign-off to the resulting commit when told to continue after a
+ conflict stops its operation.
+
+ * The procedure to build multi-pack-index got confused by the
+ replace-refs mechanism, which has been corrected by disabling the
+ latter.
+
+ * The "-k" and "--rfc" options of "format-patch" will now error out
+ when used together, as one tells us not to add anything to the
+ title of the commit, and the other one tells us to add "RFC" in
+ addition to "PATCH".
+
+ * "git stash -S" did not handle binary files correctly, which has
+ been corrected.
+
+ * A scheduled "git maintenance" job is expected to work on all
+ repositories it knows about, but it stopped at the first one that
+ errored out. Now it keeps going.
+
+ * zsh can pretend to be a normal shell pretty well except for some
+ glitches that we tickle in some of our scripts. Work them around
+ so that "vimdiff" and our test suite works well enough with it.
+
+ * Command line completion support for zsh (in contrib/) has been
+ updated to stop exposing internal state to end-user shell
+ interaction.
+
+ * Tests that try to corrupt in-repository files in chunked format did
+ not work well on macOS due to its broken "mv", which has been
+ worked around.
+
+ * The maximum size of attribute files is enforced more consistently.
+
+ * Unbreak CI jobs so that we do not attempt to use Python 2 that has
+ been removed from the platform.
+
+ * Git 2.43 started using the tree of HEAD as the source of attributes
+ in a bare repository, which has severe performance implications.
+ For now, revert the change, without ripping out a more explicit
+ support for the attr.tree configuration variable.
+
+ * The "--exit-code" option of "git diff" command learned to work with
+ the "--ext-diff" option.
+
+ * Windows CI running in GitHub Actions started complaining about the
+ order of arguments given to calloc(); the imported regex code uses
+ the wrong order almost consistently, which has been corrected.
+
+ * Expose "name conflict" error when a ref creation fails due to D/F
+ conflict in the ref namespace, to improve an error message given by
+ "git fetch".
+ (merge 9339fca23e it/refs-name-conflict later to maint).
+
+ * The SubmittingPatches document now refers folks to manpages
+ translation project.
+
+ * The documentation for "git diff --name-only" has been clarified
+ that it is about showing the names in the post-image tree.
+
+ * The credential helper that talks with osx keychain learned to avoid
+ storing back the authentication material it just got received from
+ the keychain.
+ (merge e1ab45b2da kn/osxkeychain-skip-idempotent-store later to maint).
+
+ * The chainlint script (invoked during "make test") did nothing when
+ it failed to detect the number of available CPUs. It now falls
+ back to 1 CPU to avoid the problem.
+
+ * Revert overly aggressive "layered defence" that went into 2.45.1
+ and friends, which broke "git-lfs", "git-annex", and other use
+ cases, so that we can rebuild necessary counterparts in the open.
+
+ * "git init" in an already created directory, when the user
+ configuration has includeif.onbranch, started to fail recently,
+ which has been corrected.
+
+ * Memory leaks in "git mv" has been plugged.
+
+ * The safe.directory configuration knob has been updated to
+ optionally allow leading path matches.
+
+ * An overly large ".gitignore" files are now rejected silently.
+
+ * Upon expiration event, the credential subsystem forgot to clear
+ in-core authentication material other than password (whose support
+ was added recently), which has been corrected.
+
+ * Fix for an embarrassing typo that prevented Python2 tests from running
+ anywhere.
+
+ * Varargs functions that are unannotated as printf-like or execl-like
+ have been annotated as such.
+
+ * "git am" has a safety feature to prevent it from starting a new
+ session when there already is a session going. It reliably
+ triggers when a mbox is given on the command line, but it has to
+ rely on the tty-ness of the standard input. Add an explicit way to
+ opt out of this safety with a command line option.
+ (merge 62c71ace44 jk/am-retry later to maint).
+
+ * A leak in "git imap-send" that somehow escapes LSan has been
+ plugged.
+
+ * Setting core.abbrev too early before the repository set-up
+ (typically in "git clone") caused segfault, which as been
+ corrected.
+
+ * When the user adds to "git rebase -i" instruction to "pick" a merge
+ commit, the error experience is not pleasant. Such an error is now
+ caught earlier in the process that parses the todo list.
+
+ * We forgot to normalize the result of getcwd() to NFC on macOS where
+ all other paths are normalized, which has been corrected. This still
+ does not address the case where core.precomposeUnicode configuration
+ is not defined globally.
+
+ * Earlier we stopped using the tree of HEAD as the default source of
+ attributes in a bare repository, but failed to document it. This
+ has been corrected.
+
+ * "git update-server-info" and "git commit-graph --write" have been
+ updated to use the tempfile API to avoid leaving cruft after
+ failing.
+
+ * An unused extern declaration for mingw has been removed to prevent
+ it from causing build failure.
+
+ * A helper function shared between two tests had a copy-paste bug,
+ which has been corrected.
+
+ * "git fetch-pack -k -k" without passing "--lock-pack" (which we
+ never do ourselves) did not work at all, which has been corrected.
+
+ * CI job to build minimum fuzzers learned to pass NO_CURL=NoThanks to
+ the build procedure, as its build environment does not offer, or
+ the rest of the build needs, anything cURL.
+ (merge 4e66b5a990 jc/fuzz-sans-curl later to maint).
+
+ * "git diff --no-ext-diff" when diff.external is configured ignored
+ the "--color-moved" option.
+ (merge 0f4b0d4cf0 rs/diff-color-moved-w-no-ext-diff-fix later to maint).
+
+ * "git archive --add-virtual-file=<path>:<contents>" never paid
+ attention to the --prefix=<prefix> option but the documentation
+ said it would. The documentation has been corrected.
+ (merge 72c282098d jc/archive-prefix-with-add-virtual-file later to maint).
+
+ * When GIT_PAGER failed to spawn, depending on the code path taken,
+ we failed immediately (correct) or just spew the payload to the
+ standard output (incorrect). The code now always fail immediately
+ when GIT_PAGER fails.
+ (merge 78f0a5d187 rj/pager-die-upon-exec-failure later to maint).
+
+ * date parser updates to be more careful about underflowing epoch
+ based timestamp.
+ (merge 9d69789770 db/date-underflow-fix later to maint).
+
+ * The Bloom filter used for path limited history traversal was broken
+ on systems whose "char" is unsigned; update the implementation and
+ bump the format version to 2.
+ (merge 9c8a9ec787 tb/path-filter-fix later to maint).
+
+ * Typofix.
+ (merge 231cf7370e as/pathspec-h-typofix later to maint).
+
+ * Code clean-up.
+ (merge 4b837f821e rs/simplify-submodule-helper-super-prefix-invocation later to maint).
+
+ * "git describe --dirty --broken" forgot to refresh the index before
+ seeing if there is any chang, ("git describe --dirty" correctly did
+ so), which has been corrected.
+ (merge b8ae42e292 as/describe-broken-refresh-index-fix later to maint).
+
+ * Test suite has been taught not to unnecessarily rely on DNS failing
+ a bogus external name.
+ (merge 407cdbd271 jk/tests-without-dns later to maint).
+
+ * GitWeb update to use committer date consistently in rss/atom feeds.
+ (merge cf6ead095b am/gitweb-feed-use-committer-date later to maint).
+
+ * Custom control structures we invented more recently have been
+ taught to the clang-format file.
+ (merge 1457dff9be rs/clang-format-updates later to maint).
+
+ * Developer build procedure fix.
+ (merge df32729866 tb/dev-build-pedantic-fix later to maint).
+
+ * "git push" that pushes only deletion gave an unnecessary and
+ harmless error message when push negotiation is configured, which
+ has been corrected.
+ (merge 4d8ee0317f jc/disable-push-nego-for-deletion later to maint).
+
+ * Address-looking strings found on the trailer are now placed on the
+ Cc: list after running through sanitize_address by "git send-email".
+ (merge c852531f45 cb/send-email-sanitize-trailer-addresses later to maint).
+
+ * Tests that use GIT_TEST_SANITIZE_LEAK_LOG feature got their exit
+ status inverted, which has been corrected.
+ (merge 8c1d6691bc rj/test-sanitize-leak-log-fix later to maint).
+
+ * The http.cookieFile and http.saveCookies configuration variables
+ have a few values that need to be avoided, which are now ignored
+ with warning messages.
+ (merge 4f5822076f jc/http-cookiefile later to maint).
+
+ * Other code cleanup, docfix, build fix, etc.
+ (merge 493fdae046 ew/object-convert-leakfix later to maint).
+ (merge 00f3661a0a ss/doc-eol-attr-fix later to maint).
+ (merge 428c40da61 ri/doc-show-branch-fix later to maint).
+ (merge 58696bfcaa jc/where-is-bash-for-ci later to maint).
diff --git a/Documentation/SubmittingPatches b/Documentation/SubmittingPatches
index c647c7e1b4..d8a8caa791 100644
--- a/Documentation/SubmittingPatches
+++ b/Documentation/SubmittingPatches
@@ -7,6 +7,73 @@ Here are some guidelines for contributing back to this
project. There is also a link:MyFirstContribution.html[step-by-step tutorial]
available which covers many of these same guidelines.
+[[patch-flow]]
+=== A typical life cycle of a patch series
+
+To help us understand the reason behind various guidelines given later
+in the document, first let's understand how the life cycle of a
+typical patch series for this project goes.
+
+. You come up with an itch. You code it up. You do not need any
+ pre-authorization from the project to do so.
++
+Your patches will be reviewed by other contributors on the mailing
+list, and the reviews will be done to assess the merit of various
+things, like the general idea behind your patch (including "is it
+solving a problem worth solving in the first place?"), the reason
+behind the design of the solution, and the actual implementation.
+The guidelines given here are there to help your patches by making
+them easier to understand by the reviewers.
+
+. You send the patches to the list and cc people who may need to know
+ about the change. Your goal is *not* necessarily to convince others
+ that what you are building is good. Your goal is to get help in
+ coming up with a solution for the "itch" that is better than what
+ you can build alone.
++
+The people who may need to know are the ones who worked on the code
+you are touching. These people happen to be the ones who are
+most likely to be knowledgeable enough to help you, but
+they have no obligation to help you (i.e. you ask them for help,
+you don't demand). +git log -p {litdd} _$area_you_are_modifying_+ would
+help you find out who they are.
+
+. You get comments and suggestions for improvements. You may even get
+ them in an "on top of your change" patch form. You are expected to
+ respond to them with "Reply-All" on the mailing list, while taking
+ them into account while preparing an updated set of patches.
+
+. Polish, refine, and re-send your patches to the list and to the people
+ who spent their time to improve your patch. Go back to step (2).
+
+. While the above iterations improve your patches, the maintainer may
+ pick the patches up from the list and queue them to the `seen`
+ branch, in order to make it easier for people to play with it
+ without having to pick up and apply the patches to their trees
+ themselves. Being in `seen` has no other meaning. Specifically, it
+ does not mean the patch was "accepted" in any way.
+
+. When the discussion reaches a consensus that the latest iteration of
+ the patches are in good enough shape, the maintainer includes the
+ topic in the "What's cooking" report that are sent out a few times a
+ week to the mailing list, marked as "Will merge to 'next'." This
+ decision is primarily made by the maintainer with help from those
+ who participated in the review discussion.
+
+. After the patches are merged to the 'next' branch, the discussion
+ can still continue to further improve them by adding more patches on
+ top, but by the time a topic gets merged to 'next', it is expected
+ that everybody agrees that the scope and the basic direction of the
+ topic are appropriate, so such an incremental updates are limited to
+ small corrections and polishing. After a topic cooks for some time
+ (like 7 calendar days) in 'next' without needing further tweaks on
+ top, it gets merged to the 'master' branch and wait to become part
+ of the next major release.
+
+In the following sections, many techniques and conventions are listed
+to help your patches get reviewed effectively in such a life cycle.
+
+
[[choose-starting-point]]
=== Choose a starting point.
@@ -192,8 +259,9 @@ reasons:
which case, they can explain why they extend your code to cover
files, too).
-The goal of your log message is to convey the _why_ behind your
-change to help future developers.
+The goal of your log message is to convey the _why_ behind your change
+to help future developers. The reviewers will also make sure that
+your proposed log message will serve this purpose well.
The first line of the commit message should be a short description (50
characters is the soft limit, see DISCUSSION in linkgit:git-commit[1]),
@@ -397,17 +465,57 @@ letter.
[[send-patches]]
=== Sending your patches.
+==== Choosing your reviewers
+
:security-ml: footnoteref:[security-ml,The Git Security mailing list: git-security@googlegroups.com]
-Before sending any patches, please note that patches that may be
+NOTE: Patches that may be
security relevant should be submitted privately to the Git Security
mailing list{security-ml}, instead of the public mailing list.
-Learn to use format-patch and send-email if possible. These commands
+:contrib-scripts: footnoteref:[contrib-scripts,Scripts under `contrib/` are +
+not part of the core `git` binary and must be called directly. Clone the Git +
+codebase and run `perl contrib/contacts/git-contacts`.]
+
+Send your patch with "To:" set to the mailing list, with "cc:" listing
+people who are involved in the area you are touching (the `git-contacts`
+script in `contrib/contacts/`{contrib-scripts} can help to
+identify them), to solicit comments and reviews. Also, when you made
+trial merges of your topic to `next` and `seen`, you may have noticed
+work by others conflicting with your changes. There is a good possibility
+that these people may know the area you are touching well.
+
+If you are using `send-email`, you can feed it the output of `git-contacts` like
+this:
+
+....
+ git send-email --cc-cmd='perl contrib/contacts/git-contacts' feature/*.patch
+....
+
+:current-maintainer: footnote:[The current maintainer: gitster@pobox.com]
+:git-ml: footnote:[The mailing list: git@vger.kernel.org]
+
+After the list reached a consensus that it is a good idea to apply the
+patch, re-send it with "To:" set to the maintainer{current-maintainer}
+and "cc:" the list{git-ml} for inclusion. This is especially relevant
+when the maintainer did not heavily participate in the discussion and
+instead left the review to trusted others.
+
+Do not forget to add trailers such as `Acked-by:`, `Reviewed-by:` and
+`Tested-by:` lines as necessary to credit people who helped your
+patch, and "cc:" them when sending such a final version for inclusion.
+
+==== `format-patch` and `send-email`
+
+Learn to use `format-patch` and `send-email` if possible. These commands
are optimized for the workflow of sending patches, avoiding many ways
your existing e-mail client (often optimized for "multipart/*" MIME
type e-mails) might render your patches unusable.
+NOTE: Here we outline the procedure using `format-patch` and
+`send-email`, but you can instead use GitGitGadget to send in your
+patches (see link:MyFirstContribution.html[MyFirstContribution]).
+
People on the Git mailing list need to be able to read and
comment on the changes you are submitting. It is important for
a developer to be able to "quote" your changes, using standard
@@ -415,10 +523,12 @@ e-mail tools, so that they may comment on specific portions of
your code. For this reason, each patch should be submitted
"inline" in a separate message.
-Multiple related patches should be grouped into their own e-mail
-thread to help readers find all parts of the series. To that end,
-send them as replies to either an additional "cover letter" message
-(see below), the first patch, or the respective preceding patch.
+All subsequent versions of a patch series and other related patches should be
+grouped into their own e-mail thread to help readers find all parts of the
+series. To that end, send them as replies to either an additional "cover
+letter" message (see below), the first patch, or the respective preceding patch.
+Here is a link:MyFirstContribution.html#v2-git-send-email[step-by-step guide] on
+how to submit updated versions of a patch series.
If your log message (including your name on the
`Signed-off-by` trailer) is not writable in ASCII, make sure that
@@ -498,42 +608,93 @@ patch, format it as "multipart/signed", not a text/plain message
that starts with `-----BEGIN PGP SIGNED MESSAGE-----`. That is
not a text/plain, it's something else.
-:security-ml-ref: footnoteref:[security-ml]
+=== Handling Conflicts and Iterating Patches
-As mentioned at the beginning of the section, patches that may be
-security relevant should not be submitted to the public mailing list
-mentioned below, but should instead be sent privately to the Git
-Security mailing list{security-ml-ref}.
+When revising changes made to your patches, it's important to
+acknowledge the possibility of conflicts with other ongoing topics. To
+navigate these potential conflicts effectively, follow the recommended
+steps outlined below:
-Send your patch with "To:" set to the mailing list, with "cc:" listing
-people who are involved in the area you are touching (the `git
-contacts` command in `contrib/contacts/` can help to
-identify them), to solicit comments and reviews. Also, when you made
-trial merges of your topic to `next` and `seen`, you may have noticed
-work by others conflicting with your changes. There is a good possibility
-that these people may know the area you are touching well.
+. Build on a suitable base branch, see the <<choose-starting-point, section above>>,
+and format-patch the series. If you are doing "rebase -i" in-place to
+update from the previous round, this will reuse the previous base so
+(2) and (3) may become trivial.
-:current-maintainer: footnote:[The current maintainer: gitster@pobox.com]
-:git-ml: footnote:[The mailing list: git@vger.kernel.org]
+. Find the base of where the last round was queued
++
+ $ mine='kn/ref-transaction-symref'
+ $ git checkout "origin/seen^{/^Merge branch '$mine'}...master"
-After the list reached a consensus that it is a good idea to apply the
-patch, re-send it with "To:" set to the maintainer{current-maintainer}
-and "cc:" the list{git-ml} for inclusion. This is especially relevant
-when the maintainer did not heavily participate in the discussion and
-instead left the review to trusted others.
+. Apply your format-patch result. There are two cases
+.. Things apply cleanly and tests fine. Go to (4).
+.. Things apply cleanly but does not build or test fails, or things do
+not apply cleanly.
++
+In the latter case, you have textual or semantic conflicts coming from
+the difference between the old base and the base you used to build in
+(1). Identify what caused the breakages (e.g., a topic or two may have
+merged since the base used by (2) until the base used by (1)).
++
+Check out the latest 'origin/master' (which may be newer than the base
+used by (2)), "merge --no-ff" the topics you newly depend on in there,
+and use the result of the merge(s) as the base, rebuild the series and
+test again. Run format-patch from the last such merges to the tip of
+your topic. If you did
++
+ $ git checkout origin/master
+ $ git merge --no-ff --into-name kn/ref-transaction-symref fo/obar
+ $ git merge --no-ff --into-name kn/ref-transaction-symref ba/zqux
+ ... rebuild the topic ...
++
+Then you'd just format your topic above these "preparing the ground"
+merges, e.g.
++
+ $ git format-patch "HEAD^{/^Merge branch 'ba/zqux'}"..HEAD
++
+Do not forget to write in the cover letter you did this, including the
+topics you have in your base on top of 'master'. Then go to (4).
-Do not forget to add trailers such as `Acked-by:`, `Reviewed-by:` and
-`Tested-by:` lines as necessary to credit people who helped your
-patch, and "cc:" them when sending such a final version for inclusion.
+. Make a trial merge of your topic into 'next' and 'seen', e.g.
++
+ $ git checkout --detach 'origin/seen'
+ $ git revert -m 1 <the merge of the previous iteration into seen>
+ $ git merge kn/ref-transaction-symref
++
+The "revert" is needed if the previous iteration of your topic is
+already in 'seen' (like in this case). You could choose to rebuild
+master..origin/seen from scratch while excluding your previous
+iteration, which may emulate what happens on the maintainers end more
+closely.
++
+This trial merge may conflict. It is primarily to see what conflicts
+_other_ topics may have with your topic. In other words, you do not
+have to depend on it to make your topic work on 'master'. It may
+become the job of the other topic owners to resolve conflicts if your
+topic goes to 'next' before theirs.
++
+Make a note on what conflict you saw in the cover letter. You do not
+necessarily have to resolve them, but it would be a good opportunity to
+learn what others are doing in related areas.
++
+ $ git checkout --detach 'origin/next'
+ $ git merge kn/ref-transaction-symref
++
+This is to see what conflicts your topic has with other topics that are
+already cooking. This should not conflict if (3)-2 prepared a base on
+top of updated master plus dependent topics taken from 'next'. Unless
+the context is severe (one way to tell is try the same trial merge with
+your old iteration, which may conflict in a similar way), expect that it
+will be handled on maintainers end (if it gets unmanageable, I'll ask to
+rebase when I receive your patches).
== Subsystems with dedicated maintainers
Some parts of the system have dedicated maintainers with their own
repositories.
-- `git-gui/` comes from git-gui project, maintained by Pratyush Yadav:
+- `git-gui/` comes from git-gui project, maintained by Johannes Sixt:
- https://github.com/prati0100/git-gui.git
+ https://github.com/j6t/git-gui
- `gitk-git/` comes from Paul Mackerras's gitk project:
@@ -548,54 +709,12 @@ repositories.
Patches to these parts should be based on their trees.
-[[patch-flow]]
-== An ideal patch flow
-
-Here is an ideal patch flow for this project the current maintainer
-suggests to the contributors:
-
-. You come up with an itch. You code it up.
-
-. Send it to the list and cc people who may need to know about
- the change.
-+
-The people who may need to know are the ones whose code you
-are butchering. These people happen to be the ones who are
-most likely to be knowledgeable enough to help you, but
-they have no obligation to help you (i.e. you ask for help,
-don't demand). +git log -p {litdd} _$area_you_are_modifying_+ would
-help you find out who they are.
-
-. You get comments and suggestions for improvements. You may
- even get them in an "on top of your change" patch form.
-
-. Polish, refine, and re-send to the list and the people who
- spend their time to improve your patch. Go back to step (2).
-
-. The list forms consensus that the last round of your patch is
- good. Send it to the maintainer and cc the list.
-
-. A topic branch is created with the patch and is merged to `next`,
- and cooked further and eventually graduates to `master`.
-
-In any time between the (2)-(3) cycle, the maintainer may pick it up
-from the list and queue it to `seen`, in order to make it easier for
-people to play with it without having to pick up and apply the patch to
-their trees themselves.
-
-[[patch-status]]
-== Know the status of your patch after submission
+- The "Git documentation translations" project, led by Jean-Noël
+ Avila, translates our documentation pages. Their work products are
+ maintained separately from this project, not as part of our tree:
-* You can use Git itself to find out when your patch is merged in
- master. `git pull --rebase` will automatically skip already-applied
- patches, and will let you know. This works only if you rebase on top
- of the branch in which your patch has been merged (i.e. it will not
- tell you if your patch is merged in `seen` if you rebase on top of
- master).
+ https://github.com/jnavila/git-manpages-l10n/
-* Read the Git mailing list, the maintainer regularly posts messages
- entitled "What's cooking in git.git" giving
- the status of various proposed changes.
== GitHub CI[[GHCI]]
diff --git a/Documentation/config.txt b/Documentation/config.txt
index 70b448b132..8c0b3ed807 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -316,7 +316,8 @@ terminals, this is usually not the same as setting to "white black".
Colors may also be given as numbers between 0 and 255; these use ANSI
256-color mode (but note that not all terminals may support this). If
your terminal supports it, you may also specify 24-bit RGB values as
-hex, like `#ff0ab3`.
+hex, like `#ff0ab3`, or 12-bit RGB values like `#f1b`, which is
+equivalent to the 24-bit color `#ff11bb`.
+
The accepted attributes are `bold`, `dim`, `ul`, `blink`, `reverse`,
`italic`, and `strike` (for crossed-out or "strikethrough" letters).
@@ -383,6 +384,8 @@ include::config/apply.txt[]
include::config/attr.txt[]
+include::config/bitmap-pseudo-merge.txt[]
+
include::config/blame.txt[]
include::config/branch.txt[]
@@ -487,6 +490,8 @@ include::config/pager.txt[]
include::config/pretty.txt[]
+include::config/promisor.txt[]
+
include::config/protocol.txt[]
include::config/pull.txt[]
@@ -497,6 +502,8 @@ include::config/rebase.txt[]
include::config/receive.txt[]
+include::config/reftable.txt[]
+
include::config/remote.txt[]
include::config/remotes.txt[]
diff --git a/Documentation/config/add.txt b/Documentation/config/add.txt
index e0354ceaed..4d753f006e 100644
--- a/Documentation/config/add.txt
+++ b/Documentation/config/add.txt
@@ -5,9 +5,3 @@ add.ignore-errors (deprecated)::
option of linkgit:git-add[1]. `add.ignore-errors` is deprecated,
as it does not follow the usual naming convention for configuration
variables.
-
-add.interactive.useBuiltin::
- Unused configuration variable. Used in Git versions v2.25.0 to
- v2.36.0 to enable the built-in version of linkgit:git-add[1]'s
- interactive mode, which then became the default in Git
- versions v2.37.0 to v2.39.0.
diff --git a/Documentation/config/advice.txt b/Documentation/config/advice.txt
index 0e35ae5240..0ba8989820 100644
--- a/Documentation/config/advice.txt
+++ b/Documentation/config/advice.txt
@@ -96,6 +96,8 @@ advice.*::
`pushNonFFCurrent`, `pushNonFFMatching`, `pushAlreadyExists`,
`pushFetchFirst`, `pushNeedsForce`, and `pushRefNeedsUpdate`
simultaneously.
+ rebaseTodoError::
+ Shown when there is an error after editing the rebase todo list.
refSyntax::
Shown when the user provides an illegal ref name, to
tell the user about the ref syntax documentation.
@@ -114,6 +116,10 @@ advice.*::
skippedCherryPicks::
Shown when linkgit:git-rebase[1] skips a commit that has already
been cherry-picked onto the upstream branch.
+ sparseIndexExpanded::
+ Shown when a sparse index is expanded to a full index, which is likely
+ due to an unexpected set of files existing outside of the
+ sparse-checkout.
statusAheadBehind::
Shown when linkgit:git-status[1] computes the ahead/behind
counts for a local ref compared to its remote tracking ref,
diff --git a/Documentation/config/alias.txt b/Documentation/config/alias.txt
index 01df96fab3..2c5db0ad84 100644
--- a/Documentation/config/alias.txt
+++ b/Documentation/config/alias.txt
@@ -21,8 +21,23 @@ If the alias expansion is prefixed with an exclamation point,
it will be treated as a shell command. For example, defining
`alias.new = !gitk --all --not ORIG_HEAD`, the invocation
`git new` is equivalent to running the shell command
-`gitk --all --not ORIG_HEAD`. Note that shell commands will be
-executed from the top-level directory of a repository, which may
-not necessarily be the current directory.
-`GIT_PREFIX` is set as returned by running `git rev-parse --show-prefix`
-from the original current directory. See linkgit:git-rev-parse[1].
+`gitk --all --not ORIG_HEAD`. Note:
++
+* Shell commands will be executed from the top-level directory of a
+ repository, which may not necessarily be the current directory.
+* `GIT_PREFIX` is set as returned by running `git rev-parse --show-prefix`
+ from the original current directory. See linkgit:git-rev-parse[1].
+* Shell command aliases always receive any extra arguments provided to
+ the Git command-line as positional arguments.
+** Care should be taken if your shell alias is a "one-liner" script
+ with multiple commands (e.g. in a pipeline), references multiple
+ arguments, or is otherwise not able to handle positional arguments
+ added at the end. For example: `alias.cmd = "!echo $1 | grep $2"`
+ called as `git cmd 1 2` will be executed as 'echo $1 | grep $2
+ 1 2', which is not what you want.
+** A convenient way to deal with this is to write your script
+ operations in an inline function that is then called with any
+ arguments from the command-line. For example `alias.cmd = "!c() {
+ echo $1 | grep $2 ; }; c" will correctly execute the prior example.
+** Setting `GIT_TRACE=1` can help you debug the command being run for
+ your alias.
diff --git a/Documentation/config/attr.txt b/Documentation/config/attr.txt
index 1a482d6af2..c4a5857993 100644
--- a/Documentation/config/attr.txt
+++ b/Documentation/config/attr.txt
@@ -1,7 +1,6 @@
attr.tree::
A reference to a tree in the repository from which to read attributes,
- instead of the `.gitattributes` file in the working tree. In a bare
- repository, this defaults to `HEAD:.gitattributes`. If the value does
- not resolve to a valid tree object, an empty tree is used instead.
+ instead of the `.gitattributes` file in the working tree. If the value
+ does not resolve to a valid tree object, an empty tree is used instead.
When the `GIT_ATTR_SOURCE` environment variable or `--attr-source`
command line option are used, this configuration variable has no effect.
diff --git a/Documentation/config/bitmap-pseudo-merge.txt b/Documentation/config/bitmap-pseudo-merge.txt
new file mode 100644
index 0000000000..1f264eca99
--- /dev/null
+++ b/Documentation/config/bitmap-pseudo-merge.txt
@@ -0,0 +1,91 @@
+NOTE: The configuration options in `bitmapPseudoMerge.*` are considered
+EXPERIMENTAL and may be subject to change or be removed entirely in the
+future. For more information about the pseudo-merge bitmap feature, see
+the "Pseudo-merge bitmaps" section of linkgit:gitpacking[7].
+
+bitmapPseudoMerge.<name>.pattern::
+ Regular expression used to match reference names. Commits
+ pointed to by references matching this pattern (and meeting
+ the below criteria, like `bitmapPseudoMerge.<name>.sampleRate`
+ and `bitmapPseudoMerge.<name>.threshold`) will be considered
+ for inclusion in a pseudo-merge bitmap.
++
+Commits are grouped into pseudo-merge groups based on whether or not
+any reference(s) that point at a given commit match the pattern, which
+is an extended regular expression.
++
+Within a pseudo-merge group, commits may be further grouped into
+sub-groups based on the capture groups in the pattern. These
+sub-groupings are formed from the regular expressions by concatenating
+any capture groups from the regular expression, with a '-' dash in
+between.
++
+For example, if the pattern is `refs/tags/`, then all tags (provided
+they meet the below criteria) will be considered candidates for the
+same pseudo-merge group. However, if the pattern is instead
+`refs/remotes/([0-9])+/tags/`, then tags from different remotes will
+be grouped into separate pseudo-merge groups, based on the remote
+number.
+
+bitmapPseudoMerge.<name>.decay::
+ Determines the rate at which consecutive pseudo-merge bitmap
+ groups decrease in size. Must be non-negative. This parameter
+ can be thought of as `k` in the function `f(n) = C * n^-k`,
+ where `f(n)` is the size of the `n`th group.
++
+Setting the decay rate equal to `0` will cause all groups to be the
+same size. Setting the decay rate equal to `1` will cause the `n`th
+group to be `1/n` the size of the initial group. Higher values of the
+decay rate cause consecutive groups to shrink at an increasing rate.
+The default is `1`.
++
+If all groups are the same size, it is possible that groups containing
+newer commits will be able to be used less often than earlier groups,
+since it is more likely that the references pointing at newer commits
+will be updated more often than a reference pointing at an old commit.
+
+bitmapPseudoMerge.<name>.sampleRate::
+ Determines the proportion of non-bitmapped commits (among
+ reference tips) which are selected for inclusion in an
+ unstable pseudo-merge bitmap. Must be between `0` and `1`
+ (inclusive). The default is `1`.
+
+bitmapPseudoMerge.<name>.threshold::
+ Determines the minimum age of non-bitmapped commits (among
+ reference tips, as above) which are candidates for inclusion
+ in an unstable pseudo-merge bitmap. The default is
+ `1.week.ago`.
+
+bitmapPseudoMerge.<name>.maxMerges::
+ Determines the maximum number of pseudo-merge commits among
+ which commits may be distributed.
++
+For pseudo-merge groups whose pattern does not contain any capture
+groups, this setting is applied for all commits matching the regular
+expression. For patterns that have one or more capture groups, this
+setting is applied for each distinct capture group.
++
+For example, if your capture group is `refs/tags/`, then this setting
+will distribute all tags into a maximum of `maxMerges` pseudo-merge
+commits. However, if your capture group is, say,
+`refs/remotes/([0-9]+)/tags/`, then this setting will be applied to
+each remote's set of tags individually.
++
+Must be non-negative. The default value is 64.
+
+bitmapPseudoMerge.<name>.stableThreshold::
+ Determines the minimum age of commits (among reference tips,
+ as above, however stable commits are still considered
+ candidates even when they have been covered by a bitmap) which
+ are candidates for a stable a pseudo-merge bitmap. The default
+ is `1.month.ago`.
++
+Setting this threshold to a smaller value (e.g., 1.week.ago) will cause
+more stable groups to be generated (which impose a one-time generation
+cost) but those groups will likely become stale over time. Using a
+larger value incurs the opposite penalty (fewer stable groups which are
+more useful).
+
+bitmapPseudoMerge.<name>.stableSize::
+ Determines the size (in number of commits) of a stable
+ psuedo-merge bitmap. The default is `512`.
diff --git a/Documentation/config/commitgraph.txt b/Documentation/config/commitgraph.txt
index 30604e4a4c..7f8c9d6638 100644
--- a/Documentation/config/commitgraph.txt
+++ b/Documentation/config/commitgraph.txt
@@ -9,6 +9,29 @@ commitGraph.maxNewFilters::
commit-graph write` (c.f., linkgit:git-commit-graph[1]).
commitGraph.readChangedPaths::
- If true, then git will use the changed-path Bloom filters in the
- commit-graph file (if it exists, and they are present). Defaults to
- true. See linkgit:git-commit-graph[1] for more information.
+ Deprecated. Equivalent to commitGraph.changedPathsVersion=-1 if true, and
+ commitGraph.changedPathsVersion=0 if false. (If commitGraph.changedPathVersion
+ is also set, commitGraph.changedPathsVersion takes precedence.)
+
+commitGraph.changedPathsVersion::
+ Specifies the version of the changed-path Bloom filters that Git will read and
+ write. May be -1, 0, 1, or 2. Note that values greater than 1 may be
+ incompatible with older versions of Git which do not yet understand
+ those versions. Use caution when operating in a mixed-version
+ environment.
++
+Defaults to -1.
++
+If -1, Git will use the version of the changed-path Bloom filters in the
+repository, defaulting to 1 if there are none.
++
+If 0, Git will not read any Bloom filters, and will write version 1 Bloom
+filters when instructed to write.
++
+If 1, Git will only read version 1 Bloom filters, and will write version 1
+Bloom filters.
++
+If 2, Git will only read version 2 Bloom filters, and will write version 2
+Bloom filters.
++
+See linkgit:git-commit-graph[1] for more information.
diff --git a/Documentation/config/core.txt b/Documentation/config/core.txt
index 93d65e1dfd..60ca9f2b68 100644
--- a/Documentation/config/core.txt
+++ b/Documentation/config/core.txt
@@ -756,4 +756,5 @@ core.maxTreeDepth::
The maximum depth Git is willing to recurse while traversing a
tree (e.g., "a/b/cde/f" has a depth of 4). This is a fail-safe
to allow Git to abort cleanly, and should not generally need to
- be adjusted. The default is 4096.
+ be adjusted. When Git is compiled with MSVC, the default is 512.
+ Otherwise, the default is 2048.
diff --git a/Documentation/config/diff.txt b/Documentation/config/diff.txt
index 5ce7b91f1d..190bda17e5 100644
--- a/Documentation/config/diff.txt
+++ b/Documentation/config/diff.txt
@@ -79,6 +79,15 @@ diff.external::
you want to use an external diff program only on a subset of
your files, you might want to use linkgit:gitattributes[5] instead.
+diff.trustExitCode::
+ If this boolean value is set to true then the
+ `diff.external` command is expected to return exit code
+ 0 if it considers the input files to be equal or 1 if it
+ considers them to be different, like `diff(1)`.
+ If it is set to false, which is the default, then the command
+ is expected to return exit code 0 regardless of equality.
+ Any other exit code causes Git to report a fatal error.
+
diff.ignoreSubmodules::
Sets the default value of --ignore-submodules. Note that this
affects only 'git diff' Porcelain, and not lower level 'diff'
@@ -164,6 +173,15 @@ diff.<driver>.command::
The custom diff driver command. See linkgit:gitattributes[5]
for details.
+diff.<driver>.trustExitCode::
+ If this boolean value is set to true then the
+ `diff.<driver>.command` command is expected to return exit code
+ 0 if it considers the input files to be equal or 1 if it
+ considers them to be different, like `diff(1)`.
+ If it is set to false, which is the default, then the command
+ is expected to return exit code 0 regardless of equality.
+ Any other exit code causes Git to report a fatal error.
+
diff.<driver>.xfuncname::
The regular expression that the diff driver should use to
recognize the hunk header. A built-in pattern may also be used.
diff --git a/Documentation/config/http.txt b/Documentation/config/http.txt
index 2d4e0c9b86..162b33fc52 100644
--- a/Documentation/config/http.txt
+++ b/Documentation/config/http.txt
@@ -7,6 +7,11 @@ http.proxy::
linkgit:gitcredentials[7] for more information. The syntax thus is
'[protocol://][user[:password]@]proxyhost[:port]'. This can be overridden
on a per-remote basis; see remote.<name>.proxy
++
+Any proxy, however configured, must be completely transparent and must not
+modify, transform, or buffer the request or response in any way. Proxies which
+are not completely transparent are known to cause various forms of breakage
+with Git.
http.proxyAuthMethod::
Set the method with which to authenticate against the HTTP proxy. This
@@ -56,6 +61,26 @@ http.emptyAuth::
a username in the URL, as libcurl normally requires a username for
authentication.
+http.proactiveAuth::
+ Attempt authentication without first making an unauthenticated attempt and
+ receiving a 401 response. This can be used to ensure that all requests are
+ authenticated. If `http.emptyAuth` is set to true, this value has no effect.
++
+If the credential helper used specifies an authentication scheme (i.e., via the
+`authtype` field), that value will be used; if a username and password is
+provided without a scheme, then Basic authentication is used. The value of the
+option determines the scheme requested from the helper. Possible values are:
++
+--
+* `basic` - Request Basic authentication from the helper.
+* `auto` - Allow the helper to pick an appropriate scheme.
+* `none` - Disable proactive authentication.
+--
++
+Note that TLS should always be used with this configuration, since otherwise it
+is easy to accidentally expose plaintext credentials if Basic authentication
+is selected.
+
http.delegation::
Control GSSAPI credential delegation. The delegation is disabled
by default in libcurl since version 7.21.7. Set parameter to tell
@@ -82,12 +107,16 @@ http.cookieFile::
in the Git http session, if they match the server. The file format
of the file to read cookies from should be plain HTTP headers or
the Netscape/Mozilla cookie file format (see `curl(1)`).
+ Set it to an empty string, to accept only new cookies from
+ the server and send them back in successive requests within same
+ connection.
NOTE that the file specified with http.cookieFile is used only as
input unless http.saveCookies is set.
http.saveCookies::
If set, store cookies received during requests to the file specified by
- http.cookieFile. Has no effect if http.cookieFile is unset.
+ http.cookieFile. Has no effect if http.cookieFile is unset, or set to
+ an empty string.
http.version::
Use the specified HTTP protocol version when communicating with a server.
diff --git a/Documentation/config/interactive.txt b/Documentation/config/interactive.txt
index 5cc26555f1..8b876cb4eb 100644
--- a/Documentation/config/interactive.txt
+++ b/Documentation/config/interactive.txt
@@ -1,8 +1,8 @@
interactive.singleKey::
- In interactive commands, allow the user to provide one-letter
- input with a single key (i.e., without hitting enter).
- Currently this is used by the `--patch` mode of
- linkgit:git-add[1], linkgit:git-checkout[1],
+ When set to true, allow the user to provide one-letter input
+ with a single key (i.e., without hitting the Enter key) in
+ interactive commands. This is currently used by the `--patch`
+ mode of linkgit:git-add[1], linkgit:git-checkout[1],
linkgit:git-restore[1], linkgit:git-commit[1],
linkgit:git-reset[1], and linkgit:git-stash[1].
diff --git a/Documentation/config/promisor.txt b/Documentation/config/promisor.txt
new file mode 100644
index 0000000000..98c5cb2ec2
--- /dev/null
+++ b/Documentation/config/promisor.txt
@@ -0,0 +1,3 @@
+promisor.quiet::
+ If set to "true" assume `--quiet` when fetching additional
+ objects for a partial clone.
diff --git a/Documentation/config/reftable.txt b/Documentation/config/reftable.txt
new file mode 100644
index 0000000000..0515727977
--- /dev/null
+++ b/Documentation/config/reftable.txt
@@ -0,0 +1,48 @@
+reftable.blockSize::
+ The size in bytes used by the reftable backend when writing blocks.
+ The block size is determined by the writer, and does not have to be a
+ power of 2. The block size must be larger than the longest reference
+ name or log entry used in the repository, as references cannot span
+ blocks.
++
+Powers of two that are friendly to the virtual memory system or
+filesystem (such as 4kB or 8kB) are recommended. Larger sizes (64kB) can
+yield better compression, with a possible increased cost incurred by
+readers during access.
++
+The largest block size is `16777215` bytes (15.99 MiB). The default value is
+`4096` bytes (4kB). A value of `0` will use the default value.
+
+reftable.restartInterval::
+ The interval at which to create restart points. The reftable backend
+ determines the restart points at file creation. Every 16 may be
+ more suitable for smaller block sizes (4k or 8k), every 64 for larger
+ block sizes (64k).
++
+More frequent restart points reduces prefix compression and increases
+space consumed by the restart table, both of which increase file size.
++
+Less frequent restart points makes prefix compression more effective,
+decreasing overall file size, with increased penalties for readers
+walking through more records after the binary search step.
++
+A maximum of `65535` restart points per block is supported.
++
+The default value is to create restart points every 16 records. A value of `0`
+will use the default value.
+
+reftable.indexObjects::
+ Whether the reftable backend shall write object blocks. Object blocks
+ are a reverse mapping of object ID to the references pointing to them.
++
+The default value is `true`.
+
+reftable.geometricFactor::
+ Whenever the reftable backend appends a new table to the stack, it
+ performs auto compaction to ensure that there is only a handful of
+ tables. The backend does this by ensuring that tables form a geometric
+ sequence regarding the respective sizes of each table.
++
+By default, the geometric sequence uses a factor of 2, meaning that for any
+table, the next-biggest table must at least be twice as big. A maximum factor
+of 256 is supported.
diff --git a/Documentation/config/remote.txt b/Documentation/config/remote.txt
index 0678b4bcfe..8efc53e836 100644
--- a/Documentation/config/remote.txt
+++ b/Documentation/config/remote.txt
@@ -5,10 +5,19 @@ remote.pushDefault::
remote.<name>.url::
The URL of a remote repository. See linkgit:git-fetch[1] or
- linkgit:git-push[1].
+ linkgit:git-push[1]. A configured remote can have multiple URLs;
+ in this case the first is used for fetching, and all are used
+ for pushing (assuming no `remote.<name>.pushurl` is defined).
+ Setting this key to the empty string clears the list of urls,
+ allowing you to override earlier config.
remote.<name>.pushurl::
The push URL of a remote repository. See linkgit:git-push[1].
+ If a `pushurl` option is present in a configured remote, it
+ is used for pushing instead of `remote.<name>.url`. A configured
+ remote can have multiple push URLs; in this case a push goes to
+ all of them. Setting this key to the empty string clears the
+ list of urls, allowing you to override earlier config.
remote.<name>.proxy::
For remotes that require curl (http, https and ftp), the URL to
diff --git a/Documentation/config/safe.txt b/Documentation/config/safe.txt
index 577df40223..2d45c98b12 100644
--- a/Documentation/config/safe.txt
+++ b/Documentation/config/safe.txt
@@ -44,7 +44,8 @@ string `*`. This will allow all repositories to be treated as if their
directory was listed in the `safe.directory` list. If `safe.directory=*`
is set in system config and you want to re-enable this protection, then
initialize your list with an empty value before listing the repositories
-that you deem safe.
+that you deem safe. Giving a directory with `/*` appended to it will
+allow access to all repositories under the named directory.
+
As explained, Git only allows you to access repositories owned by
yourself, i.e. the user who is running Git, by default. When Git
diff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt
index 0e9456957e..cd0b81adbb 100644
--- a/Documentation/diff-options.txt
+++ b/Documentation/diff-options.txt
@@ -329,12 +329,13 @@ explained for the configuration variable `core.quotePath` (see
linkgit:git-config[1]).
--name-only::
- Show only names of changed files. The file names are often encoded in UTF-8.
+ Show only the name of each changed file in the post-image tree.
+ The file names are often encoded in UTF-8.
For more information see the discussion about encoding in the linkgit:git-log[1]
manual page.
--name-status::
- Show only names and status of changed files. See the description
+ Show only the name(s) and status of each changed file. See the description
of the `--diff-filter` option on what the status letters mean.
Just like `--name-only` the file names are often encoded in UTF-8.
@@ -819,6 +820,11 @@ ifndef::git-log[]
--quiet::
Disable all output of the program. Implies `--exit-code`.
+ Disables execution of external diff helpers whose exit code
+ is not trusted, i.e. their respective configuration option
+ `diff.trustExitCode` or `diff.<driver>.trustExitCode` or
+ environment variable `GIT_EXTERNAL_DIFF_TRUST_EXIT_CODE` is
+ false.
endif::git-log[]
endif::git-format-patch[]
diff --git a/Documentation/git-am.txt b/Documentation/git-am.txt
index 624a6e6fe4..69d5cc9f21 100644
--- a/Documentation/git-am.txt
+++ b/Documentation/git-am.txt
@@ -18,7 +18,7 @@ SYNOPSIS
[--quoted-cr=<action>]
[--empty=(stop|drop|keep)]
[(<mbox> | <Maildir>)...]
-'git am' (--continue | --skip | --abort | --quit | --show-current-patch[=(diff|raw)] | --allow-empty)
+'git am' (--continue | --skip | --abort | --quit | --retry | --show-current-patch[=(diff|raw)] | --allow-empty)
DESCRIPTION
-----------
@@ -208,6 +208,12 @@ Valid <action> for the `--whitespace` option are:
Abort the patching operation but keep HEAD and the index
untouched.
+--retry::
+ Try to apply the last conflicting patch again. This is generally
+ only useful for passing extra options to the retry attempt
+ (e.g., `--3way`), since otherwise you'll just see the same
+ failure again.
+
--show-current-patch[=(diff|raw)]::
Show the message at which `git am` has stopped due to
conflicts. If `raw` is specified, show the raw contents of
diff --git a/Documentation/git-archive.txt b/Documentation/git-archive.txt
index 98526f2beb..a0e3fe7996 100644
--- a/Documentation/git-archive.txt
+++ b/Documentation/git-archive.txt
@@ -53,7 +53,7 @@ OPTIONS
--prefix=<prefix>/::
Prepend <prefix>/ to paths in the archive. Can be repeated; its
rightmost value is used for all tracked files. See below which
- value gets used by `--add-file` and `--add-virtual-file`.
+ value gets used by `--add-file`.
-o <file>::
--output=<file>::
@@ -67,9 +67,7 @@ OPTIONS
--add-virtual-file=<path>:<content>::
Add the specified contents to the archive. Can be repeated to add
- multiple files. The path of the file in the archive is built
- by concatenating the value of the last `--prefix` option (if any)
- before this `--add-virtual-file` and `<path>`.
+ multiple files.
+
The `<path>` argument can start and end with a literal double-quote
character; the contained file name is interpreted as a C-style string,
@@ -81,6 +79,10 @@ if the path begins or ends with a double-quote character.
The file mode is limited to a regular file, and the option may be
subject to platform-dependent command-line limits. For non-trivial
cases, write an untracked file and use `--add-file` instead.
++
+Note that unlike `--add-file` the path created in the archive is not
+affected by the `--prefix` option, as a full `<path>` can be given as
+the value of the option.
--worktree-attributes::
Look for attributes in .gitattributes files in the working tree
diff --git a/Documentation/git-config.txt b/Documentation/git-config.txt
index ac61113fcc..65c645d461 100644
--- a/Documentation/git-config.txt
+++ b/Documentation/git-config.txt
@@ -9,21 +9,14 @@ git-config - Get and set repository or global options
SYNOPSIS
--------
[verse]
-'git config' [<file-option>] [--type=<type>] [--comment=<message>] [--fixed-value] [--show-origin] [--show-scope] [-z|--null] <name> [<value> [<value-pattern>]]
-'git config' [<file-option>] [--type=<type>] [--comment=<message>] --add <name> <value>
-'git config' [<file-option>] [--type=<type>] [--comment=<message>] [--fixed-value] --replace-all <name> <value> [<value-pattern>]
-'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] [--fixed-value] --get <name> [<value-pattern>]
-'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] [--fixed-value] --get-all <name> [<value-pattern>]
-'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] [--fixed-value] [--name-only] --get-regexp <name-regex> [<value-pattern>]
-'git config' [<file-option>] [--type=<type>] [-z|--null] --get-urlmatch <name> <URL>
-'git config' [<file-option>] [--fixed-value] --unset <name> [<value-pattern>]
-'git config' [<file-option>] [--fixed-value] --unset-all <name> [<value-pattern>]
-'git config' [<file-option>] --rename-section <old-name> <new-name>
-'git config' [<file-option>] --remove-section <name>
-'git config' [<file-option>] [--show-origin] [--show-scope] [-z|--null] [--name-only] -l | --list
-'git config' [<file-option>] --get-color <name> [<default>]
+'git config list' [<file-option>] [<display-option>] [--includes]
+'git config get' [<file-option>] [<display-option>] [--includes] [--all] [--regexp=<regexp>] [--value=<value>] [--fixed-value] [--default=<default>] <name>
+'git config set' [<file-option>] [--type=<type>] [--all] [--value=<value>] [--fixed-value] <name> <value>
+'git config unset' [<file-option>] [--all] [--value=<value>] [--fixed-value] <name> <value>
+'git config rename-section' [<file-option>] <old-name> <new-name>
+'git config remove-section' [<file-option>] <name>
+'git config edit' [<file-option>]
'git config' [<file-option>] --get-colorbool <name> [<stdout-is-tty>]
-'git config' [<file-option>] -e | --edit
DESCRIPTION
-----------
@@ -31,7 +24,7 @@ You can query/set/replace/unset options with this command. The name is
actually the section and the key separated by a dot, and the value will be
escaped.
-Multiple lines can be added to an option by using the `--add` option.
+Multiple lines can be added to an option by using the `--append` option.
If you want to update or unset an option which can occur on multiple
lines, a `value-pattern` (which is an extended regular expression,
unless the `--fixed-value` option is given) needs to be given. Only the
@@ -74,6 +67,42 @@ On success, the command returns the exit code 0.
A list of all available configuration variables can be obtained using the
`git help --config` command.
+COMMANDS
+--------
+
+list::
+ List all variables set in config file, along with their values.
+
+get::
+ Emits the value of the specified key. If key is present multiple times
+ in the configuration, emits the last value. If `--all` is specified,
+ emits all values associated with key. Returns error code 1 if key is
+ not present.
+
+set::
+ Set value for one or more config options. By default, this command
+ refuses to write multi-valued config options. Passing `--all` will
+ replace all multi-valued config options with the new value, whereas
+ `--value=` will replace all config options whose values match the given
+ pattern.
+
+unset::
+ Unset value for one or more config options. By default, this command
+ refuses to unset multi-valued keys. Passing `--all` will unset all
+ multi-valued config options, whereas `--value` will unset all config
+ options whose values match the given pattern.
+
+rename-section::
+ Rename the given section to a new name.
+
+remove-section::
+ Remove the given section from the configuration file.
+
+edit::
+ Opens an editor to modify the specified config file; either
+ `--system`, `--global`, `--local` (default), `--worktree`, or
+ `--file <config-file>`.
+
[[OPTIONS]]
OPTIONS
-------
@@ -82,10 +111,9 @@ OPTIONS
Default behavior is to replace at most one line. This replaces
all lines matching the key (and optionally the `value-pattern`).
---add::
+--append::
Adds a new line to the option without altering any existing
- values. This is the same as providing '^$' as the `value-pattern`
- in `--replace-all`.
+ values. This is the same as providing '--value=^$' in `set`.
--comment <message>::
Append a comment at the end of new or modified lines.
@@ -99,22 +127,16 @@ OPTIONS
not contain linefeed characters (no multi-line comments are
permitted).
---get::
- Get the value for a given key (optionally filtered by a regex
- matching the value). Returns error code 1 if the key was not
- found and the last value if multiple key values were found.
+--all::
+ With `get`, return all values for a multi-valued key.
---get-all::
- Like get, but returns all values for a multi-valued key.
+---regexp::
+ With `get`, interpret the name as a regular expression. Regular
+ expression matching is currently case-sensitive and done against a
+ canonicalized version of the key in which section and variable names
+ are lowercased, but subsection names are not.
---get-regexp::
- Like --get-all, but interprets the name as a regular expression and
- writes out the key names. Regular expression matching is currently
- case-sensitive and done against a canonicalized version of the key
- in which section and variable names are lowercased, but subsection
- names are not.
-
---get-urlmatch <name> <URL>::
+--url=<URL>::
When given a two-part <name> as <section>.<key>, the value for
<section>.<URL>.<key> whose <URL> part matches the best to the
given URL is returned (if no such key exists, the value for
@@ -178,22 +200,6 @@ See also <<FILES>>.
section in linkgit:gitrevisions[7] for a more complete list of
ways to spell blob names.
---remove-section::
- Remove the given section from the configuration file.
-
---rename-section::
- Rename the given section to a new name.
-
---unset::
- Remove the line matching the key from config file.
-
---unset-all::
- Remove all lines matching the key from config file.
-
--l::
---list::
- List all variables set in config file, along with their values.
-
--fixed-value::
When used with the `value-pattern` argument, treat `value-pattern` as
an exact string instead of a regular expression. This will restrict
@@ -248,8 +254,8 @@ Valid `<type>`'s include:
contain line breaks.
--name-only::
- Output only the names of config variables for `--list` or
- `--get-regexp`.
+ Output only the names of config variables for `list` or
+ `get`.
--show-origin::
Augment the output of all queried config options with the
@@ -273,23 +279,6 @@ Valid `<type>`'s include:
When the color setting for `name` is undefined, the command uses
`color.ui` as fallback.
---get-color <name> [<default>]::
-
- Find the color configured for `name` (e.g. `color.diff.new`) and
- output it as the ANSI color escape sequence to the standard
- output. The optional `default` parameter is used instead, if
- there is no color configured for `name`.
-+
-`--type=color [--default=<default>]` is preferred over `--get-color`
-(but note that `--get-color` will omit the trailing newline printed by
-`--type=color`).
-
--e::
---edit::
- Opens an editor to modify the specified config file; either
- `--system`, `--global`, `--local` (default), `--worktree`, or
- `--file <config-file>`.
-
--[no-]includes::
Respect `include.*` directives in config files when looking up
values. Defaults to `off` when a specific file is given (e.g.,
@@ -297,14 +286,64 @@ Valid `<type>`'s include:
config files.
--default <value>::
- When using `--get`, and the requested variable is not found, behave as if
+ When using `get`, and the requested variable is not found, behave as if
<value> were the value assigned to that variable.
+DEPRECATED MODES
+----------------
+
+The following modes have been deprecated in favor of subcommands. It is
+recommended to migrate to the new syntax.
+
+'git config <name>'::
+ Replaced by `git config get <name>`.
+
+'git config <name> <value> [<value-pattern>]'::
+ Replaced by `git config set [--value=<pattern>] <name> <value>`.
+
+-l::
+--list::
+ Replaced by `git config list`.
+
+--get <name> [<value-pattern>]::
+ Replaced by `git config get [--value=<pattern>] <name>`.
+
+--get-all <name> [<value-pattern>]::
+ Replaced by `git config get [--value=<pattern>] --all --show-names <name>`.
+
+--get-regexp <name-regexp>::
+ Replaced by `git config get --all --show-names --regexp <name-regexp>`.
+
+--get-urlmatch <name> <URL>::
+ Replaced by `git config get --all --show-names --url=<URL> <name>`.
+
+--get-color <name> [<default>]::
+ Replaced by `git config get --type=color [--default=<default>] <name>`.
+
+--add <name> <value>::
+ Replaced by `git config set --append <name> <value>`.
+
+--unset <name> [<value-pattern>]::
+ Replaced by `git config unset [--value=<pattern>] <name>`.
+
+--unset-all <name> [<value-pattern>]::
+ Replaced by `git config unset [--value=<pattern>] --all <name>`.
+
+--rename-section <old-name> <new-name>::
+ Replaced by `git config rename-section <old-name> <new-name>`.
+
+--remove-section <name>::
+ Replaced by `git config remove-section <name>`.
+
+-e::
+--edit::
+ Replaced by `git config edit`.
+
CONFIGURATION
-------------
`pager.config` is only respected when listing configuration, i.e., when
-using `--list` or any of the `--get-*` which may return multiple results.
-The default is to use a pager.
+using `list` or `get` which may return multiple results. The default is to use
+a pager.
[[FILES]]
FILES
@@ -346,8 +385,8 @@ precedence over values read earlier. When multiple values are taken then all
values of a key from all files will be used.
By default, options are only written to the repository specific
-configuration file. Note that this also affects options like `--replace-all`
-and `--unset`. *'git config' will only ever change one file at a time*.
+configuration file. Note that this also affects options like `set`
+and `unset`. *'git config' will only ever change one file at a time*.
You can limit which configuration sources are read from or written to by
specifying the path of a file with the `--file` option, or by specifying a
@@ -482,7 +521,7 @@ Given a .git/config like this:
you can set the filemode to true with
------------
-% git config core.filemode true
+% git config set core.filemode true
------------
The hypothetical proxy command entries actually have a postfix to discern
@@ -490,7 +529,7 @@ what URL they apply to. Here is how to change the entry for kernel.org
to "ssh".
------------
-% git config core.gitproxy '"ssh" for kernel.org' 'for kernel.org$'
+% git config set --value='for kernel.org$' core.gitproxy '"ssh" for kernel.org'
------------
This makes sure that only the key/value pair for kernel.org is replaced.
@@ -498,7 +537,7 @@ This makes sure that only the key/value pair for kernel.org is replaced.
To delete the entry for renames, do
------------
-% git config --unset diff.renames
+% git config unset diff.renames
------------
If you want to delete an entry for a multivar (like core.gitproxy above),
@@ -507,51 +546,45 @@ you have to provide a regex matching the value of exactly one line.
To query the value for a given key, do
------------
-% git config --get core.filemode
-------------
-
-or
-
-------------
-% git config core.filemode
+% git config get core.filemode
------------
or, to query a multivar:
------------
-% git config --get core.gitproxy "for kernel.org$"
+% git config get --value="for kernel.org$" core.gitproxy
------------
If you want to know all the values for a multivar, do:
------------
-% git config --get-all core.gitproxy
+% git config get --all --show-names core.gitproxy
------------
If you like to live dangerously, you can replace *all* core.gitproxy by a
new one with
------------
-% git config --replace-all core.gitproxy ssh
+% git config set --all core.gitproxy ssh
------------
However, if you really only want to replace the line for the default proxy,
i.e. the one without a "for ..." postfix, do something like this:
------------
-% git config core.gitproxy ssh '! for '
+% git config set --value='! for ' core.gitproxy ssh
------------
To actually match only values with an exclamation mark, you have to
------------
-% git config section.key value '[!]'
+% git config set --value='[!]' section.key value
------------
To add a new proxy, without altering any of the existing ones, use
------------
-% git config --add core.gitproxy '"proxy-command" for example.com'
+% git config set --append core.gitproxy '"proxy-command" for example.com'
------------
An example to use customized color from the configuration in your
@@ -559,8 +592,8 @@ script:
------------
#!/bin/sh
-WS=$(git config --get-color color.diff.whitespace "blue reverse")
-RESET=$(git config --get-color "" "reset")
+WS=$(git config get --type=color --default="blue reverse" color.diff.whitespace)
+RESET=$(git config get --type=color --default="reset" "")
echo "${WS}your whitespace color or blue reverse${RESET}"
------------
@@ -568,11 +601,11 @@ For URLs in `https://weak.example.com`, `http.sslVerify` is set to
false, while it is set to `true` for all others:
------------
-% git config --type=bool --get-urlmatch http.sslverify https://good.example.com
+% git config get --type=bool --url=https://good.example.com http.sslverify
true
-% git config --type=bool --get-urlmatch http.sslverify https://weak.example.com
+% git config get --type=bool --url=https://weak.example.com http.sslverify
false
-% git config --get-urlmatch http https://weak.example.com
+% git config get --url=https://weak.example.com http
http.cookieFile /tmp/cookie.txt
http.sslverify false
------------
diff --git a/Documentation/git-credential.txt b/Documentation/git-credential.txt
index 918a0aa42b..e41493292f 100644
--- a/Documentation/git-credential.txt
+++ b/Documentation/git-credential.txt
@@ -8,7 +8,7 @@ git-credential - Retrieve and store user credentials
SYNOPSIS
--------
------------------
-'git credential' (fill|approve|reject)
+'git credential' (fill|approve|reject|capability)
------------------
DESCRIPTION
@@ -41,6 +41,9 @@ If the action is `reject`, git-credential will send the description to
any configured credential helpers, which may erase any stored
credentials matching the description.
+If the action is `capability`, git-credential will announce any capabilities
+it supports to standard output.
+
If the action is `approve` or `reject`, no output should be emitted.
TYPICAL USE OF GIT CREDENTIAL
@@ -111,7 +114,9 @@ attribute per line. Each attribute is specified by a key-value pair,
separated by an `=` (equals) sign, followed by a newline.
The key may contain any bytes except `=`, newline, or NUL. The value may
-contain any bytes except newline or NUL.
+contain any bytes except newline or NUL. A line, including the trailing
+newline, may not exceed 65535 bytes in order to allow implementations to
+parse efficiently.
Attributes with keys that end with C-style array brackets `[]` can have
multiple values. Each instance of a multi-valued attribute forms an
@@ -178,6 +183,61 @@ empty string.
Components which are missing from the URL (e.g., there is no
username in the example above) will be left unset.
+`authtype`::
+ This indicates that the authentication scheme in question should be used.
+ Common values for HTTP and HTTPS include `basic`, `bearer`, and `digest`,
+ although the latter is insecure and should not be used. If `credential`
+ is used, this may be set to an arbitrary string suitable for the protocol in
+ question (usually HTTP).
++
+This value should not be sent unless the appropriate capability (see below) is
+provided on input.
+
+`credential`::
+ The pre-encoded credential, suitable for the protocol in question (usually
+ HTTP). If this key is sent, `authtype` is mandatory, and `username` and
+ `password` are not used. For HTTP, Git concatenates the `authtype` value and
+ this value with a single space to determine the `Authorization` header.
++
+This value should not be sent unless the appropriate capability (see below) is
+provided on input.
+
+`ephemeral`::
+ This boolean value indicates, if true, that the value in the `credential`
+ field should not be saved by the credential helper because its usefulness is
+ limited in time. For example, an HTTP Digest `credential` value is computed
+ using a nonce and reusing it will not result in successful authentication.
+ This may also be used for situations with short duration (e.g., 24-hour)
+ credentials. The default value is false.
++
+The credential helper will still be invoked with `store` or `erase` so that it
+can determine whether the operation was successful.
++
+This value should not be sent unless the appropriate capability (see below) is
+provided on input.
+
+`state[]`::
+ This value provides an opaque state that will be passed back to this helper
+ if it is called again. Each different credential helper may specify this
+ once. The value should include a prefix unique to the credential helper and
+ should ignore values that don't match its prefix.
++
+This value should not be sent unless the appropriate capability (see below) is
+provided on input.
+
+`continue`::
+ This is a boolean value, which, if enabled, indicates that this
+ authentication is a non-final part of a multistage authentication step. This
+ is common in protocols such as NTLM and Kerberos, where two rounds of client
+ authentication are required, and setting this flag allows the credential
+ helper to implement the multistage authentication step. This flag should
+ only be sent if a further stage is required; that is, if another round of
+ authentication is expected.
++
+This value should not be sent unless the appropriate capability (see below) is
+provided on input. This attribute is 'one-way' from a credential helper to
+pass information to Git (or other programs invoking `git credential`).
+
`wwwauth[]`::
When an HTTP response is received by Git that includes one or more
@@ -189,7 +249,45 @@ attribute 'wwwauth[]', where the order of the attributes is the same as
they appear in the HTTP response. This attribute is 'one-way' from Git
to pass additional information to credential helpers.
-Unrecognised attributes are silently discarded.
+`capability[]`::
+ This signals that Git, or the helper, as appropriate, supports the capability
+ in question. This can be used to provide better, more specific data as part
+ of the protocol. A `capability[]` directive must precede any value depending
+ on it and these directives _should_ be the first item announced in the
+ protocol.
++
+There are two currently supported capabilities. The first is `authtype`, which
+indicates that the `authtype`, `credential`, and `ephemeral` values are
+understood. The second is `state`, which indicates that the `state[]` and
+`continue` values are understood.
++
+It is not obligatory to use the additional features just because the capability
+is supported, but they should not be provided without the capability.
+
+Unrecognised attributes and capabilities are silently discarded.
+
+[[CAPA-IOFMT]]
+CAPABILITY INPUT/OUTPUT FORMAT
+------------------------------
+
+For `git credential capability`, the format is slightly different. First, a
+`version 0` announcement is made to indicate the current version of the
+protocol, and then each capability is announced with a line like `capability
+authtype`. Credential helpers may also implement this format, again with the
+`capability` argument. Additional lines may be added in the future; callers
+should ignore lines which they don't understand.
+
+Because this is a new part of the credential helper protocol, older versions of
+Git, as well as some credential helpers, may not support it. If a non-zero
+exit status is received, or if the first line doesn't start with the word
+`version` and a space, callers should assume that no capabilities are supported.
+
+The intention of this format is to differentiate it from the credential output
+in an unambiguous way. It is possible to use very simple credential helpers
+(e.g., inline shell scripts) which always produce identical output. Using a
+distinct format allows users to continue to use this syntax without having to
+worry about correctly implementing capability advertisements or accidentally
+confusing callers querying for capabilities.
GIT
---
diff --git a/Documentation/git-for-each-repo.txt b/Documentation/git-for-each-repo.txt
index 94bd19da26..abe3527aac 100644
--- a/Documentation/git-for-each-repo.txt
+++ b/Documentation/git-for-each-repo.txt
@@ -42,6 +42,15 @@ These config values are loaded from system, global, and local Git config,
as available. If `git for-each-repo` is run in a directory that is not a
Git repository, then only the system and global config is used.
+--keep-going::
+ Continue with the remaining repositories if the command failed
+ on a repository. The exit code will still indicate that the
+ overall operation was not successful.
++
+Note that the exact exit code of the failing command is not passed
+through as the exit code of the `for-each-repo` command: If the command
+failed in any of the specified repositories, the overall exit code will
+be 1.
SUBPROCESS BEHAVIOR
-------------------
diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt
index 728bb3821c..8708b31593 100644
--- a/Documentation/git-format-patch.txt
+++ b/Documentation/git-format-patch.txt
@@ -20,7 +20,7 @@ SYNOPSIS
[--in-reply-to=<message-id>] [--suffix=.<sfx>]
[--ignore-if-in-upstream] [--always]
[--cover-from-description=<mode>]
- [--rfc] [--subject-prefix=<subject-prefix>]
+ [--rfc[=<rfc>]] [--subject-prefix=<subject-prefix>]
[(--reroll-count|-v) <n>]
[--to=<email>] [--cc=<email>]
[--[no-]cover-letter] [--quiet]
@@ -238,10 +238,21 @@ the patches (with a value of e.g. "PATCH my-project").
value of the `format.filenameMaxLength` configuration
variable, or 64 if unconfigured.
---rfc::
- Prepends "RFC" to the subject prefix (producing "RFC PATCH" by
- default). RFC means "Request For Comments"; use this when sending
- an experimental patch for discussion rather than application.
+--rfc[=<rfc>]::
+ Prepends the string _<rfc>_ (defaults to "RFC") to
+ the subject prefix. As the subject prefix defaults to
+ "PATCH", you'll get "RFC PATCH" by default.
++
+RFC means "Request For Comments"; use this when sending
+an experimental patch for discussion rather than application.
+"--rfc=WIP" may also be a useful way to indicate that a patch
+is not complete yet ("WIP" stands for "Work In Progress").
++
+If the convention of the receiving community for a particular extra
+string is to have it _after_ the subject prefix, the string _<rfc>_
+can be prefixed with a dash ("`-`") to signal that the the rest of
+the _<rfc>_ string should be appended to the subject prefix instead,
+e.g., `--rfc='-(WIP)'` results in "PATCH (WIP)".
-v <n>::
--reroll-count=<n>::
@@ -346,6 +357,11 @@ material (this may change in the future).
between the previous and current series of patches by adjusting the
creation/deletion cost fudge factor. See linkgit:git-range-diff[1])
for details.
++
+Defaults to 999 (the linkgit:git-range-diff[1] uses 60), as the use
+case is to show comparison with an older iteration of the same
+topic and the tool should find more correspondence between the two
+sets of patches.
--notes[=<ref>]::
--no-notes::
diff --git a/Documentation/git-gui.txt b/Documentation/git-gui.txt
index e8f3ccb433..f5b02ef114 100644
--- a/Documentation/git-gui.txt
+++ b/Documentation/git-gui.txt
@@ -114,7 +114,7 @@ of end users.
The official repository of the 'git gui' project can be found at:
- https://github.com/prati0100/git-gui.git/
+ https://github.com/j6t/git-gui
GIT
---
diff --git a/Documentation/git-ls-remote.txt b/Documentation/git-ls-remote.txt
index 1c4f696ab5..76c86c3ce4 100644
--- a/Documentation/git-ls-remote.txt
+++ b/Documentation/git-ls-remote.txt
@@ -9,7 +9,7 @@ git-ls-remote - List references in a remote repository
SYNOPSIS
--------
[verse]
-'git ls-remote' [--heads] [--tags] [--refs] [--upload-pack=<exec>]
+'git ls-remote' [--branches] [--tags] [--refs] [--upload-pack=<exec>]
[-q | --quiet] [--exit-code] [--get-url] [--sort=<key>]
[--symref] [<repository> [<patterns>...]]
@@ -21,14 +21,16 @@ commit IDs.
OPTIONS
-------
--h::
---heads::
+-b::
+--branches::
-t::
--tags::
- Limit to only refs/heads and refs/tags, respectively.
+ Limit to only local branches and local tags, respectively.
These options are _not_ mutually exclusive; when given
both, references stored in refs/heads and refs/tags are
- displayed. Note that `git ls-remote -h` used without
+ displayed. Note that `--heads` and `-h` are deprecated
+ synonyms for `--branches` and `-b` and may be removed in
+ the future. Also note that `git ls-remote -h` used without
anything else on the command line gives help, consistent
with other git subcommands.
diff --git a/Documentation/git-merge-tree.txt b/Documentation/git-merge-tree.txt
index dd388fa21d..84cb2edf6d 100644
--- a/Documentation/git-merge-tree.txt
+++ b/Documentation/git-merge-tree.txt
@@ -72,6 +72,11 @@ OPTIONS
As the merge-base is provided directly, <branch1> and <branch2> do not need
to specify commits; trees are enough.
+-X<option>::
+--strategy-option=<option>::
+ Pass the merge strategy-specific option through to the merge strategy.
+ See linkgit:git-merge[1] for details.
+
[[OUTPUT]]
OUTPUT
------
diff --git a/Documentation/git-refs.txt b/Documentation/git-refs.txt
new file mode 100644
index 0000000000..5b99e04385
--- /dev/null
+++ b/Documentation/git-refs.txt
@@ -0,0 +1,61 @@
+git-refs(1)
+===========
+
+NAME
+----
+git-refs - Low-level access to refs
+
+
+SYNOPSIS
+--------
+[verse]
+'git refs migrate' --ref-format=<format> [--dry-run]
+
+DESCRIPTION
+-----------
+
+This command provides low-level access to refs.
+
+COMMANDS
+--------
+
+migrate::
+ Migrate ref store between different formats.
+
+OPTIONS
+-------
+
+The following options are specific to 'git refs migrate':
+
+--ref-format=<format>::
+ The ref format to migrate the ref store to. Can be one of:
++
+include::ref-storage-format.txt[]
+
+--dry-run::
+ Perform the migration, but do not modify the repository. The migrated
+ refs will be written into a separate directory that can be inspected
+ separately. The name of the directory will be reported on stdout. This
+ can be used to double check that the migration works as expected before
+ performing the actual migration.
+
+KNOWN LIMITATIONS
+-----------------
+
+The ref format migration has several known limitations in its current form:
+
+* It is not possible to migrate repositories that have reflogs.
+
+* It is not possible to migrate repositories that have worktrees.
+
+* There is no way to block concurrent writes to the repository during an
+ ongoing migration. Concurrent writes can lead to an inconsistent migrated
+ state. Users are expected to block writes on a higher level. If your
+ repository is registered for scheduled maintenance, it is recommended to
+ unregister it first with git-maintenance(1).
+
+These limitations may eventually be lifted.
+
+GIT
+---
+Part of the linkgit:git[1] suite
diff --git a/Documentation/git-rev-parse.txt b/Documentation/git-rev-parse.txt
index f9d5a35fa0..dc12d38534 100644
--- a/Documentation/git-rev-parse.txt
+++ b/Documentation/git-rev-parse.txt
@@ -18,8 +18,15 @@ Many Git porcelainish commands take a mixture of flags
(i.e. parameters that begin with a dash '-') and parameters
meant for the underlying 'git rev-list' command they use internally
and flags and parameters for the other commands they use
-downstream of 'git rev-list'. This command is used to
-distinguish between them.
+downstream of 'git rev-list'. The primary purpose of this command
+is to allow calling programs to distinguish between them. There are
+a few other operation modes that have nothing to do with the above
+"help parse command line options".
+
+Unless otherwise specified, most of the options and operation modes
+require you to run this command inside a git repository or a working
+tree that is under the control of a git repository, and will give you
+a fatal error otherwise.
OPTIONS
@@ -32,11 +39,15 @@ Each of these options must appear first on the command line.
--parseopt::
Use 'git rev-parse' in option parsing mode (see PARSEOPT section below).
+ The command in this mode can be used outside a repository or
+ a working tree controlled by a repository.
--sq-quote::
Use 'git rev-parse' in shell quoting mode (see SQ-QUOTE
section below). In contrast to the `--sq` option below, this
mode only does quoting. Nothing else is done to command input.
+ The command in this mode can be used outside a repository or
+ a working tree controlled by a repository.
Options for --parseopt
~~~~~~~~~~~~~~~~~~~~~~
diff --git a/Documentation/git-show-branch.txt b/Documentation/git-show-branch.txt
index c771c89770..bc31d8b6d3 100644
--- a/Documentation/git-show-branch.txt
+++ b/Documentation/git-show-branch.txt
@@ -22,7 +22,7 @@ Shows the commit ancestry graph starting from the commits named
with <rev>s or <glob>s (or all refs under refs/heads
and/or refs/tags) semi-visually.
-It cannot show more than 29 branches and commits at a time.
+It cannot show more than 26 branches and commits at a time.
It uses `showbranch.default` multi-valued configuration items if
no <rev> or <glob> is given on the command line.
diff --git a/Documentation/git-show-ref.txt b/Documentation/git-show-ref.txt
index ba75747005..616d919655 100644
--- a/Documentation/git-show-ref.txt
+++ b/Documentation/git-show-ref.txt
@@ -9,8 +9,8 @@ SYNOPSIS
--------
[verse]
'git show-ref' [--head] [-d | --dereference]
- [-s | --hash[=<n>]] [--abbrev[=<n>]] [--tags]
- [--heads] [--] [<pattern>...]
+ [-s | --hash[=<n>]] [--abbrev[=<n>]] [--branches] [--tags]
+ [--] [<pattern>...]
'git show-ref' --verify [-q | --quiet] [-d | --dereference]
[-s | --hash[=<n>]] [--abbrev[=<n>]]
[--] [<ref>...]
@@ -45,12 +45,14 @@ OPTIONS
Show the HEAD reference, even if it would normally be filtered out.
---heads::
+--branches::
--tags::
- Limit to "refs/heads" and "refs/tags", respectively. These options
+ Limit to local branches and local tags, respectively. These options
are not mutually exclusive; when given both, references stored in
- "refs/heads" and "refs/tags" are displayed.
+ "refs/heads" and "refs/tags" are displayed. Note that `--heads`
+ is a deprecated synonym for `--branches` and may be removed
+ in the future.
-d::
--dereference::
@@ -139,7 +141,7 @@ When using `--hash` (and not `--dereference`), the output is in the format:
For example,
-----------------------------------------------------------------------------
-$ git show-ref --heads --hash
+$ git show-ref --branches --hash
2e3ba0114a1f52b47df29743d6915d056be13278
185008ae97960c8d551adcd9e23565194651b5d1
03adf42c988195b50e1a1935ba5fcbc39b2b029b
@@ -183,8 +185,8 @@ 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`
-respectively (using both means that it shows tags and heads, but not other
+To show only tags, or only proper branch heads, use `--tags` and/or `--branches`
+respectively (using both means that it shows tags and branches, but not other
random references under the refs/ subdirectory).
To do automatic tag object dereferencing, use the `-d` or `--dereference`
diff --git a/Documentation/git-tag.txt b/Documentation/git-tag.txt
index 5fe519c31e..4494729f5e 100644
--- a/Documentation/git-tag.txt
+++ b/Documentation/git-tag.txt
@@ -10,6 +10,7 @@ SYNOPSIS
--------
[verse]
'git tag' [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>] [-e]
+ [(--trailer <token>[(=|:)<value>])...]
<tagname> [<commit> | <object>]
'git tag' -d <tagname>...
'git tag' [-n[<num>]] -l [--contains <commit>] [--no-contains <commit>]
@@ -31,8 +32,8 @@ creates a 'tag' object, and requires a tag message. Unless
`-m <msg>` or `-F <file>` is given, an editor is started for the user to type
in the tag message.
-If `-m <msg>` or `-F <file>` is given and `-a`, `-s`, and `-u <key-id>`
-are absent, `-a` is implied.
+If `-m <msg>` or `-F <file>` or `--trailer <token>[=<value>]` is given
+and `-a`, `-s`, and `-u <key-id>` are absent, `-a` is implied.
Otherwise, a tag reference that points directly at the given object
(i.e., a lightweight tag) is created.
@@ -178,6 +179,17 @@ This option is only applicable when listing tags without annotation lines.
Implies `-a` if none of `-a`, `-s`, or `-u <key-id>`
is given.
+--trailer <token>[(=|:)<value>]::
+ Specify a (<token>, <value>) pair that should be applied as a
+ trailer. (e.g. `git tag --trailer "Custom-Key: value"`
+ will add a "Custom-Key" trailer to the tag message.)
+ The `trailer.*` configuration variables
+ (linkgit:git-interpret-trailers[1]) can be used to define if
+ a duplicated trailer is omitted, where in the run of trailers
+ each trailer would appear, and other details.
+ The trailers can be extracted in `git tag --list`, using
+ `--format="%(trailers)"` placeholder.
+
-e::
--edit::
The message taken from file with `-F` and command line with
diff --git a/Documentation/git-update-index.txt b/Documentation/git-update-index.txt
index 8c47890a6a..7128aed540 100644
--- a/Documentation/git-update-index.txt
+++ b/Documentation/git-update-index.txt
@@ -25,6 +25,7 @@ SYNOPSIS
[--really-refresh] [--unresolve] [--again | -g]
[--info-only] [--index-info]
[-z] [--stdin] [--index-version <n>]
+ [--show-index-version]
[--verbose]
[--] [<file>...]
diff --git a/Documentation/git-update-ref.txt b/Documentation/git-update-ref.txt
index 374a2ebd2b..afcf33cf60 100644
--- a/Documentation/git-update-ref.txt
+++ b/Documentation/git-update-ref.txt
@@ -65,6 +65,10 @@ performs all modifications together. Specify commands of the form:
create SP <ref> SP <new-oid> LF
delete SP <ref> [SP <old-oid>] LF
verify SP <ref> [SP <old-oid>] LF
+ symref-update SP <ref> SP <new-target> [SP (ref SP <old-target> | oid SP <old-oid>)] LF
+ symref-create SP <ref> SP <new-target> LF
+ symref-delete SP <ref> [SP <old-target>] LF
+ symref-verify SP <ref> [SP <old-target>] LF
option SP <opt> LF
start LF
prepare LF
@@ -86,6 +90,10 @@ quoting:
create SP <ref> NUL <new-oid> NUL
delete SP <ref> NUL [<old-oid>] NUL
verify SP <ref> NUL [<old-oid>] NUL
+ symref-update SP <ref> NUL <new-target> [NUL (ref NUL <old-target> | oid NUL <old-oid>)] NUL
+ symref-create SP <ref> NUL <new-target> NUL
+ symref-delete SP <ref> [NUL <old-target>] NUL
+ symref-verify SP <ref> [NUL <old-target>] NUL
option SP <opt> NUL
start NUL
prepare NUL
@@ -113,10 +121,27 @@ delete::
Delete <ref> after verifying it exists with <old-oid>, if
given. If given, <old-oid> may not be zero.
+symref-update::
+ Set <ref> to <new-target> after verifying <old-target> or <old-oid>,
+ if given. Specify a zero <old-oid> to ensure that the ref does not
+ exist before the update.
+
verify::
Verify <ref> against <old-oid> but do not change it. If
<old-oid> is zero or missing, the ref must not exist.
+symref-create:
+ Create symbolic ref <ref> with <new-target> after verifying
+ it does not exist.
+
+symref-delete::
+ Delete <ref> after verifying it exists with <old-target>, if given.
+
+symref-verify::
+ Verify symbolic <ref> against <old-target> but do not change it.
+ If <old-target> is missing, the ref must not exist. Can only be
+ used in `no-deref` mode.
+
option::
Modify the behavior of the next command naming a <ref>.
The only valid option is `no-deref` to avoid dereferencing
diff --git a/Documentation/git-upload-pack.txt b/Documentation/git-upload-pack.txt
index 7ad60bc348..516d1639d9 100644
--- a/Documentation/git-upload-pack.txt
+++ b/Documentation/git-upload-pack.txt
@@ -55,6 +55,37 @@ ENVIRONMENT
admins may need to configure some transports to allow this
variable to be passed. See the discussion in linkgit:git[1].
+`GIT_NO_LAZY_FETCH`::
+ When cloning or fetching from a partial repository (i.e., one
+ itself cloned with `--filter`), the server-side `upload-pack`
+ may need to fetch extra objects from its upstream in order to
+ complete the request. By default, `upload-pack` will refuse to
+ perform such a lazy fetch, because `git fetch` may run arbitrary
+ commands specified in configuration and hooks of the source
+ repository (and `upload-pack` tries to be safe to run even in
+ untrusted `.git` directories).
++
+This is implemented by having `upload-pack` internally set the
+`GIT_NO_LAZY_FETCH` variable to `1`. If you want to override it
+(because you are fetching from a partial clone, and you are sure
+you trust it), you can explicitly set `GIT_NO_LAZY_FETCH` to
+`0`.
+
+SECURITY
+--------
+
+Most Git commands should not be run in an untrusted `.git` directory
+(see the section `SECURITY` in linkgit:git[1]). `upload-pack` tries to
+avoid any dangerous configuration options or hooks from the repository
+it's serving, making it safe to clone an untrusted directory and run
+commands on the resulting clone.
+
+For an extra level of safety, you may be able to run `upload-pack` as an
+alternate user. The details will be platform dependent, but on many
+systems you can run:
+
+ git clone --no-local --upload-pack='sudo -u nobody git-upload-pack' ...
+
SEE ALSO
--------
linkgit:gitnamespaces[7]
diff --git a/Documentation/git.txt b/Documentation/git.txt
index 7a1b112a3e..4489e2297a 100644
--- a/Documentation/git.txt
+++ b/Documentation/git.txt
@@ -11,9 +11,10 @@ SYNOPSIS
[verse]
'git' [-v | --version] [-h | --help] [-C <path>] [-c <name>=<value>]
[--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]
- [-p|--paginate|-P|--no-pager] [--no-replace-objects] [--bare]
- [--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]
- [--config-env=<name>=<envvar>] <command> [<args>]
+ [-p | --paginate | -P | --no-pager] [--no-replace-objects] [--no-lazy-fetch]
+ [--no-optional-locks] [--no-advice] [--bare] [--git-dir=<path>]
+ [--work-tree=<path>] [--namespace=<name>] [--config-env=<name>=<envvar>]
+ <command> [<args>]
DESCRIPTION
-----------
@@ -186,6 +187,13 @@ If you just want to run git as if it was started in `<path>` then use
This is equivalent to setting the `GIT_NO_LAZY_FETCH`
environment variable to `1`.
+--no-optional-locks::
+ Do not perform optional operations that require locks. This is
+ equivalent to setting the `GIT_OPTIONAL_LOCKS` to `0`.
+
+--no-advice::
+ Disable all advice hints from being printed.
+
--literal-pathspecs::
Treat pathspecs literally (i.e. no globbing, no pathspec magic).
This is equivalent to setting the `GIT_LITERAL_PATHSPECS` environment
@@ -207,10 +215,6 @@ If you just want to run git as if it was started in `<path>` then use
Add "icase" magic to all pathspec. This is equivalent to setting
the `GIT_ICASE_PATHSPECS` environment variable to `1`.
---no-optional-locks::
- Do not perform optional operations that require locks. This is
- equivalent to setting the `GIT_OPTIONAL_LOCKS` to `0`.
-
--list-cmds=<group>[,<group>...]::
List commands by group. This is an internal/experimental
option and may change or be removed in the future. Supported
@@ -640,6 +644,16 @@ parameter, <path>.
For each path `GIT_EXTERNAL_DIFF` is called, two environment variables,
`GIT_DIFF_PATH_COUNTER` and `GIT_DIFF_PATH_TOTAL` are set.
+`GIT_EXTERNAL_DIFF_TRUST_EXIT_CODE`::
+ If this Boolean environment variable is set to true then the
+ `GIT_EXTERNAL_DIFF` command is expected to return exit code
+ 0 if it considers the input files to be equal or 1 if it
+ considers them to be different, like `diff(1)`.
+ If it is set to false, which is the default, then the command
+ is expected to return exit code 0 regardless of equality.
+ Any other exit code causes Git to report a fatal error.
+
+
`GIT_DIFF_PATH_COUNTER`::
A 1-based counter incremented by one for every path.
@@ -1067,6 +1081,37 @@ The index is also capable of storing multiple entries (called "stages")
for a given pathname. These stages are used to hold the various
unmerged version of a file when a merge is in progress.
+SECURITY
+--------
+
+Some configuration options and hook files may cause Git to run arbitrary
+shell commands. Because configuration and hooks are not copied using
+`git clone`, it is generally safe to clone remote repositories with
+untrusted content, inspect them with `git log`, and so on.
+
+However, it is not safe to run Git commands in a `.git` directory (or
+the working tree that surrounds it) when that `.git` directory itself
+comes from an untrusted source. The commands in its config and hooks
+are executed in the usual way.
+
+By default, Git will refuse to run when the repository is owned by
+someone other than the user running the command. See the entry for
+`safe.directory` in linkgit:git-config[1]. While this can help protect
+you in a multi-user environment, note that you can also acquire
+untrusted repositories that are owned by you (for example, if you
+extract a zip file or tarball from an untrusted source). In such cases,
+you'd need to "sanitize" the untrusted repository first.
+
+If you have an untrusted `.git` directory, you should first clone it
+with `git clone --no-local` to obtain a clean copy. Git does restrict
+the set of options and hooks that will be run by `upload-pack`, which
+handles the server side of a clone or fetch, but beware that the
+surface area for attack against `upload-pack` is large, so this does
+carry some risk. The safest thing is to serve the repository as an
+unprivileged user (either via linkgit:git-daemon[1], ssh, or using
+other tools to change user ids). See the discussion in the `SECURITY`
+section of linkgit:git-upload-pack[1].
+
FURTHER DOCUMENTATION
---------------------
diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt
index 4338d023d9..e6150595af 100644
--- a/Documentation/gitattributes.txt
+++ b/Documentation/gitattributes.txt
@@ -374,7 +374,7 @@ explicitly define the line endings with `eol` if the `working-tree-encoding`
attribute is used to avoid ambiguity.
------------------------
-*.ps1 text working-tree-encoding=UTF-16LE eol=CRLF
+*.ps1 text working-tree-encoding=UTF-16LE eol=crlf
------------------------
You can get a list of all available encodings on your platform with the
@@ -776,6 +776,11 @@ with the above configuration, i.e. `j-c-diff`, with 7
parameters, just like `GIT_EXTERNAL_DIFF` program is called.
See linkgit:git[1] for details.
+If the program is able to ignore certain changes (similar to
+`git diff --ignore-space-change`), then also set the option
+`trustExitCode` to true. It is then expected to return exit code 1 if
+it finds significant changes and 0 if it doesn't.
+
Setting the internal diff algorithm
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/Documentation/gitfaq.txt b/Documentation/gitfaq.txt
index 8c1f2d5675..f2917d142c 100644
--- a/Documentation/gitfaq.txt
+++ b/Documentation/gitfaq.txt
@@ -185,6 +185,58 @@ Then, you can adjust your push URL to use `git@example_author` or
`git@example_committer` instead of `git@example.org` (e.g., `git remote set-url
git@example_author:org1/project1.git`).
+Transfers
+---------
+
+[[sync-working-tree]]
+How do I sync a working tree across systems?::
+ First, decide whether you want to do this at all. Git works best when you
+ push or pull your work using the typical `git push` and `git fetch` commands
+ and isn't designed to share a working tree across systems. This is
+ potentially risky and in some cases can cause repository corruption or data
+ loss.
++
+Usually, doing so will cause `git status` to need to re-read every file in the
+working tree. Additionally, Git's security model does not permit sharing a
+working tree across untrusted users, so it is only safe to sync a working tree
+if it will only be used by a single user across all machines.
++
+It is important not to use a cloud syncing service to sync any portion of a Git
+repository, since this can cause corruption, such as missing objects, changed
+or added files, broken refs, and a wide variety of other problems. These
+services tend to sync file by file on a continuous basis and don't understand
+the structure of a Git repository. This is especially bad if they sync the
+repository in the middle of it being updated, since that is very likely to
+cause incomplete or partial updates and therefore data loss.
++
+An example of the kind of corruption that can occur is conflicts over the state
+of refs, such that both sides end up with different commits on a branch that
+the other doesn't have. This can result in important objects becoming
+unreferenced and possibly pruned by `git gc`, causing data loss.
++
+Therefore, it's better to push your work to either the other system or a central
+server using the normal push and pull mechanism. However, this doesn't always
+preserve important data, like stashes, so some people prefer to share a working
+tree across systems.
++
+If you do this, the recommended approach is to use `rsync -a --delete-after`
+(ideally with an encrypted connection such as with `ssh`) on the root of
+repository. You should ensure several things when you do this:
++
+* If you have additional worktrees or a separate Git directory, they must be
+ synced at the same time as the main working tree and repository.
+* You are comfortable with the destination directory being an exact copy of the
+ source directory, _deleting any data that is already there_.
+* The repository (including all worktrees and the Git directory) is in a
+ quiescent state for the duration of the transfer (that is, no operations of
+ any sort are taking place on it, including background operations like `git
+ gc` and operations invoked by your editor).
++
+Be aware that even with these recommendations, syncing in this way has some risk
+since it bypasses Git's normal integrity checking for repositories, so having
+backups is advised. You may also wish to do a `git fsck` to verify the
+integrity of your data on the destination system after syncing.
+
Common Issues
-------------
@@ -241,6 +293,42 @@ How do I know if I want to do a fetch or a pull?::
ignore the upstream changes. A pull consists of a fetch followed
immediately by either a merge or rebase. See linkgit:git-pull[1].
+[[proxy]]
+Can I use a proxy with Git?::
+ Yes, Git supports the use of proxies. Git honors the standard `http_proxy`,
+ `https_proxy`, and `no_proxy` environment variables commonly used on Unix, and
+ it also can be configured with `http.proxy` and similar options for HTTPS (see
+ linkgit:git-config[1]). The `http.proxy` and related options can be
+ customized on a per-URL pattern basis. In addition, Git can in theory
+ function normally with transparent proxies that exist on the network.
++
+For SSH, Git can support a proxy using OpenSSH's `ProxyCommand`. Commonly used
+tools include `netcat` and `socat`. However, they must be configured not to
+exit when seeing EOF on standard input, which usually means that `netcat` will
+require `-q` and `socat` will require a timeout with something like `-t 10`.
+This is required because the way the Git SSH server knows that no more requests
+will be made is an EOF on standard input, but when that happens, the server may
+not have yet processed the final request, so dropping the connection at that
+point would interrupt that request.
++
+An example configuration entry in `~/.ssh/config` with an HTTP proxy might look
+like this:
++
+----
+Host git.example.org
+ User git
+ ProxyCommand socat -t 10 - PROXY:proxy.example.org:%h:%p,proxyport=8080
+----
++
+Note that in all cases, for Git to work properly, the proxy must be completely
+transparent. The proxy cannot modify, tamper with, or buffer the connection in
+any way, or Git will almost certainly fail to work. Note that many proxies,
+including many TLS middleboxes, Windows antivirus and firewall programs other
+than Windows Defender and Windows Firewall, and filtering proxies fail to meet
+this standard, and as a result end up breaking Git. Because of the many
+reports of problems and their poor security history, we recommend against the
+use of these classes of software and devices.
+
Merging and Rebasing
--------------------
@@ -357,8 +445,9 @@ I'm on Windows and git diff shows my files as having a `^M` at the end.::
+
You can store the files in the repository with Unix line endings and convert
them automatically to your platform's line endings. To do that, set the
-configuration option `core.eol` to `native` and see the following entry for
-information about how to configure files as text or binary.
+configuration option `core.eol` to `native` and see
+<<recommended-storage-settings,the question on recommended storage settings>>
+for information about how to configure files as text or binary.
+
You can also control this behavior with the `core.whitespace` setting if you
don't wish to remove the carriage returns from your line endings.
@@ -420,14 +509,26 @@ references, URLs, and hashes stored in the repository.
+
We also recommend setting a linkgit:gitattributes[5] file to explicitly mark
which files are text and which are binary. If you want Git to guess, you can
-set the attribute `text=auto`. For example, the following might be appropriate
-in some projects:
+set the attribute `text=auto`.
++
+With text files, Git will generally ensure that LF endings are used in the
+repository. The `core.autocrlf` and `core.eol` configuration variables specify
+what line-ending convention is followed when any text file is checked out. You
+can also use the `eol` attribute (e.g., `eol=crlf`) to override which files get
+what line-ending treatment.
++
+For example, generally shell files must have LF endings and batch files must
+have CRLF endings, so the following might be appropriate in some projects:
+
----
# By default, guess.
* text=auto
# Mark all C files as text.
*.c text
+# Ensure all shell files have LF endings and all batch files have CRLF
+# endings in the working tree and both have LF in the repo.
+*.sh text eol=lf
+*.bat text eol=crlf
# Mark all JPEG files as binary.
*.jpg binary
----
diff --git a/Documentation/gitformat-commit-graph.txt b/Documentation/gitformat-commit-graph.txt
index 31cad585e2..3e906e8030 100644
--- a/Documentation/gitformat-commit-graph.txt
+++ b/Documentation/gitformat-commit-graph.txt
@@ -142,13 +142,16 @@ All multi-byte numbers are in network byte order.
==== Bloom Filter Data (ID: {'B', 'D', 'A', 'T'}) [Optional]
* It starts with header consisting of three unsigned 32-bit integers:
- - Version of the hash algorithm being used. We currently only support
- value 1 which corresponds to the 32-bit version of the murmur3 hash
+ - Version of the hash algorithm being used. We currently support
+ value 2 which corresponds to the 32-bit version of the murmur3 hash
implemented exactly as described in
https://en.wikipedia.org/wiki/MurmurHash#Algorithm and the double
hashing technique using seed values 0x293ae76f and 0x7e646e2 as
described in https://doi.org/10.1007/978-3-540-30494-4_26 "Bloom Filters
- in Probabilistic Verification"
+ in Probabilistic Verification". Version 1 Bloom filters have a bug that appears
+ when char is signed and the repository has path names that have characters >=
+ 0x80; Git supports reading and writing them, but this ability will be removed
+ in a future version of Git.
- The number of times a path is hashed and hence the number of bit positions
that cumulatively determine whether a file is present in the commit.
- The minimum number of bits 'b' per entry in the Bloom filter. If the filter
diff --git a/Documentation/githooks.txt b/Documentation/githooks.txt
index ee9b92c90d..06e997131b 100644
--- a/Documentation/githooks.txt
+++ b/Documentation/githooks.txt
@@ -486,7 +486,7 @@ reference-transaction
This hook is invoked by any Git command that performs reference
updates. It executes whenever a reference transaction is prepared,
committed or aborted and may thus get called multiple times. The hook
-does not cover symbolic references (but that may change in the future).
+also supports symbolic reference updates.
The hook takes exactly one argument, which is the current state the
given reference transaction is in:
@@ -503,16 +503,20 @@ given reference transaction is in:
For each reference update that was added to the transaction, the hook
receives on standard input a line of the format:
- <old-oid> SP <new-oid> SP <ref-name> LF
+ <old-value> SP <new-value> SP <ref-name> LF
-where `<old-oid>` is the old object name passed into the reference
-transaction, `<new-oid>` is the new object name to be stored in the
+where `<old-value>` is the old object name passed into the reference
+transaction, `<new-value>` is the new object name to be stored in the
ref and `<ref-name>` is the full name of the ref. When force updating
the reference regardless of its current value or when the reference is
-to be created anew, `<old-oid>` is the all-zeroes object name. To
+to be created anew, `<old-value>` is the all-zeroes object name. To
distinguish these cases, you can inspect the current value of
`<ref-name>` via `git rev-parse`.
+For symbolic reference updates the `<old_value>` and `<new-value>`
+fields could denote references instead of objects. A reference will be
+denoted with a 'ref:' prefix, like `ref:<ref-target>`.
+
The exit status of the hook is ignored for any state except for the
"prepared" state. In the "prepared" state, a non-zero exit status will
cause the transaction to be aborted. The hook will not be called with
diff --git a/Documentation/gitpacking.txt b/Documentation/gitpacking.txt
new file mode 100644
index 0000000000..4a6fcba6f7
--- /dev/null
+++ b/Documentation/gitpacking.txt
@@ -0,0 +1,189 @@
+gitpacking(7)
+=============
+
+NAME
+----
+gitpacking - Advanced concepts related to packing in Git
+
+SYNOPSIS
+--------
+gitpacking
+
+DESCRIPTION
+-----------
+
+This document aims to describe some advanced concepts related to packing
+in Git.
+
+Many concepts are currently described scattered between manual pages of
+various Git commands, including linkgit:git-pack-objects[1],
+linkgit:git-repack[1], and others, as well as linkgit:gitformat-pack[5],
+and parts of the `Documentation/technical` tree.
+
+There are many aspects of packing in Git that are not covered in this
+document that instead live in the aforementioned areas. Over time, those
+scattered bits may coalesce into this document.
+
+== Pseudo-merge bitmaps
+
+NOTE: Pseudo-merge bitmaps are considered an experimental feature, so
+the configuration and many of the ideas are subject to change.
+
+=== Background
+
+Reachability bitmaps are most efficient when we have on-disk stored
+bitmaps for one or more of the starting points of a traversal. For this
+reason, Git prefers storing bitmaps for commits at the tips of refs,
+because traversals tend to start with those points.
+
+But if you have a large number of refs, it's not feasible to store a
+bitmap for _every_ ref tip. It takes up space, and just OR-ing all of
+those bitmaps together is expensive.
+
+One way we can deal with that is to create bitmaps that represent
+_groups_ of refs. When a traversal asks about the entire group, then we
+can use this single bitmap instead of considering each ref individually.
+Because these bitmaps represent the set of objects which would be
+reachable in a hypothetical merge of all of the commits, we call them
+pseudo-merge bitmaps.
+
+=== Overview
+
+A "pseudo-merge bitmap" is used to refer to a pair of bitmaps, as
+follows:
+
+Commit bitmap::
+
+ A bitmap whose set bits describe the set of commits included in the
+ pseudo-merge's "merge" bitmap (as below).
+
+Merge bitmap::
+
+ A bitmap whose set bits describe the reachability closure over the set
+ of commits in the pseudo-merge's "commits" bitmap (as above). An
+ identical bitmap would be generated for an octopus merge with the same
+ set of parents as described in the commits bitmap.
+
+Pseudo-merge bitmaps can accelerate bitmap traversals when all commits
+for a given pseudo-merge are listed on either side of the traversal,
+either directly (by explicitly asking for them as part of the `HAVES`
+or `WANTS`) or indirectly (by encountering them during a fill-in
+traversal).
+
+=== Use-cases
+
+For example, suppose there exists a pseudo-merge bitmap with a large
+number of commits, all of which are listed in the `WANTS` section of
+some bitmap traversal query. When pseudo-merge bitmaps are enabled, the
+bitmap machinery can quickly determine there is a pseudo-merge which
+satisfies some subset of the wanted objects on either side of the query.
+Then, we can inflate the EWAH-compressed bitmap, and `OR` it in to the
+resulting bitmap. By contrast, without pseudo-merge bitmaps, we would
+have to repeat the decompression and `OR`-ing step over a potentially
+large number of individual bitmaps, which can take proportionally more
+time.
+
+Another benefit of pseudo-merges arises when there is some combination
+of (a) a large number of references, with (b) poor bitmap coverage, and
+(c) deep, nested trees, making fill-in traversal relatively expensive.
+For example, suppose that there are a large enough number of tags where
+bitmapping each of the tags individually is infeasible. Without
+pseudo-merge bitmaps, computing the result of, say, `git rev-list
+--use-bitmap-index --count --objects --tags` would likely require a
+large amount of fill-in traversal. But when a large quantity of those
+tags are stored together in a pseudo-merge bitmap, the bitmap machinery
+can take advantage of the fact that we only care about the union of
+objects reachable from all of those tags, and answer the query much
+faster.
+
+=== Configuration
+
+Reference tips are grouped into different pseudo-merge groups according
+to two criteria. A reference name matches one or more of the defined
+pseudo-merge patterns, and optionally one or more capture groups within
+that pattern which further partition the group.
+
+Within a group, commits may be considered "stable", or "unstable"
+depending on their age. These are adjusted by setting the
+`bitmapPseudoMerge.<name>.stableThreshold` and
+`bitmapPseudoMerge.<name>.threshold` configuration values, respectively.
+
+All stable commits are grouped into pseudo-merges of equal size
+(`bitmapPseudoMerge.<name>.stableSize`). If the `stableSize`
+configuration is set to, say, 100, then the first 100 commits (ordered
+by committer date) which are older than the `stableThreshold` value will
+form one group, the next 100 commits will form another group, and so on.
+
+Among unstable commits, the pseudo-merge machinery will attempt to
+combine older commits into large groups as opposed to newer commits
+which will appear in smaller groups. This is based on the heuristic that
+references whose tip commit is older are less likely to be modified to
+point at a different commit than a reference whose tip commit is newer.
+
+The size of groups is determined by a power-law decay function, and the
+decay parameter roughly corresponds to "k" in `f(n) = C*n^(-k/100)`,
+where `f(n)` describes the size of the `n`-th pseudo-merge group. The
+sample rate controls what percentage of eligible commits are considered
+as candidates. The threshold parameter indicates the minimum age (so as
+to avoid including too-recent commits in a pseudo-merge group, making it
+less likely to be valid). The "maxMerges" parameter sets an upper-bound
+on the number of pseudo-merge commits an individual group
+
+The "stable"-related parameters control "stable" pseudo-merge groups,
+comprised of a fixed number of commits which are older than the
+configured "stable threshold" value and may be grouped together in
+chunks of "stableSize" in order of age.
+
+The exact configuration for pseudo-merges is as follows:
+
+include::config/bitmap-pseudo-merge.txt[]
+
+=== Examples
+
+Suppose that you have a repository with a large number of references,
+and you want a bare-bones configuration of pseudo-merge bitmaps that
+will enhance bitmap coverage of the `refs/` namespace. You may start
+wiht a configuration like so:
+
+ [bitmapPseudoMerge "all"]
+ pattern = "refs/"
+ threshold = now
+ stableThreshold = never
+ sampleRate = 100
+ maxMerges = 64
+
+This will create pseudo-merge bitmaps for all references, regardless of
+their age, and group them into 64 pseudo-merge commits.
+
+If you wanted to separate tags from branches when generating
+pseudo-merge commits, you would instead define the pattern with a
+capture group, like so:
+
+ [bitmapPseudoMerge "all"]
+ pattern = "refs/(heads/tags)/"
+
+Suppose instead that you are working in a fork-network repository, with
+each fork specified by some numeric ID, and whose refs reside in
+`refs/virtual/NNN/` (where `NNN` is the numeric ID corresponding to some
+fork) in the network. In this instance, you may instead write something
+like:
+
+ [bitmapPseudoMerge "all"]
+ pattern = "refs/virtual/([0-9]+)/(heads|tags)/"
+ threshold = now
+ stableThreshold = never
+ sampleRate = 100
+ maxMerges = 64
+
+Which would generate pseudo-merge group identifiers like "1234-heads",
+and "5678-tags" (for branches in fork "1234", and tags in remote "5678",
+respectively).
+
+SEE ALSO
+--------
+linkgit:git-pack-objects[1]
+linkgit:git-repack[1]
+
+GIT
+---
+Part of the linkgit:git[1] suite
diff --git a/Documentation/glossary-content.txt b/Documentation/glossary-content.txt
index 9195bf8767..42afe04869 100644
--- a/Documentation/glossary-content.txt
+++ b/Documentation/glossary-content.txt
@@ -592,7 +592,8 @@ the `refs/tags/` hierarchy is used to represent local tags..
[[def_refspec]]refspec::
A "refspec" is used by <<def_fetch,fetch>> and
<<def_push,push>> to describe the mapping between remote
- <<def_ref,ref>> and local ref.
+ <<def_ref,ref>> and local ref. See linkgit:git-fetch[1] or
+ linkgit:git-push[1] for details.
[[def_remote]]remote repository::
A <<def_repository,repository>> which is used to track the same
diff --git a/Documentation/lint-manpages.sh b/Documentation/lint-manpages.sh
new file mode 100755
index 0000000000..92cfc0a15a
--- /dev/null
+++ b/Documentation/lint-manpages.sh
@@ -0,0 +1,108 @@
+#!/bin/sh
+
+extract_variable () {
+ (
+ cat ../Makefile
+ cat <<EOF
+print_variable:
+ @\$(foreach b,\$($1),echo XXX \$(b:\$X=) YYY;)
+EOF
+ ) |
+ make -C .. -f - print_variable 2>/dev/null |
+ sed -n -e 's/.*XXX \(.*\) YYY.*/\1/p'
+}
+
+check_missing_docs () (
+ ret=0
+
+ for v in $ALL_COMMANDS
+ do
+ case "$v" in
+ git-merge-octopus) continue;;
+ git-merge-ours) continue;;
+ git-merge-recursive) continue;;
+ git-merge-resolve) continue;;
+ git-merge-subtree) continue;;
+ git-fsck-objects) continue;;
+ git-init-db) continue;;
+ git-remote-*) continue;;
+ git-stage) continue;;
+ git-legacy-*) continue;;
+ git-?*--?* ) continue ;;
+ esac
+
+ if ! test -f "$v.txt"
+ then
+ echo "no doc: $v"
+ ret=1
+ fi
+
+ if ! sed -e '1,/^### command list/d' -e '/^#/d' ../command-list.txt |
+ grep -q "^$v[ ]"
+ then
+ case "$v" in
+ git)
+ ;;
+ *)
+ echo "no link: $v"
+ ret=1
+ ;;
+ esac
+ fi
+ done
+
+ exit $ret
+)
+
+check_extraneous_docs () {
+ (
+ sed -e '1,/^### command list/d' \
+ -e '/^#/d' \
+ -e '/guide$/d' \
+ -e '/interfaces$/d' \
+ -e 's/[ ].*//' \
+ -e 's/^/listed /' ../command-list.txt
+ make print-man1 |
+ grep '\.txt$' |
+ sed -e 's|^|documented |' \
+ -e 's/\.txt//'
+ ) | (
+ all_commands="$(printf "%s " "$ALL_COMMANDS" "$BUILT_INS" "$EXCLUDED_PROGRAMS" | tr '\n' ' ')"
+ ret=0
+
+ while read how cmd
+ do
+ case " $all_commands " in
+ *" $cmd "*) ;;
+ *)
+ echo "removed but $how: $cmd"
+ ret=1;;
+ esac
+ done
+
+ exit $ret
+ )
+}
+
+BUILT_INS="$(extract_variable BUILT_INS)"
+ALL_COMMANDS="$(extract_variable ALL_COMMANDS)"
+EXCLUDED_PROGRAMS="$(extract_variable EXCLUDED_PROGRAMS)"
+
+findings=$(
+ if ! check_missing_docs
+ then
+ ret=1
+ fi
+
+ if ! check_extraneous_docs
+ then
+ ret=1
+ fi
+
+ exit $ret
+)
+ret=$?
+
+printf "%s" "$findings" | sort
+
+exit $ret
diff --git a/Documentation/technical/bitmap-format.txt b/Documentation/technical/bitmap-format.txt
index f5d200939b..bfb0ec7beb 100644
--- a/Documentation/technical/bitmap-format.txt
+++ b/Documentation/technical/bitmap-format.txt
@@ -255,3 +255,144 @@ triplet is -
xor_row (4 byte integer, network byte order): ::
The position of the triplet whose bitmap is used to compress
this one, or `0xffffffff` if no such bitmap exists.
+
+Pseudo-merge bitmaps
+--------------------
+
+If the `BITMAP_OPT_PSEUDO_MERGES` flag is set, a variable number of
+bytes (preceding the name-hash cache, commit lookup table, and trailing
+checksum) of the `.bitmap` file is used to store pseudo-merge bitmaps.
+
+For more information on what pseudo-merges are, why they are useful, and
+how to configure them, see the information in linkgit:gitpacking[7].
+
+=== File format
+
+If enabled, pseudo-merge bitmaps are stored in an optional section at
+the end of a `.bitmap` file. The format is as follows:
+
+....
++-------------------------------------------+
+| .bitmap File |
++-------------------------------------------+
+| |
+| Pseudo-merge bitmaps (Variable Length) |
+| +---------------------------+ |
+| | commits_bitmap (EWAH) | |
+| +---------------------------+ |
+| | merge_bitmap (EWAH) | |
+| +---------------------------+ |
+| |
++-------------------------------------------+
+| |
+| Lookup Table |
+| +---------------------------+ |
+| | commit_pos (4 bytes) | |
+| +---------------------------+ |
+| | offset (8 bytes) | |
+| +------------+--------------+ |
+| |
+| Offset Cases: |
+| ------------- |
+| |
+| 1. MSB Unset: single pseudo-merge bitmap |
+| + offset to pseudo-merge bitmap |
+| |
+| 2. MSB Set: multiple pseudo-merges |
+| + offset to extended lookup table |
+| |
++-------------------------------------------+
+| |
+| Extended Lookup Table (Optional) |
+| +----+----------+----------+----------+ |
+| | N | Offset 1 | .... | Offset N | |
+| +----+----------+----------+----------+ |
+| | | 8 bytes | .... | 8 bytes | |
+| +----+----------+----------+----------+ |
+| |
++-------------------------------------------+
+| |
+| Pseudo-merge position table |
+| +----+----------+----------+----------+ |
+| | N | Offset 1 | .... | Offset N | |
+| +----+----------+----------+----------+ |
+| | | 8 bytes | .... | 8 bytes | |
+| +----+----------+----------+----------+ |
+| |
++-------------------------------------------+
+| |
+| Pseudo-merge Metadata |
+| +-----------------------------------+ |
+| | # pseudo-merges (4 bytes) | |
+| +-----------------------------------+ |
+| | # commits (4 bytes) | |
+| +-----------------------------------+ |
+| | Lookup offset (8 bytes) | |
+| +-----------------------------------+ |
+| | Extension size (8 bytes) | |
+| +-----------------------------------+ |
+| |
++-------------------------------------------+
+....
+
+* One or more pseudo-merge bitmaps, each containing:
+
+ ** `commits_bitmap`, an EWAH-compressed bitmap describing the set of
+ commits included in the this psuedo-merge.
+
+ ** `merge_bitmap`, an EWAH-compressed bitmap describing the union of
+ the set of objects reachable from all commits listed in the
+ `commits_bitmap`.
+
+* A lookup table, mapping pseudo-merged commits to the pseudo-merges
+ they belong to. Entries appear in increasing order of each commit's
+ bit position. Each entry is 12 bytes wide, and is comprised of the
+ following:
+
+ ** `commit_pos`, a 4-byte unsigned value (in network byte-order)
+ containing the bit position for this commit.
+
+ ** `offset`, an 8-byte unsigned value (also in network byte-order)
+ containing either one of two possible offsets, depending on whether or
+ not the most-significant bit is set.
+
+ *** If unset (i.e. `offset & ((uint64_t)1<<63) == 0`), the offset
+ (relative to the beginning of the `.bitmap` file) at which the
+ pseudo-merge bitmap for this commit can be read. This indicates
+ only a single pseudo-merge bitmap contains this commit.
+
+ *** If set (i.e. `offset & ((uint64_t)1<<63) != 0`), the offset
+ (again relative to the beginning of the `.bitmap` file) at which
+ the extended offset table can be located describing the set of
+ pseudo-merge bitmaps which contain this commit. This indicates
+ that multiple pseudo-merge bitmaps contain this commit.
+
+* An (optional) extended lookup table (written if and only if there is
+ at least one commit which appears in more than one pseudo-merge).
+ There are as many entries as commits which appear in multiple
+ pseudo-merges. Each entry contains the following:
+
+ ** `N`, a 4-byte unsigned value equal to the number of pseudo-merges
+ which contain a given commit.
+
+ ** An array of `N` 8-byte unsigned values, each of which is
+ interpreted as an offset (relative to the beginning of the
+ `.bitmap` file) at which a pseudo-merge bitmap for this commit can
+ be read. These values occur in no particular order.
+
+* Positions for all pseudo-merges, each stored as an 8-byte unsigned
+ value (in network byte-order) containing the offset (relative to the
+ beginning of the `.bitmap` file) of each consecutive pseudo-merge.
+
+* A 4-byte unsigned value (in network byte-order) equal to the number of
+ pseudo-merges.
+
+* A 4-byte unsigned value (in network byte-order) equal to the number of
+ unique commits which appear in any pseudo-merge.
+
+* An 8-byte unsigned value (in network byte-order) equal to the number
+ of bytes between the start of the pseudo-merge section and the
+ beginning of the lookup table.
+
+* An 8-byte unsigned value (in network byte-order) equal to the number
+ of bytes in the pseudo-merge section (including this field).
diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN
index 433cf41e59..3e96943375 100755
--- a/GIT-VERSION-GEN
+++ b/GIT-VERSION-GEN
@@ -1,7 +1,7 @@
#!/bin/sh
GVF=GIT-VERSION-FILE
-DEF_VER=v2.45.0
+DEF_VER=v2.46.0-rc0
LF='
'
diff --git a/Makefile b/Makefile
index 1e31acc72e..d6479092a0 100644
--- a/Makefile
+++ b/Makefile
@@ -409,6 +409,9 @@ include shared.mak
# to the "<name>" of the corresponding `compat/fsmonitor/fsm-settings-<name>.c`
# that implements the `fsm_os_settings__*()` routines.
#
+# Define LINK_FUZZ_PROGRAMS if you want `make all` to also build the fuzz test
+# programs in oss-fuzz/.
+#
# === Optional library: libintl ===
#
# Define NO_GETTEXT if you don't want Git output to be translated.
@@ -752,23 +755,6 @@ SCRIPTS = $(SCRIPT_SH_GEN) \
ETAGS_TARGET = TAGS
-# If you add a new fuzzer, please also make sure to run it in
-# ci/run-build-and-minimal-fuzzers.sh so that we make sure it still links and
-# runs in the future.
-FUZZ_OBJS += oss-fuzz/dummy-cmd-main.o
-FUZZ_OBJS += oss-fuzz/fuzz-commit-graph.o
-FUZZ_OBJS += oss-fuzz/fuzz-config.o
-FUZZ_OBJS += oss-fuzz/fuzz-date.o
-FUZZ_OBJS += oss-fuzz/fuzz-pack-headers.o
-FUZZ_OBJS += oss-fuzz/fuzz-pack-idx.o
-.PHONY: fuzz-objs
-fuzz-objs: $(FUZZ_OBJS)
-
-# Always build fuzz objects even if not testing, to prevent bit-rot.
-all:: $(FUZZ_OBJS)
-
-FUZZ_PROGRAMS += $(patsubst %.o,%,$(filter-out %dummy-cmd-main.o,$(FUZZ_OBJS)))
-
# Empty...
EXTRA_PROGRAMS =
@@ -807,7 +793,7 @@ TEST_BUILTINS_OBJS += test-dump-fsmonitor.o
TEST_BUILTINS_OBJS += test-dump-split-index.o
TEST_BUILTINS_OBJS += test-dump-untracked-cache.o
TEST_BUILTINS_OBJS += test-env-helper.o
-TEST_BUILTINS_OBJS += test-example-decorate.o
+TEST_BUILTINS_OBJS += test-example-tap.o
TEST_BUILTINS_OBJS += test-find-pack.o
TEST_BUILTINS_OBJS += test-fsmonitor-client.o
TEST_BUILTINS_OBJS += test-genrandom.o
@@ -823,8 +809,6 @@ TEST_BUILTINS_OBJS += test-match-trees.o
TEST_BUILTINS_OBJS += test-mergesort.o
TEST_BUILTINS_OBJS += test-mktemp.o
TEST_BUILTINS_OBJS += test-oid-array.o
-TEST_BUILTINS_OBJS += test-oidmap.o
-TEST_BUILTINS_OBJS += test-oidtree.o
TEST_BUILTINS_OBJS += test-online-cpus.o
TEST_BUILTINS_OBJS += test-pack-mtimes.o
TEST_BUILTINS_OBJS += test-parse-options.o
@@ -852,7 +836,6 @@ TEST_BUILTINS_OBJS += test-sha1.o
TEST_BUILTINS_OBJS += test-sha256.o
TEST_BUILTINS_OBJS += test-sigchain.o
TEST_BUILTINS_OBJS += test-simple-ipc.o
-TEST_BUILTINS_OBJS += test-strcmp-offset.o
TEST_BUILTINS_OBJS += test-string-list.o
TEST_BUILTINS_OBJS += test-submodule-config.o
TEST_BUILTINS_OBJS += test-submodule-nested-repo-config.o
@@ -1119,6 +1102,7 @@ LIB_OBJS += prompt.o
LIB_OBJS += protocol.o
LIB_OBJS += protocol-caps.o
LIB_OBJS += prune-packed.o
+LIB_OBJS += pseudo-merge.o
LIB_OBJS += quote.o
LIB_OBJS += range-diff.o
LIB_OBJS += reachable.o
@@ -1296,6 +1280,7 @@ BUILTIN_OBJS += builtin/read-tree.o
BUILTIN_OBJS += builtin/rebase.o
BUILTIN_OBJS += builtin/receive-pack.o
BUILTIN_OBJS += builtin/reflog.o
+BUILTIN_OBJS += builtin/refs.o
BUILTIN_OBJS += builtin/remote-ext.o
BUILTIN_OBJS += builtin/remote-fd.o
BUILTIN_OBJS += builtin/remote.o
@@ -1347,14 +1332,23 @@ THIRD_PARTY_SOURCES += compat/regex/%
THIRD_PARTY_SOURCES += sha1collisiondetection/%
THIRD_PARTY_SOURCES += sha1dc/%
-UNIT_TEST_PROGRAMS += t-basic
-UNIT_TEST_PROGRAMS += t-mem-pool
-UNIT_TEST_PROGRAMS += t-strbuf
UNIT_TEST_PROGRAMS += t-ctype
+UNIT_TEST_PROGRAMS += t-example-decorate
+UNIT_TEST_PROGRAMS += t-hash
+UNIT_TEST_PROGRAMS += t-mem-pool
+UNIT_TEST_PROGRAMS += t-oidmap
+UNIT_TEST_PROGRAMS += t-oidtree
UNIT_TEST_PROGRAMS += t-prio-queue
+UNIT_TEST_PROGRAMS += t-reftable-basics
+UNIT_TEST_PROGRAMS += t-reftable-record
+UNIT_TEST_PROGRAMS += t-strbuf
+UNIT_TEST_PROGRAMS += t-strcmp-offset
+UNIT_TEST_PROGRAMS += t-strvec
+UNIT_TEST_PROGRAMS += t-trailer
UNIT_TEST_PROGS = $(patsubst %,$(UNIT_TEST_BIN)/%$X,$(UNIT_TEST_PROGRAMS))
UNIT_TEST_OBJS = $(patsubst %,$(UNIT_TEST_DIR)/%.o,$(UNIT_TEST_PROGRAMS))
UNIT_TEST_OBJS += $(UNIT_TEST_DIR)/test-lib.o
+UNIT_TEST_OBJS += $(UNIT_TEST_DIR)/lib-oid.o
# xdiff and reftable libs may in turn depend on what is in libgit.a
GITLIBS = common-main.o $(LIB_FILE) $(XDIFF_LIB) $(REFTABLE_LIB) $(LIB_FILE)
@@ -1458,8 +1452,8 @@ ALL_COMMANDS_TO_INSTALL += git-upload-archive$(X)
ALL_COMMANDS_TO_INSTALL += git-upload-pack$(X)
endif
-ALL_CFLAGS = $(DEVELOPER_CFLAGS) $(CPPFLAGS) $(CFLAGS)
-ALL_LDFLAGS = $(LDFLAGS)
+ALL_CFLAGS = $(DEVELOPER_CFLAGS) $(CPPFLAGS) $(CFLAGS) $(CFLAGS_APPEND)
+ALL_LDFLAGS = $(LDFLAGS) $(LDFLAGS_APPEND)
ifdef SANITIZE
SANITIZERS := $(foreach flag,$(subst $(comma),$(space),$(SANITIZE)),$(flag))
@@ -2373,6 +2367,29 @@ ifndef NO_TCLTK
endif
$(QUIET_SUBDIR0)templates $(QUIET_SUBDIR1) SHELL_PATH='$(SHELL_PATH_SQ)' PERL_PATH='$(PERL_PATH_SQ)'
+# If you add a new fuzzer, please also make sure to run it in
+# ci/run-build-and-minimal-fuzzers.sh so that we make sure it still links and
+# runs in the future.
+FUZZ_OBJS += oss-fuzz/dummy-cmd-main.o
+FUZZ_OBJS += oss-fuzz/fuzz-commit-graph.o
+FUZZ_OBJS += oss-fuzz/fuzz-config.o
+FUZZ_OBJS += oss-fuzz/fuzz-date.o
+FUZZ_OBJS += oss-fuzz/fuzz-pack-headers.o
+FUZZ_OBJS += oss-fuzz/fuzz-pack-idx.o
+.PHONY: fuzz-objs
+fuzz-objs: $(FUZZ_OBJS)
+
+# Always build fuzz objects even if not testing, to prevent bit-rot.
+all:: $(FUZZ_OBJS)
+
+FUZZ_PROGRAMS += $(patsubst %.o,%,$(filter-out %dummy-cmd-main.o,$(FUZZ_OBJS)))
+
+# Build fuzz programs when possible, even without the necessary fuzzing support,
+# to prevent bit-rot.
+ifdef LINK_FUZZ_PROGRAMS
+all:: $(FUZZ_PROGRAMS)
+endif
+
please_set_SHELL_PATH_to_a_more_modern_shell:
@$$(:)
@@ -2656,20 +2673,16 @@ REFTABLE_OBJS += reftable/merged.o
REFTABLE_OBJS += reftable/pq.o
REFTABLE_OBJS += reftable/reader.o
REFTABLE_OBJS += reftable/record.o
-REFTABLE_OBJS += reftable/refname.o
REFTABLE_OBJS += reftable/generic.o
REFTABLE_OBJS += reftable/stack.o
REFTABLE_OBJS += reftable/tree.o
REFTABLE_OBJS += reftable/writer.o
-REFTABLE_TEST_OBJS += reftable/basics_test.o
REFTABLE_TEST_OBJS += reftable/block_test.o
REFTABLE_TEST_OBJS += reftable/dump.o
REFTABLE_TEST_OBJS += reftable/merged_test.o
REFTABLE_TEST_OBJS += reftable/pq_test.o
-REFTABLE_TEST_OBJS += reftable/record_test.o
REFTABLE_TEST_OBJS += reftable/readwrite_test.o
-REFTABLE_TEST_OBJS += reftable/refname_test.o
REFTABLE_TEST_OBJS += reftable/stack_test.o
REFTABLE_TEST_OBJS += reftable/test_framework.o
REFTABLE_TEST_OBJS += reftable/tree_test.o
@@ -3228,7 +3241,7 @@ perf: all
.PRECIOUS: $(TEST_OBJS)
-t/helper/test-tool$X: $(patsubst %,t/helper/%,$(TEST_BUILTINS_OBJS))
+t/helper/test-tool$X: $(patsubst %,t/helper/%,$(TEST_BUILTINS_OBJS)) $(UNIT_TEST_DIR)/test-lib.o
t/helper/test-%$X: t/helper/test-%.o GIT-LDFLAGS $(GITLIBS) $(REFTABLE_TEST_LIB)
$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(filter %.a,$^) $(LIBS)
@@ -3749,42 +3762,6 @@ ALL_COMMANDS += scalar
.PHONY: check-docs
check-docs::
$(MAKE) -C Documentation lint-docs
- @(for v in $(patsubst %$X,%,$(ALL_COMMANDS)); \
- do \
- case "$$v" in \
- git-merge-octopus | git-merge-ours | git-merge-recursive | \
- git-merge-resolve | git-merge-subtree | \
- git-fsck-objects | git-init-db | \
- git-remote-* | git-stage | git-legacy-* | \
- git-?*--?* ) continue ;; \
- esac ; \
- test -f "Documentation/$$v.txt" || \
- echo "no doc: $$v"; \
- sed -e '1,/^### command list/d' -e '/^#/d' command-list.txt | \
- grep -q "^$$v[ ]" || \
- case "$$v" in \
- git) ;; \
- *) echo "no link: $$v";; \
- esac ; \
- done; \
- ( \
- sed -e '1,/^### command list/d' \
- -e '/^#/d' \
- -e '/guide$$/d' \
- -e '/interfaces$$/d' \
- -e 's/[ ].*//' \
- -e 's/^/listed /' command-list.txt; \
- $(MAKE) -C Documentation print-man1 | \
- grep '\.txt$$' | \
- sed -e 's|^|documented |' \
- -e 's/\.txt//'; \
- ) | while read how cmd; \
- do \
- case " $(patsubst %$X,%,$(ALL_COMMANDS) $(BUILT_INS) $(EXCLUDED_PROGRAMS)) " in \
- *" $$cmd "*) ;; \
- *) echo "removed but $$how: $$cmd" ;; \
- esac; \
- done ) | sort
### Make sure built-ins do not have dups and listed in git.c
#
@@ -3858,28 +3835,31 @@ cover_db_html: cover_db
#
# An example command to build against libFuzzer from LLVM 11.0.0:
#
-# make CC=clang CXX=clang++ \
+# make CC=clang FUZZ_CXX=clang++ \
# CFLAGS="-fsanitize=fuzzer-no-link,address" \
# LIB_FUZZING_ENGINE="-fsanitize=fuzzer,address" \
# fuzz-all
#
+FUZZ_CXX ?= $(CC)
FUZZ_CXXFLAGS ?= $(ALL_CFLAGS)
.PHONY: fuzz-all
+fuzz-all: $(FUZZ_PROGRAMS)
$(FUZZ_PROGRAMS): %: %.o oss-fuzz/dummy-cmd-main.o $(GITLIBS) GIT-LDFLAGS
- $(QUIET_LINK)$(CXX) $(FUZZ_CXXFLAGS) -o $@ $(ALL_LDFLAGS) \
+ $(QUIET_LINK)$(FUZZ_CXX) $(FUZZ_CXXFLAGS) -o $@ $(ALL_LDFLAGS) \
-Wl,--allow-multiple-definition \
$(filter %.o,$^) $(filter %.a,$^) $(LIBS) $(LIB_FUZZING_ENGINE)
-fuzz-all: $(FUZZ_PROGRAMS)
-
-$(UNIT_TEST_PROGS): $(UNIT_TEST_BIN)/%$X: $(UNIT_TEST_DIR)/%.o $(UNIT_TEST_DIR)/test-lib.o $(GITLIBS) GIT-LDFLAGS
+$(UNIT_TEST_PROGS): $(UNIT_TEST_BIN)/%$X: $(UNIT_TEST_DIR)/%.o \
+ $(UNIT_TEST_DIR)/test-lib.o \
+ $(UNIT_TEST_DIR)/lib-oid.o \
+ $(GITLIBS) GIT-LDFLAGS
$(call mkdir_p_parent_template)
$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) \
$(filter %.o,$^) $(filter %.a,$^) $(LIBS)
.PHONY: build-unit-tests unit-tests
build-unit-tests: $(UNIT_TEST_PROGS)
-unit-tests: $(UNIT_TEST_PROGS)
+unit-tests: $(UNIT_TEST_PROGS) t/helper/test-tool$X
$(MAKE) -C t/ unit-tests
diff --git a/RelNotes b/RelNotes
index ae70277109..cc696fc869 120000
--- a/RelNotes
+++ b/RelNotes
@@ -1 +1 @@
-Documentation/RelNotes/2.45.0.txt \ No newline at end of file
+Documentation/RelNotes/2.46.0.txt \ No newline at end of file
diff --git a/add-interactive.c b/add-interactive.c
index e17602b5e4..49042b3026 100644
--- a/add-interactive.c
+++ b/add-interactive.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "add-interactive.h"
#include "color.h"
@@ -532,8 +534,9 @@ static int get_modified_files(struct repository *r,
size_t *binary_count)
{
struct object_id head_oid;
- int is_initial = !resolve_ref_unsafe("HEAD", RESOLVE_REF_READING,
- &head_oid, NULL);
+ int is_initial = !refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
+ "HEAD", RESOLVE_REF_READING,
+ &head_oid, NULL);
struct collection_status s = { 0 };
int i;
@@ -556,7 +559,7 @@ static int get_modified_files(struct repository *r,
s.skip_unseen = filter && i;
opt.def = is_initial ?
- empty_tree_oid_hex() : oid_to_hex(&head_oid);
+ empty_tree_oid_hex(the_repository->hash_algo) : oid_to_hex(&head_oid);
repo_init_revisions(r, &rev, NULL);
setup_revisions(0, NULL, &rev, &opt);
@@ -761,8 +764,10 @@ static int run_revert(struct add_i_state *s, const struct pathspec *ps,
size_t count, i, j;
struct object_id oid;
- int is_initial = !resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, &oid,
- NULL);
+ int is_initial = !refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
+ "HEAD", RESOLVE_REF_READING,
+ &oid,
+ NULL);
struct lock_file index_lock;
const char **paths;
struct tree *tree;
@@ -990,8 +995,10 @@ static int run_diff(struct add_i_state *s, const struct pathspec *ps,
ssize_t count, i;
struct object_id oid;
- int is_initial = !resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, &oid,
- NULL);
+ int is_initial = !refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
+ "HEAD", RESOLVE_REF_READING,
+ &oid,
+ NULL);
if (get_modified_files(s->r, INDEX_ONLY, files, ps, NULL, NULL) < 0)
return -1;
diff --git a/add-patch.c b/add-patch.c
index 0997d4af73..6e176cd21a 100644
--- a/add-patch.c
+++ b/add-patch.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "add-interactive.h"
#include "advice.h"
@@ -293,13 +295,13 @@ static void err(struct add_p_state *s, const char *fmt, ...)
va_list args;
va_start(args, fmt);
- fputs(s->s.error_color, stderr);
- vfprintf(stderr, fmt, args);
- fputs(s->s.reset_color, stderr);
- fputc('\n', stderr);
+ fputs(s->s.error_color, stdout);
+ vprintf(fmt, args);
+ puts(s->s.reset_color);
va_end(args);
}
+LAST_ARG_MUST_BE_NULL
static void setup_child_process(struct add_p_state *s,
struct child_process *cp, ...)
{
@@ -421,7 +423,7 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
/* could be on an unborn branch */
!strcmp("HEAD", s->revision) &&
repo_get_oid(the_repository, "HEAD", &oid) ?
- empty_tree_oid_hex() : s->revision);
+ empty_tree_oid_hex(the_repository->hash_algo) : s->revision);
}
color_arg_index = args.nr;
/* Use `--no-color` explicitly, just in case `diff.color = always`. */
@@ -1228,6 +1230,7 @@ static int prompt_yesno(struct add_p_state *s, const char *prompt)
fflush(stdout);
if (read_single_character(s) == EOF)
return -1;
+ /* do not limit to 1-byte input to allow 'no' etc. */
switch (tolower(s->answer.buf[0])) {
case 'n': return 0;
case 'y': return 1;
@@ -1326,7 +1329,7 @@ static int apply_for_checkout(struct add_p_state *s, struct strbuf *diff,
err(s, _("Nothing was applied.\n"));
} else
/* As a last resort, show the diff to the user */
- fwrite(diff->buf, diff->len, 1, stderr);
+ fwrite(diff->buf, diff->len, 1, stdout);
return 0;
}
@@ -1511,6 +1514,12 @@ static int patch_update_file(struct add_p_state *s,
if (!s->answer.len)
continue;
ch = tolower(s->answer.buf[0]);
+
+ /* 'g' takes a hunk number and '/' takes a regexp */
+ if (s->answer.len != 1 && (ch != 'g' && ch != '/')) {
+ err(s, _("Only one letter is expected, got '%s'"), s->answer.buf);
+ continue;
+ }
if (ch == 'y') {
hunk->use = USE_HUNK;
soft_increment:
@@ -1668,7 +1677,7 @@ soft_increment:
}
} else if (s->answer.buf[0] == 'p') {
rendered_hunk_index = -1;
- } else {
+ } else if (s->answer.buf[0] == '?') {
const char *p = _(help_patch_remainder), *eol = p;
color_fprintf(stdout, s->s.help_color, "%s",
@@ -1692,6 +1701,9 @@ soft_increment:
color_fprintf_ln(stdout, s->s.help_color,
"%.*s", (int)(eol - p), p);
}
+ } else {
+ err(s, _("Unknown command '%s' (use '?' for help)"),
+ s->answer.buf);
}
}
@@ -1778,9 +1790,9 @@ int run_add_p(struct repository *r, enum add_p_mode mode,
break;
if (s.file_diff_nr == 0)
- fprintf(stderr, _("No changes.\n"));
+ err(&s, _("No changes."));
else if (binary_count == s.file_diff_nr)
- fprintf(stderr, _("Only binary files changed.\n"));
+ err(&s, _("Only binary files changed."));
add_p_state_clear(&s);
return 0;
diff --git a/advice.c b/advice.c
index 75111191ad..6b879d805c 100644
--- a/advice.c
+++ b/advice.c
@@ -2,6 +2,7 @@
#include "advice.h"
#include "config.h"
#include "color.h"
+#include "environment.h"
#include "gettext.h"
#include "help.h"
#include "string-list.h"
@@ -69,6 +70,7 @@ static struct {
[ADVICE_PUSH_UNQUALIFIED_REF_NAME] = { "pushUnqualifiedRefName" },
[ADVICE_PUSH_UPDATE_REJECTED] = { "pushUpdateRejected" },
[ADVICE_PUSH_UPDATE_REJECTED_ALIAS] = { "pushNonFastForward" }, /* backwards compatibility */
+ [ADVICE_REBASE_TODO_ERROR] = { "rebaseTodoError" },
[ADVICE_REF_SYNTAX] = { "refSyntax" },
[ADVICE_RESET_NO_REFRESH_WARNING] = { "resetNoRefresh" },
[ADVICE_RESOLVE_CONFLICT] = { "resolveConflict" },
@@ -76,6 +78,7 @@ static struct {
[ADVICE_SEQUENCER_IN_USE] = { "sequencerInUse" },
[ADVICE_SET_UPSTREAM_FAILURE] = { "setUpstreamFailure" },
[ADVICE_SKIPPED_CHERRY_PICKS] = { "skippedCherryPicks" },
+ [ADVICE_SPARSE_INDEX_EXPANDED] = { "sparseIndexExpanded" },
[ADVICE_STATUS_AHEAD_BEHIND_WARNING] = { "statusAheadBehindWarning" },
[ADVICE_STATUS_HINTS] = { "statusHints" },
[ADVICE_STATUS_U_OPTION] = { "statusUoption" },
@@ -127,6 +130,12 @@ void advise(const char *advice, ...)
int advice_enabled(enum advice_type type)
{
int enabled = advice_setting[type].level != ADVICE_LEVEL_DISABLED;
+ static int globally_enabled = -1;
+
+ if (globally_enabled < 0)
+ globally_enabled = git_env_bool(GIT_ADVICE_ENVIRONMENT, 1);
+ if (!globally_enabled)
+ return 0;
if (type == ADVICE_PUSH_UPDATE_REJECTED)
return enabled &&
diff --git a/advice.h b/advice.h
index c8d29f97f3..d7466bc0ef 100644
--- a/advice.h
+++ b/advice.h
@@ -37,6 +37,7 @@ enum advice_type {
ADVICE_PUSH_UNQUALIFIED_REF_NAME,
ADVICE_PUSH_UPDATE_REJECTED,
ADVICE_PUSH_UPDATE_REJECTED_ALIAS,
+ ADVICE_REBASE_TODO_ERROR,
ADVICE_REF_SYNTAX,
ADVICE_RESET_NO_REFRESH_WARNING,
ADVICE_RESOLVE_CONFLICT,
@@ -44,6 +45,7 @@ enum advice_type {
ADVICE_SEQUENCER_IN_USE,
ADVICE_SET_UPSTREAM_FAILURE,
ADVICE_SKIPPED_CHERRY_PICKS,
+ ADVICE_SPARSE_INDEX_EXPANDED,
ADVICE_STATUS_AHEAD_BEHIND_WARNING,
ADVICE_STATUS_HINTS,
ADVICE_STATUS_U_OPTION,
diff --git a/alias.c b/alias.c
index 5a238f2e30..4daafd9bda 100644
--- a/alias.c
+++ b/alias.c
@@ -21,9 +21,11 @@ static int config_alias_cb(const char *key, const char *value,
return 0;
if (data->alias) {
- if (!strcasecmp(p, data->alias))
- return git_config_string((const char **)&data->v,
+ if (!strcasecmp(p, data->alias)) {
+ FREE_AND_NULL(data->v);
+ return git_config_string(&data->v,
key, value);
+ }
} else if (data->list) {
string_list_append(data->list, p);
}
diff --git a/apply.c b/apply.c
index 901b67e625..0f2f5dabe3 100644
--- a/apply.c
+++ b/apply.c
@@ -7,6 +7,8 @@
*
*/
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "abspath.h"
#include "base85.h"
@@ -135,6 +137,7 @@ void clear_apply_state(struct apply_state *state)
strset_clear(&state->removed_symlinks);
strset_clear(&state->kept_symlinks);
strbuf_release(&state->root);
+ FREE_AND_NULL(state->fake_ancestor);
/* &state->fn_table is cleared at the end of apply_patch() */
}
@@ -2493,18 +2496,21 @@ static int match_fragment(struct apply_state *state,
int match_beginning, int match_end)
{
int i;
- char *fixed_buf, *buf, *orig, *target;
- struct strbuf fixed;
- size_t fixed_len, postlen;
+ const char *orig, *target;
+ struct strbuf fixed = STRBUF_INIT;
+ size_t postlen;
int preimage_limit;
+ int ret;
if (preimage->nr + current_lno <= img->nr) {
/*
* The hunk falls within the boundaries of img.
*/
preimage_limit = preimage->nr;
- if (match_end && (preimage->nr + current_lno != img->nr))
- return 0;
+ if (match_end && (preimage->nr + current_lno != img->nr)) {
+ ret = 0;
+ goto out;
+ }
} else if (state->ws_error_action == correct_ws_error &&
(ws_rule & WS_BLANK_AT_EOF)) {
/*
@@ -2521,17 +2527,23 @@ static int match_fragment(struct apply_state *state,
* we are not removing blanks at the end, so we
* should reject the hunk at this position.
*/
- return 0;
+ ret = 0;
+ goto out;
}
- if (match_beginning && current_lno)
- return 0;
+ if (match_beginning && current_lno) {
+ ret = 0;
+ goto out;
+ }
/* Quick hash check */
- for (i = 0; i < preimage_limit; i++)
+ for (i = 0; i < preimage_limit; i++) {
if ((img->line[current_lno + i].flag & LINE_PATCHED) ||
- (preimage->line[i].hash != img->line[current_lno + i].hash))
- return 0;
+ (preimage->line[i].hash != img->line[current_lno + i].hash)) {
+ ret = 0;
+ goto out;
+ }
+ }
if (preimage_limit == preimage->nr) {
/*
@@ -2544,8 +2556,10 @@ static int match_fragment(struct apply_state *state,
if ((match_end
? (current + preimage->len == img->len)
: (current + preimage->len <= img->len)) &&
- !memcmp(img->buf + current, preimage->buf, preimage->len))
- return 1;
+ !memcmp(img->buf + current, preimage->buf, preimage->len)) {
+ ret = 1;
+ goto out;
+ }
} else {
/*
* The preimage extends beyond the end of img, so
@@ -2554,7 +2568,7 @@ static int match_fragment(struct apply_state *state,
* There must be one non-blank context line that match
* a line before the end of img.
*/
- char *buf_end;
+ const char *buf, *buf_end;
buf = preimage->buf;
buf_end = buf;
@@ -2564,8 +2578,10 @@ static int match_fragment(struct apply_state *state,
for ( ; buf < buf_end; buf++)
if (!isspace(*buf))
break;
- if (buf == buf_end)
- return 0;
+ if (buf == buf_end) {
+ ret = 0;
+ goto out;
+ }
}
/*
@@ -2573,12 +2589,16 @@ static int match_fragment(struct apply_state *state,
* fuzzy matching. We collect all the line length information because
* we need it to adjust whitespace if we match.
*/
- if (state->ws_ignore_action == ignore_ws_change)
- return line_by_line_fuzzy_match(img, preimage, postimage,
- current, current_lno, preimage_limit);
+ if (state->ws_ignore_action == ignore_ws_change) {
+ ret = line_by_line_fuzzy_match(img, preimage, postimage,
+ current, current_lno, preimage_limit);
+ goto out;
+ }
- if (state->ws_error_action != correct_ws_error)
- return 0;
+ if (state->ws_error_action != correct_ws_error) {
+ ret = 0;
+ goto out;
+ }
/*
* The hunk does not apply byte-by-byte, but the hash says
@@ -2607,7 +2627,7 @@ static int match_fragment(struct apply_state *state,
* but in this loop we will only handle the part of the
* preimage that falls within the file.
*/
- strbuf_init(&fixed, preimage->len + 1);
+ strbuf_grow(&fixed, preimage->len + 1);
orig = preimage->buf;
target = img->buf + current;
for (i = 0; i < preimage_limit; i++) {
@@ -2643,8 +2663,10 @@ static int match_fragment(struct apply_state *state,
postlen += tgtfix.len;
strbuf_release(&tgtfix);
- if (!match)
- goto unmatch_exit;
+ if (!match) {
+ ret = 0;
+ goto out;
+ }
orig += oldlen;
target += tgtlen;
@@ -2665,9 +2687,13 @@ static int match_fragment(struct apply_state *state,
/* Try fixing the line in the preimage */
ws_fix_copy(&fixed, orig, oldlen, ws_rule, NULL);
- for (j = fixstart; j < fixed.len; j++)
- if (!isspace(fixed.buf[j]))
- goto unmatch_exit;
+ for (j = fixstart; j < fixed.len; j++) {
+ if (!isspace(fixed.buf[j])) {
+ ret = 0;
+ goto out;
+ }
+ }
+
orig += oldlen;
}
@@ -2677,16 +2703,16 @@ static int match_fragment(struct apply_state *state,
* has whitespace breakages unfixed, and fixing them makes the
* hunk match. Update the context lines in the postimage.
*/
- fixed_buf = strbuf_detach(&fixed, &fixed_len);
if (postlen < postimage->len)
postlen = 0;
update_pre_post_images(preimage, postimage,
- fixed_buf, fixed_len, postlen);
- return 1;
+ fixed.buf, fixed.len, postlen);
- unmatch_exit:
+ ret = 1;
+
+out:
strbuf_release(&fixed);
- return 0;
+ return ret;
}
static int find_pos(struct apply_state *state,
@@ -3680,7 +3706,7 @@ static int try_threeway(struct apply_state *state,
if (status) {
patch->conflicted_threeway = 1;
if (patch->is_new)
- oidclr(&patch->threeway_stage[0]);
+ oidclr(&patch->threeway_stage[0], the_repository->hash_algo);
else
oidcpy(&patch->threeway_stage[0], &pre_oid);
oidcpy(&patch->threeway_stage[1], &our_oid);
diff --git a/apply.h b/apply.h
index 7cd38b1443..cd25d24cc4 100644
--- a/apply.h
+++ b/apply.h
@@ -1,7 +1,7 @@
#ifndef APPLY_H
#define APPLY_H
-#include "hash-ll.h"
+#include "hash.h"
#include "lockfile.h"
#include "string-list.h"
#include "strmap.h"
@@ -59,7 +59,7 @@ struct apply_state {
struct repository *repo;
const char *index_file;
enum apply_verbosity apply_verbosity;
- const char *fake_ancestor;
+ char *fake_ancestor;
const char *patch_input_file;
int line_termination;
struct strbuf root;
diff --git a/archive-tar.c b/archive-tar.c
index 8ae30125f8..e7b3489e1e 100644
--- a/archive-tar.c
+++ b/archive-tar.c
@@ -1,6 +1,9 @@
/*
* Copyright (c) 2005, 2006 Rene Scharfe
*/
+
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "config.h"
#include "gettext.h"
diff --git a/archive-zip.c b/archive-zip.c
index fd1d3f816d..9f32730181 100644
--- a/archive-zip.c
+++ b/archive-zip.c
@@ -1,6 +1,9 @@
/*
* Copyright (c) 2006 Rene Scharfe
*/
+
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "config.h"
#include "archive.h"
diff --git a/archive.c b/archive.c
index 5287fcdd8e..7bd60d0632 100644
--- a/archive.c
+++ b/archive.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "abspath.h"
#include "config.h"
diff --git a/attr.c b/attr.c
index 679e42258c..06b5b5e55e 100644
--- a/attr.c
+++ b/attr.c
@@ -6,6 +6,8 @@
* an insanely large number of attributes.
*/
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "config.h"
#include "environment.h"
@@ -25,7 +27,7 @@
#include "tree-walk.h"
#include "object-name.h"
-const char *git_attr_tree;
+char *git_attr_tree;
const char git_attr__true[] = "(builtin)true";
const char git_attr__false[] = "\0(builtin)false";
@@ -765,8 +767,8 @@ static struct attr_stack *read_attr_from_file(const char *path, unsigned flags)
return res;
}
-static struct attr_stack *read_attr_from_buf(char *buf, const char *path,
- unsigned flags)
+static struct attr_stack *read_attr_from_buf(char *buf, size_t length,
+ const char *path, unsigned flags)
{
struct attr_stack *res;
char *sp;
@@ -774,6 +776,11 @@ static struct attr_stack *read_attr_from_buf(char *buf, const char *path,
if (!buf)
return NULL;
+ if (length >= ATTR_MAX_FILE_SIZE) {
+ warning(_("ignoring overly large gitattributes blob '%s'"), path);
+ free(buf);
+ return NULL;
+ }
CALLOC_ARRAY(res, 1);
for (sp = buf; *sp;) {
@@ -813,7 +820,7 @@ static struct attr_stack *read_attr_from_blob(struct index_state *istate,
return NULL;
}
- return read_attr_from_buf(buf, path, flags);
+ return read_attr_from_buf(buf, sz, path, flags);
}
static struct attr_stack *read_attr_from_index(struct index_state *istate,
@@ -860,13 +867,8 @@ static struct attr_stack *read_attr_from_index(struct index_state *istate,
stack = read_attr_from_blob(istate, &istate->cache[sparse_dir_pos]->oid, relative_path, flags);
} else {
buf = read_blob_data_from_index(istate, path, &size);
- if (!buf)
- return NULL;
- if (size >= ATTR_MAX_FILE_SIZE) {
- warning(_("ignoring overly large gitattributes blob '%s'"), path);
- return NULL;
- }
- stack = read_attr_from_buf(buf, path, flags);
+ if (buf)
+ stack = read_attr_from_buf(buf, size, path, flags);
}
return stack;
}
@@ -1206,15 +1208,16 @@ static void collect_some_attrs(struct index_state *istate,
}
static const char *default_attr_source_tree_object_name;
-static int ignore_bad_attr_tree;
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)
+static int compute_default_attr_source(struct object_id *attr_source)
{
+ int ignore_bad_attr_tree = 0;
+
if (!default_attr_source_tree_object_name)
default_attr_source_tree_object_name = getenv(GIT_ATTR_SOURCE_ENVIRONMENT);
@@ -1223,29 +1226,34 @@ static void compute_default_attr_source(struct object_id *attr_source)
ignore_bad_attr_tree = 1;
}
- if (!default_attr_source_tree_object_name &&
- startup_info->have_repository &&
- is_bare_repository()) {
- default_attr_source_tree_object_name = "HEAD";
- ignore_bad_attr_tree = 1;
- }
+ if (!default_attr_source_tree_object_name)
+ return 0;
- if (!default_attr_source_tree_object_name || !is_null_oid(attr_source))
- return;
+ if (!startup_info->have_repository) {
+ if (!ignore_bad_attr_tree)
+ die(_("cannot use --attr-source or GIT_ATTR_SOURCE without repo"));
+ return 0;
+ }
if (repo_get_oid_treeish(the_repository,
default_attr_source_tree_object_name,
- attr_source) && !ignore_bad_attr_tree)
- die(_("bad --attr-source or GIT_ATTR_SOURCE"));
+ attr_source)) {
+ if (!ignore_bad_attr_tree)
+ die(_("bad --attr-source or GIT_ATTR_SOURCE"));
+ return 0;
+ }
+
+ return 1;
}
static struct object_id *default_attr_source(void)
{
static struct object_id attr_source;
+ static int has_attr_source = -1;
- if (is_null_oid(&attr_source))
- compute_default_attr_source(&attr_source);
- if (is_null_oid(&attr_source))
+ if (has_attr_source < 0)
+ has_attr_source = compute_default_attr_source(&attr_source);
+ if (!has_attr_source)
return NULL;
return &attr_source;
}
@@ -1296,7 +1304,8 @@ static const char *builtin_object_mode_attr(struct index_state *istate, const ch
if (pos >= 0) {
if (S_ISGITLINK(istate->cache[pos]->ce_mode))
mode = istate->cache[pos]->ce_mode;
- } else if (resolve_gitlink_ref(path, "HEAD", &oid) == 0) {
+ } else if (repo_resolve_gitlink_ref(the_repository, path,
+ "HEAD", &oid) == 0) {
mode = S_IFGITLINK;
}
}
diff --git a/attr.h b/attr.h
index 127998ae01..bb33b60880 100644
--- a/attr.h
+++ b/attr.h
@@ -190,6 +190,8 @@ struct attr_check {
};
struct attr_check *attr_check_alloc(void);
+
+LAST_ARG_MUST_BE_NULL
struct attr_check *attr_check_initl(const char *, ...);
struct attr_check *attr_check_dup(const struct attr_check *check);
@@ -236,6 +238,6 @@ const char *git_attr_global_file(void);
/* Return whether the system gitattributes file is enabled and should be used. */
int git_attr_system_is_enabled(void);
-extern const char *git_attr_tree;
+extern char *git_attr_tree;
#endif /* ATTR_H */
diff --git a/bisect.c b/bisect.c
index 29aae879b8..135f94ba09 100644
--- a/bisect.c
+++ b/bisect.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "config.h"
#include "commit.h"
@@ -469,7 +471,8 @@ static int register_ref(const char *refname, const struct object_id *oid,
static int read_bisect_refs(void)
{
- return for_each_ref_in("refs/bisect/", register_ref, NULL);
+ return refs_for_each_ref_in(get_main_ref_store(the_repository),
+ "refs/bisect/", register_ref, NULL);
}
static GIT_PATH_FUNC(git_path_bisect_names, "BISECT_NAMES")
@@ -709,7 +712,7 @@ static enum bisect_error error_if_skipped_commits(struct commit_list *tried,
static int is_expected_rev(const struct object_id *oid)
{
struct object_id expected_oid;
- if (read_ref("BISECT_EXPECTED_REV", &expected_oid))
+ if (refs_read_ref(get_main_ref_store(the_repository), "BISECT_EXPECTED_REV", &expected_oid))
return 0;
return oideq(oid, &expected_oid);
}
@@ -721,11 +724,14 @@ enum bisect_error bisect_checkout(const struct object_id *bisect_rev,
struct pretty_print_context pp = {0};
struct strbuf commit_msg = STRBUF_INIT;
- update_ref(NULL, "BISECT_EXPECTED_REV", bisect_rev, NULL, 0, UPDATE_REFS_DIE_ON_ERR);
+ refs_update_ref(get_main_ref_store(the_repository), NULL,
+ "BISECT_EXPECTED_REV", bisect_rev, NULL, 0,
+ UPDATE_REFS_DIE_ON_ERR);
if (no_checkout) {
- update_ref(NULL, "BISECT_HEAD", bisect_rev, NULL, 0,
- UPDATE_REFS_DIE_ON_ERR);
+ refs_update_ref(get_main_ref_store(the_repository), NULL,
+ "BISECT_HEAD", bisect_rev, NULL, 0,
+ UPDATE_REFS_DIE_ON_ERR);
} else {
struct child_process cmd = CHILD_PROCESS_INIT;
@@ -1027,7 +1033,8 @@ enum bisect_error bisect_next_all(struct repository *r, const char *prefix)
* If no_checkout is non-zero, the bisection process does not
* checkout the trial commit but instead simply updates BISECT_HEAD.
*/
- int no_checkout = ref_exists("BISECT_HEAD");
+ int no_checkout = refs_ref_exists(get_main_ref_store(the_repository),
+ "BISECT_HEAD");
unsigned bisect_flags = 0;
read_bisect_terms(&term_bad, &term_good);
@@ -1178,10 +1185,14 @@ int bisect_clean_state(void)
/* There may be some refs packed during bisection */
struct string_list refs_for_removal = STRING_LIST_INIT_NODUP;
- for_each_ref_in("refs/bisect", mark_for_removal, (void *) &refs_for_removal);
+ refs_for_each_ref_in(get_main_ref_store(the_repository),
+ "refs/bisect", mark_for_removal,
+ (void *) &refs_for_removal);
string_list_append(&refs_for_removal, xstrdup("BISECT_HEAD"));
string_list_append(&refs_for_removal, xstrdup("BISECT_EXPECTED_REV"));
- result = delete_refs("bisect: remove", &refs_for_removal, REF_NO_DEREF);
+ result = refs_delete_refs(get_main_ref_store(the_repository),
+ "bisect: remove", &refs_for_removal,
+ REF_NO_DEREF);
refs_for_removal.strdup_strings = 1;
string_list_clear(&refs_for_removal, 0);
unlink_or_warn(git_path_bisect_ancestors_ok());
diff --git a/blame.c b/blame.c
index 1a16d4eb6a..90633380cd 100644
--- a/blame.c
+++ b/blame.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "refs.h"
#include "object-store-ll.h"
@@ -1246,7 +1248,7 @@ static int fill_blob_sha1_and_mode(struct repository *r,
goto error_out;
return 0;
error_out:
- oidclr(&origin->blob_oid);
+ oidclr(&origin->blob_oid, the_repository->hash_algo);
origin->mode = S_IFINVALID;
return -1;
}
@@ -2700,7 +2702,7 @@ static struct commit *dwim_reverse_initial(struct rev_info *revs,
return NULL;
/* Do we have HEAD? */
- if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, &head_oid, NULL))
+ if (!refs_resolve_ref_unsafe(get_main_ref_store(the_repository), "HEAD", RESOLVE_REF_READING, &head_oid, NULL))
return NULL;
head_commit = lookup_commit_reference_gently(revs->repo,
&head_oid, 1);
@@ -2803,7 +2805,7 @@ void setup_scoreboard(struct blame_scoreboard *sb,
if (sb->final) {
parent_oid = &sb->final->object.oid;
} else {
- if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, &head_oid, NULL))
+ if (!refs_resolve_ref_unsafe(get_main_ref_store(the_repository), "HEAD", RESOLVE_REF_READING, &head_oid, NULL))
die("no such ref: HEAD");
parent_oid = &head_oid;
}
@@ -2928,6 +2930,10 @@ void setup_blame_bloom_data(struct blame_scoreboard *sb)
void cleanup_scoreboard(struct blame_scoreboard *sb)
{
+ free(sb->lineno);
+ clear_prio_queue(&sb->commits);
+ oidset_clear(&sb->ignore_list);
+
if (sb->bloom_data) {
int i;
for (i = 0; i < sb->bloom_data->nr; i++) {
diff --git a/bloom.c b/bloom.c
index e529f7605c..c915f8b1ba 100644
--- a/bloom.c
+++ b/bloom.c
@@ -6,6 +6,10 @@
#include "commit-graph.h"
#include "commit.h"
#include "commit-slab.h"
+#include "tree.h"
+#include "tree-walk.h"
+#include "config.h"
+#include "repository.h"
define_commit_slab(bloom_filter_slab, struct bloom_filter);
@@ -48,9 +52,9 @@ static int check_bloom_offset(struct commit_graph *g, uint32_t pos,
return -1;
}
-static int load_bloom_filter_from_graph(struct commit_graph *g,
- struct bloom_filter *filter,
- uint32_t graph_pos)
+int load_bloom_filter_from_graph(struct commit_graph *g,
+ struct bloom_filter *filter,
+ uint32_t graph_pos)
{
uint32_t lex_pos, start_index, end_index;
@@ -88,6 +92,8 @@ static int load_bloom_filter_from_graph(struct commit_graph *g,
filter->data = (unsigned char *)(g->chunk_bloom_data +
sizeof(unsigned char) * start_index +
BLOOMDATA_CHUNK_HEADER_SIZE);
+ filter->version = g->bloom_filter_settings->hash_version;
+ filter->to_free = NULL;
return 1;
}
@@ -99,7 +105,64 @@ static int load_bloom_filter_from_graph(struct commit_graph *g,
* Not considered to be cryptographically secure.
* Implemented as described in https://en.wikipedia.org/wiki/MurmurHash#Algorithm
*/
-uint32_t murmur3_seeded(uint32_t seed, const char *data, size_t len)
+uint32_t murmur3_seeded_v2(uint32_t seed, const char *data, size_t len)
+{
+ const uint32_t c1 = 0xcc9e2d51;
+ const uint32_t c2 = 0x1b873593;
+ const uint32_t r1 = 15;
+ const uint32_t r2 = 13;
+ const uint32_t m = 5;
+ const uint32_t n = 0xe6546b64;
+ int i;
+ uint32_t k1 = 0;
+ const char *tail;
+
+ int len4 = len / sizeof(uint32_t);
+
+ uint32_t k;
+ for (i = 0; i < len4; i++) {
+ uint32_t byte1 = (uint32_t)(unsigned char)data[4*i];
+ uint32_t byte2 = ((uint32_t)(unsigned char)data[4*i + 1]) << 8;
+ uint32_t byte3 = ((uint32_t)(unsigned char)data[4*i + 2]) << 16;
+ uint32_t byte4 = ((uint32_t)(unsigned char)data[4*i + 3]) << 24;
+ k = byte1 | byte2 | byte3 | byte4;
+ k *= c1;
+ k = rotate_left(k, r1);
+ k *= c2;
+
+ seed ^= k;
+ seed = rotate_left(seed, r2) * m + n;
+ }
+
+ tail = (data + len4 * sizeof(uint32_t));
+
+ switch (len & (sizeof(uint32_t) - 1)) {
+ case 3:
+ k1 ^= ((uint32_t)(unsigned char)tail[2]) << 16;
+ /*-fallthrough*/
+ case 2:
+ k1 ^= ((uint32_t)(unsigned char)tail[1]) << 8;
+ /*-fallthrough*/
+ case 1:
+ k1 ^= ((uint32_t)(unsigned char)tail[0]) << 0;
+ k1 *= c1;
+ k1 = rotate_left(k1, r1);
+ k1 *= c2;
+ seed ^= k1;
+ break;
+ }
+
+ seed ^= (uint32_t)len;
+ seed ^= (seed >> 16);
+ seed *= 0x85ebca6b;
+ seed ^= (seed >> 13);
+ seed *= 0xc2b2ae35;
+ seed ^= (seed >> 16);
+
+ return seed;
+}
+
+static uint32_t murmur3_seeded_v1(uint32_t seed, const char *data, size_t len)
{
const uint32_t c1 = 0xcc9e2d51;
const uint32_t c2 = 0x1b873593;
@@ -164,8 +227,14 @@ void fill_bloom_key(const char *data,
int i;
const uint32_t seed0 = 0x293ae76f;
const uint32_t seed1 = 0x7e646e2c;
- const uint32_t hash0 = murmur3_seeded(seed0, data, len);
- const uint32_t hash1 = murmur3_seeded(seed1, data, len);
+ uint32_t hash0, hash1;
+ if (settings->hash_version == 2) {
+ hash0 = murmur3_seeded_v2(seed0, data, len);
+ hash1 = murmur3_seeded_v2(seed1, data, len);
+ } else {
+ hash0 = murmur3_seeded_v1(seed0, data, len);
+ hash1 = murmur3_seeded_v1(seed1, data, len);
+ }
key->hashes = (uint32_t *)xcalloc(settings->num_hashes, sizeof(uint32_t));
for (i = 0; i < settings->num_hashes; i++)
@@ -197,6 +266,18 @@ void init_bloom_filters(void)
init_bloom_filter_slab(&bloom_filters);
}
+static void free_one_bloom_filter(struct bloom_filter *filter)
+{
+ if (!filter)
+ return;
+ free(filter->to_free);
+}
+
+void deinit_bloom_filters(void)
+{
+ deep_clear_bloom_filter_slab(&bloom_filters, free_one_bloom_filter);
+}
+
static int pathmap_cmp(const void *hashmap_cmp_fn_data UNUSED,
const struct hashmap_entry *eptr,
const struct hashmap_entry *entry_or_key,
@@ -210,11 +291,97 @@ static int pathmap_cmp(const void *hashmap_cmp_fn_data UNUSED,
return strcmp(e1->path, e2->path);
}
-static void init_truncated_large_filter(struct bloom_filter *filter)
+static void init_truncated_large_filter(struct bloom_filter *filter,
+ int version)
{
- filter->data = xmalloc(1);
+ filter->data = filter->to_free = xmalloc(1);
filter->data[0] = 0xFF;
filter->len = 1;
+ filter->version = version;
+}
+
+#define VISITED (1u<<21)
+#define HIGH_BITS (1u<<22)
+
+static int has_entries_with_high_bit(struct repository *r, struct tree *t)
+{
+ if (parse_tree(t))
+ return 1;
+
+ if (!(t->object.flags & VISITED)) {
+ struct tree_desc desc;
+ struct name_entry entry;
+
+ init_tree_desc(&desc, &t->object.oid, t->buffer, t->size);
+ while (tree_entry(&desc, &entry)) {
+ size_t i;
+ for (i = 0; i < entry.pathlen; i++) {
+ if (entry.path[i] & 0x80) {
+ t->object.flags |= HIGH_BITS;
+ goto done;
+ }
+ }
+
+ if (S_ISDIR(entry.mode)) {
+ struct tree *sub = lookup_tree(r, &entry.oid);
+ if (sub && has_entries_with_high_bit(r, sub)) {
+ t->object.flags |= HIGH_BITS;
+ goto done;
+ }
+ }
+
+ }
+
+done:
+ t->object.flags |= VISITED;
+ }
+
+ return !!(t->object.flags & HIGH_BITS);
+}
+
+static int commit_tree_has_high_bit_paths(struct repository *r,
+ struct commit *c)
+{
+ struct tree *t;
+ if (repo_parse_commit(r, c))
+ return 1;
+ t = repo_get_commit_tree(r, c);
+ if (!t)
+ return 1;
+ return has_entries_with_high_bit(r, t);
+}
+
+static struct bloom_filter *upgrade_filter(struct repository *r, struct commit *c,
+ struct bloom_filter *filter,
+ int hash_version)
+{
+ struct commit_list *p = c->parents;
+ if (commit_tree_has_high_bit_paths(r, c))
+ return NULL;
+
+ if (p && commit_tree_has_high_bit_paths(r, p->item))
+ return NULL;
+
+ filter->version = hash_version;
+
+ return filter;
+}
+
+struct bloom_filter *get_bloom_filter(struct repository *r, struct commit *c)
+{
+ struct bloom_filter *filter;
+ int hash_version;
+
+ filter = get_or_compute_bloom_filter(r, c, 0, NULL, NULL);
+ if (!filter)
+ return NULL;
+
+ prepare_repo_settings(r);
+ hash_version = r->settings.commit_graph_changed_paths_version;
+
+ if (!(hash_version == -1 || hash_version == filter->version))
+ return NULL; /* unusable filter */
+ return filter;
}
struct bloom_filter *get_or_compute_bloom_filter(struct repository *r,
@@ -242,8 +409,23 @@ struct bloom_filter *get_or_compute_bloom_filter(struct repository *r,
filter, graph_pos);
}
- if (filter->data && filter->len)
- return filter;
+ if (filter->data && filter->len) {
+ struct bloom_filter *upgrade;
+ if (!settings || settings->hash_version == filter->version)
+ return filter;
+
+ /* version mismatch, see if we can upgrade */
+ if (compute_if_not_present &&
+ git_env_bool("GIT_TEST_UPGRADE_BLOOM_FILTERS", 1)) {
+ upgrade = upgrade_filter(r, c, filter,
+ settings->hash_version);
+ if (upgrade) {
+ if (computed)
+ *computed |= BLOOM_UPGRADED;
+ return upgrade;
+ }
+ }
+ }
if (!compute_if_not_present)
return NULL;
@@ -299,19 +481,22 @@ struct bloom_filter *get_or_compute_bloom_filter(struct repository *r,
}
if (hashmap_get_size(&pathmap) > settings->max_changed_paths) {
- init_truncated_large_filter(filter);
+ init_truncated_large_filter(filter,
+ settings->hash_version);
if (computed)
*computed |= BLOOM_TRUNC_LARGE;
goto cleanup;
}
filter->len = (hashmap_get_size(&pathmap) * settings->bits_per_entry + BITS_PER_WORD - 1) / BITS_PER_WORD;
+ filter->version = settings->hash_version;
if (!filter->len) {
if (computed)
*computed |= BLOOM_TRUNC_EMPTY;
filter->len = 1;
}
CALLOC_ARRAY(filter->data, filter->len);
+ filter->to_free = filter->data;
hashmap_for_each_entry(&pathmap, &iter, e, entry) {
struct bloom_key key;
@@ -325,7 +510,7 @@ struct bloom_filter *get_or_compute_bloom_filter(struct repository *r,
} else {
for (i = 0; i < diff_queued_diff.nr; i++)
diff_free_filepair(diff_queued_diff.queue[i]);
- init_truncated_large_filter(filter);
+ init_truncated_large_filter(filter, settings->hash_version);
if (computed)
*computed |= BLOOM_TRUNC_LARGE;
diff --git a/bloom.h b/bloom.h
index adde6dfe21..d20e64bfbb 100644
--- a/bloom.h
+++ b/bloom.h
@@ -3,13 +3,16 @@
struct commit;
struct repository;
+struct commit_graph;
struct bloom_filter_settings {
/*
* The version of the hashing technique being used.
- * We currently only support version = 1 which is
+ * The newest version is 2, which is
* the seeded murmur3 hashing technique implemented
- * in bloom.c.
+ * in bloom.c. Bloom filters of version 1 were created
+ * with prior versions of Git, which had a bug in the
+ * implementation of the hash function.
*/
uint32_t hash_version;
@@ -52,6 +55,9 @@ struct bloom_filter_settings {
struct bloom_filter {
unsigned char *data;
size_t len;
+ int version;
+
+ void *to_free;
};
/*
@@ -68,6 +74,10 @@ struct bloom_key {
uint32_t *hashes;
};
+int load_bloom_filter_from_graph(struct commit_graph *g,
+ struct bloom_filter *filter,
+ uint32_t graph_pos);
+
/*
* Calculate the murmur3 32-bit hash value for the given data
* using the given seed.
@@ -75,7 +85,7 @@ struct bloom_key {
* Not considered to be cryptographically secure.
* Implemented as described in https://en.wikipedia.org/wiki/MurmurHash#Algorithm
*/
-uint32_t murmur3_seeded(uint32_t seed, const char *data, size_t len);
+uint32_t murmur3_seeded_v2(uint32_t seed, const char *data, size_t len);
void fill_bloom_key(const char *data,
size_t len,
@@ -88,12 +98,14 @@ void add_key_to_filter(const struct bloom_key *key,
const struct bloom_filter_settings *settings);
void init_bloom_filters(void);
+void deinit_bloom_filters(void);
enum bloom_filter_computed {
BLOOM_NOT_COMPUTED = (1 << 0),
BLOOM_COMPUTED = (1 << 1),
BLOOM_TRUNC_LARGE = (1 << 2),
BLOOM_TRUNC_EMPTY = (1 << 3),
+ BLOOM_UPGRADED = (1 << 4),
};
struct bloom_filter *get_or_compute_bloom_filter(struct repository *r,
@@ -102,8 +114,24 @@ struct bloom_filter *get_or_compute_bloom_filter(struct repository *r,
const struct bloom_filter_settings *settings,
enum bloom_filter_computed *computed);
-#define get_bloom_filter(r, c) get_or_compute_bloom_filter( \
- (r), (c), 0, NULL, NULL)
+/*
+ * Find the Bloom filter associated with the given commit "c".
+ *
+ * If any of the following are true
+ *
+ * - the repository does not have a commit-graph, or
+ * - the repository disables reading from the commit-graph, or
+ * - the given commit does not have a Bloom filter computed, or
+ * - there is a Bloom filter for commit "c", but it cannot be read
+ * because the filter uses an incompatible version of murmur3
+ *
+ * , then `get_bloom_filter()` will return NULL. Otherwise, the corresponding
+ * Bloom filter will be returned.
+ *
+ * For callers who wish to inspect Bloom filters with incompatible hash
+ * versions, use get_or_compute_bloom_filter().
+ */
+struct bloom_filter *get_bloom_filter(struct repository *r, struct commit *c);
int bloom_filter_contains(const struct bloom_filter *filter,
const struct bloom_key *key,
diff --git a/branch.c b/branch.c
index e4a738fc7b..c887ea2151 100644
--- a/branch.c
+++ b/branch.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "advice.h"
#include "config.h"
@@ -377,7 +379,7 @@ int validate_branchname(const char *name, struct strbuf *ref)
exit(code);
}
- return ref_exists(ref->buf);
+ return refs_ref_exists(get_main_ref_store(the_repository), ref->buf);
}
static int initialized_checked_out_branches;
@@ -623,11 +625,12 @@ void create_branch(struct repository *r,
msg = xstrfmt("branch: Reset to %s", start_name);
else
msg = xstrfmt("branch: Created from %s", start_name);
- transaction = ref_transaction_begin(&err);
+ transaction = ref_store_transaction_begin(get_main_ref_store(the_repository),
+ &err);
if (!transaction ||
ref_transaction_update(transaction, ref.buf,
&oid, forcing ? NULL : null_oid(),
- 0, msg, &err) ||
+ NULL, NULL, 0, msg, &err) ||
ref_transaction_commit(transaction, &err))
die("%s", err.buf);
ref_transaction_free(transaction);
diff --git a/builtin.h b/builtin.h
index 28280636da..14fa017160 100644
--- a/builtin.h
+++ b/builtin.h
@@ -1,6 +1,14 @@
#ifndef BUILTIN_H
#define BUILTIN_H
+/*
+ * TODO: Almost all of our builtins access `the_repository` by necessity
+ * because they do not get passed a pointer to it. We should adapt the function
+ * signature of those main functions to accept a `struct repository *` and then
+ * remove the macro here.
+ */
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
/*
@@ -207,6 +215,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix);
int cmd_rebase__interactive(int argc, const char **argv, const char *prefix);
int cmd_receive_pack(int argc, const char **argv, const char *prefix);
int cmd_reflog(int argc, const char **argv, const char *prefix);
+int cmd_refs(int argc, const char **argv, const char *prefix);
int cmd_remote(int argc, const char **argv, const char *prefix);
int cmd_remote_ext(int argc, const char **argv, const char *prefix);
int cmd_remote_fd(int argc, const char **argv, const char *prefix);
diff --git a/builtin/add.c b/builtin/add.c
index b7d3ff1e28..40b61ef90d 100644
--- a/builtin/add.c
+++ b/builtin/add.c
@@ -3,7 +3,7 @@
*
* Copyright (C) 2006 Linus Torvalds
*/
-#define USE_THE_INDEX_VARIABLE
+
#include "builtin.h"
#include "advice.h"
#include "config.h"
@@ -40,20 +40,20 @@ static int chmod_pathspec(struct pathspec *pathspec, char flip, int show_only)
{
int i, ret = 0;
- for (i = 0; i < the_index.cache_nr; i++) {
- struct cache_entry *ce = the_index.cache[i];
+ for (i = 0; i < the_repository->index->cache_nr; i++) {
+ struct cache_entry *ce = the_repository->index->cache[i];
int err;
if (!include_sparse &&
(ce_skip_worktree(ce) ||
- !path_in_sparse_checkout(ce->name, &the_index)))
+ !path_in_sparse_checkout(ce->name, the_repository->index)))
continue;
- if (pathspec && !ce_path_match(&the_index, ce, pathspec, NULL))
+ if (pathspec && !ce_path_match(the_repository->index, ce, pathspec, NULL))
continue;
if (!show_only)
- err = chmod_index_entry(&the_index, ce, flip);
+ err = chmod_index_entry(the_repository->index, ce, flip);
else
err = S_ISREG(ce->ce_mode) ? 0 : -1;
@@ -68,20 +68,20 @@ static int renormalize_tracked_files(const struct pathspec *pathspec, int flags)
{
int i, retval = 0;
- for (i = 0; i < the_index.cache_nr; i++) {
- struct cache_entry *ce = the_index.cache[i];
+ for (i = 0; i < the_repository->index->cache_nr; i++) {
+ struct cache_entry *ce = the_repository->index->cache[i];
if (!include_sparse &&
(ce_skip_worktree(ce) ||
- !path_in_sparse_checkout(ce->name, &the_index)))
+ !path_in_sparse_checkout(ce->name, the_repository->index)))
continue;
if (ce_stage(ce))
continue; /* do not touch unmerged paths */
if (!S_ISREG(ce->ce_mode) && !S_ISLNK(ce->ce_mode))
continue; /* do not touch non blobs */
- if (pathspec && !ce_path_match(&the_index, ce, pathspec, NULL))
+ if (pathspec && !ce_path_match(the_repository->index, ce, pathspec, NULL))
continue;
- retval |= add_file_to_index(&the_index, ce->name,
+ retval |= add_file_to_index(the_repository->index, ce->name,
flags | ADD_CACHE_RENORMALIZE);
}
@@ -100,11 +100,11 @@ static char *prune_directory(struct dir_struct *dir, struct pathspec *pathspec,
i = dir->nr;
while (--i >= 0) {
struct dir_entry *entry = *src++;
- if (dir_path_match(&the_index, entry, pathspec, prefix, seen))
+ if (dir_path_match(the_repository->index, entry, pathspec, prefix, seen))
*dst++ = entry;
}
dir->nr = dst - dir->entries;
- add_pathspec_matches_against_index(pathspec, &the_index, seen,
+ add_pathspec_matches_against_index(pathspec, the_repository->index, seen,
PS_IGNORE_SKIP_WORKTREE);
return seen;
}
@@ -119,14 +119,14 @@ static int refresh(int verbose, const struct pathspec *pathspec)
(verbose ? REFRESH_IN_PORCELAIN : REFRESH_QUIET);
seen = xcalloc(pathspec->nr, 1);
- refresh_index(&the_index, flags, pathspec, seen,
+ refresh_index(the_repository->index, flags, pathspec, seen,
_("Unstaged changes after refreshing the index:"));
for (i = 0; i < pathspec->nr; i++) {
if (!seen[i]) {
const char *path = pathspec->items[i].original;
if (matches_skip_worktree(pathspec, i, &skip_worktree_seen) ||
- !path_in_sparse_checkout(path, &the_index)) {
+ !path_in_sparse_checkout(path, the_repository->index)) {
string_list_append(&only_match_skip_worktree,
pathspec->items[i].original);
} else {
@@ -150,11 +150,7 @@ static int refresh(int verbose, const struct pathspec *pathspec)
int interactive_add(const char **argv, const char *prefix, int patch)
{
struct pathspec pathspec;
- int unused, ret;
-
- if (!git_config_get_bool("add.interactive.usebuiltin", &unused))
- warning(_("the add.interactive.useBuiltin setting has been removed!\n"
- "See its entry in 'git help config' for details."));
+ int ret;
parse_pathspec(&pathspec, 0,
PATHSPEC_PREFER_FULL |
@@ -338,12 +334,12 @@ static int add_files(struct dir_struct *dir, int flags)
for (i = 0; i < dir->nr; i++) {
if (!include_sparse &&
- !path_in_sparse_checkout(dir->entries[i]->name, &the_index)) {
+ !path_in_sparse_checkout(dir->entries[i]->name, the_repository->index)) {
string_list_append(&matched_sparse_paths,
dir->entries[i]->name);
continue;
}
- if (add_file_to_index(&the_index, dir->entries[i]->name, flags)) {
+ if (add_file_to_index(the_repository->index, dir->entries[i]->name, flags)) {
if (!ignore_add_errors)
die(_("adding files failed"));
exit_status = 1;
@@ -461,8 +457,8 @@ int cmd_add(int argc, const char **argv, const char *prefix)
if (repo_read_index_preload(the_repository, &pathspec, 0) < 0)
die(_("index file corrupt"));
- die_in_unpopulated_submodule(&the_index, prefix);
- die_path_inside_submodule(&the_index, &pathspec);
+ die_in_unpopulated_submodule(the_repository->index, prefix);
+ die_path_inside_submodule(the_repository->index, &pathspec);
if (add_new_files) {
int baselen;
@@ -474,7 +470,7 @@ int cmd_add(int argc, const char **argv, const char *prefix)
}
/* This picks up the paths that are not tracked */
- baselen = fill_directory(&dir, &the_index, &pathspec);
+ baselen = fill_directory(&dir, the_repository->index, &pathspec);
if (pathspec.nr)
seen = prune_directory(&dir, &pathspec, baselen);
}
@@ -491,7 +487,7 @@ int cmd_add(int argc, const char **argv, const char *prefix)
if (!seen)
seen = find_pathspecs_matching_against_index(&pathspec,
- &the_index, PS_IGNORE_SKIP_WORKTREE);
+ the_repository->index, PS_IGNORE_SKIP_WORKTREE);
/*
* file_exists() assumes exact match
@@ -527,8 +523,8 @@ int cmd_add(int argc, const char **argv, const char *prefix)
!file_exists(path)) {
if (ignore_missing) {
int dtype = DT_UNKNOWN;
- if (is_excluded(&dir, &the_index, path, &dtype))
- dir_add_ignored(&dir, &the_index,
+ if (is_excluded(&dir, the_repository->index, path, &dtype))
+ dir_add_ignored(&dir, the_repository->index,
path, pathspec.items[i].len);
} else
die(_("pathspec '%s' did not match any files"),
@@ -569,7 +565,7 @@ int cmd_add(int argc, const char **argv, const char *prefix)
end_odb_transaction();
finish:
- if (write_locked_index(&the_index, &lock_file,
+ if (write_locked_index(the_repository->index, &lock_file,
COMMIT_LOCK | SKIP_IF_UNCHANGED))
die(_("unable to write new index file"));
diff --git a/builtin/am.c b/builtin/am.c
index 022cae2e8d..370f5593f2 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -3,7 +3,7 @@
*
* Based on git-am.sh by Junio C Hamano.
*/
-#define USE_THE_INDEX_VARIABLE
+
#include "builtin.h"
#include "abspath.h"
#include "advice.h"
@@ -408,7 +408,7 @@ static void am_load(struct am_state *state)
read_commit_msg(state);
if (read_state_file(&sb, state, "original-commit", 1) < 0)
- oidclr(&state->orig_commit);
+ oidclr(&state->orig_commit, the_repository->hash_algo);
else if (get_oid_hex(sb.buf, &state->orig_commit) < 0)
die(_("could not parse %s"), am_path(state, "original-commit"));
@@ -1001,7 +1001,8 @@ static void am_setup(struct am_state *state, enum patch_format patch_format,
if (mkdir(state->dir, 0777) < 0 && errno != EEXIST)
die_errno(_("failed to create directory '%s'"), state->dir);
- delete_ref(NULL, "REBASE_HEAD", NULL, REF_NO_DEREF);
+ refs_delete_ref(get_main_ref_store(the_repository), NULL,
+ "REBASE_HEAD", NULL, REF_NO_DEREF);
if (split_mail(state, patch_format, paths, keep_cr) < 0) {
am_destroy(state);
@@ -1081,12 +1082,15 @@ static void am_setup(struct am_state *state, enum patch_format patch_format,
if (!repo_get_oid(the_repository, "HEAD", &curr_head)) {
write_state_text(state, "abort-safety", oid_to_hex(&curr_head));
if (!state->rebasing)
- update_ref("am", "ORIG_HEAD", &curr_head, NULL, 0,
- UPDATE_REFS_DIE_ON_ERR);
+ refs_update_ref(get_main_ref_store(the_repository),
+ "am", "ORIG_HEAD", &curr_head, NULL,
+ 0,
+ UPDATE_REFS_DIE_ON_ERR);
} else {
write_state_text(state, "abort-safety", "");
if (!state->rebasing)
- delete_ref(NULL, "ORIG_HEAD", NULL, 0);
+ refs_delete_ref(get_main_ref_store(the_repository),
+ NULL, "ORIG_HEAD", NULL, 0);
}
/*
@@ -1117,9 +1121,10 @@ static void am_next(struct am_state *state)
unlink(am_path(state, "author-script"));
unlink(am_path(state, "final-commit"));
- oidclr(&state->orig_commit);
+ oidclr(&state->orig_commit, the_repository->hash_algo);
unlink(am_path(state, "original-commit"));
- delete_ref(NULL, "REBASE_HEAD", NULL, REF_NO_DEREF);
+ refs_delete_ref(get_main_ref_store(the_repository), NULL,
+ "REBASE_HEAD", NULL, REF_NO_DEREF);
if (!repo_get_oid(the_repository, "HEAD", &head))
write_state_text(state, "abort-safety", oid_to_hex(&head));
@@ -1466,8 +1471,9 @@ static int parse_mail_rebase(struct am_state *state, const char *mail)
oidcpy(&state->orig_commit, &commit_oid);
write_state_text(state, "original-commit", oid_to_hex(&commit_oid));
- update_ref("am", "REBASE_HEAD", &commit_oid,
- NULL, REF_NO_DEREF, UPDATE_REFS_DIE_ON_ERR);
+ refs_update_ref(get_main_ref_store(the_repository), "am",
+ "REBASE_HEAD", &commit_oid,
+ NULL, REF_NO_DEREF, UPDATE_REFS_DIE_ON_ERR);
return 0;
}
@@ -1536,8 +1542,8 @@ static int run_apply(const struct am_state *state, const char *index_file)
if (index_file) {
/* Reload index as apply_all_patches() will have modified it. */
- discard_index(&the_index);
- read_index_from(&the_index, index_file, get_git_dir());
+ discard_index(the_repository->index);
+ read_index_from(the_repository->index, index_file, get_git_dir());
}
return 0;
@@ -1567,8 +1573,8 @@ static int build_fake_ancestor(const struct am_state *state, const char *index_f
*/
static int fall_back_threeway(const struct am_state *state, const char *index_path)
{
- struct object_id orig_tree, their_tree, our_tree;
- const struct object_id *bases[1] = { &orig_tree };
+ struct object_id their_tree, our_tree;
+ struct object_id bases[1] = { 0 };
struct merge_options o;
struct commit *result;
char *their_tree_name;
@@ -1579,10 +1585,10 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa
if (build_fake_ancestor(state, index_path))
return error("could not build fake ancestor");
- discard_index(&the_index);
- read_index_from(&the_index, index_path, get_git_dir());
+ discard_index(the_repository->index);
+ read_index_from(the_repository->index, index_path, get_git_dir());
- if (write_index_as_tree(&orig_tree, &the_index, index_path, 0, NULL))
+ if (write_index_as_tree(&bases[0], the_repository->index, index_path, 0, NULL))
return error(_("Repository lacks necessary blobs to fall back on 3-way merge."));
say(state, stdout, _("Using index info to reconstruct a base tree..."));
@@ -1608,12 +1614,12 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa
return error(_("Did you hand edit your patch?\n"
"It does not apply to blobs recorded in its index."));
- if (write_index_as_tree(&their_tree, &the_index, index_path, 0, NULL))
+ if (write_index_as_tree(&their_tree, the_repository->index, index_path, 0, NULL))
return error("could not write tree");
say(state, stdout, _("Falling back to patching base and 3-way merge..."));
- discard_index(&the_index);
+ discard_index(the_repository->index);
repo_read_index(the_repository);
/*
@@ -1660,7 +1666,7 @@ static void do_commit(const struct am_state *state)
if (!state->no_verify && run_hooks("pre-applypatch"))
exit(1);
- if (write_index_as_tree(&tree, &the_index, get_index_file(), 0, NULL))
+ if (write_index_as_tree(&tree, the_repository->index, get_index_file(), 0, NULL))
die(_("git write-tree failed to write a tree"));
if (!repo_get_oid_commit(the_repository, "HEAD", &parent)) {
@@ -1697,8 +1703,9 @@ static void do_commit(const struct am_state *state)
strbuf_addf(&sb, "%s: %.*s", reflog_msg, linelen(state->msg),
state->msg);
- update_ref(sb.buf, "HEAD", &commit, old_oid, 0,
- UPDATE_REFS_DIE_ON_ERR);
+ refs_update_ref(get_main_ref_store(the_repository), sb.buf, "HEAD",
+ &commit, old_oid, 0,
+ UPDATE_REFS_DIE_ON_ERR);
if (state->rebasing) {
FILE *fp = xfopen(am_path(state, "rewritten"), "a");
@@ -1711,6 +1718,7 @@ static void do_commit(const struct am_state *state)
run_hooks("post-applypatch");
+ free_commit_list(parents);
strbuf_release(&sb);
}
@@ -1948,7 +1956,7 @@ static void am_resolve(struct am_state *state, int allow_empty)
}
}
- if (unmerged_index(&the_index)) {
+ if (unmerged_index(the_repository->index)) {
printf_ln(_("You still have unmerged paths in your index.\n"
"You should 'git add' each file with resolved conflicts to mark them as such.\n"
"You might run `git rm` on a file to accept \"deleted by them\" for it."));
@@ -1987,12 +1995,12 @@ static int fast_forward_to(struct tree *head, struct tree *remote, int reset)
repo_hold_locked_index(the_repository, &lock_file, LOCK_DIE_ON_ERROR);
- refresh_index(&the_index, REFRESH_QUIET, NULL, NULL, NULL);
+ refresh_index(the_repository->index, REFRESH_QUIET, NULL, NULL, NULL);
memset(&opts, 0, sizeof(opts));
opts.head_idx = 1;
- opts.src_index = &the_index;
- opts.dst_index = &the_index;
+ opts.src_index = the_repository->index;
+ opts.dst_index = the_repository->index;
opts.update = 1;
opts.merge = 1;
opts.reset = reset ? UNPACK_RESET_PROTECT_UNTRACKED : 0;
@@ -2006,7 +2014,7 @@ static int fast_forward_to(struct tree *head, struct tree *remote, int reset)
return -1;
}
- if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
+ if (write_locked_index(the_repository->index, &lock_file, COMMIT_LOCK))
die(_("unable to write new index file"));
return 0;
@@ -2029,8 +2037,8 @@ static int merge_tree(struct tree *tree)
memset(&opts, 0, sizeof(opts));
opts.head_idx = 1;
- opts.src_index = &the_index;
- opts.dst_index = &the_index;
+ opts.src_index = the_repository->index;
+ opts.dst_index = the_repository->index;
opts.merge = 1;
opts.fn = oneway_merge;
init_tree_desc(&t[0], &tree->object.oid, tree->buffer, tree->size);
@@ -2040,7 +2048,7 @@ static int merge_tree(struct tree *tree)
return -1;
}
- if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
+ if (write_locked_index(the_repository->index, &lock_file, COMMIT_LOCK))
die(_("unable to write new index file"));
return 0;
@@ -2068,7 +2076,7 @@ static int clean_index(const struct object_id *head, const struct object_id *rem
if (fast_forward_to(head_tree, head_tree, 1))
return -1;
- if (write_index_as_tree(&index, &the_index, get_index_file(), 0, NULL))
+ if (write_index_as_tree(&index, the_repository->index, get_index_file(), 0, NULL))
return -1;
index_tree = parse_tree_indirect(&index);
@@ -2144,11 +2152,11 @@ static int safe_to_abort(const struct am_state *state)
if (get_oid_hex(sb.buf, &abort_safety))
die(_("could not parse %s"), am_path(state, "abort-safety"));
} else
- oidclr(&abort_safety);
+ oidclr(&abort_safety, the_repository->hash_algo);
strbuf_release(&sb);
if (repo_get_oid(the_repository, "HEAD", &head))
- oidclr(&head);
+ oidclr(&head, the_repository->hash_algo);
if (oideq(&head, &abort_safety))
return 1;
@@ -2175,7 +2183,8 @@ static void am_abort(struct am_state *state)
am_rerere_clear();
- curr_branch = resolve_refdup("HEAD", 0, &curr_head, NULL);
+ curr_branch = refs_resolve_refdup(get_main_ref_store(the_repository),
+ "HEAD", 0, &curr_head, NULL);
has_curr_head = curr_branch && !is_null_oid(&curr_head);
if (!has_curr_head)
oidcpy(&curr_head, the_hash_algo->empty_tree);
@@ -2188,11 +2197,13 @@ static void am_abort(struct am_state *state)
die(_("failed to clean index"));
if (has_orig_head)
- update_ref("am --abort", "HEAD", &orig_head,
- has_curr_head ? &curr_head : NULL, 0,
- UPDATE_REFS_DIE_ON_ERR);
+ refs_update_ref(get_main_ref_store(the_repository),
+ "am --abort", "HEAD", &orig_head,
+ has_curr_head ? &curr_head : NULL, 0,
+ UPDATE_REFS_DIE_ON_ERR);
else if (curr_branch)
- delete_ref(NULL, curr_branch, NULL, REF_NO_DEREF);
+ refs_delete_ref(get_main_ref_store(the_repository), NULL,
+ curr_branch, NULL, REF_NO_DEREF);
free(curr_branch);
am_destroy(state);
@@ -2383,6 +2394,9 @@ int cmd_am(int argc, const char **argv, const char *prefix)
N_("show the patch being applied"),
PARSE_OPT_CMDMODE | PARSE_OPT_OPTARG | PARSE_OPT_NONEG | PARSE_OPT_LITERAL_ARGHELP,
parse_opt_show_current_patch, RESUME_SHOW_PATCH_RAW },
+ OPT_CMDMODE(0, "retry", &resume_mode,
+ N_("try to apply current patch again"),
+ RESUME_APPLY),
OPT_CMDMODE(0, "allow-empty", &resume_mode,
N_("record the empty patch as an empty commit"),
RESUME_ALLOW_EMPTY),
diff --git a/builtin/apply.c b/builtin/apply.c
index 861a01910c..d623c52f78 100644
--- a/builtin/apply.c
+++ b/builtin/apply.c
@@ -1,6 +1,7 @@
#include "builtin.h"
#include "gettext.h"
#include "repository.h"
+#include "hash.h"
#include "apply.h"
static const char * const apply_usage[] = {
@@ -18,6 +19,15 @@ int cmd_apply(int argc, const char **argv, const char *prefix)
if (init_apply_state(&state, the_repository, prefix))
exit(128);
+ /*
+ * We could to redo the "apply.c" machinery to make this
+ * arbitrary fallback unnecessary, but it is dubious that it
+ * is worth the effort.
+ * cf. https://lore.kernel.org/git/xmqqcypfcmn4.fsf@gitster.g/
+ */
+ if (!the_hash_algo)
+ repo_set_hash_algo(the_repository, GIT_HASH_SHA1);
+
argc = apply_parse_options(argc, argv,
&state, &force_apply, &options,
apply_usage);
diff --git a/builtin/archive.c b/builtin/archive.c
index 15ee1ec7bb..b50981504f 100644
--- a/builtin/archive.c
+++ b/builtin/archive.c
@@ -31,9 +31,7 @@ static int run_remote_archiver(int argc, const char **argv,
struct packet_reader reader;
_remote = remote_get(remote);
- if (!_remote->url[0])
- die(_("git archive: Remote with no URL"));
- transport = transport_get(_remote, _remote->url[0]);
+ transport = transport_get(_remote, _remote->url.v[0]);
transport_connect(transport, "git-upload-archive", exec, fd);
/*
@@ -92,6 +90,7 @@ int cmd_archive(int argc, const char **argv, const char *prefix)
N_("path to the remote git-upload-archive command")),
OPT_END()
};
+ int ret;
argc = parse_options(argc, argv, prefix, local_opts, NULL,
PARSE_OPT_KEEP_ALL);
@@ -106,6 +105,8 @@ int cmd_archive(int argc, const char **argv, const char *prefix)
setvbuf(stderr, NULL, _IOLBF, BUFSIZ);
- UNLEAK(output);
- return write_archive(argc, argv, prefix, the_repository, output, 0);
+ ret = write_archive(argc, argv, prefix, the_repository, output, 0);
+
+ free(output);
+ return ret;
}
diff --git a/builtin/bisect.c b/builtin/bisect.c
index f69c3f7e43..dabce9b542 100644
--- a/builtin/bisect.c
+++ b/builtin/bisect.c
@@ -243,7 +243,7 @@ static int bisect_reset(const char *commit)
strbuf_addstr(&branch, commit);
}
- if (branch.len && !ref_exists("BISECT_HEAD")) {
+ if (branch.len && !refs_ref_exists(get_main_ref_store(the_repository), "BISECT_HEAD")) {
struct child_process cmd = CHILD_PROCESS_INIT;
cmd.git_cmd = 1;
@@ -262,7 +262,8 @@ static int bisect_reset(const char *commit)
return bisect_clean_state();
}
-static void log_commit(FILE *fp, char *fmt, const char *state,
+static void log_commit(FILE *fp,
+ const char *fmt, const char *state,
struct commit *commit)
{
struct pretty_print_context pp = {0};
@@ -302,8 +303,8 @@ static int bisect_write(const char *state, const char *rev,
goto finish;
}
- if (update_ref(NULL, tag.buf, &oid, NULL, 0,
- UPDATE_REFS_MSG_ON_ERR)) {
+ if (refs_update_ref(get_main_ref_store(the_repository), NULL, tag.buf, &oid, NULL, 0,
+ UPDATE_REFS_MSG_ON_ERR)) {
res = -1;
goto finish;
}
@@ -416,11 +417,12 @@ static void bisect_status(struct bisect_state *state,
char *bad_ref = xstrfmt("refs/bisect/%s", terms->term_bad);
char *good_glob = xstrfmt("%s-*", terms->term_good);
- if (ref_exists(bad_ref))
+ if (refs_ref_exists(get_main_ref_store(the_repository), bad_ref))
state->nr_bad = 1;
- for_each_glob_ref_in(inc_nr, good_glob, "refs/bisect/",
- (void *) &state->nr_good);
+ refs_for_each_glob_ref_in(get_main_ref_store(the_repository), inc_nr,
+ good_glob, "refs/bisect/",
+ (void *) &state->nr_good);
free(good_glob);
free(bad_ref);
@@ -574,9 +576,11 @@ static int prepare_revs(struct bisect_terms *terms, struct rev_info *revs)
reset_revision_walk();
repo_init_revisions(the_repository, revs, NULL);
setup_revisions(0, NULL, revs, NULL);
- for_each_glob_ref_in(add_bisect_ref, bad, "refs/bisect/", &cb);
+ refs_for_each_glob_ref_in(get_main_ref_store(the_repository),
+ add_bisect_ref, bad, "refs/bisect/", &cb);
cb.object_flags = UNINTERESTING;
- for_each_glob_ref_in(add_bisect_ref, good, "refs/bisect/", &cb);
+ refs_for_each_glob_ref_in(get_main_ref_store(the_repository),
+ add_bisect_ref, good, "refs/bisect/", &cb);
if (prepare_revision_walk(revs))
res = error(_("revision walk setup failed\n"));
@@ -636,7 +640,7 @@ static int bisect_successful(struct bisect_terms *terms)
char *bad_ref = xstrfmt("refs/bisect/%s",terms->term_bad);
int res;
- read_ref(bad_ref, &oid);
+ refs_read_ref(get_main_ref_store(the_repository), bad_ref, &oid);
commit = lookup_commit_reference_by_name(bad_ref);
repo_format_commit_message(the_repository, commit, "%s", &commit_name,
&pp);
@@ -779,7 +783,8 @@ static enum bisect_error bisect_start(struct bisect_terms *terms, int argc,
/*
* Verify HEAD
*/
- head = resolve_ref_unsafe("HEAD", 0, &head_oid, &flags);
+ head = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
+ "HEAD", 0, &head_oid, &flags);
if (!head)
if (repo_get_oid(the_repository, "HEAD", &head_oid))
return error(_("bad HEAD - I need a HEAD"));
@@ -838,8 +843,8 @@ static enum bisect_error bisect_start(struct bisect_terms *terms, int argc,
res = error(_("invalid ref: '%s'"), start_head.buf);
goto finish;
}
- if (update_ref(NULL, "BISECT_HEAD", &oid, NULL, 0,
- UPDATE_REFS_MSG_ON_ERR)) {
+ if (refs_update_ref(get_main_ref_store(the_repository), NULL, "BISECT_HEAD", &oid, NULL, 0,
+ UPDATE_REFS_MSG_ON_ERR)) {
res = BISECT_FAILED;
goto finish;
}
@@ -972,7 +977,7 @@ static enum bisect_error bisect_state(struct bisect_terms *terms, int argc,
oid_array_append(&revs, &commit->object.oid);
}
- if (read_ref("BISECT_EXPECTED_REV", &expected))
+ if (refs_read_ref(get_main_ref_store(the_repository), "BISECT_EXPECTED_REV", &expected))
verify_expected = 0; /* Ignore invalid file contents */
for (i = 0; i < revs.nr; i++) {
@@ -982,7 +987,9 @@ static enum bisect_error bisect_state(struct bisect_terms *terms, int argc,
}
if (verify_expected && !oideq(&revs.oid[i], &expected)) {
unlink_or_warn(git_path_bisect_ancestors_ok());
- delete_ref(NULL, "BISECT_EXPECTED_REV", NULL, REF_NO_DEREF);
+ refs_delete_ref(get_main_ref_store(the_repository),
+ NULL, "BISECT_EXPECTED_REV", NULL,
+ REF_NO_DEREF);
verify_expected = 0;
}
}
@@ -1179,13 +1186,15 @@ static int verify_good(const struct bisect_terms *terms, const char *command)
struct object_id good_rev;
struct object_id current_rev;
char *good_glob = xstrfmt("%s-*", terms->term_good);
- int no_checkout = ref_exists("BISECT_HEAD");
+ int no_checkout = refs_ref_exists(get_main_ref_store(the_repository),
+ "BISECT_HEAD");
- for_each_glob_ref_in(get_first_good, good_glob, "refs/bisect/",
- &good_rev);
+ refs_for_each_glob_ref_in(get_main_ref_store(the_repository),
+ get_first_good, good_glob, "refs/bisect/",
+ &good_rev);
free(good_glob);
- if (read_ref(no_checkout ? "BISECT_HEAD" : "HEAD", &current_rev))
+ if (refs_read_ref(get_main_ref_store(the_repository), no_checkout ? "BISECT_HEAD" : "HEAD", &current_rev))
return -1;
res = bisect_checkout(&good_rev, no_checkout);
diff --git a/builtin/blame.c b/builtin/blame.c
index 9aa74680a3..35e975fb13 100644
--- a/builtin/blame.c
+++ b/builtin/blame.c
@@ -5,7 +5,7 @@
* See COPYING for licensing conditions
*/
-#include "git-compat-util.h"
+#include "builtin.h"
#include "config.h"
#include "color.h"
#include "builtin.h"
@@ -67,7 +67,7 @@ static int no_whole_file_rename;
static int show_progress;
static char repeated_meta_color[COLOR_MAXLEN];
static int coloring_mode;
-static struct string_list ignore_revs_file_list = STRING_LIST_INIT_NODUP;
+static struct string_list ignore_revs_file_list = STRING_LIST_INIT_DUP;
static int mark_unblamable_lines;
static int mark_ignored_lines;
@@ -134,7 +134,7 @@ static void get_ac_line(const char *inbuf, const char *what,
{
struct ident_split ident;
size_t len, maillen, namelen;
- char *tmp, *endp;
+ const char *tmp, *endp;
const char *namebuf, *mailbuf;
tmp = strstr(inbuf, what);
@@ -687,7 +687,7 @@ static unsigned parse_score(const char *arg)
return score;
}
-static const char *add_prefix(const char *prefix, const char *path)
+static char *add_prefix(const char *prefix, const char *path)
{
return prefix_path(prefix, prefix ? strlen(prefix) : 0, path);
}
@@ -718,13 +718,14 @@ static int git_blame_config(const char *var, const char *value,
return 0;
}
if (!strcmp(var, "blame.ignorerevsfile")) {
- const char *str;
+ char *str;
int ret;
ret = git_config_pathname(&str, var, value);
if (ret)
return ret;
string_list_insert(&ignore_revs_file_list, str);
+ free(str);
return 0;
}
if (!strcmp(var, "blame.markunblamablelines")) {
@@ -852,6 +853,7 @@ static void build_ignorelist(struct blame_scoreboard *sb,
oidset_clear(&sb->ignore_list);
else
oidset_parse_file_carefully(&sb->ignore_list, i->string,
+ the_repository->hash_algo,
peel_to_commit_oid, sb);
}
for_each_string_list_item(i, ignore_rev_list) {
@@ -865,7 +867,7 @@ static void build_ignorelist(struct blame_scoreboard *sb,
int cmd_blame(int argc, const char **argv, const char *prefix)
{
struct rev_info revs;
- const char *path;
+ char *path = NULL;
struct blame_scoreboard sb;
struct blame_origin *o;
struct blame_entry *ent = NULL;
@@ -915,7 +917,6 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
struct range_set ranges;
unsigned int range_i;
long anchor;
- const int hexsz = the_hash_algo->hexsz;
long num_lines = 0;
const char *str_usage = cmd_is_annotate ? annotate_usage : blame_usage;
const char **opt_usage = cmd_is_annotate ? annotate_opt_usage : blame_opt_usage;
@@ -973,11 +974,11 @@ parse_done:
} else if (show_progress < 0)
show_progress = isatty(2);
- if (0 < abbrev && abbrev < hexsz)
+ if (0 < abbrev && abbrev < (int)the_hash_algo->hexsz)
/* one more abbrev length is needed for the boundary commit */
abbrev++;
else if (!abbrev)
- abbrev = hexsz;
+ abbrev = the_hash_algo->hexsz;
if (revs_file && read_ancestry(revs_file))
die_errno("reading graft file '%s' failed", revs_file);
@@ -1093,8 +1094,8 @@ parse_done:
struct commit *head_commit;
struct object_id head_oid;
- if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING,
- &head_oid, NULL) ||
+ if (!refs_resolve_ref_unsafe(get_main_ref_store(the_repository), "HEAD", RESOLVE_REF_READING,
+ &head_oid, NULL) ||
!(head_commit = lookup_commit_reference_gently(revs.repo,
&head_oid, 1)))
die("no such ref: HEAD");
@@ -1227,6 +1228,7 @@ parse_done:
}
cleanup:
+ free(path);
cleanup_scoreboard(&sb);
release_revisions(&revs);
return 0;
diff --git a/builtin/branch.c b/builtin/branch.c
index dd3e3a7dc0..48cac74f97 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -148,8 +148,8 @@ static int branch_merged(int kind, const char *name,
if (upstream &&
(reference_name = reference_name_to_free =
- resolve_refdup(upstream, RESOLVE_REF_READING,
- &oid, NULL)) != NULL)
+ refs_resolve_refdup(get_main_ref_store(the_repository), upstream, RESOLVE_REF_READING,
+ &oid, NULL)) != NULL)
reference_rev = lookup_commit_reference(the_repository,
&oid);
}
@@ -272,21 +272,24 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
}
}
- target = resolve_refdup(name,
- RESOLVE_REF_READING
- | RESOLVE_REF_NO_RECURSE
- | RESOLVE_REF_ALLOW_BAD_NAME,
- &oid, &flags);
+ target = refs_resolve_refdup(get_main_ref_store(the_repository),
+ name,
+ RESOLVE_REF_READING
+ | RESOLVE_REF_NO_RECURSE
+ | RESOLVE_REF_ALLOW_BAD_NAME,
+ &oid, &flags);
if (!target) {
if (remote_branch) {
error(_("remote-tracking branch '%s' not found"), bname.buf);
} else {
char *virtual_name = mkpathdup(fmt_remotes, bname.buf);
- char *virtual_target = resolve_refdup(virtual_name,
- RESOLVE_REF_READING
- | RESOLVE_REF_NO_RECURSE
- | RESOLVE_REF_ALLOW_BAD_NAME,
- &oid, &flags);
+ char *virtual_target = refs_resolve_refdup(get_main_ref_store(the_repository),
+ virtual_name,
+ RESOLVE_REF_READING
+ | RESOLVE_REF_NO_RECURSE
+ | RESOLVE_REF_ALLOW_BAD_NAME,
+ &oid,
+ &flags);
FREE_AND_NULL(virtual_name);
if (virtual_target)
@@ -317,13 +320,13 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
free(target);
}
- if (delete_refs(NULL, &refs_to_delete, REF_NO_DEREF))
+ if (refs_delete_refs(get_main_ref_store(the_repository), NULL, &refs_to_delete, REF_NO_DEREF))
ret = 1;
for_each_string_list_item(item, &refs_to_delete) {
char *describe_ref = item->util;
char *name = item->string;
- if (!ref_exists(name)) {
+ if (!refs_ref_exists(get_main_ref_store(the_repository), name)) {
char *refname = name + branch_name_pos;
if (!quiet)
printf(remote_branch
@@ -499,7 +502,8 @@ static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sortin
static void print_current_branch_name(void)
{
int flags;
- const char *refname = resolve_ref_unsafe("HEAD", 0, NULL, &flags);
+ const char *refname = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
+ "HEAD", 0, NULL, &flags);
const char *shortname;
if (!refname)
die(_("could not resolve HEAD"));
@@ -555,7 +559,7 @@ static int replace_each_worktree_head_symref(struct worktree **worktrees,
continue;
refs = get_worktree_ref_store(worktrees[i]);
- if (refs_create_symref(refs, "HEAD", newref, logmsg))
+ if (refs_update_symref(refs, "HEAD", newref, logmsg))
ret = error(_("HEAD of working tree %s is not updated"),
worktrees[i]->path);
}
@@ -580,7 +584,7 @@ static void copy_or_rename_branch(const char *oldname, const char *newname, int
* Bad name --- this could be an attempt to rename a
* ref that we used to allow to be created by accident.
*/
- if (ref_exists(oldref.buf))
+ if (refs_ref_exists(get_main_ref_store(the_repository), oldref.buf))
recovery = 1;
else {
int code = die_message(_("invalid branch name: '%s'"), oldname);
@@ -601,7 +605,7 @@ static void copy_or_rename_branch(const char *oldname, const char *newname, int
}
}
- if ((copy || !(oldref_usage & IS_HEAD)) && !ref_exists(oldref.buf)) {
+ if ((copy || !(oldref_usage & IS_HEAD)) && !refs_ref_exists(get_main_ref_store(the_repository), oldref.buf)) {
if (oldref_usage & IS_HEAD)
die(_("no commit on branch '%s' yet"), oldname);
else
@@ -632,9 +636,9 @@ static void copy_or_rename_branch(const char *oldname, const char *newname, int
oldref.buf, newref.buf);
if (!copy && !(oldref_usage & IS_ORPHAN) &&
- rename_ref(oldref.buf, newref.buf, logmsg.buf))
+ refs_rename_ref(get_main_ref_store(the_repository), oldref.buf, newref.buf, logmsg.buf))
die(_("branch rename failed"));
- if (copy && copy_existing_ref(oldref.buf, newref.buf, logmsg.buf))
+ if (copy && refs_copy_existing_ref(get_main_ref_store(the_repository), oldref.buf, newref.buf, logmsg.buf))
die(_("branch copy failed"));
if (recovery) {
@@ -786,7 +790,8 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
track = git_branch_track;
- head = resolve_refdup("HEAD", 0, &head_oid, NULL);
+ head = refs_resolve_refdup(get_main_ref_store(the_repository), "HEAD",
+ 0, &head_oid, NULL);
if (!head)
die(_("failed to resolve HEAD as a valid ref"));
if (!strcmp(head, "HEAD"))
@@ -891,7 +896,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))
+ if (!refs_ref_exists(get_main_ref_store(the_repository), branch_ref.buf))
error((!argc || branch_checked_out(branch_ref.buf))
? _("no commit on branch '%s' yet")
: _("no branch named '%s'"),
@@ -936,7 +941,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
die(_("no such branch '%s'"), argv[0]);
}
- if (!ref_exists(branch->refname)) {
+ if (!refs_ref_exists(get_main_ref_store(the_repository), branch->refname)) {
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/bugreport.c b/builtin/bugreport.c
index 25f860a0d9..b3cc77af53 100644
--- a/builtin/bugreport.c
+++ b/builtin/bugreport.c
@@ -107,7 +107,7 @@ int cmd_bugreport(int argc, const char **argv, const char *prefix)
struct tm tm;
enum diagnose_mode diagnose = DIAGNOSE_NONE;
char *option_output = NULL;
- char *option_suffix = "%Y-%m-%d-%H%M";
+ const char *option_suffix = "%Y-%m-%d-%H%M";
const char *user_relative_path = NULL;
char *prefixed_filename;
size_t output_path_len;
diff --git a/builtin/bundle.c b/builtin/bundle.c
index 3ad11dc5d0..d5d41a8f67 100644
--- a/builtin/bundle.c
+++ b/builtin/bundle.c
@@ -140,6 +140,11 @@ static int cmd_bundle_verify(int argc, const char **argv, const char *prefix) {
builtin_bundle_verify_usage, options, &bundle_file);
/* bundle internals use argv[1] as further parameters */
+ if (!startup_info->have_repository) {
+ ret = error(_("need a repository to verify a bundle"));
+ goto cleanup;
+ }
+
if ((bundle_fd = open_bundle(bundle_file, &header, &name)) < 0) {
ret = 1;
goto cleanup;
diff --git a/builtin/cat-file.c b/builtin/cat-file.c
index 0c948f40fb..18fe58d6b8 100644
--- a/builtin/cat-file.c
+++ b/builtin/cat-file.c
@@ -3,7 +3,7 @@
*
* Copyright (C) Linus Torvalds, 2005
*/
-#define USE_THE_INDEX_VARIABLE
+
#include "builtin.h"
#include "config.h"
#include "convert.h"
@@ -77,7 +77,7 @@ static int filter_object(const char *path, unsigned mode,
struct checkout_metadata meta;
init_checkout_metadata(&meta, NULL, NULL, oid);
- if (convert_to_working_tree(&the_index, path, *buf, *size, &strbuf, &meta)) {
+ if (convert_to_working_tree(the_repository->index, path, *buf, *size, &strbuf, &meta)) {
free(*buf);
*size = strbuf.len;
*buf = strbuf_detach(&strbuf, NULL);
@@ -102,7 +102,7 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
enum object_type type;
char *buf;
unsigned long size;
- struct object_context obj_context;
+ struct object_context obj_context = {0};
struct object_info oi = OBJECT_INFO_INIT;
struct strbuf sb = STRBUF_INIT;
unsigned flags = OBJECT_INFO_LOOKUP_REPLACE;
@@ -163,7 +163,8 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
goto cleanup;
case 'e':
- return !repo_has_object_file(the_repository, &oid);
+ ret = !repo_has_object_file(the_repository, &oid);
+ goto cleanup;
case 'w':
@@ -268,7 +269,7 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
ret = 0;
cleanup:
free(buf);
- free(obj_context.path);
+ object_context_release(&obj_context);
return ret;
}
@@ -520,7 +521,7 @@ static void batch_one_object(const char *obj_name,
struct batch_options *opt,
struct expand_data *data)
{
- struct object_context ctx;
+ struct object_context ctx = {0};
int flags =
GET_OID_HASH_ANY |
(opt->follow_symlinks ? GET_OID_FOLLOW_SYMLINKS : 0);
@@ -557,7 +558,8 @@ static void batch_one_object(const char *obj_name,
break;
}
fflush(stdout);
- return;
+
+ goto out;
}
if (ctx.mode == 0) {
@@ -565,10 +567,13 @@ static void batch_one_object(const char *obj_name,
(uintmax_t)ctx.symlink_path.len,
opt->output_delim, ctx.symlink_path.buf, opt->output_delim);
fflush(stdout);
- return;
+ goto out;
}
batch_object_write(obj_name, scratch, opt, data, NULL, 0);
+
+out:
+ object_context_release(&ctx);
}
struct object_cb_data {
diff --git a/builtin/check-attr.c b/builtin/check-attr.c
index c1da1d184e..9376810710 100644
--- a/builtin/check-attr.c
+++ b/builtin/check-attr.c
@@ -1,4 +1,3 @@
-#define USE_THE_INDEX_VARIABLE
#include "builtin.h"
#include "config.h"
#include "attr.h"
@@ -71,9 +70,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, full_path, check);
+ git_all_attrs(the_repository->index, full_path, check);
} else {
- git_check_attr(&the_index, full_path, check);
+ git_check_attr(the_repository->index, full_path, check);
}
output_attr(check, file);
diff --git a/builtin/check-ignore.c b/builtin/check-ignore.c
index 906cd96753..2bda6a1d46 100644
--- a/builtin/check-ignore.c
+++ b/builtin/check-ignore.c
@@ -1,4 +1,3 @@
-#define USE_THE_INDEX_VARIABLE
#include "builtin.h"
#include "config.h"
#include "dir.h"
@@ -36,8 +35,8 @@ static const struct option check_ignore_options[] = {
static void output_pattern(const char *path, struct path_pattern *pattern)
{
- char *bang = (pattern && pattern->flags & PATTERN_FLAG_NEGATIVE) ? "!" : "";
- char *slash = (pattern && pattern->flags & PATTERN_FLAG_MUSTBEDIR) ? "/" : "";
+ const char *bang = (pattern && pattern->flags & PATTERN_FLAG_NEGATIVE) ? "!" : "";
+ const char *slash = (pattern && pattern->flags & PATTERN_FLAG_MUSTBEDIR) ? "/" : "";
if (!nul_term_line) {
if (!verbose) {
write_name_quoted(path, stdout, '\n');
@@ -95,21 +94,21 @@ static int check_ignore(struct dir_struct *dir,
PATHSPEC_KEEP_ORDER,
prefix, argv);
- die_path_inside_submodule(&the_index, &pathspec);
+ die_path_inside_submodule(the_repository->index, &pathspec);
/*
* look for pathspecs matching entries in the index, since these
* should not be ignored, in order to be consistent with
* 'git status', 'git add' etc.
*/
- seen = find_pathspecs_matching_against_index(&pathspec, &the_index,
+ seen = find_pathspecs_matching_against_index(&pathspec, the_repository->index,
PS_HEED_SKIP_WORKTREE);
for (i = 0; i < pathspec.nr; i++) {
full_path = pathspec.items[i].match;
pattern = NULL;
if (!seen[i]) {
int dtype = DT_UNKNOWN;
- pattern = last_matching_pattern(dir, &the_index,
+ pattern = last_matching_pattern(dir, the_repository->index,
full_path, &dtype);
if (!verbose && pattern &&
pattern->flags & PATTERN_FLAG_NEGATIVE)
diff --git a/builtin/checkout-index.c b/builtin/checkout-index.c
index 2e086a204d..29e744d11b 100644
--- a/builtin/checkout-index.c
+++ b/builtin/checkout-index.c
@@ -4,7 +4,7 @@
* Copyright (C) 2005 Linus Torvalds
*
*/
-#define USE_THE_INDEX_VARIABLE
+
#include "builtin.h"
#include "config.h"
#include "gettext.h"
@@ -69,7 +69,7 @@ static void write_tempfile_record(const char *name, const char *prefix)
static int checkout_file(const char *name, const char *prefix)
{
int namelen = strlen(name);
- int pos = index_name_pos(&the_index, name, namelen);
+ int pos = index_name_pos(the_repository->index, name, namelen);
int has_same_name = 0;
int is_file = 0;
int is_skipped = 1;
@@ -79,8 +79,8 @@ static int checkout_file(const char *name, const char *prefix)
if (pos < 0)
pos = -pos - 1;
- while (pos < the_index.cache_nr) {
- struct cache_entry *ce = the_index.cache[pos];
+ while (pos <the_repository->index->cache_nr) {
+ struct cache_entry *ce =the_repository->index->cache[pos];
if (ce_namelen(ce) != namelen ||
memcmp(ce->name, name, namelen))
break;
@@ -140,8 +140,8 @@ static int checkout_all(const char *prefix, int prefix_length)
int i, errs = 0;
struct cache_entry *last_ce = NULL;
- for (i = 0; i < the_index.cache_nr ; i++) {
- struct cache_entry *ce = the_index.cache[i];
+ for (i = 0; i < the_repository->index->cache_nr ; i++) {
+ struct cache_entry *ce = the_repository->index->cache[i];
if (S_ISSPARSEDIR(ce->ce_mode)) {
if (!ce_skip_worktree(ce))
@@ -154,8 +154,8 @@ static int checkout_all(const char *prefix, int prefix_length)
* first entry inside the expanded sparse directory).
*/
if (ignore_skip_worktree) {
- ensure_full_index(&the_index);
- ce = the_index.cache[i];
+ ensure_full_index(the_repository->index);
+ ce = the_repository->index->cache[i];
}
}
@@ -260,7 +260,7 @@ int cmd_checkout_index(int argc, const char **argv, const char *prefix)
argc = parse_options(argc, argv, prefix, builtin_checkout_index_options,
builtin_checkout_index_usage, 0);
- state.istate = &the_index;
+ state.istate = the_repository->index;
state.force = force;
state.quiet = quiet;
state.not_new = not_new;
@@ -280,7 +280,7 @@ int cmd_checkout_index(int argc, const char **argv, const char *prefix)
*/
if (index_opt && !state.base_dir_len && !to_tempfile) {
state.refresh_cache = 1;
- state.istate = &the_index;
+ state.istate = the_repository->index;
repo_hold_locked_index(the_repository, &lock_file,
LOCK_DIE_ON_ERROR);
}
@@ -339,7 +339,7 @@ int cmd_checkout_index(int argc, const char **argv, const char *prefix)
return 1;
if (is_lock_file_locked(&lock_file) &&
- write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
+ write_locked_index(the_repository->index, &lock_file, COMMIT_LOCK))
die("Unable to write new index file");
return 0;
}
diff --git a/builtin/checkout.c b/builtin/checkout.c
index 71e6036aab..3cf44b4683 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -1,4 +1,3 @@
-#define USE_THE_INDEX_VARIABLE
#include "builtin.h"
#include "advice.h"
#include "branch.h"
@@ -146,7 +145,7 @@ static int update_some(const struct object_id *oid, struct strbuf *base,
return READ_TREE_RECURSIVE;
len = base->len + strlen(pathname);
- ce = make_empty_cache_entry(&the_index, len);
+ ce = make_empty_cache_entry(the_repository->index, len);
oidcpy(&ce->oid, oid);
memcpy(ce->name, base->buf, base->len);
memcpy(ce->name + base->len, pathname, len - base->len);
@@ -159,9 +158,9 @@ static int update_some(const struct object_id *oid, struct strbuf *base,
* entry in place. Whether it is UPTODATE or not, checkout_entry will
* do the right thing.
*/
- pos = index_name_pos(&the_index, ce->name, ce->ce_namelen);
+ pos = index_name_pos(the_repository->index, ce->name, ce->ce_namelen);
if (pos >= 0) {
- struct cache_entry *old = the_index.cache[pos];
+ struct cache_entry *old = the_repository->index->cache[pos];
if (ce->ce_mode == old->ce_mode &&
!ce_intent_to_add(old) &&
oideq(&ce->oid, &old->oid)) {
@@ -171,7 +170,7 @@ static int update_some(const struct object_id *oid, struct strbuf *base,
}
}
- add_index_entry(&the_index, ce,
+ add_index_entry(the_repository->index, ce,
ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE);
return 0;
}
@@ -190,8 +189,8 @@ static int read_tree_some(struct tree *tree, const struct pathspec *pathspec)
static int skip_same_name(const struct cache_entry *ce, int pos)
{
- while (++pos < the_index.cache_nr &&
- !strcmp(the_index.cache[pos]->name, ce->name))
+ while (++pos < the_repository->index->cache_nr &&
+ !strcmp(the_repository->index->cache[pos]->name, ce->name))
; /* skip */
return pos;
}
@@ -199,9 +198,9 @@ static int skip_same_name(const struct cache_entry *ce, int pos)
static int check_stage(int stage, const struct cache_entry *ce, int pos,
int overlay_mode)
{
- while (pos < the_index.cache_nr &&
- !strcmp(the_index.cache[pos]->name, ce->name)) {
- if (ce_stage(the_index.cache[pos]) == stage)
+ while (pos < the_repository->index->cache_nr &&
+ !strcmp(the_repository->index->cache[pos]->name, ce->name)) {
+ if (ce_stage(the_repository->index->cache[pos]) == stage)
return 0;
pos++;
}
@@ -218,8 +217,8 @@ static int check_stages(unsigned stages, const struct cache_entry *ce, int pos)
unsigned seen = 0;
const char *name = ce->name;
- while (pos < the_index.cache_nr) {
- ce = the_index.cache[pos];
+ while (pos < the_repository->index->cache_nr) {
+ ce = the_repository->index->cache[pos];
if (strcmp(name, ce->name))
break;
seen |= (1 << ce_stage(ce));
@@ -235,10 +234,10 @@ static int checkout_stage(int stage, const struct cache_entry *ce, int pos,
const struct checkout *state, int *nr_checkouts,
int overlay_mode)
{
- while (pos < the_index.cache_nr &&
- !strcmp(the_index.cache[pos]->name, ce->name)) {
- if (ce_stage(the_index.cache[pos]) == stage)
- return checkout_entry(the_index.cache[pos], state,
+ while (pos < the_repository->index->cache_nr &&
+ !strcmp(the_repository->index->cache[pos]->name, ce->name)) {
+ if (ce_stage(the_repository->index->cache[pos]) == stage)
+ return checkout_entry(the_repository->index->cache[pos], state,
NULL, nr_checkouts);
pos++;
}
@@ -256,7 +255,7 @@ static int checkout_merged(int pos, const struct checkout *state,
int *nr_checkouts, struct mem_pool *ce_mem_pool,
int conflict_style)
{
- struct cache_entry *ce = the_index.cache[pos];
+ struct cache_entry *ce = the_repository->index->cache[pos];
const char *path = ce->name;
mmfile_t ancestor, ours, theirs;
enum ll_merge_result merge_status;
@@ -269,7 +268,7 @@ static int checkout_merged(int pos, const struct checkout *state,
int renormalize = 0;
memset(threeway, 0, sizeof(threeway));
- while (pos < the_index.cache_nr) {
+ while (pos < the_repository->index->cache_nr) {
int stage;
stage = ce_stage(ce);
if (!stage || strcmp(path, ce->name))
@@ -278,7 +277,7 @@ static int checkout_merged(int pos, const struct checkout *state,
if (stage == 2)
mode = create_ce_mode(ce->ce_mode);
pos++;
- ce = the_index.cache[pos];
+ ce = the_repository->index->cache[pos];
}
if (is_null_oid(&threeway[1]) || is_null_oid(&threeway[2]))
return error(_("path '%s' does not have necessary versions"), path);
@@ -356,7 +355,7 @@ static void mark_ce_for_checkout_overlay(struct cache_entry *ce,
* match_pathspec() for _all_ entries when
* opts->source_tree != NULL.
*/
- if (ce_path_match(&the_index, ce, &opts->pathspec, ps_matched))
+ if (ce_path_match(the_repository->index, ce, &opts->pathspec, ps_matched))
ce->ce_flags |= CE_MATCHED;
}
@@ -367,7 +366,7 @@ static void mark_ce_for_checkout_no_overlay(struct cache_entry *ce,
ce->ce_flags &= ~CE_MATCHED;
if (!opts->ignore_skipworktree && ce_skip_worktree(ce))
return;
- if (ce_path_match(&the_index, ce, &opts->pathspec, ps_matched)) {
+ if (ce_path_match(the_repository->index, ce, &opts->pathspec, ps_matched)) {
ce->ce_flags |= CE_MATCHED;
if (opts->source_tree && !(ce->ce_flags & CE_UPDATE))
/*
@@ -391,7 +390,7 @@ static int checkout_worktree(const struct checkout_opts *opts,
state.force = 1;
state.refresh_cache = 1;
- state.istate = &the_index;
+ state.istate = the_repository->index;
mem_pool_init(&ce_mem_pool, 0);
get_parallel_checkout_configs(&pc_workers, &pc_threshold);
@@ -404,8 +403,8 @@ static int checkout_worktree(const struct checkout_opts *opts,
if (pc_workers > 1)
init_parallel_checkout();
- for (pos = 0; pos < the_index.cache_nr; pos++) {
- struct cache_entry *ce = the_index.cache[pos];
+ for (pos = 0; pos < the_repository->index->cache_nr; pos++) {
+ struct cache_entry *ce = the_repository->index->cache[pos];
if (ce->ce_flags & CE_MATCHED) {
if (!ce_stage(ce)) {
errs |= checkout_entry(ce, &state,
@@ -429,7 +428,7 @@ static int checkout_worktree(const struct checkout_opts *opts,
errs |= run_parallel_checkout(&state, pc_workers, pc_threshold,
NULL, NULL);
mem_pool_discard(&ce_mem_pool, should_validate_cache_entries());
- remove_marked_cache_entries(&the_index, 1);
+ remove_marked_cache_entries(the_repository->index, 1);
remove_scheduled_dirs();
errs |= finish_delayed_checkout(&state, opts->show_progress);
@@ -571,7 +570,7 @@ static int checkout_paths(const struct checkout_opts *opts,
if (opts->source_tree)
read_tree_some(opts->source_tree, &opts->pathspec);
if (opts->merge)
- unmerge_index(&the_index, &opts->pathspec, CE_MATCHED);
+ unmerge_index(the_repository->index, &opts->pathspec, CE_MATCHED);
ps_matched = xcalloc(opts->pathspec.nr, 1);
@@ -579,13 +578,13 @@ static int checkout_paths(const struct checkout_opts *opts,
* Make sure all pathspecs participated in locating the paths
* to be checked out.
*/
- for (pos = 0; pos < the_index.cache_nr; pos++)
+ for (pos = 0; pos < the_repository->index->cache_nr; pos++)
if (opts->overlay_mode)
- mark_ce_for_checkout_overlay(the_index.cache[pos],
+ mark_ce_for_checkout_overlay(the_repository->index->cache[pos],
ps_matched,
opts);
else
- mark_ce_for_checkout_no_overlay(the_index.cache[pos],
+ mark_ce_for_checkout_no_overlay(the_repository->index->cache[pos],
ps_matched,
opts);
@@ -596,8 +595,8 @@ static int checkout_paths(const struct checkout_opts *opts,
free(ps_matched);
/* Any unmerged paths? */
- for (pos = 0; pos < the_index.cache_nr; pos++) {
- const struct cache_entry *ce = the_index.cache[pos];
+ for (pos = 0; pos < the_repository->index->cache_nr; pos++) {
+ const struct cache_entry *ce = the_repository->index->cache[pos];
if (ce->ce_flags & CE_MATCHED) {
if (!ce_stage(ce))
continue;
@@ -622,7 +621,7 @@ static int checkout_paths(const struct checkout_opts *opts,
if (opts->checkout_worktree)
errs |= checkout_worktree(opts, new_branch_info);
else
- remove_marked_cache_entries(&the_index, 1);
+ remove_marked_cache_entries(the_repository->index, 1);
/*
* Allow updating the index when checking out from the index.
@@ -634,7 +633,7 @@ static int checkout_paths(const struct checkout_opts *opts,
checkout_index = opts->checkout_index;
if (checkout_index) {
- if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
+ if (write_locked_index(the_repository->index, &lock_file, COMMIT_LOCK))
die(_("unable to write new index file"));
} else {
/*
@@ -646,7 +645,8 @@ static int checkout_paths(const struct checkout_opts *opts,
rollback_lock_file(&lock_file);
}
- read_ref_full("HEAD", 0, &rev, NULL);
+ refs_read_ref_full(get_main_ref_store(the_repository), "HEAD", 0,
+ &rev, NULL);
head = lookup_commit_reference_gently(the_repository, &rev, 1);
errs |= post_checkout_hook(head, head, 0);
@@ -703,8 +703,8 @@ static int reset_tree(struct tree *tree, const struct checkout_opts *o,
opts.merge = 1;
opts.fn = oneway_merge;
opts.verbose_update = o->show_progress;
- opts.src_index = &the_index;
- opts.dst_index = &the_index;
+ opts.src_index = the_repository->index;
+ opts.dst_index = the_repository->index;
init_checkout_metadata(&opts.meta, info->refname,
info->commit ? &info->commit->object.oid : null_oid(),
NULL);
@@ -756,12 +756,12 @@ static void init_topts(struct unpack_trees_options *topts, int merge,
{
memset(topts, 0, sizeof(*topts));
topts->head_idx = -1;
- topts->src_index = &the_index;
- topts->dst_index = &the_index;
+ topts->src_index = the_repository->index;
+ topts->dst_index = the_repository->index;
setup_unpack_trees_porcelain(topts, "checkout");
- topts->initial_checkout = is_index_unborn(&the_index);
+ topts->initial_checkout = is_index_unborn(the_repository->index);
topts->update = 1;
topts->merge = 1;
topts->quiet = merge && old_commit;
@@ -783,7 +783,7 @@ static int merge_working_tree(const struct checkout_opts *opts,
if (repo_read_index_preload(the_repository, NULL, 0) < 0)
return error(_("index file corrupt"));
- resolve_undo_clear_index(&the_index);
+ resolve_undo_clear_index(the_repository->index);
if (opts->new_orphan_branch && opts->orphan_from_empty_tree) {
if (new_branch_info->commit)
BUG("'switch --orphan' should never accept a commit as starting point");
@@ -807,9 +807,9 @@ static int merge_working_tree(const struct checkout_opts *opts,
struct unpack_trees_options topts;
const struct object_id *old_commit_oid;
- refresh_index(&the_index, REFRESH_QUIET, NULL, NULL, NULL);
+ refresh_index(the_repository->index, REFRESH_QUIET, NULL, NULL, NULL);
- if (unmerged_index(&the_index)) {
+ if (unmerged_index(the_repository->index)) {
error(_("you need to resolve your current index first"));
return 1;
}
@@ -919,10 +919,10 @@ static int merge_working_tree(const struct checkout_opts *opts,
}
}
- if (!cache_tree_fully_valid(the_index.cache_tree))
- cache_tree_update(&the_index, WRITE_TREE_SILENT | WRITE_TREE_REPAIR);
+ if (!cache_tree_fully_valid(the_repository->index->cache_tree))
+ cache_tree_update(the_repository->index, WRITE_TREE_SILENT | WRITE_TREE_REPAIR);
- if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
+ if (write_locked_index(the_repository->index, &lock_file, COMMIT_LOCK))
die(_("unable to write new index file"));
if (!opts->discard_changes && !opts->quiet && new_branch_info->commit)
@@ -958,7 +958,8 @@ static void update_refs_for_switch(const struct checkout_opts *opts,
int ret;
struct strbuf err = STRBUF_INIT;
- ret = safe_create_reflog(refname, &err);
+ ret = refs_create_reflog(get_main_ref_store(the_repository),
+ refname, &err);
if (ret) {
fprintf(stderr, _("Can not do reflog for '%s': %s\n"),
opts->new_orphan_branch, err.buf);
@@ -999,8 +1000,10 @@ static void update_refs_for_switch(const struct checkout_opts *opts,
if (!strcmp(new_branch_info->name, "HEAD") && !new_branch_info->path && !opts->force_detach) {
/* Nothing to do. */
} else if (opts->force_detach || !new_branch_info->path) { /* No longer on any branch. */
- update_ref(msg.buf, "HEAD", &new_branch_info->commit->object.oid, NULL,
- REF_NO_DEREF, UPDATE_REFS_DIE_ON_ERR);
+ refs_update_ref(get_main_ref_store(the_repository), msg.buf,
+ "HEAD", &new_branch_info->commit->object.oid,
+ NULL,
+ REF_NO_DEREF, UPDATE_REFS_DIE_ON_ERR);
if (!opts->quiet) {
if (old_branch_info->path &&
advice_enabled(ADVICE_DETACHED_HEAD) && !opts->force_detach)
@@ -1008,7 +1011,7 @@ static void update_refs_for_switch(const struct checkout_opts *opts,
describe_detached_head(_("HEAD is now at"), new_branch_info->commit);
}
} else if (new_branch_info->path) { /* Switch branches. */
- if (create_symref("HEAD", new_branch_info->path, msg.buf) < 0)
+ if (refs_update_symref(get_main_ref_store(the_repository), "HEAD", new_branch_info->path, msg.buf) < 0)
die(_("unable to update HEAD"));
if (!opts->quiet) {
if (old_branch_info->path && !strcmp(new_branch_info->path, old_branch_info->path)) {
@@ -1029,8 +1032,9 @@ static void update_refs_for_switch(const struct checkout_opts *opts,
}
}
if (old_branch_info->path && old_branch_info->name) {
- if (!ref_exists(old_branch_info->path) && reflog_exists(old_branch_info->path))
- delete_reflog(old_branch_info->path);
+ if (!refs_ref_exists(get_main_ref_store(the_repository), old_branch_info->path) && refs_reflog_exists(get_main_ref_store(the_repository), old_branch_info->path))
+ refs_delete_reflog(get_main_ref_store(the_repository),
+ old_branch_info->path);
}
}
remove_branch_state(the_repository, !opts->quiet);
@@ -1129,7 +1133,8 @@ static void orphaned_commit_warning(struct commit *old_commit, struct commit *ne
object->flags &= ~UNINTERESTING;
add_pending_object(&revs, object, oid_to_hex(&object->oid));
- for_each_ref(add_pending_uninteresting_ref, &revs);
+ refs_for_each_ref(get_main_ref_store(the_repository),
+ add_pending_uninteresting_ref, &revs);
if (new_commit)
add_pending_oid(&revs, "HEAD",
&new_commit->object.oid,
@@ -1159,7 +1164,8 @@ static int switch_branches(const struct checkout_opts *opts,
trace2_cmd_mode("branch");
memset(&old_branch_info, 0, sizeof(old_branch_info));
- old_branch_info.path = resolve_refdup("HEAD", 0, &rev, &flag);
+ old_branch_info.path = refs_resolve_refdup(get_main_ref_store(the_repository),
+ "HEAD", 0, &rev, &flag);
if (old_branch_info.path)
old_branch_info.commit = lookup_commit_reference_gently(the_repository, &rev, 1);
if (!(flag & REF_ISSYMREF))
@@ -1247,7 +1253,7 @@ static void setup_new_branch_info_and_source_tree(
setup_branch_path(new_branch_info);
if (!check_refname_format(new_branch_info->path, 0) &&
- !read_ref(new_branch_info->path, &branch_rev))
+ !refs_read_ref(get_main_ref_store(the_repository), new_branch_info->path, &branch_rev))
oidcpy(rev, &branch_rev);
else
/* not an existing branch */
@@ -1269,12 +1275,12 @@ static void setup_new_branch_info_and_source_tree(
}
}
-static const char *parse_remote_branch(const char *arg,
- struct object_id *rev,
- int could_be_checkout_paths)
+static char *parse_remote_branch(const char *arg,
+ struct object_id *rev,
+ int could_be_checkout_paths)
{
int num_matches = 0;
- const char *remote = unique_tracking_name(arg, rev, &num_matches);
+ char *remote = unique_tracking_name(arg, rev, &num_matches);
if (remote && could_be_checkout_paths) {
die(_("'%s' could be both a local file and a tracking branch.\n"
@@ -1310,6 +1316,7 @@ static int parse_branchname_arg(int argc, const char **argv,
const char **new_branch = &opts->new_branch;
int argcount = 0;
const char *arg;
+ char *remote = NULL;
int dash_dash_pos;
int has_dash_dash = 0;
int i;
@@ -1410,8 +1417,8 @@ static int parse_branchname_arg(int argc, const char **argv,
recover_with_dwim = 0;
if (recover_with_dwim) {
- const char *remote = parse_remote_branch(arg, rev,
- could_be_checkout_paths);
+ remote = parse_remote_branch(arg, rev,
+ could_be_checkout_paths);
if (remote) {
*new_branch = arg;
arg = remote;
@@ -1453,6 +1460,7 @@ static int parse_branchname_arg(int argc, const char **argv,
argc--;
}
+ free(remote);
return argcount;
}
@@ -1466,7 +1474,8 @@ static int switch_unborn_to_new_branch(const struct checkout_opts *opts)
if (!opts->new_branch)
die(_("You are on a branch yet to be born"));
strbuf_addf(&branch_ref, "refs/heads/%s", opts->new_branch);
- status = create_symref("HEAD", branch_ref.buf, "checkout -b");
+ status = refs_update_symref(get_main_ref_store(the_repository),
+ "HEAD", branch_ref.buf, "checkout -b");
strbuf_release(&branch_ref);
if (!opts->quiet)
fprintf(stderr, _("Switched to a new branch '%s'\n"),
@@ -1553,7 +1562,8 @@ static void die_if_switching_to_a_branch_in_use(struct checkout_opts *opts,
if (opts->ignore_other_worktrees)
return;
- head_ref = resolve_refdup("HEAD", 0, NULL, &flags);
+ head_ref = refs_resolve_refdup(get_main_ref_store(the_repository),
+ "HEAD", 0, NULL, &flags);
if (head_ref && (!(flags & REF_ISSYMREF) || strcmp(head_ref, full_ref)))
die_if_checked_out(full_ref, 1);
free(head_ref);
@@ -1634,7 +1644,7 @@ static int checkout_branch(struct checkout_opts *opts,
struct object_id rev;
int flag;
- if (!read_ref_full("HEAD", 0, &rev, &flag) &&
+ if (!refs_read_ref_full(get_main_ref_store(the_repository), "HEAD", 0, &rev, &flag) &&
(flag & REF_ISSYMREF) && is_null_oid(&rev))
return switch_unborn_to_new_branch(opts);
}
diff --git a/builtin/clean.c b/builtin/clean.c
index 29efe84153..ded5a91534 100644
--- a/builtin/clean.c
+++ b/builtin/clean.c
@@ -6,7 +6,6 @@
* Based on git-clean.sh by Pavel Roskin
*/
-#define USE_THE_INDEX_VARIABLE
#include "builtin.h"
#include "abspath.h"
#include "config.h"
@@ -714,7 +713,7 @@ static int filter_by_patterns_cmd(void)
for_each_string_list_item(item, &del_list) {
int dtype = DT_UNKNOWN;
- if (is_excluded(&dir, &the_index, item->string, &dtype)) {
+ if (is_excluded(&dir, the_repository->index, item->string, &dtype)) {
*item->string = '\0';
changed++;
}
@@ -1021,7 +1020,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
PATHSPEC_PREFER_CWD,
prefix, argv);
- fill_directory(&dir, &the_index, &pathspec);
+ fill_directory(&dir, the_repository->index, &pathspec);
correct_untracked_entries(&dir);
for (i = 0; i < dir.nr; i++) {
@@ -1029,7 +1028,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
struct stat st;
const char *rel;
- if (!index_name_is_other(&the_index, ent->name, ent->len))
+ if (!index_name_is_other(the_repository->index, ent->name, ent->len))
continue;
if (lstat(ent->name, &st))
diff --git a/builtin/clone.c b/builtin/clone.c
index 74ec14542e..af6017d41a 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -8,7 +8,6 @@
* Clone a repository into a different directory that does not yet exist.
*/
-#define USE_THE_INDEX_VARIABLE
#include "builtin.h"
#include "abspath.h"
#include "advice.h"
@@ -72,7 +71,7 @@ static char *option_branch = NULL;
static struct string_list option_not = STRING_LIST_INIT_NODUP;
static const char *real_git_dir;
static const char *ref_format;
-static char *option_upload_pack = "git-upload-pack";
+static const char *option_upload_pack = "git-upload-pack";
static int option_verbosity;
static int option_progress = -1;
static int option_sparse_checkout;
@@ -178,8 +177,8 @@ static struct option builtin_clone_options[] = {
static const char *get_repo_path_1(struct strbuf *path, int *is_bundle)
{
- static char *suffix[] = { "/.git", "", ".git/.git", ".git" };
- static char *bundle_suffix[] = { ".bundle", "" };
+ static const char *suffix[] = { "/.git", "", ".git/.git", ".git" };
+ static const char *bundle_suffix[] = { ".bundle", "" };
size_t baselen = path->len;
struct stat st;
int i;
@@ -329,7 +328,20 @@ static void copy_or_link_directory(struct strbuf *src, struct strbuf *dest,
int src_len, dest_len;
struct dir_iterator *iter;
int iter_status;
- struct strbuf realpath = STRBUF_INIT;
+
+ /*
+ * Refuse copying directories by default which aren't owned by us. The
+ * code that performs either the copying or hardlinking is not prepared
+ * to handle various edge cases where an adversary may for example
+ * racily swap out files for symlinks. This can cause us to
+ * inadvertently use the wrong source file.
+ *
+ * Furthermore, even if we were prepared to handle such races safely,
+ * creating hardlinks across user boundaries is an inherently unsafe
+ * operation as the hardlinked files can be rewritten at will by the
+ * potentially-untrusted user. We thus refuse to do so by default.
+ */
+ die_upon_dubious_ownership(NULL, NULL, src_repo);
mkdir_if_missing(dest->buf, 0777);
@@ -377,9 +389,27 @@ static void copy_or_link_directory(struct strbuf *src, struct strbuf *dest,
if (unlink(dest->buf) && errno != ENOENT)
die_errno(_("failed to unlink '%s'"), dest->buf);
if (!option_no_hardlinks) {
- strbuf_realpath(&realpath, src->buf, 1);
- if (!link(realpath.buf, dest->buf))
+ if (!link(src->buf, dest->buf)) {
+ struct stat st;
+
+ /*
+ * Sanity-check whether the created hardlink
+ * actually links to the expected file now. This
+ * catches time-of-check-time-of-use bugs in
+ * case the source file was meanwhile swapped.
+ */
+ if (lstat(dest->buf, &st))
+ die(_("hardlink cannot be checked at '%s'"), dest->buf);
+ if (st.st_mode != iter->st.st_mode ||
+ st.st_ino != iter->st.st_ino ||
+ st.st_dev != iter->st.st_dev ||
+ st.st_size != iter->st.st_size ||
+ st.st_uid != iter->st.st_uid ||
+ st.st_gid != iter->st.st_gid)
+ die(_("hardlink different from source at '%s'"), dest->buf);
+
continue;
+ }
if (option_local > 0)
die_errno(_("failed to create link '%s'"), dest->buf);
option_no_hardlinks = 1;
@@ -392,8 +422,6 @@ static void copy_or_link_directory(struct strbuf *src, struct strbuf *dest,
strbuf_setlen(src, src_len);
die(_("failed to iterate over '%s'"), src->buf);
}
-
- strbuf_release(&realpath);
}
static void clone_local(const char *src_repo, const char *dest_repo)
@@ -495,6 +523,9 @@ static struct ref *wanted_peer_refs(const struct ref *refs,
struct ref *head = copy_ref(find_ref_by_name(refs, "HEAD"));
struct ref *local_refs = head;
struct ref **tail = head ? &head->next : &local_refs;
+ struct refspec_item tag_refspec;
+
+ refspec_item_init(&tag_refspec, TAG_REFSPEC, 0);
if (option_single_branch) {
struct ref *remote_head = NULL;
@@ -502,7 +533,8 @@ static struct ref *wanted_peer_refs(const struct ref *refs,
if (!option_branch)
remote_head = guess_remote_head(head, refs, 0);
else {
- local_refs = NULL;
+ free_one_ref(head);
+ local_refs = head = NULL;
tail = &local_refs;
remote_head = copy_ref(find_remote_branch(refs, option_branch));
}
@@ -517,7 +549,7 @@ static struct ref *wanted_peer_refs(const struct ref *refs,
&tail, 0);
/* if --branch=tag, pull the requested tag explicitly */
- get_fetch_map(remote_head, tag_refspec, &tail, 0);
+ get_fetch_map(remote_head, &tag_refspec, &tail, 0);
}
free_refs(remote_head);
} else {
@@ -527,8 +559,9 @@ static struct ref *wanted_peer_refs(const struct ref *refs,
}
if (!option_mirror && !option_single_branch && !option_no_tags)
- get_fetch_map(refs, tag_refspec, &tail, 0);
+ get_fetch_map(refs, &tag_refspec, &tail, 0);
+ refspec_item_clear(&tag_refspec);
return local_refs;
}
@@ -539,7 +572,8 @@ static void write_remote_refs(const struct ref *local_refs)
struct ref_transaction *t;
struct strbuf err = STRBUF_INIT;
- t = ref_transaction_begin(&err);
+ t = ref_store_transaction_begin(get_main_ref_store(the_repository),
+ &err);
if (!t)
die("%s", err.buf);
@@ -547,7 +581,7 @@ static void write_remote_refs(const struct ref *local_refs)
if (!r->peer_ref)
continue;
if (ref_transaction_create(t, r->peer_ref->name, &r->old_oid,
- 0, NULL, &err))
+ NULL, 0, NULL, &err))
die("%s", err.buf);
}
@@ -570,8 +604,9 @@ static void write_followtags(const struct ref *refs, const char *msg)
OBJECT_INFO_QUICK |
OBJECT_INFO_SKIP_FETCH_OBJECT))
continue;
- update_ref(msg, ref->name, &ref->old_oid, NULL, 0,
- UPDATE_REFS_DIE_ON_ERR);
+ refs_update_ref(get_main_ref_store(the_repository), msg,
+ ref->name, &ref->old_oid, NULL, 0,
+ UPDATE_REFS_DIE_ON_ERR);
}
}
@@ -623,9 +658,9 @@ static void update_remote_refs(const struct ref *refs,
struct strbuf head_ref = STRBUF_INIT;
strbuf_addstr(&head_ref, branch_top);
strbuf_addstr(&head_ref, "HEAD");
- if (create_symref(head_ref.buf,
- remote_head_points_at->peer_ref->name,
- msg) < 0)
+ if (refs_update_symref(get_main_ref_store(the_repository), head_ref.buf,
+ remote_head_points_at->peer_ref->name,
+ msg) < 0)
die(_("unable to update %s"), head_ref.buf);
strbuf_release(&head_ref);
}
@@ -637,33 +672,36 @@ static void update_head(const struct ref *our, const struct ref *remote,
const char *head;
if (our && skip_prefix(our->name, "refs/heads/", &head)) {
/* Local default branch link */
- if (create_symref("HEAD", our->name, NULL) < 0)
+ if (refs_update_symref(get_main_ref_store(the_repository), "HEAD", our->name, NULL) < 0)
die(_("unable to update HEAD"));
if (!option_bare) {
- update_ref(msg, "HEAD", &our->old_oid, NULL, 0,
- UPDATE_REFS_DIE_ON_ERR);
+ refs_update_ref(get_main_ref_store(the_repository),
+ msg, "HEAD", &our->old_oid, NULL, 0,
+ UPDATE_REFS_DIE_ON_ERR);
install_branch_config(0, head, remote_name, our->name);
}
} else if (our) {
struct commit *c = lookup_commit_reference(the_repository,
&our->old_oid);
/* --branch specifies a non-branch (i.e. tags), detach HEAD */
- update_ref(msg, "HEAD", &c->object.oid, NULL, REF_NO_DEREF,
- UPDATE_REFS_DIE_ON_ERR);
+ refs_update_ref(get_main_ref_store(the_repository), msg,
+ "HEAD", &c->object.oid, NULL, REF_NO_DEREF,
+ UPDATE_REFS_DIE_ON_ERR);
} else if (remote) {
/*
* We know remote HEAD points to a non-branch, or
* HEAD points to a branch but we don't know which one.
* Detach HEAD in all these cases.
*/
- update_ref(msg, "HEAD", &remote->old_oid, NULL, REF_NO_DEREF,
- UPDATE_REFS_DIE_ON_ERR);
+ refs_update_ref(get_main_ref_store(the_repository), msg,
+ "HEAD", &remote->old_oid, NULL, REF_NO_DEREF,
+ UPDATE_REFS_DIE_ON_ERR);
} else if (unborn && skip_prefix(unborn, "refs/heads/", &head)) {
/*
* Unborn head from remote; same as "our" case above except
* that we have no ref to update.
*/
- if (create_symref("HEAD", unborn, NULL) < 0)
+ if (refs_update_symref(get_main_ref_store(the_repository), "HEAD", unborn, NULL) < 0)
die(_("unable to update HEAD"));
if (!option_bare)
install_branch_config(0, head, remote_name, unborn);
@@ -704,7 +742,8 @@ static int checkout(int submodule_progress, int filter_submodules)
if (option_no_checkout)
return 0;
- head = resolve_refdup("HEAD", RESOLVE_REF_READING, &oid, NULL);
+ head = refs_resolve_refdup(get_main_ref_store(the_repository), "HEAD",
+ RESOLVE_REF_READING, &oid, NULL);
if (!head) {
warning(_("remote HEAD refers to nonexistent ref, "
"unable to checkout"));
@@ -731,8 +770,8 @@ static int checkout(int submodule_progress, int filter_submodules)
opts.preserve_ignored = 0;
opts.fn = oneway_merge;
opts.verbose_update = (option_verbosity >= 0);
- opts.src_index = &the_index;
- opts.dst_index = &the_index;
+ opts.src_index = the_repository->index;
+ opts.dst_index = the_repository->index;
init_checkout_metadata(&opts.meta, head, &oid, NULL);
tree = parse_tree_indirect(&oid);
@@ -746,7 +785,7 @@ static int checkout(int submodule_progress, int filter_submodules)
free(head);
- if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
+ if (write_locked_index(the_repository->index, &lock_file, COMMIT_LOCK))
die(_("unable to write new index file"));
err |= run_hooks_l("post-checkout", oid_to_hex(null_oid()),
@@ -936,7 +975,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
int submodule_progress;
int filter_submodules = 0;
int hash_algo;
- unsigned int ref_storage_format = REF_STORAGE_FORMAT_UNKNOWN;
+ enum ref_storage_format ref_storage_format = REF_STORAGE_FORMAT_UNKNOWN;
const int do_not_override_repo_unix_permissions = -1;
struct transport_ls_refs_options transport_ls_refs_options =
@@ -1256,7 +1295,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
refspec_appendf(&remote->fetch, "+%s*:%s*", src_ref_prefix,
branch_top.buf);
- path = get_repo_path(remote->url[0], &is_bundle);
+ path = get_repo_path(remote->url.v[0], &is_bundle);
is_local = option_local != 0 && path && !is_bundle;
if (is_local) {
if (option_depth)
@@ -1278,7 +1317,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
if (option_local > 0 && !is_local)
warning(_("--local is ignored"));
- transport = transport_get(remote, path ? path : remote->url[0]);
+ transport = transport_get(remote, path ? path : remote->url.v[0]);
transport_set_verbosity(transport, option_verbosity, option_progress);
transport->family = family;
transport->cloning = 1;
@@ -1425,6 +1464,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
} else if (remote_head) {
our_head_points_at = NULL;
} else {
+ char *to_free = NULL;
const char *branch;
if (!mapped_refs) {
@@ -1437,7 +1477,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
"refs/heads/", &branch)) {
unborn_head = xstrdup(transport_ls_refs_options.unborn_head_target);
} else {
- branch = git_default_branch_name(0);
+ branch = to_free = repo_default_branch_name(the_repository, 0);
unborn_head = xstrfmt("refs/heads/%s", branch);
}
@@ -1453,6 +1493,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
* a match.
*/
our_head_points_at = find_remote_branch(mapped_refs, branch);
+
+ free(to_free);
}
write_refspec_config(src_ref_prefix, our_head_points_at,
@@ -1507,6 +1549,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
free(dir);
free(path);
free(repo_to_free);
+ UNLEAK(repo);
junk_mode = JUNK_LEAVE_ALL;
transport_ls_refs_options_release(&transport_ls_refs_options);
diff --git a/builtin/commit-tree.c b/builtin/commit-tree.c
index 1bb7819839..84bb450222 100644
--- a/builtin/commit-tree.c
+++ b/builtin/commit-tree.c
@@ -111,6 +111,7 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix)
N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
OPT_END()
};
+ int ret;
git_config(git_default_config, NULL);
@@ -132,11 +133,15 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix)
if (commit_tree(buffer.buf, buffer.len, &tree_oid, parents, &commit_oid,
NULL, sign_commit)) {
- strbuf_release(&buffer);
- return 1;
+ ret = 1;
+ goto out;
}
printf("%s\n", oid_to_hex(&commit_oid));
+ ret = 0;
+
+out:
+ free_commit_list(parents);
strbuf_release(&buffer);
- return 0;
+ return ret;
}
diff --git a/builtin/commit.c b/builtin/commit.c
index 6e1484446b..dec78dfb86 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -5,7 +5,6 @@
* Based on git-commit.sh by Junio C Hamano and Linus Torvalds
*/
-#define USE_THE_INDEX_VARIABLE
#include "builtin.h"
#include "advice.h"
#include "config.h"
@@ -38,6 +37,7 @@
#include "commit-reach.h"
#include "commit-graph.h"
#include "pretty.h"
+#include "trailer.h"
static const char * const builtin_commit_usage[] = {
N_("git commit [-a | --interactive | --patch] [-s] [-v] [-u<mode>] [--amend]\n"
@@ -106,14 +106,15 @@ static enum {
COMMIT_PARTIAL
} commit_style;
-static const char *logfile, *force_author;
-static const char *template_file;
+static const char *force_author;
+static char *logfile;
+static char *template_file;
/*
* The _message variables are commit names from which to take
* the commit message and/or authorship.
*/
static const char *author_message, *author_message_buffer;
-static char *edit_message, *use_message;
+static const char *edit_message, *use_message;
static char *fixup_message, *fixup_commit, *squash_message;
static const char *fixup_prefix;
static int all, also, interactive, patch_interactive, only, amend, signoff;
@@ -121,8 +122,8 @@ static int edit_flag = -1; /* unspecified */
static int quiet, verbose, no_verify, allow_empty, dry_run, renew_authorship;
static int config_commit_verbose = -1; /* unspecified */
static int no_post_rewrite, allow_empty_message, pathspec_file_nul;
-static char *untracked_files_arg, *force_date, *ignore_submodule_arg, *ignored_arg;
-static char *sign_commit, *pathspec_from_file;
+static const char *untracked_files_arg, *force_date, *ignore_submodule_arg, *ignored_arg;
+static const char *sign_commit, *pathspec_from_file;
static struct strvec trailer_args = STRVEC_INIT;
/*
@@ -133,7 +134,7 @@ static struct strvec trailer_args = STRVEC_INIT;
* is specified explicitly.
*/
static enum commit_msg_cleanup_mode cleanup_mode;
-static const char *cleanup_arg;
+static char *cleanup_arg;
static enum commit_whence whence;
static int use_editor = 1, include_status = 1;
@@ -142,14 +143,6 @@ static struct strbuf message = STRBUF_INIT;
static enum wt_status_format status_format = STATUS_FORMAT_UNSPECIFIED;
-static int opt_pass_trailer(const struct option *opt, const char *arg, int unset)
-{
- BUG_ON_OPT_NEG(unset);
-
- strvec_pushl(opt->value, "--trailer", arg, NULL);
- return 0;
-}
-
static int opt_parse_porcelain(const struct option *opt, const char *arg, int unset)
{
enum wt_status_format *value = (enum wt_status_format *)opt->value;
@@ -266,19 +259,19 @@ static int list_paths(struct string_list *list, const char *with_tree,
if (with_tree) {
char *max_prefix = common_prefix(pattern);
- overlay_tree_on_index(&the_index, with_tree, max_prefix);
+ overlay_tree_on_index(the_repository->index, with_tree, max_prefix);
free(max_prefix);
}
/* TODO: audit for interaction with sparse-index. */
- ensure_full_index(&the_index);
- for (i = 0; i < the_index.cache_nr; i++) {
- const struct cache_entry *ce = the_index.cache[i];
+ ensure_full_index(the_repository->index);
+ for (i = 0; i < the_repository->index->cache_nr; i++) {
+ const struct cache_entry *ce = the_repository->index->cache[i];
struct string_list_item *item;
if (ce->ce_flags & CE_UPDATE)
continue;
- if (!ce_path_match(&the_index, ce, pattern, m))
+ if (!ce_path_match(the_repository->index, ce, pattern, m))
continue;
item = string_list_insert(list, ce->name);
if (ce_skip_worktree(ce))
@@ -302,10 +295,10 @@ static void add_remove_files(struct string_list *list)
continue;
if (!lstat(p->string, &st)) {
- if (add_to_index(&the_index, p->string, &st, 0))
+ if (add_to_index(the_repository->index, p->string, &st, 0))
die(_("updating files failed"));
} else
- remove_file_from_index(&the_index, p->string);
+ remove_file_from_index(the_repository->index, p->string);
}
}
@@ -316,7 +309,7 @@ static void create_base_index(const struct commit *current_head)
struct tree_desc t;
if (!current_head) {
- discard_index(&the_index);
+ discard_index(the_repository->index);
return;
}
@@ -324,8 +317,8 @@ static void create_base_index(const struct commit *current_head)
opts.head_idx = 1;
opts.index_only = 1;
opts.merge = 1;
- opts.src_index = &the_index;
- opts.dst_index = &the_index;
+ opts.src_index = the_repository->index;
+ opts.dst_index = the_repository->index;
opts.fn = oneway_merge;
tree = parse_tree_indirect(&current_head->object.oid);
@@ -344,7 +337,7 @@ static void refresh_cache_or_die(int refresh_flags)
* refresh_flags contains REFRESH_QUIET, so the only errors
* are for unmerged entries.
*/
- if (refresh_index(&the_index, refresh_flags | REFRESH_IN_PORCELAIN, NULL, NULL, NULL))
+ if (refresh_index(the_repository->index, refresh_flags | REFRESH_IN_PORCELAIN, NULL, NULL, NULL))
die_resolve_conflict("commit");
}
@@ -393,7 +386,7 @@ static const char *prepare_index(const char **argv, const char *prefix,
refresh_cache_or_die(refresh_flags);
- if (write_locked_index(&the_index, &index_lock, 0))
+ if (write_locked_index(the_repository->index, &index_lock, 0))
die(_("unable to create temporary index"));
old_repo_index_file = the_repository->index_file;
@@ -412,13 +405,13 @@ static const char *prepare_index(const char **argv, const char *prefix,
unsetenv(INDEX_ENVIRONMENT);
FREE_AND_NULL(old_index_env);
- discard_index(&the_index);
- read_index_from(&the_index, get_lock_file_path(&index_lock),
+ discard_index(the_repository->index);
+ read_index_from(the_repository->index, get_lock_file_path(&index_lock),
get_git_dir());
- if (cache_tree_update(&the_index, WRITE_TREE_SILENT) == 0) {
+ if (cache_tree_update(the_repository->index, WRITE_TREE_SILENT) == 0) {
if (reopen_lock_file(&index_lock) < 0)
die(_("unable to write index file"));
- if (write_locked_index(&the_index, &index_lock, 0))
+ if (write_locked_index(the_repository->index, &index_lock, 0))
die(_("unable to update temporary index"));
} else
warning(_("Failed to update main cache tree"));
@@ -450,8 +443,8 @@ static const char *prepare_index(const char **argv, const char *prefix,
exit(128);
refresh_cache_or_die(refresh_flags);
- cache_tree_update(&the_index, WRITE_TREE_SILENT);
- if (write_locked_index(&the_index, &index_lock, 0))
+ cache_tree_update(the_repository->index, WRITE_TREE_SILENT);
+ if (write_locked_index(the_repository->index, &index_lock, 0))
die(_("unable to write new index file"));
commit_style = COMMIT_NORMAL;
ret = get_lock_file_path(&index_lock);
@@ -472,10 +465,10 @@ static const char *prepare_index(const char **argv, const char *prefix,
repo_hold_locked_index(the_repository, &index_lock,
LOCK_DIE_ON_ERROR);
refresh_cache_or_die(refresh_flags);
- if (the_index.cache_changed
- || !cache_tree_fully_valid(the_index.cache_tree))
- cache_tree_update(&the_index, WRITE_TREE_SILENT);
- if (write_locked_index(&the_index, &index_lock,
+ if (the_repository->index->cache_changed
+ || !cache_tree_fully_valid(the_repository->index->cache_tree))
+ cache_tree_update(the_repository->index, WRITE_TREE_SILENT);
+ if (write_locked_index(the_repository->index, &index_lock,
COMMIT_LOCK | SKIP_IF_UNCHANGED))
die(_("unable to write new index file"));
commit_style = COMMIT_AS_IS;
@@ -516,15 +509,15 @@ static const char *prepare_index(const char **argv, const char *prefix,
if (list_paths(&partial, !current_head ? NULL : "HEAD", &pathspec))
exit(1);
- discard_index(&the_index);
+ discard_index(the_repository->index);
if (repo_read_index(the_repository) < 0)
die(_("cannot read the index"));
repo_hold_locked_index(the_repository, &index_lock, LOCK_DIE_ON_ERROR);
add_remove_files(&partial);
- refresh_index(&the_index, REFRESH_QUIET, NULL, NULL, NULL);
- cache_tree_update(&the_index, WRITE_TREE_SILENT);
- if (write_locked_index(&the_index, &index_lock, 0))
+ refresh_index(the_repository->index, REFRESH_QUIET, NULL, NULL, NULL);
+ cache_tree_update(the_repository->index, WRITE_TREE_SILENT);
+ if (write_locked_index(the_repository->index, &index_lock, 0))
die(_("unable to write new index file"));
hold_lock_file_for_update(&false_lock,
@@ -534,14 +527,14 @@ static const char *prepare_index(const char **argv, const char *prefix,
create_base_index(current_head);
add_remove_files(&partial);
- refresh_index(&the_index, REFRESH_QUIET, NULL, NULL, NULL);
+ refresh_index(the_repository->index, REFRESH_QUIET, NULL, NULL, NULL);
- if (write_locked_index(&the_index, &false_lock, 0))
+ if (write_locked_index(the_repository->index, &false_lock, 0))
die(_("unable to write temporary index file"));
- discard_index(&the_index);
+ discard_index(the_repository->index);
ret = get_lock_file_path(&false_lock);
- read_index_from(&the_index, ret, get_git_dir());
+ read_index_from(the_repository->index, ret, get_git_dir());
out:
string_list_clear(&partial, 0);
clear_pathspec(&pathspec);
@@ -999,7 +992,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
struct object_id oid;
const char *parent = "HEAD";
- if (!the_index.initialized && repo_read_index(the_repository) < 0)
+ if (!the_repository->index->initialized && repo_read_index(the_repository) < 0)
die(_("Cannot read index"));
if (amend)
@@ -1009,11 +1002,11 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
int i, ita_nr = 0;
/* TODO: audit for interaction with sparse-index. */
- ensure_full_index(&the_index);
- for (i = 0; i < the_index.cache_nr; i++)
- if (ce_intent_to_add(the_index.cache[i]))
+ ensure_full_index(the_repository->index);
+ for (i = 0; i < the_repository->index->cache_nr; i++)
+ if (ce_intent_to_add(the_repository->index->cache[i]))
ita_nr++;
- committable = the_index.cache_nr - ita_nr > 0;
+ committable = the_repository->index->cache_nr - ita_nr > 0;
} else {
/*
* Unless the user did explicitly request a submodule
@@ -1038,14 +1031,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
fclose(s->fp);
if (trailer_args.nr) {
- struct child_process run_trailer = CHILD_PROCESS_INIT;
-
- strvec_pushl(&run_trailer.args, "interpret-trailers",
- "--in-place", "--no-divider",
- git_path_commit_editmsg(), NULL);
- strvec_pushv(&run_trailer.args, trailer_args.v);
- run_trailer.git_cmd = 1;
- if (run_command(&run_trailer))
+ if (amend_file_with_trailers(git_path_commit_editmsg(), &trailer_args))
die(_("unable to pass trailers to --trailers"));
strvec_clear(&trailer_args);
}
@@ -1081,11 +1067,11 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
* and could have updated it. We must do this before we invoke
* the editor and after we invoke run_status above.
*/
- discard_index(&the_index);
+ discard_index(the_repository->index);
}
- read_index_from(&the_index, index_file, get_git_dir());
+ read_index_from(the_repository->index, index_file, get_git_dir());
- if (cache_tree_update(&the_index, 0)) {
+ if (cache_tree_update(the_repository->index, 0)) {
error(_("Error building trees"));
return 0;
}
@@ -1324,7 +1310,7 @@ static int parse_and_validate_options(int argc, const char *argv[],
!!use_message, "-C",
!!logfile, "-F");
if (use_message || edit_message || logfile ||fixup_message || have_option_m)
- template_file = NULL;
+ FREE_AND_NULL(template_file);
if (edit_message)
use_message = edit_message;
if (amend && !use_message && !fixup_message)
@@ -1586,7 +1572,7 @@ int cmd_status(int argc, const char **argv, const char *prefix)
status_format != STATUS_FORMAT_PORCELAIN_V2)
progress_flag = REFRESH_PROGRESS;
repo_read_index(the_repository);
- refresh_index(&the_index,
+ refresh_index(the_repository->index,
REFRESH_QUIET|REFRESH_UNMERGED|progress_flag,
&s.pathspec, NULL, NULL);
@@ -1673,7 +1659,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
OPT_STRING(0, "fixup", &fixup_message, N_("[(amend|reword):]commit"), N_("use autosquash formatted message to fixup or amend/reword specified commit")),
OPT_STRING(0, "squash", &squash_message, N_("commit"), N_("use autosquash formatted message to squash specified commit")),
OPT_BOOL(0, "reset-author", &renew_authorship, N_("the commit is authored by me now (used with -C/-c/--amend)")),
- OPT_CALLBACK_F(0, "trailer", &trailer_args, N_("trailer"), N_("add custom trailer(s)"), PARSE_OPT_NONEG, opt_pass_trailer),
+ OPT_PASSTHRU_ARGV(0, "trailer", &trailer_args, N_("trailer"), N_("add custom trailer(s)"), PARSE_OPT_NONEG),
OPT_BOOL('s', "signoff", &signoff, N_("add a Signed-off-by trailer")),
OPT_FILENAME('t', "template", &template_file, N_("use specified template file")),
OPT_BOOL('e', "edit", &edit_flag, N_("force edit of commit")),
@@ -1856,13 +1842,12 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
append_merge_tag_headers(parents, &tail);
}
- if (commit_tree_extended(sb.buf, sb.len, &the_index.cache_tree->oid,
+ if (commit_tree_extended(sb.buf, sb.len, &the_repository->index->cache_tree->oid,
parents, &oid, author_ident.buf, NULL,
sign_commit, extra)) {
rollback_index_files();
die(_("failed to write commit object"));
}
- free_commit_extra_headers(extra);
if (update_head_with_reflog(current_head, &oid, reflog_msg, &sb,
&err)) {
@@ -1904,8 +1889,12 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
apply_autostash_ref(the_repository, "MERGE_AUTOSTASH");
cleanup:
+ free_commit_extra_headers(extra);
+ free_commit_list(parents);
strbuf_release(&author_ident);
strbuf_release(&err);
strbuf_release(&sb);
+ free(logfile);
+ free(template_file);
return ret;
}
diff --git a/builtin/config.c b/builtin/config.c
index 0015620dde..20a0b64090 100644
--- a/builtin/config.c
+++ b/builtin/config.c
@@ -16,59 +16,112 @@
#include "worktree.h"
static const char *const builtin_config_usage[] = {
- N_("git config [<options>]"),
+ N_("git config list [<file-option>] [<display-option>] [--includes]"),
+ N_("git config get [<file-option>] [<display-option>] [--includes] [--all] [--regexp=<regexp>] [--value=<value>] [--fixed-value] [--default=<default>] <name>"),
+ N_("git config set [<file-option>] [--type=<type>] [--all] [--value=<value>] [--fixed-value] <name> <value>"),
+ N_("git config unset [<file-option>] [--all] [--value=<value>] [--fixed-value] <name> <value>"),
+ N_("git config rename-section [<file-option>] <old-name> <new-name>"),
+ N_("git config remove-section [<file-option>] <name>"),
+ N_("git config edit [<file-option>]"),
+ N_("git config [<file-option>] --get-colorbool <name> [<stdout-is-tty>]"),
NULL
};
-static char *key;
-static regex_t *key_regexp;
-static const char *value_pattern;
-static regex_t *regexp;
-static int show_keys;
-static int omit_values;
-static int use_key_regexp;
-static int do_all;
-static int do_not_match;
-static char delim = '=';
-static char key_delim = ' ';
-static char term = '\n';
-
-static int use_global_config, use_system_config, use_local_config;
-static int use_worktree_config;
-static struct git_config_source given_config_source;
-static int actions, type;
-static char *default_value;
-static int end_nul;
-static int respect_includes_opt = -1;
-static struct config_options config_options;
-static int show_origin;
-static int show_scope;
-static int fixed_value;
-static const char *comment;
-
-#define ACTION_GET (1<<0)
-#define ACTION_GET_ALL (1<<1)
-#define ACTION_GET_REGEXP (1<<2)
-#define ACTION_REPLACE_ALL (1<<3)
-#define ACTION_ADD (1<<4)
-#define ACTION_UNSET (1<<5)
-#define ACTION_UNSET_ALL (1<<6)
-#define ACTION_RENAME_SECTION (1<<7)
-#define ACTION_REMOVE_SECTION (1<<8)
-#define ACTION_LIST (1<<9)
-#define ACTION_EDIT (1<<10)
-#define ACTION_SET (1<<11)
-#define ACTION_SET_ALL (1<<12)
-#define ACTION_GET_COLOR (1<<13)
-#define ACTION_GET_COLORBOOL (1<<14)
-#define ACTION_GET_URLMATCH (1<<15)
-
-/*
- * The actions "ACTION_LIST | ACTION_GET_*" which may produce more than
- * one line of output and which should therefore be paged.
- */
-#define PAGING_ACTIONS (ACTION_LIST | ACTION_GET_ALL | \
- ACTION_GET_REGEXP | ACTION_GET_URLMATCH)
+static const char *const builtin_config_list_usage[] = {
+ N_("git config list [<file-option>] [<display-option>] [--includes]"),
+ NULL
+};
+
+static const char *const builtin_config_get_usage[] = {
+ N_("git config get [<file-option>] [<display-option>] [--includes] [--all] [--regexp=<regexp>] [--value=<value>] [--fixed-value] [--default=<default>] <name>"),
+ NULL
+};
+
+static const char *const builtin_config_set_usage[] = {
+ N_("git config set [<file-option>] [--type=<type>] [--comment=<message>] [--all] [--value=<value>] [--fixed-value] <name> <value>"),
+ NULL
+};
+
+static const char *const builtin_config_unset_usage[] = {
+ N_("git config unset [<file-option>] [--all] [--value=<value>] [--fixed-value] <name> <value>"),
+ NULL
+};
+
+static const char *const builtin_config_rename_section_usage[] = {
+ N_("git config rename-section [<file-option>] <old-name> <new-name>"),
+ NULL
+};
+
+static const char *const builtin_config_remove_section_usage[] = {
+ N_("git config remove-section [<file-option>] <name>"),
+ NULL
+};
+
+static const char *const builtin_config_edit_usage[] = {
+ N_("git config edit [<file-option>]"),
+ NULL
+};
+
+#define CONFIG_LOCATION_OPTIONS(opts) \
+ OPT_GROUP(N_("Config file location")), \
+ OPT_BOOL(0, "global", &opts.use_global_config, N_("use global config file")), \
+ OPT_BOOL(0, "system", &opts.use_system_config, N_("use system config file")), \
+ OPT_BOOL(0, "local", &opts.use_local_config, N_("use repository config file")), \
+ OPT_BOOL(0, "worktree", &opts.use_worktree_config, N_("use per-worktree config file")), \
+ OPT_STRING('f', "file", &opts.source.file, N_("file"), N_("use given config file")), \
+ OPT_STRING(0, "blob", &opts.source.blob, N_("blob-id"), N_("read config from given blob object"))
+
+struct config_location_options {
+ struct git_config_source source;
+ struct config_options options;
+ char *file_to_free;
+ int use_global_config;
+ int use_system_config;
+ int use_local_config;
+ int use_worktree_config;
+ int respect_includes_opt;
+};
+#define CONFIG_LOCATION_OPTIONS_INIT { \
+ .respect_includes_opt = -1, \
+}
+
+#define CONFIG_TYPE_OPTIONS(type) \
+ OPT_GROUP(N_("Type")), \
+ OPT_CALLBACK('t', "type", &type, N_("type"), N_("value is given this type"), option_parse_type), \
+ OPT_CALLBACK_VALUE(0, "bool", &type, N_("value is \"true\" or \"false\""), TYPE_BOOL), \
+ OPT_CALLBACK_VALUE(0, "int", &type, N_("value is decimal number"), TYPE_INT), \
+ OPT_CALLBACK_VALUE(0, "bool-or-int", &type, N_("value is --bool or --int"), TYPE_BOOL_OR_INT), \
+ OPT_CALLBACK_VALUE(0, "bool-or-str", &type, N_("value is --bool or string"), TYPE_BOOL_OR_STR), \
+ OPT_CALLBACK_VALUE(0, "path", &type, N_("value is a path (file or directory name)"), TYPE_PATH), \
+ OPT_CALLBACK_VALUE(0, "expiry-date", &type, N_("value is an expiry date"), TYPE_EXPIRY_DATE)
+
+#define CONFIG_DISPLAY_OPTIONS(opts) \
+ OPT_GROUP(N_("Display options")), \
+ OPT_BOOL('z', "null", &opts.end_nul, N_("terminate values with NUL byte")), \
+ OPT_BOOL(0, "name-only", &opts.omit_values, N_("show variable names only")), \
+ OPT_BOOL(0, "show-origin", &opts.show_origin, N_("show origin of config (file, standard input, blob, command line)")), \
+ OPT_BOOL(0, "show-scope", &opts.show_scope, N_("show scope of config (worktree, local, global, system, command)")), \
+ OPT_BOOL(0, "show-names", &opts.show_keys, N_("show config keys in addition to their values")), \
+ CONFIG_TYPE_OPTIONS(opts.type)
+
+struct config_display_options {
+ int end_nul;
+ int omit_values;
+ int show_origin;
+ int show_scope;
+ int show_keys;
+ int type;
+ char *default_value;
+ /* Populated via `display_options_init()`. */
+ int term;
+ int delim;
+ int key_delim;
+};
+#define CONFIG_DISPLAY_OPTIONS_INIT { \
+ .term = '\n', \
+ .delim = '=', \
+ .key_delim = ' ', \
+}
#define TYPE_BOOL 1
#define TYPE_INT 2
@@ -82,8 +135,6 @@ static const char *comment;
{ OPTION_CALLBACK, (s), (l), (v), NULL, (h), PARSE_OPT_NOARG | \
PARSE_OPT_NONEG, option_parse_type, (i) }
-static NORETURN void usage_builtin_config(void);
-
static int option_parse_type(const struct option *opt, const char *arg,
int unset)
{
@@ -128,61 +179,13 @@ static int option_parse_type(const struct option *opt, const char *arg,
* --type=int'.
*/
error(_("only one type at a time"));
- usage_builtin_config();
+ exit(129);
}
*to_type = new_type;
return 0;
}
-static struct option builtin_config_options[] = {
- OPT_GROUP(N_("Config file location")),
- OPT_BOOL(0, "global", &use_global_config, N_("use global config file")),
- OPT_BOOL(0, "system", &use_system_config, N_("use system config file")),
- OPT_BOOL(0, "local", &use_local_config, N_("use repository config file")),
- OPT_BOOL(0, "worktree", &use_worktree_config, N_("use per-worktree config file")),
- OPT_STRING('f', "file", &given_config_source.file, N_("file"), N_("use given config file")),
- OPT_STRING(0, "blob", &given_config_source.blob, N_("blob-id"), N_("read config from given blob object")),
- OPT_GROUP(N_("Action")),
- OPT_BIT(0, "get", &actions, N_("get value: name [value-pattern]"), ACTION_GET),
- OPT_BIT(0, "get-all", &actions, N_("get all values: key [value-pattern]"), ACTION_GET_ALL),
- OPT_BIT(0, "get-regexp", &actions, N_("get values for regexp: name-regex [value-pattern]"), ACTION_GET_REGEXP),
- OPT_BIT(0, "get-urlmatch", &actions, N_("get value specific for the URL: section[.var] URL"), ACTION_GET_URLMATCH),
- OPT_BIT(0, "replace-all", &actions, N_("replace all matching variables: name value [value-pattern]"), ACTION_REPLACE_ALL),
- OPT_BIT(0, "add", &actions, N_("add a new variable: name value"), ACTION_ADD),
- OPT_BIT(0, "unset", &actions, N_("remove a variable: name [value-pattern]"), ACTION_UNSET),
- OPT_BIT(0, "unset-all", &actions, N_("remove all matches: name [value-pattern]"), ACTION_UNSET_ALL),
- OPT_BIT(0, "rename-section", &actions, N_("rename section: old-name new-name"), ACTION_RENAME_SECTION),
- OPT_BIT(0, "remove-section", &actions, N_("remove a section: name"), ACTION_REMOVE_SECTION),
- OPT_BIT('l', "list", &actions, N_("list all"), ACTION_LIST),
- OPT_BOOL(0, "fixed-value", &fixed_value, N_("use string equality when comparing values to 'value-pattern'")),
- OPT_BIT('e', "edit", &actions, N_("open an editor"), ACTION_EDIT),
- OPT_BIT(0, "get-color", &actions, N_("find the color configured: slot [default]"), ACTION_GET_COLOR),
- OPT_BIT(0, "get-colorbool", &actions, N_("find the color setting: slot [stdout-is-tty]"), ACTION_GET_COLORBOOL),
- OPT_GROUP(N_("Type")),
- OPT_CALLBACK('t', "type", &type, N_("type"), N_("value is given this type"), option_parse_type),
- OPT_CALLBACK_VALUE(0, "bool", &type, N_("value is \"true\" or \"false\""), TYPE_BOOL),
- OPT_CALLBACK_VALUE(0, "int", &type, N_("value is decimal number"), TYPE_INT),
- OPT_CALLBACK_VALUE(0, "bool-or-int", &type, N_("value is --bool or --int"), TYPE_BOOL_OR_INT),
- OPT_CALLBACK_VALUE(0, "bool-or-str", &type, N_("value is --bool or string"), TYPE_BOOL_OR_STR),
- OPT_CALLBACK_VALUE(0, "path", &type, N_("value is a path (file or directory name)"), TYPE_PATH),
- OPT_CALLBACK_VALUE(0, "expiry-date", &type, N_("value is an expiry date"), TYPE_EXPIRY_DATE),
- OPT_GROUP(N_("Other")),
- OPT_BOOL('z', "null", &end_nul, N_("terminate values with NUL byte")),
- OPT_BOOL(0, "name-only", &omit_values, N_("show variable names only")),
- OPT_BOOL(0, "includes", &respect_includes_opt, N_("respect include directives on lookup")),
- OPT_BOOL(0, "show-origin", &show_origin, N_("show origin of config (file, standard input, blob, command line)")),
- OPT_BOOL(0, "show-scope", &show_scope, N_("show scope of config (worktree, local, global, system, command)")),
- OPT_STRING(0, "default", &default_value, N_("value"), N_("with --get, use default value when missing entry")),
- OPT_STRING(0, "comment", &comment, N_("value"), N_("human-readable comment string (# will be prepended as needed)")),
- OPT_END(),
-};
-
-static NORETURN void usage_builtin_config(void)
-{
- usage_with_options(builtin_config_usage, builtin_config_options);
-}
-
static void check_argc(int argc, int min, int max)
{
if (argc >= min && argc <= max)
@@ -192,27 +195,29 @@ static void check_argc(int argc, int min, int max)
else
error(_("wrong number of arguments, should be from %d to %d"),
min, max);
- usage_builtin_config();
+ exit(129);
}
-static void show_config_origin(const struct key_value_info *kvi,
+static void show_config_origin(const struct config_display_options *opts,
+ const struct key_value_info *kvi,
struct strbuf *buf)
{
- const char term = end_nul ? '\0' : '\t';
+ const char term = opts->end_nul ? '\0' : '\t';
strbuf_addstr(buf, config_origin_type_name(kvi->origin_type));
strbuf_addch(buf, ':');
- if (end_nul)
+ if (opts->end_nul)
strbuf_addstr(buf, kvi->filename ? kvi->filename : "");
else
quote_c_style(kvi->filename ? kvi->filename : "", buf, NULL, 0);
strbuf_addch(buf, term);
}
-static void show_config_scope(const struct key_value_info *kvi,
+static void show_config_scope(const struct config_display_options *opts,
+ const struct key_value_info *kvi,
struct strbuf *buf)
{
- const char term = end_nul ? '\0' : '\t';
+ const char term = opts->end_nul ? '\0' : '\t';
const char *scope = config_scope_name(kvi->scope);
strbuf_addstr(buf, N_(scope));
@@ -221,24 +226,25 @@ static void show_config_scope(const struct key_value_info *kvi,
static int show_all_config(const char *key_, const char *value_,
const struct config_context *ctx,
- void *cb UNUSED)
+ void *cb)
{
+ const struct config_display_options *opts = cb;
const struct key_value_info *kvi = ctx->kvi;
- if (show_origin || show_scope) {
+ if (opts->show_origin || opts->show_scope) {
struct strbuf buf = STRBUF_INIT;
- if (show_scope)
- show_config_scope(kvi, &buf);
- if (show_origin)
- show_config_origin(kvi, &buf);
+ if (opts->show_scope)
+ show_config_scope(opts, kvi, &buf);
+ if (opts->show_origin)
+ show_config_origin(opts, kvi, &buf);
/* Use fwrite as "buf" can contain \0's if "end_null" is set. */
fwrite(buf.buf, 1, buf.len, stdout);
strbuf_release(&buf);
}
- if (!omit_values && value_)
- printf("%s%c%s%c", key_, delim, value_, term);
+ if (!opts->omit_values && value_)
+ printf("%s%c%s%c", key_, opts->delim, value_, opts->term);
else
- printf("%s%c", key_, term);
+ printf("%s%c", key_, opts->term);
return 0;
}
@@ -248,26 +254,27 @@ struct strbuf_list {
int alloc;
};
-static int format_config(struct strbuf *buf, const char *key_,
+static int format_config(const struct config_display_options *opts,
+ struct strbuf *buf, const char *key_,
const char *value_, const struct key_value_info *kvi)
{
- if (show_scope)
- show_config_scope(kvi, buf);
- if (show_origin)
- show_config_origin(kvi, buf);
- if (show_keys)
+ if (opts->show_scope)
+ show_config_scope(opts, kvi, buf);
+ if (opts->show_origin)
+ show_config_origin(opts, kvi, buf);
+ if (opts->show_keys)
strbuf_addstr(buf, key_);
- if (!omit_values) {
- if (show_keys)
- strbuf_addch(buf, key_delim);
+ if (!opts->omit_values) {
+ if (opts->show_keys)
+ strbuf_addch(buf, opts->key_delim);
- if (type == TYPE_INT)
+ if (opts->type == TYPE_INT)
strbuf_addf(buf, "%"PRId64,
git_config_int64(key_, value_ ? value_ : "", kvi));
- else if (type == TYPE_BOOL)
+ else if (opts->type == TYPE_BOOL)
strbuf_addstr(buf, git_config_bool(key_, value_) ?
"true" : "false");
- else if (type == TYPE_BOOL_OR_INT) {
+ else if (opts->type == TYPE_BOOL_OR_INT) {
int is_bool, v;
v = git_config_bool_or_int(key_, value_, kvi,
&is_bool);
@@ -275,24 +282,24 @@ static int format_config(struct strbuf *buf, const char *key_,
strbuf_addstr(buf, v ? "true" : "false");
else
strbuf_addf(buf, "%d", v);
- } else if (type == TYPE_BOOL_OR_STR) {
+ } else if (opts->type == TYPE_BOOL_OR_STR) {
int v = git_parse_maybe_bool(value_);
if (v < 0)
strbuf_addstr(buf, value_);
else
strbuf_addstr(buf, v ? "true" : "false");
- } else if (type == TYPE_PATH) {
- const char *v;
+ } else if (opts->type == TYPE_PATH) {
+ char *v;
if (git_config_pathname(&v, key_, value_) < 0)
return -1;
strbuf_addstr(buf, v);
free((char *)v);
- } else if (type == TYPE_EXPIRY_DATE) {
+ } else if (opts->type == TYPE_EXPIRY_DATE) {
timestamp_t t;
if (git_config_expiry_date(&t, key_, value_) < 0)
return -1;
strbuf_addf(buf, "%"PRItime, t);
- } else if (type == TYPE_COLOR) {
+ } else if (opts->type == TYPE_COLOR) {
char v[COLOR_MAXLEN];
if (git_config_color(v, key_, value_) < 0)
return -1;
@@ -301,43 +308,73 @@ static int format_config(struct strbuf *buf, const char *key_,
strbuf_addstr(buf, value_);
} else {
/* Just show the key name; back out delimiter */
- if (show_keys)
+ if (opts->show_keys)
strbuf_setlen(buf, buf->len - 1);
}
}
- strbuf_addch(buf, term);
+ strbuf_addch(buf, opts->term);
return 0;
}
+#define GET_VALUE_ALL (1 << 0)
+#define GET_VALUE_KEY_REGEXP (1 << 1)
+
+struct collect_config_data {
+ const struct config_display_options *display_opts;
+ struct strbuf_list *values;
+ const char *value_pattern;
+ const char *key;
+ regex_t *regexp;
+ regex_t *key_regexp;
+ int do_not_match;
+ unsigned get_value_flags;
+ unsigned flags;
+};
+
static int collect_config(const char *key_, const char *value_,
const struct config_context *ctx, void *cb)
{
- struct strbuf_list *values = cb;
+ struct collect_config_data *data = cb;
+ struct strbuf_list *values = data->values;
const struct key_value_info *kvi = ctx->kvi;
- if (!use_key_regexp && strcmp(key_, key))
+ if (!(data->get_value_flags & GET_VALUE_KEY_REGEXP) &&
+ strcmp(key_, data->key))
return 0;
- if (use_key_regexp && regexec(key_regexp, key_, 0, NULL, 0))
+ if ((data->get_value_flags & GET_VALUE_KEY_REGEXP) &&
+ regexec(data->key_regexp, key_, 0, NULL, 0))
return 0;
- if (fixed_value && strcmp(value_pattern, (value_?value_:"")))
+ if ((data->flags & CONFIG_FLAGS_FIXED_VALUE) &&
+ strcmp(data->value_pattern, (value_?value_:"")))
return 0;
- if (regexp != NULL &&
- (do_not_match ^ !!regexec(regexp, (value_?value_:""), 0, NULL, 0)))
+ if (data->regexp &&
+ (data->do_not_match ^ !!regexec(data->regexp, (value_?value_:""), 0, NULL, 0)))
return 0;
ALLOC_GROW(values->items, values->nr + 1, values->alloc);
strbuf_init(&values->items[values->nr], 0);
- return format_config(&values->items[values->nr++], key_, value_, kvi);
+ return format_config(data->display_opts, &values->items[values->nr++],
+ key_, value_, kvi);
}
-static int get_value(const char *key_, const char *regex_, unsigned flags)
+static int get_value(const struct config_location_options *opts,
+ const struct config_display_options *display_opts,
+ const char *key_, const char *regex_,
+ unsigned get_value_flags, unsigned flags)
{
int ret = CONFIG_GENERIC_ERROR;
struct strbuf_list values = {NULL};
+ struct collect_config_data data = {
+ .display_opts = display_opts,
+ .values = &values,
+ .get_value_flags = get_value_flags,
+ .flags = flags,
+ };
+ char *key = NULL;
int i;
- if (use_key_regexp) {
+ if (get_value_flags & GET_VALUE_KEY_REGEXP) {
char *tl;
/*
@@ -354,10 +391,10 @@ static int get_value(const char *key_, const char *regex_, unsigned flags)
for (tl = key; *tl && *tl != '.'; tl++)
*tl = tolower(*tl);
- key_regexp = (regex_t*)xmalloc(sizeof(regex_t));
- if (regcomp(key_regexp, key, REG_EXTENDED)) {
+ data.key_regexp = (regex_t*)xmalloc(sizeof(regex_t));
+ if (regcomp(data.key_regexp, key, REG_EXTENDED)) {
error(_("invalid key pattern: %s"), key_);
- FREE_AND_NULL(key_regexp);
+ FREE_AND_NULL(data.key_regexp);
ret = CONFIG_INVALID_PATTERN;
goto free_strings;
}
@@ -366,30 +403,32 @@ static int get_value(const char *key_, const char *regex_, unsigned flags)
ret = CONFIG_INVALID_KEY;
goto free_strings;
}
+
+ data.key = key;
}
if (regex_ && (flags & CONFIG_FLAGS_FIXED_VALUE))
- value_pattern = regex_;
+ data.value_pattern = regex_;
else if (regex_) {
if (regex_[0] == '!') {
- do_not_match = 1;
+ data.do_not_match = 1;
regex_++;
}
- regexp = (regex_t*)xmalloc(sizeof(regex_t));
- if (regcomp(regexp, regex_, REG_EXTENDED)) {
+ data.regexp = (regex_t*)xmalloc(sizeof(regex_t));
+ if (regcomp(data.regexp, regex_, REG_EXTENDED)) {
error(_("invalid pattern: %s"), regex_);
- FREE_AND_NULL(regexp);
+ FREE_AND_NULL(data.regexp);
ret = CONFIG_INVALID_PATTERN;
goto free_strings;
}
}
- config_with_options(collect_config, &values,
- &given_config_source, the_repository,
- &config_options);
+ config_with_options(collect_config, &data,
+ &opts->source, the_repository,
+ &opts->options);
- if (!values.nr && default_value) {
+ if (!values.nr && display_opts->default_value) {
struct key_value_info kvi = KVI_INIT;
struct strbuf *item;
@@ -397,16 +436,17 @@ static int get_value(const char *key_, const char *regex_, unsigned flags)
ALLOC_GROW(values.items, values.nr + 1, values.alloc);
item = &values.items[values.nr++];
strbuf_init(item, 0);
- if (format_config(item, key_, default_value, &kvi) < 0)
+ if (format_config(display_opts, item, key_,
+ display_opts->default_value, &kvi) < 0)
die(_("failed to format default config value: %s"),
- default_value);
+ display_opts->default_value);
}
ret = !values.nr;
for (i = 0; i < values.nr; i++) {
struct strbuf *buf = values.items + i;
- if (do_all || i == values.nr - 1)
+ if ((get_value_flags & GET_VALUE_ALL) || i == values.nr - 1)
fwrite(buf->buf, 1, buf->len, stdout);
strbuf_release(buf);
}
@@ -414,20 +454,20 @@ static int get_value(const char *key_, const char *regex_, unsigned flags)
free_strings:
free(key);
- if (key_regexp) {
- regfree(key_regexp);
- free(key_regexp);
+ if (data.key_regexp) {
+ regfree(data.key_regexp);
+ free(data.key_regexp);
}
- if (regexp) {
- regfree(regexp);
- free(regexp);
+ if (data.regexp) {
+ regfree(data.regexp);
+ free(data.regexp);
}
return ret;
}
static char *normalize_value(const char *key, const char *value,
- struct key_value_info *kvi)
+ int type, struct key_value_info *kvi)
{
if (!value)
return NULL;
@@ -478,97 +518,113 @@ static char *normalize_value(const char *key, const char *value,
BUG("cannot normalize type %d", type);
}
-static int get_color_found;
-static const char *get_color_slot;
-static const char *get_colorbool_slot;
-static char parsed_color[COLOR_MAXLEN];
+struct get_color_config_data {
+ int get_color_found;
+ const char *get_color_slot;
+ char parsed_color[COLOR_MAXLEN];
+};
static int git_get_color_config(const char *var, const char *value,
const struct config_context *ctx UNUSED,
- void *cb UNUSED)
+ void *cb)
{
- if (!strcmp(var, get_color_slot)) {
+ struct get_color_config_data *data = cb;
+
+ if (!strcmp(var, data->get_color_slot)) {
if (!value)
config_error_nonbool(var);
- if (color_parse(value, parsed_color) < 0)
+ if (color_parse(value, data->parsed_color) < 0)
return -1;
- get_color_found = 1;
+ data->get_color_found = 1;
}
return 0;
}
-static void get_color(const char *var, const char *def_color)
+static void get_color(const struct config_location_options *opts,
+ const char *var, const char *def_color)
{
- get_color_slot = var;
- get_color_found = 0;
- parsed_color[0] = '\0';
- config_with_options(git_get_color_config, NULL,
- &given_config_source, the_repository,
- &config_options);
-
- if (!get_color_found && def_color) {
- if (color_parse(def_color, parsed_color) < 0)
+ struct get_color_config_data data = {
+ .get_color_slot = var,
+ .parsed_color[0] = '\0',
+ };
+
+ config_with_options(git_get_color_config, &data,
+ &opts->source, the_repository,
+ &opts->options);
+
+ if (!data.get_color_found && def_color) {
+ if (color_parse(def_color, data.parsed_color) < 0)
die(_("unable to parse default color value"));
}
- fputs(parsed_color, stdout);
+ fputs(data.parsed_color, stdout);
}
-static int get_colorbool_found;
-static int get_diff_color_found;
-static int get_color_ui_found;
+struct get_colorbool_config_data {
+ int get_colorbool_found;
+ int get_diff_color_found;
+ int get_color_ui_found;
+ const char *get_colorbool_slot;
+};
+
static int git_get_colorbool_config(const char *var, const char *value,
const struct config_context *ctx UNUSED,
- void *data UNUSED)
+ void *cb)
{
- if (!strcmp(var, get_colorbool_slot))
- get_colorbool_found = git_config_colorbool(var, value);
+ struct get_colorbool_config_data *data = cb;
+
+ if (!strcmp(var, data->get_colorbool_slot))
+ data->get_colorbool_found = git_config_colorbool(var, value);
else if (!strcmp(var, "diff.color"))
- get_diff_color_found = git_config_colorbool(var, value);
+ data->get_diff_color_found = git_config_colorbool(var, value);
else if (!strcmp(var, "color.ui"))
- get_color_ui_found = git_config_colorbool(var, value);
+ data->get_color_ui_found = git_config_colorbool(var, value);
return 0;
}
-static int get_colorbool(const char *var, int print)
+static int get_colorbool(const struct config_location_options *opts,
+ const char *var, int print)
{
- get_colorbool_slot = var;
- get_colorbool_found = -1;
- get_diff_color_found = -1;
- get_color_ui_found = -1;
- config_with_options(git_get_colorbool_config, NULL,
- &given_config_source, the_repository,
- &config_options);
-
- if (get_colorbool_found < 0) {
- if (!strcmp(get_colorbool_slot, "color.diff"))
- get_colorbool_found = get_diff_color_found;
- if (get_colorbool_found < 0)
- get_colorbool_found = get_color_ui_found;
- }
-
- if (get_colorbool_found < 0)
+ struct get_colorbool_config_data data = {
+ .get_colorbool_slot = var,
+ .get_colorbool_found = -1,
+ .get_diff_color_found = -1,
+ .get_color_ui_found = -1,
+ };
+
+ config_with_options(git_get_colorbool_config, &data,
+ &opts->source, the_repository,
+ &opts->options);
+
+ if (data.get_colorbool_found < 0) {
+ if (!strcmp(data.get_colorbool_slot, "color.diff"))
+ data.get_colorbool_found = data.get_diff_color_found;
+ if (data.get_colorbool_found < 0)
+ data.get_colorbool_found = data.get_color_ui_found;
+ }
+
+ if (data.get_colorbool_found < 0)
/* default value if none found in config */
- get_colorbool_found = GIT_COLOR_AUTO;
+ data.get_colorbool_found = GIT_COLOR_AUTO;
- get_colorbool_found = want_color(get_colorbool_found);
+ data.get_colorbool_found = want_color(data.get_colorbool_found);
if (print) {
- printf("%s\n", get_colorbool_found ? "true" : "false");
+ printf("%s\n", data.get_colorbool_found ? "true" : "false");
return 0;
} else
- return get_colorbool_found ? 0 : 1;
+ return data.get_colorbool_found ? 0 : 1;
}
-static void check_write(void)
+static void check_write(const struct git_config_source *source)
{
- if (!given_config_source.file && !startup_info->have_repository)
+ if (!source->file && !startup_info->have_repository)
die(_("not in a git directory"));
- if (given_config_source.use_stdin)
+ if (source->use_stdin)
die(_("writing to stdin is not supported"));
- if (given_config_source.blob)
+ if (source->blob)
die(_("writing config blobs is not supported"));
}
@@ -605,10 +661,13 @@ static int urlmatch_collect_fn(const char *var, const char *value,
return 0;
}
-static int get_urlmatch(const char *var, const char *url)
+static int get_urlmatch(const struct config_location_options *opts,
+ const struct config_display_options *_display_opts,
+ const char *var, const char *url)
{
int ret;
char *section_tail;
+ struct config_display_options display_opts = *_display_opts;
struct string_list_item *item;
struct urlmatch_config config = URLMATCH_CONFIG_INIT;
struct string_list values = STRING_LIST_INIT_DUP;
@@ -625,15 +684,15 @@ static int get_urlmatch(const char *var, const char *url)
if (section_tail) {
*section_tail = '\0';
config.key = section_tail + 1;
- show_keys = 0;
+ display_opts.show_keys = 0;
} else {
config.key = NULL;
- show_keys = 1;
+ display_opts.show_keys = 1;
}
config_with_options(urlmatch_config_entry, &config,
- &given_config_source, the_repository,
- &config_options);
+ &opts->source, the_repository,
+ &opts->options);
ret = !values.nr;
@@ -641,7 +700,7 @@ static int get_urlmatch(const char *var, const char *url)
struct urlmatch_current_candidate_value *matched = item->util;
struct strbuf buf = STRBUF_INIT;
- format_config(&buf, item->string,
+ format_config(&display_opts, &buf, item->string,
matched->value_is_null ? NULL : matched->value.buf,
&matched->kvi);
fwrite(buf.buf, 1, buf.len, stdout);
@@ -671,47 +730,39 @@ static char *default_user_config(void)
return strbuf_detach(&buf, NULL);
}
-int cmd_config(int argc, const char **argv, const char *prefix)
+static void location_options_init(struct config_location_options *opts,
+ const char *prefix)
{
- int nongit = !startup_info->have_repository;
- char *value = NULL;
- int flags = 0;
- int ret = 0;
- struct key_value_info default_kvi = KVI_INIT;
-
- given_config_source.file = xstrdup_or_null(getenv(CONFIG_ENVIRONMENT));
+ if (!opts->source.file)
+ opts->source.file = opts->file_to_free =
+ xstrdup_or_null(getenv(CONFIG_ENVIRONMENT));
- argc = parse_options(argc, argv, prefix, builtin_config_options,
- builtin_config_usage,
- PARSE_OPT_STOP_AT_NON_OPTION);
-
- if (use_global_config + use_system_config + use_local_config +
- use_worktree_config +
- !!given_config_source.file + !!given_config_source.blob > 1) {
+ if (opts->use_global_config + opts->use_system_config +
+ opts->use_local_config + opts->use_worktree_config +
+ !!opts->source.file + !!opts->source.blob > 1) {
error(_("only one config file at a time"));
- usage_builtin_config();
+ exit(129);
}
- if (nongit) {
- if (use_local_config)
+ if (!startup_info->have_repository) {
+ if (opts->use_local_config)
die(_("--local can only be used inside a git repository"));
- if (given_config_source.blob)
+ if (opts->source.blob)
die(_("--blob can only be used inside a git repository"));
- if (use_worktree_config)
+ if (opts->use_worktree_config)
die(_("--worktree can only be used inside a git repository"));
-
}
- if (given_config_source.file &&
- !strcmp(given_config_source.file, "-")) {
- given_config_source.file = NULL;
- given_config_source.use_stdin = 1;
- given_config_source.scope = CONFIG_SCOPE_COMMAND;
+ if (opts->source.file &&
+ !strcmp(opts->source.file, "-")) {
+ opts->source.file = NULL;
+ opts->source.use_stdin = 1;
+ opts->source.scope = CONFIG_SCOPE_COMMAND;
}
- if (use_global_config) {
- given_config_source.file = git_global_config();
- if (!given_config_source.file)
+ if (opts->use_global_config) {
+ opts->source.file = opts->file_to_free = git_global_config();
+ if (!opts->source.file)
/*
* It is unknown if HOME/.gitconfig exists, so
* we do not know if we should write to XDG
@@ -719,17 +770,18 @@ int cmd_config(int argc, const char **argv, const char *prefix)
* is set and points at a sane location.
*/
die(_("$HOME not set"));
- given_config_source.scope = CONFIG_SCOPE_GLOBAL;
- } else if (use_system_config) {
- given_config_source.file = git_system_config();
- given_config_source.scope = CONFIG_SCOPE_SYSTEM;
- } else if (use_local_config) {
- given_config_source.file = git_pathdup("config");
- given_config_source.scope = CONFIG_SCOPE_LOCAL;
- } else if (use_worktree_config) {
+ opts->source.scope = CONFIG_SCOPE_GLOBAL;
+ } else if (opts->use_system_config) {
+ opts->source.file = opts->file_to_free = git_system_config();
+ opts->source.scope = CONFIG_SCOPE_SYSTEM;
+ } else if (opts->use_local_config) {
+ opts->source.file = opts->file_to_free = git_pathdup("config");
+ opts->source.scope = CONFIG_SCOPE_LOCAL;
+ } else if (opts->use_worktree_config) {
struct worktree **worktrees = get_worktrees();
if (the_repository->repository_format_worktree_config)
- given_config_source.file = git_pathdup("config.worktree");
+ opts->source.file = opts->file_to_free =
+ git_pathdup("config.worktree");
else if (worktrees[0] && worktrees[1])
die(_("--worktree cannot be used with multiple "
"working trees unless the config\n"
@@ -737,76 +789,439 @@ int cmd_config(int argc, const char **argv, const char *prefix)
"Please read \"CONFIGURATION FILE\"\n"
"section in \"git help worktree\" for details"));
else
- given_config_source.file = git_pathdup("config");
- given_config_source.scope = CONFIG_SCOPE_LOCAL;
+ opts->source.file = opts->file_to_free =
+ git_pathdup("config");
+ opts->source.scope = CONFIG_SCOPE_LOCAL;
free_worktrees(worktrees);
- } else if (given_config_source.file) {
- if (!is_absolute_path(given_config_source.file) && prefix)
- given_config_source.file =
- prefix_filename(prefix, given_config_source.file);
- given_config_source.scope = CONFIG_SCOPE_COMMAND;
- } else if (given_config_source.blob) {
- given_config_source.scope = CONFIG_SCOPE_COMMAND;
+ } else if (opts->source.file) {
+ if (!is_absolute_path(opts->source.file) && prefix)
+ opts->source.file = opts->file_to_free =
+ prefix_filename(prefix, opts->source.file);
+ opts->source.scope = CONFIG_SCOPE_COMMAND;
+ } else if (opts->source.blob) {
+ opts->source.scope = CONFIG_SCOPE_COMMAND;
}
- if (respect_includes_opt == -1)
- config_options.respect_includes = !given_config_source.file;
+ if (opts->respect_includes_opt == -1)
+ opts->options.respect_includes = !opts->source.file;
else
- config_options.respect_includes = respect_includes_opt;
- if (!nongit) {
- config_options.commondir = get_git_common_dir();
- config_options.git_dir = get_git_dir();
+ opts->options.respect_includes = opts->respect_includes_opt;
+ if (startup_info->have_repository) {
+ opts->options.commondir = get_git_common_dir();
+ opts->options.git_dir = get_git_dir();
+ }
+}
+
+static void location_options_release(struct config_location_options *opts)
+{
+ free(opts->file_to_free);
+}
+
+static void display_options_init(struct config_display_options *opts)
+{
+ if (opts->end_nul) {
+ opts->term = '\0';
+ opts->delim = '\n';
+ opts->key_delim = '\n';
}
+}
- if (end_nul) {
- term = '\0';
- delim = '\n';
- key_delim = '\n';
+static int cmd_config_list(int argc, const char **argv, const char *prefix)
+{
+ struct config_location_options location_opts = CONFIG_LOCATION_OPTIONS_INIT;
+ struct config_display_options display_opts = CONFIG_DISPLAY_OPTIONS_INIT;
+ struct option opts[] = {
+ CONFIG_LOCATION_OPTIONS(location_opts),
+ CONFIG_DISPLAY_OPTIONS(display_opts),
+ OPT_GROUP(N_("Other")),
+ OPT_BOOL(0, "includes", &location_opts.respect_includes_opt,
+ N_("respect include directives on lookup")),
+ OPT_END(),
+ };
+
+ argc = parse_options(argc, argv, prefix, opts, builtin_config_list_usage, 0);
+ check_argc(argc, 0, 0);
+
+ location_options_init(&location_opts, prefix);
+ display_options_init(&display_opts);
+
+ setup_auto_pager("config", 1);
+
+ if (config_with_options(show_all_config, &display_opts,
+ &location_opts.source, the_repository,
+ &location_opts.options) < 0) {
+ if (location_opts.source.file)
+ die_errno(_("unable to read config file '%s'"),
+ location_opts.source.file);
+ else
+ die(_("error processing config file(s)"));
}
- if ((actions & (ACTION_GET_COLOR|ACTION_GET_COLORBOOL)) && type) {
- error(_("--get-color and variable type are incoherent"));
- usage_builtin_config();
+ location_options_release(&location_opts);
+ return 0;
+}
+
+static int cmd_config_get(int argc, const char **argv, const char *prefix)
+{
+ struct config_location_options location_opts = CONFIG_LOCATION_OPTIONS_INIT;
+ struct config_display_options display_opts = CONFIG_DISPLAY_OPTIONS_INIT;
+ const char *value_pattern = NULL, *url = NULL;
+ int flags = 0;
+ unsigned get_value_flags = 0;
+ struct option opts[] = {
+ CONFIG_LOCATION_OPTIONS(location_opts),
+ OPT_GROUP(N_("Filter options")),
+ OPT_BIT(0, "all", &get_value_flags, N_("return all values for multi-valued config options"), GET_VALUE_ALL),
+ OPT_BIT(0, "regexp", &get_value_flags, N_("interpret the name as a regular expression"), GET_VALUE_KEY_REGEXP),
+ OPT_STRING(0, "value", &value_pattern, N_("pattern"), N_("show config with values matching the pattern")),
+ OPT_BIT(0, "fixed-value", &flags, N_("use string equality when comparing values to value pattern"), CONFIG_FLAGS_FIXED_VALUE),
+ OPT_STRING(0, "url", &url, N_("URL"), N_("show config matching the given URL")),
+ CONFIG_DISPLAY_OPTIONS(display_opts),
+ OPT_GROUP(N_("Other")),
+ OPT_BOOL(0, "includes", &location_opts.respect_includes_opt,
+ N_("respect include directives on lookup")),
+ OPT_STRING(0, "default", &display_opts.default_value,
+ N_("value"), N_("use default value when missing entry")),
+ OPT_END(),
+ };
+ int ret;
+
+ argc = parse_options(argc, argv, prefix, opts, builtin_config_get_usage,
+ PARSE_OPT_STOP_AT_NON_OPTION);
+ check_argc(argc, 1, 1);
+
+ if ((flags & CONFIG_FLAGS_FIXED_VALUE) && !value_pattern)
+ die(_("--fixed-value only applies with 'value-pattern'"));
+ if (display_opts.default_value &&
+ ((get_value_flags & GET_VALUE_ALL) || url))
+ die(_("--default= cannot be used with --all or --url="));
+ if (url && ((get_value_flags & GET_VALUE_ALL) ||
+ (get_value_flags & GET_VALUE_KEY_REGEXP) ||
+ value_pattern))
+ die(_("--url= cannot be used with --all, --regexp or --value"));
+
+ location_options_init(&location_opts, prefix);
+ display_options_init(&display_opts);
+
+ setup_auto_pager("config", 1);
+
+ if (url)
+ ret = get_urlmatch(&location_opts, &display_opts, argv[0], url);
+ else
+ ret = get_value(&location_opts, &display_opts, argv[0], value_pattern,
+ get_value_flags, flags);
+
+ location_options_release(&location_opts);
+ return ret;
+}
+
+static int cmd_config_set(int argc, const char **argv, const char *prefix)
+{
+ struct config_location_options location_opts = CONFIG_LOCATION_OPTIONS_INIT;
+ const char *value_pattern = NULL, *comment_arg = NULL;
+ char *comment = NULL;
+ int flags = 0, append = 0, type = 0;
+ struct option opts[] = {
+ CONFIG_LOCATION_OPTIONS(location_opts),
+ CONFIG_TYPE_OPTIONS(type),
+ OPT_GROUP(N_("Filter")),
+ OPT_BIT(0, "all", &flags, N_("replace multi-valued config option with new value"), CONFIG_FLAGS_MULTI_REPLACE),
+ OPT_STRING(0, "value", &value_pattern, N_("pattern"), N_("show config with values matching the pattern")),
+ OPT_BIT(0, "fixed-value", &flags, N_("use string equality when comparing values to value pattern"), CONFIG_FLAGS_FIXED_VALUE),
+ OPT_GROUP(N_("Other")),
+ OPT_STRING(0, "comment", &comment_arg, N_("value"), N_("human-readable comment string (# will be prepended as needed)")),
+ OPT_BOOL(0, "append", &append, N_("add a new line without altering any existing values")),
+ OPT_END(),
+ };
+ struct key_value_info default_kvi = KVI_INIT;
+ char *value;
+ int ret;
+
+ argc = parse_options(argc, argv, prefix, opts, builtin_config_set_usage,
+ PARSE_OPT_STOP_AT_NON_OPTION);
+ check_argc(argc, 2, 2);
+
+ if ((flags & CONFIG_FLAGS_FIXED_VALUE) && !value_pattern)
+ die(_("--fixed-value only applies with --value=<pattern>"));
+ if (append && value_pattern)
+ die(_("--append cannot be used with --value=<pattern>"));
+ if (append)
+ value_pattern = CONFIG_REGEX_NONE;
+
+ comment = git_config_prepare_comment_string(comment_arg);
+
+ location_options_init(&location_opts, prefix);
+ check_write(&location_opts.source);
+
+ value = normalize_value(argv[0], argv[1], type, &default_kvi);
+
+ if ((flags & CONFIG_FLAGS_MULTI_REPLACE) || value_pattern) {
+ ret = git_config_set_multivar_in_file_gently(location_opts.source.file,
+ argv[0], value, value_pattern,
+ comment, flags);
+ } else {
+ ret = git_config_set_in_file_gently(location_opts.source.file,
+ argv[0], comment, value);
+ if (ret == CONFIG_NOTHING_SET)
+ error(_("cannot overwrite multiple values with a single value\n"
+ " Use a regexp, --add or --replace-all to change %s."), argv[0]);
}
- if (HAS_MULTI_BITS(actions)) {
- error(_("only one action at a time"));
- usage_builtin_config();
+ location_options_release(&location_opts);
+ free(comment);
+ free(value);
+ return ret;
+}
+
+static int cmd_config_unset(int argc, const char **argv, const char *prefix)
+{
+ struct config_location_options location_opts = CONFIG_LOCATION_OPTIONS_INIT;
+ const char *value_pattern = NULL;
+ int flags = 0;
+ struct option opts[] = {
+ CONFIG_LOCATION_OPTIONS(location_opts),
+ OPT_GROUP(N_("Filter")),
+ OPT_BIT(0, "all", &flags, N_("replace multi-valued config option with new value"), CONFIG_FLAGS_MULTI_REPLACE),
+ OPT_STRING(0, "value", &value_pattern, N_("pattern"), N_("show config with values matching the pattern")),
+ OPT_BIT(0, "fixed-value", &flags, N_("use string equality when comparing values to value pattern"), CONFIG_FLAGS_FIXED_VALUE),
+ OPT_END(),
+ };
+ int ret;
+
+ argc = parse_options(argc, argv, prefix, opts, builtin_config_unset_usage,
+ PARSE_OPT_STOP_AT_NON_OPTION);
+ check_argc(argc, 1, 1);
+
+ if ((flags & CONFIG_FLAGS_FIXED_VALUE) && !value_pattern)
+ die(_("--fixed-value only applies with 'value-pattern'"));
+
+ location_options_init(&location_opts, prefix);
+ check_write(&location_opts.source);
+
+ if ((flags & CONFIG_FLAGS_MULTI_REPLACE) || value_pattern)
+ ret = git_config_set_multivar_in_file_gently(location_opts.source.file,
+ argv[0], NULL, value_pattern,
+ NULL, flags);
+ else
+ ret = git_config_set_in_file_gently(location_opts.source.file, argv[0],
+ NULL, NULL);
+
+ location_options_release(&location_opts);
+ return ret;
+}
+
+static int cmd_config_rename_section(int argc, const char **argv, const char *prefix)
+{
+ struct config_location_options location_opts = CONFIG_LOCATION_OPTIONS_INIT;
+ struct option opts[] = {
+ CONFIG_LOCATION_OPTIONS(location_opts),
+ OPT_END(),
+ };
+ int ret;
+
+ argc = parse_options(argc, argv, prefix, opts, builtin_config_rename_section_usage,
+ PARSE_OPT_STOP_AT_NON_OPTION);
+ check_argc(argc, 2, 2);
+
+ location_options_init(&location_opts, prefix);
+ check_write(&location_opts.source);
+
+ ret = git_config_rename_section_in_file(location_opts.source.file,
+ argv[0], argv[1]);
+ if (ret < 0)
+ goto out;
+ else if (!ret)
+ die(_("no such section: %s"), argv[0]);
+ ret = 0;
+
+out:
+ location_options_release(&location_opts);
+ return ret;
+}
+
+static int cmd_config_remove_section(int argc, const char **argv, const char *prefix)
+{
+ struct config_location_options location_opts = CONFIG_LOCATION_OPTIONS_INIT;
+ struct option opts[] = {
+ CONFIG_LOCATION_OPTIONS(location_opts),
+ OPT_END(),
+ };
+ int ret;
+
+ argc = parse_options(argc, argv, prefix, opts, builtin_config_remove_section_usage,
+ PARSE_OPT_STOP_AT_NON_OPTION);
+ check_argc(argc, 1, 1);
+
+ location_options_init(&location_opts, prefix);
+ check_write(&location_opts.source);
+
+ ret = git_config_rename_section_in_file(location_opts.source.file,
+ argv[0], NULL);
+ if (ret < 0)
+ goto out;
+ else if (!ret)
+ die(_("no such section: %s"), argv[0]);
+ ret = 0;
+
+out:
+ location_options_release(&location_opts);
+ return ret;
+}
+
+static int show_editor(struct config_location_options *opts)
+{
+ char *config_file;
+
+ if (!opts->source.file && !startup_info->have_repository)
+ die(_("not in a git directory"));
+ if (opts->source.use_stdin)
+ die(_("editing stdin is not supported"));
+ if (opts->source.blob)
+ die(_("editing blobs is not supported"));
+ git_config(git_default_config, NULL);
+ config_file = opts->source.file ?
+ xstrdup(opts->source.file) :
+ git_pathdup("config");
+ if (opts->use_global_config) {
+ int fd = open(config_file, O_CREAT | O_EXCL | O_WRONLY, 0666);
+ if (fd >= 0) {
+ char *content = default_user_config();
+ write_str_in_full(fd, content);
+ free(content);
+ close(fd);
+ }
+ else if (errno != EEXIST)
+ die_errno(_("cannot create configuration file %s"), config_file);
+ }
+ launch_editor(config_file, NULL, NULL);
+ free(config_file);
+
+ return 0;
+}
+
+static int cmd_config_edit(int argc, const char **argv, const char *prefix)
+{
+ struct config_location_options location_opts = CONFIG_LOCATION_OPTIONS_INIT;
+ struct option opts[] = {
+ CONFIG_LOCATION_OPTIONS(location_opts),
+ OPT_END(),
+ };
+ int ret;
+
+ argc = parse_options(argc, argv, prefix, opts, builtin_config_edit_usage, 0);
+ check_argc(argc, 0, 0);
+
+ location_options_init(&location_opts, prefix);
+ check_write(&location_opts.source);
+
+ ret = show_editor(&location_opts);
+ location_options_release(&location_opts);
+ return ret;
+}
+
+static int cmd_config_actions(int argc, const char **argv, const char *prefix)
+{
+ enum {
+ ACTION_GET = (1<<0),
+ ACTION_GET_ALL = (1<<1),
+ ACTION_GET_REGEXP = (1<<2),
+ ACTION_REPLACE_ALL = (1<<3),
+ ACTION_ADD = (1<<4),
+ ACTION_UNSET = (1<<5),
+ ACTION_UNSET_ALL = (1<<6),
+ ACTION_RENAME_SECTION = (1<<7),
+ ACTION_REMOVE_SECTION = (1<<8),
+ ACTION_LIST = (1<<9),
+ ACTION_EDIT = (1<<10),
+ ACTION_SET = (1<<11),
+ ACTION_SET_ALL = (1<<12),
+ ACTION_GET_COLOR = (1<<13),
+ ACTION_GET_COLORBOOL = (1<<14),
+ ACTION_GET_URLMATCH = (1<<15),
+ };
+ struct config_location_options location_opts = CONFIG_LOCATION_OPTIONS_INIT;
+ struct config_display_options display_opts = CONFIG_DISPLAY_OPTIONS_INIT;
+ const char *comment_arg = NULL;
+ int actions = 0;
+ unsigned flags = 0;
+ struct option opts[] = {
+ CONFIG_LOCATION_OPTIONS(location_opts),
+ OPT_GROUP(N_("Action")),
+ OPT_CMDMODE(0, "get", &actions, N_("get value: name [<value-pattern>]"), ACTION_GET),
+ OPT_CMDMODE(0, "get-all", &actions, N_("get all values: key [<value-pattern>]"), ACTION_GET_ALL),
+ OPT_CMDMODE(0, "get-regexp", &actions, N_("get values for regexp: name-regex [<value-pattern>]"), ACTION_GET_REGEXP),
+ OPT_CMDMODE(0, "get-urlmatch", &actions, N_("get value specific for the URL: section[.var] URL"), ACTION_GET_URLMATCH),
+ OPT_CMDMODE(0, "replace-all", &actions, N_("replace all matching variables: name value [<value-pattern>]"), ACTION_REPLACE_ALL),
+ OPT_CMDMODE(0, "add", &actions, N_("add a new variable: name value"), ACTION_ADD),
+ OPT_CMDMODE(0, "unset", &actions, N_("remove a variable: name [<value-pattern>]"), ACTION_UNSET),
+ OPT_CMDMODE(0, "unset-all", &actions, N_("remove all matches: name [<value-pattern>]"), ACTION_UNSET_ALL),
+ OPT_CMDMODE(0, "rename-section", &actions, N_("rename section: old-name new-name"), ACTION_RENAME_SECTION),
+ OPT_CMDMODE(0, "remove-section", &actions, N_("remove a section: name"), ACTION_REMOVE_SECTION),
+ OPT_CMDMODE('l', "list", &actions, N_("list all"), ACTION_LIST),
+ OPT_CMDMODE('e', "edit", &actions, N_("open an editor"), ACTION_EDIT),
+ OPT_CMDMODE(0, "get-color", &actions, N_("find the color configured: slot [<default>]"), ACTION_GET_COLOR),
+ OPT_CMDMODE(0, "get-colorbool", &actions, N_("find the color setting: slot [<stdout-is-tty>]"), ACTION_GET_COLORBOOL),
+ CONFIG_DISPLAY_OPTIONS(display_opts),
+ OPT_GROUP(N_("Other")),
+ OPT_STRING(0, "default", &display_opts.default_value,
+ N_("value"), N_("with --get, use default value when missing entry")),
+ OPT_STRING(0, "comment", &comment_arg, N_("value"), N_("human-readable comment string (# will be prepended as needed)")),
+ OPT_BIT(0, "fixed-value", &flags, N_("use string equality when comparing values to value pattern"), CONFIG_FLAGS_FIXED_VALUE),
+ OPT_BOOL(0, "includes", &location_opts.respect_includes_opt,
+ N_("respect include directives on lookup")),
+ OPT_END(),
+ };
+ char *value = NULL, *comment = NULL;
+ int ret = 0;
+ struct key_value_info default_kvi = KVI_INIT;
+
+ argc = parse_options(argc, argv, prefix, opts,
+ builtin_config_usage,
+ PARSE_OPT_STOP_AT_NON_OPTION);
+
+ location_options_init(&location_opts, prefix);
+ display_options_init(&display_opts);
+
+ if ((actions & (ACTION_GET_COLOR|ACTION_GET_COLORBOOL)) && display_opts.type) {
+ error(_("--get-color and variable type are incoherent"));
+ exit(129);
}
+
if (actions == 0)
switch (argc) {
case 1: actions = ACTION_GET; break;
case 2: actions = ACTION_SET; break;
case 3: actions = ACTION_SET_ALL; break;
default:
- usage_builtin_config();
+ error(_("no action specified"));
+ exit(129);
}
- if (omit_values &&
+ if (display_opts.omit_values &&
!(actions == ACTION_LIST || actions == ACTION_GET_REGEXP)) {
error(_("--name-only is only applicable to --list or --get-regexp"));
- usage_builtin_config();
+ exit(129);
}
- if (show_origin && !(actions &
+ if (display_opts.show_origin && !(actions &
(ACTION_GET|ACTION_GET_ALL|ACTION_GET_REGEXP|ACTION_LIST))) {
error(_("--show-origin is only applicable to --get, --get-all, "
"--get-regexp, and --list"));
- usage_builtin_config();
+ exit(129);
}
- if (default_value && !(actions & ACTION_GET)) {
+ if (display_opts.default_value && !(actions & ACTION_GET)) {
error(_("--default is only applicable to --get"));
- usage_builtin_config();
+ exit(129);
}
- if (comment &&
+ if (comment_arg &&
!(actions & (ACTION_ADD|ACTION_SET|ACTION_SET_ALL|ACTION_REPLACE_ALL))) {
error(_("--comment is only applicable to add/set/replace operations"));
- usage_builtin_config();
+ exit(129);
}
/* check usage of --fixed-value */
- if (fixed_value) {
+ if (flags & CONFIG_FLAGS_FIXED_VALUE) {
int allowed_usage = 0;
switch (actions) {
@@ -835,148 +1250,125 @@ int cmd_config(int argc, const char **argv, const char *prefix)
if (!allowed_usage) {
error(_("--fixed-value only applies with 'value-pattern'"));
- usage_builtin_config();
+ exit(129);
}
-
- flags |= CONFIG_FLAGS_FIXED_VALUE;
}
- comment = git_config_prepare_comment_string(comment);
+ comment = git_config_prepare_comment_string(comment_arg);
- if (actions & PAGING_ACTIONS)
+ /*
+ * The following actions may produce more than one line of output and
+ * should therefore be paged.
+ */
+ if (actions & (ACTION_LIST | ACTION_GET_ALL | ACTION_GET_REGEXP | ACTION_GET_URLMATCH))
setup_auto_pager("config", 1);
if (actions == ACTION_LIST) {
check_argc(argc, 0, 0);
- if (config_with_options(show_all_config, NULL,
- &given_config_source, the_repository,
- &config_options) < 0) {
- if (given_config_source.file)
+ if (config_with_options(show_all_config, &display_opts,
+ &location_opts.source, the_repository,
+ &location_opts.options) < 0) {
+ if (location_opts.source.file)
die_errno(_("unable to read config file '%s'"),
- given_config_source.file);
+ location_opts.source.file);
else
die(_("error processing config file(s)"));
}
}
else if (actions == ACTION_EDIT) {
- char *config_file;
-
- check_argc(argc, 0, 0);
- if (!given_config_source.file && nongit)
- die(_("not in a git directory"));
- if (given_config_source.use_stdin)
- die(_("editing stdin is not supported"));
- if (given_config_source.blob)
- die(_("editing blobs is not supported"));
- git_config(git_default_config, NULL);
- config_file = given_config_source.file ?
- xstrdup(given_config_source.file) :
- git_pathdup("config");
- if (use_global_config) {
- int fd = open(config_file, O_CREAT | O_EXCL | O_WRONLY, 0666);
- if (fd >= 0) {
- char *content = default_user_config();
- write_str_in_full(fd, content);
- free(content);
- close(fd);
- }
- else if (errno != EEXIST)
- die_errno(_("cannot create configuration file %s"), config_file);
- }
- launch_editor(config_file, NULL, NULL);
- free(config_file);
+ ret = show_editor(&location_opts);
}
else if (actions == ACTION_SET) {
- check_write();
+ check_write(&location_opts.source);
check_argc(argc, 2, 2);
- value = normalize_value(argv[0], argv[1], &default_kvi);
- ret = git_config_set_in_file_gently(given_config_source.file, argv[0], comment, value);
+ value = normalize_value(argv[0], argv[1], display_opts.type, &default_kvi);
+ ret = git_config_set_in_file_gently(location_opts.source.file, argv[0], comment, value);
if (ret == CONFIG_NOTHING_SET)
error(_("cannot overwrite multiple values with a single value\n"
" Use a regexp, --add or --replace-all to change %s."), argv[0]);
}
else if (actions == ACTION_SET_ALL) {
- check_write();
+ check_write(&location_opts.source);
check_argc(argc, 2, 3);
- value = normalize_value(argv[0], argv[1], &default_kvi);
- ret = git_config_set_multivar_in_file_gently(given_config_source.file,
+ value = normalize_value(argv[0], argv[1], display_opts.type, &default_kvi);
+ ret = git_config_set_multivar_in_file_gently(location_opts.source.file,
argv[0], value, argv[2],
comment, flags);
}
else if (actions == ACTION_ADD) {
- check_write();
+ check_write(&location_opts.source);
check_argc(argc, 2, 2);
- value = normalize_value(argv[0], argv[1], &default_kvi);
- ret = git_config_set_multivar_in_file_gently(given_config_source.file,
+ value = normalize_value(argv[0], argv[1], display_opts.type, &default_kvi);
+ ret = git_config_set_multivar_in_file_gently(location_opts.source.file,
argv[0], value,
CONFIG_REGEX_NONE,
comment, flags);
}
else if (actions == ACTION_REPLACE_ALL) {
- check_write();
+ check_write(&location_opts.source);
check_argc(argc, 2, 3);
- value = normalize_value(argv[0], argv[1], &default_kvi);
- ret = git_config_set_multivar_in_file_gently(given_config_source.file,
+ value = normalize_value(argv[0], argv[1], display_opts.type, &default_kvi);
+ ret = git_config_set_multivar_in_file_gently(location_opts.source.file,
argv[0], value, argv[2],
comment, flags | CONFIG_FLAGS_MULTI_REPLACE);
}
else if (actions == ACTION_GET) {
check_argc(argc, 1, 2);
- return get_value(argv[0], argv[1], flags);
+ ret = get_value(&location_opts, &display_opts, argv[0], argv[1],
+ 0, flags);
}
else if (actions == ACTION_GET_ALL) {
- do_all = 1;
check_argc(argc, 1, 2);
- return get_value(argv[0], argv[1], flags);
+ ret = get_value(&location_opts, &display_opts, argv[0], argv[1],
+ GET_VALUE_ALL, flags);
}
else if (actions == ACTION_GET_REGEXP) {
- show_keys = 1;
- use_key_regexp = 1;
- do_all = 1;
+ display_opts.show_keys = 1;
check_argc(argc, 1, 2);
- return get_value(argv[0], argv[1], flags);
+ ret = get_value(&location_opts, &display_opts, argv[0], argv[1],
+ GET_VALUE_ALL|GET_VALUE_KEY_REGEXP, flags);
}
else if (actions == ACTION_GET_URLMATCH) {
check_argc(argc, 2, 2);
- return get_urlmatch(argv[0], argv[1]);
+ ret = get_urlmatch(&location_opts, &display_opts, argv[0], argv[1]);
}
else if (actions == ACTION_UNSET) {
- check_write();
+ check_write(&location_opts.source);
check_argc(argc, 1, 2);
if (argc == 2)
- return git_config_set_multivar_in_file_gently(given_config_source.file,
- argv[0], NULL, argv[1],
- NULL, flags);
+ ret = git_config_set_multivar_in_file_gently(location_opts.source.file,
+ argv[0], NULL, argv[1],
+ NULL, flags);
else
- return git_config_set_in_file_gently(given_config_source.file,
- argv[0], NULL, NULL);
+ ret = git_config_set_in_file_gently(location_opts.source.file,
+ argv[0], NULL, NULL);
}
else if (actions == ACTION_UNSET_ALL) {
- check_write();
+ check_write(&location_opts.source);
check_argc(argc, 1, 2);
- return git_config_set_multivar_in_file_gently(given_config_source.file,
- argv[0], NULL, argv[1],
- NULL, flags | CONFIG_FLAGS_MULTI_REPLACE);
+ ret = git_config_set_multivar_in_file_gently(location_opts.source.file,
+ argv[0], NULL, argv[1],
+ NULL, flags | CONFIG_FLAGS_MULTI_REPLACE);
}
else if (actions == ACTION_RENAME_SECTION) {
- check_write();
+ check_write(&location_opts.source);
check_argc(argc, 2, 2);
- ret = git_config_rename_section_in_file(given_config_source.file,
+ ret = git_config_rename_section_in_file(location_opts.source.file,
argv[0], argv[1]);
if (ret < 0)
- return ret;
+ goto out;
else if (!ret)
die(_("no such section: %s"), argv[0]);
else
ret = 0;
}
else if (actions == ACTION_REMOVE_SECTION) {
- check_write();
+ check_write(&location_opts.source);
check_argc(argc, 1, 1);
- ret = git_config_rename_section_in_file(given_config_source.file,
+ ret = git_config_rename_section_in_file(location_opts.source.file,
argv[0], NULL);
if (ret < 0)
- return ret;
+ goto out;
else if (!ret)
die(_("no such section: %s"), argv[0]);
else
@@ -984,15 +1376,51 @@ int cmd_config(int argc, const char **argv, const char *prefix)
}
else if (actions == ACTION_GET_COLOR) {
check_argc(argc, 1, 2);
- get_color(argv[0], argv[1]);
+ get_color(&location_opts, argv[0], argv[1]);
}
else if (actions == ACTION_GET_COLORBOOL) {
check_argc(argc, 1, 2);
if (argc == 2)
color_stdout_is_tty = git_config_bool("command line", argv[1]);
- return get_colorbool(argv[0], argc == 2);
+ ret = get_colorbool(&location_opts, argv[0], argc == 2);
}
+out:
+ location_options_release(&location_opts);
+ free(comment);
free(value);
return ret;
}
+
+int cmd_config(int argc, const char **argv, const char *prefix)
+{
+ parse_opt_subcommand_fn *subcommand = NULL;
+ struct option subcommand_opts[] = {
+ OPT_SUBCOMMAND("list", &subcommand, cmd_config_list),
+ OPT_SUBCOMMAND("get", &subcommand, cmd_config_get),
+ OPT_SUBCOMMAND("set", &subcommand, cmd_config_set),
+ OPT_SUBCOMMAND("unset", &subcommand, cmd_config_unset),
+ OPT_SUBCOMMAND("rename-section", &subcommand, cmd_config_rename_section),
+ OPT_SUBCOMMAND("remove-section", &subcommand, cmd_config_remove_section),
+ OPT_SUBCOMMAND("edit", &subcommand, cmd_config_edit),
+ OPT_END(),
+ };
+
+ /*
+ * This is somewhat hacky: we first parse the command line while
+ * keeping all args intact in order to determine whether a subcommand
+ * has been specified. If so, we re-parse it a second time, but this
+ * time we drop KEEP_ARGV0. This is so that we don't munge the command
+ * line in case no subcommand was given, which would otherwise confuse
+ * us when parsing the legacy-style modes that don't use subcommands.
+ */
+ argc = parse_options(argc, argv, prefix, subcommand_opts, builtin_config_usage,
+ PARSE_OPT_SUBCOMMAND_OPTIONAL|PARSE_OPT_KEEP_ARGV0|PARSE_OPT_KEEP_UNKNOWN_OPT);
+ if (subcommand) {
+ argc = parse_options(argc, argv, prefix, subcommand_opts, builtin_config_usage,
+ PARSE_OPT_SUBCOMMAND_OPTIONAL|PARSE_OPT_KEEP_UNKNOWN_OPT);
+ return subcommand(argc, argv, prefix);
+ }
+
+ return cmd_config_actions(argc, argv, prefix);
+}
diff --git a/builtin/credential-cache--daemon.c b/builtin/credential-cache--daemon.c
index 17f929dede..4952b22547 100644
--- a/builtin/credential-cache--daemon.c
+++ b/builtin/credential-cache--daemon.c
@@ -115,7 +115,9 @@ static int read_request(FILE *fh, struct credential *c,
return error("client sent bogus timeout line: %s", item.buf);
*timeout = atoi(p);
- if (credential_read(c, fh) < 0)
+ credential_set_all_capabilities(c, CREDENTIAL_OP_INITIAL);
+
+ if (credential_read(c, fh, CREDENTIAL_OP_HELPER) < 0)
return -1;
return 0;
}
@@ -131,8 +133,18 @@ static void serve_one_client(FILE *in, FILE *out)
else if (!strcmp(action.buf, "get")) {
struct credential_cache_entry *e = lookup_credential(&c);
if (e) {
- fprintf(out, "username=%s\n", e->item.username);
- fprintf(out, "password=%s\n", e->item.password);
+ e->item.capa_authtype.request_initial = 1;
+ e->item.capa_authtype.request_helper = 1;
+
+ fprintf(out, "capability[]=authtype\n");
+ if (e->item.username)
+ fprintf(out, "username=%s\n", e->item.username);
+ if (e->item.password)
+ fprintf(out, "password=%s\n", e->item.password);
+ if (credential_has_capability(&c.capa_authtype, CREDENTIAL_OP_HELPER) && e->item.authtype)
+ fprintf(out, "authtype=%s\n", e->item.authtype);
+ if (credential_has_capability(&c.capa_authtype, CREDENTIAL_OP_HELPER) && e->item.credential)
+ fprintf(out, "credential=%s\n", e->item.credential);
if (e->item.password_expiry_utc != TIME_MAX)
fprintf(out, "password_expiry_utc=%"PRItime"\n",
e->item.password_expiry_utc);
@@ -157,8 +169,10 @@ static void serve_one_client(FILE *in, FILE *out)
else if (!strcmp(action.buf, "store")) {
if (timeout < 0)
warning("cache client didn't specify a timeout");
- else if (!c.username || !c.password)
+ else if ((!c.username || !c.password) && (!c.authtype && !c.credential))
warning("cache client gave us a partial credential");
+ else if (c.ephemeral)
+ warning("not storing ephemeral credential");
else {
remove_credential(&c, 0);
cache_credential(&c, timeout);
diff --git a/builtin/credential-cache.c b/builtin/credential-cache.c
index bef120b537..3db8df70a9 100644
--- a/builtin/credential-cache.c
+++ b/builtin/credential-cache.c
@@ -1,4 +1,5 @@
#include "builtin.h"
+#include "credential.h"
#include "gettext.h"
#include "parse-options.h"
#include "path.h"
@@ -127,6 +128,13 @@ static char *get_socket_path(void)
return socket;
}
+static void announce_capabilities(void)
+{
+ struct credential c = CREDENTIAL_INIT;
+ c.capa_authtype.request_initial = 1;
+ credential_announce_capabilities(&c, stdout);
+}
+
int cmd_credential_cache(int argc, const char **argv, const char *prefix)
{
char *socket_path = NULL;
@@ -163,6 +171,8 @@ int cmd_credential_cache(int argc, const char **argv, const char *prefix)
do_cache(socket_path, op, timeout, FLAG_RELAY);
else if (!strcmp(op, "store"))
do_cache(socket_path, op, timeout, FLAG_RELAY|FLAG_SPAWN);
+ else if (!strcmp(op, "capability"))
+ announce_capabilities();
else
; /* ignore unknown operation */
diff --git a/builtin/credential-store.c b/builtin/credential-store.c
index 4a492411bb..494c809332 100644
--- a/builtin/credential-store.c
+++ b/builtin/credential-store.c
@@ -205,7 +205,7 @@ int cmd_credential_store(int argc, const char **argv, const char *prefix)
if (!fns.nr)
die("unable to set up default path; use --file");
- if (credential_read(&c, stdin) < 0)
+ if (credential_read(&c, stdin, CREDENTIAL_OP_HELPER) < 0)
die("unable to read credential");
if (!strcmp(op, "get"))
diff --git a/builtin/credential.c b/builtin/credential.c
index 7010752987..b72e76dd9a 100644
--- a/builtin/credential.c
+++ b/builtin/credential.c
@@ -17,18 +17,29 @@ int cmd_credential(int argc, const char **argv, const char *prefix UNUSED)
usage(usage_msg);
op = argv[1];
- if (credential_read(&c, stdin) < 0)
+ if (!strcmp(op, "capability")) {
+ credential_set_all_capabilities(&c, CREDENTIAL_OP_INITIAL);
+ credential_announce_capabilities(&c, stdout);
+ return 0;
+ }
+
+ if (credential_read(&c, stdin, CREDENTIAL_OP_INITIAL) < 0)
die("unable to read credential from stdin");
if (!strcmp(op, "fill")) {
- credential_fill(&c);
- credential_write(&c, stdout);
+ credential_fill(&c, 0);
+ credential_next_state(&c);
+ credential_write(&c, stdout, CREDENTIAL_OP_RESPONSE);
} else if (!strcmp(op, "approve")) {
+ credential_set_all_capabilities(&c, CREDENTIAL_OP_HELPER);
credential_approve(&c);
} else if (!strcmp(op, "reject")) {
+ credential_set_all_capabilities(&c, CREDENTIAL_OP_HELPER);
credential_reject(&c);
} else {
usage(usage_msg);
}
+
+ credential_clear(&c);
return 0;
}
diff --git a/builtin/describe.c b/builtin/describe.c
index d6c77a714f..cf8edc4222 100644
--- a/builtin/describe.c
+++ b/builtin/describe.c
@@ -1,4 +1,3 @@
-#define USE_THE_INDEX_VARIABLE
#include "builtin.h"
#include "config.h"
#include "environment.h"
@@ -54,6 +53,10 @@ static const char *diff_index_args[] = {
"diff-index", "--quiet", "HEAD", "--", NULL
};
+static const char *update_index_args[] = {
+ "update-index", "--unmerged", "-q", "--refresh", NULL
+};
+
struct commit_name {
struct hashmap_entry entry;
struct object_id peeled;
@@ -201,7 +204,7 @@ static int get_name(const char *path, const struct object_id *oid,
}
/* Is it annotated? */
- if (!peel_iterated_oid(oid, &peeled)) {
+ if (!peel_iterated_oid(the_repository, oid, &peeled)) {
is_annotated = !oideq(oid, &peeled);
} else {
oidcpy(&peeled, oid);
@@ -638,13 +641,22 @@ int cmd_describe(int argc, const char **argv, const char *prefix)
}
hashmap_init(&names, commit_name_neq, NULL, 0);
- for_each_rawref(get_name, NULL);
+ refs_for_each_rawref(get_main_ref_store(the_repository), get_name,
+ NULL);
if (!hashmap_get_size(&names) && !always)
die(_("No names found, cannot describe anything."));
if (argc == 0) {
if (broken) {
struct child_process cp = CHILD_PROCESS_INIT;
+
+ strvec_pushv(&cp.args, update_index_args);
+ cp.git_cmd = 1;
+ cp.no_stdin = 1;
+ cp.no_stdout = 1;
+ run_command(&cp);
+
+ child_process_init(&cp);
strvec_pushv(&cp.args, diff_index_args);
cp.git_cmd = 1;
cp.no_stdin = 1;
@@ -674,7 +686,7 @@ int cmd_describe(int argc, const char **argv, const char *prefix)
prepare_repo_settings(the_repository);
the_repository->settings.command_requires_full_index = 0;
repo_read_index(the_repository);
- refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED,
+ refresh_index(the_repository->index, REFRESH_QUIET|REFRESH_UNMERGED,
NULL, NULL, NULL);
fd = repo_hold_locked_index(the_repository,
&index_lock, 0);
diff --git a/builtin/diagnose.c b/builtin/diagnose.c
index 4f22eb2b55..4857a4395b 100644
--- a/builtin/diagnose.c
+++ b/builtin/diagnose.c
@@ -18,7 +18,7 @@ int cmd_diagnose(int argc, const char **argv, const char *prefix)
struct tm tm;
enum diagnose_mode mode = DIAGNOSE_STATS;
char *option_output = NULL;
- char *option_suffix = "%Y-%m-%d-%H%M";
+ const char *option_suffix = "%Y-%m-%d-%H%M";
char *prefixed_filename;
const struct option diagnose_options[] = {
diff --git a/builtin/diff-tree.c b/builtin/diff-tree.c
index a8e68ce8ef..0d3c611aac 100644
--- a/builtin/diff-tree.c
+++ b/builtin/diff-tree.c
@@ -1,4 +1,3 @@
-#define USE_THE_INDEX_VARIABLE
#include "builtin.h"
#include "config.h"
#include "diff.h"
@@ -206,7 +205,7 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix)
opt->diffopt.rotate_to_strict = 0;
opt->diffopt.no_free = 1;
if (opt->diffopt.detect_rename) {
- if (!the_index.cache)
+ if (the_repository->index->cache)
repo_read_index(the_repository);
opt->diffopt.setup |= DIFF_SETUP_USE_SIZE_CACHE;
}
diff --git a/builtin/diff.c b/builtin/diff.c
index 6e196e0c7d..9b6cdabe15 100644
--- a/builtin/diff.c
+++ b/builtin/diff.c
@@ -3,7 +3,7 @@
*
* Copyright (c) 2006 Junio C Hamano
*/
-#define USE_THE_INDEX_VARIABLE
+
#include "builtin.h"
#include "config.h"
#include "ewah/ewok.h"
@@ -239,9 +239,9 @@ static void refresh_index_quietly(void)
fd = repo_hold_locked_index(the_repository, &lock_file, 0);
if (fd < 0)
return;
- discard_index(&the_index);
+ discard_index(the_repository->index);
repo_read_index(the_repository);
- refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, NULL, NULL,
+ refresh_index(the_repository->index, REFRESH_QUIET|REFRESH_UNMERGED, NULL, NULL,
NULL);
repo_update_index_if_able(the_repository, &lock_file);
}
@@ -465,6 +465,15 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
no_index = DIFF_NO_INDEX_IMPLICIT;
}
+ /*
+ * When operating outside of a Git repository we need to have a hash
+ * algorithm at hand so that we can generate the blob hashes. We
+ * default to SHA1 here, but may eventually want to change this to be
+ * configurable via a command line option.
+ */
+ if (nongit)
+ repo_set_hash_algo(the_repository, GIT_HASH_SHA1);
+
init_diff_ui_defaults();
git_config(git_diff_ui_config, NULL);
prefix = precompose_argv_prefix(argc, argv, prefix);
diff --git a/builtin/difftool.c b/builtin/difftool.c
index a3c72b8258..dcc68e190c 100644
--- a/builtin/difftool.c
+++ b/builtin/difftool.c
@@ -11,7 +11,7 @@
*
* Copyright (C) 2016 Johannes Schindelin
*/
-#define USE_THE_INDEX_VARIABLE
+
#include "builtin.h"
#include "abspath.h"
#include "config.h"
@@ -117,7 +117,7 @@ static int use_wt_file(const char *workdir, const char *name,
int fd = open(buf.buf, O_RDONLY);
if (fd >= 0 &&
- !index_fd(&the_index, &wt_oid, fd, &st, OBJ_BLOB, name, 0)) {
+ !index_fd(the_repository->index, &wt_oid, fd, &st, OBJ_BLOB, name, 0)) {
if (is_null_oid(oid)) {
oidcpy(oid, &wt_oid);
use = 1;
@@ -662,6 +662,9 @@ finish:
free(lbase_dir);
free(rbase_dir);
+ strbuf_release(&info);
+ strbuf_release(&lpath);
+ strbuf_release(&rpath);
strbuf_release(&ldir);
strbuf_release(&rdir);
strbuf_release(&wtdir);
@@ -674,19 +677,15 @@ finish:
static int run_file_diff(int prompt, const char *prefix,
struct child_process *child)
{
- const char *env[] = {
- "GIT_PAGER=", "GIT_EXTERNAL_DIFF=git-difftool--helper", NULL,
- NULL
- };
-
+ strvec_push(&child->env, "GIT_PAGER=");
+ strvec_push(&child->env, "GIT_EXTERNAL_DIFF=git-difftool--helper");
if (prompt > 0)
- env[2] = "GIT_DIFFTOOL_PROMPT=true";
+ strvec_push(&child->env, "GIT_DIFFTOOL_PROMPT=true");
else if (!prompt)
- env[2] = "GIT_DIFFTOOL_NO_PROMPT=true";
+ strvec_push(&child->env, "GIT_DIFFTOOL_NO_PROMPT=true");
child->git_cmd = 1;
child->dir = prefix;
- strvec_pushv(&child->env, env);
return run_command(child);
}
diff --git a/builtin/fast-export.c b/builtin/fast-export.c
index 4693d18cc9..4b6e8c6832 100644
--- a/builtin/fast-export.c
+++ b/builtin/fast-export.c
@@ -415,7 +415,7 @@ static char *generate_fake_oid(void)
struct object_id oid;
char *hex = xmallocz(GIT_MAX_HEXSZ);
- oidclr(&oid);
+ oidclr(&oid, the_repository->hash_algo);
put_be32(oid.hash + hashsz - 4, counter++);
return oid_to_hex_r(hex, &oid);
}
diff --git a/builtin/fast-import.c b/builtin/fast-import.c
index dc5a9d32dd..d21c4053a7 100644
--- a/builtin/fast-import.c
+++ b/builtin/fast-import.c
@@ -1279,8 +1279,10 @@ static void load_tree(struct tree_entry *root)
e->versions[0].mode = e->versions[1].mode;
e->name = to_atom(c, strlen(c));
c += e->name->str_len + 1;
- oidread(&e->versions[0].oid, (unsigned char *)c);
- oidread(&e->versions[1].oid, (unsigned char *)c);
+ oidread(&e->versions[0].oid, (unsigned char *)c,
+ the_repository->hash_algo);
+ oidread(&e->versions[1].oid, (unsigned char *)c,
+ the_repository->hash_algo);
c += the_hash_algo->rawsz;
}
free(buf);
@@ -1386,7 +1388,7 @@ static void tree_content_replace(
{
if (!S_ISDIR(mode))
die("Root cannot be a non-directory");
- oidclr(&root->versions[0].oid);
+ oidclr(&root->versions[0].oid, the_repository->hash_algo);
oidcpy(&root->versions[1].oid, oid);
if (root->tree)
release_tree_content_recursive(root->tree);
@@ -1445,7 +1447,7 @@ static int tree_content_set(
if (S_ISDIR(e->versions[0].mode))
e->versions[0].mode |= NO_DELTA;
- oidclr(&root->versions[1].oid);
+ oidclr(&root->versions[1].oid, the_repository->hash_algo);
return 1;
}
if (!S_ISDIR(e->versions[1].mode)) {
@@ -1455,7 +1457,7 @@ static int tree_content_set(
if (!e->tree)
load_tree(e);
if (tree_content_set(e, slash1 + 1, oid, mode, subtree)) {
- oidclr(&root->versions[1].oid);
+ oidclr(&root->versions[1].oid, the_repository->hash_algo);
return 1;
}
return 0;
@@ -1467,7 +1469,7 @@ static int tree_content_set(
e = new_tree_entry();
e->name = to_atom(p, n);
e->versions[0].mode = 0;
- oidclr(&e->versions[0].oid);
+ oidclr(&e->versions[0].oid, the_repository->hash_algo);
t->entries[t->entry_count++] = e;
if (*slash1) {
e->tree = new_tree_content(8);
@@ -1478,7 +1480,7 @@ static int tree_content_set(
e->versions[1].mode = mode;
oidcpy(&e->versions[1].oid, oid);
}
- oidclr(&root->versions[1].oid);
+ oidclr(&root->versions[1].oid, the_repository->hash_algo);
return 1;
}
@@ -1523,7 +1525,8 @@ static int tree_content_remove(
if (tree_content_remove(e, slash1 + 1, backup_leaf, 0)) {
for (n = 0; n < e->tree->entry_count; n++) {
if (e->tree->entries[n]->versions[1].mode) {
- oidclr(&root->versions[1].oid);
+ oidclr(&root->versions[1].oid,
+ the_repository->hash_algo);
return 1;
}
}
@@ -1542,8 +1545,8 @@ del_entry:
release_tree_content_recursive(e->tree);
e->tree = NULL;
e->versions[1].mode = 0;
- oidclr(&e->versions[1].oid);
- oidclr(&root->versions[1].oid);
+ oidclr(&e->versions[1].oid, the_repository->hash_algo);
+ oidclr(&root->versions[1].oid, the_repository->hash_algo);
return 1;
}
@@ -1604,11 +1607,12 @@ static int update_branch(struct branch *b)
if (is_null_oid(&b->oid)) {
if (b->delete)
- delete_ref(NULL, b->name, NULL, 0);
+ refs_delete_ref(get_main_ref_store(the_repository),
+ NULL, b->name, NULL, 0);
return 0;
}
- if (read_ref(b->name, &old_oid))
- oidclr(&old_oid);
+ if (refs_read_ref(get_main_ref_store(the_repository), b->name, &old_oid))
+ oidclr(&old_oid, the_repository->hash_algo);
if (!force_update && !is_null_oid(&old_oid)) {
struct commit *old_cmit, *new_cmit;
int ret;
@@ -1631,10 +1635,11 @@ static int update_branch(struct branch *b)
return -1;
}
}
- transaction = ref_transaction_begin(&err);
+ transaction = ref_store_transaction_begin(get_main_ref_store(the_repository),
+ &err);
if (!transaction ||
ref_transaction_update(transaction, b->name, &b->oid, &old_oid,
- 0, msg, &err) ||
+ NULL, NULL, 0, msg, &err) ||
ref_transaction_commit(transaction, &err)) {
ref_transaction_free(transaction);
error("%s", err.buf);
@@ -1665,7 +1670,8 @@ static void dump_tags(void)
struct strbuf err = STRBUF_INIT;
struct ref_transaction *transaction;
- transaction = ref_transaction_begin(&err);
+ transaction = ref_store_transaction_begin(get_main_ref_store(the_repository),
+ &err);
if (!transaction) {
failure |= error("%s", err.buf);
goto cleanup;
@@ -1675,7 +1681,8 @@ static void dump_tags(void)
strbuf_addf(&ref_name, "refs/tags/%s", t->name);
if (ref_transaction_update(transaction, ref_name.buf,
- &t->oid, NULL, 0, msg, &err)) {
+ &t->oid, NULL, NULL, NULL,
+ 0, msg, &err)) {
failure |= error("%s", err.buf);
goto cleanup;
}
@@ -2354,7 +2361,9 @@ static void file_change_m(const char *p, struct branch *b)
parse_path_eol(&path, p, "path");
/* Git does not track empty, non-toplevel directories. */
- if (S_ISDIR(mode) && is_empty_tree_oid(&oid) && *path.buf) {
+ if (S_ISDIR(mode) &&
+ is_empty_tree_oid(&oid, the_repository->hash_algo) &&
+ *path.buf) {
tree_content_remove(&b->branch_tree, path.buf, NULL, 0);
return;
}
@@ -2546,8 +2555,8 @@ static void note_change_n(const char *p, struct branch *b, unsigned char *old_fa
static void file_change_deleteall(struct branch *b)
{
release_tree_content_recursive(b->branch_tree.tree);
- oidclr(&b->branch_tree.versions[0].oid);
- oidclr(&b->branch_tree.versions[1].oid);
+ oidclr(&b->branch_tree.versions[0].oid, the_repository->hash_algo);
+ oidclr(&b->branch_tree.versions[1].oid, the_repository->hash_algo);
load_tree(&b->branch_tree);
b->num_notes = 0;
}
@@ -2566,8 +2575,8 @@ static void parse_from_commit(struct branch *b, char *buf, unsigned long size)
static void parse_from_existing(struct branch *b)
{
if (is_null_oid(&b->oid)) {
- oidclr(&b->branch_tree.versions[0].oid);
- oidclr(&b->branch_tree.versions[1].oid);
+ oidclr(&b->branch_tree.versions[0].oid, the_repository->hash_algo);
+ oidclr(&b->branch_tree.versions[1].oid, the_repository->hash_algo);
} else {
unsigned long size;
char *buf;
@@ -2890,9 +2899,9 @@ static void parse_reset_branch(const char *arg)
b = lookup_branch(arg);
if (b) {
- oidclr(&b->oid);
- oidclr(&b->branch_tree.versions[0].oid);
- oidclr(&b->branch_tree.versions[1].oid);
+ oidclr(&b->oid, the_repository->hash_algo);
+ oidclr(&b->branch_tree.versions[0].oid, the_repository->hash_algo);
+ oidclr(&b->branch_tree.versions[1].oid, the_repository->hash_algo);
if (b->branch_tree.tree) {
release_tree_content_recursive(b->branch_tree.tree);
b->branch_tree.tree = NULL;
diff --git a/builtin/fetch-pack.c b/builtin/fetch-pack.c
index 44c05ee86c..af329e8d5c 100644
--- a/builtin/fetch-pack.c
+++ b/builtin/fetch-pack.c
@@ -29,11 +29,11 @@ static void add_sought_entry(struct ref ***sought, int *nr, int *alloc,
; /* <oid>, leave oid as name */
} else {
/* <ref>, clear cruft from oid */
- oidclr(&oid);
+ oidclr(&oid, the_repository->hash_algo);
}
} else {
/* <ref>, clear cruft from get_oid_hex */
- oidclr(&oid);
+ oidclr(&oid, the_repository->hash_algo);
}
ref = alloc_ref(name);
diff --git a/builtin/fetch.c b/builtin/fetch.c
index 5857d860db..693f02b958 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -340,7 +340,8 @@ static void find_non_local_tags(const struct ref *refs,
refname_hash_init(&remote_refs);
create_fetch_oidset(head, &fetch_oids);
- for_each_ref(add_one_refname, &existing_refs);
+ refs_for_each_ref(get_main_ref_store(the_repository), add_one_refname,
+ &existing_refs);
/*
* If we already have a transaction, then we need to filter out all
@@ -581,11 +582,16 @@ static struct ref *get_ref_map(struct remote *remote,
}
}
- if (tags == TAGS_SET)
+ if (tags == TAGS_SET) {
+ struct refspec_item tag_refspec;
+
/* also fetch all tags */
- get_fetch_map(remote_refs, tag_refspec, &tail, 0);
- else if (tags == TAGS_DEFAULT && *autotags)
+ refspec_item_init(&tag_refspec, TAG_REFSPEC, 0);
+ get_fetch_map(remote_refs, &tag_refspec, &tail, 0);
+ refspec_item_clear(&tag_refspec);
+ } else if (tags == TAGS_DEFAULT && *autotags) {
find_non_local_tags(remote_refs, NULL, &ref_map, &tail);
+ }
/* Now append any refs to be updated opportunistically: */
*tail = orefs;
@@ -614,7 +620,9 @@ static struct ref *get_ref_map(struct remote *remote,
if (!existing_refs_populated) {
refname_hash_init(&existing_refs);
- for_each_ref(add_one_refname, &existing_refs);
+ refs_for_each_ref(get_main_ref_store(the_repository),
+ add_one_refname,
+ &existing_refs);
existing_refs_populated = 1;
}
@@ -659,7 +667,8 @@ static int s_update_ref(const char *action,
* lifecycle.
*/
if (!transaction) {
- transaction = our_transaction = ref_transaction_begin(&err);
+ transaction = our_transaction = ref_store_transaction_begin(get_main_ref_store(the_repository),
+ &err);
if (!transaction) {
ret = STORE_REF_ERROR_OTHER;
goto out;
@@ -668,7 +677,7 @@ static int s_update_ref(const char *action,
ret = ref_transaction_update(transaction, ref->name, &ref->new_oid,
check_old ? &ref->old_oid : NULL,
- 0, msg, &err);
+ NULL, NULL, 0, msg, &err);
if (ret) {
ret = STORE_REF_ERROR_OTHER;
goto out;
@@ -1382,8 +1391,8 @@ static int prune_refs(struct display_state *display_state,
if (!dry_run) {
if (transaction) {
for (ref = stale_refs; ref; ref = ref->next) {
- result = ref_transaction_delete(transaction, ref->name, NULL, 0,
- "fetch: prune", &err);
+ result = ref_transaction_delete(transaction, ref->name, NULL,
+ NULL, 0, "fetch: prune", &err);
if (result)
goto cleanup;
}
@@ -1393,7 +1402,9 @@ static int prune_refs(struct display_state *display_state,
for (ref = stale_refs; ref; ref = ref->next)
string_list_append(&refnames, ref->name);
- result = delete_refs("fetch: prune", &refnames, 0);
+ result = refs_delete_refs(get_main_ref_store(the_repository),
+ "fetch: prune", &refnames,
+ 0);
string_list_clear(&refnames, 0);
}
}
@@ -1406,7 +1417,8 @@ static int prune_refs(struct display_state *display_state,
_("(none)"), ref->name,
&ref->new_oid, &ref->old_oid,
summary_width);
- warn_dangling_symref(stderr, dangling_msg, ref->name);
+ refs_warn_dangling_symref(get_main_ref_store(the_repository),
+ stderr, dangling_msg, ref->name);
}
}
@@ -1479,7 +1491,8 @@ static void add_negotiation_tips(struct git_transport_options *smart_options)
continue;
}
old_nr = oids->nr;
- for_each_glob_ref(add_oid, s, oids);
+ refs_for_each_glob_ref(get_main_ref_store(the_repository),
+ add_oid, s, oids);
if (old_nr == oids->nr)
warning("ignoring --negotiation-tip=%s because it does not match any refs",
s);
@@ -1655,7 +1668,8 @@ static int do_fetch(struct transport *transport,
config->display_format);
if (atomic_fetch) {
- transaction = ref_transaction_begin(&err);
+ transaction = ref_store_transaction_begin(get_main_ref_store(the_repository),
+ &err);
if (!transaction) {
retcode = -1;
goto cleanup;
diff --git a/builtin/fmt-merge-msg.c b/builtin/fmt-merge-msg.c
index 0f9855b680..957786d1b3 100644
--- a/builtin/fmt-merge-msg.c
+++ b/builtin/fmt-merge-msg.c
@@ -11,7 +11,7 @@ static const char * const fmt_merge_msg_usage[] = {
int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix)
{
- const char *inpath = NULL;
+ char *inpath = NULL;
const char *message = NULL;
char *into_name = NULL;
int shortlog_len = -1;
@@ -66,5 +66,7 @@ int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix)
if (ret)
return ret;
write_in_full(STDOUT_FILENO, output.buf, output.len);
+
+ free(inpath);
return 0;
}
diff --git a/builtin/for-each-repo.c b/builtin/for-each-repo.c
index 28186b30f5..c4fa41fda9 100644
--- a/builtin/for-each-repo.c
+++ b/builtin/for-each-repo.c
@@ -32,6 +32,7 @@ static int run_command_on_repo(const char *path, int argc, const char ** argv)
int cmd_for_each_repo(int argc, const char **argv, const char *prefix)
{
static const char *config_key = NULL;
+ int keep_going = 0;
int i, result = 0;
const struct string_list *values;
int err;
@@ -39,6 +40,8 @@ int cmd_for_each_repo(int argc, const char **argv, const char *prefix)
const struct option options[] = {
OPT_STRING(0, "config", &config_key, N_("config"),
N_("config key storing a list of repository paths")),
+ OPT_BOOL(0, "keep-going", &keep_going,
+ N_("keep going even if command fails in a repository")),
OPT_END()
};
@@ -55,8 +58,14 @@ int cmd_for_each_repo(int argc, const char **argv, const char *prefix)
else if (err)
return 0;
- for (i = 0; !result && i < values->nr; i++)
- result = run_command_on_repo(values->items[i].string, argc, argv);
+ for (i = 0; i < values->nr; i++) {
+ int ret = run_command_on_repo(values->items[i].string, argc, argv);
+ if (ret) {
+ if (!keep_going)
+ return ret;
+ result = 1;
+ }
+ }
return result;
}
diff --git a/builtin/fsck.c b/builtin/fsck.c
index f892487c9b..d13a226c2e 100644
--- a/builtin/fsck.c
+++ b/builtin/fsck.c
@@ -514,7 +514,9 @@ static int fsck_handle_reflog(const char *logname, void *cb_data)
struct strbuf refname = STRBUF_INIT;
strbuf_worktree_ref(cb_data, &refname, logname);
- for_each_reflog_ent(refname.buf, fsck_handle_reflog_ent, refname.buf);
+ refs_for_each_reflog_ent(get_main_ref_store(the_repository),
+ refname.buf, fsck_handle_reflog_ent,
+ refname.buf);
strbuf_release(&refname);
return 0;
}
@@ -563,7 +565,8 @@ static void get_default_heads(void)
const char *head_points_at;
struct object_id head_oid;
- for_each_rawref(fsck_handle_ref, NULL);
+ refs_for_each_rawref(get_main_ref_store(the_repository),
+ fsck_handle_ref, NULL);
worktrees = get_worktrees();
for (p = worktrees; *p; p++) {
@@ -712,7 +715,9 @@ static int fsck_head_link(const char *head_ref_name,
if (verbose)
fprintf_ln(stderr, _("Checking %s link"), head_ref_name);
- *head_points_at = resolve_ref_unsafe(head_ref_name, 0, head_oid, NULL);
+ *head_points_at = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
+ head_ref_name, 0, head_oid,
+ NULL);
if (!*head_points_at) {
errors_found |= ERROR_REFS;
return error(_("invalid %s"), head_ref_name);
diff --git a/builtin/gc.c b/builtin/gc.c
index d187cec1ea..72bac2554f 100644
--- a/builtin/gc.c
+++ b/builtin/gc.c
@@ -846,7 +846,7 @@ static int dfs_on_ref(const char *refname UNUSED,
struct commit_list *stack = NULL;
struct commit *commit;
- if (!peel_iterated_oid(oid, &peeled))
+ if (!peel_iterated_oid(the_repository, oid, &peeled))
oid = &peeled;
if (oid_object_info(the_repository, oid, NULL) != OBJ_COMMIT)
return 0;
@@ -907,7 +907,8 @@ static int should_write_commit_graph(void)
if (data.limit < 0)
return 1;
- result = for_each_ref(dfs_on_ref, &data);
+ result = refs_for_each_ref(get_main_ref_store(the_repository),
+ dfs_on_ref, &data);
repo_clear_commit_marks(the_repository, SEEN);
@@ -1870,6 +1871,7 @@ static int launchctl_schedule_plist(const char *exec_path, enum schedule_priorit
"<string>%s/git</string>\n"
"<string>--exec-path=%s</string>\n"
"<string>for-each-repo</string>\n"
+ "<string>--keep-going</string>\n"
"<string>--config=maintenance.repo</string>\n"
"<string>maintenance</string>\n"
"<string>run</string>\n"
@@ -2112,7 +2114,7 @@ static int schtasks_schedule_task(const char *exec_path, enum schedule_priority
"<Actions Context=\"Author\">\n"
"<Exec>\n"
"<Command>\"%s\\headless-git.exe\"</Command>\n"
- "<Arguments>--exec-path=\"%s\" for-each-repo --config=maintenance.repo maintenance run --schedule=%s</Arguments>\n"
+ "<Arguments>--exec-path=\"%s\" for-each-repo --keep-going --config=maintenance.repo maintenance run --schedule=%s</Arguments>\n"
"</Exec>\n"
"</Actions>\n"
"</Task>\n";
@@ -2257,7 +2259,7 @@ static int crontab_update_schedule(int run_maintenance, int fd)
"# replaced in the future by a Git command.\n\n");
strbuf_addf(&line_format,
- "%%d %%s * * %%s \"%s/git\" --exec-path=\"%s\" for-each-repo --config=maintenance.repo maintenance run --schedule=%%s\n",
+ "%%d %%s * * %%s \"%s/git\" --exec-path=\"%s\" for-each-repo --keep-going --config=maintenance.repo maintenance run --schedule=%%s\n",
exec_path, exec_path);
fprintf(cron_in, line_format.buf, minute, "1-23", "*", "hourly");
fprintf(cron_in, line_format.buf, minute, "0", "1-6", "daily");
@@ -2458,7 +2460,7 @@ static int systemd_timer_write_service_template(const char *exec_path)
"\n"
"[Service]\n"
"Type=oneshot\n"
- "ExecStart=\"%s/git\" --exec-path=\"%s\" for-each-repo --config=maintenance.repo maintenance run --schedule=%%i\n"
+ "ExecStart=\"%s/git\" --exec-path=\"%s\" for-each-repo --keep-going --config=maintenance.repo maintenance run --schedule=%%i\n"
"LockPersonality=yes\n"
"MemoryDenyWriteExecute=yes\n"
"NoNewPrivileges=yes\n"
diff --git a/builtin/grep.c b/builtin/grep.c
index 5777ba82a9..dfc3c3e8bd 100644
--- a/builtin/grep.c
+++ b/builtin/grep.c
@@ -1114,7 +1114,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
for (i = 0; i < argc; i++) {
const char *arg = argv[i];
struct object_id oid;
- struct object_context oc;
+ struct object_context oc = {0};
struct object *object;
if (!strcmp(arg, "--")) {
@@ -1140,7 +1140,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
if (!seen_dashdash)
verify_non_filename(prefix, arg);
add_object_array_with_path(object, arg, &list, oc.mode, oc.path);
- free(oc.path);
+ object_context_release(&oc);
}
/*
diff --git a/builtin/hash-object.c b/builtin/hash-object.c
index 82ca6d2bfd..c767414a0c 100644
--- a/builtin/hash-object.c
+++ b/builtin/hash-object.c
@@ -123,6 +123,9 @@ int cmd_hash_object(int argc, const char **argv, const char *prefix)
else
prefix = setup_git_directory_gently(&nongit);
+ if (nongit && !the_hash_algo)
+ repo_set_hash_algo(the_repository, GIT_HASH_SHA1);
+
if (vpath && prefix) {
vpath_free = prefix_filename(prefix, vpath);
vpath = vpath_free;
diff --git a/builtin/index-pack.c b/builtin/index-pack.c
index 856428fef9..fd968d673d 100644
--- a/builtin/index-pack.c
+++ b/builtin/index-pack.c
@@ -528,7 +528,8 @@ static void *unpack_raw_entry(struct object_entry *obj,
switch (obj->type) {
case OBJ_REF_DELTA:
- oidread(ref_oid, fill(the_hash_algo->rawsz));
+ oidread(ref_oid, fill(the_hash_algo->rawsz),
+ the_repository->hash_algo);
use(the_hash_algo->rawsz);
break;
case OBJ_OFS_DELTA:
@@ -1204,7 +1205,7 @@ static void parse_pack_objects(unsigned char *hash)
the_hash_algo->init_fn(&tmp_ctx);
the_hash_algo->clone_fn(&tmp_ctx, &input_ctx);
the_hash_algo->final_fn(hash, &tmp_ctx);
- if (!hasheq(fill(the_hash_algo->rawsz), hash))
+ if (!hasheq(fill(the_hash_algo->rawsz), hash, the_repository->hash_algo))
die(_("pack is corrupted (SHA1 mismatch)"));
use(the_hash_algo->rawsz);
@@ -1307,11 +1308,11 @@ static void conclude_pack(int fix_thin_pack, const char *curr_pack, unsigned cha
stop_progress_msg(&progress, msg.buf);
strbuf_release(&msg);
finalize_hashfile(f, tail_hash, FSYNC_COMPONENT_PACK, 0);
- hashcpy(read_hash, pack_hash);
+ hashcpy(read_hash, pack_hash, the_repository->hash_algo);
fixup_pack_header_footer(output_fd, pack_hash,
curr_pack, nr_objects,
read_hash, consumed_bytes-the_hash_algo->rawsz);
- if (!hasheq(read_hash, tail_hash))
+ if (!hasheq(read_hash, tail_hash, the_repository->hash_algo))
die(_("Unexpected tail checksum for %s "
"(disk corruption?)"), curr_pack);
}
@@ -1372,7 +1373,7 @@ static struct object_entry *append_obj_to_pack(struct hashfile *f,
obj[1].idx.offset += write_compressed(f, buf, size);
obj[0].idx.crc32 = crc32_end(f);
hashflush(f);
- oidread(&obj->idx.oid, sha1);
+ oidread(&obj->idx.oid, sha1, the_repository->hash_algo);
return obj;
}
diff --git a/builtin/init-db.c b/builtin/init-db.c
index 0170469b84..582dcf20f8 100644
--- a/builtin/init-db.c
+++ b/builtin/init-db.c
@@ -81,7 +81,7 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
const char *ref_format = NULL;
const char *initial_branch = NULL;
int hash_algo = GIT_HASH_UNKNOWN;
- unsigned int ref_storage_format = REF_STORAGE_FORMAT_UNKNOWN;
+ enum ref_storage_format ref_storage_format = REF_STORAGE_FORMAT_UNKNOWN;
int init_shared_repository = -1;
const struct option init_db_options[] = {
OPT_STRING(0, "template", &template_dir, N_("template-directory"),
diff --git a/builtin/interpret-trailers.c b/builtin/interpret-trailers.c
index 8768bfea3c..1d969494cf 100644
--- a/builtin/interpret-trailers.c
+++ b/builtin/interpret-trailers.c
@@ -141,7 +141,7 @@ static void interpret_trailers(const struct process_trailer_options *opts,
LIST_HEAD(head);
struct strbuf sb = STRBUF_INIT;
struct strbuf trailer_block = STRBUF_INIT;
- struct trailer_info info;
+ struct trailer_info *info;
FILE *outfile = stdout;
trailer_config_init();
@@ -151,13 +151,13 @@ static void interpret_trailers(const struct process_trailer_options *opts,
if (opts->in_place)
outfile = create_in_place_tempfile(file);
- parse_trailers(opts, &info, sb.buf, &head);
+ info = parse_trailers(opts, sb.buf, &head);
/* Print the lines before the trailers */
if (!opts->only_trailers)
- fwrite(sb.buf, 1, info.trailer_block_start, outfile);
+ fwrite(sb.buf, 1, trailer_block_start(info), outfile);
- if (!opts->only_trailers && !info.blank_line_before_trailer)
+ if (!opts->only_trailers && !blank_line_before_trailer_block(info))
fprintf(outfile, "\n");
@@ -178,8 +178,8 @@ static void interpret_trailers(const struct process_trailer_options *opts,
/* Print the lines after the trailers as is */
if (!opts->only_trailers)
- fwrite(sb.buf + info.trailer_block_end, 1, sb.len - info.trailer_block_end, outfile);
- trailer_info_release(&info);
+ fwrite(sb.buf + trailer_block_end(info), 1, sb.len - trailer_block_end(info), outfile);
+ trailer_info_release(info);
if (opts->in_place)
if (rename_tempfile(&trailers_tempfile, file))
diff --git a/builtin/log.c b/builtin/log.c
index c0a8bb95e9..4d4b60caa7 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -4,7 +4,7 @@
* (C) Copyright 2006 Linus Torvalds
* 2006 Junio Hamano
*/
-#include "git-compat-util.h"
+#include "builtin.h"
#include "abspath.h"
#include "config.h"
#include "environment.h"
@@ -48,22 +48,8 @@
#define COVER_FROM_AUTO_MAX_SUBJECT_LEN 100
#define FORMAT_PATCH_NAME_MAX_DEFAULT 64
-/* Set a default date-time format for git log ("log.date" config variable) */
-static const char *default_date_mode = NULL;
-
-static int default_abbrev_commit;
-static int default_show_root = 1;
-static int default_follow;
-static int default_show_signature;
-static int default_encode_email_headers = 1;
-static int decoration_style;
-static int decoration_given;
-static int use_mailmap_config = 1;
static unsigned int force_in_body_from;
static int stdout_mboxrd;
-static const char *fmt_patch_subject_prefix = "PATCH";
-static int fmt_patch_name_max = FORMAT_PATCH_NAME_MAX_DEFAULT;
-static const char *fmt_pretty;
static int format_no_prefix;
static const char * const builtin_log_usage[] = {
@@ -111,6 +97,39 @@ static int parse_decoration_style(const char *value)
return -1;
}
+struct log_config {
+ int default_abbrev_commit;
+ int default_show_root;
+ int default_follow;
+ int default_show_signature;
+ int default_encode_email_headers;
+ int decoration_style;
+ int decoration_given;
+ int use_mailmap_config;
+ char *fmt_patch_subject_prefix;
+ int fmt_patch_name_max;
+ char *fmt_pretty;
+ char *default_date_mode;
+};
+
+static void log_config_init(struct log_config *cfg)
+{
+ memset(cfg, 0, sizeof(*cfg));
+ cfg->default_show_root = 1;
+ cfg->default_encode_email_headers = 1;
+ cfg->use_mailmap_config = 1;
+ cfg->fmt_patch_subject_prefix = xstrdup("PATCH");
+ cfg->fmt_patch_name_max = FORMAT_PATCH_NAME_MAX_DEFAULT;
+ cfg->decoration_style = auto_decoration_style();
+}
+
+static void log_config_release(struct log_config *cfg)
+{
+ free(cfg->default_date_mode);
+ free(cfg->fmt_pretty);
+ free(cfg->fmt_patch_subject_prefix);
+}
+
static int use_default_decoration_filter = 1;
static struct string_list decorate_refs_exclude = STRING_LIST_INIT_NODUP;
static struct string_list decorate_refs_exclude_config = STRING_LIST_INIT_NODUP;
@@ -127,20 +146,22 @@ static int clear_decorations_callback(const struct option *opt UNUSED,
return 0;
}
-static int decorate_callback(const struct option *opt UNUSED, const char *arg,
+static int decorate_callback(const struct option *opt, const char *arg,
int unset)
{
+ struct log_config *cfg = opt->value;
+
if (unset)
- decoration_style = 0;
+ cfg->decoration_style = 0;
else if (arg)
- decoration_style = parse_decoration_style(arg);
+ cfg->decoration_style = parse_decoration_style(arg);
else
- decoration_style = DECORATE_SHORT_REFS;
+ cfg->decoration_style = DECORATE_SHORT_REFS;
- if (decoration_style < 0)
+ if (cfg->decoration_style < 0)
die(_("invalid --decorate option: %s"), arg);
- decoration_given = 1;
+ cfg->decoration_given = 1;
return 0;
}
@@ -160,32 +181,26 @@ static int log_line_range_callback(const struct option *option, const char *arg,
return 0;
}
-static void init_log_defaults(void)
-{
- init_diff_ui_defaults();
-
- decoration_style = auto_decoration_style();
-}
-
-static void cmd_log_init_defaults(struct rev_info *rev)
+static void cmd_log_init_defaults(struct rev_info *rev,
+ struct log_config *cfg)
{
- if (fmt_pretty)
- get_commit_format(fmt_pretty, rev);
- if (default_follow)
+ if (cfg->fmt_pretty)
+ get_commit_format(cfg->fmt_pretty, rev);
+ if (cfg->default_follow)
rev->diffopt.flags.default_follow_renames = 1;
rev->verbose_header = 1;
init_diffstat_widths(&rev->diffopt);
rev->diffopt.flags.recursive = 1;
rev->diffopt.flags.allow_textconv = 1;
- rev->abbrev_commit = default_abbrev_commit;
- rev->show_root_diff = default_show_root;
- rev->subject_prefix = fmt_patch_subject_prefix;
- rev->patch_name_max = fmt_patch_name_max;
- rev->show_signature = default_show_signature;
- rev->encode_email_headers = default_encode_email_headers;
+ rev->abbrev_commit = cfg->default_abbrev_commit;
+ rev->show_root_diff = cfg->default_show_root;
+ rev->subject_prefix = cfg->fmt_patch_subject_prefix;
+ rev->patch_name_max = cfg->fmt_patch_name_max;
+ rev->show_signature = cfg->default_show_signature;
+ rev->encode_email_headers = cfg->default_encode_email_headers;
- if (default_date_mode)
- parse_date_format(default_date_mode, &rev->date_mode);
+ if (cfg->default_date_mode)
+ parse_date_format(cfg->default_date_mode, &rev->date_mode);
}
static void set_default_decoration_filter(struct decoration_filter *decoration_filter)
@@ -233,7 +248,8 @@ static void set_default_decoration_filter(struct decoration_filter *decoration_f
}
static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,
- struct rev_info *rev, struct setup_revision_opt *opt)
+ struct rev_info *rev, struct setup_revision_opt *opt,
+ struct log_config *cfg)
{
struct userformat_want w;
int quiet = 0, source = 0, mailmap;
@@ -258,7 +274,7 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,
N_("pattern"), N_("only decorate refs that match <pattern>")),
OPT_STRING_LIST(0, "decorate-refs-exclude", &decorate_refs_exclude,
N_("pattern"), N_("do not decorate refs that match <pattern>")),
- OPT_CALLBACK_F(0, "decorate", NULL, NULL, N_("decorate options"),
+ OPT_CALLBACK_F(0, "decorate", cfg, NULL, N_("decorate options"),
PARSE_OPT_OPTARG, decorate_callback),
OPT_CALLBACK('L', NULL, &line_cb, "range:file",
N_("trace the evolution of line range <start>,<end> or function :<funcname> in <file>"),
@@ -269,7 +285,7 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,
line_cb.rev = rev;
line_cb.prefix = prefix;
- mailmap = use_mailmap_config;
+ mailmap = cfg->use_mailmap_config;
argc = parse_options(argc, argv, prefix,
builtin_log_options, builtin_log_usage,
PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN_OPT |
@@ -314,8 +330,8 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,
* "log --pretty=raw" is special; ignore UI oriented
* configuration variables such as decoration.
*/
- if (!decoration_given)
- decoration_style = 0;
+ if (!cfg->decoration_given)
+ cfg->decoration_style = 0;
if (!rev->abbrev_commit_given)
rev->abbrev_commit = 0;
}
@@ -326,24 +342,24 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,
* Disable decoration loading if the format will not
* show them anyway.
*/
- decoration_style = 0;
- } else if (!decoration_style) {
+ cfg->decoration_style = 0;
+ } else if (!cfg->decoration_style) {
/*
* If we are going to show them, make sure we do load
* them here, but taking care not to override a
* specific style set by config or --decorate.
*/
- decoration_style = DECORATE_SHORT_REFS;
+ cfg->decoration_style = DECORATE_SHORT_REFS;
}
}
- if (decoration_style || rev->simplify_by_decoration) {
+ if (cfg->decoration_style || rev->simplify_by_decoration) {
set_default_decoration_filter(&decoration_filter);
- if (decoration_style)
+ if (cfg->decoration_style)
rev->show_decorations = 1;
- load_ref_decorations(&decoration_filter, decoration_style);
+ load_ref_decorations(&decoration_filter, cfg->decoration_style);
}
if (rev->line_level_traverse)
@@ -353,16 +369,11 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,
}
static void cmd_log_init(int argc, const char **argv, const char *prefix,
- struct rev_info *rev, struct setup_revision_opt *opt)
+ struct rev_info *rev, struct setup_revision_opt *opt,
+ struct log_config *cfg)
{
- cmd_log_init_defaults(rev);
- cmd_log_init_finish(argc, argv, prefix, rev, opt);
-}
-
-static int cmd_log_deinit(int ret, struct rev_info *rev)
-{
- release_revisions(rev);
- return ret;
+ cmd_log_init_defaults(rev, cfg);
+ cmd_log_init_finish(argc, argv, prefix, rev, opt, cfg);
}
/*
@@ -566,30 +577,37 @@ static int cmd_log_walk(struct rev_info *rev)
static int git_log_config(const char *var, const char *value,
const struct config_context *ctx, void *cb)
{
+ struct log_config *cfg = cb;
const char *slot_name;
- if (!strcmp(var, "format.pretty"))
- return git_config_string(&fmt_pretty, var, value);
- if (!strcmp(var, "format.subjectprefix"))
- return git_config_string(&fmt_patch_subject_prefix, var, value);
+ if (!strcmp(var, "format.pretty")) {
+ FREE_AND_NULL(cfg->fmt_pretty);
+ return git_config_string(&cfg->fmt_pretty, var, value);
+ }
+ if (!strcmp(var, "format.subjectprefix")) {
+ FREE_AND_NULL(cfg->fmt_patch_subject_prefix);
+ return git_config_string(&cfg->fmt_patch_subject_prefix, var, value);
+ }
if (!strcmp(var, "format.filenamemaxlength")) {
- fmt_patch_name_max = git_config_int(var, value, ctx->kvi);
+ cfg->fmt_patch_name_max = git_config_int(var, value, ctx->kvi);
return 0;
}
if (!strcmp(var, "format.encodeemailheaders")) {
- default_encode_email_headers = git_config_bool(var, value);
+ cfg->default_encode_email_headers = git_config_bool(var, value);
return 0;
}
if (!strcmp(var, "log.abbrevcommit")) {
- default_abbrev_commit = git_config_bool(var, value);
+ cfg->default_abbrev_commit = git_config_bool(var, value);
return 0;
}
- if (!strcmp(var, "log.date"))
- return git_config_string(&default_date_mode, var, value);
+ if (!strcmp(var, "log.date")) {
+ FREE_AND_NULL(cfg->default_date_mode);
+ return git_config_string(&cfg->default_date_mode, var, value);
+ }
if (!strcmp(var, "log.decorate")) {
- decoration_style = parse_decoration_style(value);
- if (decoration_style < 0)
- decoration_style = 0; /* maybe warn? */
+ cfg->decoration_style = parse_decoration_style(value);
+ if (cfg->decoration_style < 0)
+ cfg->decoration_style = 0; /* maybe warn? */
return 0;
}
if (!strcmp(var, "log.diffmerges")) {
@@ -598,21 +616,21 @@ static int git_log_config(const char *var, const char *value,
return diff_merges_config(value);
}
if (!strcmp(var, "log.showroot")) {
- default_show_root = git_config_bool(var, value);
+ cfg->default_show_root = git_config_bool(var, value);
return 0;
}
if (!strcmp(var, "log.follow")) {
- default_follow = git_config_bool(var, value);
+ cfg->default_follow = git_config_bool(var, value);
return 0;
}
if (skip_prefix(var, "color.decorate.", &slot_name))
return parse_decorate_color_config(var, slot_name, value);
if (!strcmp(var, "log.mailmap")) {
- use_mailmap_config = git_config_bool(var, value);
+ cfg->use_mailmap_config = git_config_bool(var, value);
return 0;
}
if (!strcmp(var, "log.showsignature")) {
- default_show_signature = git_config_bool(var, value);
+ cfg->default_show_signature = git_config_bool(var, value);
return 0;
}
@@ -621,11 +639,14 @@ static int git_log_config(const char *var, const char *value,
int cmd_whatchanged(int argc, const char **argv, const char *prefix)
{
+ struct log_config cfg;
struct rev_info rev;
struct setup_revision_opt opt;
+ int ret;
- init_log_defaults();
- git_config(git_log_config, NULL);
+ log_config_init(&cfg);
+ init_diff_ui_defaults();
+ git_config(git_log_config, &cfg);
repo_init_revisions(the_repository, &rev, prefix);
git_config(grep_config, &rev.grep_filter);
@@ -635,10 +656,15 @@ int cmd_whatchanged(int argc, const char **argv, const char *prefix)
memset(&opt, 0, sizeof(opt));
opt.def = "HEAD";
opt.revarg_opt = REVARG_COMMITTISH;
- cmd_log_init(argc, argv, prefix, &rev, &opt);
+ cmd_log_init(argc, argv, prefix, &rev, &opt, &cfg);
if (!rev.diffopt.output_format)
rev.diffopt.output_format = DIFF_FORMAT_RAW;
- return cmd_log_deinit(cmd_log_walk(&rev), &rev);
+
+ ret = cmd_log_walk(&rev);
+
+ release_revisions(&rev);
+ log_config_release(&cfg);
+ return ret;
}
static void show_tagger(const char *buf, struct rev_info *rev)
@@ -656,7 +682,7 @@ static void show_tagger(const char *buf, struct rev_info *rev)
static int show_blob_object(const struct object_id *oid, struct rev_info *rev, const char *obj_name)
{
struct object_id oidc;
- struct object_context obj_context;
+ struct object_context obj_context = {0};
char *buf;
unsigned long size;
@@ -672,7 +698,7 @@ static int show_blob_object(const struct object_id *oid, struct rev_info *rev, c
if (!obj_context.path ||
!textconv_object(the_repository, obj_context.path,
obj_context.mode, &oidc, 1, &buf, &size)) {
- free(obj_context.path);
+ object_context_release(&obj_context);
return stream_blob_to_fd(1, oid, NULL, 0);
}
@@ -680,7 +706,7 @@ static int show_blob_object(const struct object_id *oid, struct rev_info *rev, c
die(_("git show %s: bad file"), obj_name);
write_or_die(1, buf, size);
- free(obj_context.path);
+ object_context_release(&obj_context);
return 0;
}
@@ -733,14 +759,16 @@ static void show_setup_revisions_tweak(struct rev_info *rev)
int cmd_show(int argc, const char **argv, const char *prefix)
{
+ struct log_config cfg;
struct rev_info rev;
unsigned int i;
struct setup_revision_opt opt;
struct pathspec match_all;
int ret = 0;
- init_log_defaults();
- git_config(git_log_config, NULL);
+ log_config_init(&cfg);
+ init_diff_ui_defaults();
+ git_config(git_log_config, &cfg);
if (the_repository->gitdir) {
prepare_repo_settings(the_repository);
@@ -759,10 +787,14 @@ int cmd_show(int argc, const char **argv, const char *prefix)
memset(&opt, 0, sizeof(opt));
opt.def = "HEAD";
opt.tweak = show_setup_revisions_tweak;
- cmd_log_init(argc, argv, prefix, &rev, &opt);
+ cmd_log_init(argc, argv, prefix, &rev, &opt, &cfg);
- if (!rev.no_walk)
- return cmd_log_deinit(cmd_log_walk(&rev), &rev);
+ if (!rev.no_walk) {
+ ret = cmd_log_walk(&rev);
+ release_revisions(&rev);
+ log_config_release(&cfg);
+ return ret;
+ }
rev.diffopt.no_free = 1;
for (i = 0; i < rev.pending.nr && !ret; i++) {
@@ -832,8 +864,10 @@ int cmd_show(int argc, const char **argv, const char *prefix)
rev.diffopt.no_free = 0;
diff_free(&rev.diffopt);
+ release_revisions(&rev);
+ log_config_release(&cfg);
- return cmd_log_deinit(ret, &rev);
+ return ret;
}
/*
@@ -841,11 +875,14 @@ int cmd_show(int argc, const char **argv, const char *prefix)
*/
int cmd_log_reflog(int argc, const char **argv, const char *prefix)
{
+ struct log_config cfg;
struct rev_info rev;
struct setup_revision_opt opt;
+ int ret;
- init_log_defaults();
- git_config(git_log_config, NULL);
+ log_config_init(&cfg);
+ init_diff_ui_defaults();
+ git_config(git_log_config, &cfg);
repo_init_revisions(the_repository, &rev, prefix);
init_reflog_walk(&rev.reflog_info);
@@ -854,14 +891,18 @@ int cmd_log_reflog(int argc, const char **argv, const char *prefix)
rev.verbose_header = 1;
memset(&opt, 0, sizeof(opt));
opt.def = "HEAD";
- cmd_log_init_defaults(&rev);
+ cmd_log_init_defaults(&rev, &cfg);
rev.abbrev_commit = 1;
rev.commit_format = CMIT_FMT_ONELINE;
rev.use_terminator = 1;
rev.always_show_header = 1;
- cmd_log_init_finish(argc, argv, prefix, &rev, &opt);
+ cmd_log_init_finish(argc, argv, prefix, &rev, &opt, &cfg);
- return cmd_log_deinit(cmd_log_walk(&rev), &rev);
+ ret = cmd_log_walk(&rev);
+
+ release_revisions(&rev);
+ log_config_release(&cfg);
+ return ret;
}
static void log_setup_revisions_tweak(struct rev_info *rev)
@@ -876,11 +917,14 @@ static void log_setup_revisions_tweak(struct rev_info *rev)
int cmd_log(int argc, const char **argv, const char *prefix)
{
+ struct log_config cfg;
struct rev_info rev;
struct setup_revision_opt opt;
+ int ret;
- init_log_defaults();
- git_config(git_log_config, NULL);
+ log_config_init(&cfg);
+ init_diff_ui_defaults();
+ git_config(git_log_config, &cfg);
repo_init_revisions(the_repository, &rev, prefix);
git_config(grep_config, &rev.grep_filter);
@@ -890,42 +934,17 @@ int cmd_log(int argc, const char **argv, const char *prefix)
opt.def = "HEAD";
opt.revarg_opt = REVARG_COMMITTISH;
opt.tweak = log_setup_revisions_tweak;
- cmd_log_init(argc, argv, prefix, &rev, &opt);
- return cmd_log_deinit(cmd_log_walk(&rev), &rev);
-}
-
-/* format-patch */
-
-static const char *fmt_patch_suffix = ".patch";
-static int numbered = 0;
-static int auto_number = 1;
+ cmd_log_init(argc, argv, prefix, &rev, &opt, &cfg);
-static char *default_attach = NULL;
+ ret = cmd_log_walk(&rev);
-static struct string_list extra_hdr = STRING_LIST_INIT_NODUP;
-static struct string_list extra_to = STRING_LIST_INIT_NODUP;
-static struct string_list extra_cc = STRING_LIST_INIT_NODUP;
-
-static void add_header(const char *value)
-{
- struct string_list_item *item;
- int len = strlen(value);
- while (len && value[len - 1] == '\n')
- len--;
-
- if (!strncasecmp(value, "to: ", 4)) {
- item = string_list_append(&extra_to, value + 4);
- len -= 4;
- } else if (!strncasecmp(value, "cc: ", 4)) {
- item = string_list_append(&extra_cc, value + 4);
- len -= 4;
- } else {
- item = string_list_append(&extra_hdr, value);
- }
-
- item->string[len] = '\0';
+ release_revisions(&rev);
+ log_config_release(&cfg);
+ return ret;
}
+/* format-patch */
+
enum cover_setting {
COVER_UNSET,
COVER_OFF,
@@ -952,17 +971,61 @@ enum auto_base_setting {
AUTO_BASE_WHEN_ABLE
};
-static enum thread_level thread;
-static int do_signoff;
-static enum auto_base_setting auto_base;
-static char *from;
-static const char *signature = git_version_string;
-static const char *signature_file;
-static enum cover_setting config_cover_letter;
-static const char *config_output_directory;
-static enum cover_from_description cover_from_description_mode = COVER_FROM_MESSAGE;
-static int show_notes;
-static struct display_notes_opt notes_opt;
+struct format_config {
+ struct log_config log;
+ enum thread_level thread;
+ int do_signoff;
+ enum auto_base_setting auto_base;
+ char *base_commit;
+ char *from;
+ char *signature;
+ char *signature_file;
+ enum cover_setting config_cover_letter;
+ char *config_output_directory;
+ enum cover_from_description cover_from_description_mode;
+ int show_notes;
+ struct display_notes_opt notes_opt;
+ int numbered_cmdline_opt;
+ int numbered;
+ int auto_number;
+ char *default_attach;
+ struct string_list extra_hdr;
+ struct string_list extra_to;
+ struct string_list extra_cc;
+ int keep_subject;
+ int subject_prefix;
+ struct strbuf sprefix;
+ char *fmt_patch_suffix;
+};
+
+static void format_config_init(struct format_config *cfg)
+{
+ memset(cfg, 0, sizeof(*cfg));
+ log_config_init(&cfg->log);
+ cfg->cover_from_description_mode = COVER_FROM_MESSAGE;
+ cfg->auto_number = 1;
+ string_list_init_dup(&cfg->extra_hdr);
+ string_list_init_dup(&cfg->extra_to);
+ string_list_init_dup(&cfg->extra_cc);
+ strbuf_init(&cfg->sprefix, 0);
+ cfg->fmt_patch_suffix = xstrdup(".patch");
+}
+
+static void format_config_release(struct format_config *cfg)
+{
+ log_config_release(&cfg->log);
+ free(cfg->base_commit);
+ free(cfg->from);
+ free(cfg->signature);
+ free(cfg->signature_file);
+ free(cfg->config_output_directory);
+ free(cfg->default_attach);
+ string_list_clear(&cfg->extra_hdr, 0);
+ string_list_clear(&cfg->extra_to, 0);
+ string_list_clear(&cfg->extra_cc, 0);
+ strbuf_release(&cfg->sprefix);
+ free(cfg->fmt_patch_suffix);
+}
static enum cover_from_description parse_cover_from_description(const char *arg)
{
@@ -980,27 +1043,51 @@ static enum cover_from_description parse_cover_from_description(const char *arg)
die(_("%s: invalid cover from description mode"), arg);
}
+static void add_header(struct format_config *cfg, const char *value)
+{
+ struct string_list_item *item;
+ int len = strlen(value);
+ while (len && value[len - 1] == '\n')
+ len--;
+
+ if (!strncasecmp(value, "to: ", 4)) {
+ item = string_list_append(&cfg->extra_to, value + 4);
+ len -= 4;
+ } else if (!strncasecmp(value, "cc: ", 4)) {
+ item = string_list_append(&cfg->extra_cc, value + 4);
+ len -= 4;
+ } else {
+ item = string_list_append(&cfg->extra_hdr, value);
+ }
+
+ item->string[len] = '\0';
+}
+
static int git_format_config(const char *var, const char *value,
const struct config_context *ctx, void *cb)
{
+ struct format_config *cfg = cb;
+
if (!strcmp(var, "format.headers")) {
if (!value)
die(_("format.headers without value"));
- add_header(value);
+ add_header(cfg, value);
return 0;
}
- if (!strcmp(var, "format.suffix"))
- return git_config_string(&fmt_patch_suffix, var, value);
+ if (!strcmp(var, "format.suffix")) {
+ FREE_AND_NULL(cfg->fmt_patch_suffix);
+ return git_config_string(&cfg->fmt_patch_suffix, var, value);
+ }
if (!strcmp(var, "format.to")) {
if (!value)
return config_error_nonbool(var);
- string_list_append(&extra_to, value);
+ string_list_append(&cfg->extra_to, value);
return 0;
}
if (!strcmp(var, "format.cc")) {
if (!value)
return config_error_nonbool(var);
- string_list_append(&extra_cc, value);
+ string_list_append(&cfg->extra_cc, value);
return 0;
}
if (!strcmp(var, "diff.color") || !strcmp(var, "color.diff") ||
@@ -1009,69 +1096,76 @@ static int git_format_config(const char *var, const char *value,
}
if (!strcmp(var, "format.numbered")) {
if (value && !strcasecmp(value, "auto")) {
- auto_number = 1;
+ cfg->auto_number = 1;
return 0;
}
- numbered = git_config_bool(var, value);
- auto_number = auto_number && numbered;
+ cfg->numbered = git_config_bool(var, value);
+ cfg->auto_number = cfg->auto_number && cfg->numbered;
return 0;
}
if (!strcmp(var, "format.attach")) {
- if (value && *value)
- default_attach = xstrdup(value);
- else if (value && !*value)
- FREE_AND_NULL(default_attach);
- else
- default_attach = xstrdup(git_version_string);
+ if (value && *value) {
+ FREE_AND_NULL(cfg->default_attach);
+ cfg->default_attach = xstrdup(value);
+ } else if (value && !*value) {
+ FREE_AND_NULL(cfg->default_attach);
+ } else {
+ FREE_AND_NULL(cfg->default_attach);
+ cfg->default_attach = xstrdup(git_version_string);
+ }
return 0;
}
if (!strcmp(var, "format.thread")) {
if (value && !strcasecmp(value, "deep")) {
- thread = THREAD_DEEP;
+ cfg->thread = THREAD_DEEP;
return 0;
}
if (value && !strcasecmp(value, "shallow")) {
- thread = THREAD_SHALLOW;
+ cfg->thread = THREAD_SHALLOW;
return 0;
}
- thread = git_config_bool(var, value) ? THREAD_SHALLOW : THREAD_UNSET;
+ cfg->thread = git_config_bool(var, value) ? THREAD_SHALLOW : THREAD_UNSET;
return 0;
}
if (!strcmp(var, "format.signoff")) {
- do_signoff = git_config_bool(var, value);
+ cfg->do_signoff = git_config_bool(var, value);
return 0;
}
- if (!strcmp(var, "format.signature"))
- return git_config_string(&signature, var, value);
- if (!strcmp(var, "format.signaturefile"))
- return git_config_pathname(&signature_file, var, value);
+ if (!strcmp(var, "format.signature")) {
+ FREE_AND_NULL(cfg->signature);
+ return git_config_string(&cfg->signature, var, value);
+ }
+ if (!strcmp(var, "format.signaturefile")) {
+ FREE_AND_NULL(cfg->signature_file);
+ return git_config_pathname(&cfg->signature_file, var, value);
+ }
if (!strcmp(var, "format.coverletter")) {
if (value && !strcasecmp(value, "auto")) {
- config_cover_letter = COVER_AUTO;
+ cfg->config_cover_letter = COVER_AUTO;
return 0;
}
- config_cover_letter = git_config_bool(var, value) ? COVER_ON : COVER_OFF;
+ cfg->config_cover_letter = git_config_bool(var, value) ? COVER_ON : COVER_OFF;
return 0;
}
- if (!strcmp(var, "format.outputdirectory"))
- return git_config_string(&config_output_directory, var, value);
+ if (!strcmp(var, "format.outputdirectory")) {
+ FREE_AND_NULL(cfg->config_output_directory);
+ return git_config_string(&cfg->config_output_directory, var, value);
+ }
if (!strcmp(var, "format.useautobase")) {
if (value && !strcasecmp(value, "whenAble")) {
- auto_base = AUTO_BASE_WHEN_ABLE;
+ cfg->auto_base = AUTO_BASE_WHEN_ABLE;
return 0;
}
- auto_base = git_config_bool(var, value) ? AUTO_BASE_ALWAYS : AUTO_BASE_NEVER;
+ cfg->auto_base = git_config_bool(var, value) ? AUTO_BASE_ALWAYS : AUTO_BASE_NEVER;
return 0;
}
if (!strcmp(var, "format.from")) {
int b = git_parse_maybe_bool(value);
- free(from);
+ FREE_AND_NULL(cfg->from);
if (b < 0)
- from = xstrdup(value);
+ cfg->from = xstrdup(value);
else if (b)
- from = xstrdup(git_committer_info(IDENT_NO_DATE));
- else
- from = NULL;
+ cfg->from = xstrdup(git_committer_info(IDENT_NO_DATE));
return 0;
}
if (!strcmp(var, "format.forceinbodyfrom")) {
@@ -1081,15 +1175,15 @@ static int git_format_config(const char *var, const char *value,
if (!strcmp(var, "format.notes")) {
int b = git_parse_maybe_bool(value);
if (b < 0)
- enable_ref_display_notes(&notes_opt, &show_notes, value);
+ enable_ref_display_notes(&cfg->notes_opt, &cfg->show_notes, value);
else if (b)
- enable_default_display_notes(&notes_opt, &show_notes);
+ enable_default_display_notes(&cfg->notes_opt, &cfg->show_notes);
else
- disable_display_notes(&notes_opt, &show_notes);
+ disable_display_notes(&cfg->notes_opt, &cfg->show_notes);
return 0;
}
if (!strcmp(var, "format.coverfromdescription")) {
- cover_from_description_mode = parse_cover_from_description(value);
+ cfg->cover_from_description_mode = parse_cover_from_description(value);
return 0;
}
if (!strcmp(var, "format.mboxrd")) {
@@ -1110,7 +1204,7 @@ static int git_format_config(const char *var, const char *value,
if (!strcmp(var, "diff.noprefix"))
return 0;
- return git_log_config(var, value, ctx, cb);
+ return git_log_config(var, value, ctx, &cfg->log);
}
static const char *output_directory = NULL;
@@ -1189,7 +1283,7 @@ static void get_patch_ids(struct rev_info *rev, struct patch_ids *ids)
o2->flags = flags2;
}
-static void gen_message_id(struct rev_info *info, char *base)
+static void gen_message_id(struct rev_info *info, const char *base)
{
struct strbuf buf = STRBUF_INIT;
strbuf_addf(&buf, "%s.%"PRItime".git.%s", base,
@@ -1198,7 +1292,7 @@ static void gen_message_id(struct rev_info *info, char *base)
info->message_id = strbuf_detach(&buf, NULL);
}
-static void print_signature(FILE *file)
+static void print_signature(const char *signature, FILE *file)
{
if (!signature || !*signature)
return;
@@ -1268,14 +1362,15 @@ static void prepare_cover_text(struct pretty_print_context *pp,
const char *branch_name,
struct strbuf *sb,
const char *encoding,
- int need_8bit_cte)
+ int need_8bit_cte,
+ const struct format_config *cfg)
{
const char *subject = "*** SUBJECT HERE ***";
const char *body = "*** BLURB HERE ***";
struct strbuf description_sb = STRBUF_INIT;
struct strbuf subject_sb = STRBUF_INIT;
- if (cover_from_description_mode == COVER_FROM_NONE)
+ if (cfg->cover_from_description_mode == COVER_FROM_NONE)
goto do_pp;
if (description_file && *description_file)
@@ -1285,13 +1380,13 @@ static void prepare_cover_text(struct pretty_print_context *pp,
if (!description_sb.len)
goto do_pp;
- if (cover_from_description_mode == COVER_FROM_SUBJECT ||
- cover_from_description_mode == COVER_FROM_AUTO)
+ if (cfg->cover_from_description_mode == COVER_FROM_SUBJECT ||
+ cfg->cover_from_description_mode == COVER_FROM_AUTO)
body = format_subject(&subject_sb, description_sb.buf, " ");
- if (cover_from_description_mode == COVER_FROM_MESSAGE ||
- (cover_from_description_mode == COVER_FROM_AUTO &&
- subject_sb.len > COVER_FROM_AUTO_MAX_SUBJECT_LEN))
+ if (cfg->cover_from_description_mode == COVER_FROM_MESSAGE ||
+ (cfg->cover_from_description_mode == COVER_FROM_AUTO &&
+ subject_sb.len > COVER_FROM_AUTO_MAX_SUBJECT_LEN))
body = description_sb.buf;
else
subject = subject_sb.buf;
@@ -1328,7 +1423,8 @@ static void make_cover_letter(struct rev_info *rev, int use_separate_file,
int nr, struct commit **list,
const char *description_file,
const char *branch_name,
- int quiet)
+ int quiet,
+ const struct format_config *cfg)
{
const char *committer;
struct shortlog log;
@@ -1367,7 +1463,7 @@ static void make_cover_letter(struct rev_info *rev, int use_separate_file,
pp.encode_email_headers = rev->encode_email_headers;
pp_user_info(&pp, NULL, &sb, committer, encoding);
prepare_cover_text(&pp, description_file, branch_name, &sb,
- encoding, need_8bit_cte);
+ encoding, need_8bit_cte, cfg);
fprintf(rev->diffopt.file, "%s\n", sb.buf);
free(pp.after_subject);
@@ -1468,41 +1564,54 @@ static const char * const builtin_format_patch_usage[] = {
NULL
};
-static int keep_subject = 0;
+struct keep_callback_data {
+ struct format_config *cfg;
+ struct rev_info *revs;
+};
static int keep_callback(const struct option *opt, const char *arg, int unset)
{
+ struct keep_callback_data *data = opt->value;
BUG_ON_OPT_NEG(unset);
BUG_ON_OPT_ARG(arg);
- ((struct rev_info *)opt->value)->total = -1;
- keep_subject = 1;
+ data->revs->total = -1;
+ data->cfg->keep_subject = 1;
return 0;
}
-static int subject_prefix = 0;
-
static int subject_prefix_callback(const struct option *opt, const char *arg,
int unset)
{
- struct strbuf *sprefix;
+ struct format_config *cfg = opt->value;
BUG_ON_OPT_NEG(unset);
- sprefix = opt->value;
- subject_prefix = 1;
- strbuf_reset(sprefix);
- strbuf_addstr(sprefix, arg);
+ cfg->subject_prefix = 1;
+ strbuf_reset(&cfg->sprefix);
+ strbuf_addstr(&cfg->sprefix, arg);
return 0;
}
-static int numbered_cmdline_opt = 0;
+static int rfc_callback(const struct option *opt, const char *arg,
+ int unset)
+{
+ const char **rfc = opt->value;
+
+ *rfc = opt->value;
+ if (unset)
+ *rfc = NULL;
+ else
+ *rfc = arg ? arg : "RFC";
+ return 0;
+}
static int numbered_callback(const struct option *opt, const char *arg,
int unset)
{
+ struct format_config *cfg = opt->value;
BUG_ON_OPT_ARG(arg);
- *(int *)opt->value = numbered_cmdline_opt = unset ? 0 : 1;
+ cfg->numbered = cfg->numbered_cmdline_opt = unset ? 0 : 1;
if (unset)
- auto_number = 0;
+ cfg->auto_number = 0;
return 0;
}
@@ -1526,13 +1635,14 @@ static int output_directory_callback(const struct option *opt, const char *arg,
static int thread_callback(const struct option *opt, const char *arg, int unset)
{
- enum thread_level *thread = (enum thread_level *)opt->value;
+ struct format_config *cfg = opt->value;
+
if (unset)
- *thread = THREAD_UNSET;
+ cfg->thread = THREAD_UNSET;
else if (!arg || !strcmp(arg, "shallow"))
- *thread = THREAD_SHALLOW;
+ cfg->thread = THREAD_SHALLOW;
else if (!strcmp(arg, "deep"))
- *thread = THREAD_DEEP;
+ cfg->thread = THREAD_DEEP;
/*
* Please update _git_formatpatch() in git-completion.bash
* when you add new options.
@@ -1568,15 +1678,17 @@ static int inline_callback(const struct option *opt, const char *arg, int unset)
return 0;
}
-static int header_callback(const struct option *opt UNUSED, const char *arg,
+static int header_callback(const struct option *opt, const char *arg,
int unset)
{
+ struct format_config *cfg = opt->value;
+
if (unset) {
- string_list_clear(&extra_hdr, 0);
- string_list_clear(&extra_to, 0);
- string_list_clear(&extra_cc, 0);
+ string_list_clear(&cfg->extra_hdr, 0);
+ string_list_clear(&cfg->extra_to, 0);
+ string_list_clear(&cfg->extra_cc, 0);
} else {
- add_header(arg);
+ add_header(cfg, arg);
}
return 0;
}
@@ -1598,17 +1710,17 @@ static int from_callback(const struct option *opt, const char *arg, int unset)
static int base_callback(const struct option *opt, const char *arg, int unset)
{
- const char **base_commit = opt->value;
+ struct format_config *cfg = opt->value;
if (unset) {
- auto_base = AUTO_BASE_NEVER;
- *base_commit = NULL;
+ cfg->auto_base = AUTO_BASE_NEVER;
+ FREE_AND_NULL(cfg->base_commit);
} else if (!strcmp(arg, "auto")) {
- auto_base = AUTO_BASE_ALWAYS;
- *base_commit = NULL;
+ cfg->auto_base = AUTO_BASE_ALWAYS;
+ FREE_AND_NULL(cfg->base_commit);
} else {
- auto_base = AUTO_BASE_NEVER;
- *base_commit = arg;
+ cfg->auto_base = AUTO_BASE_NEVER;
+ cfg->base_commit = xstrdup(arg);
}
return 0;
}
@@ -1619,7 +1731,7 @@ struct base_tree_info {
struct object_id *patch_id;
};
-static struct commit *get_base_commit(const char *base_commit,
+static struct commit *get_base_commit(const struct format_config *cfg,
struct commit **list,
int total)
{
@@ -1627,9 +1739,9 @@ static struct commit *get_base_commit(const char *base_commit,
struct commit **rev;
int i = 0, rev_nr = 0, auto_select, die_on_failure, ret;
- switch (auto_base) {
+ switch (cfg->auto_base) {
case AUTO_BASE_NEVER:
- if (base_commit) {
+ if (cfg->base_commit) {
auto_select = 0;
die_on_failure = 1;
} else {
@@ -1639,11 +1751,11 @@ static struct commit *get_base_commit(const char *base_commit,
break;
case AUTO_BASE_ALWAYS:
case AUTO_BASE_WHEN_ABLE:
- if (base_commit) {
+ if (cfg->base_commit) {
BUG("requested automatic base selection but a commit was provided");
} else {
auto_select = 1;
- die_on_failure = auto_base == AUTO_BASE_ALWAYS;
+ die_on_failure = cfg->auto_base == AUTO_BASE_ALWAYS;
}
break;
default:
@@ -1651,9 +1763,9 @@ static struct commit *get_base_commit(const char *base_commit,
}
if (!auto_select) {
- base = lookup_commit_reference_by_name(base_commit);
+ base = lookup_commit_reference_by_name(cfg->base_commit);
if (!base)
- die(_("unknown commit %s"), base_commit);
+ die(_("unknown commit %s"), cfg->base_commit);
} else {
struct branch *curr_branch = branch_get(NULL);
const char *upstream = branch_get_upstream(curr_branch, NULL);
@@ -1826,7 +1938,7 @@ static void print_bases(struct base_tree_info *bases, FILE *file)
free(bases->patch_id);
bases->nr_patch_id = 0;
bases->alloc_patch_id = 0;
- oidclr(&bases->base_commit);
+ oidclr(&bases->base_commit, the_repository->hash_algo);
}
static const char *diff_title(struct strbuf *sb,
@@ -1871,6 +1983,7 @@ static void infer_range_diff_ranges(struct strbuf *r1,
int cmd_format_patch(int argc, const char **argv, const char *prefix)
{
+ struct format_config cfg;
struct commit *commit;
struct commit **list = NULL;
struct rev_info rev;
@@ -1895,7 +2008,6 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
char *cover_from_description_arg = NULL;
char *description_file = NULL;
char *branch_name = NULL;
- char *base_commit = NULL;
struct base_tree_info bases;
struct commit *base;
int show_progress = 0;
@@ -1906,18 +2018,24 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
struct strbuf rdiff1 = STRBUF_INIT;
struct strbuf rdiff2 = STRBUF_INIT;
struct strbuf rdiff_title = STRBUF_INIT;
- struct strbuf sprefix = STRBUF_INIT;
+ const char *rfc = NULL;
int creation_factor = -1;
- int rfc = 0;
+ const char *signature = git_version_string;
+ char *signature_file_arg = NULL;
+ struct keep_callback_data keep_callback_data = {
+ .cfg = &cfg,
+ .revs = &rev,
+ };
+ const char *fmt_patch_suffix = NULL;
const struct option builtin_format_patch_options[] = {
- OPT_CALLBACK_F('n', "numbered", &numbered, NULL,
+ OPT_CALLBACK_F('n', "numbered", &cfg, NULL,
N_("use [PATCH n/m] even with a single patch"),
PARSE_OPT_NOARG, numbered_callback),
- OPT_CALLBACK_F('N', "no-numbered", &numbered, NULL,
+ OPT_CALLBACK_F('N', "no-numbered", &cfg, NULL,
N_("use [PATCH] even with multiple patches"),
PARSE_OPT_NOARG | PARSE_OPT_NONEG, no_numbered_callback),
- OPT_BOOL('s', "signoff", &do_signoff, N_("add a Signed-off-by trailer")),
+ OPT_BOOL('s', "signoff", &cfg.do_signoff, N_("add a Signed-off-by trailer")),
OPT_BOOL(0, "stdout", &use_stdout,
N_("print patches to standard out")),
OPT_BOOL(0, "cover-letter", &cover_letter,
@@ -1930,21 +2048,23 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
N_("start numbering patches at <n> instead of 1")),
OPT_STRING('v', "reroll-count", &reroll_count, N_("reroll-count"),
N_("mark the series as Nth re-roll")),
- OPT_INTEGER(0, "filename-max-length", &fmt_patch_name_max,
+ OPT_INTEGER(0, "filename-max-length", &cfg.log.fmt_patch_name_max,
N_("max length of output filename")),
- OPT_BOOL(0, "rfc", &rfc, N_("use [RFC PATCH] instead of [PATCH]")),
+ OPT_CALLBACK_F(0, "rfc", &rfc, N_("rfc"),
+ N_("add <rfc> (default 'RFC') before 'PATCH'"),
+ PARSE_OPT_OPTARG, rfc_callback),
OPT_STRING(0, "cover-from-description", &cover_from_description_arg,
N_("cover-from-description-mode"),
N_("generate parts of a cover letter based on a branch's description")),
OPT_FILENAME(0, "description-file", &description_file,
N_("use branch description from file")),
- OPT_CALLBACK_F(0, "subject-prefix", &sprefix, N_("prefix"),
+ OPT_CALLBACK_F(0, "subject-prefix", &cfg, N_("prefix"),
N_("use [<prefix>] instead of [PATCH]"),
PARSE_OPT_NONEG, subject_prefix_callback),
OPT_CALLBACK_F('o', "output-directory", &output_directory,
N_("dir"), N_("store resulting files in <dir>"),
PARSE_OPT_NONEG, output_directory_callback),
- OPT_CALLBACK_F('k', "keep-subject", &rev, NULL,
+ OPT_CALLBACK_F('k', "keep-subject", &keep_callback_data, NULL,
N_("don't strip/add [PATCH]"),
PARSE_OPT_NOARG | PARSE_OPT_NONEG, keep_callback),
OPT_BOOL(0, "no-binary", &no_binary_diff,
@@ -1957,11 +2077,11 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
N_("show patch format instead of default (patch + stat)"),
1, PARSE_OPT_NONEG),
OPT_GROUP(N_("Messaging")),
- OPT_CALLBACK(0, "add-header", NULL, N_("header"),
+ OPT_CALLBACK(0, "add-header", &cfg, N_("header"),
N_("add email header"), header_callback),
- OPT_STRING_LIST(0, "to", &extra_to, N_("email"), N_("add To: header")),
- OPT_STRING_LIST(0, "cc", &extra_cc, N_("email"), N_("add Cc: header")),
- OPT_CALLBACK_F(0, "from", &from, N_("ident"),
+ OPT_STRING_LIST(0, "to", &cfg.extra_to, N_("email"), N_("add To: header")),
+ OPT_STRING_LIST(0, "cc", &cfg.extra_cc, N_("email"), N_("add Cc: header")),
+ OPT_CALLBACK_F(0, "from", &cfg.from, N_("ident"),
N_("set From address to <ident> (or committer ident if absent)"),
PARSE_OPT_OPTARG, from_callback),
OPT_STRING(0, "in-reply-to", &in_reply_to, N_("message-id"),
@@ -1973,15 +2093,15 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
N_("inline the patch"),
PARSE_OPT_OPTARG | PARSE_OPT_NONEG,
inline_callback),
- OPT_CALLBACK_F(0, "thread", &thread, N_("style"),
+ OPT_CALLBACK_F(0, "thread", &cfg, N_("style"),
N_("enable message threading, styles: shallow, deep"),
PARSE_OPT_OPTARG, thread_callback),
OPT_STRING(0, "signature", &signature, N_("signature"),
N_("add a signature")),
- OPT_CALLBACK_F(0, "base", &base_commit, N_("base-commit"),
+ OPT_CALLBACK_F(0, "base", &cfg, N_("base-commit"),
N_("add prerequisite tree info to the patch series"),
0, base_callback),
- OPT_FILENAME(0, "signature-file", &signature_file,
+ OPT_FILENAME(0, "signature-file", &signature_file_arg,
N_("add a signature from a file")),
OPT__QUIET(&quiet, N_("don't print the patch filenames")),
OPT_BOOL(0, "progress", &show_progress,
@@ -1998,20 +2118,17 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
OPT_END()
};
- extra_hdr.strdup_strings = 1;
- extra_to.strdup_strings = 1;
- extra_cc.strdup_strings = 1;
-
- init_log_defaults();
- init_display_notes(&notes_opt);
- git_config(git_format_config, NULL);
+ format_config_init(&cfg);
+ init_diff_ui_defaults();
+ init_display_notes(&cfg.notes_opt);
+ git_config(git_format_config, &cfg);
repo_init_revisions(the_repository, &rev, prefix);
git_config(grep_config, &rev.grep_filter);
- rev.show_notes = show_notes;
- memcpy(&rev.notes_opt, &notes_opt, sizeof(notes_opt));
+ rev.show_notes = cfg.show_notes;
+ memcpy(&rev.notes_opt, &cfg.notes_opt, sizeof(cfg.notes_opt));
rev.commit_format = CMIT_FMT_EMAIL;
- rev.encode_email_headers = default_encode_email_headers;
+ rev.encode_email_headers = cfg.log.default_encode_email_headers;
rev.expand_tabs_in_log_default = 0;
rev.verbose_header = 1;
rev.diff = 1;
@@ -2022,12 +2139,12 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
s_r_opt.def = "HEAD";
s_r_opt.revarg_opt = REVARG_COMMITTISH;
- strbuf_addstr(&sprefix, fmt_patch_subject_prefix);
+ strbuf_addstr(&cfg.sprefix, cfg.log.fmt_patch_subject_prefix);
if (format_no_prefix)
diff_set_noprefix(&rev.diffopt);
- if (default_attach) {
- rev.mime_boundary = default_attach;
+ if (cfg.default_attach) {
+ rev.mime_boundary = cfg.default_attach;
rev.no_inline = 1;
}
@@ -2043,55 +2160,63 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
rev.force_in_body_from = force_in_body_from;
+ if (!fmt_patch_suffix)
+ fmt_patch_suffix = cfg.fmt_patch_suffix;
+
/* Make sure "0000-$sub.patch" gives non-negative length for $sub */
- if (fmt_patch_name_max <= strlen("0000-") + strlen(fmt_patch_suffix))
- fmt_patch_name_max = strlen("0000-") + strlen(fmt_patch_suffix);
+ if (cfg.log.fmt_patch_name_max <= strlen("0000-") + strlen(fmt_patch_suffix))
+ cfg.log.fmt_patch_name_max = strlen("0000-") + strlen(fmt_patch_suffix);
if (cover_from_description_arg)
- cover_from_description_mode = parse_cover_from_description(cover_from_description_arg);
+ cfg.cover_from_description_mode = parse_cover_from_description(cover_from_description_arg);
- if (rfc)
- strbuf_insertstr(&sprefix, 0, "RFC ");
+ if (rfc && rfc[0]) {
+ cfg.subject_prefix = 1;
+ if (rfc[0] == '-')
+ strbuf_addf(&cfg.sprefix, " %s", rfc + 1);
+ else
+ strbuf_insertf(&cfg.sprefix, 0, "%s ", rfc);
+ }
if (reroll_count) {
- strbuf_addf(&sprefix, " v%s", reroll_count);
+ strbuf_addf(&cfg.sprefix, " v%s", reroll_count);
rev.reroll_count = reroll_count;
}
- rev.subject_prefix = sprefix.buf;
+ rev.subject_prefix = cfg.sprefix.buf;
- for (i = 0; i < extra_hdr.nr; i++) {
- strbuf_addstr(&buf, extra_hdr.items[i].string);
+ for (i = 0; i < cfg.extra_hdr.nr; i++) {
+ strbuf_addstr(&buf, cfg.extra_hdr.items[i].string);
strbuf_addch(&buf, '\n');
}
- if (extra_to.nr)
+ if (cfg.extra_to.nr)
strbuf_addstr(&buf, "To: ");
- for (i = 0; i < extra_to.nr; i++) {
+ for (i = 0; i < cfg.extra_to.nr; i++) {
if (i)
strbuf_addstr(&buf, " ");
- strbuf_addstr(&buf, extra_to.items[i].string);
- if (i + 1 < extra_to.nr)
+ strbuf_addstr(&buf, cfg.extra_to.items[i].string);
+ if (i + 1 < cfg.extra_to.nr)
strbuf_addch(&buf, ',');
strbuf_addch(&buf, '\n');
}
- if (extra_cc.nr)
+ if (cfg.extra_cc.nr)
strbuf_addstr(&buf, "Cc: ");
- for (i = 0; i < extra_cc.nr; i++) {
+ for (i = 0; i < cfg.extra_cc.nr; i++) {
if (i)
strbuf_addstr(&buf, " ");
- strbuf_addstr(&buf, extra_cc.items[i].string);
- if (i + 1 < extra_cc.nr)
+ strbuf_addstr(&buf, cfg.extra_cc.items[i].string);
+ if (i + 1 < cfg.extra_cc.nr)
strbuf_addch(&buf, ',');
strbuf_addch(&buf, '\n');
}
rev.extra_headers = to_free = strbuf_detach(&buf, NULL);
- if (from) {
- if (split_ident_line(&rev.from_ident, from, strlen(from)))
- die(_("invalid ident line: %s"), from);
+ if (cfg.from) {
+ if (split_ident_line(&rev.from_ident, cfg.from, strlen(cfg.from)))
+ die(_("invalid ident line: %s"), cfg.from);
}
if (start_number < 0)
@@ -2102,14 +2227,14 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
* and it would conflict with --keep-subject (-k) from the
* command line, reset "numbered".
*/
- if (numbered && keep_subject && !numbered_cmdline_opt)
- numbered = 0;
+ if (cfg.numbered && cfg.keep_subject && !cfg.numbered_cmdline_opt)
+ cfg.numbered = 0;
- if (numbered && keep_subject)
+ if (cfg.numbered && cfg.keep_subject)
die(_("options '%s' and '%s' cannot be used together"), "-n", "-k");
- if (keep_subject && subject_prefix)
+ if (cfg.keep_subject && cfg.subject_prefix)
die(_("options '%s' and '%s' cannot be used together"), "--subject-prefix/--rfc", "-k");
- rev.preserve_subject = keep_subject;
+ rev.preserve_subject = cfg.keep_subject;
argc = setup_revisions(argc, argv, &rev, &s_r_opt);
if (argc > 1)
@@ -2136,7 +2261,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
rev.always_show_header = 1;
rev.zero_commit = zero_commit;
- rev.patch_name_max = fmt_patch_name_max;
+ rev.patch_name_max = cfg.log.fmt_patch_name_max;
if (!rev.diffopt.flags.text && !no_binary_diff)
rev.diffopt.flags.binary = 1;
@@ -2157,7 +2282,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
int saved;
if (!output_directory)
- output_directory = config_output_directory;
+ output_directory = cfg.config_output_directory;
output_directory = set_outdir(prefix, output_directory);
if (rev.diffopt.use_color != GIT_COLOR_ALWAYS)
@@ -2206,8 +2331,10 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
if (check_head) {
const char *ref, *v;
- ref = resolve_ref_unsafe("HEAD", RESOLVE_REF_READING,
- NULL, NULL);
+ ref = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
+ "HEAD",
+ RESOLVE_REF_READING,
+ NULL, NULL);
if (ref && skip_prefix(ref, "refs/heads/", &v))
branch_name = xstrdup(v);
else
@@ -2253,14 +2380,16 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
goto done;
total = nr;
if (cover_letter == -1) {
- if (config_cover_letter == COVER_AUTO)
+ if (cfg.config_cover_letter == COVER_AUTO)
cover_letter = (total > 1);
+ else if ((idiff_prev.nr || rdiff_prev) && (total > 1))
+ cover_letter = (cfg.config_cover_letter != COVER_OFF);
else
- cover_letter = (config_cover_letter == COVER_ON);
+ cover_letter = (cfg.config_cover_letter == COVER_ON);
}
- if (!keep_subject && auto_number && (total > 1 || cover_letter))
- numbered = 1;
- if (numbered)
+ if (!cfg.keep_subject && cfg.auto_number && (total > 1 || cover_letter))
+ cfg.numbered = 1;
+ if (cfg.numbered)
rev.total = total + start_number - 1;
if (idiff_prev.nr) {
@@ -2274,7 +2403,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
}
if (creation_factor < 0)
- creation_factor = RANGE_DIFF_CREATION_FACTOR_DEFAULT;
+ creation_factor = CREATION_FACTOR_FOR_THE_SAME_SERIES;
else if (!rdiff_prev)
die(_("the option '%s' requires '%s'"), "--creation-factor", "--range-diff");
@@ -2292,27 +2421,40 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
_("Range-diff against v%d:"));
}
+ /*
+ * The order of precedence is:
+ *
+ * 1. The `--signature` and `--no-signature` options.
+ * 2. The `--signature-file` option.
+ * 3. The `format.signature` config.
+ * 4. The `format.signatureFile` config.
+ * 5. Default `git_version_string`.
+ */
if (!signature) {
; /* --no-signature inhibits all signatures */
} else if (signature && signature != git_version_string) {
; /* non-default signature already set */
- } else if (signature_file) {
+ } else if (signature_file_arg || (cfg.signature_file && !cfg.signature)) {
struct strbuf buf = STRBUF_INIT;
+ const char *signature_file = signature_file_arg ?
+ signature_file_arg : cfg.signature_file;
if (strbuf_read_file(&buf, signature_file, 128) < 0)
die_errno(_("unable to read signature file '%s'"), signature_file);
signature = strbuf_detach(&buf, NULL);
+ } else if (cfg.signature) {
+ signature = cfg.signature;
}
memset(&bases, 0, sizeof(bases));
- base = get_base_commit(base_commit, list, nr);
+ base = get_base_commit(&cfg, list, nr);
if (base) {
reset_revision_walk();
clear_object_flags(UNINTERESTING);
prepare_bases(&bases, base, list, nr);
}
- if (in_reply_to || thread || cover_letter) {
+ if (in_reply_to || cfg.thread || cover_letter) {
rev.ref_message_ids = xmalloc(sizeof(*rev.ref_message_ids));
string_list_init_dup(rev.ref_message_ids);
}
@@ -2323,19 +2465,19 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
rev.numbered_files = just_numbers;
rev.patch_suffix = fmt_patch_suffix;
if (cover_letter) {
- if (thread)
+ if (cfg.thread)
gen_message_id(&rev, "cover");
make_cover_letter(&rev, !!output_directory,
- origin, nr, list, description_file, branch_name, quiet);
+ origin, nr, list, description_file, branch_name, quiet, &cfg);
print_bases(&bases, rev.diffopt.file);
- print_signature(rev.diffopt.file);
+ print_signature(signature, rev.diffopt.file);
total++;
start_number--;
/* interdiff/range-diff in cover-letter; omit from patches */
rev.idiff_oid1 = NULL;
rev.rdiff1 = NULL;
}
- rev.add_signoff = do_signoff;
+ rev.add_signoff = cfg.do_signoff;
if (show_progress)
progress = start_delayed_progress(_("Generating patches"), total);
@@ -2345,7 +2487,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
commit = list[nr];
rev.nr = total - nr + (start_number - 1);
/* Make the second and subsequent mails replies to the first */
- if (thread) {
+ if (cfg.thread) {
/* Have we already had a message ID? */
if (rev.message_id) {
/*
@@ -2369,7 +2511,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
* letter is a reply to the
* --in-reply-to, if specified.
*/
- if (thread == THREAD_SHALLOW
+ if (cfg.thread == THREAD_SHALLOW
&& rev.ref_message_ids->nr > 0
&& (!cover_letter || rev.nr > 1))
free(rev.message_id);
@@ -2402,7 +2544,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
mime_boundary_leader,
rev.mime_boundary);
else
- print_signature(rev.diffopt.file);
+ print_signature(signature, rev.diffopt.file);
}
if (output_directory)
fclose(rev.diffopt.file);
@@ -2410,9 +2552,6 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
stop_progress(&progress);
free(list);
free(branch_name);
- string_list_clear(&extra_to, 0);
- string_list_clear(&extra_cc, 0);
- string_list_clear(&extra_hdr, 0);
if (ignore_if_in_upstream)
free_patch_ids(&ids);
@@ -2422,13 +2561,16 @@ done:
strbuf_release(&rdiff1);
strbuf_release(&rdiff2);
strbuf_release(&rdiff_title);
- strbuf_release(&sprefix);
+ free(description_file);
+ free(signature_file_arg);
free(to_free);
free(rev.message_id);
if (rev.ref_message_ids)
string_list_clear(rev.ref_message_ids, 0);
free(rev.ref_message_ids);
- return cmd_log_deinit(0, &rev);
+ release_revisions(&rev);
+ format_config_release(&cfg);
+ return 0;
}
static int add_pending_commit(const char *arg, struct rev_info *revs, int flags)
@@ -2535,16 +2677,16 @@ int cmd_cherry(int argc, const char **argv, const char *prefix)
commit_list_insert(commit, &list);
}
- while (list) {
+ for (struct commit_list *l = list; l; l = l->next) {
char sign = '+';
- commit = list->item;
+ commit = l->item;
if (has_commit_patch_id(commit, &ids))
sign = '-';
print_commit(sign, commit, verbose, abbrev, revs.diffopt.file);
- list = list->next;
}
+ free_commit_list(list);
free_patch_ids(&ids);
return 0;
}
diff --git a/builtin/ls-remote.c b/builtin/ls-remote.c
index e8d65ebbdc..debf2d4f88 100644
--- a/builtin/ls-remote.c
+++ b/builtin/ls-remote.c
@@ -9,7 +9,7 @@
#include "wildmatch.h"
static const char * const ls_remote_usage[] = {
- N_("git ls-remote [--heads] [--tags] [--refs] [--upload-pack=<exec>]\n"
+ N_("git ls-remote [--branches] [--tags] [--refs] [--upload-pack=<exec>]\n"
" [-q | --quiet] [--exit-code] [--get-url] [--sort=<key>]\n"
" [--symref] [<repository> [<patterns>...]]"),
NULL
@@ -68,7 +68,10 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)
N_("path of git-upload-pack on the remote host"),
PARSE_OPT_HIDDEN },
OPT_BIT('t', "tags", &flags, N_("limit to tags"), REF_TAGS),
- OPT_BIT('h', "heads", &flags, N_("limit to heads"), REF_HEADS),
+ OPT_BIT('b', "branches", &flags, N_("limit to branches"), REF_BRANCHES),
+ OPT_BIT_F('h', "heads", &flags,
+ N_("deprecated synonym for --branches"), REF_BRANCHES,
+ PARSE_OPT_HIDDEN),
OPT_BIT(0, "refs", &flags, N_("do not show peeled tags"), REF_NORMAL),
OPT_BOOL(0, "get-url", &get_url,
N_("take url.<base>.insteadOf into account")),
@@ -100,7 +103,7 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)
if (flags & REF_TAGS)
strvec_push(&transport_options.ref_prefixes, "refs/tags/");
- if (flags & REF_HEADS)
+ if (flags & REF_BRANCHES)
strvec_push(&transport_options.ref_prefixes, "refs/heads/");
remote = remote_get(dest);
@@ -109,11 +112,9 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)
die("bad repository '%s'", dest);
die("No remote configured to list refs from.");
}
- if (!remote->url_nr)
- die("remote %s has no configured URL", dest);
if (get_url) {
- printf("%s\n", *remote->url);
+ printf("%s\n", remote->url.v[0]);
return 0;
}
@@ -130,7 +131,7 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)
}
if (!dest && !quiet)
- fprintf(stderr, "From %s\n", *remote->url);
+ fprintf(stderr, "From %s\n", remote->url.v[0]);
for ( ; ref; ref = ref->next) {
struct ref_array_item *item;
if (!check_ref_type(ref, flags))
diff --git a/builtin/ls-tree.c b/builtin/ls-tree.c
index 7bf84b235c..bf372c67d7 100644
--- a/builtin/ls-tree.c
+++ b/builtin/ls-tree.c
@@ -367,7 +367,7 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)
OPT_END()
};
struct ls_tree_cmdmode_to_fmt *m2f = ls_tree_cmdmode_format;
- struct object_context obj_context;
+ struct object_context obj_context = {0};
int ret;
git_config(git_default_config, NULL);
@@ -441,5 +441,6 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)
ret = !!read_tree(the_repository, tree, &options.pathspec, fn, &options);
clear_pathspec(&options.pathspec);
+ object_context_release(&obj_context);
return ret;
}
diff --git a/builtin/mailsplit.c b/builtin/mailsplit.c
index 3af9ddb8ae..fe6dbc5d05 100644
--- a/builtin/mailsplit.c
+++ b/builtin/mailsplit.c
@@ -113,8 +113,8 @@ static int populate_maildir_list(struct string_list *list, const char *path)
DIR *dir;
struct dirent *dent;
char *name = NULL;
- char *subs[] = { "cur", "new", NULL };
- char **sub;
+ const char *subs[] = { "cur", "new", NULL };
+ const char **sub;
int ret = -1;
for (sub = subs; *sub; ++sub) {
diff --git a/builtin/merge-index.c b/builtin/merge-index.c
index 270d5f644a..0fabe3f6bb 100644
--- a/builtin/merge-index.c
+++ b/builtin/merge-index.c
@@ -1,4 +1,3 @@
-#define USE_THE_INDEX_VARIABLE
#include "builtin.h"
#include "hex.h"
#include "read-cache-ll.h"
@@ -18,11 +17,11 @@ static int merge_entry(int pos, const char *path)
char ownbuf[4][60];
struct child_process cmd = CHILD_PROCESS_INIT;
- if (pos >= the_index.cache_nr)
+ if (pos >= the_repository->index->cache_nr)
die("git merge-index: %s not in the cache", path);
found = 0;
do {
- const struct cache_entry *ce = the_index.cache[pos];
+ const struct cache_entry *ce = the_repository->index->cache[pos];
int stage = ce_stage(ce);
if (strcmp(ce->name, path))
@@ -32,7 +31,7 @@ static int merge_entry(int pos, const char *path)
xsnprintf(ownbuf[stage], sizeof(ownbuf[stage]), "%o", ce->ce_mode);
arguments[stage] = hexbuf[stage];
arguments[stage + 4] = ownbuf[stage];
- } while (++pos < the_index.cache_nr);
+ } while (++pos < the_repository->index->cache_nr);
if (!found)
die("git merge-index: %s not in the cache", path);
@@ -51,7 +50,7 @@ static int merge_entry(int pos, const char *path)
static void merge_one_path(const char *path)
{
- int pos = index_name_pos(&the_index, path, strlen(path));
+ int pos = index_name_pos(the_repository->index, path, strlen(path));
/*
* If it already exists in the cache as stage0, it's
@@ -65,9 +64,9 @@ static void merge_all(void)
{
int i;
/* TODO: audit for interaction with sparse-index. */
- ensure_full_index(&the_index);
- for (i = 0; i < the_index.cache_nr; i++) {
- const struct cache_entry *ce = the_index.cache[i];
+ ensure_full_index(the_repository->index);
+ for (i = 0; i < the_repository->index->cache_nr; i++) {
+ const struct cache_entry *ce = the_repository->index->cache[i];
if (!ce_stage(ce))
continue;
i += merge_entry(i, ce->name)-1;
@@ -89,7 +88,7 @@ int cmd_merge_index(int argc, const char **argv, const char *prefix UNUSED)
repo_read_index(the_repository);
/* TODO: audit for interaction with sparse-index. */
- ensure_full_index(&the_index);
+ ensure_full_index(the_repository->index);
i = 1;
if (!strcmp(argv[i], "-o")) {
diff --git a/builtin/merge-recursive.c b/builtin/merge-recursive.c
index c2ce044a20..82bebea15b 100644
--- a/builtin/merge-recursive.c
+++ b/builtin/merge-recursive.c
@@ -23,7 +23,7 @@ static char *better_branch_name(const char *branch)
int cmd_merge_recursive(int argc, const char **argv, const char *prefix UNUSED)
{
- const struct object_id *bases[21];
+ struct object_id bases[21];
unsigned bases_count = 0;
int i, failed;
struct object_id h1, h2;
@@ -49,10 +49,8 @@ int cmd_merge_recursive(int argc, const char **argv, const char *prefix UNUSED)
continue;
}
if (bases_count < ARRAY_SIZE(bases)-1) {
- struct object_id *oid = xmalloc(sizeof(struct object_id));
- if (repo_get_oid(the_repository, argv[i], oid))
+ if (repo_get_oid(the_repository, argv[i], &bases[bases_count++]))
die(_("could not parse object '%s'"), argv[i]);
- bases[bases_count++] = oid;
}
else
warning(Q_("cannot handle more than %d base. "
diff --git a/builtin/merge-tree.c b/builtin/merge-tree.c
index 8bdb439131..dab2fdc2a6 100644
--- a/builtin/merge-tree.c
+++ b/builtin/merge-tree.c
@@ -1,4 +1,3 @@
-#define USE_THE_INDEX_VARIABLE
#include "builtin.h"
#include "tree-walk.h"
#include "xdiff-interface.h"
@@ -364,7 +363,7 @@ static void trivial_merge_trees(struct tree_desc t[3], const char *base)
setup_traverse_info(&info, base);
info.fn = threeway_callback;
- traverse_trees(&the_index, 3, t, &info);
+ traverse_trees(the_repository->index, 3, t, &info);
}
static void *get_tree_descriptor(struct repository *r,
@@ -483,6 +482,7 @@ static int real_merge(struct merge_tree_options *o,
die(_("refusing to merge unrelated histories"));
merge_bases = reverse_commit_list(merge_bases);
merge_incore_recursive(&opt, merge_bases, parent1, parent2, &result);
+ free_commit_list(merge_bases);
}
if (result.clean < 0)
diff --git a/builtin/merge.c b/builtin/merge.c
index 6f4fec87fc..9fba27d85d 100644
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@ -6,7 +6,6 @@
* Based on git-merge.sh by Junio C Hamano.
*/
-#define USE_THE_INDEX_VARIABLE
#include "builtin.h"
#include "abspath.h"
#include "advice.h"
@@ -101,7 +100,7 @@ static struct strategy all_strategy[] = {
{ "subtree", NO_FAST_FORWARD | NO_TRIVIAL },
};
-static const char *pull_twohead, *pull_octopus;
+static char *pull_twohead, *pull_octopus;
enum ff_type {
FF_NO,
@@ -111,7 +110,7 @@ enum ff_type {
static enum ff_type fast_forward = FF_ALLOW;
-static const char *cleanup_arg;
+static char *cleanup_arg;
static enum commit_msg_cleanup_mode cleanup_mode;
static int option_parse_message(const struct option *opt,
@@ -165,7 +164,7 @@ static struct strategy *get_strategy(const char *name)
{
int i;
struct strategy *ret;
- static struct cmdnames main_cmds, other_cmds;
+ static struct cmdnames main_cmds = {0}, other_cmds = {0};
static int loaded;
char *default_strategy = getenv("GIT_TEST_MERGE_ALGORITHM");
@@ -183,10 +182,9 @@ static struct strategy *get_strategy(const char *name)
return &all_strategy[i];
if (!loaded) {
- struct cmdnames not_strategies;
+ struct cmdnames not_strategies = {0};
loaded = 1;
- memset(&not_strategies, 0, sizeof(struct cmdnames));
load_command_list("git-merge-", &main_cmds, &other_cmds);
for (i = 0; i < main_cmds.cnt; i++) {
int j, found = 0;
@@ -198,6 +196,8 @@ static struct strategy *get_strategy(const char *name)
add_cmdname(&not_strategies, ent->name, ent->len);
}
exclude_cmds(&main_cmds, &not_strategies);
+
+ cmdnames_release(&not_strategies);
}
if (!is_in_cmdlist(&main_cmds, name) && !is_in_cmdlist(&other_cmds, name)) {
fprintf(stderr, _("Could not find merge strategy '%s'.\n"), name);
@@ -217,6 +217,9 @@ static struct strategy *get_strategy(const char *name)
CALLOC_ARRAY(ret, 1);
ret->name = xstrdup(name);
ret->attr = NO_TRIVIAL;
+
+ cmdnames_release(&main_cmds);
+ cmdnames_release(&other_cmds);
return ret;
}
@@ -300,7 +303,7 @@ static int save_state(struct object_id *stash)
int rc = -1;
fd = repo_hold_locked_index(the_repository, &lock_file, 0);
- refresh_index(&the_index, REFRESH_QUIET, NULL, NULL, NULL);
+ refresh_index(the_repository->index, REFRESH_QUIET, NULL, NULL, NULL);
if (0 <= fd)
repo_update_index_if_able(the_repository, &lock_file);
rollback_lock_file(&lock_file);
@@ -331,7 +334,8 @@ static void read_empty(const struct object_id *oid)
{
struct child_process cmd = CHILD_PROCESS_INIT;
- strvec_pushl(&cmd.args, "read-tree", "-m", "-u", empty_tree_oid_hex(),
+ strvec_pushl(&cmd.args, "read-tree", "-m", "-u",
+ empty_tree_oid_hex(the_repository->hash_algo),
oid_to_hex(oid), NULL);
cmd.git_cmd = 1;
@@ -372,7 +376,7 @@ static void restore_state(const struct object_id *head,
run_command(&cmd);
refresh_cache:
- discard_index(&the_index);
+ discard_index(the_repository->index);
if (repo_read_index(the_repository) < 0)
die(_("could not read index"));
}
@@ -449,8 +453,10 @@ static void finish(struct commit *head_commit,
if (verbosity >= 0 && !merge_msg.len)
printf(_("No merge message -- not updating HEAD\n"));
else {
- update_ref(reflog_message.buf, "HEAD", new_head, head,
- 0, UPDATE_REFS_DIE_ON_ERR);
+ refs_update_ref(get_main_ref_store(the_repository),
+ reflog_message.buf, "HEAD", new_head,
+ head,
+ 0, UPDATE_REFS_DIE_ON_ERR);
/*
* We ignore errors in 'gc --auto', since the
* user should see them.
@@ -493,7 +499,7 @@ static void merge_name(const char *remote, struct strbuf *msg)
strbuf_branchname(&bname, remote, 0);
remote = bname.buf;
- oidclr(&branch_head);
+ oidclr(&branch_head, the_repository->hash_algo);
remote_head = get_merge_parent(remote);
if (!remote_head)
die(_("'%s' does not point to a commit"), remote);
@@ -547,7 +553,7 @@ static void merge_name(const char *remote, struct strbuf *msg)
struct strbuf truname = STRBUF_INIT;
strbuf_addf(&truname, "refs/heads/%s", remote);
strbuf_setlen(&truname, truname.len - len);
- if (ref_exists(truname.buf)) {
+ if (refs_ref_exists(get_main_ref_store(the_repository), truname.buf)) {
strbuf_addf(msg,
"%s\t\tbranch '%s'%s of .\n",
oid_to_hex(&remote_head->object.oid),
@@ -610,17 +616,19 @@ static int git_merge_config(const char *k, const char *v,
return 0;
}
- if (!strcmp(k, "merge.diffstat") || !strcmp(k, "merge.stat"))
+ if (!strcmp(k, "merge.diffstat") || !strcmp(k, "merge.stat")) {
show_diffstat = git_config_bool(k, v);
- else if (!strcmp(k, "merge.verifysignatures"))
+ } else if (!strcmp(k, "merge.verifysignatures")) {
verify_signatures = git_config_bool(k, v);
- else if (!strcmp(k, "pull.twohead"))
+ } else if (!strcmp(k, "pull.twohead")) {
+ FREE_AND_NULL(pull_twohead);
return git_config_string(&pull_twohead, k, v);
- else if (!strcmp(k, "pull.octopus"))
+ } else if (!strcmp(k, "pull.octopus")) {
+ FREE_AND_NULL(pull_octopus);
return git_config_string(&pull_octopus, k, v);
- else if (!strcmp(k, "commit.cleanup"))
+ } else if (!strcmp(k, "commit.cleanup")) {
return git_config_string(&cleanup_arg, k, v);
- else if (!strcmp(k, "merge.ff")) {
+ } else if (!strcmp(k, "merge.ff")) {
int boolval = git_parse_maybe_bool(v);
if (0 <= boolval) {
fast_forward = boolval ? FF_ALLOW : FF_NO;
@@ -657,8 +665,8 @@ static int read_tree_trivial(struct object_id *common, struct object_id *head,
memset(&opts, 0, sizeof(opts));
opts.head_idx = 2;
- opts.src_index = &the_index;
- opts.dst_index = &the_index;
+ opts.src_index = the_repository->index;
+ opts.dst_index = the_repository->index;
opts.update = 1;
opts.verbose_update = 1;
opts.trivial_merges_only = 1;
@@ -674,7 +682,7 @@ static int read_tree_trivial(struct object_id *common, struct object_id *head,
if (!trees[nr_trees++])
return -1;
opts.fn = threeway_merge;
- cache_tree_free(&the_index.cache_tree);
+ cache_tree_free(&the_repository->index->cache_tree);
for (i = 0; i < nr_trees; i++) {
parse_tree(trees[i]);
init_tree_desc(t+i, &trees[i]->object.oid,
@@ -687,7 +695,7 @@ static int read_tree_trivial(struct object_id *common, struct object_id *head,
static void write_tree_trivial(struct object_id *oid)
{
- if (write_index_as_tree(oid, &the_index, get_index_file(), 0, NULL))
+ if (write_index_as_tree(oid, the_repository->index, get_index_file(), 0, NULL))
die(_("git write-tree failed to write a tree"));
}
@@ -700,7 +708,7 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
if (repo_refresh_and_write_index(the_repository, REFRESH_QUIET,
SKIP_IF_UNCHANGED, 0, NULL, NULL,
NULL) < 0)
- return error(_("Unable to write index."));
+ die(_("Unable to write index."));
if (!strcmp(strategy, "recursive") || !strcmp(strategy, "subtree") ||
!strcmp(strategy, "ort")) {
@@ -741,11 +749,13 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
else
clean = merge_recursive(&o, head, remoteheads->item,
reversed, &result);
+ free_commit_list(reversed);
+
if (clean < 0) {
rollback_lock_file(&lock);
return 2;
}
- if (write_locked_index(&the_index, &lock,
+ if (write_locked_index(the_repository->index, &lock,
COMMIT_LOCK | SKIP_IF_UNCHANGED))
die(_("unable to write %s"), get_index_file());
return clean ? 0 : 1;
@@ -768,8 +778,8 @@ static int count_unmerged_entries(void)
{
int i, ret = 0;
- for (i = 0; i < the_index.cache_nr; i++)
- if (ce_stage(the_index.cache[i]))
+ for (i = 0; i < the_repository->index->cache_nr; i++)
+ if (ce_stage(the_repository->index->cache[i]))
ret++;
return ret;
@@ -843,9 +853,9 @@ static void prepare_to_commit(struct commit_list *remoteheads)
* the editor and after we invoke run_status above.
*/
if (invoked_hook)
- discard_index(&the_index);
+ discard_index(the_repository->index);
}
- read_index_from(&the_index, index_file, get_git_dir());
+ read_index_from(the_repository->index, index_file, get_git_dir());
strbuf_addbuf(&msg, &merge_msg);
if (squash)
BUG("the control must not reach here under --squash");
@@ -894,7 +904,7 @@ static void prepare_to_commit(struct commit_list *remoteheads)
static int merge_trivial(struct commit *head, struct commit_list *remoteheads)
{
struct object_id result_tree, result_commit;
- struct commit_list *parents, **pptr = &parents;
+ struct commit_list *parents = NULL, **pptr = &parents;
if (repo_refresh_and_write_index(the_repository, REFRESH_QUIET,
SKIP_IF_UNCHANGED, 0, NULL, NULL,
@@ -910,7 +920,9 @@ static int merge_trivial(struct commit *head, struct commit_list *remoteheads)
&result_commit, NULL, sign_commit))
die(_("failed to write commit object"));
finish(head, remoteheads, &result_commit, "In-index merge");
+
remove_merge_branch_state(the_repository);
+ free_commit_list(parents);
return 0;
}
@@ -936,8 +948,10 @@ static int finish_automerge(struct commit *head,
die(_("failed to write commit object"));
strbuf_addf(&buf, "Merge made by the '%s' strategy.", wt_strategy);
finish(head, remoteheads, &result_commit, buf.buf);
+
strbuf_release(&buf);
remove_merge_branch_state(the_repository);
+ free_commit_list(parents);
return 0;
}
@@ -957,7 +971,7 @@ static int suggest_conflicts(void)
* Thus, we will get the cleanup mode which is returned when we _are_
* using an editor.
*/
- append_conflicts_hint(&the_index, &msgbuf,
+ append_conflicts_hint(the_repository->index, &msgbuf,
get_cleanup_mode(cleanup_arg, 1));
fputs(msgbuf.buf, fp);
strbuf_release(&msgbuf);
@@ -1252,7 +1266,7 @@ static int merging_a_throwaway_tag(struct commit *commit)
*/
tag_ref = xstrfmt("refs/tags/%s",
((struct tag *)merge_remote_util(commit)->obj)->tag);
- if (!read_ref(tag_ref, &oid) &&
+ if (!refs_read_ref(get_main_ref_store(the_repository), tag_ref, &oid) &&
oideq(&oid, &merge_remote_util(commit)->obj->oid))
is_throwaway_tag = 0;
else
@@ -1284,14 +1298,16 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
* Check if we are _not_ on a detached HEAD, i.e. if there is a
* current branch.
*/
- branch = branch_to_free = resolve_refdup("HEAD", 0, &head_oid, NULL);
+ branch = branch_to_free = refs_resolve_refdup(get_main_ref_store(the_repository),
+ "HEAD", 0, &head_oid,
+ NULL);
if (branch)
skip_prefix(branch, "refs/heads/", &branch);
if (!pull_twohead) {
char *default_strategy = getenv("GIT_TEST_MERGE_ALGORITHM");
if (default_strategy && !strcmp(default_strategy, "ort"))
- pull_twohead = "ort";
+ pull_twohead = xstrdup("ort");
}
init_diff_ui_defaults();
@@ -1325,8 +1341,10 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
if (!file_exists(git_path_merge_head(the_repository)))
die(_("There is no merge to abort (MERGE_HEAD missing)."));
- if (!read_ref("MERGE_AUTOSTASH", &stash_oid))
- delete_ref("", "MERGE_AUTOSTASH", &stash_oid, REF_NO_DEREF);
+ if (!refs_read_ref(get_main_ref_store(the_repository), "MERGE_AUTOSTASH", &stash_oid))
+ refs_delete_ref(get_main_ref_store(the_repository),
+ "", "MERGE_AUTOSTASH", &stash_oid,
+ REF_NO_DEREF);
/* Invoke 'git reset --merge' */
ret = cmd_reset(nargc, nargv, prefix);
@@ -1379,14 +1397,14 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
else
die(_("You have not concluded your merge (MERGE_HEAD exists)."));
}
- if (ref_exists("CHERRY_PICK_HEAD")) {
+ if (refs_ref_exists(get_main_ref_store(the_repository), "CHERRY_PICK_HEAD")) {
if (advice_enabled(ADVICE_RESOLVE_CONFLICT))
die(_("You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists).\n"
"Please, commit your changes before you merge."));
else
die(_("You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists)."));
}
- resolve_undo_clear_index(&the_index);
+ resolve_undo_clear_index(the_repository->index);
if (option_edit < 0)
option_edit = default_edit_option();
@@ -1450,8 +1468,10 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
remote_head_oid = &remoteheads->item->object.oid;
read_empty(remote_head_oid);
- update_ref("initial pull", "HEAD", remote_head_oid, NULL, 0,
- UPDATE_REFS_DIE_ON_ERR);
+ refs_update_ref(get_main_ref_store(the_repository),
+ "initial pull", "HEAD", remote_head_oid, NULL,
+ 0,
+ UPDATE_REFS_DIE_ON_ERR);
goto done;
}
@@ -1531,8 +1551,10 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
free(list);
}
- update_ref("updating ORIG_HEAD", "ORIG_HEAD",
- &head_commit->object.oid, NULL, 0, UPDATE_REFS_DIE_ON_ERR);
+ refs_update_ref(get_main_ref_store(the_repository),
+ "updating ORIG_HEAD", "ORIG_HEAD",
+ &head_commit->object.oid, NULL, 0,
+ UPDATE_REFS_DIE_ON_ERR);
if (remoteheads && !common) {
/* No common ancestors found. */
@@ -1595,7 +1617,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
* We are not doing octopus, not fast-forward, and have
* only one common.
*/
- refresh_index(&the_index, REFRESH_QUIET, NULL, NULL, NULL);
+ refresh_index(the_repository->index, REFRESH_QUIET, NULL, NULL, NULL);
if (allow_trivial && fast_forward != FF_ONLY) {
/*
* Must first ensure that index matches HEAD before
@@ -1681,7 +1703,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
* index and working tree polluted.
*/
if (save_state(&stash))
- oidclr(&stash);
+ oidclr(&stash, the_repository->hash_algo);
for (i = 0; i < use_strategies_nr; i++) {
int ret, cnt;
@@ -1784,6 +1806,8 @@ done:
}
strbuf_release(&buf);
free(branch_to_free);
- discard_index(&the_index);
+ free(pull_twohead);
+ free(pull_octopus);
+ discard_index(the_repository->index);
return ret;
}
diff --git a/builtin/multi-pack-index.c b/builtin/multi-pack-index.c
index a72aebecaa..9cf1a32d65 100644
--- a/builtin/multi-pack-index.c
+++ b/builtin/multi-pack-index.c
@@ -8,6 +8,7 @@
#include "strbuf.h"
#include "trace2.h"
#include "object-store-ll.h"
+#include "replace-object.h"
#define BUILTIN_MIDX_WRITE_USAGE \
N_("git multi-pack-index [<options>] write [--preferred-pack=<pack>]" \
@@ -49,7 +50,7 @@ static char const * const builtin_multi_pack_index_usage[] = {
static struct opts_multi_pack_index {
char *object_dir;
const char *preferred_pack;
- const char *refs_snapshot;
+ char *refs_snapshot;
unsigned long batch_size;
unsigned flags;
int stdin_packs;
@@ -134,6 +135,7 @@ static int cmd_multi_pack_index_write(int argc, const char **argv,
N_("refs snapshot for selecting bitmap commits")),
OPT_END(),
};
+ int ret;
opts.flags |= MIDX_WRITE_BITMAP_HASH_CACHE;
@@ -156,7 +158,6 @@ static int cmd_multi_pack_index_write(int argc, const char **argv,
if (opts.stdin_packs) {
struct string_list packs = STRING_LIST_INIT_DUP;
- int ret;
read_packs_from_stdin(&packs);
@@ -165,12 +166,17 @@ static int cmd_multi_pack_index_write(int argc, const char **argv,
opts.refs_snapshot, opts.flags);
string_list_clear(&packs, 0);
+ free(opts.refs_snapshot);
return ret;
}
- return write_midx_file(opts.object_dir, opts.preferred_pack,
- opts.refs_snapshot, opts.flags);
+
+ ret = write_midx_file(opts.object_dir, opts.preferred_pack,
+ opts.refs_snapshot, opts.flags);
+
+ free(opts.refs_snapshot);
+ return ret;
}
static int cmd_multi_pack_index_verify(int argc, const char **argv,
@@ -273,6 +279,8 @@ int cmd_multi_pack_index(int argc, const char **argv,
};
struct option *options = parse_options_concat(builtin_multi_pack_index_options, common_opts);
+ disable_replace_refs();
+
git_config(git_default_config, NULL);
if (the_repository &&
diff --git a/builtin/mv.c b/builtin/mv.c
index 22e64fc290..6c69033c5f 100644
--- a/builtin/mv.c
+++ b/builtin/mv.c
@@ -3,7 +3,7 @@
*
* Copyright (C) 2006 Johannes Schindelin
*/
-#define USE_THE_INDEX_VARIABLE
+
#include "builtin.h"
#include "abspath.h"
#include "advice.h"
@@ -20,6 +20,7 @@
#include "read-cache-ll.h"
#include "repository.h"
#include "setup.h"
+#include "strvec.h"
#include "submodule.h"
#include "entry.h"
@@ -38,45 +39,35 @@ enum update_mode {
#define DUP_BASENAME 1
#define KEEP_TRAILING_SLASH 2
-static const char **internal_prefix_pathspec(const char *prefix,
- const char **pathspec,
- int count, unsigned flags)
+static void internal_prefix_pathspec(struct strvec *out,
+ const char *prefix,
+ const char **pathspec,
+ int count, unsigned flags)
{
- int i;
- const char **result;
int prefixlen = prefix ? strlen(prefix) : 0;
- ALLOC_ARRAY(result, count + 1);
/* Create an intermediate copy of the pathspec based on the flags */
- for (i = 0; i < count; i++) {
- int length = strlen(pathspec[i]);
- int to_copy = length;
- char *it;
+ for (int i = 0; i < count; i++) {
+ size_t length = strlen(pathspec[i]);
+ size_t to_copy = length;
+ const char *maybe_basename;
+ char *trimmed, *prefixed_path;
+
while (!(flags & KEEP_TRAILING_SLASH) &&
to_copy > 0 && is_dir_sep(pathspec[i][to_copy - 1]))
to_copy--;
- it = xmemdupz(pathspec[i], to_copy);
- if (flags & DUP_BASENAME) {
- result[i] = xstrdup(basename(it));
- free(it);
- } else {
- result[i] = it;
- }
- }
- result[count] = NULL;
+ trimmed = xmemdupz(pathspec[i], to_copy);
+ maybe_basename = (flags & DUP_BASENAME) ? basename(trimmed) : trimmed;
+ prefixed_path = prefix_path(prefix, prefixlen, maybe_basename);
+ strvec_push(out, prefixed_path);
- /* Prefix the pathspec and free the old intermediate strings */
- for (i = 0; i < count; i++) {
- const char *match = prefix_path(prefix, prefixlen, result[i]);
- free((char *) result[i]);
- result[i] = match;
+ free(prefixed_path);
+ free(trimmed);
}
-
- return result;
}
-static const char *add_slash(const char *path)
+static char *add_slash(const char *path)
{
size_t len = strlen(path);
if (len && path[len - 1] != '/') {
@@ -86,46 +77,48 @@ static const char *add_slash(const char *path)
with_slash[len] = 0;
return with_slash;
}
- return path;
+ return xstrdup(path);
}
#define SUBMODULE_WITH_GITDIR ((const char *)1)
-static void prepare_move_submodule(const char *src, int first,
- const char **submodule_gitfile)
+static const char *submodule_gitfile_path(const char *src, int first)
{
struct strbuf submodule_dotgit = STRBUF_INIT;
- if (!S_ISGITLINK(the_index.cache[first]->ce_mode))
+ const char *path;
+
+ if (!S_ISGITLINK(the_repository->index->cache[first]->ce_mode))
die(_("Directory %s is in index and no submodule?"), src);
- if (!is_staging_gitmodules_ok(&the_index))
+ if (!is_staging_gitmodules_ok(the_repository->index))
die(_("Please stage your changes to .gitmodules or stash them to proceed"));
+
strbuf_addf(&submodule_dotgit, "%s/.git", src);
- *submodule_gitfile = read_gitfile(submodule_dotgit.buf);
- if (*submodule_gitfile)
- *submodule_gitfile = xstrdup(*submodule_gitfile);
- else
- *submodule_gitfile = SUBMODULE_WITH_GITDIR;
+
+ path = read_gitfile(submodule_dotgit.buf);
strbuf_release(&submodule_dotgit);
+ if (path)
+ return path;
+ return SUBMODULE_WITH_GITDIR;
}
static int index_range_of_same_dir(const char *src, int length,
int *first_p, int *last_p)
{
- const char *src_w_slash = add_slash(src);
+ char *src_w_slash = add_slash(src);
int first, last, len_w_slash = length + 1;
- first = index_name_pos(&the_index, src_w_slash, len_w_slash);
+ first = index_name_pos(the_repository->index, src_w_slash, len_w_slash);
if (first >= 0)
die(_("%.*s is in index"), len_w_slash, src_w_slash);
first = -1 - first;
- for (last = first; last < the_index.cache_nr; last++) {
- const char *path = the_index.cache[last]->name;
+ for (last = first; last < the_repository->index->cache_nr; last++) {
+ const char *path = the_repository->index->cache[last]->name;
if (strncmp(path, src_w_slash, len_w_slash))
break;
}
- if (src_w_slash != src)
- free((char *)src_w_slash);
+
+ free(src_w_slash);
*first_p = first;
*last_p = last;
return last - first;
@@ -141,17 +134,17 @@ static int index_range_of_same_dir(const char *src, int length,
static int empty_dir_has_sparse_contents(const char *name)
{
int ret = 0;
- const char *with_slash = add_slash(name);
+ char *with_slash = add_slash(name);
int length = strlen(with_slash);
- int pos = index_name_pos(&the_index, with_slash, length);
+ int pos = index_name_pos(the_repository->index, with_slash, length);
const struct cache_entry *ce;
if (pos < 0) {
pos = -pos - 1;
- if (pos >= the_index.cache_nr)
+ if (pos >= the_repository->index->cache_nr)
goto free_return;
- ce = the_index.cache[pos];
+ ce = the_repository->index->cache[pos];
if (strncmp(with_slash, ce->name, length))
goto free_return;
if (ce_skip_worktree(ce))
@@ -159,11 +152,32 @@ static int empty_dir_has_sparse_contents(const char *name)
}
free_return:
- if (with_slash != name)
- free((char *)with_slash);
+ free(with_slash);
return ret;
}
+static void remove_empty_src_dirs(const char **src_dir, size_t src_dir_nr)
+{
+ size_t i;
+ struct strbuf a_src_dir = STRBUF_INIT;
+
+ for (i = 0; i < src_dir_nr; i++) {
+ int dummy;
+ strbuf_addstr(&a_src_dir, src_dir[i]);
+ /*
+ * if entries under a_src_dir are all moved away,
+ * recursively remove a_src_dir to cleanup
+ */
+ if (index_range_of_same_dir(a_src_dir.buf, a_src_dir.len,
+ &dummy, &dummy) < 1) {
+ remove_dir_recursively(&a_src_dir, 0);
+ }
+ strbuf_reset(&a_src_dir);
+ }
+
+ strbuf_release(&a_src_dir);
+}
+
int cmd_mv(int argc, const char **argv, const char *prefix)
{
int i, flags, gitmodules_modified = 0;
@@ -177,18 +191,21 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
OPT_BOOL(0, "sparse", &ignore_sparse, N_("allow updating entries outside of the sparse-checkout cone")),
OPT_END(),
};
- const char **source, **destination, **dest_path, **submodule_gitfile;
- const char *dst_w_slash;
- const char **src_dir = NULL;
- int src_dir_nr = 0, src_dir_alloc = 0;
- struct strbuf a_src_dir = STRBUF_INIT;
+ struct strvec sources = STRVEC_INIT;
+ struct strvec dest_paths = STRVEC_INIT;
+ struct strvec destinations = STRVEC_INIT;
+ struct strvec submodule_gitfiles_to_free = STRVEC_INIT;
+ const char **submodule_gitfiles;
+ char *dst_w_slash = NULL;
+ struct strvec src_dir = STRVEC_INIT;
enum update_mode *modes, dst_mode = 0;
struct stat st, dest_st;
- struct string_list src_for_dst = STRING_LIST_INIT_NODUP;
+ struct string_list src_for_dst = STRING_LIST_INIT_DUP;
struct lock_file lock_file = LOCK_INIT;
struct cache_entry *ce;
- struct string_list only_match_skip_worktree = STRING_LIST_INIT_NODUP;
- struct string_list dirty_paths = STRING_LIST_INIT_NODUP;
+ struct string_list only_match_skip_worktree = STRING_LIST_INIT_DUP;
+ struct string_list dirty_paths = STRING_LIST_INIT_DUP;
+ int ret;
git_config(git_default_config, NULL);
@@ -201,7 +218,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
if (repo_read_index(the_repository) < 0)
die(_("index file corrupt"));
- source = internal_prefix_pathspec(prefix, argv, argc, 0);
+ internal_prefix_pathspec(&sources, prefix, argv, argc, 0);
CALLOC_ARRAY(modes, argc);
/*
@@ -212,45 +229,39 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
flags = KEEP_TRAILING_SLASH;
if (argc == 1 && is_directory(argv[0]) && !is_directory(argv[1]))
flags = 0;
- dest_path = internal_prefix_pathspec(prefix, argv + argc, 1, flags);
- dst_w_slash = add_slash(dest_path[0]);
- submodule_gitfile = xcalloc(argc, sizeof(char *));
+ internal_prefix_pathspec(&dest_paths, prefix, argv + argc, 1, flags);
+ dst_w_slash = add_slash(dest_paths.v[0]);
+ submodule_gitfiles = xcalloc(argc, sizeof(char *));
- if (dest_path[0][0] == '\0')
+ if (dest_paths.v[0][0] == '\0')
/* special case: "." was normalized to "" */
- destination = internal_prefix_pathspec(dest_path[0], argv, argc, DUP_BASENAME);
- else if (!lstat(dest_path[0], &st) &&
- S_ISDIR(st.st_mode)) {
- destination = internal_prefix_pathspec(dst_w_slash, argv, argc, DUP_BASENAME);
+ internal_prefix_pathspec(&destinations, dest_paths.v[0], argv, argc, DUP_BASENAME);
+ else if (!lstat(dest_paths.v[0], &st) && S_ISDIR(st.st_mode)) {
+ internal_prefix_pathspec(&destinations, dst_w_slash, argv, argc, DUP_BASENAME);
+ } else if (!path_in_sparse_checkout(dst_w_slash, the_repository->index) &&
+ empty_dir_has_sparse_contents(dst_w_slash)) {
+ internal_prefix_pathspec(&destinations, dst_w_slash, argv, argc, DUP_BASENAME);
+ dst_mode = SKIP_WORKTREE_DIR;
+ } else if (argc != 1) {
+ die(_("destination '%s' is not a directory"), dest_paths.v[0]);
} else {
- if (!path_in_sparse_checkout(dst_w_slash, &the_index) &&
- empty_dir_has_sparse_contents(dst_w_slash)) {
- destination = internal_prefix_pathspec(dst_w_slash, argv, argc, DUP_BASENAME);
- dst_mode = SKIP_WORKTREE_DIR;
- } else if (argc != 1) {
- die(_("destination '%s' is not a directory"), dest_path[0]);
- } else {
- destination = dest_path;
- /*
- * <destination> is a file outside of sparse-checkout
- * cone. Insist on cone mode here for backward
- * compatibility. We don't want dst_mode to be assigned
- * for a file when the repo is using no-cone mode (which
- * is deprecated at this point) sparse-checkout. As
- * SPARSE here is only considering cone-mode situation.
- */
- if (!path_in_cone_mode_sparse_checkout(destination[0], &the_index))
- dst_mode = SPARSE;
- }
- }
- if (dst_w_slash != dest_path[0]) {
- free((char *)dst_w_slash);
- dst_w_slash = NULL;
+ strvec_pushv(&destinations, dest_paths.v);
+
+ /*
+ * <destination> is a file outside of sparse-checkout
+ * cone. Insist on cone mode here for backward
+ * compatibility. We don't want dst_mode to be assigned
+ * for a file when the repo is using no-cone mode (which
+ * is deprecated at this point) sparse-checkout. As
+ * SPARSE here is only considering cone-mode situation.
+ */
+ if (!path_in_cone_mode_sparse_checkout(destinations.v[0], the_repository->index))
+ dst_mode = SPARSE;
}
/* Checking */
for (i = 0; i < argc; i++) {
- const char *src = source[i], *dst = destination[i];
+ const char *src = sources.v[i], *dst = destinations.v[i];
int length;
const char *bad = NULL;
int skip_sparse = 0;
@@ -263,20 +274,22 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
int pos;
const struct cache_entry *ce;
- pos = index_name_pos(&the_index, src, length);
+ pos = index_name_pos(the_repository->index, src, length);
if (pos < 0) {
- const char *src_w_slash = add_slash(src);
- if (!path_in_sparse_checkout(src_w_slash, &the_index) &&
+ char *src_w_slash = add_slash(src);
+ if (!path_in_sparse_checkout(src_w_slash, the_repository->index) &&
empty_dir_has_sparse_contents(src)) {
+ free(src_w_slash);
modes[i] |= SKIP_WORKTREE_DIR;
goto dir_check;
}
+ free(src_w_slash);
/* only error if existence is expected. */
if (!(modes[i] & SPARSE))
bad = _("bad source");
goto act_on_entry;
}
- ce = the_index.cache[pos];
+ ce = the_repository->index->cache[pos];
if (!ce_skip_worktree(ce)) {
bad = _("bad source");
goto act_on_entry;
@@ -286,7 +299,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
goto act_on_entry;
}
/* Check if dst exists in index */
- if (index_name_pos(&the_index, dst, strlen(dst)) < 0) {
+ if (index_name_pos(the_repository->index, dst, strlen(dst)) < 0) {
modes[i] |= SPARSE;
goto act_on_entry;
}
@@ -310,12 +323,16 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
dir_check:
if (S_ISDIR(st.st_mode)) {
- int j, dst_len, n;
- int first = index_name_pos(&the_index, src, length), last;
+ char *dst_with_slash;
+ size_t dst_with_slash_len;
+ int j, n;
+ int first = index_name_pos(the_repository->index, src, length), last;
if (first >= 0) {
- prepare_move_submodule(src, first,
- submodule_gitfile + i);
+ const char *path = submodule_gitfile_path(src, first);
+ if (path != SUBMODULE_WITH_GITDIR)
+ path = strvec_push(&submodule_gitfiles_to_free, path);
+ submodule_gitfiles[i] = path;
goto act_on_entry;
} else if (index_range_of_same_dir(src, length,
&first, &last) < 1) {
@@ -326,32 +343,35 @@ dir_check:
/* last - first >= 1 */
modes[i] |= WORKING_DIRECTORY;
- ALLOC_GROW(src_dir, src_dir_nr + 1, src_dir_alloc);
- src_dir[src_dir_nr++] = src;
+ strvec_push(&src_dir, src);
n = argc + last - first;
- REALLOC_ARRAY(source, n);
- REALLOC_ARRAY(destination, n);
REALLOC_ARRAY(modes, n);
- REALLOC_ARRAY(submodule_gitfile, n);
+ REALLOC_ARRAY(submodule_gitfiles, n);
- dst = add_slash(dst);
- dst_len = strlen(dst);
+ dst_with_slash = add_slash(dst);
+ dst_with_slash_len = strlen(dst_with_slash);
for (j = 0; j < last - first; j++) {
- const struct cache_entry *ce = the_index.cache[first + j];
+ const struct cache_entry *ce = the_repository->index->cache[first + j];
const char *path = ce->name;
- source[argc + j] = path;
- destination[argc + j] =
- prefix_path(dst, dst_len, path + length + 1);
+ char *prefixed_path = prefix_path(dst_with_slash, dst_with_slash_len, path + length + 1);
+
+ strvec_push(&sources, path);
+ strvec_push(&destinations, prefixed_path);
+
memset(modes + argc + j, 0, sizeof(enum update_mode));
modes[argc + j] |= ce_skip_worktree(ce) ? SPARSE : INDEX;
- submodule_gitfile[argc + j] = NULL;
+ submodule_gitfiles[argc + j] = NULL;
+
+ free(prefixed_path);
}
+
+ free(dst_with_slash);
argc += last - first;
goto act_on_entry;
}
- if (!(ce = index_file_exists(&the_index, src, length, 0))) {
+ if (!(ce = index_file_exists(the_repository->index, src, length, 0))) {
bad = _("not under version control");
goto act_on_entry;
}
@@ -387,7 +407,7 @@ dir_check:
if (ignore_sparse &&
(dst_mode & (SKIP_WORKTREE_DIR | SPARSE)) &&
- index_entry_exists(&the_index, dst, strlen(dst))) {
+ index_entry_exists(the_repository->index, dst, strlen(dst))) {
bad = _("destination exists in the index");
if (force) {
if (verbose)
@@ -404,12 +424,12 @@ dir_check:
* option as a way to have a successful run.
*/
if (!ignore_sparse &&
- !path_in_sparse_checkout(src, &the_index)) {
+ !path_in_sparse_checkout(src, the_repository->index)) {
string_list_append(&only_match_skip_worktree, src);
skip_sparse = 1;
}
if (!ignore_sparse &&
- !path_in_sparse_checkout(dst, &the_index)) {
+ !path_in_sparse_checkout(dst, the_repository->index)) {
string_list_append(&only_match_skip_worktree, dst);
skip_sparse = 1;
}
@@ -428,28 +448,30 @@ act_on_entry:
remove_entry:
if (--argc > 0) {
int n = argc - i;
- MOVE_ARRAY(source + i, source + i + 1, n);
- MOVE_ARRAY(destination + i, destination + i + 1, n);
+ strvec_remove(&sources, i);
+ strvec_remove(&destinations, i);
MOVE_ARRAY(modes + i, modes + i + 1, n);
- MOVE_ARRAY(submodule_gitfile + i,
- submodule_gitfile + i + 1, n);
+ MOVE_ARRAY(submodule_gitfiles + i,
+ submodule_gitfiles + i + 1, n);
i--;
}
}
if (only_match_skip_worktree.nr) {
advise_on_updating_sparse_paths(&only_match_skip_worktree);
- if (!ignore_errors)
- return 1;
+ if (!ignore_errors) {
+ ret = 1;
+ goto out;
+ }
}
for (i = 0; i < argc; i++) {
- const char *src = source[i], *dst = destination[i];
+ const char *src = sources.v[i], *dst = destinations.v[i];
enum update_mode mode = modes[i];
int pos;
int sparse_and_dirty = 0;
struct checkout state = CHECKOUT_INIT;
- state.istate = &the_index;
+ state.istate = the_repository->index;
if (force)
state.force = 1;
@@ -464,26 +486,26 @@ remove_entry:
continue;
die_errno(_("renaming '%s' failed"), src);
}
- if (submodule_gitfile[i]) {
+ if (submodule_gitfiles[i]) {
if (!update_path_in_gitmodules(src, dst))
gitmodules_modified = 1;
- if (submodule_gitfile[i] != SUBMODULE_WITH_GITDIR)
+ if (submodule_gitfiles[i] != SUBMODULE_WITH_GITDIR)
connect_work_tree_and_git_dir(dst,
- submodule_gitfile[i],
+ submodule_gitfiles[i],
1);
}
if (mode & (WORKING_DIRECTORY | SKIP_WORKTREE_DIR))
continue;
- pos = index_name_pos(&the_index, src, strlen(src));
+ pos = index_name_pos(the_repository->index, src, strlen(src));
assert(pos >= 0);
if (!(mode & SPARSE) && !lstat(src, &st))
- sparse_and_dirty = ie_modified(&the_index,
- the_index.cache[pos],
+ sparse_and_dirty = ie_modified(the_repository->index,
+ the_repository->index->cache[pos],
&st,
0);
- rename_index_entry_at(&the_index, pos, dst);
+ rename_index_entry_at(the_repository->index, pos, dst);
if (ignore_sparse &&
core_apply_sparse_checkout &&
@@ -495,11 +517,11 @@ remove_entry:
* should be added in a future patch.
*/
if ((mode & SPARSE) &&
- path_in_sparse_checkout(dst, &the_index)) {
+ path_in_sparse_checkout(dst, the_repository->index)) {
/* from out-of-cone to in-cone */
- int dst_pos = index_name_pos(&the_index, dst,
+ int dst_pos = index_name_pos(the_repository->index, dst,
strlen(dst));
- struct cache_entry *dst_ce = the_index.cache[dst_pos];
+ struct cache_entry *dst_ce = the_repository->index->cache[dst_pos];
dst_ce->ce_flags &= ~CE_SKIP_WORKTREE;
@@ -507,11 +529,11 @@ remove_entry:
die(_("cannot checkout %s"), dst_ce->name);
} else if ((dst_mode & (SKIP_WORKTREE_DIR | SPARSE)) &&
!(mode & SPARSE) &&
- !path_in_sparse_checkout(dst, &the_index)) {
+ !path_in_sparse_checkout(dst, the_repository->index)) {
/* from in-cone to out-of-cone */
- int dst_pos = index_name_pos(&the_index, dst,
+ int dst_pos = index_name_pos(the_repository->index, dst,
strlen(dst));
- struct cache_entry *dst_ce = the_index.cache[dst_pos];
+ struct cache_entry *dst_ce = the_repository->index->cache[dst_pos];
/*
* if src is clean, it will suffice to remove it
@@ -535,41 +557,31 @@ remove_entry:
}
}
- /*
- * cleanup the empty src_dirs
- */
- for (i = 0; i < src_dir_nr; i++) {
- int dummy;
- strbuf_addstr(&a_src_dir, src_dir[i]);
- /*
- * if entries under a_src_dir are all moved away,
- * recursively remove a_src_dir to cleanup
- */
- if (index_range_of_same_dir(a_src_dir.buf, a_src_dir.len,
- &dummy, &dummy) < 1) {
- remove_dir_recursively(&a_src_dir, 0);
- }
- strbuf_reset(&a_src_dir);
- }
-
- strbuf_release(&a_src_dir);
- free(src_dir);
+ remove_empty_src_dirs(src_dir.v, src_dir.nr);
if (dirty_paths.nr)
advise_on_moving_dirty_path(&dirty_paths);
if (gitmodules_modified)
- stage_updated_gitmodules(&the_index);
+ stage_updated_gitmodules(the_repository->index);
- if (write_locked_index(&the_index, &lock_file,
+ if (write_locked_index(the_repository->index, &lock_file,
COMMIT_LOCK | SKIP_IF_UNCHANGED))
die(_("Unable to write new index file"));
+ ret = 0;
+
+out:
+ strvec_clear(&src_dir);
+ free(dst_w_slash);
string_list_clear(&src_for_dst, 0);
string_list_clear(&dirty_paths, 0);
- UNLEAK(source);
- UNLEAK(dest_path);
- free(submodule_gitfile);
+ string_list_clear(&only_match_skip_worktree, 0);
+ strvec_clear(&sources);
+ strvec_clear(&dest_paths);
+ strvec_clear(&destinations);
+ strvec_clear(&submodule_gitfiles_to_free);
+ free(submodule_gitfiles);
free(modes);
- return 0;
+ return ret;
}
diff --git a/builtin/name-rev.c b/builtin/name-rev.c
index ad9930c831..70e9ec4e47 100644
--- a/builtin/name-rev.c
+++ b/builtin/name-rev.c
@@ -296,7 +296,8 @@ static void add_to_tip_table(const struct object_id *oid, const char *refname,
char *short_refname = NULL;
if (shorten_unambiguous)
- short_refname = shorten_unambiguous_ref(refname, 0);
+ short_refname = refs_shorten_unambiguous_ref(get_main_ref_store(the_repository),
+ refname, 0);
else if (skip_prefix(refname, "refs/heads/", &refname))
; /* refname already advanced */
else
@@ -647,7 +648,7 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix)
adjust_cutoff_timestamp_for_slop();
- for_each_ref(name_ref, &data);
+ refs_for_each_ref(get_main_ref_store(the_repository), name_ref, &data);
name_tips(&string_pool);
if (annotate_stdin) {
diff --git a/builtin/notes.c b/builtin/notes.c
index cb011303e6..d9c356e354 100644
--- a/builtin/notes.c
+++ b/builtin/notes.c
@@ -794,9 +794,9 @@ static int merge_abort(struct notes_merge_options *o)
* notes_merge_abort() to remove .git/NOTES_MERGE_WORKTREE.
*/
- if (delete_ref(NULL, "NOTES_MERGE_PARTIAL", NULL, 0))
+ if (refs_delete_ref(get_main_ref_store(the_repository), NULL, "NOTES_MERGE_PARTIAL", NULL, 0))
ret += error(_("failed to delete ref NOTES_MERGE_PARTIAL"));
- if (delete_ref(NULL, "NOTES_MERGE_REF", NULL, REF_NO_DEREF))
+ if (refs_delete_ref(get_main_ref_store(the_repository), NULL, "NOTES_MERGE_REF", NULL, REF_NO_DEREF))
ret += error(_("failed to delete ref NOTES_MERGE_REF"));
if (notes_merge_abort(o))
ret += error(_("failed to remove 'git notes merge' worktree"));
@@ -828,13 +828,14 @@ static int merge_commit(struct notes_merge_options *o)
if (partial->parents)
oidcpy(&parent_oid, &partial->parents->item->object.oid);
else
- oidclr(&parent_oid);
+ oidclr(&parent_oid, the_repository->hash_algo);
CALLOC_ARRAY(t, 1);
init_notes(t, "NOTES_MERGE_PARTIAL", combine_notes_overwrite, 0);
o->local_ref = local_ref_to_free =
- resolve_refdup("NOTES_MERGE_REF", 0, &oid, NULL);
+ refs_resolve_refdup(get_main_ref_store(the_repository),
+ "NOTES_MERGE_REF", 0, &oid, NULL);
if (!o->local_ref)
die(_("failed to resolve NOTES_MERGE_REF"));
@@ -847,9 +848,10 @@ static int merge_commit(struct notes_merge_options *o)
&pretty_ctx);
strbuf_trim(&msg);
strbuf_insertstr(&msg, 0, "notes: ");
- update_ref(msg.buf, o->local_ref, &oid,
- is_null_oid(&parent_oid) ? NULL : &parent_oid,
- 0, UPDATE_REFS_DIE_ON_ERR);
+ refs_update_ref(get_main_ref_store(the_repository), msg.buf,
+ o->local_ref, &oid,
+ is_null_oid(&parent_oid) ? NULL : &parent_oid,
+ 0, UPDATE_REFS_DIE_ON_ERR);
free_notes(t);
strbuf_release(&msg);
@@ -961,14 +963,16 @@ static int merge(int argc, const char **argv, const char *prefix)
if (result >= 0) /* Merge resulted (trivially) in result_oid */
/* Update default notes ref with new commit */
- update_ref(msg.buf, default_notes_ref(), &result_oid, NULL, 0,
- UPDATE_REFS_DIE_ON_ERR);
+ refs_update_ref(get_main_ref_store(the_repository), msg.buf,
+ default_notes_ref(), &result_oid, NULL, 0,
+ UPDATE_REFS_DIE_ON_ERR);
else { /* Merge has unresolved conflicts */
struct worktree **worktrees;
const struct worktree *wt;
/* Update .git/NOTES_MERGE_PARTIAL with partial merge result */
- update_ref(msg.buf, "NOTES_MERGE_PARTIAL", &result_oid, NULL,
- 0, UPDATE_REFS_DIE_ON_ERR);
+ refs_update_ref(get_main_ref_store(the_repository), msg.buf,
+ "NOTES_MERGE_PARTIAL", &result_oid, NULL,
+ 0, UPDATE_REFS_DIE_ON_ERR);
/* Store ref-to-be-updated into .git/NOTES_MERGE_REF */
worktrees = get_worktrees();
wt = find_shared_symref(worktrees, "NOTES_MERGE_REF",
@@ -977,7 +981,7 @@ static int merge(int argc, const char **argv, const char *prefix)
die(_("a notes merge into %s is already in-progress at %s"),
default_notes_ref(), wt->path);
free_worktrees(worktrees);
- if (create_symref("NOTES_MERGE_REF", default_notes_ref(), NULL))
+ if (refs_update_symref(get_main_ref_store(the_repository), "NOTES_MERGE_REF", default_notes_ref(), NULL))
die(_("failed to store link to current notes ref (%s)"),
default_notes_ref());
fprintf(stderr, _("Automatic notes merge failed. Fix conflicts in %s "
diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c
index baf0090fc8..f395488971 100644
--- a/builtin/pack-objects.c
+++ b/builtin/pack-objects.c
@@ -779,7 +779,7 @@ static int mark_tagged(const char *path UNUSED, const struct object_id *oid,
if (entry)
entry->tagged = 1;
- if (!peel_iterated_oid(oid, &peeled)) {
+ if (!peel_iterated_oid(the_repository, oid, &peeled)) {
entry = packlist_find(&to_pack, &peeled);
if (entry)
entry->tagged = 1;
@@ -939,7 +939,8 @@ static struct object_entry **compute_write_order(void)
/*
* Mark objects that are at the tip of tags.
*/
- for_each_tag_ref(mark_tagged, NULL);
+ refs_for_each_tag_ref(get_main_ref_store(the_repository), mark_tagged,
+ NULL);
if (use_delta_islands) {
max_layers = compute_pack_layers(&to_pack);
@@ -1314,6 +1315,7 @@ static void write_pack_file(void)
if (!pack_to_stdout) {
struct stat st;
struct strbuf tmpname = STRBUF_INIT;
+ struct bitmap_writer bitmap_writer;
char *idx_tmp_name = NULL;
/*
@@ -1339,8 +1341,10 @@ static void write_pack_file(void)
hash_to_hex(hash));
if (write_bitmap_index) {
- bitmap_writer_set_checksum(hash);
- bitmap_writer_build_type_index(
+ bitmap_writer_init(&bitmap_writer,
+ the_repository);
+ bitmap_writer_set_checksum(&bitmap_writer, hash);
+ bitmap_writer_build_type_index(&bitmap_writer,
&to_pack, written_list, nr_written);
}
@@ -1358,12 +1362,17 @@ static void write_pack_file(void)
strbuf_addstr(&tmpname, "bitmap");
stop_progress(&progress_state);
- bitmap_writer_show_progress(progress);
- bitmap_writer_select_commits(indexed_commits, indexed_commits_nr, -1);
- if (bitmap_writer_build(&to_pack) < 0)
+ bitmap_writer_show_progress(&bitmap_writer,
+ progress);
+ bitmap_writer_select_commits(&bitmap_writer,
+ indexed_commits,
+ indexed_commits_nr);
+ if (bitmap_writer_build(&bitmap_writer, &to_pack) < 0)
die(_("failed to write bitmap index"));
- bitmap_writer_finish(written_list, nr_written,
+ bitmap_writer_finish(&bitmap_writer,
+ written_list, nr_written,
tmpname.buf, write_bitmap_options);
+ bitmap_writer_free(&bitmap_writer);
write_bitmap_index = 0;
strbuf_setlen(&tmpname, tmpname_len);
}
@@ -2070,7 +2079,8 @@ static void check_object(struct object_entry *entry, uint32_t object_index)
oidread(&base_ref,
use_pack(p, &w_curs,
entry->in_pack_offset + used,
- NULL));
+ NULL),
+ the_repository->hash_algo);
have_base = 1;
}
entry->in_pack_header_size = used + the_hash_algo->rawsz;
@@ -3124,7 +3134,7 @@ static int add_ref_tag(const char *tag UNUSED, const struct object_id *oid,
{
struct object_id peeled;
- if (!peel_iterated_oid(oid, &peeled) && obj_is_packed(&peeled))
+ if (!peel_iterated_oid(the_repository, oid, &peeled) && obj_is_packed(&peeled))
add_tag_chain(oid);
return 0;
}
@@ -4073,7 +4083,7 @@ static int mark_bitmap_preferred_tip(const char *refname,
struct object_id peeled;
struct object *object;
- if (!peel_iterated_oid(oid, &peeled))
+ if (!peel_iterated_oid(the_repository, oid, &peeled))
oid = &peeled;
object = parse_object_or_die(oid, refname);
@@ -4093,7 +4103,9 @@ static void mark_bitmap_preferred_tips(void)
return;
for_each_string_list_item(item, preferred_tips) {
- for_each_ref_in(item->string, mark_bitmap_preferred_tip, NULL);
+ refs_for_each_ref_in(get_main_ref_store(the_repository),
+ item->string, mark_bitmap_preferred_tip,
+ NULL);
}
}
@@ -4588,7 +4600,8 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
}
cleanup_preferred_base();
if (include_tag && nr_result)
- for_each_tag_ref(add_ref_tag, NULL);
+ refs_for_each_tag_ref(get_main_ref_store(the_repository),
+ add_ref_tag, NULL);
stop_progress(&progress_state);
trace2_region_leave("pack-objects", "enumerate-objects",
the_repository);
diff --git a/builtin/pack-redundant.c b/builtin/pack-redundant.c
index 4c735ba069..dd9bf35f5b 100644
--- a/builtin/pack-redundant.c
+++ b/builtin/pack-redundant.c
@@ -100,7 +100,7 @@ static inline struct llist_item *llist_insert(struct llist *list,
const unsigned char *oid)
{
struct llist_item *new_item = llist_item_get();
- oidread(&new_item->oid, oid);
+ oidread(&new_item->oid, oid, the_repository->hash_algo);
new_item->next = NULL;
if (after) {
@@ -155,7 +155,7 @@ redo_from_start:
l = (hint == NULL) ? list->front : hint;
prev = NULL;
while (l) {
- const int cmp = hashcmp(l->oid.hash, oid);
+ const int cmp = hashcmp(l->oid.hash, oid, the_repository->hash_algo);
if (cmp > 0) /* not in list, since sorted */
return prev;
if (!cmp) { /* found */
@@ -258,7 +258,8 @@ static void cmp_two_packs(struct pack_list *p1, struct pack_list *p2)
while (p1_off < p1->pack->num_objects * p1_step &&
p2_off < p2->pack->num_objects * p2_step)
{
- const int cmp = hashcmp(p1_base + p1_off, p2_base + p2_off);
+ const int cmp = hashcmp(p1_base + p1_off, p2_base + p2_off,
+ the_repository->hash_algo);
/* cmp ~ p1 - p2 */
if (cmp == 0) {
p1_hint = llist_sorted_remove(p1->unique_objects,
@@ -296,7 +297,8 @@ static size_t sizeof_union(struct packed_git *p1, struct packed_git *p2)
while (p1_off < p1->num_objects * p1_step &&
p2_off < p2->num_objects * p2_step)
{
- int cmp = hashcmp(p1_base + p1_off, p2_base + p2_off);
+ int cmp = hashcmp(p1_base + p1_off, p2_base + p2_off,
+ the_repository->hash_algo);
/* cmp ~ p1 - p2 */
if (cmp == 0) {
ret++;
diff --git a/builtin/patch-id.c b/builtin/patch-id.c
index 3894d2b970..d790ae6354 100644
--- a/builtin/patch-id.c
+++ b/builtin/patch-id.c
@@ -5,6 +5,7 @@
#include "hash.h"
#include "hex.h"
#include "parse-options.h"
+#include "setup.h"
static void flush_current_id(int patchlen, struct object_id *id, struct object_id *result)
{
@@ -69,7 +70,7 @@ static int get_one_patchid(struct object_id *next_oid, struct object_id *result,
git_hash_ctx ctx;
the_hash_algo->init_fn(&ctx);
- oidclr(result);
+ oidclr(result, the_repository->hash_algo);
while (strbuf_getwholeline(line_buf, stdin, '\n') != EOF) {
char *line = line_buf->buf;
@@ -165,7 +166,7 @@ static int get_one_patchid(struct object_id *next_oid, struct object_id *result,
}
if (!found_next)
- oidclr(next_oid);
+ oidclr(next_oid, the_repository->hash_algo);
flush_one_hunk(result, &ctx);
@@ -178,7 +179,7 @@ static void generate_id_list(int stable, int verbatim)
int patchlen;
struct strbuf line_buf = STRBUF_INIT;
- oidclr(&oid);
+ oidclr(&oid, the_repository->hash_algo);
while (!feof(stdin)) {
patchlen = get_one_patchid(&n, &result, &line_buf, stable, verbatim);
flush_current_id(patchlen, &oid, &result);
@@ -237,6 +238,18 @@ int cmd_patch_id(int argc, const char **argv, const char *prefix)
argc = parse_options(argc, argv, prefix, builtin_patch_id_options,
patch_id_usage, 0);
+ /*
+ * We rely on `the_hash_algo` to compute patch IDs. This is dubious as
+ * it means that the hash algorithm now depends on the object hash of
+ * the repository, even though git-patch-id(1) clearly defines that
+ * patch IDs always use SHA1.
+ *
+ * NEEDSWORK: This hack should be removed in favor of converting
+ * the code that computes patch IDs to always use SHA1.
+ */
+ if (!the_hash_algo)
+ repo_set_hash_algo(the_repository, GIT_HASH_SHA1);
+
generate_id_list(opts ? opts > 1 : config.stable,
opts ? opts == 3 : config.verbatim);
return 0;
diff --git a/builtin/pull.c b/builtin/pull.c
index 72cbb76d52..4c54d8196f 100644
--- a/builtin/pull.c
+++ b/builtin/pull.c
@@ -5,7 +5,7 @@
*
* Fetch one or more remote refs and merge it/them into the current HEAD.
*/
-#define USE_THE_INDEX_VARIABLE
+
#include "builtin.h"
#include "advice.h"
#include "config.h"
@@ -71,48 +71,48 @@ static const char * const pull_usage[] = {
/* Shared options */
static int opt_verbosity;
-static char *opt_progress;
+static const char *opt_progress;
static int recurse_submodules = RECURSE_SUBMODULES_DEFAULT;
static int recurse_submodules_cli = RECURSE_SUBMODULES_DEFAULT;
/* Options passed to git-merge or git-rebase */
static enum rebase_type opt_rebase = -1;
-static char *opt_diffstat;
-static char *opt_log;
-static char *opt_signoff;
-static char *opt_squash;
-static char *opt_commit;
-static char *opt_edit;
-static char *cleanup_arg;
-static char *opt_ff;
-static char *opt_verify_signatures;
-static char *opt_verify;
+static const char *opt_diffstat;
+static const char *opt_log;
+static const char *opt_signoff;
+static const char *opt_squash;
+static const char *opt_commit;
+static const char *opt_edit;
+static const char *cleanup_arg;
+static const char *opt_ff;
+static const char *opt_verify_signatures;
+static const char *opt_verify;
static int opt_autostash = -1;
static int config_autostash;
static int check_trust_level = 1;
static struct strvec opt_strategies = STRVEC_INIT;
static struct strvec opt_strategy_opts = STRVEC_INIT;
-static char *opt_gpg_sign;
+static const char *opt_gpg_sign;
static int opt_allow_unrelated_histories;
/* Options passed to git-fetch */
-static char *opt_all;
-static char *opt_append;
-static char *opt_upload_pack;
+static const char *opt_all;
+static const char *opt_append;
+static const char *opt_upload_pack;
static int opt_force;
-static char *opt_tags;
-static char *opt_prune;
-static char *max_children;
+static const char *opt_tags;
+static const char *opt_prune;
+static const char *max_children;
static int opt_dry_run;
-static char *opt_keep;
-static char *opt_depth;
-static char *opt_unshallow;
-static char *opt_update_shallow;
-static char *opt_refmap;
-static char *opt_ipv4;
-static char *opt_ipv6;
+static const char *opt_keep;
+static const char *opt_depth;
+static const char *opt_unshallow;
+static const char *opt_update_shallow;
+static const char *opt_refmap;
+static const char *opt_ipv4;
+static const char *opt_ipv6;
static int opt_show_forced_updates = -1;
-static char *set_upstream;
+static const char *set_upstream;
static struct strvec opt_fetch = STRVEC_INIT;
static struct option pull_options[] = {
@@ -611,7 +611,7 @@ static int pull_into_void(const struct object_id *merge_head,
merge_head, 0))
return 1;
- if (update_ref("initial pull", "HEAD", merge_head, curr_head, 0, UPDATE_REFS_DIE_ON_ERR))
+ if (refs_update_ref(get_main_ref_store(the_repository), "initial pull", "HEAD", merge_head, curr_head, 0, UPDATE_REFS_DIE_ON_ERR))
return 1;
return 0;
@@ -1038,13 +1038,13 @@ int cmd_pull(int argc, const char **argv, const char *prefix)
die_conclude_merge();
if (repo_get_oid(the_repository, "HEAD", &orig_head))
- oidclr(&orig_head);
+ oidclr(&orig_head, the_repository->hash_algo);
if (opt_rebase) {
if (opt_autostash == -1)
opt_autostash = config_autostash;
- if (is_null_oid(&orig_head) && !is_index_unborn(&the_index))
+ if (is_null_oid(&orig_head) && !is_index_unborn(the_repository->index))
die(_("Updating an unborn branch with changes added to the index."));
if (!opt_autostash)
@@ -1053,7 +1053,7 @@ int cmd_pull(int argc, const char **argv, const char *prefix)
_("Please commit or stash them."), 1, 0);
if (get_rebase_fork_point(&rebase_fork_point, repo, *refspecs))
- oidclr(&rebase_fork_point);
+ oidclr(&rebase_fork_point, the_repository->hash_algo);
}
if (run_fetch(repo, refspecs))
@@ -1063,7 +1063,7 @@ int cmd_pull(int argc, const char **argv, const char *prefix)
return 0;
if (repo_get_oid(the_repository, "HEAD", &curr_head))
- oidclr(&curr_head);
+ oidclr(&curr_head, the_repository->hash_algo);
if (!is_null_oid(&orig_head) && !is_null_oid(&curr_head) &&
!oideq(&orig_head, &curr_head)) {
diff --git a/builtin/push.c b/builtin/push.c
index 2fbb31c3ad..7a67398124 100644
--- a/builtin/push.c
+++ b/builtin/push.c
@@ -96,9 +96,8 @@ static void refspec_append_mapped(struct refspec *refspec, const char *ref,
refspec_append(refspec, ref);
}
-static void set_refspecs(const char **refs, int nr, const char *repo)
+static void set_refspecs(const char **refs, int nr, struct remote *remote)
{
- struct remote *remote = NULL;
struct ref *local_refs = NULL;
int i;
@@ -124,33 +123,16 @@ static void set_refspecs(const char **refs, int nr, const char *repo)
local_refs = get_local_heads();
/* Does "ref" uniquely name our ref? */
- if (count_refspec_match(ref, local_refs, &matched) != 1) {
+ if (count_refspec_match(ref, local_refs, &matched) != 1)
refspec_append(&rs, ref);
- } else {
- /* lazily grab remote */
- if (!remote)
- remote = remote_get(repo);
- if (!remote)
- BUG("must get a remote for repo '%s'", repo);
-
+ else
refspec_append_mapped(&rs, ref, remote, matched);
- }
} else
refspec_append(&rs, ref);
}
free_refs(local_refs);
}
-static int push_url_of_remote(struct remote *remote, const char ***url_p)
-{
- if (remote->pushurl_nr) {
- *url_p = remote->pushurl;
- return remote->pushurl_nr;
- }
- *url_p = remote->url;
- return remote->url_nr;
-}
-
static NORETURN void die_push_simple(struct branch *branch,
struct remote *remote)
{
@@ -434,8 +416,7 @@ static int do_push(int flags,
struct remote *remote)
{
int i, errs;
- const char **url;
- int url_nr;
+ struct strvec *url;
struct refspec *push_refspec = &rs;
if (push_options->nr)
@@ -448,19 +429,10 @@ static int do_push(int flags,
setup_default_push_refspecs(&flags, remote);
}
errs = 0;
- url_nr = push_url_of_remote(remote, &url);
- if (url_nr) {
- for (i = 0; i < url_nr; i++) {
- struct transport *transport =
- transport_get(remote, url[i]);
- if (flags & TRANSPORT_PUSH_OPTIONS)
- transport->push_options = push_options;
- if (push_with_options(transport, push_refspec, flags))
- errs++;
- }
- } else {
+ url = push_url_of_remote(remote);
+ for (i = 0; i < url->nr; i++) {
struct transport *transport =
- transport_get(remote, NULL);
+ transport_get(remote, url->v[i]);
if (flags & TRANSPORT_PUSH_OPTIONS)
transport->push_options = push_options;
if (push_with_options(transport, push_refspec, flags))
@@ -650,10 +622,8 @@ int cmd_push(int argc, const char **argv, const char *prefix)
if (tags)
refspec_append(&rs, "refs/tags/*");
- if (argc > 0) {
+ if (argc > 0)
repo = argv[0];
- set_refspecs(argv + 1, argc - 1, repo);
- }
remote = pushremote_get(repo);
if (!remote) {
@@ -669,6 +639,9 @@ int cmd_push(int argc, const char **argv, const char *prefix)
" git push <name>\n"));
}
+ if (argc > 0)
+ set_refspecs(argv + 1, argc - 1, remote);
+
if (remote->mirror)
flags |= (TRANSPORT_PUSH_MIRROR|TRANSPORT_PUSH_FORCE);
diff --git a/builtin/read-tree.c b/builtin/read-tree.c
index 6f89cec0fb..a8cf8504b8 100644
--- a/builtin/read-tree.c
+++ b/builtin/read-tree.c
@@ -4,7 +4,6 @@
* Copyright (C) Linus Torvalds, 2005
*/
-#define USE_THE_INDEX_VARIABLE
#include "builtin.h"
#include "config.h"
#include "gettext.h"
@@ -159,8 +158,8 @@ int cmd_read_tree(int argc, const char **argv, const char *cmd_prefix)
memset(&opts, 0, sizeof(opts));
opts.head_idx = -1;
- opts.src_index = &the_index;
- opts.dst_index = &the_index;
+ opts.src_index = the_repository->index;
+ opts.dst_index = the_repository->index;
git_config(git_read_tree_config, NULL);
@@ -197,7 +196,7 @@ int cmd_read_tree(int argc, const char **argv, const char *cmd_prefix)
die(_("You need to resolve your current index first"));
stage = opts.merge = 1;
}
- resolve_undo_clear_index(&the_index);
+ resolve_undo_clear_index(the_repository->index);
for (i = 0; i < argc; i++) {
const char *arg = argv[i];
@@ -225,7 +224,7 @@ int cmd_read_tree(int argc, const char **argv, const char *cmd_prefix)
setup_work_tree();
if (opts.skip_sparse_checkout)
- ensure_full_index(&the_index);
+ ensure_full_index(the_repository->index);
if (opts.merge) {
switch (stage - 1) {
@@ -237,7 +236,7 @@ int cmd_read_tree(int argc, const char **argv, const char *cmd_prefix)
break;
case 2:
opts.fn = twoway_merge;
- opts.initial_checkout = is_index_unborn(&the_index);
+ opts.initial_checkout = is_index_unborn(the_repository->index);
break;
case 3:
default:
@@ -258,7 +257,7 @@ int cmd_read_tree(int argc, const char **argv, const char *cmd_prefix)
if (nr_trees == 1 && !opts.prefix)
opts.skip_cache_tree_update = 1;
- cache_tree_free(&the_index.cache_tree);
+ cache_tree_free(&the_repository->index->cache_tree);
for (i = 0; i < nr_trees; i++) {
struct tree *tree = trees[i];
if (parse_tree(tree) < 0)
@@ -282,7 +281,7 @@ int cmd_read_tree(int argc, const char **argv, const char *cmd_prefix)
the_repository->index,
trees[0]);
- if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
+ if (write_locked_index(the_repository->index, &lock_file, COMMIT_LOCK))
die("unable to write new index file");
return 0;
}
diff --git a/builtin/rebase.c b/builtin/rebase.c
index 891f28468e..e3a8e74cfc 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -4,7 +4,6 @@
* Copyright (c) 2018 Pratik Karki
*/
-#define USE_THE_INDEX_VARIABLE
#include "builtin.h"
#include "abspath.h"
#include "environment.h"
@@ -84,7 +83,7 @@ static const char *action_names[] = {
struct rebase_options {
enum rebase_type type;
enum empty_type empty;
- const char *default_backend;
+ char *default_backend;
const char *state_dir;
struct commit *upstream;
const char *upstream_name;
@@ -136,7 +135,7 @@ struct rebase_options {
.type = REBASE_UNSPECIFIED, \
.empty = EMPTY_UNSPECIFIED, \
.keep_empty = 1, \
- .default_backend = "merge", \
+ .default_backend = xstrdup("merge"), \
.flags = REBASE_NO_QUIET, \
.git_am_opts = STRVEC_INIT, \
.exec = STRING_LIST_INIT_NODUP, \
@@ -152,6 +151,19 @@ struct rebase_options {
.strategy_opts = STRING_LIST_INIT_NODUP,\
}
+static void rebase_options_release(struct rebase_options *opts)
+{
+ free(opts->default_backend);
+ free(opts->reflog_action);
+ free(opts->head_name);
+ strvec_clear(&opts->git_am_opts);
+ free(opts->gpg_sign_opt);
+ string_list_clear(&opts->exec, 0);
+ free(opts->strategy);
+ string_list_clear(&opts->strategy_opts, 0);
+ strbuf_release(&opts->git_format_patch_opt);
+}
+
static struct replay_opts get_replay_opts(const struct rebase_options *opts)
{
struct replay_opts replay = REPLAY_OPTS_INIT;
@@ -194,7 +206,7 @@ static struct replay_opts get_replay_opts(const struct rebase_options *opts)
return replay;
}
-static int edit_todo_file(unsigned flags)
+static int edit_todo_file(unsigned flags, struct replay_opts *opts)
{
const char *todo_file = rebase_path_todo();
struct todo_list todo_list = TODO_LIST_INIT,
@@ -205,7 +217,8 @@ static int edit_todo_file(unsigned flags)
return error_errno(_("could not read '%s'."), todo_file);
strbuf_stripspace(&todo_list.buf, comment_line_str);
- res = edit_todo_list(the_repository, &todo_list, &new_todo, NULL, NULL, flags);
+ res = edit_todo_list(the_repository, opts, &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)))
res = error_errno(_("could not write '%s'"), todo_file);
@@ -252,7 +265,7 @@ static int init_basic_state(struct replay_opts *opts, const char *head_name,
if (!is_directory(merge_dir()) && mkdir_in_gitdir(merge_dir()))
return error_errno(_("could not create temporary %s"), merge_dir());
- delete_reflog("REBASE_HEAD");
+ refs_delete_reflog(get_main_ref_store(the_repository), "REBASE_HEAD");
interactive = fopen(path_interactive(), "w");
if (!interactive)
@@ -295,9 +308,9 @@ static int do_interactive_rebase(struct rebase_options *opts, unsigned flags)
if (ret)
error(_("could not generate todo list"));
else {
- discard_index(&the_index);
- if (todo_list_parse_insn_buffer(the_repository, todo_list.buf.buf,
- &todo_list))
+ discard_index(the_repository->index);
+ if (todo_list_parse_insn_buffer(the_repository, &replay,
+ todo_list.buf.buf, &todo_list))
BUG("unusable todo list");
ret = complete_action(the_repository, &replay, flags,
@@ -352,9 +365,13 @@ static int run_sequencer_rebase(struct rebase_options *opts)
replay_opts_release(&replay_opts);
break;
}
- case ACTION_EDIT_TODO:
- ret = edit_todo_file(flags);
+ case ACTION_EDIT_TODO: {
+ struct replay_opts replay_opts = get_replay_opts(opts);
+
+ ret = edit_todo_file(flags, &replay_opts);
+ replay_opts_release(&replay_opts);
break;
+ }
case ACTION_SHOW_CURRENT_PATCH: {
struct child_process cmd = CHILD_PROCESS_INIT;
@@ -514,8 +531,10 @@ static int finish_rebase(struct rebase_options *opts)
struct strbuf dir = STRBUF_INIT;
int ret = 0;
- delete_ref(NULL, "REBASE_HEAD", NULL, REF_NO_DEREF);
- delete_ref(NULL, "AUTO_MERGE", NULL, REF_NO_DEREF);
+ refs_delete_ref(get_main_ref_store(the_repository), NULL,
+ "REBASE_HEAD", NULL, REF_NO_DEREF);
+ refs_delete_ref(get_main_ref_store(the_repository), NULL,
+ "AUTO_MERGE", NULL, REF_NO_DEREF);
apply_autostash(state_dir_path("autostash", opts));
/*
* We ignore errors in 'git maintenance run --auto', since the
@@ -795,6 +814,7 @@ static int rebase_config(const char *var, const char *value,
}
if (!strcmp(var, "rebase.backend")) {
+ FREE_AND_NULL(opts->default_backend);
return git_config_string(&opts->default_backend, var, value);
}
@@ -1046,6 +1066,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
{
struct rebase_options options = REBASE_OPTIONS_INIT;
const char *branch_name;
+ const char *strategy_opt = NULL;
int ret, flags, total_argc, in_progress = 0;
int keep_base = 0;
int ok_to_skip_pre_rebase = 0;
@@ -1160,7 +1181,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
PARSE_OPT_OPTARG, parse_opt_rebase_merges),
OPT_BOOL(0, "fork-point", &options.fork_point,
N_("use 'merge-base --fork-point' to refine upstream")),
- OPT_STRING('s', "strategy", &options.strategy,
+ OPT_STRING('s', "strategy", &strategy_opt,
N_("strategy"), N_("use the given merge strategy")),
OPT_STRING_LIST('X', "strategy-option", &options.strategy_opts,
N_("option"),
@@ -1469,13 +1490,12 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
}
}
- if (options.strategy_opts.nr && !options.strategy)
- options.strategy = "ort";
-
- if (options.strategy) {
- options.strategy = xstrdup(options.strategy);
+ if (strategy_opt)
+ options.strategy = xstrdup(strategy_opt);
+ else if (options.strategy_opts.nr && !options.strategy)
+ options.strategy = xstrdup("ort");
+ if (options.strategy)
imply_merge(&options, "--strategy");
- }
if (options.root && !options.onto_name)
imply_merge(&options, "--root without --onto");
@@ -1623,7 +1643,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
/* Is it a local branch? */
strbuf_reset(&buf);
strbuf_addf(&buf, "refs/heads/%s", branch_name);
- if (!read_ref(buf.buf, &branch_oid)) {
+ if (!refs_read_ref(get_main_ref_store(the_repository), buf.buf, &branch_oid)) {
die_if_checked_out(buf.buf, 1);
options.head_name = xstrdup(buf.buf);
options.orig_head =
@@ -1640,8 +1660,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
} else if (argc == 0) {
/* Do not need to switch branches, we are already on it. */
options.head_name =
- xstrdup_or_null(resolve_ref_unsafe("HEAD", 0, NULL,
- &flags));
+ xstrdup_or_null(refs_resolve_ref_unsafe(get_main_ref_store(the_repository), "HEAD", 0, NULL,
+ &flags));
if (!options.head_name)
die(_("No such ref: %s"), "HEAD");
if (flags & REF_ISSYMREF) {
@@ -1735,7 +1755,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
if (!(options.flags & REBASE_NO_QUIET))
; /* be quiet */
else if (!strcmp(branch_name, "HEAD") &&
- resolve_ref_unsafe("HEAD", 0, NULL, &flag))
+ refs_resolve_ref_unsafe(get_main_ref_store(the_repository), "HEAD", 0, NULL, &flag))
puts(_("HEAD is up to date."));
else
printf(_("Current branch %s is up to date.\n"),
@@ -1745,7 +1765,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
} else if (!(options.flags & REBASE_NO_QUIET))
; /* be quiet */
else if (!strcmp(branch_name, "HEAD") &&
- resolve_ref_unsafe("HEAD", 0, NULL, &flag))
+ refs_resolve_ref_unsafe(get_main_ref_store(the_repository), "HEAD", 0, NULL, &flag))
puts(_("HEAD is up to date, rebase forced."));
else
printf(_("Current branch %s is up to date, rebase "
@@ -1832,14 +1852,7 @@ run_rebase:
cleanup:
strbuf_release(&buf);
strbuf_release(&revisions);
- free(options.reflog_action);
- free(options.head_name);
- strvec_clear(&options.git_am_opts);
- free(options.gpg_sign_opt);
- string_list_clear(&options.exec, 0);
- free(options.strategy);
- string_list_clear(&options.strategy_opts, 0);
- strbuf_release(&options.git_format_patch_opt);
+ rebase_options_release(&options);
free(squash_onto_name);
free(keep_base_onto_name);
return !!ret;
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index e8d7df14b6..339524ae2a 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -88,7 +88,7 @@ static struct strbuf push_cert = STRBUF_INIT;
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 char *cert_nonce_seed;
static struct strvec hidden_refs = STRVEC_INIT;
static const char *NONCE_UNSOLICITED = "UNSOLICITED";
@@ -168,13 +168,13 @@ static int receive_pack_config(const char *var, const char *value,
}
if (strcmp(var, "receive.fsck.skiplist") == 0) {
- const char *path;
+ char *path;
if (git_config_pathname(&path, var, value))
return 1;
strbuf_addf(&fsck_msg_types, "%cskiplist=%s",
fsck_msg_types.len ? ',' : '=', path);
- free((char *)path);
+ free(path);
return 0;
}
@@ -741,7 +741,7 @@ static void prepare_push_cert_sha1(struct child_process *proc)
already_done = 1;
if (write_object_file(push_cert.buf, push_cert.len, OBJ_BLOB,
&push_cert_oid))
- oidclr(&push_cert_oid);
+ oidclr(&push_cert_oid, the_repository->hash_algo);
memset(&sigcheck, '\0', sizeof(sigcheck));
@@ -1249,7 +1249,7 @@ cleanup:
return code;
}
-static char *refuse_unconfigured_deny_msg =
+static const char *refuse_unconfigured_deny_msg =
N_("By default, updating the current branch in a non-bare repository\n"
"is denied, because it will make the index and work tree inconsistent\n"
"with what you pushed, and will require 'git reset --hard' to match\n"
@@ -1269,7 +1269,7 @@ static void refuse_unconfigured_deny(void)
rp_error("%s", _(refuse_unconfigured_deny_msg));
}
-static char *refuse_unconfigured_deny_delete_current_msg =
+static const char *refuse_unconfigured_deny_delete_current_msg =
N_("By default, deleting the current branch is denied, because the next\n"
"'git clone' won't result in any file checked out, causing confusion.\n"
"\n"
@@ -1371,7 +1371,7 @@ static const char *push_to_deploy(unsigned char *sha1,
strvec_pushl(&child.args, "diff-index", "--quiet", "--cached",
"--ignore-submodules",
/* diff-index with either HEAD or an empty tree */
- head_has_history() ? "HEAD" : empty_tree_oid_hex(),
+ head_has_history() ? "HEAD" : empty_tree_oid_hex(the_repository->hash_algo),
"--", NULL);
strvec_pushv(&child.env, env->v);
child.no_stdin = 1;
@@ -1566,7 +1566,7 @@ static const char *update(struct command *cmd, struct shallow_info *si)
struct strbuf err = STRBUF_INIT;
if (!parse_object(the_repository, old_oid)) {
old_oid = NULL;
- if (ref_exists(name)) {
+ if (refs_ref_exists(get_main_ref_store(the_repository), name)) {
rp_warning("allowing deletion of corrupt ref");
} else {
rp_warning("deleting a non-existent ref");
@@ -1576,7 +1576,8 @@ static const char *update(struct command *cmd, struct shallow_info *si)
if (ref_transaction_delete(transaction,
namespaced_name,
old_oid,
- 0, "push", &err)) {
+ NULL, 0,
+ "push", &err)) {
rp_error("%s", err.buf);
ret = "failed to delete";
} else {
@@ -1595,6 +1596,7 @@ static const char *update(struct command *cmd, struct shallow_info *si)
if (ref_transaction_update(transaction,
namespaced_name,
new_oid, old_oid,
+ NULL, NULL,
0, "push",
&err)) {
rp_error("%s", err.buf);
@@ -1693,7 +1695,8 @@ static void check_aliased_update(struct command *cmd, struct string_list *list)
int flag;
strbuf_addf(&buf, "%s%s", get_git_namespace(), cmd->ref_name);
- dst_name = resolve_ref_unsafe(buf.buf, 0, NULL, &flag);
+ dst_name = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
+ buf.buf, 0, NULL, &flag);
check_aliased_update_internal(cmd, list, dst_name, flag);
strbuf_release(&buf);
}
@@ -1829,7 +1832,8 @@ static void execute_commands_non_atomic(struct command *commands,
if (!should_process_cmd(cmd) || cmd->run_proc_receive)
continue;
- transaction = ref_transaction_begin(&err);
+ transaction = ref_store_transaction_begin(get_main_ref_store(the_repository),
+ &err);
if (!transaction) {
rp_error("%s", err.buf);
strbuf_reset(&err);
@@ -1857,7 +1861,8 @@ static void execute_commands_atomic(struct command *commands,
struct strbuf err = STRBUF_INIT;
const char *reported_error = "atomic push failure";
- transaction = ref_transaction_begin(&err);
+ transaction = ref_store_transaction_begin(get_main_ref_store(the_repository),
+ &err);
if (!transaction) {
rp_error("%s", err.buf);
strbuf_reset(&err);
@@ -1983,7 +1988,9 @@ static void execute_commands(struct command *commands,
check_aliased_updates(commands);
free(head_name_to_free);
- head_name = head_name_to_free = resolve_refdup("HEAD", 0, NULL, NULL);
+ head_name = head_name_to_free = refs_resolve_refdup(get_main_ref_store(the_repository),
+ "HEAD", 0, NULL,
+ NULL);
if (run_proc_receive &&
run_proc_receive_hook(commands, push_options))
diff --git a/builtin/reflog.c b/builtin/reflog.c
index 060eb3377e..0d2ff95c6e 100644
--- a/builtin/reflog.c
+++ b/builtin/reflog.c
@@ -364,11 +364,12 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
};
set_reflog_expiry_param(&cb.cmd, item->string);
- status |= reflog_expire(item->string, flags,
- reflog_expiry_prepare,
- should_prune_fn,
- reflog_expiry_cleanup,
- &cb);
+ status |= refs_reflog_expire(get_main_ref_store(the_repository),
+ item->string, flags,
+ reflog_expiry_prepare,
+ should_prune_fn,
+ reflog_expiry_cleanup,
+ &cb);
}
string_list_clear(&collected.reflogs, 0);
}
@@ -377,16 +378,17 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
char *ref;
struct expire_reflog_policy_cb cb = { .cmd = cmd };
- if (!dwim_log(argv[i], strlen(argv[i]), NULL, &ref)) {
+ if (!repo_dwim_log(the_repository, argv[i], strlen(argv[i]), NULL, &ref)) {
status |= error(_("%s points nowhere!"), argv[i]);
continue;
}
set_reflog_expiry_param(&cb.cmd, ref);
- status |= reflog_expire(ref, flags,
- reflog_expiry_prepare,
- should_prune_fn,
- reflog_expiry_cleanup,
- &cb);
+ status |= refs_reflog_expire(get_main_ref_store(the_repository),
+ ref, flags,
+ reflog_expiry_prepare,
+ should_prune_fn,
+ reflog_expiry_cleanup,
+ &cb);
free(ref);
}
return status;
@@ -437,7 +439,8 @@ static int cmd_reflog_exists(int argc, const char **argv, const char *prefix)
refname = argv[0];
if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL))
die(_("invalid ref format: %s"), refname);
- return !reflog_exists(refname);
+ return !refs_reflog_exists(get_main_ref_store(the_repository),
+ refname);
}
/*
diff --git a/builtin/refs.c b/builtin/refs.c
new file mode 100644
index 0000000000..46dcd150d4
--- /dev/null
+++ b/builtin/refs.c
@@ -0,0 +1,75 @@
+#include "builtin.h"
+#include "parse-options.h"
+#include "refs.h"
+#include "repository.h"
+#include "strbuf.h"
+
+#define REFS_MIGRATE_USAGE \
+ N_("git refs migrate --ref-format=<format> [--dry-run]")
+
+static int cmd_refs_migrate(int argc, const char **argv, const char *prefix)
+{
+ const char * const migrate_usage[] = {
+ REFS_MIGRATE_USAGE,
+ NULL,
+ };
+ const char *format_str = NULL;
+ enum ref_storage_format format;
+ unsigned int flags = 0;
+ struct option options[] = {
+ OPT_STRING_F(0, "ref-format", &format_str, N_("format"),
+ N_("specify the reference format to convert to"),
+ PARSE_OPT_NONEG),
+ OPT_BIT(0, "dry-run", &flags,
+ N_("perform a non-destructive dry-run"),
+ REPO_MIGRATE_REF_STORAGE_FORMAT_DRYRUN),
+ OPT_END(),
+ };
+ struct strbuf errbuf = STRBUF_INIT;
+ int err;
+
+ argc = parse_options(argc, argv, prefix, options, migrate_usage, 0);
+ if (argc)
+ usage(_("too many arguments"));
+ if (!format_str)
+ usage(_("missing --ref-format=<format>"));
+
+ format = ref_storage_format_by_name(format_str);
+ if (format == REF_STORAGE_FORMAT_UNKNOWN) {
+ err = error(_("unknown ref storage format '%s'"), format_str);
+ goto out;
+ }
+
+ if (the_repository->ref_storage_format == format) {
+ err = error(_("repository already uses '%s' format"),
+ ref_storage_format_to_name(format));
+ goto out;
+ }
+
+ if (repo_migrate_ref_storage_format(the_repository, format, flags, &errbuf) < 0) {
+ err = error("%s", errbuf.buf);
+ goto out;
+ }
+
+ err = 0;
+
+out:
+ strbuf_release(&errbuf);
+ return err;
+}
+
+int cmd_refs(int argc, const char **argv, const char *prefix)
+{
+ const char * const refs_usage[] = {
+ REFS_MIGRATE_USAGE,
+ NULL,
+ };
+ parse_opt_subcommand_fn *fn = NULL;
+ struct option opts[] = {
+ OPT_SUBCOMMAND("migrate", &fn, cmd_refs_migrate),
+ OPT_END(),
+ };
+
+ argc = parse_options(argc, argv, prefix, opts, refs_usage, 0);
+ return fn(argc, argv, prefix);
+}
diff --git a/builtin/remote.c b/builtin/remote.c
index 8412d12fa5..08292498bd 100644
--- a/builtin/remote.c
+++ b/builtin/remote.c
@@ -240,7 +240,7 @@ static int add(int argc, const char **argv, const char *prefix)
strbuf_reset(&buf2);
strbuf_addf(&buf2, "refs/remotes/%s/%s", name, master);
- if (create_symref(buf.buf, buf2.buf, "remote add"))
+ if (refs_update_symref(get_main_ref_store(the_repository), buf.buf, buf2.buf, "remote add"))
return error(_("Could not setup master '%s'"), master);
}
@@ -376,7 +376,7 @@ static int get_ref_states(const struct ref *remote_refs, struct ref_states *stat
for (ref = fetch_map; ref; ref = ref->next) {
if (omit_name_by_refspec(ref->name, &states->remote->fetch))
string_list_append(&states->skipped, abbrev_branch(ref->name));
- else if (!ref->peer_ref || !ref_exists(ref->peer_ref->name))
+ else if (!ref->peer_ref || !refs_ref_exists(get_main_ref_store(the_repository), ref->peer_ref->name))
string_list_append(&states->new_refs, abbrev_branch(ref->name));
else
string_list_append(&states->tracked, abbrev_branch(ref->name));
@@ -493,12 +493,13 @@ static int get_head_names(const struct ref *remote_refs, struct ref_states *stat
{
struct ref *ref, *matches;
struct ref *fetch_map = NULL, **fetch_map_tail = &fetch_map;
- struct refspec_item refspec;
+ struct refspec_item refspec = {
+ .force = 0,
+ .pattern = 1,
+ .src = (char *) "refs/heads/*",
+ .dst = (char *) "refs/heads/*",
+ };
- memset(&refspec, 0, sizeof(refspec));
- refspec.force = 0;
- refspec.pattern = 1;
- refspec.src = refspec.dst = "refs/heads/*";
get_fetch_map(remote_refs, &refspec, &fetch_map_tail, 0);
matches = guess_remote_head(find_ref_by_name(remote_refs, "HEAD"),
fetch_map, 1);
@@ -507,7 +508,6 @@ static int get_head_names(const struct ref *remote_refs, struct ref_states *stat
free_refs(fetch_map);
free_refs(matches);
-
return 0;
}
@@ -598,8 +598,9 @@ static int read_remote_branches(const char *refname,
strbuf_addf(&buf, "refs/remotes/%s/", rename->old_name);
if (starts_with(refname, buf.buf)) {
item = string_list_append(rename->remote_branches, refname);
- symref = resolve_ref_unsafe(refname, RESOLVE_REF_READING,
- NULL, &flag);
+ symref = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
+ refname, RESOLVE_REF_READING,
+ NULL, &flag);
if (symref && (flag & REF_ISSYMREF)) {
item->util = xstrdup(symref);
rename->symrefs_nr++;
@@ -618,8 +619,8 @@ static int migrate_file(struct remote *remote)
int i;
strbuf_addf(&buf, "remote.%s.url", remote->name);
- for (i = 0; i < remote->url_nr; i++)
- git_config_set_multivar(buf.buf, remote->url[i], "^$", 0);
+ for (i = 0; i < remote->url.nr; i++)
+ git_config_set_multivar(buf.buf, remote->url.v[i], "^$", 0);
strbuf_reset(&buf);
strbuf_addf(&buf, "remote.%s.push", remote->name);
for (i = 0; i < remote->push.raw_nr; i++)
@@ -789,7 +790,8 @@ static int mv(int argc, const char **argv, const char *prefix)
* First remove symrefs, then rename the rest, finally create
* the new symrefs.
*/
- for_each_ref(read_remote_branches, &rename);
+ refs_for_each_ref(get_main_ref_store(the_repository),
+ read_remote_branches, &rename);
if (show_progress) {
/*
* Count symrefs twice, since "renaming" them is done by
@@ -805,7 +807,7 @@ static int mv(int argc, const char **argv, const char *prefix)
if (refs_read_symbolic_ref(get_main_ref_store(the_repository), item->string,
&referent))
continue;
- if (delete_ref(NULL, item->string, NULL, REF_NO_DEREF))
+ if (refs_delete_ref(get_main_ref_store(the_repository), NULL, item->string, NULL, REF_NO_DEREF))
die(_("deleting '%s' failed"), item->string);
strbuf_release(&referent);
@@ -823,7 +825,7 @@ static int mv(int argc, const char **argv, const char *prefix)
strbuf_reset(&buf2);
strbuf_addf(&buf2, "remote: renamed %s to %s",
item->string, buf.buf);
- if (rename_ref(item->string, buf.buf, buf2.buf))
+ if (refs_rename_ref(get_main_ref_store(the_repository), item->string, buf.buf, buf2.buf))
die(_("renaming '%s' failed"), item->string);
display_progress(progress, ++refs_renamed_nr);
}
@@ -843,7 +845,7 @@ static int mv(int argc, const char **argv, const char *prefix)
strbuf_reset(&buf3);
strbuf_addf(&buf3, "remote: renamed %s to %s",
item->string, buf.buf);
- if (create_symref(buf.buf, buf2.buf, buf3.buf))
+ if (refs_update_symref(get_main_ref_store(the_repository), buf.buf, buf2.buf, buf3.buf))
die(_("creating '%s' failed"), buf.buf);
display_progress(progress, ++refs_renamed_nr);
}
@@ -917,11 +919,14 @@ static int rm(int argc, const char **argv, const char *prefix)
* refs, which are invalidated when deleting a branch.
*/
cb_data.remote = remote;
- result = for_each_ref(add_branch_for_removal, &cb_data);
+ result = refs_for_each_ref(get_main_ref_store(the_repository),
+ add_branch_for_removal, &cb_data);
strbuf_release(&buf);
if (!result)
- result = delete_refs("remote: remove", &branches, REF_NO_DEREF);
+ result = refs_delete_refs(get_main_ref_store(the_repository),
+ "remote: remove", &branches,
+ REF_NO_DEREF);
string_list_clear(&branches, 0);
if (skipped.nr) {
@@ -997,8 +1002,7 @@ static int get_remote_ref_states(const char *name,
struct transport *transport;
const struct ref *remote_refs;
- transport = transport_get(states->remote, states->remote->url_nr > 0 ?
- states->remote->url[0] : NULL);
+ transport = transport_get(states->remote, states->remote->url.v[0]);
remote_refs = transport_get_remote_refs(transport, NULL);
states->queried = 1;
@@ -1010,7 +1014,8 @@ static int get_remote_ref_states(const char *name,
get_push_ref_states(remote_refs, states);
transport_disconnect(transport);
} else {
- for_each_ref(append_ref_to_tracked_list, states);
+ refs_for_each_ref(get_main_ref_store(the_repository),
+ append_ref_to_tracked_list, states);
string_list_sort(&states->tracked);
get_push_ref_states_noquery(states);
}
@@ -1207,15 +1212,15 @@ static int get_one_entry(struct remote *remote, void *priv)
{
struct string_list *list = priv;
struct strbuf remote_info_buf = STRBUF_INIT;
- const char **url;
- int i, url_nr;
+ struct strvec *url;
+ int i;
- if (remote->url_nr > 0) {
+ if (remote->url.nr > 0) {
struct strbuf promisor_config = STRBUF_INIT;
const char *partial_clone_filter = NULL;
strbuf_addf(&promisor_config, "remote.%s.partialclonefilter", remote->name);
- strbuf_addf(&remote_info_buf, "%s (fetch)", remote->url[0]);
+ strbuf_addf(&remote_info_buf, "%s (fetch)", remote->url.v[0]);
if (!git_config_get_string_tmp(promisor_config.buf, &partial_clone_filter))
strbuf_addf(&remote_info_buf, " [%s]", partial_clone_filter);
@@ -1224,16 +1229,10 @@ static int get_one_entry(struct remote *remote, void *priv)
strbuf_detach(&remote_info_buf, NULL);
} else
string_list_append(list, remote->name)->util = NULL;
- if (remote->pushurl_nr) {
- url = remote->pushurl;
- url_nr = remote->pushurl_nr;
- } else {
- url = remote->url;
- url_nr = remote->url_nr;
- }
- for (i = 0; i < url_nr; i++)
+ url = push_url_of_remote(remote);
+ for (i = 0; i < url->nr; i++)
{
- strbuf_addf(&remote_info_buf, "%s (push)", url[i]);
+ strbuf_addf(&remote_info_buf, "%s (push)", url->v[i]);
string_list_append(list, remote->name)->util =
strbuf_detach(&remote_info_buf, NULL);
}
@@ -1289,28 +1288,20 @@ static int show(int argc, const char **argv, const char *prefix)
for (; argc; argc--, argv++) {
int i;
- const char **url;
- int url_nr;
+ struct strvec *url;
get_remote_ref_states(*argv, &info.states, query_flag);
printf_ln(_("* remote %s"), *argv);
- printf_ln(_(" Fetch URL: %s"), info.states.remote->url_nr > 0 ?
- info.states.remote->url[0] : _("(no URL)"));
- if (info.states.remote->pushurl_nr) {
- url = info.states.remote->pushurl;
- url_nr = info.states.remote->pushurl_nr;
- } else {
- url = info.states.remote->url;
- url_nr = info.states.remote->url_nr;
- }
- for (i = 0; i < url_nr; i++)
+ printf_ln(_(" Fetch URL: %s"), info.states.remote->url.v[0]);
+ url = push_url_of_remote(info.states.remote);
+ for (i = 0; i < url->nr; i++)
/*
* TRANSLATORS: the colon ':' should align
* with the one in " Fetch URL: %s"
* translation.
*/
- printf_ln(_(" Push URL: %s"), url[i]);
+ printf_ln(_(" Push URL: %s"), url->v[i]);
if (!i)
printf_ln(_(" Push URL: %s"), _("(no URL)"));
if (no_query)
@@ -1407,7 +1398,7 @@ static int set_head(int argc, const char **argv, const char *prefix)
head_name = xstrdup(states.heads.items[0].string);
free_remote_ref_states(&states);
} else if (opt_d && !opt_a && argc == 1) {
- if (delete_ref(NULL, buf.buf, NULL, REF_NO_DEREF))
+ if (refs_delete_ref(get_main_ref_store(the_repository), NULL, buf.buf, NULL, REF_NO_DEREF))
result |= error(_("Could not delete %s"), buf.buf);
} else
usage_with_options(builtin_remote_sethead_usage, options);
@@ -1415,9 +1406,9 @@ static int set_head(int argc, const char **argv, const char *prefix)
if (head_name) {
strbuf_addf(&buf2, "refs/remotes/%s/%s", argv[0], head_name);
/* make sure it's valid */
- if (!ref_exists(buf2.buf))
+ if (!refs_ref_exists(get_main_ref_store(the_repository), buf2.buf))
result |= error(_("Not a valid ref: %s"), buf2.buf);
- else if (create_symref(buf.buf, buf2.buf, "remote set-head"))
+ else if (refs_update_symref(get_main_ref_store(the_repository), buf.buf, buf2.buf, "remote set-head"))
result |= error(_("Could not setup %s"), buf.buf);
else if (opt_a)
printf("%s/HEAD set to %s\n", argv[0], head_name);
@@ -1447,17 +1438,15 @@ static int prune_remote(const char *remote, int dry_run)
}
printf_ln(_("Pruning %s"), remote);
- printf_ln(_("URL: %s"),
- states.remote->url_nr
- ? states.remote->url[0]
- : _("(no URL)"));
+ printf_ln(_("URL: %s"), states.remote->url.v[0]);
for_each_string_list_item(item, &states.stale)
string_list_append(&refs_to_prune, item->util);
string_list_sort(&refs_to_prune);
if (!dry_run)
- result |= delete_refs("remote: prune", &refs_to_prune, 0);
+ result |= refs_delete_refs(get_main_ref_store(the_repository),
+ "remote: prune", &refs_to_prune, 0);
for_each_string_list_item(item, &states.stale) {
const char *refname = item->util;
@@ -1470,7 +1459,8 @@ static int prune_remote(const char *remote, int dry_run)
abbrev_ref(refname, "refs/remotes/"));
}
- warn_dangling_symrefs(stdout, dangling_msg, &refs_to_prune);
+ refs_warn_dangling_symrefs(get_main_ref_store(the_repository),
+ stdout, dangling_msg, &refs_to_prune);
string_list_clear(&refs_to_prune, 0);
free_remote_ref_states(&states);
@@ -1614,8 +1604,7 @@ static int get_url(int argc, const char **argv, const char *prefix)
int i, push_mode = 0, all_mode = 0;
const char *remotename = NULL;
struct remote *remote;
- const char **url;
- int url_nr;
+ struct strvec *url;
struct option options[] = {
OPT_BOOL('\0', "push", &push_mode,
N_("query push URLs rather than fetch URLs")),
@@ -1637,27 +1626,13 @@ static int get_url(int argc, const char **argv, const char *prefix)
exit(2);
}
- url_nr = 0;
- if (push_mode) {
- url = remote->pushurl;
- url_nr = remote->pushurl_nr;
- }
- /* else fetch mode */
-
- /* Use the fetch URL when no push URLs were found or requested. */
- if (!url_nr) {
- url = remote->url;
- url_nr = remote->url_nr;
- }
-
- if (!url_nr)
- die(_("no URLs configured for remote '%s'"), remotename);
+ url = push_mode ? push_url_of_remote(remote) : &remote->url;
if (all_mode) {
- for (i = 0; i < url_nr; i++)
- printf_ln("%s", url[i]);
+ for (i = 0; i < url->nr; i++)
+ printf_ln("%s", url->v[i]);
} else {
- printf_ln("%s", *url);
+ printf_ln("%s", url->v[0]);
}
return 0;
@@ -1672,8 +1647,7 @@ static int set_url(int argc, const char **argv, const char *prefix)
const char *oldurl = NULL;
struct remote *remote;
regex_t old_regex;
- const char **urlset;
- int urlset_nr;
+ struct strvec *urlset;
struct strbuf name_buf = STRBUF_INIT;
struct option options[] = {
OPT_BOOL('\0', "push", &push_mode,
@@ -1710,12 +1684,10 @@ static int set_url(int argc, const char **argv, const char *prefix)
if (push_mode) {
strbuf_addf(&name_buf, "remote.%s.pushurl", remotename);
- urlset = remote->pushurl;
- urlset_nr = remote->pushurl_nr;
+ urlset = &remote->pushurl;
} else {
strbuf_addf(&name_buf, "remote.%s.url", remotename);
- urlset = remote->url;
- urlset_nr = remote->url_nr;
+ urlset = &remote->url;
}
/* Special cases that add new entry. */
@@ -1732,8 +1704,8 @@ static int set_url(int argc, const char **argv, const char *prefix)
if (regcomp(&old_regex, oldurl, REG_EXTENDED))
die(_("Invalid old URL pattern: %s"), oldurl);
- for (i = 0; i < urlset_nr; i++)
- if (!regexec(&old_regex, urlset[i], 0, NULL, 0))
+ for (i = 0; i < urlset->nr; i++)
+ if (!regexec(&old_regex, urlset->v[i], 0, NULL, 0))
matches++;
else
negative_matches++;
diff --git a/builtin/repack.c b/builtin/repack.c
index 15e4cccc45..f0317fa94a 100644
--- a/builtin/repack.c
+++ b/builtin/repack.c
@@ -48,10 +48,10 @@ static const char incremental_bitmap_conflict_error[] = N_(
);
struct pack_objects_args {
- const char *window;
- const char *window_memory;
- const char *depth;
- const char *threads;
+ char *window;
+ char *window_memory;
+ char *depth;
+ char *threads;
unsigned long max_pack_size;
int no_reuse_delta;
int no_reuse_object;
@@ -673,7 +673,7 @@ static int midx_snapshot_ref_one(const char *refname UNUSED,
struct midx_snapshot_ref_data *data = _data;
struct object_id peeled;
- if (!peel_iterated_oid(oid, &peeled))
+ if (!peel_iterated_oid(the_repository, oid, &peeled))
oid = &peeled;
if (oidset_insert(&data->seen, oid))
@@ -706,11 +706,14 @@ static void midx_snapshot_refs(struct tempfile *f)
data.preferred = 1;
for_each_string_list_item(item, preferred)
- for_each_ref_in(item->string, midx_snapshot_ref_one, &data);
+ refs_for_each_ref_in(get_main_ref_store(the_repository),
+ item->string,
+ midx_snapshot_ref_one, &data);
data.preferred = 0;
}
- for_each_ref(midx_snapshot_ref_one, &data);
+ refs_for_each_ref(get_main_ref_store(the_repository),
+ midx_snapshot_ref_one, &data);
if (close_tempfile_gently(f)) {
int save_errno = errno;
diff --git a/builtin/replace.c b/builtin/replace.c
index da59600ad2..1ef833c07f 100644
--- a/builtin/replace.c
+++ b/builtin/replace.c
@@ -43,11 +43,12 @@ enum replace_format {
};
struct show_data {
+ struct repository *repo;
const char *pattern;
enum replace_format format;
};
-static int show_reference(struct repository *r, const char *refname,
+static int show_reference(const char *refname,
const struct object_id *oid,
int flag UNUSED, void *cb_data)
{
@@ -62,11 +63,11 @@ static int show_reference(struct repository *r, const char *refname,
struct object_id object;
enum object_type obj_type, repl_type;
- if (repo_get_oid(r, refname, &object))
+ if (repo_get_oid(data->repo, refname, &object))
return error(_("failed to resolve '%s' as a valid ref"), refname);
- obj_type = oid_object_info(r, &object, NULL);
- repl_type = oid_object_info(r, oid, NULL);
+ obj_type = oid_object_info(data->repo, &object, NULL);
+ repl_type = oid_object_info(data->repo, oid, NULL);
printf("%s (%s) -> %s (%s)\n", refname, type_name(obj_type),
oid_to_hex(oid), type_name(repl_type));
@@ -80,6 +81,7 @@ static int list_replace_refs(const char *pattern, const char *format)
{
struct show_data data;
+ data.repo = the_repository;
if (!pattern)
pattern = "*";
data.pattern = pattern;
@@ -99,7 +101,8 @@ static int list_replace_refs(const char *pattern, const char *format)
"valid formats are 'short', 'medium' and 'long'"),
format);
- for_each_replace_ref(the_repository, show_reference, (void *)&data);
+ refs_for_each_replace_ref(get_main_ref_store(the_repository),
+ show_reference, (void *)&data);
return 0;
}
@@ -130,7 +133,7 @@ static int for_each_replace_name(const char **argv, each_replace_name_fn fn)
strbuf_addstr(&ref, oid_to_hex(&oid));
full_hex = ref.buf + base_len;
- if (read_ref(ref.buf, &oid)) {
+ if (refs_read_ref(get_main_ref_store(the_repository), ref.buf, &oid)) {
error(_("replace ref '%s' not found"), full_hex);
had_error = 1;
continue;
@@ -145,7 +148,7 @@ static int for_each_replace_name(const char **argv, each_replace_name_fn fn)
static int delete_replace_ref(const char *name, const char *ref,
const struct object_id *oid)
{
- if (delete_ref(NULL, ref, oid, 0))
+ if (refs_delete_ref(get_main_ref_store(the_repository), NULL, ref, oid, 0))
return 1;
printf_ln(_("Deleted replace ref '%s'"), name);
return 0;
@@ -163,8 +166,8 @@ static int check_ref_valid(struct object_id *object,
if (check_refname_format(ref->buf, 0))
return error(_("'%s' is not a valid ref name"), ref->buf);
- if (read_ref(ref->buf, prev))
- oidclr(prev);
+ if (refs_read_ref(get_main_ref_store(the_repository), ref->buf, prev))
+ oidclr(prev, the_repository->hash_algo);
else if (!force)
return error(_("replace ref '%s' already exists"), ref->buf);
return 0;
@@ -198,10 +201,11 @@ static int replace_object_oid(const char *object_ref,
return -1;
}
- transaction = ref_transaction_begin(&err);
+ transaction = ref_store_transaction_begin(get_main_ref_store(the_repository),
+ &err);
if (!transaction ||
ref_transaction_update(transaction, ref.buf, repl, &prev,
- 0, NULL, &err) ||
+ NULL, NULL, 0, NULL, &err) ||
ref_transaction_commit(transaction, &err))
res = error("%s", err.buf);
diff --git a/builtin/replay.c b/builtin/replay.c
index 6bc4b47f09..0448326636 100644
--- a/builtin/replay.c
+++ b/builtin/replay.c
@@ -2,7 +2,6 @@
* "git replay" builtin command
*/
-#define USE_THE_INDEX_VARIABLE
#include "git-compat-util.h"
#include "builtin.h"
@@ -53,11 +52,11 @@ static struct commit *create_commit(struct tree *tree,
struct commit *parent)
{
struct object_id ret;
- struct object *obj;
+ struct object *obj = NULL;
struct commit_list *parents = NULL;
char *author;
char *sign_commit = NULL; /* FIXME: cli users might want to sign again */
- struct commit_extra_header *extra;
+ struct commit_extra_header *extra = NULL;
struct strbuf msg = STRBUF_INIT;
const char *out_enc = get_commit_output_encoding();
const char *message = repo_logmsg_reencode(the_repository, based_on,
@@ -74,12 +73,16 @@ static struct commit *create_commit(struct tree *tree,
if (commit_tree_extended(msg.buf, msg.len, &tree->object.oid, parents,
&ret, author, NULL, sign_commit, extra)) {
error(_("failed to write commit object"));
- return NULL;
+ goto out;
}
- free(author);
- strbuf_release(&msg);
obj = parse_object(the_repository, &ret);
+
+out:
+ free_commit_extra_headers(extra);
+ free_commit_list(parents);
+ strbuf_release(&msg);
+ free(author);
return (struct commit *)obj;
}
diff --git a/builtin/reset.c b/builtin/reset.c
index 1d62ff6332..5f941fb3a2 100644
--- a/builtin/reset.c
+++ b/builtin/reset.c
@@ -7,7 +7,7 @@
*
* Copyright (c) 2005, 2006 Linus Torvalds and Junio C Hamano
*/
-#define USE_THE_INDEX_VARIABLE
+
#include "builtin.h"
#include "advice.h"
#include "config.h"
@@ -66,8 +66,8 @@ static int reset_index(const char *ref, const struct object_id *oid, int reset_t
memset(&opts, 0, sizeof(opts));
opts.head_idx = 1;
- opts.src_index = &the_index;
- opts.dst_index = &the_index;
+ opts.src_index = the_repository->index;
+ opts.dst_index = the_repository->index;
opts.fn = oneway_merge;
opts.merge = 1;
init_checkout_metadata(&opts.meta, ref, oid, NULL);
@@ -159,11 +159,11 @@ static void update_index_from_diff(struct diff_queue_struct *q,
struct cache_entry *ce;
if (!is_in_reset_tree && !intent_to_add) {
- remove_file_from_index(&the_index, one->path);
+ remove_file_from_index(the_repository->index, one->path);
continue;
}
- ce = make_cache_entry(&the_index, one->mode, &one->oid, one->path,
+ ce = make_cache_entry(the_repository->index, one->mode, &one->oid, one->path,
0, 0);
/*
@@ -174,9 +174,9 @@ static void update_index_from_diff(struct diff_queue_struct *q,
* if this entry is outside the sparse cone - this is necessary
* to properly construct the reset sparse directory.
*/
- pos = index_name_pos(&the_index, one->path, strlen(one->path));
- if ((pos >= 0 && ce_skip_worktree(the_index.cache[pos])) ||
- (pos < 0 && !path_in_sparse_checkout(one->path, &the_index)))
+ pos = index_name_pos(the_repository->index, one->path, strlen(one->path));
+ if ((pos >= 0 && ce_skip_worktree(the_repository->index->cache[pos])) ||
+ (pos < 0 && !path_in_sparse_checkout(one->path, the_repository->index)))
ce->ce_flags |= CE_SKIP_WORKTREE;
if (!ce)
@@ -186,7 +186,7 @@ static void update_index_from_diff(struct diff_queue_struct *q,
ce->ce_flags |= CE_INTENT_TO_ADD;
set_object_name_for_intent_to_add_entry(ce);
}
- add_index_entry(&the_index, ce,
+ add_index_entry(the_repository->index, ce,
ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE);
}
}
@@ -208,8 +208,8 @@ static int read_from_tree(const struct pathspec *pathspec,
opt.change = diff_change;
opt.add_remove = diff_addremove;
- if (pathspec->nr && pathspec_needs_expanded_index(&the_index, pathspec))
- ensure_full_index(&the_index);
+ if (pathspec->nr && pathspec_needs_expanded_index(the_repository->index, pathspec))
+ ensure_full_index(the_repository->index);
if (do_diff_cache(tree_oid, &opt))
return 1;
@@ -235,7 +235,7 @@ static void set_reflog_message(struct strbuf *sb, const char *action,
static void die_if_unmerged_cache(int reset_type)
{
- if (is_merge() || unmerged_index(&the_index))
+ if (is_merge() || unmerged_index(the_repository->index))
die(_("Cannot do a %s reset in the middle of a merge."),
_(reset_type_names[reset_type]));
@@ -307,13 +307,16 @@ static int reset_refs(const char *rev, const struct object_id *oid)
if (!repo_get_oid(the_repository, "HEAD", &oid_orig)) {
orig = &oid_orig;
set_reflog_message(&msg, "updating ORIG_HEAD", NULL);
- update_ref(msg.buf, "ORIG_HEAD", orig, old_orig, 0,
- UPDATE_REFS_MSG_ON_ERR);
+ refs_update_ref(get_main_ref_store(the_repository), msg.buf,
+ "ORIG_HEAD", orig, old_orig, 0,
+ UPDATE_REFS_MSG_ON_ERR);
} else if (old_orig)
- delete_ref(NULL, "ORIG_HEAD", old_orig, 0);
+ refs_delete_ref(get_main_ref_store(the_repository), NULL,
+ "ORIG_HEAD", old_orig, 0);
set_reflog_message(&msg, "updating HEAD", rev);
- update_ref_status = update_ref(msg.buf, "HEAD", oid, orig, 0,
- UPDATE_REFS_MSG_ON_ERR);
+ update_ref_status = refs_update_ref(get_main_ref_store(the_repository),
+ msg.buf, "HEAD", oid, orig, 0,
+ UPDATE_REFS_MSG_ON_ERR);
strbuf_release(&msg);
return update_ref_status;
}
@@ -470,12 +473,12 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
update_ref_status = 1;
goto cleanup;
}
- the_index.updated_skipworktree = 1;
+ the_repository->index->updated_skipworktree = 1;
if (!no_refresh && get_git_work_tree()) {
uint64_t t_begin, t_delta_in_ms;
t_begin = getnanotime();
- refresh_index(&the_index, flags, NULL, NULL,
+ refresh_index(the_repository->index, flags, NULL, NULL,
_("Unstaged changes after reset:"));
t_delta_in_ms = (getnanotime() - t_begin) / 1000000;
if (!quiet && advice_enabled(ADVICE_RESET_NO_REFRESH_WARNING) && t_delta_in_ms > REFRESH_INDEX_DELAY_WARNING_IN_MS) {
@@ -501,7 +504,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
free(ref);
}
- if (write_locked_index(&the_index, &lock, COMMIT_LOCK))
+ if (write_locked_index(the_repository->index, &lock, COMMIT_LOCK))
die(_("Could not write new index file."));
}
@@ -516,7 +519,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
if (!pathspec.nr)
remove_branch_state(the_repository, 0);
- discard_index(&the_index);
+ discard_index(the_repository->index);
cleanup:
clear_pathspec(&pathspec);
diff --git a/builtin/rev-list.c b/builtin/rev-list.c
index 77803727e0..97d077a994 100644
--- a/builtin/rev-list.c
+++ b/builtin/rev-list.c
@@ -508,6 +508,8 @@ static int try_bitmap_disk_usage(struct rev_info *revs,
size_from_bitmap = get_disk_usage_from_bitmap(bitmap_git, revs);
print_disk_usage(size_from_bitmap);
+
+ free_bitmap_index(bitmap_git);
return 0;
}
diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c
index 624182e507..2e64f5bda7 100644
--- a/builtin/rev-parse.c
+++ b/builtin/rev-parse.c
@@ -3,7 +3,7 @@
*
* Copyright (C) Linus Torvalds, 2005
*/
-#define USE_THE_INDEX_VARIABLE
+
#include "builtin.h"
#include "abspath.h"
#include "config.h"
@@ -160,8 +160,9 @@ static void show_rev(int type, const struct object_id *oid, const char *name)
case 1: /* happy */
if (abbrev_ref) {
char *old = full;
- full = shorten_unambiguous_ref(full,
- abbrev_ref_strict);
+ full = refs_shorten_unambiguous_ref(get_main_ref_store(the_repository),
+ full,
+ abbrev_ref_strict);
free(old);
}
show_with_type(type, full);
@@ -422,12 +423,12 @@ static char *findspace(const char *s)
static int cmd_parseopt(int argc, const char **argv, const char *prefix)
{
- static int keep_dashdash = 0, stop_at_non_option = 0;
- static char const * const parseopt_usage[] = {
+ int keep_dashdash = 0, stop_at_non_option = 0;
+ char const * const parseopt_usage[] = {
N_("git rev-parse --parseopt [<options>] -- [<args>...]"),
NULL
};
- static struct option parseopt_opts[] = {
+ struct option parseopt_opts[] = {
OPT_BOOL(0, "keep-dashdash", &keep_dashdash,
N_("keep the `--` passed as an arg")),
OPT_BOOL(0, "stop-at-non-option", &stop_at_non_option,
@@ -437,12 +438,11 @@ static int cmd_parseopt(int argc, const char **argv, const char *prefix)
N_("output in stuck long form")),
OPT_END(),
};
- static const char * const flag_chars = "*=?!";
-
struct strbuf sb = STRBUF_INIT, parsed = STRBUF_INIT;
- const char **usage = NULL;
+ struct strvec longnames = STRVEC_INIT;
+ struct strvec usage = STRVEC_INIT;
struct option *opts = NULL;
- int onb = 0, osz = 0, unb = 0, usz = 0;
+ size_t opts_nr = 0, opts_alloc = 0;
strbuf_addstr(&parsed, "set --");
argc = parse_options(argc, argv, prefix, parseopt_opts, parseopt_usage,
@@ -452,16 +452,16 @@ static int cmd_parseopt(int argc, const char **argv, const char *prefix)
/* get the usage up to the first line with a -- on it */
for (;;) {
+ strbuf_reset(&sb);
if (strbuf_getline(&sb, stdin) == EOF)
die(_("premature end of input"));
- ALLOC_GROW(usage, unb + 1, usz);
if (!strcmp("--", sb.buf)) {
- if (unb < 1)
+ if (!usage.nr)
die(_("no usage string given before the `--' separator"));
- usage[unb] = NULL;
break;
}
- usage[unb++] = strbuf_detach(&sb, NULL);
+
+ strvec_push(&usage, sb.buf);
}
/* parse: (<short>|<short>,<long>|<long>)[*=?!]*<arghint>? SP+ <help> */
@@ -473,10 +473,10 @@ static int cmd_parseopt(int argc, const char **argv, const char *prefix)
if (!sb.len)
continue;
- ALLOC_GROW(opts, onb + 1, osz);
- memset(opts + onb, 0, sizeof(opts[onb]));
+ ALLOC_GROW(opts, opts_nr + 1, opts_alloc);
+ memset(opts + opts_nr, 0, sizeof(*opts));
- o = &opts[onb++];
+ o = &opts[opts_nr++];
help = findspace(sb.buf);
if (!help || sb.buf == help) {
o->type = OPTION_GROUP;
@@ -493,20 +493,22 @@ static int cmd_parseopt(int argc, const char **argv, const char *prefix)
o->callback = &parseopt_dump;
/* name(s) */
- s = strpbrk(sb.buf, flag_chars);
+ s = strpbrk(sb.buf, "*=?!");
if (!s)
s = help;
if (s == sb.buf)
die(_("missing opt-spec before option flags"));
- if (s - sb.buf == 1) /* short option only */
+ if (s - sb.buf == 1) { /* short option only */
o->short_name = *sb.buf;
- else if (sb.buf[1] != ',') /* long option only */
- o->long_name = xmemdupz(sb.buf, s - sb.buf);
- else {
+ } else if (sb.buf[1] != ',') { /* long option only */
+ o->long_name = strvec_pushf(&longnames, "%.*s",
+ (int)(s - sb.buf), sb.buf);
+ } else {
o->short_name = *sb.buf;
- o->long_name = xmemdupz(sb.buf + 2, s - sb.buf - 2);
+ o->long_name = strvec_pushf(&longnames, "%.*s",
+ (int)(s - sb.buf - 2), sb.buf + 2);
}
/* flags */
@@ -536,9 +538,9 @@ static int cmd_parseopt(int argc, const char **argv, const char *prefix)
strbuf_release(&sb);
/* put an OPT_END() */
- ALLOC_GROW(opts, onb + 1, osz);
- memset(opts + onb, 0, sizeof(opts[onb]));
- argc = parse_options(argc, argv, prefix, opts, usage,
+ ALLOC_GROW(opts, opts_nr + 1, opts_alloc);
+ memset(opts + opts_nr, 0, sizeof(*opts));
+ argc = parse_options(argc, argv, prefix, opts, usage.v,
(keep_dashdash ? PARSE_OPT_KEEP_DASHDASH : 0) |
(stop_at_non_option ? PARSE_OPT_STOP_AT_NON_OPTION : 0) |
PARSE_OPT_SHELL_EVAL);
@@ -546,7 +548,13 @@ static int cmd_parseopt(int argc, const char **argv, const char *prefix)
strbuf_addstr(&parsed, " --");
sq_quote_argv(&parsed, argv);
puts(parsed.buf);
+
strbuf_release(&parsed);
+ strbuf_release(&sb);
+ strvec_clear(&longnames);
+ strvec_clear(&usage);
+ free((char *) opts->help);
+ free(opts);
return 0;
}
@@ -599,9 +607,12 @@ static int opt_with_value(const char *arg, const char *opt, const char **value)
static void handle_ref_opt(const char *pattern, const char *prefix)
{
if (pattern)
- for_each_glob_ref_in(show_reference, pattern, prefix, NULL);
+ refs_for_each_glob_ref_in(get_main_ref_store(the_repository),
+ show_reference, pattern, prefix,
+ NULL);
else
- for_each_ref_in(prefix, show_reference, NULL);
+ refs_for_each_ref_in(get_main_ref_store(the_repository),
+ prefix, show_reference, NULL);
clear_ref_exclusions(&ref_excludes);
}
@@ -687,7 +698,6 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
const char *name = NULL;
struct object_context unused;
struct strbuf buf = STRBUF_INIT;
- const int hexsz = the_hash_algo->hexsz;
int seen_end_of_options = 0;
enum format_type format = FORMAT_DEFAULT;
@@ -863,8 +873,8 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
abbrev = strtoul(arg, NULL, 10);
if (abbrev < MINIMUM_ABBREV)
abbrev = MINIMUM_ABBREV;
- else if (hexsz <= abbrev)
- abbrev = hexsz;
+ else if ((int)the_hash_algo->hexsz <= abbrev)
+ abbrev = the_hash_algo->hexsz;
continue;
}
if (!strcmp(arg, "--sq")) {
@@ -898,7 +908,8 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
continue;
}
if (!strcmp(arg, "--all")) {
- for_each_ref(show_reference, NULL);
+ refs_for_each_ref(get_main_ref_store(the_repository),
+ show_reference, NULL);
clear_ref_exclusions(&ref_excludes);
continue;
}
@@ -908,8 +919,14 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
continue;
}
if (!strcmp(arg, "--bisect")) {
- for_each_fullref_in("refs/bisect/bad", show_reference, NULL);
- for_each_fullref_in("refs/bisect/good", anti_reference, NULL);
+ refs_for_each_fullref_in(get_main_ref_store(the_repository),
+ "refs/bisect/bad",
+ NULL, show_reference,
+ NULL);
+ refs_for_each_fullref_in(get_main_ref_store(the_repository),
+ "refs/bisect/good",
+ NULL, anti_reference,
+ NULL);
continue;
}
if (opt_with_value(arg, "--branches", &arg)) {
@@ -1049,8 +1066,8 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
if (!strcmp(arg, "--shared-index-path")) {
if (repo_read_index(the_repository) < 0)
die(_("Could not read the index"));
- if (the_index.split_index) {
- const struct object_id *oid = &the_index.split_index->base_oid;
+ if (the_repository->index->split_index) {
+ const struct object_id *oid = &the_repository->index->split_index->base_oid;
const char *path = git_path("sharedindex.%s", oid_to_hex(oid));
print_path(path, prefix, format, DEFAULT_RELATIVE);
}
@@ -1111,6 +1128,7 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
}
if (!get_oid_with_context(the_repository, name,
flags, &oid, &unused)) {
+ object_context_release(&unused);
if (output_algo)
repo_oid_to_algop(the_repository, &oid,
output_algo, &oid);
@@ -1120,6 +1138,7 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
show_rev(type, &oid, name);
continue;
}
+ object_context_release(&unused);
if (verify)
die_no_single_rev(quiet);
if (has_dashdash)
diff --git a/builtin/revert.c b/builtin/revert.c
index 53935d2c68..7bf2b4e11d 100644
--- a/builtin/revert.c
+++ b/builtin/revert.c
@@ -179,7 +179,7 @@ static int run_sequencer(int argc, const char **argv, const char *prefix,
/* Check for incompatible command line arguments */
if (cmd) {
- char *this_operation;
+ const char *this_operation;
if (cmd == 'q')
this_operation = "--quit";
else if (cmd == 'c')
diff --git a/builtin/rm.c b/builtin/rm.c
index fd130cea2d..0e79cbab62 100644
--- a/builtin/rm.c
+++ b/builtin/rm.c
@@ -3,7 +3,7 @@
*
* Copyright (C) Linus Torvalds 2006
*/
-#define USE_THE_INDEX_VARIABLE
+
#include "builtin.h"
#include "advice.h"
#include "config.h"
@@ -41,8 +41,8 @@ static int get_ours_cache_pos(const char *path, int pos)
{
int i = -pos - 1;
- while ((i < the_index.cache_nr) && !strcmp(the_index.cache[i]->name, path)) {
- if (ce_stage(the_index.cache[i]) == 2)
+ while ((i < the_repository->index->cache_nr) && !strcmp(the_repository->index->cache[i]->name, path)) {
+ if (ce_stage(the_repository->index->cache[i]) == 2)
return i;
i++;
}
@@ -78,13 +78,13 @@ static void submodules_absorb_gitdir_if_needed(void)
int pos;
const struct cache_entry *ce;
- pos = index_name_pos(&the_index, name, strlen(name));
+ pos = index_name_pos(the_repository->index, name, strlen(name));
if (pos < 0) {
pos = get_ours_cache_pos(name, pos);
if (pos < 0)
continue;
}
- ce = the_index.cache[pos];
+ ce = the_repository->index->cache[pos];
if (!S_ISGITLINK(ce->ce_mode) ||
!file_exists(ce->name) ||
@@ -122,7 +122,7 @@ static int check_local_mod(struct object_id *head, int index_only)
int local_changes = 0;
int staged_changes = 0;
- pos = index_name_pos(&the_index, name, strlen(name));
+ pos = index_name_pos(the_repository->index, name, strlen(name));
if (pos < 0) {
/*
* Skip unmerged entries except for populated submodules
@@ -132,11 +132,11 @@ static int check_local_mod(struct object_id *head, int index_only)
if (pos < 0)
continue;
- if (!S_ISGITLINK(the_index.cache[pos]->ce_mode) ||
+ if (!S_ISGITLINK(the_repository->index->cache[pos]->ce_mode) ||
is_empty_dir(name))
continue;
}
- ce = the_index.cache[pos];
+ ce = the_repository->index->cache[pos];
if (lstat(ce->name, &st) < 0) {
if (!is_missing_file_error(errno))
@@ -173,7 +173,7 @@ static int check_local_mod(struct object_id *head, int index_only)
* Is the index different from the file in the work tree?
* If it's a submodule, is its work tree modified?
*/
- if (ie_match_stat(&the_index, ce, &st, 0) ||
+ if (ie_match_stat(the_repository->index, ce, &st, 0) ||
(S_ISGITLINK(ce->ce_mode) &&
bad_to_remove_submodule(ce->name,
SUBMODULE_REMOVAL_DIE_ON_ERROR |
@@ -301,27 +301,27 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
if (repo_read_index(the_repository) < 0)
die(_("index file corrupt"));
- refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, &pathspec, NULL, NULL);
+ refresh_index(the_repository->index, REFRESH_QUIET|REFRESH_UNMERGED, &pathspec, NULL, NULL);
seen = xcalloc(pathspec.nr, 1);
- if (pathspec_needs_expanded_index(&the_index, &pathspec))
- ensure_full_index(&the_index);
+ if (pathspec_needs_expanded_index(the_repository->index, &pathspec))
+ ensure_full_index(the_repository->index);
- for (i = 0; i < the_index.cache_nr; i++) {
- const struct cache_entry *ce = the_index.cache[i];
+ for (i = 0; i < the_repository->index->cache_nr; i++) {
+ const struct cache_entry *ce = the_repository->index->cache[i];
if (!include_sparse &&
(ce_skip_worktree(ce) ||
- !path_in_sparse_checkout(ce->name, &the_index)))
+ !path_in_sparse_checkout(ce->name, the_repository->index)))
continue;
- if (!ce_path_match(&the_index, ce, &pathspec, seen))
+ if (!ce_path_match(the_repository->index, ce, &pathspec, seen))
continue;
ALLOC_GROW(list.entry, list.nr + 1, list.alloc);
list.entry[list.nr].name = xstrdup(ce->name);
list.entry[list.nr].is_submodule = S_ISGITLINK(ce->ce_mode);
if (list.entry[list.nr++].is_submodule &&
- !is_staging_gitmodules_ok(&the_index))
+ !is_staging_gitmodules_ok(the_repository->index))
die(_("please stage your changes to .gitmodules or stash them to proceed"));
}
@@ -377,7 +377,7 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
if (!force) {
struct object_id oid;
if (repo_get_oid(the_repository, "HEAD", &oid))
- oidclr(&oid);
+ oidclr(&oid, the_repository->hash_algo);
if (check_local_mod(&oid, index_only))
exit(1);
}
@@ -391,7 +391,7 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
if (!quiet)
printf("rm '%s'\n", path);
- if (remove_file_from_index(&the_index, path))
+ if (remove_file_from_index(the_repository->index, path))
die(_("git rm: unable to remove %s"), path);
}
@@ -432,10 +432,10 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
}
strbuf_release(&buf);
if (gitmodules_modified)
- stage_updated_gitmodules(&the_index);
+ stage_updated_gitmodules(the_repository->index);
}
- if (write_locked_index(&the_index, &lock_file,
+ if (write_locked_index(the_repository->index, &lock_file,
COMMIT_LOCK | SKIP_IF_UNCHANGED))
die(_("Unable to write new index file"));
diff --git a/builtin/send-pack.c b/builtin/send-pack.c
index 3df9eaad09..17cae6bbbd 100644
--- a/builtin/send-pack.c
+++ b/builtin/send-pack.c
@@ -336,5 +336,7 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)
/* stable plumbing output; do not modify or localize */
fprintf(stderr, "Everything up-to-date\n");
+ free_refs(remote_refs);
+ free_refs(local_refs);
return ret;
}
diff --git a/builtin/shortlog.c b/builtin/shortlog.c
index 3c7cd2d6ef..5bde7c68c2 100644
--- a/builtin/shortlog.c
+++ b/builtin/shortlog.c
@@ -435,7 +435,7 @@ parse_done:
usage_with_options(shortlog_usage, options);
}
- if (setup_revisions(argc, argv, &rev, NULL) != 1) {
+ if (!nongit && setup_revisions(argc, argv, &rev, NULL) != 1) {
error(_("unrecognized argument: %s"), argv[1]);
usage_with_options(shortlog_usage, options);
}
@@ -460,11 +460,8 @@ parse_done:
else
get_from_rev(&rev, &log);
- release_revisions(&rev);
-
shortlog_output(&log);
- if (log.file != stdout)
- fclose(log.file);
+ release_revisions(&rev);
return 0;
}
diff --git a/builtin/show-branch.c b/builtin/show-branch.c
index b01ec761d2..d72f4cb98d 100644
--- a/builtin/show-branch.c
+++ b/builtin/show-branch.c
@@ -479,13 +479,15 @@ static void snarf_refs(int head, int remotes)
if (head) {
int orig_cnt = ref_name_cnt;
- for_each_ref(append_head_ref, NULL);
+ refs_for_each_ref(get_main_ref_store(the_repository),
+ append_head_ref, NULL);
sort_ref_range(orig_cnt, ref_name_cnt);
}
if (remotes) {
int orig_cnt = ref_name_cnt;
- for_each_ref(append_remote_ref, NULL);
+ refs_for_each_ref(get_main_ref_store(the_repository),
+ append_remote_ref, NULL);
sort_ref_range(orig_cnt, ref_name_cnt);
}
}
@@ -549,7 +551,8 @@ static void append_one_rev(const char *av)
match_ref_pattern = av;
match_ref_slash = count_slashes(av);
- for_each_ref(append_matching_ref, NULL);
+ refs_for_each_ref(get_main_ref_store(the_repository),
+ append_matching_ref, NULL);
if (saved_matches == ref_name_cnt &&
ref_name_cnt < MAX_REVS)
error(_("no matching refs with %s"), av);
@@ -740,9 +743,11 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
if (ac == 0) {
static const char *fake_av[2];
- fake_av[0] = resolve_refdup("HEAD",
- RESOLVE_REF_READING, &oid,
- NULL);
+ fake_av[0] = refs_resolve_refdup(get_main_ref_store(the_repository),
+ "HEAD",
+ RESOLVE_REF_READING,
+ &oid,
+ NULL);
fake_av[1] = NULL;
av = fake_av;
ac = 1;
@@ -815,8 +820,9 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
snarf_refs(all_heads, all_remotes);
}
- head = resolve_refdup("HEAD", RESOLVE_REF_READING,
- &head_oid, NULL);
+ head = refs_resolve_refdup(get_main_ref_store(the_repository), "HEAD",
+ RESOLVE_REF_READING,
+ &head_oid, NULL);
if (with_current_branch && head) {
int has_head = 0;
diff --git a/builtin/show-ref.c b/builtin/show-ref.c
index 1c15421e60..839a5c29f3 100644
--- a/builtin/show-ref.c
+++ b/builtin/show-ref.c
@@ -11,8 +11,8 @@
static const char * const show_ref_usage[] = {
N_("git show-ref [--head] [-d | --dereference]\n"
- " [-s | --hash[=<n>]] [--abbrev[=<n>]] [--tags]\n"
- " [--heads] [--] [<pattern>...]"),
+ " [-s | --hash[=<n>]] [--abbrev[=<n>]] [--branches] [--tags]\n"
+ " [--] [<pattern>...]"),
N_("git show-ref --verify [-q | --quiet] [-d | --dereference]\n"
" [-s | --hash[=<n>]] [--abbrev[=<n>]]\n"
" [--] [<ref>...]"),
@@ -50,7 +50,7 @@ static void show_one(const struct show_one_options *opts,
if (!opts->deref_tags)
return;
- if (!peel_iterated_oid(oid, &peeled)) {
+ if (!peel_iterated_oid(the_repository, oid, &peeled)) {
hex = repo_find_unique_abbrev(the_repository, &peeled, opts->abbrev);
printf("%s %s^{}\n", hex, refname);
}
@@ -129,7 +129,8 @@ static int cmd_show_ref__exclude_existing(const struct exclude_existing_options
char buf[1024];
int patternlen = opts->pattern ? strlen(opts->pattern) : 0;
- for_each_ref(add_existing, &existing_refs);
+ refs_for_each_ref(get_main_ref_store(the_repository), add_existing,
+ &existing_refs);
while (fgets(buf, sizeof(buf), stdin)) {
char *ref;
int len = strlen(buf);
@@ -173,7 +174,7 @@ static int cmd_show_ref__verify(const struct show_one_options *show_one_opts,
struct object_id oid;
if ((starts_with(*refs, "refs/") || refname_is_safe(*refs)) &&
- !read_ref(*refs, &oid)) {
+ !refs_read_ref(get_main_ref_store(the_repository), *refs, &oid)) {
show_one(show_one_opts, *refs, &oid);
}
else if (!show_one_opts->quiet)
@@ -188,7 +189,7 @@ static int cmd_show_ref__verify(const struct show_one_options *show_one_opts,
struct patterns_options {
int show_head;
- int heads_only;
+ int branches_only;
int tags_only;
};
@@ -205,14 +206,20 @@ static int cmd_show_ref__patterns(const struct patterns_options *opts,
show_ref_data.patterns = patterns;
if (opts->show_head)
- head_ref(show_ref, &show_ref_data);
- if (opts->heads_only || opts->tags_only) {
- if (opts->heads_only)
- for_each_fullref_in("refs/heads/", show_ref, &show_ref_data);
+ refs_head_ref(get_main_ref_store(the_repository), show_ref,
+ &show_ref_data);
+ if (opts->branches_only || opts->tags_only) {
+ if (opts->branches_only)
+ refs_for_each_fullref_in(get_main_ref_store(the_repository),
+ "refs/heads/", NULL,
+ show_ref, &show_ref_data);
if (opts->tags_only)
- for_each_fullref_in("refs/tags/", show_ref, &show_ref_data);
+ refs_for_each_fullref_in(get_main_ref_store(the_repository),
+ "refs/tags/", NULL, show_ref,
+ &show_ref_data);
} else {
- for_each_ref(show_ref, &show_ref_data);
+ refs_for_each_ref(get_main_ref_store(the_repository),
+ show_ref, &show_ref_data);
}
if (!show_ref_data.found_match)
return 1;
@@ -286,8 +293,10 @@ int cmd_show_ref(int argc, const char **argv, const char *prefix)
struct show_one_options show_one_opts = {0};
int verify = 0, exists = 0;
const struct option show_ref_options[] = {
- OPT_BOOL(0, "tags", &patterns_opts.tags_only, N_("only show tags (can be combined with heads)")),
- OPT_BOOL(0, "heads", &patterns_opts.heads_only, N_("only show heads (can be combined with tags)")),
+ OPT_BOOL(0, "tags", &patterns_opts.tags_only, N_("only show tags (can be combined with branches)")),
+ OPT_BOOL(0, "branches", &patterns_opts.branches_only, N_("only show branches (can be combined with tags)")),
+ OPT_HIDDEN_BOOL(0, "heads", &patterns_opts.branches_only,
+ N_("deprecated synonym for --branches")),
OPT_BOOL(0, "exists", &exists, N_("check for reference existence without resolving")),
OPT_BOOL(0, "verify", &verify, N_("stricter reference checking, "
"requires exact ref path")),
diff --git a/builtin/sparse-checkout.c b/builtin/sparse-checkout.c
index 0f52e25249..2604ab04df 100644
--- a/builtin/sparse-checkout.c
+++ b/builtin/sparse-checkout.c
@@ -96,10 +96,11 @@ static int sparse_checkout_list(int argc, const char **argv, const char *prefix)
printf("\n");
}
- return 0;
+ string_list_clear(&sl, 0);
+ } else {
+ write_patterns_to_file(stdout, &pl);
}
- write_patterns_to_file(stdout, &pl);
clear_pattern_list(&pl);
return 0;
@@ -205,11 +206,13 @@ static int update_working_directory(struct pattern_list *pl)
struct unpack_trees_options o;
struct lock_file lock_file = LOCK_INIT;
struct repository *r = the_repository;
+ struct pattern_list *old_pl;
/* If no branch has been checked out, there are no updates to make. */
if (is_index_unborn(r->index))
return UPDATE_SPARSITY_SUCCESS;
+ old_pl = r->index->sparse_checkout_patterns;
r->index->sparse_checkout_patterns = pl;
memset(&o, 0, sizeof(o));
@@ -241,7 +244,12 @@ static int update_working_directory(struct pattern_list *pl)
clean_tracked_sparse_directories(r);
- r->index->sparse_checkout_patterns = NULL;
+ if (r->index->sparse_checkout_patterns != pl) {
+ clear_pattern_list(r->index->sparse_checkout_patterns);
+ FREE_AND_NULL(r->index->sparse_checkout_patterns);
+ }
+ r->index->sparse_checkout_patterns = old_pl;
+
return result;
}
@@ -311,6 +319,8 @@ static void write_cone_to_file(FILE *fp, struct pattern_list *pl)
fprintf(fp, "%s/\n", pattern);
free(pattern);
}
+
+ string_list_clear(&sl, 0);
}
static int write_patterns_and_update(struct pattern_list *pl)
@@ -440,7 +450,6 @@ static int sparse_checkout_init(int argc, const char **argv, const char *prefix)
char *sparse_filename;
int res;
struct object_id oid;
- struct strbuf pattern = STRBUF_INIT;
static struct option builtin_sparse_checkout_init_options[] = {
OPT_BOOL(0, "cone", &init_opts.cone_mode,
@@ -471,6 +480,7 @@ static int sparse_checkout_init(int argc, const char **argv, const char *prefix)
/* If we already have a sparse-checkout file, use it. */
if (res >= 0) {
free(sparse_filename);
+ clear_pattern_list(&pl);
return update_working_directory(NULL);
}
@@ -491,10 +501,10 @@ static int sparse_checkout_init(int argc, const char **argv, const char *prefix)
return 0;
}
- strbuf_addstr(&pattern, "/*");
- add_pattern(strbuf_detach(&pattern, NULL), empty_base, 0, &pl, 0);
- strbuf_addstr(&pattern, "!/*/");
- add_pattern(strbuf_detach(&pattern, NULL), empty_base, 0, &pl, 0);
+ free(sparse_filename);
+
+ add_pattern("/*", empty_base, 0, &pl, 0);
+ add_pattern("!/*/", empty_base, 0, &pl, 0);
pl.use_cone_patterns = init_opts.cone_mode;
return write_patterns_and_update(&pl);
@@ -513,6 +523,7 @@ static void insert_recursive_pattern(struct pattern_list *pl, struct strbuf *pat
char *slash = strrchr(e->pattern, '/');
char *oldpattern = e->pattern;
size_t newlen;
+ struct pattern_entry *dup;
if (!slash || slash == e->pattern)
break;
@@ -523,8 +534,14 @@ static void insert_recursive_pattern(struct pattern_list *pl, struct strbuf *pat
e->pattern = xstrndup(oldpattern, newlen);
hashmap_entry_init(&e->ent, fspathhash(e->pattern));
- if (!hashmap_get_entry(&pl->parent_hashmap, e, ent, NULL))
+ dup = hashmap_get_entry(&pl->parent_hashmap, e, ent, NULL);
+ if (!dup) {
hashmap_add(&pl->parent_hashmap, &e->ent);
+ } else {
+ free(e->pattern);
+ free(e);
+ e = dup;
+ }
}
}
@@ -581,15 +598,15 @@ static void add_patterns_from_input(struct pattern_list *pl,
strbuf_to_cone_pattern(&line, pl);
}
}
+ strbuf_release(&line);
} else {
if (file) {
struct strbuf line = STRBUF_INIT;
- while (!strbuf_getline(&line, file)) {
- size_t len;
- char *buf = strbuf_detach(&line, &len);
- add_pattern(buf, empty_base, 0, pl, 0);
- }
+ while (!strbuf_getline(&line, file))
+ add_pattern(line.buf, empty_base, 0, pl, 0);
+
+ strbuf_release(&line);
} else {
for (i = 0; i < argc; i++)
add_pattern(argv[i], empty_base, 0, pl, 0);
@@ -891,7 +908,6 @@ static int sparse_checkout_disable(int argc, const char **argv,
OPT_END(),
};
struct pattern_list pl;
- struct strbuf match_all = STRBUF_INIT;
/*
* We do not exit early if !core_apply_sparse_checkout; due to the
@@ -917,8 +933,7 @@ static int sparse_checkout_disable(int argc, const char **argv,
pl.use_cone_patterns = 0;
core_apply_sparse_checkout = 1;
- strbuf_addstr(&match_all, "/*");
- add_pattern(strbuf_detach(&match_all, NULL), empty_base, 0, &pl, 0);
+ add_pattern("/*", empty_base, 0, &pl, 0);
prepare_repo_settings(the_repository);
the_repository->settings.sparse_index = 0;
@@ -1011,6 +1026,7 @@ static int sparse_checkout_check_rules(int argc, const char **argv, const char *
ret = check_rules(&pl, check_rules_opts.null_termination);
clear_pattern_list(&pl);
+ free(check_rules_opts.rules_file);
return ret;
}
diff --git a/builtin/stash.c b/builtin/stash.c
index 062be1fbc0..46b981c4dd 100644
--- a/builtin/stash.c
+++ b/builtin/stash.c
@@ -1,4 +1,3 @@
-#define USE_THE_INDEX_VARIABLE
#include "builtin.h"
#include "abspath.h"
#include "config.h"
@@ -196,7 +195,7 @@ static int get_stash_info(struct stash_info *info, int argc, const char **argv)
commit = argv[0];
if (!commit) {
- if (!ref_exists(ref_stash)) {
+ if (!refs_ref_exists(get_main_ref_store(the_repository), ref_stash)) {
fprintf_ln(stderr, _("No stash entries found."));
return -1;
}
@@ -244,7 +243,8 @@ static int do_clear_stash(void)
if (repo_get_oid(the_repository, ref_stash, &obj))
return 0;
- return delete_ref(NULL, ref_stash, &obj, 0);
+ return refs_delete_ref(get_main_ref_store(the_repository), NULL,
+ ref_stash, &obj, 0);
}
static int clear_stash(int argc, const char **argv, const char *prefix)
@@ -273,7 +273,7 @@ static int reset_tree(struct object_id *i_tree, int update, int reset)
struct lock_file lock_file = LOCK_INIT;
repo_read_index_preload(the_repository, NULL, 0);
- if (refresh_index(&the_index, REFRESH_QUIET, NULL, NULL, NULL))
+ if (refresh_index(the_repository->index, REFRESH_QUIET, NULL, NULL, NULL))
return -1;
repo_hold_locked_index(the_repository, &lock_file, LOCK_DIE_ON_ERROR);
@@ -287,8 +287,8 @@ static int reset_tree(struct object_id *i_tree, int update, int reset)
init_tree_desc(t, &tree->object.oid, tree->buffer, tree->size);
opts.head_idx = 1;
- opts.src_index = &the_index;
- opts.dst_index = &the_index;
+ opts.src_index = the_repository->index;
+ opts.dst_index = the_repository->index;
opts.merge = 1;
opts.reset = reset ? UNPACK_RESET_PROTECT_UNTRACKED : 0;
opts.update = update;
@@ -299,7 +299,7 @@ static int reset_tree(struct object_id *i_tree, int update, int reset)
if (unpack_trees(nr_trees, t, &opts))
return -1;
- if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
+ if (write_locked_index(the_repository->index, &lock_file, COMMIT_LOCK))
return error(_("unable to write new index file"));
return 0;
@@ -430,7 +430,7 @@ static void unstage_changes_unless_new(struct object_id *orig_tree)
state.force = 1;
state.quiet = 1;
state.refresh_cache = 1;
- state.istate = &the_index;
+ state.istate = the_repository->index;
/*
* Step 1: get a difference between orig_tree (which corresponding
@@ -454,7 +454,7 @@ static void unstage_changes_unless_new(struct object_id *orig_tree)
/* Look up the path's position in the current index. */
p = diff_queued_diff.queue[i];
- pos = index_name_pos(&the_index, p->two->path,
+ pos = index_name_pos(the_repository->index, p->two->path,
strlen(p->two->path));
/*
@@ -465,10 +465,10 @@ static void unstage_changes_unless_new(struct object_id *orig_tree)
* path, but left it out of the working tree, then clear the
* SKIP_WORKTREE bit and write it to the working tree.
*/
- if (pos >= 0 && ce_skip_worktree(the_index.cache[pos])) {
+ if (pos >= 0 && ce_skip_worktree(the_repository->index->cache[pos])) {
struct stat st;
- ce = the_index.cache[pos];
+ ce = the_repository->index->cache[pos];
if (!lstat(ce->name, &st)) {
/* Conflicting path present; relocate it */
struct strbuf new_path = STRBUF_INIT;
@@ -504,12 +504,12 @@ static void unstage_changes_unless_new(struct object_id *orig_tree)
if (pos < 0)
option = ADD_CACHE_OK_TO_ADD;
- ce = make_cache_entry(&the_index,
+ ce = make_cache_entry(the_repository->index,
p->one->mode,
&p->one->oid,
p->one->path,
0, 0);
- add_index_entry(&the_index, ce, option);
+ add_index_entry(the_repository->index, ce, option);
}
}
diff_flush(&diff_opts);
@@ -518,7 +518,7 @@ static void unstage_changes_unless_new(struct object_id *orig_tree)
* Step 4: write the new index to disk
*/
repo_hold_locked_index(the_repository, &lock, LOCK_DIE_ON_ERROR);
- if (write_locked_index(&the_index, &lock,
+ if (write_locked_index(the_repository->index, &lock,
COMMIT_LOCK | SKIP_IF_UNCHANGED))
die(_("could not write index"));
}
@@ -539,7 +539,7 @@ static int do_apply_stash(const char *prefix, struct stash_info *info,
NULL, NULL, NULL))
return error(_("could not write index"));
- if (write_index_as_tree(&c_tree, &the_index, get_index_file(), 0,
+ if (write_index_as_tree(&c_tree, the_repository->index, get_index_file(), 0,
NULL))
return error(_("cannot apply a stash in the middle of a merge"));
@@ -562,14 +562,14 @@ static int do_apply_stash(const char *prefix, struct stash_info *info,
return error(_("conflicts in index. "
"Try without --index."));
- discard_index(&the_index);
+ discard_index(the_repository->index);
repo_read_index(the_repository);
- if (write_index_as_tree(&index_tree, &the_index,
+ if (write_index_as_tree(&index_tree, the_repository->index,
get_index_file(), 0, NULL))
return error(_("could not save index tree"));
reset_head();
- discard_index(&the_index);
+ discard_index(the_repository->index);
repo_read_index(the_repository);
}
}
@@ -687,7 +687,8 @@ static int reject_reflog_ent(struct object_id *ooid UNUSED,
static int reflog_is_empty(const char *refname)
{
- return !for_each_reflog_ent(refname, reject_reflog_ent, NULL);
+ return !refs_for_each_reflog_ent(get_main_ref_store(the_repository),
+ refname, reject_reflog_ent, NULL);
}
static int do_drop_stash(struct stash_info *info, int quiet)
@@ -824,7 +825,7 @@ static int list_stash(int argc, const char **argv, const char *prefix)
git_stash_list_usage,
PARSE_OPT_KEEP_UNKNOWN_OPT);
- if (!ref_exists(ref_stash))
+ if (!refs_ref_exists(get_main_ref_store(the_repository), ref_stash))
return 0;
cp.git_cmd = 1;
@@ -875,8 +876,8 @@ static void diff_include_untracked(const struct stash_info *info, struct diff_op
}
unpack_tree_opt.head_idx = -1;
- unpack_tree_opt.src_index = &the_index;
- unpack_tree_opt.dst_index = &the_index;
+ unpack_tree_opt.src_index = the_repository->index;
+ unpack_tree_opt.dst_index = the_repository->index;
unpack_tree_opt.merge = 1;
unpack_tree_opt.fn = stash_worktree_untracked_merge;
@@ -974,7 +975,9 @@ static int show_stash(int argc, const char **argv, const char *prefix)
log_tree_diff_flush(&rev);
ret = diff_result_code(&rev.diffopt);
+
cleanup:
+ strvec_clear(&revision_args);
strvec_clear(&stash_args);
free_stash_info(&info);
release_revisions(&rev);
@@ -998,10 +1001,10 @@ static int do_store_stash(const struct object_id *w_commit, const char *stash_ms
if (!stash_msg)
stash_msg = "Created via \"git stash store\".";
- if (update_ref(stash_msg, ref_stash, w_commit, NULL,
- REF_FORCE_CREATE_REFLOG,
- quiet ? UPDATE_REFS_QUIET_ON_ERR :
- UPDATE_REFS_MSG_ON_ERR)) {
+ if (refs_update_ref(get_main_ref_store(the_repository), stash_msg, ref_stash, w_commit, NULL,
+ REF_FORCE_CREATE_REFLOG,
+ quiet ? UPDATE_REFS_QUIET_ON_ERR :
+ UPDATE_REFS_MSG_ON_ERR)) {
if (!quiet) {
fprintf_ln(stderr, _("Cannot update %s with %s"),
ref_stash, oid_to_hex(w_commit));
@@ -1017,13 +1020,14 @@ static int store_stash(int argc, const char **argv, const char *prefix)
int quiet = 0;
const char *stash_msg = NULL;
struct object_id obj;
- struct object_context dummy;
+ struct object_context dummy = {0};
struct option options[] = {
OPT__QUIET(&quiet, N_("be quiet")),
OPT_STRING('m', "message", &stash_msg, "message",
N_("stash message")),
OPT_END()
};
+ int ret;
argc = parse_options(argc, argv, prefix, options,
git_stash_store_usage,
@@ -1042,10 +1046,15 @@ static int store_stash(int argc, const char **argv, const char *prefix)
if (!quiet)
fprintf_ln(stderr, _("Cannot update %s with %s"),
ref_stash, argv[0]);
- return -1;
+ ret = -1;
+ goto out;
}
- return do_store_stash(&obj, stash_msg, quiet);
+ ret = do_store_stash(&obj, stash_msg, quiet);
+
+out:
+ object_context_release(&dummy);
+ return ret;
}
static void add_pathspecs(struct strvec *args,
@@ -1205,8 +1214,8 @@ static int stash_staged(struct stash_info *info, struct strbuf *out_patch,
}
cp_diff_tree.git_cmd = 1;
- strvec_pushl(&cp_diff_tree.args, "diff-tree", "-p", "-U1", "HEAD",
- oid_to_hex(&info->w_tree), "--", NULL);
+ strvec_pushl(&cp_diff_tree.args, "diff-tree", "-p", "--binary",
+ "-U1", "HEAD", oid_to_hex(&info->w_tree), "--", NULL);
if (pipe_command(&cp_diff_tree, NULL, 0, out_patch, 0, NULL, 0)) {
ret = -1;
goto done;
@@ -1384,7 +1393,8 @@ static int do_create_stash(const struct pathspec *ps, struct strbuf *stash_msg_b
goto done;
}
- branch_ref = resolve_ref_unsafe("HEAD", 0, NULL, &flags);
+ branch_ref = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
+ "HEAD", 0, NULL, &flags);
if (flags & REF_ISSYMREF)
skip_prefix(branch_ref, "refs/heads/", &branch_name);
head_short_sha1 = repo_find_unique_abbrev(the_repository,
@@ -1395,7 +1405,7 @@ static int do_create_stash(const struct pathspec *ps, struct strbuf *stash_msg_b
strbuf_addf(&commit_tree_label, "index on %s\n", msg.buf);
commit_list_insert(head_commit, &parents);
- if (write_index_as_tree(&info->i_tree, &the_index, get_index_file(), 0,
+ if (write_index_as_tree(&info->i_tree, the_repository->index, get_index_file(), 0,
NULL) ||
commit_tree(commit_tree_label.buf, commit_tree_label.len,
&info->i_tree, parents, &info->i_commit, NULL, NULL)) {
@@ -1406,6 +1416,9 @@ static int do_create_stash(const struct pathspec *ps, struct strbuf *stash_msg_b
goto done;
}
+ free_commit_list(parents);
+ parents = NULL;
+
if (include_untracked) {
if (save_untracked_files(info, &msg, untracked_files)) {
if (!quiet)
@@ -1451,11 +1464,6 @@ static int do_create_stash(const struct pathspec *ps, struct strbuf *stash_msg_b
else
strbuf_insertf(stash_msg_buf, 0, "On %s: ", branch_name);
- /*
- * `parents` will be empty after calling `commit_tree()`, so there is
- * no need to call `free_commit_list()`
- */
- parents = NULL;
if (untracked_commit_option)
commit_list_insert(lookup_commit(the_repository,
&info->u_commit),
@@ -1477,6 +1485,7 @@ done:
strbuf_release(&commit_tree_label);
strbuf_release(&msg);
strbuf_release(&untracked_files);
+ free_commit_list(parents);
return ret;
}
@@ -1540,9 +1549,9 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q
char *ps_matched = xcalloc(ps->nr, 1);
/* TODO: audit for interaction with sparse-index. */
- ensure_full_index(&the_index);
- for (i = 0; i < the_index.cache_nr; i++)
- ce_path_match(&the_index, the_index.cache[i], ps,
+ ensure_full_index(the_repository->index);
+ for (i = 0; i < the_repository->index->cache_nr; i++)
+ ce_path_match(the_repository->index, the_repository->index->cache[i], ps,
ps_matched);
if (report_path_error(ps_matched, ps)) {
@@ -1566,7 +1575,7 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q
goto done;
}
- if (!reflog_exists(ref_stash) && do_clear_stash()) {
+ if (!refs_reflog_exists(get_main_ref_store(the_repository), ref_stash) && do_clear_stash()) {
ret = -1;
if (!quiet)
fprintf_ln(stderr, _("Cannot initialize stash"));
@@ -1612,7 +1621,7 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q
goto done;
}
}
- discard_index(&the_index);
+ discard_index(the_repository->index);
if (ps->nr) {
struct child_process cp_add = CHILD_PROCESS_INIT;
struct child_process cp_diff = CHILD_PROCESS_INIT;
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index e4e18adb57..f1218a1995 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -1,4 +1,3 @@
-#define USE_THE_INDEX_VARIABLE
#include "builtin.h"
#include "abspath.h"
#include "environment.h"
@@ -207,18 +206,18 @@ static int module_list_compute(const char **argv,
if (repo_read_index(the_repository) < 0)
die(_("index file corrupt"));
- for (i = 0; i < the_index.cache_nr; i++) {
- const struct cache_entry *ce = the_index.cache[i];
+ for (i = 0; i < the_repository->index->cache_nr; i++) {
+ const struct cache_entry *ce = the_repository->index->cache[i];
- if (!match_pathspec(&the_index, pathspec, ce->name, ce_namelen(ce),
+ if (!match_pathspec(the_repository->index, pathspec, ce->name, ce_namelen(ce),
0, ps_matched, 1) ||
!S_ISGITLINK(ce->ce_mode))
continue;
ALLOC_GROW(list->entries, list->nr + 1, list->alloc);
list->entries[list->nr++] = ce;
- while (i + 1 < the_index.cache_nr &&
- !strcmp(ce->name, the_index.cache[i + 1]->name))
+ while (i + 1 < the_repository->index->cache_nr &&
+ !strcmp(ce->name, the_repository->index->cache[i + 1]->name))
/*
* Skip entries with the same name in different stages
* to make sure an entry is returned only once.
@@ -257,11 +256,9 @@ static void module_list_active(struct module_list *list)
static char *get_up_path(const char *path)
{
- int i;
struct strbuf sb = STRBUF_INIT;
- for (i = count_slashes(path); i; i--)
- strbuf_addstr(&sb, "../");
+ strbuf_addstrings(&sb, "../", count_slashes(path));
/*
* Check if 'path' ends with slash or not
@@ -303,6 +300,9 @@ static void runcommand_in_submodule_cb(const struct cache_entry *list_item,
struct child_process cp = CHILD_PROCESS_INIT;
char *displaypath;
+ if (validate_submodule_path(path) < 0)
+ exit(128);
+
displaypath = get_submodule_displaypath(path, info->prefix,
info->super_prefix);
@@ -376,8 +376,7 @@ static void runcommand_in_submodule_cb(const struct cache_entry *list_item,
strvec_pushl(&cpr.args, "submodule--helper", "foreach", "--recursive",
NULL);
- strvec_pushl(&cpr.args, "--super-prefix", NULL);
- strvec_pushf(&cpr.args, "%s/", displaypath);
+ strvec_pushf(&cpr.args, "--super-prefix=%s/", displaypath);
if (info->quiet)
strvec_push(&cpr.args, "--quiet");
@@ -634,6 +633,9 @@ static void status_submodule(const char *path, const struct object_id *ce_oid,
.free_removed_argv_elements = 1,
};
+ if (validate_submodule_path(path) < 0)
+ exit(128);
+
if (!submodule_from_path(the_repository, null_oid(), path))
die(_("no submodule mapping found in .gitmodules for path '%s'"),
path);
@@ -674,7 +676,8 @@ static void status_submodule(const char *path, const struct object_id *ce_oid,
displaypath);
} else if (!(flags & OPT_CACHED)) {
struct object_id oid;
- struct ref_store *refs = get_submodule_ref_store(path);
+ struct ref_store *refs = repo_get_submodule_ref_store(the_repository,
+ path);
if (!refs) {
print_status(flags, '-', path, ce_oid, displaypath);
@@ -698,8 +701,7 @@ static void status_submodule(const char *path, const struct object_id *ce_oid,
strvec_pushl(&cpr.args, "submodule--helper", "status",
"--recursive", NULL);
- strvec_push(&cpr.args, "--super-prefix");
- strvec_pushf(&cpr.args, "%s/", displaypath);
+ strvec_pushf(&cpr.args, "--super-prefix=%s/", displaypath);
if (flags & OPT_CACHED)
strvec_push(&cpr.args, "--cached");
@@ -898,7 +900,8 @@ static void generate_submodule_summary(struct summary_cb *info,
if (!info->cached && oideq(&p->oid_dst, null_oid())) {
if (S_ISGITLINK(p->mod_dst)) {
- struct ref_store *refs = get_submodule_ref_store(p->sm_path);
+ struct ref_store *refs = repo_get_submodule_ref_store(the_repository,
+ p->sm_path);
if (refs)
refs_head_ref(refs, handle_submodule_head_ref, &p->oid_dst);
@@ -907,7 +910,7 @@ static void generate_submodule_summary(struct summary_cb *info,
int fd = open(p->sm_path, O_RDONLY);
if (fd < 0 || fstat(fd, &st) < 0 ||
- index_fd(&the_index, &p->oid_dst, fd, &st, OBJ_BLOB,
+ index_fd(the_repository->index, &p->oid_dst, fd, &st, OBJ_BLOB,
p->sm_path, 0))
error(_("couldn't hash object from '%s'"), p->sm_path);
} else {
@@ -1238,6 +1241,9 @@ static void sync_submodule(const char *path, const char *prefix,
if (!is_submodule_active(the_repository, path))
return;
+ if (validate_submodule_path(path) < 0)
+ exit(128);
+
sub = submodule_from_path(the_repository, null_oid(), path);
if (sub && sub->url) {
@@ -1296,9 +1302,7 @@ static void sync_submodule(const char *path, const char *prefix,
strvec_pushl(&cpr.args, "submodule--helper", "sync",
"--recursive", NULL);
- strvec_push(&cpr.args, "--super-prefix");
- strvec_pushf(&cpr.args, "%s/", displaypath);
-
+ strvec_pushf(&cpr.args, "--super-prefix=%s/", displaypath);
if (flags & OPT_QUIET)
strvec_push(&cpr.args, "--quiet");
@@ -1381,6 +1385,9 @@ static void deinit_submodule(const char *path, const char *prefix,
struct strbuf sb_config = STRBUF_INIT;
char *sub_git_dir = xstrfmt("%s/.git", path);
+ if (validate_submodule_path(path) < 0)
+ exit(128);
+
sub = submodule_from_path(the_repository, null_oid(), path);
if (!sub || !sub->name)
@@ -1662,16 +1669,42 @@ static char *clone_submodule_sm_gitdir(const char *name)
return sm_gitdir;
}
+static int dir_contains_only_dotgit(const char *path)
+{
+ DIR *dir = opendir(path);
+ struct dirent *e;
+ int ret = 1;
+
+ if (!dir)
+ return 0;
+
+ e = readdir_skip_dot_and_dotdot(dir);
+ if (!e)
+ ret = 0;
+ else if (strcmp(DEFAULT_GIT_DIR_ENVIRONMENT, e->d_name) ||
+ (e = readdir_skip_dot_and_dotdot(dir))) {
+ error("unexpected item '%s' in '%s'", e->d_name, path);
+ ret = 0;
+ }
+
+ closedir(dir);
+ return ret;
+}
+
static int clone_submodule(const struct module_clone_data *clone_data,
struct string_list *reference)
{
char *p;
char *sm_gitdir = clone_submodule_sm_gitdir(clone_data->name);
char *sm_alternate = NULL, *error_strategy = NULL;
+ struct stat st;
struct child_process cp = CHILD_PROCESS_INIT;
const char *clone_data_path = clone_data->path;
char *to_free = NULL;
+ if (validate_submodule_path(clone_data_path) < 0)
+ exit(128);
+
if (!is_absolute_path(clone_data->path))
clone_data_path = to_free = xstrfmt("%s/%s", get_git_work_tree(),
clone_data->path);
@@ -1681,6 +1714,10 @@ static int clone_submodule(const struct module_clone_data *clone_data,
"git dir"), sm_gitdir);
if (!file_exists(sm_gitdir)) {
+ if (clone_data->require_init && !stat(clone_data_path, &st) &&
+ !is_empty_dir(clone_data_path))
+ die(_("directory not empty: '%s'"), clone_data_path);
+
if (safe_create_leading_directories_const(sm_gitdir) < 0)
die(_("could not create directory '%s'"), sm_gitdir);
@@ -1725,10 +1762,18 @@ static int clone_submodule(const struct module_clone_data *clone_data,
if(run_command(&cp))
die(_("clone of '%s' into submodule path '%s' failed"),
clone_data->url, clone_data_path);
+
+ if (clone_data->require_init && !stat(clone_data_path, &st) &&
+ !dir_contains_only_dotgit(clone_data_path)) {
+ char *dot_git = xstrfmt("%s/.git", clone_data_path);
+ unlink(dot_git);
+ free(dot_git);
+ die(_("directory not empty: '%s'"), clone_data_path);
+ }
} else {
char *path;
- if (clone_data->require_init && !access(clone_data_path, X_OK) &&
+ if (clone_data->require_init && !stat(clone_data_path, &st) &&
!is_empty_dir(clone_data_path))
die(_("directory not empty: '%s'"), clone_data_path);
if (safe_create_leading_directories_const(clone_data_path) < 0)
@@ -1738,6 +1783,23 @@ static int clone_submodule(const struct module_clone_data *clone_data,
free(path);
}
+ /*
+ * We already performed this check at the beginning of this function,
+ * before cloning the objects. This tries to detect racy behavior e.g.
+ * in parallel clones, where another process could easily have made the
+ * gitdir nested _after_ it was created.
+ *
+ * To prevent further harm coming from this unintentionally-nested
+ * gitdir, let's disable it by deleting the `HEAD` file.
+ */
+ if (validate_submodule_git_dir(sm_gitdir, clone_data->name) < 0) {
+ char *head = xstrfmt("%s/HEAD", sm_gitdir);
+ unlink(head);
+ free(head);
+ die(_("refusing to create/use '%s' in another submodule's "
+ "git dir"), sm_gitdir);
+ }
+
connect_work_tree_and_git_dir(clone_data_path, sm_gitdir, 0);
p = git_pathdup_submodule(clone_data_path, "config");
@@ -2390,7 +2452,9 @@ static int remote_submodule_branch(const char *path, const char **branch)
}
if (!strcmp(*branch, ".")) {
- const char *refname = resolve_ref_unsafe("HEAD", 0, NULL, NULL);
+ const char *refname = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
+ "HEAD", 0, NULL,
+ NULL);
if (!refname)
return die_message(_("No such ref: %s"), "HEAD");
@@ -2466,10 +2530,9 @@ static void update_data_to_args(const struct update_data *update_data,
enum submodule_update_type update_type = update_data->update_default;
strvec_pushl(args, "submodule--helper", "update", "--recursive", NULL);
- if (update_data->displaypath) {
- strvec_push(args, "--super-prefix");
- strvec_pushf(args, "%s/", update_data->displaypath);
- }
+ if (update_data->displaypath)
+ strvec_pushf(args, "--super-prefix=%s/",
+ update_data->displaypath);
strvec_pushf(args, "--jobs=%d", update_data->max_jobs);
if (update_data->quiet)
strvec_push(args, "--quiet");
@@ -2517,6 +2580,9 @@ static int update_submodule(struct update_data *update_data)
{
int ret;
+ if (validate_submodule_path(update_data->sm_path) < 0)
+ return -1;
+
ret = determine_submodule_update_strategy(the_repository,
update_data->just_cloned,
update_data->sm_path,
@@ -2527,7 +2593,8 @@ static int update_submodule(struct update_data *update_data)
if (update_data->just_cloned)
oidcpy(&update_data->suboid, null_oid());
- else if (resolve_gitlink_ref(update_data->sm_path, "HEAD", &update_data->suboid))
+ else if (repo_resolve_gitlink_ref(the_repository, update_data->sm_path,
+ "HEAD", &update_data->suboid))
return die_message(_("Unable to find current revision in submodule path '%s'"),
update_data->displaypath);
@@ -2554,7 +2621,8 @@ static int update_submodule(struct update_data *update_data)
update_data->sm_path);
}
- if (resolve_gitlink_ref(update_data->sm_path, remote_ref, &update_data->oid))
+ if (repo_resolve_gitlink_ref(the_repository, update_data->sm_path,
+ remote_ref, &update_data->oid))
return die_message(_("Unable to find %s revision in submodule path '%s'"),
remote_ref, update_data->sm_path);
@@ -2624,12 +2692,21 @@ static int update_submodules(struct update_data *update_data)
for (i = 0; i < suc.update_clone_nr; i++) {
struct update_clone_data ucd = suc.update_clone[i];
- int code;
+ int code = 128;
oidcpy(&update_data->oid, &ucd.oid);
update_data->just_cloned = ucd.just_cloned;
update_data->sm_path = ucd.sub->path;
+ /*
+ * Verify that the submodule path does not contain any
+ * symlinks; if it does, it might have been tampered with.
+ * TODO: allow exempting it via
+ * `safe.submodule.path` or something
+ */
+ if (validate_submodule_path(update_data->sm_path) < 0)
+ goto fail;
+
code = ensure_core_worktree(update_data->sm_path);
if (code)
goto fail;
@@ -2796,7 +2873,8 @@ static int push_check(int argc, const char **argv, const char *prefix UNUSED)
argv++;
argc--;
/* Get the submodule's head ref and determine if it is detached */
- head = resolve_refdup("HEAD", 0, &head_oid, NULL);
+ head = refs_resolve_refdup(get_main_ref_store(the_repository), "HEAD",
+ 0, &head_oid, NULL);
if (!head)
die(_("Failed to resolve HEAD as a valid ref."));
if (!strcmp(head, "HEAD"))
@@ -3243,21 +3321,21 @@ static void die_on_index_match(const char *path, int force)
char *ps_matched = xcalloc(ps.nr, 1);
/* TODO: audit for interaction with sparse-index. */
- ensure_full_index(&the_index);
+ ensure_full_index(the_repository->index);
/*
* Since there is only one pathspec, we just need to
* check ps_matched[0] to know if a cache entry matched.
*/
- for (i = 0; i < the_index.cache_nr; i++) {
- ce_path_match(&the_index, the_index.cache[i], &ps,
+ for (i = 0; i < the_repository->index->cache_nr; i++) {
+ ce_path_match(the_repository->index, the_repository->index->cache[i], &ps,
ps_matched);
if (ps_matched[0]) {
if (!force)
die(_("'%s' already exists in the index"),
path);
- if (!S_ISGITLINK(the_index.cache[i]->ce_mode))
+ if (!S_ISGITLINK(the_repository->index->cache[i]->ce_mode))
die(_("'%s' already exists in the index "
"and is not a submodule"), path);
break;
@@ -3274,7 +3352,7 @@ static void die_on_repo_without_commits(const char *path)
strbuf_addstr(&sb, path);
if (is_nonbare_repository_dir(&sb)) {
struct object_id oid;
- if (resolve_gitlink_ref(path, "HEAD", &oid) < 0)
+ if (repo_resolve_gitlink_ref(the_repository, path, "HEAD", &oid) < 0)
die(_("'%s' does not have a commit checked out"), path);
}
strbuf_release(&sb);
@@ -3356,6 +3434,9 @@ static int module_add(int argc, const char **argv, const char *prefix)
normalize_path_copy(add_data.sm_path, add_data.sm_path);
strip_dir_trailing_slashes(add_data.sm_path);
+ if (validate_submodule_path(add_data.sm_path) < 0)
+ exit(128);
+
die_on_index_match(add_data.sm_path, force);
die_on_repo_without_commits(add_data.sm_path);
diff --git a/builtin/symbolic-ref.c b/builtin/symbolic-ref.c
index c9defe4d2e..81abdd170f 100644
--- a/builtin/symbolic-ref.c
+++ b/builtin/symbolic-ref.c
@@ -18,7 +18,8 @@ static int check_symref(const char *HEAD, int quiet, int shorten, int recurse, i
const char *refname;
resolve_flags = (recurse ? 0 : RESOLVE_REF_NO_RECURSE);
- refname = resolve_ref_unsafe(HEAD, resolve_flags, NULL, &flag);
+ refname = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
+ HEAD, resolve_flags, NULL, &flag);
if (!refname)
die("No such ref: %s", HEAD);
@@ -31,7 +32,9 @@ static int check_symref(const char *HEAD, int quiet, int shorten, int recurse, i
if (print) {
char *to_free = NULL;
if (shorten)
- refname = to_free = shorten_unambiguous_ref(refname, 0);
+ refname = to_free = refs_shorten_unambiguous_ref(get_main_ref_store(the_repository),
+ refname,
+ 0);
puts(refname);
free(to_free);
}
@@ -66,7 +69,8 @@ int cmd_symbolic_ref(int argc, const char **argv, const char *prefix)
die("Cannot delete %s, not a symbolic ref", argv[0]);
if (!strcmp(argv[0], "HEAD"))
die("deleting '%s' is not allowed", argv[0]);
- return delete_ref(NULL, argv[0], NULL, REF_NO_DEREF);
+ return refs_delete_ref(get_main_ref_store(the_repository),
+ NULL, argv[0], NULL, REF_NO_DEREF);
}
switch (argc) {
@@ -79,7 +83,8 @@ int cmd_symbolic_ref(int argc, const char **argv, const char *prefix)
die("Refusing to point HEAD outside of refs/");
if (check_refname_format(argv[1], REFNAME_ALLOW_ONELEVEL) < 0)
die("Refusing to set '%s' to invalid ref '%s'", argv[0], argv[1]);
- ret = !!create_symref(argv[0], argv[1], msg);
+ ret = !!refs_update_symref(get_main_ref_store(the_repository),
+ argv[0], argv[1], msg);
break;
default:
usage_with_options(git_symbolic_ref_usage, options);
diff --git a/builtin/tag.c b/builtin/tag.c
index 9a33cb50b4..a1fb218512 100644
--- a/builtin/tag.c
+++ b/builtin/tag.c
@@ -28,9 +28,11 @@
#include "date.h"
#include "write-or-die.h"
#include "object-file-convert.h"
+#include "trailer.h"
static const char * const git_tag_usage[] = {
N_("git tag [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>] [-e]\n"
+ " [(--trailer <token>[(=|:)<value>])...]\n"
" <tagname> [<commit> | <object>]"),
N_("git tag -d <tagname>..."),
N_("git tag [-n[<num>]] -l [--contains <commit>] [--no-contains <commit>]\n"
@@ -87,7 +89,7 @@ static int for_each_tag_name(const char **argv, each_tag_name_fn fn,
for (p = argv; *p; p++) {
strbuf_reset(&ref);
strbuf_addf(&ref, "refs/tags/%s", *p);
- if (read_ref(ref.buf, &oid)) {
+ if (refs_read_ref(get_main_ref_store(the_repository), ref.buf, &oid)) {
error(_("tag '%s' not found."), *p);
had_error = 1;
continue;
@@ -116,13 +118,13 @@ static int delete_tags(const char **argv)
struct string_list_item *item;
result = for_each_tag_name(argv, collect_tags, (void *)&refs_to_delete);
- if (delete_refs(NULL, &refs_to_delete, REF_NO_DEREF))
+ if (refs_delete_refs(get_main_ref_store(the_repository), NULL, &refs_to_delete, REF_NO_DEREF))
result = 1;
for_each_string_list_item(item, &refs_to_delete) {
const char *name = item->string;
struct object_id *oid = item->util;
- if (!ref_exists(name))
+ if (!refs_ref_exists(get_main_ref_store(the_repository), name))
printf(_("Deleted tag '%s' (was %s)\n"),
item->string + 10,
repo_find_unique_abbrev(the_repository, oid, DEFAULT_ABBREV));
@@ -290,10 +292,12 @@ 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, char *path)
+ struct object_id *prev, struct object_id *result,
+ struct strvec *trailer_args, char *path)
{
enum object_type type;
struct strbuf header = STRBUF_INIT;
+ int should_edit;
type = oid_object_info(the_repository, object, NULL);
if (type <= OBJ_NONE)
@@ -313,13 +317,15 @@ static void create_tag(const struct object_id *object, const char *object_ref,
tag,
git_committer_info(IDENT_STRICT));
- if (!opt->message_given || opt->use_editor) {
+ should_edit = opt->use_editor || !opt->message_given;
+ if (should_edit || trailer_args->nr) {
int fd;
/* write the template message before editing: */
fd = xopen(path, O_CREAT | O_TRUNC | O_WRONLY, 0600);
- if (opt->message_given) {
+ if (opt->message_given && buf->len) {
+ strbuf_complete(buf, '\n');
write_or_die(fd, buf->buf, buf->len);
strbuf_reset(buf);
} else if (!is_null_oid(prev)) {
@@ -338,10 +344,19 @@ static void create_tag(const struct object_id *object, const char *object_ref,
}
close(fd);
- if (launch_editor(path, buf, NULL)) {
- fprintf(stderr,
- _("Please supply the message using either -m or -F option.\n"));
- exit(1);
+ if (trailer_args->nr && amend_file_with_trailers(path, trailer_args))
+ die(_("unable to pass trailers to --trailers"));
+
+ if (should_edit) {
+ if (launch_editor(path, buf, NULL)) {
+ fprintf(stderr,
+ _("Please supply the message using either -m or -F option.\n"));
+ exit(1);
+ }
+ } else if (trailer_args->nr) {
+ strbuf_reset(buf);
+ if (strbuf_read_file(buf, path, 0) < 0)
+ die_errno(_("failed to read '%s'"), path);
}
}
@@ -463,6 +478,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
struct ref_sorting *sorting;
struct string_list sorting_options = STRING_LIST_INIT_DUP;
struct ref_format format = REF_FORMAT_INIT;
+ struct strvec trailer_args = STRVEC_INIT;
int icase = 0;
int edit_flag = 0;
struct option options[] = {
@@ -479,6 +495,8 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
OPT_CALLBACK_F('m', "message", &msg, N_("message"),
N_("tag message"), PARSE_OPT_NONEG, parse_msg_arg),
OPT_FILENAME('F', "file", &msgfile, N_("read message from file")),
+ OPT_PASSTHRU_ARGV(0, "trailer", &trailer_args, N_("trailer"),
+ N_("add custom trailer(s)"), PARSE_OPT_NONEG),
OPT_BOOL('e', "edit", &edit_flag, N_("force edit of tag message")),
OPT_BOOL('s', "sign", &opt.sign, N_("annotated and GPG-signed tag")),
OPT_CLEANUP(&cleanup_arg),
@@ -548,7 +566,8 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
opt.sign = 1;
set_signing_key(keyid);
}
- create_tag_object = (opt.sign || annotate || msg.given || msgfile);
+ create_tag_object = (opt.sign || annotate || msg.given || msgfile ||
+ edit_flag || trailer_args.nr);
if ((create_tag_object || force) && (cmdmode != 0))
usage_with_options(git_tag_usage, options);
@@ -630,8 +649,8 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
if (strbuf_check_tag_ref(&ref, tag))
die(_("'%s' is not a valid tag name."), tag);
- if (read_ref(ref.buf, &prev))
- oidclr(&prev);
+ if (refs_read_ref(get_main_ref_store(the_repository), ref.buf, &prev))
+ oidclr(&prev, the_repository->hash_algo);
else if (!force)
die(_("tag '%s' already exists"), tag);
@@ -654,12 +673,14 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
opt.sign = 1;
path = git_pathdup("TAG_EDITMSG");
create_tag(&object, object_ref, tag, &buf, &opt, &prev, &object,
- path);
+ &trailer_args, path);
}
- transaction = ref_transaction_begin(&err);
+ transaction = ref_store_transaction_begin(get_main_ref_store(the_repository),
+ &err);
if (!transaction ||
ref_transaction_update(transaction, ref.buf, &object, &prev,
+ NULL, NULL,
create_reflog ? REF_FORCE_CREATE_REFLOG : 0,
reflog_msg.buf, &err) ||
ref_transaction_commit(transaction, &err)) {
@@ -686,6 +707,7 @@ cleanup:
strbuf_release(&reflog_msg);
strbuf_release(&msg.buf);
strbuf_release(&err);
+ strvec_clear(&trailer_args);
free(msgfile);
return ret;
}
diff --git a/builtin/unpack-objects.c b/builtin/unpack-objects.c
index f1c85a00ae..08fa2a7a74 100644
--- a/builtin/unpack-objects.c
+++ b/builtin/unpack-objects.c
@@ -439,7 +439,7 @@ static void unpack_delta_entry(enum object_type type, unsigned long delta_size,
struct object_id base_oid;
if (type == OBJ_REF_DELTA) {
- oidread(&base_oid, fill(the_hash_algo->rawsz));
+ oidread(&base_oid, fill(the_hash_algo->rawsz), the_repository->hash_algo);
use(the_hash_algo->rawsz);
delta_data = get_data(delta_size);
if (!delta_data)
@@ -451,7 +451,7 @@ static void unpack_delta_entry(enum object_type type, unsigned long delta_size,
return; /* we are done */
else {
/* cannot resolve yet --- queue it */
- oidclr(&obj_list[nr].oid);
+ oidclr(&obj_list[nr].oid, the_repository->hash_algo);
add_delta_to_list(nr, &base_oid, 0, delta_data, delta_size);
return;
}
@@ -500,7 +500,7 @@ static void unpack_delta_entry(enum object_type type, unsigned long delta_size,
* The delta base object is itself a delta that
* has not been resolved yet.
*/
- oidclr(&obj_list[nr].oid);
+ oidclr(&obj_list[nr].oid, the_repository->hash_algo);
add_delta_to_list(nr, null_oid(), base_offset,
delta_data, delta_size);
return;
@@ -674,7 +674,8 @@ int cmd_unpack_objects(int argc, const char **argv, const char *prefix UNUSED)
if (fsck_finish(&fsck_options))
die(_("fsck error in pack objects"));
}
- if (!hasheq(fill(the_hash_algo->rawsz), oid.hash))
+ if (!hasheq(fill(the_hash_algo->rawsz), oid.hash,
+ the_repository->hash_algo))
die("final sha1 did not match");
use(the_hash_algo->rawsz);
diff --git a/builtin/update-index.c b/builtin/update-index.c
index 7bcaa1476c..d343416ae2 100644
--- a/builtin/update-index.c
+++ b/builtin/update-index.c
@@ -3,7 +3,7 @@
*
* Copyright (C) Linus Torvalds, 2005
*/
-#define USE_THE_INDEX_VARIABLE
+
#include "builtin.h"
#include "bulk-checkin.h"
#include "config.h"
@@ -247,16 +247,16 @@ done:
static int mark_ce_flags(const char *path, int flag, int mark)
{
int namelen = strlen(path);
- int pos = index_name_pos(&the_index, path, namelen);
+ int pos = index_name_pos(the_repository->index, path, namelen);
if (0 <= pos) {
- mark_fsmonitor_invalid(&the_index, the_index.cache[pos]);
+ mark_fsmonitor_invalid(the_repository->index, the_repository->index->cache[pos]);
if (mark)
- the_index.cache[pos]->ce_flags |= flag;
+ the_repository->index->cache[pos]->ce_flags |= flag;
else
- the_index.cache[pos]->ce_flags &= ~flag;
- the_index.cache[pos]->ce_flags |= CE_UPDATE_IN_BASE;
- cache_tree_invalidate_path(&the_index, path);
- the_index.cache_changed |= CE_ENTRY_CHANGED;
+ the_repository->index->cache[pos]->ce_flags &= ~flag;
+ the_repository->index->cache[pos]->ce_flags |= CE_UPDATE_IN_BASE;
+ cache_tree_invalidate_path(the_repository->index, path);
+ the_repository->index->cache_changed |= CE_ENTRY_CHANGED;
return 0;
}
return -1;
@@ -266,7 +266,7 @@ static int remove_one_path(const char *path)
{
if (!allow_remove)
return error("%s: does not exist and --remove not passed", path);
- if (remove_file_from_index(&the_index, path))
+ if (remove_file_from_index(the_repository->index, path))
return error("%s: cannot remove from the index", path);
return 0;
}
@@ -291,24 +291,24 @@ static int add_one_path(const struct cache_entry *old, const char *path, int len
struct cache_entry *ce;
/* Was the old index entry already up-to-date? */
- if (old && !ce_stage(old) && !ie_match_stat(&the_index, old, st, 0))
+ if (old && !ce_stage(old) && !ie_match_stat(the_repository->index, old, st, 0))
return 0;
- ce = make_empty_cache_entry(&the_index, len);
+ ce = make_empty_cache_entry(the_repository->index, len);
memcpy(ce->name, path, len);
ce->ce_flags = create_ce_flags(0);
ce->ce_namelen = len;
- fill_stat_cache_info(&the_index, ce, st);
+ fill_stat_cache_info(the_repository->index, ce, st);
ce->ce_mode = ce_mode_from_stat(old, st->st_mode);
- if (index_path(&the_index, &ce->oid, path, st,
+ if (index_path(the_repository->index, &ce->oid, path, st,
info_only ? 0 : HASH_WRITE_OBJECT)) {
discard_cache_entry(ce);
return -1;
}
option = allow_add ? ADD_CACHE_OK_TO_ADD : 0;
option |= allow_replace ? ADD_CACHE_OK_TO_REPLACE : 0;
- if (add_index_entry(&the_index, ce, option)) {
+ if (add_index_entry(the_repository->index, ce, option)) {
discard_cache_entry(ce);
return error("%s: cannot add to the index - missing --add option?", path);
}
@@ -341,15 +341,16 @@ static int add_one_path(const struct cache_entry *old, const char *path, int len
static int process_directory(const char *path, int len, struct stat *st)
{
struct object_id oid;
- int pos = index_name_pos(&the_index, path, len);
+ int pos = index_name_pos(the_repository->index, path, len);
/* Exact match: file or existing gitlink */
if (pos >= 0) {
- const struct cache_entry *ce = the_index.cache[pos];
+ const struct cache_entry *ce = the_repository->index->cache[pos];
if (S_ISGITLINK(ce->ce_mode)) {
/* Do nothing to the index if there is no HEAD! */
- if (resolve_gitlink_ref(path, "HEAD", &oid) < 0)
+ if (repo_resolve_gitlink_ref(the_repository, path,
+ "HEAD", &oid) < 0)
return 0;
return add_one_path(ce, path, len, st);
@@ -360,8 +361,8 @@ static int process_directory(const char *path, int len, struct stat *st)
/* Inexact match: is there perhaps a subdirectory match? */
pos = -pos-1;
- while (pos < the_index.cache_nr) {
- const struct cache_entry *ce = the_index.cache[pos++];
+ while (pos < the_repository->index->cache_nr) {
+ const struct cache_entry *ce = the_repository->index->cache[pos++];
if (strncmp(ce->name, path, len))
break;
@@ -375,7 +376,7 @@ static int process_directory(const char *path, int len, struct stat *st)
}
/* No match - should we add it as a gitlink? */
- if (!resolve_gitlink_ref(path, "HEAD", &oid))
+ if (!repo_resolve_gitlink_ref(the_repository, path, "HEAD", &oid))
return add_one_path(NULL, path, len, st);
/* Error out. */
@@ -391,8 +392,8 @@ static int process_path(const char *path, struct stat *st, int stat_errno)
if (has_symlink_leading_path(path, len))
return error("'%s' is beyond a symbolic link", path);
- pos = index_name_pos(&the_index, path, len);
- ce = pos < 0 ? NULL : the_index.cache[pos];
+ pos = index_name_pos(the_repository->index, path, len);
+ ce = pos < 0 ? NULL : the_repository->index->cache[pos];
if (ce && ce_skip_worktree(ce)) {
/*
* working directory version is assumed "good"
@@ -400,7 +401,7 @@ static int process_path(const char *path, struct stat *st, int stat_errno)
* On the other hand, removing it from index should work
*/
if (!ignore_skip_worktree_entries && allow_remove &&
- remove_file_from_index(&the_index, path))
+ remove_file_from_index(the_repository->index, path))
return error("%s: cannot remove from the index", path);
return 0;
}
@@ -428,7 +429,7 @@ static int add_cacheinfo(unsigned int mode, const struct object_id *oid,
return error("Invalid path '%s'", path);
len = strlen(path);
- ce = make_empty_cache_entry(&the_index, len);
+ ce = make_empty_cache_entry(the_repository->index, len);
oidcpy(&ce->oid, oid);
memcpy(ce->name, path, len);
@@ -439,7 +440,7 @@ static int add_cacheinfo(unsigned int mode, const struct object_id *oid,
ce->ce_flags |= CE_VALID;
option = allow_add ? ADD_CACHE_OK_TO_ADD : 0;
option |= allow_replace ? ADD_CACHE_OK_TO_REPLACE : 0;
- if (add_index_entry(&the_index, ce, option))
+ if (add_index_entry(the_repository->index, ce, option))
return error("%s: cannot add to the index - missing --add option?",
path);
report("add '%s'", path);
@@ -451,11 +452,11 @@ static void chmod_path(char flip, const char *path)
int pos;
struct cache_entry *ce;
- pos = index_name_pos(&the_index, path, strlen(path));
+ pos = index_name_pos(the_repository->index, path, strlen(path));
if (pos < 0)
goto fail;
- ce = the_index.cache[pos];
- if (chmod_index_entry(&the_index, ce, flip) < 0)
+ ce = the_repository->index->cache[pos];
+ if (chmod_index_entry(the_repository->index, ce, flip) < 0)
goto fail;
report("chmod %cx '%s'", flip, path);
@@ -498,7 +499,7 @@ static void update_one(const char *path)
}
if (force_remove) {
- if (remove_file_from_index(&the_index, path))
+ if (remove_file_from_index(the_repository->index, path))
die("git update-index: unable to remove %s", path);
report("remove '%s'", path);
return;
@@ -581,7 +582,7 @@ static void read_index_info(int nul_term_line)
if (!mode) {
/* mode == 0 means there is no such path -- remove */
- if (remove_file_from_index(&the_index, path_name))
+ if (remove_file_from_index(the_repository->index, path_name))
die("git update-index: unable to remove %s",
ptr);
}
@@ -622,12 +623,12 @@ static struct cache_entry *read_one_ent(const char *which,
error("%s: not in %s branch.", path, which);
return NULL;
}
- if (!the_index.sparse_index && mode == S_IFDIR) {
+ if (!the_repository->index->sparse_index && mode == S_IFDIR) {
if (which)
error("%s: not a blob in %s branch.", path, which);
return NULL;
}
- ce = make_empty_cache_entry(&the_index, namelen);
+ ce = make_empty_cache_entry(the_repository->index, namelen);
oidcpy(&ce->oid, &oid);
memcpy(ce->name, path, namelen);
@@ -642,12 +643,12 @@ static int unresolve_one(const char *path)
struct string_list_item *item;
int res = 0;
- if (!the_index.resolve_undo)
+ if (!the_repository->index->resolve_undo)
return res;
- item = string_list_lookup(the_index.resolve_undo, path);
+ item = string_list_lookup(the_repository->index->resolve_undo, path);
if (!item)
return res; /* no resolve-undo record for the path */
- res = unmerge_index_entry(&the_index, path, item->util, 0);
+ res = unmerge_index_entry(the_repository->index, path, item->util, 0);
FREE_AND_NULL(item->util);
return res;
}
@@ -682,19 +683,19 @@ static int do_reupdate(const char **paths,
PATHSPEC_PREFER_CWD,
prefix, paths);
- if (read_ref("HEAD", &head_oid))
+ if (refs_read_ref(get_main_ref_store(the_repository), "HEAD", &head_oid))
/* If there is no HEAD, that means it is an initial
* commit. Update everything in the index.
*/
has_head = 0;
redo:
- for (pos = 0; pos < the_index.cache_nr; pos++) {
- const struct cache_entry *ce = the_index.cache[pos];
+ for (pos = 0; pos < the_repository->index->cache_nr; pos++) {
+ const struct cache_entry *ce = the_repository->index->cache[pos];
struct cache_entry *old = NULL;
int save_nr;
char *path;
- if (ce_stage(ce) || !ce_path_match(&the_index, ce, &pathspec, NULL))
+ if (ce_stage(ce) || !ce_path_match(the_repository->index, ce, &pathspec, NULL))
continue;
if (has_head)
old = read_one_ent(NULL, &head_oid,
@@ -710,7 +711,7 @@ static int do_reupdate(const char **paths,
* to process each path individually
*/
if (S_ISSPARSEDIR(ce->ce_mode)) {
- ensure_full_index(&the_index);
+ ensure_full_index(the_repository->index);
goto redo;
}
@@ -718,12 +719,12 @@ static int do_reupdate(const char **paths,
* path anymore, in which case, under 'allow_remove',
* or worse yet 'allow_replace', active_nr may decrease.
*/
- save_nr = the_index.cache_nr;
+ save_nr = the_repository->index->cache_nr;
path = xstrdup(ce->name);
update_one(path);
free(path);
discard_cache_entry(old);
- if (save_nr != the_index.cache_nr)
+ if (save_nr != the_repository->index->cache_nr)
goto redo;
}
clear_pathspec(&pathspec);
@@ -739,9 +740,9 @@ static int refresh(struct refresh_params *o, unsigned int flag)
{
setup_work_tree();
repo_read_index(the_repository);
- *o->has_errors |= refresh_index(&the_index, o->flags | flag, NULL,
+ *o->has_errors |= refresh_index(the_repository->index, o->flags | flag, NULL,
NULL, NULL);
- if (has_racy_timestamp(&the_index)) {
+ if (has_racy_timestamp(the_repository->index)) {
/*
* Even if nothing else has changed, updating the file
* increases the chance that racy timestamps become
@@ -750,7 +751,7 @@ static int refresh(struct refresh_params *o, unsigned int flag)
* refresh_index() as these are no actual errors.
* cmd_status() does the same.
*/
- the_index.cache_changed |= SOMETHING_CHANGED;
+ the_repository->index->cache_changed |= SOMETHING_CHANGED;
}
return 0;
}
@@ -787,7 +788,7 @@ static int resolve_undo_clear_callback(const struct option *opt UNUSED,
{
BUG_ON_OPT_NEG(unset);
BUG_ON_OPT_ARG(arg);
- resolve_undo_clear_index(&the_index);
+ resolve_undo_clear_index(the_repository->index);
return 0;
}
@@ -888,7 +889,7 @@ static enum parse_opt_result unresolve_callback(
*has_errors = do_unresolve(ctx->argc, ctx->argv,
prefix, prefix ? strlen(prefix) : 0);
if (*has_errors)
- the_index.cache_changed = 0;
+ the_repository->index->cache_changed = 0;
ctx->argv += ctx->argc - 1;
ctx->argc = 1;
@@ -909,7 +910,7 @@ static enum parse_opt_result reupdate_callback(
setup_work_tree();
*has_errors = do_reupdate(ctx->argv + 1, prefix);
if (*has_errors)
- the_index.cache_changed = 0;
+ the_repository->index->cache_changed = 0;
ctx->argv += ctx->argc - 1;
ctx->argc = 1;
@@ -1056,7 +1057,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
if (entries < 0)
die("cache corrupted");
- the_index.updated_skipworktree = 1;
+ the_repository->index->updated_skipworktree = 1;
/*
* Custom copy of parse_options() because we want to handle
@@ -1111,18 +1112,18 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
getline_fn = nul_term_line ? strbuf_getline_nul : strbuf_getline_lf;
if (preferred_index_format) {
if (preferred_index_format < 0) {
- printf(_("%d\n"), the_index.version);
+ printf(_("%d\n"), the_repository->index->version);
} else if (preferred_index_format < INDEX_FORMAT_LB ||
INDEX_FORMAT_UB < preferred_index_format) {
die("index-version %d not in range: %d..%d",
preferred_index_format,
INDEX_FORMAT_LB, INDEX_FORMAT_UB);
} else {
- if (the_index.version != preferred_index_format)
- the_index.cache_changed |= SOMETHING_CHANGED;
+ if (the_repository->index->version != preferred_index_format)
+ the_repository->index->cache_changed |= SOMETHING_CHANGED;
report(_("index-version: was %d, set to %d"),
- the_index.version, preferred_index_format);
- the_index.version = preferred_index_format;
+ the_repository->index->version, preferred_index_format);
+ the_repository->index->version = preferred_index_format;
}
}
@@ -1159,16 +1160,16 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
warning(_("core.splitIndex is set to false; "
"remove or change it, if you really want to "
"enable split index"));
- if (the_index.split_index)
- the_index.cache_changed |= SPLIT_INDEX_ORDERED;
+ if (the_repository->index->split_index)
+ the_repository->index->cache_changed |= SPLIT_INDEX_ORDERED;
else
- add_split_index(&the_index);
+ add_split_index(the_repository->index);
} else if (!split_index) {
if (git_config_get_split_index() == 1)
warning(_("core.splitIndex is set to true; "
"remove or change it, if you really want to "
"disable split index"));
- remove_split_index(&the_index);
+ remove_split_index(the_repository->index);
}
prepare_repo_settings(r);
@@ -1180,7 +1181,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
warning(_("core.untrackedCache is set to true; "
"remove or change it, if you really want to "
"disable the untracked cache"));
- remove_untracked_cache(&the_index);
+ remove_untracked_cache(the_repository->index);
report(_("Untracked cache disabled"));
break;
case UC_TEST:
@@ -1192,7 +1193,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
warning(_("core.untrackedCache is set to false; "
"remove or change it, if you really want to "
"enable the untracked cache"));
- add_untracked_cache(&the_index);
+ add_untracked_cache(the_repository->index);
report(_("Untracked cache enabled for '%s'"), get_git_work_tree());
break;
default:
@@ -1222,7 +1223,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
"set it if you really want to "
"enable fsmonitor"));
}
- add_fsmonitor(&the_index);
+ add_fsmonitor(the_repository->index);
report(_("fsmonitor enabled"));
} else if (!fsmonitor) {
enum fsmonitor_mode fsm_mode = fsm_settings__get_mode(r);
@@ -1230,17 +1231,17 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
warning(_("core.fsmonitor is set; "
"remove it if you really want to "
"disable fsmonitor"));
- remove_fsmonitor(&the_index);
+ remove_fsmonitor(the_repository->index);
report(_("fsmonitor disabled"));
}
- if (the_index.cache_changed || force_write) {
+ if (the_repository->index->cache_changed || force_write) {
if (newfd < 0) {
if (refresh_args.flags & REFRESH_QUIET)
exit(128);
unable_to_lock_die(get_index_file(), lock_error);
}
- if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
+ if (write_locked_index(the_repository->index, &lock_file, COMMIT_LOCK))
die("Unable to write new index file");
}
diff --git a/builtin/update-ref.c b/builtin/update-ref.c
index e46afbc46d..6a6a2ff55d 100644
--- a/builtin/update-ref.c
+++ b/builtin/update-ref.c
@@ -77,6 +77,65 @@ static char *parse_refname(const char **next)
}
/*
+ * Wrapper around parse_refname which skips the next delimiter.
+ */
+static char *parse_next_refname(const char **next)
+{
+ if (line_termination) {
+ /* Without -z, consume SP and use next argument */
+ if (!**next || **next == line_termination)
+ return NULL;
+ if (**next != ' ')
+ die("expected SP but got: %s", *next);
+ } else {
+ /* With -z, read the next NUL-terminated line */
+ if (**next)
+ return NULL;
+ }
+ /* Skip the delimiter */
+ (*next)++;
+
+ return parse_refname(next);
+}
+
+/*
+ * Wrapper around parse_arg which skips the next delimiter.
+ */
+static char *parse_next_arg(const char **next)
+{
+ struct strbuf arg = STRBUF_INIT;
+
+ if (line_termination) {
+ /* Without -z, consume SP and use next argument */
+ if (!**next || **next == line_termination)
+ return NULL;
+ if (**next != ' ')
+ die("expected SP but got: %s", *next);
+ } else {
+ /* With -z, read the next NUL-terminated line */
+ if (**next)
+ return NULL;
+ }
+ /* Skip the delimiter */
+ (*next)++;
+
+ if (line_termination) {
+ /* Without -z, use the next argument */
+ *next = parse_arg(*next, &arg);
+ } else {
+ /* With -z, use everything up to the next NUL */
+ strbuf_addstr(&arg, *next);
+ *next += arg.len;
+ }
+
+ if (arg.len)
+ return strbuf_detach(&arg, NULL);
+
+ strbuf_release(&arg);
+ return NULL;
+}
+
+/*
* The value being parsed is <old-oid> (as opposed to <new-oid>; the
* difference affects which error messages are generated):
*/
@@ -122,7 +181,7 @@ static int parse_next_oid(const char **next, const char *end,
goto invalid;
} else {
/* Without -z, an empty value means all zeros: */
- oidclr(oid);
+ oidclr(oid, the_repository->hash_algo);
}
} else {
/* With -z, read the next NUL-terminated line */
@@ -142,7 +201,7 @@ static int parse_next_oid(const char **next, const char *end,
/* With -z, treat an empty value as all zeros: */
warning("%s %s: missing <new-oid>, treating as zero",
command, refname);
- oidclr(oid);
+ oidclr(oid, the_repository->hash_algo);
} else {
/*
* With -z, an empty non-required value means
@@ -204,12 +263,68 @@ static void parse_cmd_update(struct ref_transaction *transaction,
if (ref_transaction_update(transaction, refname,
&new_oid, have_old ? &old_oid : NULL,
+ NULL, NULL,
+ update_flags | create_reflog_flag,
+ msg, &err))
+ die("%s", err.buf);
+
+ update_flags = default_flags;
+ free(refname);
+ strbuf_release(&err);
+}
+
+static void parse_cmd_symref_update(struct ref_transaction *transaction,
+ const char *next, const char *end)
+{
+ char *refname, *new_target, *old_arg;
+ char *old_target = NULL;
+ struct strbuf err = STRBUF_INIT;
+ struct object_id old_oid;
+ int have_old_oid = 0;
+
+ refname = parse_refname(&next);
+ if (!refname)
+ die("symref-update: missing <ref>");
+
+ new_target = parse_next_refname(&next);
+ if (!new_target)
+ die("symref-update %s: missing <new-target>", refname);
+
+ old_arg = parse_next_arg(&next);
+ if (old_arg) {
+ old_target = parse_next_arg(&next);
+ if (!old_target)
+ die("symref-update %s: expected old value", refname);
+
+ if (!strcmp(old_arg, "oid")) {
+ if (repo_get_oid(the_repository, old_target, &old_oid))
+ die("symref-update %s: invalid oid: %s", refname, old_target);
+
+ have_old_oid = 1;
+ } else if (!strcmp(old_arg, "ref")) {
+ if (check_refname_format(old_target, REFNAME_ALLOW_ONELEVEL))
+ die("symref-update %s: invalid ref: %s", refname, old_target);
+ } else {
+ die("symref-update %s: invalid arg '%s' for old value", refname, old_arg);
+ }
+ }
+
+ if (*next != line_termination)
+ die("symref-update %s: extra input: %s", refname, next);
+
+ if (ref_transaction_update(transaction, refname, NULL,
+ have_old_oid ? &old_oid : NULL,
+ new_target,
+ have_old_oid ? NULL : old_target,
update_flags | create_reflog_flag,
msg, &err))
die("%s", err.buf);
update_flags = default_flags;
free(refname);
+ free(old_arg);
+ free(old_target);
+ free(new_target);
strbuf_release(&err);
}
@@ -233,13 +348,42 @@ static void parse_cmd_create(struct ref_transaction *transaction,
if (*next != line_termination)
die("create %s: extra input: %s", refname, next);
- if (ref_transaction_create(transaction, refname, &new_oid,
+ if (ref_transaction_create(transaction, refname, &new_oid, NULL,
+ update_flags | create_reflog_flag,
+ msg, &err))
+ die("%s", err.buf);
+
+ update_flags = default_flags;
+ free(refname);
+ strbuf_release(&err);
+}
+
+
+static void parse_cmd_symref_create(struct ref_transaction *transaction,
+ const char *next, const char *end)
+{
+ struct strbuf err = STRBUF_INIT;
+ char *refname, *new_target;
+
+ refname = parse_refname(&next);
+ if (!refname)
+ die("symref-create: missing <ref>");
+
+ new_target = parse_next_refname(&next);
+ if (!new_target)
+ die("symref-create %s: missing <new-target>", refname);
+
+ if (*next != line_termination)
+ die("symref-create %s: extra input: %s", refname, next);
+
+ if (ref_transaction_create(transaction, refname, NULL, new_target,
update_flags | create_reflog_flag,
msg, &err))
die("%s", err.buf);
update_flags = default_flags;
free(refname);
+ free(new_target);
strbuf_release(&err);
}
@@ -269,7 +413,7 @@ static void parse_cmd_delete(struct ref_transaction *transaction,
if (ref_transaction_delete(transaction, refname,
have_old ? &old_oid : NULL,
- update_flags, msg, &err))
+ NULL, update_flags, msg, &err))
die("%s", err.buf);
update_flags = default_flags;
@@ -277,6 +421,36 @@ static void parse_cmd_delete(struct ref_transaction *transaction,
strbuf_release(&err);
}
+
+static void parse_cmd_symref_delete(struct ref_transaction *transaction,
+ const char *next, const char *end)
+{
+ struct strbuf err = STRBUF_INIT;
+ char *refname, *old_target;
+
+ if (!(update_flags & REF_NO_DEREF))
+ die("symref-delete: cannot operate with deref mode");
+
+ refname = parse_refname(&next);
+ if (!refname)
+ die("symref-delete: missing <ref>");
+
+ old_target = parse_next_refname(&next);
+
+ if (*next != line_termination)
+ die("symref-delete %s: extra input: %s", refname, next);
+
+ if (ref_transaction_delete(transaction, refname, NULL,
+ old_target, update_flags, msg, &err))
+ die("%s", err.buf);
+
+ update_flags = default_flags;
+ free(refname);
+ free(old_target);
+ strbuf_release(&err);
+}
+
+
static void parse_cmd_verify(struct ref_transaction *transaction,
const char *next, const char *end)
{
@@ -290,17 +464,53 @@ static void parse_cmd_verify(struct ref_transaction *transaction,
if (parse_next_oid(&next, end, &old_oid, "verify", refname,
PARSE_SHA1_OLD))
- oidclr(&old_oid);
+ oidclr(&old_oid, the_repository->hash_algo);
if (*next != line_termination)
die("verify %s: extra input: %s", refname, next);
if (ref_transaction_verify(transaction, refname, &old_oid,
- update_flags, &err))
+ NULL, update_flags, &err))
+ die("%s", err.buf);
+
+ update_flags = default_flags;
+ free(refname);
+ strbuf_release(&err);
+}
+
+static void parse_cmd_symref_verify(struct ref_transaction *transaction,
+ const char *next, const char *end)
+{
+ struct strbuf err = STRBUF_INIT;
+ struct object_id old_oid;
+ char *refname, *old_target;
+
+ if (!(update_flags & REF_NO_DEREF))
+ die("symref-verify: cannot operate with deref mode");
+
+ refname = parse_refname(&next);
+ if (!refname)
+ die("symref-verify: missing <ref>");
+
+ /*
+ * old_ref is optional, if not provided, we need to ensure that the
+ * ref doesn't exist.
+ */
+ old_target = parse_next_refname(&next);
+ if (!old_target)
+ oidcpy(&old_oid, null_oid());
+
+ if (*next != line_termination)
+ die("symref-verify %s: extra input: %s", refname, next);
+
+ if (ref_transaction_verify(transaction, refname,
+ old_target ? NULL : &old_oid,
+ old_target, update_flags, &err))
die("%s", err.buf);
update_flags = default_flags;
free(refname);
+ free(old_target);
strbuf_release(&err);
}
@@ -379,15 +589,19 @@ static const struct parse_cmd {
unsigned args;
enum update_refs_state state;
} command[] = {
- { "update", parse_cmd_update, 3, UPDATE_REFS_OPEN },
- { "create", parse_cmd_create, 2, UPDATE_REFS_OPEN },
- { "delete", parse_cmd_delete, 2, UPDATE_REFS_OPEN },
- { "verify", parse_cmd_verify, 2, UPDATE_REFS_OPEN },
- { "option", parse_cmd_option, 1, UPDATE_REFS_OPEN },
- { "start", parse_cmd_start, 0, UPDATE_REFS_STARTED },
- { "prepare", parse_cmd_prepare, 0, UPDATE_REFS_PREPARED },
- { "abort", parse_cmd_abort, 0, UPDATE_REFS_CLOSED },
- { "commit", parse_cmd_commit, 0, UPDATE_REFS_CLOSED },
+ { "update", parse_cmd_update, 3, UPDATE_REFS_OPEN },
+ { "create", parse_cmd_create, 2, UPDATE_REFS_OPEN },
+ { "delete", parse_cmd_delete, 2, UPDATE_REFS_OPEN },
+ { "verify", parse_cmd_verify, 2, UPDATE_REFS_OPEN },
+ { "symref-update", parse_cmd_symref_update, 4, UPDATE_REFS_OPEN },
+ { "symref-create", parse_cmd_symref_create, 2, UPDATE_REFS_OPEN },
+ { "symref-delete", parse_cmd_symref_delete, 2, UPDATE_REFS_OPEN },
+ { "symref-verify", parse_cmd_symref_verify, 2, UPDATE_REFS_OPEN },
+ { "option", parse_cmd_option, 1, UPDATE_REFS_OPEN },
+ { "start", parse_cmd_start, 0, UPDATE_REFS_STARTED },
+ { "prepare", parse_cmd_prepare, 0, UPDATE_REFS_PREPARED },
+ { "abort", parse_cmd_abort, 0, UPDATE_REFS_CLOSED },
+ { "commit", parse_cmd_commit, 0, UPDATE_REFS_CLOSED },
};
static void update_refs_stdin(void)
@@ -397,7 +611,8 @@ static void update_refs_stdin(void)
struct ref_transaction *transaction;
int i, j;
- transaction = ref_transaction_begin(&err);
+ transaction = ref_store_transaction_begin(get_main_ref_store(the_repository),
+ &err);
if (!transaction)
die("%s", err.buf);
@@ -464,7 +679,8 @@ static void update_refs_stdin(void)
* get a "start".
*/
state = cmd->state;
- transaction = ref_transaction_begin(&err);
+ transaction = ref_store_transaction_begin(get_main_ref_store(the_repository),
+ &err);
if (!transaction)
die("%s", err.buf);
@@ -561,7 +777,7 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix)
* The empty string implies that the reference
* must not already exist:
*/
- oidclr(&oldoid);
+ oidclr(&oldoid, the_repository->hash_algo);
else if (repo_get_oid(the_repository, oldval, &oldoid))
die("%s: not a valid old SHA1", oldval);
}
@@ -571,11 +787,14 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix)
* For purposes of backwards compatibility, we treat
* NULL_SHA1 as "don't care" here:
*/
- return delete_ref(msg, refname,
- (oldval && !is_null_oid(&oldoid)) ? &oldoid : NULL,
- default_flags);
+ return refs_delete_ref(get_main_ref_store(the_repository),
+ msg, refname,
+ (oldval && !is_null_oid(&oldoid)) ? &oldoid : NULL,
+ default_flags);
else
- return update_ref(msg, refname, &oid, oldval ? &oldoid : NULL,
- default_flags | create_reflog_flag,
- UPDATE_REFS_DIE_ON_ERR);
+ return refs_update_ref(get_main_ref_store(the_repository),
+ msg, refname, &oid,
+ oldval ? &oldoid : NULL,
+ default_flags | create_reflog_flag,
+ UPDATE_REFS_DIE_ON_ERR);
}
diff --git a/builtin/upload-pack.c b/builtin/upload-pack.c
index 15afb97260..46d93278d9 100644
--- a/builtin/upload-pack.c
+++ b/builtin/upload-pack.c
@@ -9,6 +9,7 @@
#include "upload-pack.h"
#include "serve.h"
#include "commit.h"
+#include "environment.h"
static const char * const upload_pack_usage[] = {
N_("git-upload-pack [--[no-]strict] [--timeout=<n>] [--stateless-rpc]\n"
@@ -39,6 +40,7 @@ int cmd_upload_pack(int argc, const char **argv, const char *prefix)
packet_trace_identity("upload-pack");
disable_replace_refs();
save_commit_buffer = 0;
+ xsetenv(NO_LAZY_FETCH_ENVIRONMENT, "1", 0);
argc = parse_options(argc, argv, prefix, options, upload_pack_usage, 0);
diff --git a/builtin/var.c b/builtin/var.c
index cf5567208a..e30ff45be1 100644
--- a/builtin/var.c
+++ b/builtin/var.c
@@ -12,6 +12,7 @@
#include "refs.h"
#include "path.h"
#include "strbuf.h"
+#include "run-command.h"
static const char var_usage[] = "git var (-l | <variable>)";
@@ -46,12 +47,12 @@ static char *pager(int ident_flag UNUSED)
static char *default_branch(int ident_flag UNUSED)
{
- return xstrdup_or_null(git_default_branch_name(1));
+ return repo_default_branch_name(the_repository, 1);
}
static char *shell_path(int ident_flag UNUSED)
{
- return xstrdup(SHELL_PATH);
+ return git_shell_path();
}
static char *git_attr_val_system(int ident_flag UNUSED)
diff --git a/builtin/worktree.c b/builtin/worktree.c
index 7c6c72536b..1d51e54fcd 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -433,7 +433,7 @@ static int add_worktree(const char *path, const char *refname,
/* is 'refname' a branch or commit? */
if (!opts->detach && !strbuf_check_branch_ref(&symref, refname) &&
- ref_exists(symref.buf)) {
+ refs_ref_exists(get_main_ref_store(the_repository), symref.buf)) {
is_branch = 1;
if (!opts->force)
die_if_checked_out(symref.buf, 0);
@@ -509,7 +509,7 @@ static int add_worktree(const char *path, const char *refname,
}
wt_refs = get_worktree_ref_store(wt);
- ret = refs_init_db(wt_refs, REFS_INIT_DB_IS_WORKTREE, &sb);
+ ret = ref_store_create_on_disk(wt_refs, REF_STORE_CREATE_ON_DISK_IS_WORKTREE, &sb);
if (ret)
goto done;
@@ -517,7 +517,7 @@ static int add_worktree(const char *path, const char *refname,
ret = refs_update_ref(wt_refs, NULL, "HEAD", &commit->object.oid,
NULL, 0, UPDATE_REFS_MSG_ON_ERR);
else
- ret = refs_create_symref(wt_refs, "HEAD", symref.buf, NULL);
+ ret = refs_update_symref(wt_refs, "HEAD", symref.buf, NULL);
if (ret)
goto done;
@@ -605,7 +605,7 @@ static void print_preparing_worktree_line(int detach,
} else {
struct strbuf s = STRBUF_INIT;
if (!detach && !strbuf_check_branch_ref(&s, branch) &&
- ref_exists(s.buf))
+ refs_ref_exists(get_main_ref_store(the_repository), s.buf))
fprintf_ln(stderr, _("Preparing worktree (checking out '%s')"),
branch);
else {
@@ -647,9 +647,9 @@ static int first_valid_ref(const char *refname UNUSED,
*/
static int can_use_local_refs(const struct add_opts *opts)
{
- if (head_ref(first_valid_ref, NULL)) {
+ if (refs_head_ref(get_main_ref_store(the_repository), first_valid_ref, NULL)) {
return 1;
- } else if (for_each_branch_ref(first_valid_ref, NULL)) {
+ } else if (refs_for_each_branch_ref(get_main_ref_store(the_repository), first_valid_ref, NULL)) {
if (!opts->quiet) {
struct strbuf path = STRBUF_INIT;
struct strbuf contents = STRBUF_INIT;
@@ -689,7 +689,7 @@ 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)) {
+ } else if (refs_for_each_remote_ref(get_main_ref_store(the_repository), 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"
@@ -736,18 +736,17 @@ static int dwim_orphan(const struct add_opts *opts, int opt_track, int remote)
return 1;
}
-static const char *dwim_branch(const char *path, const char **new_branch)
+static char *dwim_branch(const char *path, char **new_branch)
{
int n;
int branch_exists;
const char *s = worktree_basename(path, &n);
- const char *branchname = xstrndup(s, n);
+ char *branchname = xstrndup(s, n);
struct strbuf ref = STRBUF_INIT;
- UNLEAK(branchname);
-
branch_exists = !strbuf_check_branch_ref(&ref, branchname) &&
- ref_exists(ref.buf);
+ refs_ref_exists(get_main_ref_store(the_repository),
+ ref.buf);
strbuf_release(&ref);
if (branch_exists)
return branchname;
@@ -755,8 +754,7 @@ static const char *dwim_branch(const char *path, const char **new_branch)
*new_branch = branchname;
if (guess_remote) {
struct object_id oid;
- const char *remote =
- unique_tracking_name(*new_branch, &oid, NULL);
+ char *remote = unique_tracking_name(*new_branch, &oid, NULL);
return remote;
}
return NULL;
@@ -768,6 +766,8 @@ static int add(int ac, const char **av, const char *prefix)
const char *new_branch_force = NULL;
char *path;
const char *branch;
+ char *branch_to_free = NULL;
+ char *new_branch_to_free = NULL;
const char *new_branch = NULL;
const char *opt_track = NULL;
const char *lock_reason = NULL;
@@ -838,7 +838,7 @@ static int add(int ac, const char **av, const char *prefix)
if (!opts.force &&
!strbuf_check_branch_ref(&symref, new_branch) &&
- ref_exists(symref.buf))
+ refs_ref_exists(get_main_ref_store(the_repository), symref.buf))
die_if_checked_out(symref.buf, 0);
strbuf_release(&symref);
}
@@ -858,16 +858,17 @@ static int add(int ac, const char **av, const char *prefix)
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);
+ char *s = dwim_branch(path, &new_branch_to_free);
if (s)
- branch = s;
+ branch = branch_to_free = s;
+ new_branch = new_branch_to_free;
/* 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;
+ char *remote;
commit = lookup_commit_reference_by_name(branch);
if (!commit) {
@@ -922,6 +923,8 @@ static int add(int ac, const char **av, const char *prefix)
ret = add_worktree(path, branch, &opts);
free(path);
+ free(branch_to_free);
+ free(new_branch_to_free);
return ret;
}
@@ -974,7 +977,9 @@ static void show_worktree(struct worktree *wt, int path_maxlen, int abbrev_len)
if (wt->is_detached)
strbuf_addstr(&sb, "(detached HEAD)");
else if (wt->head_ref) {
- char *ref = shorten_unambiguous_ref(wt->head_ref, 0);
+ char *ref = refs_shorten_unambiguous_ref(get_main_ref_store(the_repository),
+ wt->head_ref,
+ 0);
strbuf_addf(&sb, "[%s]", ref);
free(ref);
} else
diff --git a/builtin/write-tree.c b/builtin/write-tree.c
index 66e83d0ecb..8c75b4609b 100644
--- a/builtin/write-tree.c
+++ b/builtin/write-tree.c
@@ -3,7 +3,7 @@
*
* Copyright (C) Linus Torvalds, 2005
*/
-#define USE_THE_INDEX_VARIABLE
+
#include "builtin.h"
#include "config.h"
#include "environment.h"
@@ -44,8 +44,8 @@ int cmd_write_tree(int argc, const char **argv, const char *cmd_prefix)
prepare_repo_settings(the_repository);
the_repository->settings.command_requires_full_index = 0;
- ret = write_index_as_tree(&oid, &the_index, get_index_file(), flags,
- tree_prefix);
+ ret = write_index_as_tree(&oid, the_repository->index, get_index_file(),
+ flags, tree_prefix);
switch (ret) {
case 0:
printf("%s\n", oid_to_hex(&oid));
diff --git a/bulk-checkin.c b/bulk-checkin.c
index eb46b88637..da8673199b 100644
--- a/bulk-checkin.c
+++ b/bulk-checkin.c
@@ -1,6 +1,9 @@
/*
* Copyright (c) 2011, Google Inc.
*/
+
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "bulk-checkin.h"
#include "environment.h"
diff --git a/bundle-uri.c b/bundle-uri.c
index ca32050a78..1e0ee156ba 100644
--- a/bundle-uri.c
+++ b/bundle-uri.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "bundle-uri.h"
#include "bundle.h"
@@ -9,6 +11,7 @@
#include "hashmap.h"
#include "pkt-line.h"
#include "config.h"
+#include "fetch-pack.h"
#include "remote.h"
static struct {
@@ -373,7 +376,7 @@ static int unbundle_from_file(struct repository *r, const char *file)
* the prerequisite commits.
*/
if ((result = unbundle(r, &header, bundle_fd, NULL,
- VERIFY_BUNDLE_QUIET)))
+ VERIFY_BUNDLE_QUIET | (fetch_pack_fsck_objects() ? VERIFY_BUNDLE_FSCK : 0))))
return 1;
/*
@@ -395,11 +398,12 @@ static int unbundle_from_file(struct repository *r, const char *file)
strbuf_setlen(&bundle_ref, bundle_prefix_len);
strbuf_addstr(&bundle_ref, branch_name);
- has_old = !read_ref(bundle_ref.buf, &old_oid);
- update_ref("fetched bundle", bundle_ref.buf, oid,
- has_old ? &old_oid : NULL,
- REF_SKIP_OID_VERIFICATION,
- UPDATE_REFS_MSG_ON_ERR);
+ has_old = !refs_read_ref(get_main_ref_store(the_repository),
+ bundle_ref.buf, &old_oid);
+ refs_update_ref(get_main_ref_store(the_repository),
+ "fetched bundle", bundle_ref.buf, oid,
+ has_old ? &old_oid : NULL,
+ 0, UPDATE_REFS_MSG_ON_ERR);
}
bundle_header_release(&header);
diff --git a/bundle.c b/bundle.c
index a9744da255..ce164c37bc 100644
--- a/bundle.c
+++ b/bundle.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "lockfile.h"
#include "bundle.h"
@@ -389,7 +391,7 @@ static int write_bundle_refs(int bundle_fd, struct rev_info *revs)
if (repo_dwim_ref(the_repository, e->name, strlen(e->name),
&oid, &ref, 0) != 1)
goto skip_write_ref;
- if (read_ref_full(e->name, RESOLVE_REF_READING, &oid, &flag))
+ if (refs_read_ref_full(get_main_ref_store(the_repository), e->name, RESOLVE_REF_READING, &oid, &flag))
flag = 0;
display_ref = (flag & REF_ISSYMREF) ? e->name : ref;
@@ -500,6 +502,7 @@ int create_bundle(struct repository *r, const char *path,
struct rev_info revs, revs_copy;
int min_version = 2;
struct bundle_prerequisites_info bpi;
+ int ret;
int i;
/* init revs to list objects for pack-objects later */
@@ -525,8 +528,8 @@ int create_bundle(struct repository *r, const char *path,
min_version = 3;
if (argc > 1) {
- error(_("unrecognized argument: %s"), argv[1]);
- goto err;
+ ret = error(_("unrecognized argument: %s"), argv[1]);
+ goto out;
}
bundle_to_stdout = !strcmp(path, "-");
@@ -591,23 +594,31 @@ int create_bundle(struct repository *r, const char *path,
/* write bundle refs */
ref_count = write_bundle_refs(bundle_fd, &revs_copy);
- if (!ref_count)
+ if (!ref_count) {
die(_("Refusing to create empty bundle."));
- else if (ref_count < 0)
- goto err;
+ } else if (ref_count < 0) {
+ ret = -1;
+ goto out;
+ }
/* write pack */
- if (write_pack_data(bundle_fd, &revs_copy, pack_options))
- goto err;
+ if (write_pack_data(bundle_fd, &revs_copy, pack_options)) {
+ ret = -1;
+ goto out;
+ }
if (!bundle_to_stdout) {
if (commit_lock_file(&lock))
die_errno(_("cannot create '%s'"), path);
}
- return 0;
-err:
+
+ ret = 0;
+
+out:
+ object_array_clear(&revs_copy.pending);
+ release_revisions(&revs);
rollback_lock_file(&lock);
- return -1;
+ return ret;
}
int unbundle(struct repository *r, struct bundle_header *header,
@@ -625,6 +636,9 @@ int unbundle(struct repository *r, struct bundle_header *header,
if (header->filter.choice)
strvec_push(&ip.args, "--promisor=from-bundle");
+ if (flags & VERIFY_BUNDLE_FSCK)
+ strvec_push(&ip.args, "--fsck-objects");
+
if (extra_index_pack_args) {
strvec_pushv(&ip.args, extra_index_pack_args->v);
strvec_clear(extra_index_pack_args);
diff --git a/bundle.h b/bundle.h
index 021adbdcbb..5ccc9a061a 100644
--- a/bundle.h
+++ b/bundle.h
@@ -33,6 +33,7 @@ int create_bundle(struct repository *r, const char *path,
enum verify_bundle_flags {
VERIFY_BUNDLE_VERBOSE = (1 << 0),
VERIFY_BUNDLE_QUIET = (1 << 1),
+ VERIFY_BUNDLE_FSCK = (1 << 2),
};
int verify_bundle(struct repository *r, struct bundle_header *header,
diff --git a/cache-tree.c b/cache-tree.c
index 387c0a3e5b..50610c3f3c 100644
--- a/cache-tree.c
+++ b/cache-tree.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "environment.h"
#include "hex.h"
@@ -422,7 +424,7 @@ static int update_one(struct cache_tree *it,
/*
* "sub" can be an empty tree if all subentries are i-t-a.
*/
- if (contains_ita && is_empty_tree_oid(oid))
+ if (contains_ita && is_empty_tree_oid(oid, the_repository->hash_algo))
continue;
strbuf_grow(&buffer, entlen + 100);
@@ -578,7 +580,8 @@ static struct cache_tree *read_one(const char **buffer, unsigned long *size_p)
if (0 <= it->entry_count) {
if (size < rawsz)
goto free_return;
- oidread(&it->oid, (const unsigned char *)buf);
+ oidread(&it->oid, (const unsigned char *)buf,
+ the_repository->hash_algo);
buf += rawsz;
size -= rawsz;
}
diff --git a/checkout.c b/checkout.c
index 4256e71a7c..0b1cf8b40b 100644
--- a/checkout.c
+++ b/checkout.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "object-name.h"
#include "remote.h"
@@ -45,8 +47,8 @@ static int check_tracking_name(struct remote *remote, void *cb_data)
return 0;
}
-const char *unique_tracking_name(const char *name, struct object_id *oid,
- int *dwim_remotes_matched)
+char *unique_tracking_name(const char *name, struct object_id *oid,
+ int *dwim_remotes_matched)
{
struct tracking_name_data cb_data = TRACKING_NAME_DATA_INIT;
const char *default_remote = NULL;
diff --git a/checkout.h b/checkout.h
index 3c514a5ab4..55920e7aeb 100644
--- a/checkout.h
+++ b/checkout.h
@@ -1,15 +1,15 @@
#ifndef CHECKOUT_H
#define CHECKOUT_H
-#include "hash-ll.h"
+#include "hash.h"
/*
* Check if the branch name uniquely matches a branch name on a remote
* tracking branch. Return the name of the remote if such a branch
* exists, NULL otherwise.
*/
-const char *unique_tracking_name(const char *name,
- struct object_id *oid,
- int *dwim_remotes_matched);
+char *unique_tracking_name(const char *name,
+ struct object_id *oid,
+ int *dwim_remotes_matched);
#endif /* CHECKOUT_H */
diff --git a/chunk-format.c b/chunk-format.c
index cdc7f39b70..2dde24e6a3 100644
--- a/chunk-format.c
+++ b/chunk-format.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "chunk-format.h"
#include "csum-file.h"
diff --git a/chunk-format.h b/chunk-format.h
index 14b76180ef..212a0a6af1 100644
--- a/chunk-format.h
+++ b/chunk-format.h
@@ -1,7 +1,7 @@
#ifndef CHUNK_FORMAT_H
#define CHUNK_FORMAT_H
-#include "hash-ll.h"
+#include "hash.h"
struct hashfile;
struct chunkfile;
diff --git a/ci/check-directional-formatting.bash b/ci/check-directional-formatting.bash
index e6211b141a..3cbbb7030e 100755
--- a/ci/check-directional-formatting.bash
+++ b/ci/check-directional-formatting.bash
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
# This script verifies that the non-binary files tracked in the Git index do
# not contain any Unicode directional formatting: such formatting could be used
diff --git a/ci/check-whitespace.sh b/ci/check-whitespace.sh
new file mode 100755
index 0000000000..db399097a5
--- /dev/null
+++ b/ci/check-whitespace.sh
@@ -0,0 +1,95 @@
+#!/usr/bin/env bash
+#
+# Check that commits after a specified point do not contain new or modified
+# lines with whitespace errors. An optional formatted summary can be generated
+# by providing an output file path and url as additional arguments.
+#
+
+baseCommit=$1
+outputFile=$2
+url=$3
+
+if test "$#" -ne 1 && test "$#" -ne 3
+then
+ echo "USAGE: $0 <BASE_COMMIT> [<OUTPUT_FILE> <URL>]"
+ exit 1
+fi
+
+problems=()
+commit=
+commitText=
+commitTextmd=
+goodParent=
+
+while read dash sha etc
+do
+ case "${dash}" in
+ "---") # Line contains commit information.
+ if test -z "${goodParent}"
+ then
+ # Assume the commit has no whitespace errors until detected otherwise.
+ goodParent=${sha}
+ fi
+
+ commit="${sha}"
+ commitText="${sha} ${etc}"
+ commitTextmd="[${sha}](${url}/commit/${sha}) ${etc}"
+ ;;
+ "")
+ ;;
+ *) # Line contains whitespace error information for current commit.
+ if test -n "${goodParent}"
+ then
+ problems+=("1) --- ${commitTextmd}")
+ echo ""
+ echo "--- ${commitText}"
+ goodParent=
+ fi
+
+ case "${dash}" in
+ *:[1-9]*:) # contains file and line number information
+ dashend=${dash#*:}
+ problems+=("[${dash}](${url}/blob/${commit}/${dash%%:*}#L${dashend%:}) ${sha} ${etc}")
+ ;;
+ *)
+ problems+=("\`${dash} ${sha} ${etc}\`")
+ ;;
+ esac
+ echo "${dash} ${sha} ${etc}"
+ ;;
+ esac
+done <<< "$(git log --check --pretty=format:"---% h% s" "${baseCommit}"..)"
+
+if test ${#problems[*]} -gt 0
+then
+ if test -z "${goodParent}"
+ then
+ goodParent=${baseCommit: 0:7}
+ fi
+
+ echo "A whitespace issue was found in onen of more of the commits."
+ echo "Run the following command to resolve whitespace issues:"
+ echo "git rebase --whitespace=fix ${goodParent}"
+
+ # If target output file is provided, write formatted output.
+ if test -n "$outputFile"
+ then
+ echo "🛑 Please review the Summary output for further information."
+ (
+ echo "### :x: A whitespace issue was found in one or more of the commits."
+ echo ""
+ echo "Run these commands to correct the problem:"
+ echo "1. \`git rebase --whitespace=fix ${goodParent}\`"
+ echo "1. \`git push --force\`"
+ echo ""
+ echo "Errors:"
+
+ for i in "${problems[@]}"
+ do
+ echo "${i}"
+ done
+ ) >"$outputFile"
+ fi
+
+ exit 2
+fi
diff --git a/ci/install-dependencies.sh b/ci/install-dependencies.sh
index b4e22de3cb..6ec0f85972 100755
--- a/ci/install-dependencies.sh
+++ b/ci/install-dependencies.sh
@@ -1,49 +1,81 @@
-#!/usr/bin/env bash
+#!/bin/sh
#
# Install dependencies required to build and test Git on Linux and macOS
#
. ${0%/*}/lib.sh
+begin_group "Install dependencies"
+
P4WHENCE=https://cdist2.perforce.com/perforce/r21.2
LFSWHENCE=https://github.com/github/git-lfs/releases/download/v$LINUX_GIT_LFS_VERSION
-UBUNTU_COMMON_PKGS="make libssl-dev libcurl4-openssl-dev libexpat-dev
- tcl tk gettext zlib1g-dev perl-modules liberror-perl libauthen-sasl-perl
- libemail-valid-perl libio-socket-ssl-perl libnet-smtp-ssl-perl"
+JGITWHENCE=https://repo.eclipse.org/content/groups/releases//org/eclipse/jgit/org.eclipse.jgit.pgm/6.8.0.202311291450-r/org.eclipse.jgit.pgm-6.8.0.202311291450-r.sh
-case "$runs_on_pool" in
+# Make sudo a no-op and execute the command directly when running as root.
+# While using sudo would be fine on most platforms when we are root already,
+# some platforms like e.g. Alpine Linux do not have sudo available by default
+# and would thus break.
+if test "$(id -u)" -eq 0
+then
+ sudo () {
+ "$@"
+ }
+fi
+
+case "$distro" in
+alpine-*)
+ apk add --update shadow sudo build-base curl-dev openssl-dev expat-dev gettext \
+ pcre2-dev python3 musl-libintl perl-utils ncurses \
+ apache2 apache2-http2 apache2-proxy apache2-ssl apache2-webdav apr-util-dbd_sqlite3 \
+ bash cvs gnupg perl-cgi perl-dbd-sqlite perl-io-tty >/dev/null
+ ;;
+fedora-*)
+ dnf -yq update >/dev/null &&
+ dnf -yq install make gcc findutils diffutils perl python3 gettext zlib-devel expat-devel openssl-devel curl-devel pcre2-devel >/dev/null
+ ;;
ubuntu-*)
+ # Required so that apt doesn't wait for user input on certain packages.
+ export DEBIAN_FRONTEND=noninteractive
+
sudo apt-get -q update
- sudo apt-get -q -y install language-pack-is libsvn-perl apache2 \
- $UBUNTU_COMMON_PKGS $CC_PACKAGE $PYTHON_PACKAGE
- mkdir --parents "$P4_PATH"
- pushd "$P4_PATH"
- wget --quiet "$P4WHENCE/bin.linux26x86_64/p4d"
- wget --quiet "$P4WHENCE/bin.linux26x86_64/p4"
- chmod u+x p4d
- chmod u+x p4
- popd
- mkdir --parents "$GIT_LFS_PATH"
- pushd "$GIT_LFS_PATH"
- wget --quiet "$LFSWHENCE/git-lfs-linux-amd64-$LINUX_GIT_LFS_VERSION.tar.gz"
- tar --extract --gunzip --file "git-lfs-linux-amd64-$LINUX_GIT_LFS_VERSION.tar.gz"
- cp git-lfs-$LINUX_GIT_LFS_VERSION/git-lfs .
- popd
+ sudo apt-get -q -y install \
+ language-pack-is libsvn-perl apache2 cvs cvsps git gnupg subversion \
+ make libssl-dev libcurl4-openssl-dev libexpat-dev wget sudo default-jre \
+ tcl tk gettext zlib1g-dev perl-modules liberror-perl libauthen-sasl-perl \
+ libemail-valid-perl libio-pty-perl libio-socket-ssl-perl libnet-smtp-ssl-perl libdbd-sqlite3-perl libcgi-pm-perl \
+ ${CC_PACKAGE:-${CC:-gcc}} $PYTHON_PACKAGE
+
+ mkdir --parents "$CUSTOM_PATH"
+ wget --quiet --directory-prefix="$CUSTOM_PATH" \
+ "$P4WHENCE/bin.linux26x86_64/p4d" "$P4WHENCE/bin.linux26x86_64/p4"
+ chmod a+x "$CUSTOM_PATH/p4d" "$CUSTOM_PATH/p4"
+
+ wget --quiet "$LFSWHENCE/git-lfs-linux-amd64-$LINUX_GIT_LFS_VERSION.tar.gz"
+ tar -xzf "git-lfs-linux-amd64-$LINUX_GIT_LFS_VERSION.tar.gz" \
+ -C "$CUSTOM_PATH" --strip-components=1 "git-lfs-$LINUX_GIT_LFS_VERSION/git-lfs"
+ rm "git-lfs-linux-amd64-$LINUX_GIT_LFS_VERSION.tar.gz"
+
+ wget --quiet "$JGITWHENCE" --output-document="$CUSTOM_PATH/jgit"
+ chmod a+x "$CUSTOM_PATH/jgit"
+ ;;
+ubuntu32-*)
+ sudo linux32 --32bit i386 sh -c '
+ apt update >/dev/null &&
+ apt install -y build-essential libcurl4-openssl-dev \
+ libssl-dev libexpat-dev gettext python >/dev/null
+ '
;;
macos-*)
export HOMEBREW_NO_AUTO_UPDATE=1 HOMEBREW_NO_INSTALL_CLEANUP=1
# Uncomment this if you want to run perf tests:
# brew install gnu-time
- test -z "$BREW_INSTALL_PACKAGES" ||
- brew install $BREW_INSTALL_PACKAGES
brew link --force gettext
- mkdir -p "$P4_PATH"
- pushd "$P4_PATH"
- wget -q "$P4WHENCE/bin.macosx1015x86_64/helix-core-server.tgz" &&
- tar -xf helix-core-server.tgz &&
- sudo xattr -d com.apple.quarantine p4 p4d 2>/dev/null || true
- popd
+ mkdir -p "$CUSTOM_PATH"
+ wget -q "$P4WHENCE/bin.macosx1015x86_64/helix-core-server.tgz" &&
+ tar -xf helix-core-server.tgz -C "$CUSTOM_PATH" p4 p4d &&
+ sudo xattr -d com.apple.quarantine "$CUSTOM_PATH/p4" "$CUSTOM_PATH/p4d" 2>/dev/null || true
+ rm helix-core-server.tgz
if test -n "$CC_PACKAGE"
then
@@ -72,10 +104,6 @@ Documentation)
test -n "$ALREADY_HAVE_ASCIIDOCTOR" ||
sudo gem install --version 1.5.8 asciidoctor
;;
-linux-gcc-default)
- sudo apt-get -q update
- sudo apt-get -q -y install $UBUNTU_COMMON_PKGS
- ;;
esac
if type p4d >/dev/null 2>&1 && type p4 >/dev/null 2>&1
@@ -87,6 +115,7 @@ then
else
echo >&2 "WARNING: perforce wasn't installed, see above for clues why"
fi
+
if type git-lfs >/dev/null 2>&1
then
echo "$(tput setaf 6)Git-LFS Version$(tput sgr0)"
@@ -94,3 +123,13 @@ then
else
echo >&2 "WARNING: git-lfs wasn't installed, see above for clues why"
fi
+
+if type jgit >/dev/null 2>&1
+then
+ echo "$(tput setaf 6)JGit Version$(tput sgr0)"
+ jgit version
+else
+ echo >&2 "WARNING: JGit wasn't installed, see above for clues why"
+fi
+
+end_group "Install dependencies"
diff --git a/ci/install-docker-dependencies.sh b/ci/install-docker-dependencies.sh
deleted file mode 100755
index eb2c9e1eca..0000000000
--- a/ci/install-docker-dependencies.sh
+++ /dev/null
@@ -1,46 +0,0 @@
-#!/bin/sh
-#
-# Install dependencies required to build and test Git inside container
-#
-
-. ${0%/*}/lib.sh
-
-begin_group "Install dependencies"
-
-case "$jobname" in
-linux32)
- linux32 --32bit i386 sh -c '
- apt update >/dev/null &&
- apt install -y build-essential libcurl4-openssl-dev \
- libssl-dev libexpat-dev gettext python >/dev/null
- '
- ;;
-linux-musl)
- apk add --update shadow sudo build-base curl-dev openssl-dev expat-dev gettext \
- pcre2-dev python3 musl-libintl perl-utils ncurses \
- apache2 apache2-http2 apache2-proxy apache2-ssl apache2-webdav apr-util-dbd_sqlite3 \
- bash cvs gnupg perl-cgi perl-dbd-sqlite >/dev/null
- ;;
-linux-*|StaticAnalysis)
- # Required so that apt doesn't wait for user input on certain packages.
- export DEBIAN_FRONTEND=noninteractive
-
- apt update -q &&
- apt install -q -y sudo git make language-pack-is libsvn-perl apache2 libssl-dev \
- libcurl4-openssl-dev libexpat-dev tcl tk gettext zlib1g-dev \
- perl-modules liberror-perl libauthen-sasl-perl libemail-valid-perl \
- libdbd-sqlite3-perl libio-socket-ssl-perl libnet-smtp-ssl-perl ${CC_PACKAGE:-${CC:-gcc}} \
- apache2 cvs cvsps gnupg libcgi-pm-perl subversion
-
- if test "$jobname" = StaticAnalysis
- then
- apt install -q -y coccinelle
- fi
- ;;
-pedantic)
- dnf -yq update >/dev/null &&
- dnf -yq install make gcc findutils diffutils perl python3 gettext zlib-devel expat-devel openssl-devel curl-devel pcre2-devel >/dev/null
- ;;
-esac
-
-end_group "Install dependencies"
diff --git a/ci/lib.sh b/ci/lib.sh
index 0a73fc7bd1..51f8f59a29 100755
--- a/ci/lib.sh
+++ b/ci/lib.sh
@@ -18,7 +18,7 @@ elif test true = "$GITLAB_CI"
then
begin_group () {
need_to_end_group=t
- printf "\e[0Ksection_start:$(date +%s):$(echo "$1" | tr ' ' _)\r\e[0K$1\n"
+ printf "\e[0Ksection_start:$(date +%s):$(echo "$1" | tr ' ' _)[collapsed=true]\r\e[0K$1\n"
trap "end_group '$1'" EXIT
set -x
}
@@ -279,7 +279,7 @@ then
cache_dir="$HOME/none"
- runs_on_pool=$(echo "$CI_JOB_IMAGE" | tr : -)
+ distro=$(echo "$CI_JOB_IMAGE" | tr : -)
JOBS=$(nproc)
else
echo "Could not identify CI type" >&2
@@ -318,16 +318,20 @@ export DEFAULT_TEST_TARGET=prove
export GIT_TEST_CLONE_2GB=true
export SKIP_DASHED_BUILT_INS=YesPlease
-case "$runs_on_pool" in
+case "$distro" in
ubuntu-*)
if test "$jobname" = "linux-gcc-default"
then
break
fi
- PYTHON_PACKAGE=python2
- if test "$jobname" = linux-gcc
+ # Python 2 is end of life, and Ubuntu 23.04 and newer don't actually
+ # have it anymore. We thus only test with Python 2 on older LTS
+ # releases.
+ if test "$distro" = "ubuntu-20.04"
then
+ PYTHON_PACKAGE=python2
+ else
PYTHON_PACKAGE=python3
fi
MAKEFLAGS="$MAKEFLAGS PYTHON_PATH=/usr/bin/$PYTHON_PACKAGE"
@@ -340,10 +344,6 @@ ubuntu-*)
# image.
# Keep that in mind when you encounter a broken OS X build!
export LINUX_GIT_LFS_VERSION="1.5.2"
-
- P4_PATH="$HOME/custom/p4"
- GIT_LFS_PATH="$HOME/custom/git-lfs"
- export PATH="$GIT_LFS_PATH:$P4_PATH:$PATH"
;;
macos-*)
MAKEFLAGS="$MAKEFLAGS PYTHON_PATH=$(which python3)"
@@ -351,12 +351,12 @@ macos-*)
then
MAKEFLAGS="$MAKEFLAGS APPLE_COMMON_CRYPTO_SHA1=Yes"
fi
-
- P4_PATH="$HOME/custom/p4"
- export PATH="$P4_PATH:$PATH"
;;
esac
+CUSTOM_PATH="${CUSTOM_PATH:-$HOME/path}"
+export PATH="$CUSTOM_PATH:$PATH"
+
case "$jobname" in
linux32)
CC=gcc
@@ -370,7 +370,6 @@ linux-musl)
linux-leaks|linux-reftable-leaks)
export SANITIZE=leak
export GIT_TEST_PASSING_SANITIZE_LEAK=true
- export GIT_TEST_SANITIZE_LEAK_LOG=true
;;
linux-asan-ubsan)
export SANITIZE=address,undefined
diff --git a/ci/run-build-and-minimal-fuzzers.sh b/ci/run-build-and-minimal-fuzzers.sh
index a51076d18d..af8065f349 100755
--- a/ci/run-build-and-minimal-fuzzers.sh
+++ b/ci/run-build-and-minimal-fuzzers.sh
@@ -6,8 +6,9 @@
. ${0%/*}/lib.sh
group "Build fuzzers" make \
+ NO_CURL=NoThanks \
CC=clang \
- CXX=clang++ \
+ FUZZ_CXX=clang++ \
CFLAGS="-fsanitize=fuzzer-no-link,address" \
LIB_FUZZING_ENGINE="-fsanitize=fuzzer,address" \
fuzz-all
diff --git a/ci/run-build-and-tests.sh b/ci/run-build-and-tests.sh
index c192bd613c..98dda42045 100755
--- a/ci/run-build-and-tests.sh
+++ b/ci/run-build-and-tests.sh
@@ -53,8 +53,6 @@ if test -n "$run_tests"
then
group "Run tests" make test ||
handle_failed_tests
- group "Run unit tests" \
- make DEFAULT_UNIT_TEST_TARGET=unit-tests-prove unit-tests
fi
check_unignored_build_artifacts
diff --git a/ci/run-test-slice.sh b/ci/run-test-slice.sh
index ae8094382f..e167e646f7 100755
--- a/ci/run-test-slice.sh
+++ b/ci/run-test-slice.sh
@@ -17,7 +17,7 @@ handle_failed_tests
# We only have one unit test at the moment, so run it in the first slice
if [ "$1" == "0" ] ; then
- group "Run unit tests" make --quiet -C t unit-tests-prove
+ group "Run unit tests" make --quiet -C t unit-tests-test-tool
fi
check_unignored_build_artifacts
diff --git a/ci/test-documentation.sh b/ci/test-documentation.sh
index de41888430..02b3af3941 100755
--- a/ci/test-documentation.sh
+++ b/ci/test-documentation.sh
@@ -11,6 +11,7 @@ filter_log () {
-e '/^ \* new asciidoc flags$/d' \
-e '/stripped namespace before processing/d' \
-e '/Attributed.*IDs for element/d' \
+ -e '/SyntaxWarning: invalid escape sequence/d' \
"$1"
}
diff --git a/color.c b/color.c
index f663c06ac4..227a5ab2f4 100644
--- a/color.c
+++ b/color.c
@@ -64,12 +64,16 @@ static int match_word(const char *word, int len, const char *match)
return !strncasecmp(word, match, len) && !match[len];
}
-static int get_hex_color(const char *in, unsigned char *out)
+static int get_hex_color(const char **inp, int width, unsigned char *out)
{
+ const char *in = *inp;
unsigned int val;
- val = (hexval(in[0]) << 4) | hexval(in[1]);
+
+ assert(width == 1 || width == 2);
+ val = (hexval(in[0]) << 4) | hexval(in[width - 1]);
if (val & ~0xff)
return -1;
+ *inp += width;
*out = val;
return 0;
}
@@ -135,11 +139,14 @@ static int parse_color(struct color *out, const char *name, int len)
return 0;
}
- /* Try a 24-bit RGB value */
- if (len == 7 && name[0] == '#') {
- if (!get_hex_color(name + 1, &out->red) &&
- !get_hex_color(name + 3, &out->green) &&
- !get_hex_color(name + 5, &out->blue)) {
+ /* Try a 24- or 12-bit RGB value prefixed with '#' */
+ if ((len == 7 || len == 4) && name[0] == '#') {
+ int width_per_color = (len == 7) ? 2 : 1;
+ const char *color = name + 1;
+
+ if (!get_hex_color(&color, width_per_color, &out->red) &&
+ !get_hex_color(&color, width_per_color, &out->green) &&
+ !get_hex_color(&color, width_per_color, &out->blue)) {
out->type = COLOR_RGB;
return 0;
}
diff --git a/color.h b/color.h
index bb28343be2..7ed259a35b 100644
--- a/color.h
+++ b/color.h
@@ -112,7 +112,8 @@ int want_color_fd(int fd, int var);
* Translate a Git color from 'value' into a string that the terminal can
* interpret and store it into 'dst'. The Git color values are of the form
* "foreground [background] [attr]" where fore- and background can be a color
- * name ("red"), a RGB code (#0xFF0000) or a 256-color-mode from the terminal.
+ * name ("red"), a RGB code (#FF0000 or #F00) or a 256-color-mode from the
+ * terminal.
*/
int color_parse(const char *value, char *dst);
int color_parse_mem(const char *value, int len, char *dst);
diff --git a/combine-diff.c b/combine-diff.c
index d6d6fa1689..829a44e416 100644
--- a/combine-diff.c
+++ b/combine-diff.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "object-store-ll.h"
#include "commit.h"
@@ -1066,7 +1068,8 @@ static void show_patch_diff(struct combine_diff_path *elem, int num_parent,
elem->mode = canon_mode(st.st_mode);
} else if (S_ISDIR(st.st_mode)) {
struct object_id oid;
- if (resolve_gitlink_ref(elem->path, "HEAD", &oid) < 0)
+ if (repo_resolve_gitlink_ref(the_repository, elem->path,
+ "HEAD", &oid) < 0)
result = grab_blob(opt->repo, &elem->oid,
elem->mode, &result_size,
NULL, NULL);
diff --git a/command-list.txt b/command-list.txt
index c4cd0f352b..e0bb87b3b5 100644
--- a/command-list.txt
+++ b/command-list.txt
@@ -157,6 +157,7 @@ git-read-tree plumbingmanipulators
git-rebase mainporcelain history
git-receive-pack synchelpers
git-reflog ancillarymanipulators complete
+git-refs ancillarymanipulators complete
git-remote ancillarymanipulators complete
git-repack ancillarymanipulators complete
git-replace ancillarymanipulators complete
diff --git a/commit-graph.c b/commit-graph.c
index 45417d7412..79b0e72cc4 100644
--- a/commit-graph.c
+++ b/commit-graph.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "config.h"
#include "csum-file.h"
@@ -344,7 +346,6 @@ static int graph_read_bloom_data(const unsigned char *chunk_start,
size_t chunk_size, void *data)
{
struct commit_graph *g = data;
- uint32_t hash_version;
if (chunk_size < BLOOMDATA_CHUNK_HEADER_SIZE) {
warning(_("ignoring too-small changed-path chunk"
@@ -356,13 +357,9 @@ static int graph_read_bloom_data(const unsigned char *chunk_start,
g->chunk_bloom_data = chunk_start;
g->chunk_bloom_data_size = chunk_size;
- hash_version = get_be32(chunk_start);
-
- if (hash_version != 1)
- return 0;
g->bloom_filter_settings = xmalloc(sizeof(struct bloom_filter_settings));
- g->bloom_filter_settings->hash_version = hash_version;
+ g->bloom_filter_settings->hash_version = get_be32(chunk_start);
g->bloom_filter_settings->num_hashes = get_be32(chunk_start + 4);
g->bloom_filter_settings->bits_per_entry = get_be32(chunk_start + 8);
g->bloom_filter_settings->max_changed_paths = DEFAULT_BLOOM_MAX_CHANGES;
@@ -459,7 +456,7 @@ struct commit_graph *parse_commit_graph(struct repo_settings *s,
graph->read_generation_data = 1;
}
- if (s->commit_graph_read_changed_paths) {
+ if (s->commit_graph_changed_paths_version) {
read_chunk(cf, GRAPH_CHUNKID_BLOOMINDEXES,
graph_read_bloom_index, graph);
read_chunk(cf, GRAPH_CHUNKID_BLOOMDATA,
@@ -475,7 +472,8 @@ struct commit_graph *parse_commit_graph(struct repo_settings *s,
FREE_AND_NULL(graph->bloom_filter_settings);
}
- oidread(&graph->oid, graph->data + graph->data_len - graph->hash_len);
+ oidread(&graph->oid, graph->data + graph->data_len - graph->hash_len,
+ the_repository->hash_algo);
free_chunkfile(cf);
return graph;
@@ -543,6 +541,31 @@ static int validate_mixed_generation_chain(struct commit_graph *g)
return 0;
}
+static void validate_mixed_bloom_settings(struct commit_graph *g)
+{
+ struct bloom_filter_settings *settings = NULL;
+ for (; g; g = g->base_graph) {
+ if (!g->bloom_filter_settings)
+ continue;
+ if (!settings) {
+ settings = g->bloom_filter_settings;
+ continue;
+ }
+
+ if (g->bloom_filter_settings->bits_per_entry != settings->bits_per_entry ||
+ g->bloom_filter_settings->num_hashes != settings->num_hashes ||
+ g->bloom_filter_settings->hash_version != settings->hash_version) {
+ g->chunk_bloom_indexes = NULL;
+ g->chunk_bloom_data = NULL;
+ FREE_AND_NULL(g->bloom_filter_settings);
+
+ warning(_("disabling Bloom filters for commit-graph "
+ "layer '%s' due to incompatible settings"),
+ oid_to_hex(&g->oid));
+ }
+ }
+}
+
static int add_graph_to_chain(struct commit_graph *g,
struct commit_graph *chain,
struct object_id *oids,
@@ -565,7 +588,8 @@ static int add_graph_to_chain(struct commit_graph *g,
if (!cur_g ||
!oideq(&oids[n], &cur_g->oid) ||
- !hasheq(oids[n].hash, g->chunk_base_graphs + st_mult(g->hash_len, n))) {
+ !hasheq(oids[n].hash, g->chunk_base_graphs + st_mult(g->hash_len, n),
+ the_repository->hash_algo)) {
warning(_("commit-graph chain does not match"));
return 0;
}
@@ -666,6 +690,7 @@ struct commit_graph *load_commit_graph_chain_fd_st(struct repository *r,
}
validate_mixed_generation_chain(graph_chain);
+ validate_mixed_bloom_settings(graph_chain);
free(oids);
fclose(fp);
@@ -810,6 +835,7 @@ void close_commit_graph(struct raw_object_store *o)
return;
clear_commit_graph_data_slab(&commit_graph_data_slab);
+ deinit_bloom_filters();
free_commit_graph(o->commit_graph);
o->commit_graph = NULL;
}
@@ -837,7 +863,8 @@ static void load_oid_from_graph(struct commit_graph *g,
lex_index = pos - g->num_commits_in_base;
- oidread(oid, g->chunk_oid_lookup + st_mult(g->hash_len, lex_index));
+ oidread(oid, g->chunk_oid_lookup + st_mult(g->hash_len, lex_index),
+ the_repository->hash_algo);
}
static struct commit_list **insert_parent_or_die(struct repository *r,
@@ -1079,7 +1106,7 @@ static struct tree *load_tree_for_commit(struct repository *r,
commit_data = g->chunk_commit_data +
st_mult(GRAPH_DATA_WIDTH, graph_pos - g->num_commits_in_base);
- oidread(&oid, commit_data);
+ oidread(&oid, commit_data, the_repository->hash_algo);
set_commit_tree(c, lookup_tree(r, &oid));
return c->maybe_tree;
@@ -1147,6 +1174,7 @@ struct write_commit_graph_context {
int count_bloom_filter_not_computed;
int count_bloom_filter_trunc_empty;
int count_bloom_filter_trunc_large;
+ int count_bloom_filter_upgraded;
};
static int write_graph_chunk_fanout(struct hashfile *f,
@@ -1597,7 +1625,7 @@ static void compute_reachable_generation_numbers(
timestamp_t gen;
repo_parse_commit(info->r, c);
gen = info->get_generation(c, info->data);
- display_progress(info->progress, info->progress_cnt + 1);
+ display_progress(info->progress, ++info->progress_cnt);
if (gen != GENERATION_NUMBER_ZERO && gen != GENERATION_NUMBER_INFINITY)
continue;
@@ -1754,6 +1782,8 @@ static void trace2_bloom_filter_write_statistics(struct write_commit_graph_conte
ctx->count_bloom_filter_trunc_empty);
trace2_data_intmax("commit-graph", ctx->r, "filter-trunc-large",
ctx->count_bloom_filter_trunc_large);
+ trace2_data_intmax("commit-graph", ctx->r, "filter-upgraded",
+ ctx->count_bloom_filter_upgraded);
}
static void compute_bloom_filters(struct write_commit_graph_context *ctx)
@@ -1795,6 +1825,8 @@ static void compute_bloom_filters(struct write_commit_graph_context *ctx)
ctx->count_bloom_filter_trunc_empty++;
if (computed & BLOOM_TRUNC_LARGE)
ctx->count_bloom_filter_trunc_large++;
+ } else if (computed & BLOOM_UPGRADED) {
+ ctx->count_bloom_filter_upgraded++;
} else if (computed & BLOOM_NOT_COMPUTED)
ctx->count_bloom_filter_not_computed++;
ctx->total_bloom_filter_data_size += filter
@@ -1821,7 +1853,7 @@ static int add_ref_to_set(const char *refname UNUSED,
struct object_id peeled;
struct refs_cb_data *data = (struct refs_cb_data *)cb_data;
- if (!peel_iterated_oid(oid, &peeled))
+ if (!peel_iterated_oid(the_repository, oid, &peeled))
oid = &peeled;
if (oid_object_info(the_repository, oid, NULL) == OBJ_COMMIT)
oidset_insert(data->commits, oid);
@@ -1845,7 +1877,8 @@ int write_commit_graph_reachable(struct object_directory *odb,
data.progress = start_delayed_progress(
_("Collecting referenced commits"), 0);
- for_each_ref(add_ref_to_set, &data);
+ refs_for_each_ref(get_main_ref_store(the_repository), add_ref_to_set,
+ &data);
stop_progress(&data.progress);
@@ -2001,8 +2034,8 @@ static int write_graph_chunk_base(struct hashfile *f,
static int write_commit_graph_file(struct write_commit_graph_context *ctx)
{
uint32_t i;
- int fd;
struct hashfile *f;
+ struct tempfile *graph_layer; /* when ctx->split is non-zero */
struct lock_file lk = LOCK_INIT;
const unsigned hashsz = the_hash_algo->rawsz;
struct strbuf progress_title = STRBUF_INIT;
@@ -2034,24 +2067,23 @@ static int write_commit_graph_file(struct write_commit_graph_context *ctx)
LOCK_DIE_ON_ERROR, 0444);
free(lock_name);
- fd = git_mkstemp_mode(ctx->graph_name, 0444);
- if (fd < 0) {
+ graph_layer = mks_tempfile_m(ctx->graph_name, 0444);
+ if (!graph_layer) {
error(_("unable to create temporary graph layer"));
return -1;
}
- if (adjust_shared_perm(ctx->graph_name)) {
+ if (adjust_shared_perm(get_tempfile_path(graph_layer))) {
error(_("unable to adjust shared permissions for '%s'"),
- ctx->graph_name);
+ get_tempfile_path(graph_layer));
return -1;
}
- f = hashfd(fd, ctx->graph_name);
+ f = hashfd(get_tempfile_fd(graph_layer), get_tempfile_path(graph_layer));
} else {
hold_lock_file_for_update_mode(&lk, ctx->graph_name,
LOCK_DIE_ON_ERROR, 0444);
- fd = get_lock_file_fd(&lk);
- f = hashfd(fd, get_lock_file_path(&lk));
+ f = hashfd(get_lock_file_fd(&lk), get_lock_file_path(&lk));
}
cf = init_chunkfile(f);
@@ -2132,8 +2164,6 @@ static int write_commit_graph_file(struct write_commit_graph_context *ctx)
char *final_graph_name;
int result;
- close(fd);
-
if (!chainf) {
error(_("unable to open commit-graph chain file"));
return -1;
@@ -2168,7 +2198,7 @@ static int write_commit_graph_file(struct write_commit_graph_context *ctx)
free(ctx->commit_graph_filenames_after[ctx->num_commit_graphs_after - 1]);
ctx->commit_graph_filenames_after[ctx->num_commit_graphs_after - 1] = final_graph_name;
- result = rename(ctx->graph_name, final_graph_name);
+ result = rename_tempfile(&graph_layer, final_graph_name);
for (i = 0; i < ctx->num_commit_graphs_after; i++)
fprintf(get_lock_file_fp(&lk), "%s\n", ctx->commit_graph_hash_after[i]);
@@ -2478,6 +2508,13 @@ int write_commit_graph(struct object_directory *odb,
}
if (!commit_graph_compatible(r))
return 0;
+ if (r->settings.commit_graph_changed_paths_version < -1
+ || r->settings.commit_graph_changed_paths_version > 2) {
+ warning(_("attempting to write a commit-graph, but "
+ "'commitGraph.changedPathsVersion' (%d) is not supported"),
+ r->settings.commit_graph_changed_paths_version);
+ return 0;
+ }
CALLOC_ARRAY(ctx, 1);
ctx->r = r;
@@ -2490,6 +2527,7 @@ int write_commit_graph(struct object_directory *odb,
ctx->write_generation_data = (get_configured_generation_version(r) == 2);
ctx->num_generation_data_overflows = 0;
+ bloom_settings.hash_version = r->settings.commit_graph_changed_paths_version;
bloom_settings.bits_per_entry = git_env_ulong("GIT_TEST_BLOOM_SETTINGS_BITS_PER_ENTRY",
bloom_settings.bits_per_entry);
bloom_settings.num_hashes = git_env_ulong("GIT_TEST_BLOOM_SETTINGS_NUM_HASHES",
@@ -2519,12 +2557,20 @@ int write_commit_graph(struct object_directory *odb,
g = ctx->r->objects->commit_graph;
/* We have changed-paths already. Keep them in the next graph */
- if (g && g->chunk_bloom_data) {
+ if (g && g->bloom_filter_settings) {
ctx->changed_paths = 1;
- ctx->bloom_settings = g->bloom_filter_settings;
+
+ /* don't propagate the hash_version unless unspecified */
+ if (bloom_settings.hash_version == -1)
+ bloom_settings.hash_version = g->bloom_filter_settings->hash_version;
+ bloom_settings.bits_per_entry = g->bloom_filter_settings->bits_per_entry;
+ bloom_settings.num_hashes = g->bloom_filter_settings->num_hashes;
+ bloom_settings.max_changed_paths = g->bloom_filter_settings->max_changed_paths;
}
}
+ bloom_settings.hash_version = bloom_settings.hash_version == 2 ? 2 : 1;
+
if (ctx->split) {
struct commit_graph *g = ctx->r->objects->commit_graph;
@@ -2554,7 +2600,8 @@ int write_commit_graph(struct object_directory *odb,
struct commit_graph *g = ctx->r->objects->commit_graph;
for (i = 0; i < g->num_commits; i++) {
struct object_id oid;
- oidread(&oid, g->chunk_oid_lookup + st_mult(g->hash_len, i));
+ oidread(&oid, g->chunk_oid_lookup + st_mult(g->hash_len, i),
+ the_repository->hash_algo);
oid_array_append(&ctx->oids, &oid);
}
}
@@ -2607,6 +2654,9 @@ int write_commit_graph(struct object_directory *odb,
res = write_commit_graph_file(ctx);
+ if (ctx->changed_paths)
+ deinit_bloom_filters();
+
if (ctx->split)
mark_commit_graphs(ctx);
@@ -2673,7 +2723,8 @@ static int verify_one_commit_graph(struct repository *r,
for (i = 0; i < g->num_commits; i++) {
struct commit *graph_commit;
- oidread(&cur_oid, g->chunk_oid_lookup + st_mult(g->hash_len, i));
+ oidread(&cur_oid, g->chunk_oid_lookup + st_mult(g->hash_len, i),
+ the_repository->hash_algo);
if (i && oidcmp(&prev_oid, &cur_oid) >= 0)
graph_report(_("commit-graph has incorrect OID order: %s then %s"),
@@ -2717,7 +2768,8 @@ static int verify_one_commit_graph(struct repository *r,
timestamp_t generation;
display_progress(progress, ++(*seen));
- oidread(&cur_oid, g->chunk_oid_lookup + st_mult(g->hash_len, i));
+ oidread(&cur_oid, g->chunk_oid_lookup + st_mult(g->hash_len, i),
+ the_repository->hash_algo);
graph_commit = lookup_commit(r, &cur_oid);
odb_commit = (struct commit *)create_object(r, &cur_oid, alloc_commit_node(r));
diff --git a/commit-graph.h b/commit-graph.h
index e519cb81cb..6781940195 100644
--- a/commit-graph.h
+++ b/commit-graph.h
@@ -122,6 +122,8 @@ struct commit_graph *load_commit_graph_chain_fd_st(struct repository *r,
struct commit_graph *read_commit_graph_one(struct repository *r,
struct object_directory *odb);
+struct repo_settings;
+
/*
* Callers should initialize the repo_settings with prepare_repo_settings()
* prior to calling parse_commit_graph().
diff --git a/commit-reach.c b/commit-reach.c
index 8f9b008f87..dabc2972e4 100644
--- a/commit-reach.c
+++ b/commit-reach.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "commit.h"
#include "commit-graph.h"
@@ -1106,6 +1108,10 @@ void ahead_behind(struct repository *r,
/* STALE is used here, PARENT2 is used by insert_no_dup(). */
repo_clear_commit_marks(r, PARENT2 | STALE);
+ while (prio_queue_peek(&queue)) {
+ struct commit *c = prio_queue_get(&queue);
+ free_bit_array(c);
+ }
clear_bit_arrays(&bit_arrays);
clear_prio_queue(&queue);
}
diff --git a/commit.c b/commit.c
index 1a479a997c..087cb19f4f 100644
--- a/commit.c
+++ b/commit.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "tag.h"
#include "commit.h"
@@ -680,7 +682,7 @@ unsigned commit_list_count(const struct commit_list *l)
return c;
}
-struct commit_list *copy_commit_list(struct commit_list *list)
+struct commit_list *copy_commit_list(const struct commit_list *list)
{
struct commit_list *head = NULL;
struct commit_list **pp = &head;
@@ -1070,7 +1072,8 @@ struct commit *get_fork_point(const char *refname, struct commit *commit)
memset(&revs, 0, sizeof(revs));
revs.initial = 1;
- for_each_reflog_ent(full_refname, collect_one_reflog_ent, &revs);
+ refs_for_each_reflog_ent(get_main_ref_store(the_repository),
+ full_refname, collect_one_reflog_ent, &revs);
if (!revs.nr)
add_one_commit(&oid, &revs);
@@ -1261,7 +1264,7 @@ int remove_signature(struct strbuf *buf)
return sigs[0].start != NULL;
}
-static void handle_signed_tag(struct commit *parent, struct commit_extra_header ***tail)
+static void handle_signed_tag(const struct commit *parent, struct commit_extra_header ***tail)
{
struct merge_remote_desc *desc;
struct commit_extra_header *mergetag;
@@ -1358,17 +1361,17 @@ void verify_merge_signature(struct commit *commit, int verbosity,
signature_check_clear(&signature_check);
}
-void append_merge_tag_headers(struct commit_list *parents,
+void append_merge_tag_headers(const struct commit_list *parents,
struct commit_extra_header ***tail)
{
while (parents) {
- struct commit *parent = parents->item;
+ const struct commit *parent = parents->item;
handle_signed_tag(parent, tail);
parents = parents->next;
}
}
-static int convert_commit_extra_headers(struct commit_extra_header *orig,
+static int convert_commit_extra_headers(const struct commit_extra_header *orig,
struct commit_extra_header **result)
{
const struct git_hash_algo *compat = the_repository->compat_hash_algo;
@@ -1402,7 +1405,7 @@ static int convert_commit_extra_headers(struct commit_extra_header *orig,
}
static void add_extra_header(struct strbuf *buffer,
- struct commit_extra_header *extra)
+ const struct commit_extra_header *extra)
{
strbuf_addstr(buffer, extra->key);
if (extra->len)
@@ -1516,7 +1519,7 @@ void free_commit_extra_headers(struct commit_extra_header *extra)
}
int commit_tree(const char *msg, size_t msg_len, const struct object_id *tree,
- struct commit_list *parents, struct object_id *ret,
+ const struct commit_list *parents, struct object_id *ret,
const char *author, const char *sign_commit)
{
struct commit_extra_header *extra = NULL, **tail = &extra;
@@ -1648,7 +1651,7 @@ static void write_commit_tree(struct strbuf *buffer, const char *msg, size_t msg
const struct object_id *tree,
const struct object_id *parents, size_t parents_len,
const char *author, const char *committer,
- struct commit_extra_header *extra)
+ const struct commit_extra_header *extra)
{
int encoding_is_utf8;
size_t i;
@@ -1689,10 +1692,10 @@ static void write_commit_tree(struct strbuf *buffer, const char *msg, size_t msg
int commit_tree_extended(const char *msg, size_t msg_len,
const struct object_id *tree,
- struct commit_list *parents, struct object_id *ret,
+ const struct commit_list *parents, struct object_id *ret,
const char *author, const char *committer,
const char *sign_commit,
- struct commit_extra_header *extra)
+ const struct commit_extra_header *extra)
{
struct repository *r = the_repository;
int result = 0;
@@ -1714,10 +1717,8 @@ int commit_tree_extended(const char *msg, size_t msg_len,
nparents = commit_list_count(parents);
CALLOC_ARRAY(parent_buf, nparents);
i = 0;
- while (parents) {
- struct commit *parent = pop_commit(&parents);
- oidcpy(&parent_buf[i++], &parent->object.oid);
- }
+ for (const struct commit_list *p = parents; p; p = p->next)
+ oidcpy(&parent_buf[i++], &p->item->object.oid);
write_commit_tree(&buffer, msg, msg_len, tree, parent_buf, nparents, author, committer, extra);
if (sign_commit && sign_commit_to_strbuf(&sig, &buffer, sign_commit)) {
@@ -1813,7 +1814,7 @@ out:
define_commit_slab(merge_desc_slab, struct merge_remote_desc *);
static struct merge_desc_slab merge_desc_slab = COMMIT_SLAB_INIT(1, merge_desc_slab);
-struct merge_remote_desc *merge_remote_util(struct commit *commit)
+struct merge_remote_desc *merge_remote_util(const struct commit *commit)
{
return *merge_desc_slab_at(&merge_desc_slab, commit);
}
@@ -1869,20 +1870,12 @@ struct commit_list **commit_list_append(struct commit *commit,
return &new_commit->next;
}
-const char *find_header_mem(const char *msg, size_t len,
- const char *key, size_t *out_len)
+const char *find_commit_header(const char *msg, const char *key, size_t *out_len)
{
int key_len = strlen(key);
const char *line = msg;
- /*
- * NEEDSWORK: It's possible for strchrnul() to scan beyond the range
- * given by len. However, current callers are safe because they compute
- * len by scanning a NUL-terminated block of memory starting at msg.
- * Nonetheless, it would be better to ensure the function does not look
- * at msg beyond the len provided by the caller.
- */
- while (line && line < msg + len) {
+ while (line) {
const char *eol = strchrnul(line, '\n');
if (line == eol)
@@ -1899,10 +1892,6 @@ const char *find_header_mem(const char *msg, size_t len,
return NULL;
}
-const char *find_commit_header(const char *msg, const char *key, size_t *out_len)
-{
- return find_header_mem(msg, strlen(msg), key, out_len);
-}
/*
* Inspect the given string and determine the true "end" of the log message, in
* order to find where to put a new Signed-off-by trailer. Ignored are
diff --git a/commit.h b/commit.h
index 62fe0d77a7..d62b1d93f9 100644
--- a/commit.h
+++ b/commit.h
@@ -181,7 +181,7 @@ struct commit_list *commit_list_insert_by_date(struct commit *item,
void commit_list_sort_by_date(struct commit_list **list);
/* Shallow copy of the input list */
-struct commit_list *copy_commit_list(struct commit_list *list);
+struct commit_list *copy_commit_list(const struct commit_list *list);
/* Modify list in-place to reverse it, returning new head; list will be tail */
struct commit_list *reverse_commit_list(struct commit_list *list);
@@ -260,19 +260,19 @@ struct commit_extra_header {
size_t len;
};
-void append_merge_tag_headers(struct commit_list *parents,
+void append_merge_tag_headers(const struct commit_list *parents,
struct commit_extra_header ***tail);
int commit_tree(const char *msg, size_t msg_len,
const struct object_id *tree,
- struct commit_list *parents, struct object_id *ret,
+ const struct commit_list *parents, struct object_id *ret,
const char *author, const char *sign_commit);
int commit_tree_extended(const char *msg, size_t msg_len,
const struct object_id *tree,
- struct commit_list *parents, struct object_id *ret,
+ const struct commit_list *parents, struct object_id *ret,
const char *author, const char *committer,
- const char *sign_commit, struct commit_extra_header *);
+ const char *sign_commit, const struct commit_extra_header *);
struct commit_extra_header *read_commit_extra_headers(struct commit *, const char **);
@@ -280,17 +280,12 @@ void free_commit_extra_headers(struct commit_extra_header *extra);
/*
* Search the commit object contents given by "msg" for the header "key".
- * Reads up to "len" bytes of "msg".
* Returns a pointer to the start of the header contents, or NULL. The length
* of the header, up to the first newline, is returned via out_len.
*
* Note that some headers (like mergetag) may be multi-line. It is the caller's
* responsibility to parse further in this case!
*/
-const char *find_header_mem(const char *msg, size_t len,
- const char *key,
- size_t *out_len);
-
const char *find_commit_header(const char *msg, const char *key,
size_t *out_len);
@@ -306,7 +301,7 @@ struct merge_remote_desc {
struct object *obj; /* the named object, could be a tag */
char name[FLEX_ARRAY];
};
-struct merge_remote_desc *merge_remote_util(struct commit *);
+struct merge_remote_desc *merge_remote_util(const struct commit *);
void set_merge_remote_desc(struct commit *commit,
const char *name, struct object *obj);
diff --git a/common-main.c b/common-main.c
index 033778b3c5..8e68ac9e42 100644
--- a/common-main.c
+++ b/common-main.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "exec-cmd.h"
#include "gettext.h"
@@ -48,7 +50,7 @@ int main(int argc, const char **argv)
setlocale(LC_CTYPE, "");
git_setup_gettext();
- initialize_the_repository();
+ initialize_repository(the_repository);
attr_start();
diff --git a/compat/basename.c b/compat/basename.c
index 96bd9533b4..c33579ef61 100644
--- a/compat/basename.c
+++ b/compat/basename.c
@@ -10,7 +10,13 @@ char *gitbasename (char *path)
skip_dos_drive_prefix(&path);
if (!path || !*path)
- return ".";
+ /*
+ * basename(3P) is mis-specified because it returns a
+ * non-constant pointer even though it is specified to return a
+ * pointer to internal memory at times. The cast is a result of
+ * that.
+ */
+ return (char *) ".";
for (base = path; *path; path++) {
if (!is_dir_sep(*path))
@@ -34,7 +40,13 @@ char *gitdirname(char *path)
int dos_drive_prefix;
if (!p)
- return ".";
+ /*
+ * dirname(3P) is mis-specified because it returns a
+ * non-constant pointer even though it is specified to return a
+ * pointer to internal memory at times. The cast is a result of
+ * that.
+ */
+ return (char *) ".";
if ((dos_drive_prefix = skip_dos_drive_prefix(&p)) && !*p)
goto dot;
diff --git a/compat/fsmonitor/fsm-ipc-darwin.c b/compat/fsmonitor/fsm-ipc-darwin.c
index 6f3a95410c..52f4f29720 100644
--- a/compat/fsmonitor/fsm-ipc-darwin.c
+++ b/compat/fsmonitor/fsm-ipc-darwin.c
@@ -17,7 +17,7 @@ const char *fsmonitor_ipc__get_path(struct repository *r)
git_SHA_CTX sha1ctx;
char *sock_dir = NULL;
struct strbuf ipc_file = STRBUF_INIT;
- unsigned char hash[GIT_MAX_RAWSZ];
+ unsigned char hash[GIT_SHA1_RAWSZ];
if (!r)
BUG("No repository passed into fsmonitor_ipc__get_path");
@@ -41,9 +41,10 @@ const char *fsmonitor_ipc__get_path(struct repository *r)
/* Create the socket file in either socketDir or $HOME */
if (sock_dir && *sock_dir) {
strbuf_addf(&ipc_file, "%s/.git-fsmonitor-%s",
- sock_dir, hash_to_hex(hash));
+ sock_dir, hash_to_hex_algop(hash, &hash_algos[GIT_HASH_SHA1]));
} else {
- strbuf_addf(&ipc_file, "~/.git-fsmonitor-%s", hash_to_hex(hash));
+ strbuf_addf(&ipc_file, "~/.git-fsmonitor-%s",
+ hash_to_hex_algop(hash, &hash_algos[GIT_HASH_SHA1]));
}
free(sock_dir);
diff --git a/compat/mingw.c b/compat/mingw.c
index 4876344b5b..29d3f09768 100644
--- a/compat/mingw.c
+++ b/compat/mingw.c
@@ -26,7 +26,6 @@ static const int delay[] = { 0, 1, 10, 20, 40 };
void open_in_gdb(void)
{
static struct child_process cp = CHILD_PROCESS_INIT;
- extern char *_pgmptr;
strvec_pushl(&cp.args, "mintty", "gdb", NULL);
strvec_pushf(&cp.args, "--pid=%d", getpid());
@@ -1547,7 +1546,7 @@ static int is_msys2_sh(const char *cmd)
return ret;
}
- if (ends_with(cmd, "\\sh.exe")) {
+ if (ends_with(cmd, "\\sh.exe") || ends_with(cmd, "/sh.exe")) {
static char *sh;
if (!sh)
@@ -2279,7 +2278,11 @@ struct passwd *getpwuid(int uid)
p->pw_name = user_name;
p->pw_gecos = get_extended_user_info(NameDisplay);
if (!p->pw_gecos)
- p->pw_gecos = "unknown";
+ /*
+ * Data returned by getpwuid(3P) is treated as internal and
+ * must never be written to or freed.
+ */
+ p->pw_gecos = (char *) "unknown";
p->pw_dir = NULL;
initialized = 1;
@@ -2800,16 +2803,16 @@ int is_path_owned_by_current_sid(const char *path, struct strbuf *report)
strbuf_addf(report, "'%s' is on a file system that does "
"not record ownership\n", path);
} else if (report) {
- LPSTR str1, str2, str3, str4, to_free1 = NULL,
- to_free3 = NULL, to_local_free2 = NULL,
- to_local_free4 = NULL;
+ PCSTR str1, str2, str3, str4;
+ LPSTR to_free1 = NULL, to_free3 = NULL,
+ to_local_free2 = NULL, to_local_free4 = NULL;
- if (user_sid_to_user_name(sid, &str1))
- to_free1 = str1;
+ if (user_sid_to_user_name(sid, &to_free1))
+ str1 = to_free1;
else
str1 = "(inconvertible)";
- if (ConvertSidToStringSidA(sid, &str2))
- to_local_free2 = str2;
+ if (ConvertSidToStringSidA(sid, &to_local_free2))
+ str2 = to_local_free2;
else
str2 = "(inconvertible)";
@@ -2822,13 +2825,13 @@ int is_path_owned_by_current_sid(const char *path, struct strbuf *report)
str4 = "(invalid)";
} else {
if (user_sid_to_user_name(current_user_sid,
- &str3))
- to_free3 = str3;
+ &to_free3))
+ str3 = to_free3;
else
str3 = "(inconvertible)";
if (ConvertSidToStringSidA(current_user_sid,
- &str4))
- to_local_free4 = str4;
+ &to_local_free4))
+ str4 = to_local_free4;
else
str4 = "(inconvertible)";
}
@@ -3159,6 +3162,7 @@ int uname(struct utsname *buf)
return 0;
}
+#ifndef NO_UNIX_SOCKETS
int mingw_have_unix_sockets(void)
{
SC_HANDLE scm, srvc;
@@ -3177,3 +3181,4 @@ int mingw_have_unix_sockets(void)
}
return ret;
}
+#endif
diff --git a/compat/regex/regcomp.c b/compat/regex/regcomp.c
index d1bc09e49b..6c5d455e92 100644
--- a/compat/regex/regcomp.c
+++ b/compat/regex/regcomp.c
@@ -848,7 +848,7 @@ init_dfa (re_dfa_t *dfa, size_t pat_len)
{
unsigned int table_size;
#ifndef _LIBC
- char *codeset_name;
+ const char *codeset_name;
#endif
memset (dfa, '\0', sizeof (re_dfa_t));
@@ -868,7 +868,7 @@ init_dfa (re_dfa_t *dfa, size_t pat_len)
if (table_size > pat_len)
break;
- dfa->state_table = calloc (sizeof (struct re_state_table_entry), table_size);
+ dfa->state_table = calloc (table_size, sizeof (struct re_state_table_entry));
dfa->state_hash_mask = table_size - 1;
dfa->mb_cur_max = MB_CUR_MAX;
@@ -936,7 +936,7 @@ init_dfa (re_dfa_t *dfa, size_t pat_len)
{
int i, j, ch;
- dfa->sb_char = (re_bitset_ptr_t) calloc (sizeof (bitset_t), 1);
+ dfa->sb_char = (re_bitset_ptr_t) calloc (1, sizeof (bitset_t));
if (BE (dfa->sb_char == NULL, 0))
return REG_ESPACE;
@@ -3079,9 +3079,9 @@ parse_bracket_exp (re_string_t *regexp, re_dfa_t *dfa, re_token_t *token,
_NL_COLLATE_SYMB_EXTRAMB);
}
#endif
- sbcset = (re_bitset_ptr_t) calloc (sizeof (bitset_t), 1);
+ sbcset = (re_bitset_ptr_t) calloc (1, sizeof (bitset_t));
#ifdef RE_ENABLE_I18N
- mbcset = (re_charset_t *) calloc (sizeof (re_charset_t), 1);
+ mbcset = (re_charset_t *) calloc (1, sizeof (re_charset_t));
#endif /* RE_ENABLE_I18N */
#ifdef RE_ENABLE_I18N
if (BE (sbcset == NULL || mbcset == NULL, 0))
@@ -3626,9 +3626,9 @@ build_charclass_op (re_dfa_t *dfa, RE_TRANSLATE_TYPE trans,
re_token_t br_token;
bin_tree_t *tree;
- sbcset = (re_bitset_ptr_t) calloc (sizeof (bitset_t), 1);
+ sbcset = (re_bitset_ptr_t) calloc (1, sizeof (bitset_t));
#ifdef RE_ENABLE_I18N
- mbcset = (re_charset_t *) calloc (sizeof (re_charset_t), 1);
+ mbcset = (re_charset_t *) calloc (1, sizeof (re_charset_t));
#endif /* RE_ENABLE_I18N */
#ifdef RE_ENABLE_I18N
diff --git a/compat/regex/regex_internal.c b/compat/regex/regex_internal.c
index ec51cf3446..ec5cc5d2dd 100644
--- a/compat/regex/regex_internal.c
+++ b/compat/regex/regex_internal.c
@@ -1628,7 +1628,7 @@ create_ci_newstate (const re_dfa_t *dfa, const re_node_set *nodes,
reg_errcode_t err;
re_dfastate_t *newstate;
- newstate = (re_dfastate_t *) calloc (sizeof (re_dfastate_t), 1);
+ newstate = (re_dfastate_t *) calloc (1, sizeof (re_dfastate_t));
if (BE (newstate == NULL, 0))
return NULL;
err = re_node_set_init_copy (&newstate->nodes, nodes);
@@ -1678,7 +1678,7 @@ create_cd_newstate (const re_dfa_t *dfa, const re_node_set *nodes,
reg_errcode_t err;
re_dfastate_t *newstate;
- newstate = (re_dfastate_t *) calloc (sizeof (re_dfastate_t), 1);
+ newstate = (re_dfastate_t *) calloc (1, sizeof (re_dfastate_t));
if (BE (newstate == NULL, 0))
return NULL;
err = re_node_set_init_copy (&newstate->nodes, nodes);
diff --git a/compat/regex/regexec.c b/compat/regex/regexec.c
index 49358ae475..e92be5741d 100644
--- a/compat/regex/regexec.c
+++ b/compat/regex/regexec.c
@@ -2796,8 +2796,8 @@ get_subexp (re_match_context_t *mctx, int bkref_node, int bkref_str_idx)
continue; /* No. */
if (sub_top->path == NULL)
{
- sub_top->path = calloc (sizeof (state_array_t),
- sl_str - sub_top->str_idx + 1);
+ sub_top->path = calloc (sl_str - sub_top->str_idx + 1,
+ sizeof (state_array_t));
if (sub_top->path == NULL)
return REG_ESPACE;
}
@@ -3361,7 +3361,7 @@ build_trtable (const re_dfa_t *dfa, re_dfastate_t *state)
if (ndests == 0)
{
state->trtable = (re_dfastate_t **)
- calloc (sizeof (re_dfastate_t *), SBC_MAX);
+ calloc (SBC_MAX, sizeof (re_dfastate_t *));
return 1;
}
return 0;
@@ -3457,7 +3457,7 @@ out_free:
discern by looking at the character code: allocate a
256-entry transition table. */
trtable = state->trtable =
- (re_dfastate_t **) calloc (sizeof (re_dfastate_t *), SBC_MAX);
+ (re_dfastate_t **) calloc (SBC_MAX, sizeof (re_dfastate_t *));
if (BE (trtable == NULL, 0))
goto out_free;
@@ -3488,7 +3488,7 @@ out_free:
transition tables, one starting at trtable[0] and one
starting at trtable[SBC_MAX]. */
trtable = state->word_trtable =
- (re_dfastate_t **) calloc (sizeof (re_dfastate_t *), 2 * SBC_MAX);
+ (re_dfastate_t **) calloc (2 * SBC_MAX, sizeof (re_dfastate_t *));
if (BE (trtable == NULL, 0))
goto out_free;
diff --git a/compat/sha1-chunked.c b/compat/sha1-chunked.c
index a4a6f930d7..0446f9983f 100644
--- a/compat/sha1-chunked.c
+++ b/compat/sha1-chunked.c
@@ -1,5 +1,5 @@
#include "git-compat-util.h"
-#include "hash-ll.h"
+#include "hash.h"
int git_SHA1_Update_Chunked(platform_SHA_CTX *c, const void *data, size_t len)
{
diff --git a/compat/win32/path-utils.c b/compat/win32/path-utils.c
index ebf2f12eb6..b658ca3f81 100644
--- a/compat/win32/path-utils.c
+++ b/compat/win32/path-utils.c
@@ -1,4 +1,5 @@
#include "../../git-compat-util.h"
+#include "../../environment.h"
int win32_has_dos_drive_prefix(const char *path)
{
@@ -50,3 +51,39 @@ int win32_offset_1st_component(const char *path)
return pos + is_dir_sep(*pos) - path;
}
+
+int win32_fspathncmp(const char *a, const char *b, size_t count)
+{
+ int diff;
+
+ for (;;) {
+ if (!count--)
+ return 0;
+ if (!*a)
+ return *b ? -1 : 0;
+ if (!*b)
+ return +1;
+
+ if (is_dir_sep(*a)) {
+ if (!is_dir_sep(*b))
+ return -1;
+ a++;
+ b++;
+ continue;
+ } else if (is_dir_sep(*b))
+ return +1;
+
+ diff = ignore_case ?
+ (unsigned char)tolower(*a) - (int)(unsigned char)tolower(*b) :
+ (unsigned char)*a - (int)(unsigned char)*b;
+ if (diff)
+ return diff;
+ a++;
+ b++;
+ }
+}
+
+int win32_fspathcmp(const char *a, const char *b)
+{
+ return win32_fspathncmp(a, b, (size_t)-1);
+}
diff --git a/compat/win32/path-utils.h b/compat/win32/path-utils.h
index 65fa3b9263..a561c700e7 100644
--- a/compat/win32/path-utils.h
+++ b/compat/win32/path-utils.h
@@ -29,5 +29,9 @@ static inline int win32_has_dir_sep(const char *path)
#define has_dir_sep(path) win32_has_dir_sep(path)
int win32_offset_1st_component(const char *path);
#define offset_1st_component win32_offset_1st_component
+int win32_fspathcmp(const char *a, const char *b);
+#define fspathcmp win32_fspathcmp
+int win32_fspathncmp(const char *a, const char *b, size_t count);
+#define fspathncmp win32_fspathncmp
#endif
diff --git a/compat/win32/trace2_win32_process_info.c b/compat/win32/trace2_win32_process_info.c
index 3ef0936f6f..f147da706a 100644
--- a/compat/win32/trace2_win32_process_info.c
+++ b/compat/win32/trace2_win32_process_info.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "../../git-compat-util.h"
#include "../../json-writer.h"
#include "../../repository.h"
diff --git a/compat/winansi.c b/compat/winansi.c
index f83610f684..575813bde8 100644
--- a/compat/winansi.c
+++ b/compat/winansi.c
@@ -139,7 +139,7 @@ static void write_console(unsigned char *str, size_t len)
/* convert utf-8 to utf-16 */
int wlen = xutftowcsn(wbuf, (char*) str, ARRAY_SIZE(wbuf), len);
if (wlen < 0) {
- wchar_t *err = L"[invalid]";
+ const wchar_t *err = L"[invalid]";
WriteConsoleW(console, err, wcslen(err), &dummy, NULL);
return;
}
diff --git a/config.c b/config.c
index ae3652b08f..6421894614 100644
--- a/config.c
+++ b/config.c
@@ -5,6 +5,9 @@
* Copyright (C) Johannes Schindelin, 2005
*
*/
+
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "abspath.h"
#include "advice.h"
@@ -125,7 +128,7 @@ struct config_include_data {
config_fn_t fn;
void *data;
const struct config_options *opts;
- struct git_config_source *config_source;
+ const struct git_config_source *config_source;
struct repository *repo;
/*
@@ -303,7 +306,8 @@ static int include_by_branch(const char *cond, size_t cond_len)
int ret;
struct strbuf pattern = STRBUF_INIT;
const char *refname = !the_repository->gitdir ?
- NULL : resolve_ref_unsafe("HEAD", 0, NULL, &flags);
+ NULL : refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
+ "HEAD", 0, NULL, &flags);
const char *shortname;
if (!refname || !(flags & REF_ISSYMREF) ||
@@ -1243,6 +1247,15 @@ ssize_t git_config_ssize_t(const char *name, const char *value,
return ret;
}
+double git_config_double(const char *name, const char *value,
+ const struct key_value_info *kvi)
+{
+ double ret;
+ if (!git_parse_double(value, &ret))
+ die_bad_number(name, value, kvi);
+ return ret;
+}
+
static const struct fsync_component_name {
const char *name;
enum fsync_component component_bits;
@@ -1337,7 +1350,7 @@ int git_config_bool(const char *name, const char *value)
return v;
}
-int git_config_string(const char **dest, const char *var, const char *value)
+int git_config_string(char **dest, const char *var, const char *value)
{
if (!value)
return config_error_nonbool(var);
@@ -1345,7 +1358,7 @@ int git_config_string(const char **dest, const char *var, const char *value)
return 0;
}
-int git_config_pathname(const char **dest, const char *var, const char *value)
+int git_config_pathname(char **dest, const char *var, const char *value)
{
if (!value)
return config_error_nonbool(var);
@@ -1413,11 +1426,15 @@ static int git_default_core_config(const char *var, const char *value,
return 0;
}
- if (!strcmp(var, "core.attributesfile"))
+ if (!strcmp(var, "core.attributesfile")) {
+ FREE_AND_NULL(git_attributes_file);
return git_config_pathname(&git_attributes_file, var, value);
+ }
- if (!strcmp(var, "core.hookspath"))
+ if (!strcmp(var, "core.hookspath")) {
+ FREE_AND_NULL(git_hooks_path);
return git_config_pathname(&git_hooks_path, var, value);
+ }
if (!strcmp(var, "core.bare")) {
is_bare_repository_cfg = git_config_bool(var, value);
@@ -1455,10 +1472,10 @@ static int git_default_core_config(const char *var, const char *value,
if (!strcasecmp(value, "auto"))
default_abbrev = -1;
else if (!git_parse_maybe_bool_text(value))
- default_abbrev = the_hash_algo->hexsz;
+ default_abbrev = GIT_MAX_HEXSZ;
else {
int abbrev = git_config_int(var, value, ctx->kvi);
- if (abbrev < minimum_abbrev || abbrev > the_hash_algo->hexsz)
+ if (abbrev < minimum_abbrev)
return error(_("abbrev length out of range: %d"), abbrev);
default_abbrev = abbrev;
}
@@ -1552,18 +1569,23 @@ static int git_default_core_config(const char *var, const char *value,
return 0;
}
- if (!strcmp(var, "core.checkroundtripencoding"))
+ if (!strcmp(var, "core.checkroundtripencoding")) {
+ FREE_AND_NULL(check_roundtrip_encoding);
return git_config_string(&check_roundtrip_encoding, var, value);
+ }
if (!strcmp(var, "core.notesref")) {
if (!value)
return config_error_nonbool(var);
+ free(notes_ref_name);
notes_ref_name = xstrdup(value);
return 0;
}
- if (!strcmp(var, "core.editor"))
+ if (!strcmp(var, "core.editor")) {
+ FREE_AND_NULL(editor_program);
return git_config_string(&editor_program, var, value);
+ }
if (!strcmp(var, "core.commentchar") ||
!strcmp(var, "core.commentstring")) {
@@ -1581,11 +1603,13 @@ static int git_default_core_config(const char *var, const char *value,
return 0;
}
- if (!strcmp(var, "core.askpass"))
+ if (!strcmp(var, "core.askpass")) {
+ FREE_AND_NULL(askpass_program);
return git_config_string(&askpass_program, var, value);
+ }
if (!strcmp(var, "core.excludesfile")) {
- free((char *)excludes_file);
+ FREE_AND_NULL(excludes_file);
return git_config_pathname(&excludes_file, var, value);
}
@@ -1688,11 +1712,15 @@ static int git_default_sparse_config(const char *var, const char *value)
static int git_default_i18n_config(const char *var, const char *value)
{
- if (!strcmp(var, "i18n.commitencoding"))
+ if (!strcmp(var, "i18n.commitencoding")) {
+ FREE_AND_NULL(git_commit_encoding);
return git_config_string(&git_commit_encoding, var, value);
+ }
- if (!strcmp(var, "i18n.logoutputencoding"))
+ if (!strcmp(var, "i18n.logoutputencoding")) {
+ FREE_AND_NULL(git_log_output_encoding);
return git_config_string(&git_log_output_encoding, var, value);
+ }
/* Add other config variables here and to Documentation/config.txt. */
return 0;
@@ -1765,10 +1793,15 @@ static int git_default_push_config(const char *var, const char *value)
static int git_default_mailmap_config(const char *var, const char *value)
{
- if (!strcmp(var, "mailmap.file"))
+ if (!strcmp(var, "mailmap.file")) {
+ FREE_AND_NULL(git_mailmap_file);
return git_config_pathname(&git_mailmap_file, var, value);
- if (!strcmp(var, "mailmap.blob"))
+ }
+
+ if (!strcmp(var, "mailmap.blob")) {
+ FREE_AND_NULL(git_mailmap_blob);
return git_config_string(&git_mailmap_blob, var, value);
+ }
/* Add other config variables here and to Documentation/config.txt. */
return 0;
@@ -1776,8 +1809,10 @@ static int git_default_mailmap_config(const char *var, const char *value)
static int git_default_attr_config(const char *var, const char *value)
{
- if (!strcmp(var, "attr.tree"))
+ if (!strcmp(var, "attr.tree")) {
+ FREE_AND_NULL(git_attr_tree);
return git_config_string(&git_attr_tree, var, value);
+ }
/*
* Add other attribute related config variables here and to
@@ -2105,7 +2140,7 @@ static int do_git_config_sequence(const struct config_options *opts,
}
int config_with_options(config_fn_t fn, void *data,
- struct git_config_source *config_source,
+ const struct git_config_source *config_source,
struct repository *repo,
const struct config_options *opts)
{
@@ -2404,7 +2439,7 @@ int git_configset_get_string(struct config_set *set, const char *key, char **des
{
const char *value;
if (!git_configset_get_value(set, key, &value, NULL))
- return git_config_string((const char **)dest, key, value);
+ return git_config_string(dest, key, value);
else
return 1;
}
@@ -2482,7 +2517,7 @@ int git_configset_get_maybe_bool(struct config_set *set, const char *key, int *d
return 1;
}
-int git_configset_get_pathname(struct config_set *set, const char *key, const char **dest)
+int git_configset_get_pathname(struct config_set *set, const char *key, char **dest)
{
const char *value;
if (!git_configset_get_value(set, key, &value, NULL))
@@ -2627,7 +2662,7 @@ int repo_config_get_maybe_bool(struct repository *repo,
}
int repo_config_get_pathname(struct repository *repo,
- const char *key, const char **dest)
+ const char *key, char **dest)
{
int ret;
git_config_check_init(repo);
@@ -2726,7 +2761,7 @@ int git_config_get_maybe_bool(const char *key, int *dest)
return repo_config_get_maybe_bool(the_repository, key, dest);
}
-int git_config_get_pathname(const char *key, const char **dest)
+int git_config_get_pathname(const char *key, char **dest)
{
return repo_config_get_pathname(the_repository, key, dest);
}
@@ -2822,7 +2857,6 @@ void git_die_config_linenr(const char *key, const char *filename, int linenr)
key, filename, linenr);
}
-NORETURN __attribute__((format(printf, 2, 3)))
void git_die_config(const char *key, const char *err, ...)
{
const struct string_list *values;
@@ -3182,14 +3216,10 @@ void git_config_set(const char *key, const char *value)
trace2_cmd_set_config(key, value);
}
-/*
- * The ownership rule is that the caller will own the string
- * if it receives a piece of memory different from what it passed
- * as the parameter.
- */
-const char *git_config_prepare_comment_string(const char *comment)
+char *git_config_prepare_comment_string(const char *comment)
{
size_t leading_blanks;
+ char *prepared;
if (!comment)
return NULL;
@@ -3210,13 +3240,13 @@ const char *git_config_prepare_comment_string(const char *comment)
leading_blanks = strspn(comment, " \t");
if (leading_blanks && comment[leading_blanks] == '#')
- ; /* use it as-is */
+ prepared = xstrdup(comment); /* use it as-is */
else if (comment[0] == '#')
- comment = xstrfmt(" %s", comment);
+ prepared = xstrfmt(" %s", comment);
else
- comment = xstrfmt(" # %s", comment);
+ prepared = xstrfmt(" # %s", comment);
- return comment;
+ return prepared;
}
static void validate_comment_string(const char *comment)
diff --git a/config.h b/config.h
index f4966e3749..54b47dec9e 100644
--- a/config.h
+++ b/config.h
@@ -232,7 +232,7 @@ void git_config(config_fn_t fn, void *);
* sets `opts.respect_includes` to `1` by default.
*/
int config_with_options(config_fn_t fn, void *,
- struct git_config_source *config_source,
+ const struct git_config_source *config_source,
struct repository *repo,
const struct config_options *opts);
@@ -262,6 +262,13 @@ ssize_t git_config_ssize_t(const char *, const char *,
const struct key_value_info *);
/**
+ * Identically to `git_config_double`, but for double-precision floating point
+ * values.
+ */
+double git_config_double(const char *, const char *,
+ const struct key_value_info *);
+
+/**
* Same as `git_config_bool`, except that integers are returned as-is, and
* an `is_bool` flag is unset.
*/
@@ -280,13 +287,13 @@ int git_config_bool(const char *, const char *);
* Allocates and copies the value string into the `dest` parameter; if no
* string is given, prints an error message and returns -1.
*/
-int git_config_string(const char **, const char *, const char *);
+int git_config_string(char **, const char *, const char *);
/**
* Similar to `git_config_string`, but expands `~` or `~user` into the
* user's home directory when found at the beginning of the path.
*/
-int git_config_pathname(const char **, const char *, const char *);
+int git_config_pathname(char **, const char *, const char *);
int git_config_expiry_date(timestamp_t *, const char *, const char *);
int git_config_color(char *, const char *, const char *);
@@ -338,7 +345,7 @@ void git_config_set_multivar(const char *, const char *, const char *, unsigned)
int repo_config_set_multivar_gently(struct repository *, const char *, const char *, const char *, unsigned);
int git_config_set_multivar_in_file_gently(const char *, const char *, const char *, const char *, const char *, unsigned);
-const char *git_config_prepare_comment_string(const char *);
+char *git_config_prepare_comment_string(const char *);
/**
* takes four parameters:
@@ -541,7 +548,7 @@ int git_configset_get_ulong(struct config_set *cs, const char *key, unsigned lon
int git_configset_get_bool(struct config_set *cs, const char *key, int *dest);
int git_configset_get_bool_or_int(struct config_set *cs, const char *key, int *is_bool, int *dest);
int git_configset_get_maybe_bool(struct config_set *cs, const char *key, int *dest);
-int git_configset_get_pathname(struct config_set *cs, const char *key, const char **dest);
+int git_configset_get_pathname(struct config_set *cs, const char *key, char **dest);
/* Functions for reading a repository's config */
struct repository;
@@ -577,7 +584,7 @@ int repo_config_get_bool_or_int(struct repository *repo,
int repo_config_get_maybe_bool(struct repository *repo,
const char *key, int *dest);
int repo_config_get_pathname(struct repository *repo,
- const char *key, const char **dest);
+ const char *key, char **dest);
/*
* Functions for reading protected config. By definition, protected
@@ -687,7 +694,7 @@ int git_config_get_maybe_bool(const char *key, int *dest);
* Similar to `git_config_get_string`, but expands `~` or `~user` into
* the user's home directory when found at the beginning of the path.
*/
-int git_config_get_pathname(const char *key, const char **dest);
+int git_config_get_pathname(const char *key, char **dest);
int git_config_get_index_threads(int *dest);
int git_config_get_split_index(void);
diff --git a/config.mak.dev b/config.mak.dev
index 981304727c..5229c35484 100644
--- a/config.mak.dev
+++ b/config.mak.dev
@@ -10,7 +10,7 @@ endif
DEVELOPER_CFLAGS += -Wall
ifeq ($(filter no-pedantic,$(DEVOPTS)),)
DEVELOPER_CFLAGS += -pedantic
-ifneq (($or $(filter gcc5,$(COMPILER_FEATURES)),$(filter clang4,$(COMPILER_FEATURES))),)
+ifneq ($(or $(filter gcc5,$(COMPILER_FEATURES)),$(filter clang4,$(COMPILER_FEATURES))),)
DEVELOPER_CFLAGS += -Wpedantic
ifneq ($(filter gcc10,$(COMPILER_FEATURES)),)
ifeq ($(uname_S),MINGW)
@@ -37,6 +37,7 @@ DEVELOPER_CFLAGS += -Wpointer-arith
DEVELOPER_CFLAGS += -Wstrict-prototypes
DEVELOPER_CFLAGS += -Wunused
DEVELOPER_CFLAGS += -Wvla
+DEVELOPER_CFLAGS += -Wwrite-strings
DEVELOPER_CFLAGS += -fno-common
ifneq ($(filter clang4,$(COMPILER_FEATURES)),)
diff --git a/config.mak.uname b/config.mak.uname
index a7607a5676..85d63821ec 100644
--- a/config.mak.uname
+++ b/config.mak.uname
@@ -68,6 +68,7 @@ ifeq ($(uname_S),Linux)
ifneq ($(findstring .el7.,$(uname_R)),)
BASIC_CFLAGS += -std=c99
endif
+ LINK_FUZZ_PROGRAMS = YesPlease
endif
ifeq ($(uname_S),GNU/kFreeBSD)
HAVE_ALLOCA_H = YesPlease
diff --git a/connect.c b/connect.c
index 0d77737a53..cf84e631e9 100644
--- a/connect.c
+++ b/connect.c
@@ -38,8 +38,8 @@ static int check_ref(const char *name, unsigned int flags)
REFNAME_ALLOW_ONELEVEL))
return 0;
- /* REF_HEADS means that we want regular branch heads */
- if ((flags & REF_HEADS) && starts_with(name, "heads/"))
+ /* REF_BRANCHES means that we want regular branch heads */
+ if ((flags & REF_BRANCHES) && starts_with(name, "heads/"))
return 1;
/* REF_TAGS means that we want tags */
diff --git a/connected.c b/connected.c
index 8f89376dbc..87cc4b57a1 100644
--- a/connected.c
+++ b/connected.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "gettext.h"
#include "hex.h"
diff --git a/contrib/buildsystems/CMakeLists.txt b/contrib/buildsystems/CMakeLists.txt
index 804629c525..832f46b316 100644
--- a/contrib/buildsystems/CMakeLists.txt
+++ b/contrib/buildsystems/CMakeLists.txt
@@ -976,11 +976,12 @@ list(TRANSFORM test-reftable_SOURCES PREPEND "${CMAKE_SOURCE_DIR}/")
#unit-tests
add_library(unit-test-lib OBJECT ${CMAKE_SOURCE_DIR}/t/unit-tests/test-lib.c)
+add_library(unit-test-lib-oid OBJECT ${CMAKE_SOURCE_DIR}/t/unit-tests/lib-oid.c)
parse_makefile_for_scripts(unit_test_PROGRAMS "UNIT_TEST_PROGRAMS" "")
foreach(unit_test ${unit_test_PROGRAMS})
add_executable("${unit_test}" "${CMAKE_SOURCE_DIR}/t/unit-tests/${unit_test}.c")
- target_link_libraries("${unit_test}" unit-test-lib common-main)
+ target_link_libraries("${unit_test}" unit-test-lib unit-test-lib-oid common-main)
set_target_properties("${unit_test}"
PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/t/unit-tests/bin)
if(MSVC)
@@ -1005,10 +1006,11 @@ endforeach()
#test-tool
parse_makefile_for_sources(test-tool_SOURCES "TEST_BUILTINS_OBJS")
+add_library(test-lib OBJECT ${CMAKE_SOURCE_DIR}/t/unit-tests/test-lib.c)
list(TRANSFORM test-tool_SOURCES PREPEND "${CMAKE_SOURCE_DIR}/t/helper/")
add_executable(test-tool ${CMAKE_SOURCE_DIR}/t/helper/test-tool.c ${test-tool_SOURCES} ${test-reftable_SOURCES})
-target_link_libraries(test-tool common-main)
+target_link_libraries(test-tool test-lib common-main)
set_target_properties(test-fake-ssh test-tool
PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/t/helper)
diff --git a/contrib/coccinelle/refs.cocci b/contrib/coccinelle/refs.cocci
new file mode 100644
index 0000000000..31d9cad8f3
--- /dev/null
+++ b/contrib/coccinelle/refs.cocci
@@ -0,0 +1,103 @@
+// Migrate "refs.h" to not rely on `the_repository` implicitly anymore.
+@@
+@@
+(
+- resolve_ref_unsafe
++ refs_resolve_ref_unsafe
+|
+- resolve_refdup
++ refs_resolve_refdup
+|
+- read_ref_full
++ refs_read_ref_full
+|
+- read_ref
++ refs_read_ref
+|
+- ref_exists
++ refs_ref_exists
+|
+- head_ref
++ refs_head_ref
+|
+- for_each_ref
++ refs_for_each_ref
+|
+- for_each_ref_in
++ refs_for_each_ref_in
+|
+- for_each_fullref_in
++ refs_for_each_fullref_in
+|
+- for_each_tag_ref
++ refs_for_each_tag_ref
+|
+- for_each_branch_ref
++ refs_for_each_branch_ref
+|
+- for_each_remote_ref
++ refs_for_each_remote_ref
+|
+- for_each_glob_ref
++ refs_for_each_glob_ref
+|
+- for_each_glob_ref_in
++ refs_for_each_glob_ref_in
+|
+- head_ref_namespaced
++ refs_head_ref_namespaced
+|
+- for_each_namespaced_ref
++ refs_for_each_namespaced_ref
+|
+- for_each_rawref
++ refs_for_each_rawref
+|
+- safe_create_reflog
++ refs_create_reflog
+|
+- reflog_exists
++ refs_reflog_exists
+|
+- delete_ref
++ refs_delete_ref
+|
+- delete_refs
++ refs_delete_refs
+|
+- delete_reflog
++ refs_delete_reflog
+|
+- for_each_reflog_ent
++ refs_for_each_reflog_ent
+|
+- for_each_reflog_ent_reverse
++ refs_for_each_reflog_ent_reverse
+|
+- for_each_reflog
++ refs_for_each_reflog
+|
+- shorten_unambiguous_ref
++ refs_shorten_unambiguous_ref
+|
+- rename_ref
++ refs_rename_ref
+|
+- copy_existing_ref
++ refs_copy_existing_ref
+|
+- create_symref
++ refs_create_symref
+|
+- ref_transaction_begin
++ ref_store_transaction_begin
+|
+- update_ref
++ refs_update_ref
+|
+- reflog_expire
++ refs_reflog_expire
+)
+ (
++ get_main_ref_store(the_repository),
+ ...)
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index 75193ded4b..60a22d619a 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -31,15 +31,29 @@
# Note that "git" is optional --- '!f() { : commit; ...}; f' would complete
# just like the 'git commit' command.
#
-# If you have a command that is not part of git, but you would still
-# like completion, you can use __git_complete:
+# To add completion for git subcommands that are implemented in external
+# scripts, define a function of the form '_git_${subcommand}' while replacing
+# all dashes with underscores, and the main git completion will make use of it.
+# For example, to add completion for 'git do-stuff' (which could e.g. live
+# in /usr/bin/git-do-stuff), name the completion function '_git_do_stuff'.
+# See _git_show, _git_bisect etc. below for more examples.
+#
+# If you have a shell command that is not part of git (and is not called as a
+# git subcommand), but you would still like git-style completion for it, use
+# __git_complete. For example, to use the same completion as for 'git log' also
+# for the 'gl' command:
#
# __git_complete gl git_log
#
-# Or if it's a main command (i.e. git or gitk):
+# Or if the 'gk' command should be completed the same as 'gitk':
#
# __git_complete gk gitk
#
+# The second parameter of __git_complete gives the completion function; it is
+# resolved as a function named "$2", or "__$2_main", or "_$2" in that order.
+# In the examples above, the actual functions used for completion will be
+# _git_log and __gitk_main.
+#
# Compatible with bash 3.2.57.
#
# You can set the following environment variables to influence the behavior of
@@ -2975,22 +2989,42 @@ __git_complete_config_variable_name_and_value ()
_git_config ()
{
- case "$prev" in
- --get|--get-all|--unset|--unset-all)
- __gitcomp_nl "$(__git_config_get_set_variables)"
+ local subcommands subcommand
+
+ __git_resolve_builtins "config"
+
+ subcommands="$___git_resolved_builtins"
+ subcommand="$(__git_find_subcommand "$subcommands")"
+
+ if [ -z "$subcommand" ]
+ then
+ __gitcomp "$subcommands"
return
- ;;
- *.*)
- __git_complete_config_variable_value
+ fi
+
+ case "$cur" in
+ --*)
+ __gitcomp_builtin "config_$subcommand"
return
;;
esac
- case "$cur" in
- --*)
- __gitcomp_builtin config
+
+ case "$subcommand" in
+ get)
+ __gitcomp_nl "$(__git_config_get_set_variables)"
;;
- *)
- __git_complete_config_variable_name
+ set)
+ case "$prev" in
+ *.*)
+ __git_complete_config_variable_value
+ ;;
+ *)
+ __git_complete_config_variable_name
+ ;;
+ esac
+ ;;
+ unset)
+ __gitcomp_nl "$(__git_config_get_set_variables)"
;;
esac
}
@@ -3581,6 +3615,17 @@ _git_svn ()
fi
}
+_git_symbolic_ref () {
+ case "$cur" in
+ --*)
+ __gitcomp_builtin symbolic-ref
+ return
+ ;;
+ esac
+
+ __git_complete_refs
+}
+
_git_tag ()
{
local i c="$__git_cmd_idx" f=0
diff --git a/contrib/completion/git-completion.zsh b/contrib/completion/git-completion.zsh
index cac6f61881..f5877bd7a1 100644
--- a/contrib/completion/git-completion.zsh
+++ b/contrib/completion/git-completion.zsh
@@ -272,6 +272,7 @@ _git ()
{
local _ret=1
local cur cword prev
+ local __git_repo_path
cur=${words[CURRENT]}
prev=${words[CURRENT-1]}
diff --git a/contrib/credential/osxkeychain/git-credential-osxkeychain.c b/contrib/credential/osxkeychain/git-credential-osxkeychain.c
index 6a40917b1e..6ce22a28ed 100644
--- a/contrib/credential/osxkeychain/git-credential-osxkeychain.c
+++ b/contrib/credential/osxkeychain/git-credential-osxkeychain.c
@@ -12,6 +12,7 @@ static CFStringRef username;
static CFDataRef password;
static CFDataRef password_expiry_utc;
static CFDataRef oauth_refresh_token;
+static int state_seen;
static void clear_credential(void)
{
@@ -171,6 +172,9 @@ static OSStatus find_internet_password(void)
CFRelease(item);
+ write_item("capability[]", "state", strlen("state"));
+ write_item("state[]", "osxkeychain:seen=1", strlen("osxkeychain:seen=1"));
+
out:
CFRelease(attrs);
@@ -284,6 +288,9 @@ static OSStatus add_internet_password(void)
CFDictionaryRef attrs;
OSStatus result;
+ if (state_seen)
+ return errSecSuccess;
+
/* Only store complete credentials */
if (!protocol || !host || !username || !password)
return -1;
@@ -395,6 +402,10 @@ static void read_credential(void)
oauth_refresh_token = CFDataCreate(kCFAllocatorDefault,
(UInt8 *)v,
strlen(v));
+ else if (!strcmp(buf, "state[]")) {
+ if (!strcmp(v, "osxkeychain:seen=1"))
+ state_seen = 1;
+ }
/*
* Ignore other lines; we don't know what they mean, but
* this future-proofs us when later versions of git do
@@ -414,6 +425,9 @@ int main(int argc, const char **argv)
if (!argv[1])
die("%s", usage);
+ if (open(argv[0], O_RDONLY | O_EXLOCK) == -1)
+ die("failed to lock %s", argv[0]);
+
read_credential();
if (!strcmp(argv[1], "get"))
diff --git a/convert.c b/convert.c
index 35b25eb3cb..d8737fe0f2 100644
--- a/convert.c
+++ b/convert.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "advice.h"
#include "config.h"
@@ -345,30 +347,32 @@ static int check_roundtrip(const char *enc_name)
* space separated encodings (eg. "UTF-16, ASCII, CP1125").
* Search for the given encoding in that string.
*/
- const char *found = strcasestr(check_roundtrip_encoding, enc_name);
+ const char *encoding = check_roundtrip_encoding ?
+ check_roundtrip_encoding : "SHIFT-JIS";
+ const char *found = strcasestr(encoding, enc_name);
const char *next;
int len;
if (!found)
return 0;
next = found + strlen(enc_name);
- len = strlen(check_roundtrip_encoding);
+ len = strlen(encoding);
return (found && (
/*
- * check that the found encoding is at the
- * beginning of check_roundtrip_encoding or
- * that it is prefixed with a space or comma
+ * Check that the found encoding is at the beginning of
+ * encoding or that it is prefixed with a space or
+ * comma.
*/
- found == check_roundtrip_encoding || (
+ found == encoding || (
(isspace(found[-1]) || found[-1] == ',')
)
) && (
/*
- * check that the found encoding is at the
- * end of check_roundtrip_encoding or
- * that it is suffixed with a space or comma
+ * Check that the found encoding is at the end of
+ * encoding or that it is suffixed with a space
+ * or comma.
*/
- next == check_roundtrip_encoding + len || (
- next < check_roundtrip_encoding + len &&
+ next == encoding + len || (
+ next < encoding + len &&
(isspace(next[0]) || next[0] == ',')
)
));
@@ -979,9 +983,9 @@ done:
static struct convert_driver {
const char *name;
struct convert_driver *next;
- const char *smudge;
- const char *clean;
- const char *process;
+ char *smudge;
+ char *clean;
+ char *process;
int required;
} *user_convert, **user_convert_tail;
diff --git a/convert.h b/convert.h
index ab8b4fa68d..0a6e4086b8 100644
--- a/convert.h
+++ b/convert.h
@@ -4,7 +4,7 @@
#ifndef CONVERT_H
#define CONVERT_H
-#include "hash-ll.h"
+#include "hash.h"
#include "string-list.h"
struct index_state;
@@ -92,7 +92,7 @@ void convert_attrs(struct index_state *istate,
struct conv_attrs *ca, const char *path);
extern enum eol core_eol;
-extern const char *check_roundtrip_encoding;
+extern char *check_roundtrip_encoding;
const char *get_cached_convert_stats_ascii(struct index_state *istate,
const char *path);
const char *get_wt_convert_stats_ascii(const char *path);
diff --git a/copy.c b/copy.c
index 23d84c6c1d..d9d2092012 100644
--- a/copy.c
+++ b/copy.c
@@ -1,6 +1,9 @@
#include "git-compat-util.h"
#include "copy.h"
#include "path.h"
+#include "gettext.h"
+#include "strbuf.h"
+#include "abspath.h"
int copy_fd(int ifd, int ofd)
{
diff --git a/credential.c b/credential.c
index 18098bd35e..4b1a2b94fe 100644
--- a/credential.c
+++ b/credential.c
@@ -20,18 +20,68 @@ void credential_init(struct credential *c)
void credential_clear(struct credential *c)
{
+ credential_clear_secrets(c);
free(c->protocol);
free(c->host);
free(c->path);
free(c->username);
- free(c->password);
free(c->oauth_refresh_token);
+ free(c->authtype);
string_list_clear(&c->helpers, 0);
strvec_clear(&c->wwwauth_headers);
+ strvec_clear(&c->state_headers);
+ strvec_clear(&c->state_headers_to_send);
credential_init(c);
}
+void credential_next_state(struct credential *c)
+{
+ strvec_clear(&c->state_headers_to_send);
+ SWAP(c->state_headers, c->state_headers_to_send);
+}
+
+void credential_clear_secrets(struct credential *c)
+{
+ FREE_AND_NULL(c->password);
+ FREE_AND_NULL(c->credential);
+}
+
+static void credential_set_capability(struct credential_capability *capa,
+ enum credential_op_type op_type)
+{
+ switch (op_type) {
+ case CREDENTIAL_OP_INITIAL:
+ capa->request_initial = 1;
+ break;
+ case CREDENTIAL_OP_HELPER:
+ capa->request_helper = 1;
+ break;
+ case CREDENTIAL_OP_RESPONSE:
+ capa->response = 1;
+ break;
+ }
+}
+
+
+void credential_set_all_capabilities(struct credential *c,
+ enum credential_op_type op_type)
+{
+ credential_set_capability(&c->capa_authtype, op_type);
+ credential_set_capability(&c->capa_state, op_type);
+}
+
+static void announce_one(struct credential_capability *cc, const char *name, FILE *fp) {
+ if (cc->request_initial)
+ fprintf(fp, "capability %s\n", name);
+}
+
+void credential_announce_capabilities(struct credential *c, FILE *fp) {
+ fprintf(fp, "version 0\n");
+ announce_one(&c->capa_authtype, "authtype", fp);
+ announce_one(&c->capa_state, "state", fp);
+}
+
int credential_match(const struct credential *want,
const struct credential *have, int match_password)
{
@@ -40,7 +90,8 @@ int credential_match(const struct credential *want,
CHECK(host) &&
CHECK(path) &&
CHECK(username) &&
- (!match_password || CHECK(password));
+ (!match_password || CHECK(password)) &&
+ (!match_password || CHECK(credential));
#undef CHECK
}
@@ -208,7 +259,26 @@ static void credential_getpass(struct credential *c)
PROMPT_ASKPASS);
}
-int credential_read(struct credential *c, FILE *fp)
+int credential_has_capability(const struct credential_capability *capa,
+ enum credential_op_type op_type)
+{
+ /*
+ * We're checking here if each previous step indicated that we had the
+ * capability. If it did, then we want to pass it along; conversely, if
+ * it did not, we don't want to report that to our caller.
+ */
+ switch (op_type) {
+ case CREDENTIAL_OP_HELPER:
+ return capa->request_initial;
+ case CREDENTIAL_OP_RESPONSE:
+ return capa->request_initial && capa->request_helper;
+ default:
+ return 0;
+ }
+}
+
+int credential_read(struct credential *c, FILE *fp,
+ enum credential_op_type op_type)
{
struct strbuf line = STRBUF_INIT;
@@ -233,6 +303,9 @@ int credential_read(struct credential *c, FILE *fp)
} else if (!strcmp(key, "password")) {
free(c->password);
c->password = xstrdup(value);
+ } else if (!strcmp(key, "credential")) {
+ free(c->credential);
+ c->credential = xstrdup(value);
} else if (!strcmp(key, "protocol")) {
free(c->protocol);
c->protocol = xstrdup(value);
@@ -242,8 +315,19 @@ int credential_read(struct credential *c, FILE *fp)
} else if (!strcmp(key, "path")) {
free(c->path);
c->path = xstrdup(value);
+ } else if (!strcmp(key, "ephemeral")) {
+ c->ephemeral = !!git_config_bool("ephemeral", value);
} else if (!strcmp(key, "wwwauth[]")) {
strvec_push(&c->wwwauth_headers, value);
+ } else if (!strcmp(key, "state[]")) {
+ strvec_push(&c->state_headers, value);
+ } else if (!strcmp(key, "capability[]")) {
+ if (!strcmp(value, "authtype"))
+ credential_set_capability(&c->capa_authtype, op_type);
+ else if (!strcmp(value, "state"))
+ credential_set_capability(&c->capa_state, op_type);
+ } else if (!strcmp(key, "continue")) {
+ c->multistage = !!git_config_bool("continue", value);
} else if (!strcmp(key, "password_expiry_utc")) {
errno = 0;
c->password_expiry_utc = parse_timestamp(value, NULL, 10);
@@ -252,6 +336,9 @@ int credential_read(struct credential *c, FILE *fp)
} else if (!strcmp(key, "oauth_refresh_token")) {
free(c->oauth_refresh_token);
c->oauth_refresh_token = xstrdup(value);
+ } else if (!strcmp(key, "authtype")) {
+ free(c->authtype);
+ c->authtype = xstrdup(value);
} else if (!strcmp(key, "url")) {
credential_from_url(c, value);
} else if (!strcmp(key, "quit")) {
@@ -280,8 +367,20 @@ static void credential_write_item(FILE *fp, const char *key, const char *value,
fprintf(fp, "%s=%s\n", key, value);
}
-void credential_write(const struct credential *c, FILE *fp)
+void credential_write(const struct credential *c, FILE *fp,
+ enum credential_op_type op_type)
{
+ if (credential_has_capability(&c->capa_authtype, op_type))
+ credential_write_item(fp, "capability[]", "authtype", 0);
+ if (credential_has_capability(&c->capa_state, op_type))
+ credential_write_item(fp, "capability[]", "state", 0);
+
+ if (credential_has_capability(&c->capa_authtype, op_type)) {
+ credential_write_item(fp, "authtype", c->authtype, 0);
+ credential_write_item(fp, "credential", c->credential, 0);
+ if (c->ephemeral)
+ credential_write_item(fp, "ephemeral", "1", 0);
+ }
credential_write_item(fp, "protocol", c->protocol, 1);
credential_write_item(fp, "host", c->host, 1);
credential_write_item(fp, "path", c->path, 0);
@@ -295,6 +394,12 @@ void credential_write(const struct credential *c, FILE *fp)
}
for (size_t i = 0; i < c->wwwauth_headers.nr; i++)
credential_write_item(fp, "wwwauth[]", c->wwwauth_headers.v[i], 0);
+ if (credential_has_capability(&c->capa_state, op_type)) {
+ if (c->multistage)
+ credential_write_item(fp, "continue", "1", 0);
+ for (size_t i = 0; i < c->state_headers_to_send.nr; i++)
+ credential_write_item(fp, "state[]", c->state_headers_to_send.v[i], 0);
+ }
}
static int run_credential_helper(struct credential *c,
@@ -317,14 +422,14 @@ static int run_credential_helper(struct credential *c,
fp = xfdopen(helper.in, "w");
sigchain_push(SIGPIPE, SIG_IGN);
- credential_write(c, fp);
+ credential_write(c, fp, want_output ? CREDENTIAL_OP_HELPER : CREDENTIAL_OP_RESPONSE);
fclose(fp);
sigchain_pop(SIGPIPE);
if (want_output) {
int r;
fp = xfdopen(helper.out, "r");
- r = credential_read(c, fp);
+ r = credential_read(c, fp, CREDENTIAL_OP_HELPER);
fclose(fp);
if (r < 0) {
finish_command(&helper);
@@ -357,32 +462,45 @@ static int credential_do(struct credential *c, const char *helper,
return r;
}
-void credential_fill(struct credential *c)
+void credential_fill(struct credential *c, int all_capabilities)
{
int i;
- if (c->username && c->password)
+ if ((c->username && c->password) || c->credential)
return;
+ credential_next_state(c);
+ c->multistage = 0;
+
credential_apply_config(c);
+ if (all_capabilities)
+ credential_set_all_capabilities(c, CREDENTIAL_OP_INITIAL);
for (i = 0; i < c->helpers.nr; i++) {
credential_do(c, c->helpers.items[i].string, "get");
+
if (c->password_expiry_utc < time(NULL)) {
- /* Discard expired password */
- FREE_AND_NULL(c->password);
+ /*
+ * Don't use credential_clear() here: callers such as
+ * cmd_credential() expect to still be able to call
+ * credential_write() on a struct credential whose
+ * secrets have expired.
+ */
+ credential_clear_secrets(c);
/* Reset expiry to maintain consistency */
c->password_expiry_utc = TIME_MAX;
}
- if (c->username && c->password)
+ if ((c->username && c->password) || c->credential) {
+ strvec_clear(&c->wwwauth_headers);
return;
+ }
if (c->quit)
die("credential helper '%s' told us to quit",
c->helpers.items[i].string);
}
credential_getpass(c);
- if (!c->username && !c->password)
+ if (!c->username && !c->password && !c->credential)
die("unable to get password from user");
}
@@ -392,9 +510,11 @@ void credential_approve(struct credential *c)
if (c->approved)
return;
- if (!c->username || !c->password || c->password_expiry_utc < time(NULL))
+ if (((!c->username || !c->password) && !c->credential) || c->password_expiry_utc < time(NULL))
return;
+ credential_next_state(c);
+
credential_apply_config(c);
for (i = 0; i < c->helpers.nr; i++)
@@ -406,13 +526,15 @@ void credential_reject(struct credential *c)
{
int i;
+ credential_next_state(c);
+
credential_apply_config(c);
for (i = 0; i < c->helpers.nr; i++)
credential_do(c, c->helpers.items[i].string, "erase");
+ credential_clear_secrets(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 acc41adf54..5f9e6ff2ef 100644
--- a/credential.h
+++ b/credential.h
@@ -5,8 +5,8 @@
#include "strvec.h"
/**
- * The credentials API provides an abstracted way of gathering username and
- * password credentials from the user.
+ * The credentials API provides an abstracted way of gathering
+ * authentication credentials from the user.
*
* Typical setup
* -------------
@@ -93,13 +93,35 @@
* -----------------------------------------------------------------------
*/
+/*
+ * These values define the kind of operation we're performing and the
+ * capabilities at each stage. The first is either an external request (via git
+ * credential fill) or an internal request (e.g., via the HTTP) code. The
+ * second is the call to the credential helper, and the third is the response
+ * we're providing.
+ *
+ * At each stage, we will emit the capability only if the previous stage
+ * supported it.
+ */
+enum credential_op_type {
+ CREDENTIAL_OP_INITIAL = 1,
+ CREDENTIAL_OP_HELPER = 2,
+ CREDENTIAL_OP_RESPONSE = 3,
+};
+
+struct credential_capability {
+ unsigned request_initial:1,
+ request_helper:1,
+ response:1;
+};
/**
- * This struct represents a single username/password combination
- * along with any associated context. All string fields should be
- * heap-allocated (or NULL if they are not known or not applicable).
- * The meaning of the individual context fields is the same as
- * their counterparts in the helper protocol.
+ * This struct represents a single login credential (typically a
+ * username/password combination) along with any associated
+ * context. All string fields should be heap-allocated (or NULL if
+ * they are not known or not applicable). The meaning of the
+ * individual context fields is the same as their counterparts in
+ * the helper protocol.
*
* This struct should always be initialized with `CREDENTIAL_INIT` or
* `credential_init`.
@@ -124,6 +146,16 @@ struct credential {
struct strvec wwwauth_headers;
/**
+ * A `strvec` of state headers received from credential helpers.
+ */
+ struct strvec state_headers;
+
+ /**
+ * A `strvec` of state headers to send to credential helpers.
+ */
+ struct strvec state_headers_to_send;
+
+ /**
* Internal use only. Keeps track of if we previously matched against a
* WWW-Authenticate header line in order to re-fold future continuation
* lines into one value.
@@ -131,24 +163,38 @@ struct credential {
unsigned header_is_last_match:1;
unsigned approved:1,
+ ephemeral:1,
configured:1,
+ multistage: 1,
quit:1,
use_http_path:1,
username_from_proto:1;
+ struct credential_capability capa_authtype;
+ struct credential_capability capa_state;
+
char *username;
char *password;
+ char *credential;
char *protocol;
char *host;
char *path;
char *oauth_refresh_token;
timestamp_t password_expiry_utc;
+
+ /**
+ * The authorization scheme to use. If this is NULL, libcurl is free to
+ * negotiate any scheme it likes.
+ */
+ char *authtype;
};
#define CREDENTIAL_INIT { \
.helpers = STRING_LIST_INIT_DUP, \
.password_expiry_utc = TIME_MAX, \
.wwwauth_headers = STRVEC_INIT, \
+ .state_headers = STRVEC_INIT, \
+ .state_headers_to_send = STRVEC_INIT, \
}
/* Initialize a credential structure, setting all fields to empty. */
@@ -162,13 +208,17 @@ void credential_clear(struct credential *);
/**
* Instruct the credential subsystem to fill the username and
- * password fields of the passed credential struct by first
- * consulting helpers, then asking the user. After this function
- * returns, the username and password fields of the credential are
- * guaranteed to be non-NULL. If an error occurs, the function will
- * die().
+ * password (or authtype and credential) fields of the passed
+ * credential struct by first consulting helpers, then asking the
+ * user. After this function returns, either the username and
+ * password fields or the credential field of the credential are
+ * guaranteed to be non-NULL. If an error occurs, the function
+ * will die().
+ *
+ * If all_capabilities is set, this is an internal user that is prepared
+ * to deal with all known capabilities, and we should advertise that fact.
*/
-void credential_fill(struct credential *);
+void credential_fill(struct credential *, int all_capabilities);
/**
* Inform the credential subsystem that the provided credentials
@@ -184,15 +234,53 @@ void credential_approve(struct credential *);
* have been rejected. This will cause the credential subsystem to
* notify any helpers of the rejection (which allows them, for
* example, to purge the invalid credentials from storage). It
- * will also free() the username and password fields of the
- * credential and set them to NULL (readying the credential for
- * another call to `credential_fill`). Any errors from helpers are
- * ignored.
+ * will also free() the username, password, and credential fields
+ * of the credential and set them to NULL (readying the credential
+ * for another call to `credential_fill`). Any errors from helpers
+ * are ignored.
*/
void credential_reject(struct credential *);
-int credential_read(struct credential *, FILE *);
-void credential_write(const struct credential *, FILE *);
+/**
+ * Enable all of the supported credential flags in this credential.
+ */
+void credential_set_all_capabilities(struct credential *c,
+ enum credential_op_type op_type);
+
+/**
+ * Clear the secrets in this credential, but leave other data intact.
+ *
+ * This is useful for resetting credentials in preparation for a subsequent
+ * stage of filling.
+ */
+void credential_clear_secrets(struct credential *c);
+
+/**
+ * Print a list of supported capabilities and version numbers to standard
+ * output.
+ */
+void credential_announce_capabilities(struct credential *c, FILE *fp);
+
+/**
+ * Prepares the credential for the next iteration of the helper protocol by
+ * updating the state headers to send with the ones read by the last iteration
+ * of the protocol.
+ *
+ * Except for internal callers, this should be called exactly once between
+ * reading credentials with `credential_fill` and writing them.
+ */
+void credential_next_state(struct credential *c);
+
+/**
+ * Return true if the capability is enabled for an operation of op_type.
+ */
+int credential_has_capability(const struct credential_capability *capa,
+ enum credential_op_type op_type);
+
+int credential_read(struct credential *, FILE *,
+ enum credential_op_type);
+void credential_write(const struct credential *, FILE *,
+ enum credential_op_type);
/*
* Parse a url into a credential struct, replacing any existing contents.
diff --git a/csum-file.c b/csum-file.c
index 870748e016..8abbf01325 100644
--- a/csum-file.c
+++ b/csum-file.c
@@ -7,6 +7,9 @@
* files. Useful when you write a file that you want to be
* able to verify hasn't been messed with afterwards.
*/
+
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "progress.h"
#include "csum-file.h"
@@ -68,12 +71,12 @@ int finalize_hashfile(struct hashfile *f, unsigned char *result,
hashflush(f);
if (f->skip_hash)
- hashclr(f->buffer);
+ hashclr(f->buffer, the_repository->hash_algo);
else
the_hash_algo->final_fn(f->buffer, &f->ctx);
if (result)
- hashcpy(result, f->buffer);
+ hashcpy(result, f->buffer, the_repository->hash_algo);
if (flags & CSUM_HASH_IN_STREAM)
flush(f, f->buffer, the_hash_algo->rawsz);
if (flags & CSUM_FSYNC)
@@ -237,5 +240,5 @@ int hashfile_checksum_valid(const unsigned char *data, size_t total_len)
the_hash_algo->update_fn(&ctx, data, data_len);
the_hash_algo->final_fn(got, &ctx);
- return hasheq(got, data + data_len);
+ return hasheq(got, data + data_len, the_repository->hash_algo);
}
diff --git a/csum-file.h b/csum-file.h
index bc5bec27ac..566e05cbd2 100644
--- a/csum-file.h
+++ b/csum-file.h
@@ -1,7 +1,7 @@
#ifndef CSUM_FILE_H
#define CSUM_FILE_H
-#include "hash-ll.h"
+#include "hash.h"
#include "write-or-die.h"
struct progress;
diff --git a/date.c b/date.c
index 7365a4ad24..bee9fe8f10 100644
--- a/date.c
+++ b/date.c
@@ -868,6 +868,10 @@ static int match_object_header_date(const char *date, timestamp_t *timestamp, in
return 0;
}
+
+/* timestamp of 2099-12-31T23:59:59Z, including 32 leap days */
+static const timestamp_t timestamp_max = (((timestamp_t)2100 - 1970) * 365 + 32) * 24 * 60 * 60 - 1;
+
/* Gr. strptime is crap for this; it doesn't have a way to require RFC2822
(i.e. English) day/month names, and it doesn't work correctly with %z. */
int parse_date_basic(const char *date, timestamp_t *timestamp, int *offset)
@@ -937,8 +941,14 @@ int parse_date_basic(const char *date, timestamp_t *timestamp, int *offset)
}
}
- if (!tm_gmt)
+ if (!tm_gmt) {
+ if (*offset > 0 && *offset * 60 > *timestamp)
+ return -1;
+ if (*offset < 0 && -*offset * 60 > timestamp_max - *timestamp)
+ return -1;
*timestamp -= *offset * 60;
+ }
+
return 0; /* success */
}
diff --git a/decorate.h b/decorate.h
index cdeb17c9df..08af658d34 100644
--- a/decorate.h
+++ b/decorate.h
@@ -3,7 +3,7 @@
/*
* A data structure that associates Git objects to void pointers. See
- * t/helper/test-example-decorate.c for a demonstration of how to use these
+ * t/unit-tests/t-example-decorate.c for a demonstration of how to use these
* functions.
*/
diff --git a/delta-islands.c b/delta-islands.c
index f7e079425f..ffe1ca2814 100644
--- a/delta-islands.c
+++ b/delta-islands.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "object.h"
#include "commit.h"
@@ -313,7 +315,7 @@ struct island_load_data {
size_t nr;
size_t alloc;
};
-static const char *core_island_name;
+static char *core_island_name;
static void free_config_regexes(struct island_load_data *ild)
{
@@ -488,7 +490,8 @@ void load_delta_islands(struct repository *r, int progress)
git_config(island_config_callback, &ild);
ild.remote_islands = kh_init_str();
- for_each_ref(find_island_for_ref, &ild);
+ refs_for_each_ref(get_main_ref_store(the_repository),
+ find_island_for_ref, &ild);
free_config_regexes(&ild);
deduplicate_islands(ild.remote_islands, r);
free_remote_islands(ild.remote_islands);
diff --git a/diagnose.c b/diagnose.c
index 4d096c857f..cc2d535b60 100644
--- a/diagnose.c
+++ b/diagnose.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "diagnose.h"
#include "compat/disk.h"
diff --git a/diff-lib.c b/diff-lib.c
index 683f11e509..7a1eb63757 100644
--- a/diff-lib.c
+++ b/diff-lib.c
@@ -1,6 +1,9 @@
/*
* Copyright (C) 2005 Junio C Hamano
*/
+
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "commit.h"
#include "diff.h"
@@ -66,7 +69,8 @@ static int check_removed(const struct cache_entry *ce, struct stat *st)
* a directory --- the blob was removed!
*/
if (!S_ISGITLINK(ce->ce_mode) &&
- resolve_gitlink_ref(ce->name, "HEAD", &sub))
+ repo_resolve_gitlink_ref(the_repository, ce->name,
+ "HEAD", &sub))
return 1;
}
return 0;
@@ -159,7 +163,7 @@ void run_diff_files(struct rev_info *revs, unsigned int option)
dpath->next = NULL;
memcpy(dpath->path, ce->name, path_len);
dpath->path[path_len] = '\0';
- oidclr(&dpath->oid);
+ oidclr(&dpath->oid, the_repository->hash_algo);
memset(&(dpath->parent[0]), 0,
sizeof(struct combine_diff_parent)*5);
@@ -411,7 +415,7 @@ static int show_modified(struct rev_info *revs,
memcpy(p->path, new_entry->name, pathlen);
p->path[pathlen] = 0;
p->mode = mode;
- oidclr(&p->oid);
+ oidclr(&p->oid, the_repository->hash_algo);
memset(p->parent, 0, 2 * sizeof(struct combine_diff_parent));
p->parent[0].status = DIFF_STATUS_MODIFIED;
p->parent[0].mode = new_entry->ce_mode;
@@ -660,11 +664,12 @@ int do_diff_cache(const struct object_id *tree_oid, struct diff_options *opt)
repo_init_revisions(opt->repo, &revs, NULL);
copy_pathspec(&revs.prune_data, &opt->pathspec);
- diff_setup_done(&revs.diffopt);
revs.diffopt = *opt;
+ revs.diffopt.no_free = 1;
if (diff_cache(&revs, tree_oid, NULL, 1))
exit(128);
+
release_revisions(&revs);
return 0;
}
diff --git a/diff.c b/diff.c
index 108c187577..ebb7538e04 100644
--- a/diff.c
+++ b/diff.c
@@ -1,6 +1,9 @@
/*
* Copyright (C) 2005 Junio C Hamano
*/
+
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "abspath.h"
#include "base85.h"
@@ -56,14 +59,14 @@ static int diff_color_moved_default;
static int diff_color_moved_ws_default;
static int diff_context_default = 3;
static int diff_interhunk_context_default;
-static const char *diff_word_regex_cfg;
-static const char *external_diff_cmd_cfg;
-static const char *diff_order_file_cfg;
+static char *diff_word_regex_cfg;
+static struct external_diff external_diff_cfg;
+static char *diff_order_file_cfg;
int diff_auto_refresh_index = 1;
static int diff_mnemonic_prefix;
static int diff_no_prefix;
-static const char *diff_src_prefix = "a/";
-static const char *diff_dst_prefix = "b/";
+static char *diff_src_prefix;
+static char *diff_dst_prefix;
static int diff_relative;
static int diff_stat_name_width;
static int diff_stat_graph_width;
@@ -411,9 +414,11 @@ int git_diff_ui_config(const char *var, const char *value,
return 0;
}
if (!strcmp(var, "diff.srcprefix")) {
+ FREE_AND_NULL(diff_src_prefix);
return git_config_string(&diff_src_prefix, var, value);
}
if (!strcmp(var, "diff.dstprefix")) {
+ FREE_AND_NULL(diff_dst_prefix);
return git_config_string(&diff_dst_prefix, var, value);
}
if (!strcmp(var, "diff.relative")) {
@@ -429,7 +434,11 @@ int git_diff_ui_config(const char *var, const char *value,
return 0;
}
if (!strcmp(var, "diff.external"))
- return git_config_string(&external_diff_cmd_cfg, var, value);
+ return git_config_string(&external_diff_cfg.cmd, var, value);
+ if (!strcmp(var, "diff.trustexitcode")) {
+ external_diff_cfg.trust_exit_code = git_config_bool(var, value);
+ return 0;
+ }
if (!strcmp(var, "diff.wordregex"))
return git_config_string(&diff_word_regex_cfg, var, value);
if (!strcmp(var, "diff.orderfile"))
@@ -546,18 +555,22 @@ static char *quote_two(const char *one, const char *two)
return strbuf_detach(&res, NULL);
}
-static const char *external_diff(void)
+static const struct external_diff *external_diff(void)
{
- static const char *external_diff_cmd = NULL;
+ static struct external_diff external_diff_env, *external_diff_ptr;
static int done_preparing = 0;
if (done_preparing)
- return external_diff_cmd;
- external_diff_cmd = xstrdup_or_null(getenv("GIT_EXTERNAL_DIFF"));
- if (!external_diff_cmd)
- external_diff_cmd = external_diff_cmd_cfg;
+ return external_diff_ptr;
+ external_diff_env.cmd = xstrdup_or_null(getenv("GIT_EXTERNAL_DIFF"));
+ if (git_env_bool("GIT_EXTERNAL_DIFF_TRUST_EXIT_CODE", 0))
+ external_diff_env.trust_exit_code = 1;
+ if (external_diff_env.cmd)
+ external_diff_ptr = &external_diff_env;
+ else if (external_diff_cfg.cmd)
+ external_diff_ptr = &external_diff_cfg;
done_preparing = 1;
- return external_diff_cmd;
+ return external_diff_ptr;
}
/*
@@ -3433,8 +3446,8 @@ void diff_set_noprefix(struct diff_options *options)
void diff_set_default_prefix(struct diff_options *options)
{
- options->a_prefix = diff_src_prefix;
- options->b_prefix = diff_dst_prefix;
+ options->a_prefix = diff_src_prefix ? diff_src_prefix : "a/";
+ options->b_prefix = diff_dst_prefix ? diff_dst_prefix : "b/";
}
struct userdiff_driver *get_textconv(struct repository *r,
@@ -3762,7 +3775,7 @@ static void builtin_diff(const char *name_a,
return;
}
-static char *get_compact_summary(const struct diff_filepair *p, int is_renamed)
+static const char *get_compact_summary(const struct diff_filepair *p, int is_renamed)
{
if (!is_renamed) {
if (p->status == DIFF_STATUS_ADDED) {
@@ -4074,7 +4087,7 @@ static int reuse_worktree_file(struct index_state *istate,
static int diff_populate_gitlink(struct diff_filespec *s, int size_only)
{
struct strbuf buf = STRBUF_INIT;
- char *dirty = "";
+ const char *dirty = "";
/* Are we looking at the work tree? */
if (s->dirty_submodule)
@@ -4373,7 +4386,7 @@ static void add_external_diff_name(struct repository *r,
* infile2 infile2-sha1 infile2-mode [ rename-to ]
*
*/
-static void run_external_diff(const char *pgm,
+static void run_external_diff(const struct external_diff *pgm,
const char *name,
const char *other,
struct diff_filespec *one,
@@ -4383,8 +4396,21 @@ static void run_external_diff(const char *pgm,
{
struct child_process cmd = CHILD_PROCESS_INIT;
struct diff_queue_struct *q = &diff_queued_diff;
+ int quiet = !(o->output_format & DIFF_FORMAT_PATCH);
+ int rc;
- strvec_push(&cmd.args, pgm);
+ /*
+ * Trivial equality is handled by diff_unmodified_pair() before
+ * we get here. If we don't need to show the diff and the
+ * external diff program lacks the ability to tell us whether
+ * it's empty then we consider it non-empty without even asking.
+ */
+ if (!pgm->trust_exit_code && quiet) {
+ o->found_changes = 1;
+ return;
+ }
+
+ strvec_push(&cmd.args, pgm->cmd);
strvec_push(&cmd.args, name);
if (one && two) {
@@ -4404,7 +4430,15 @@ static void run_external_diff(const char *pgm,
diff_free_filespec_data(one);
diff_free_filespec_data(two);
cmd.use_shell = 1;
- if (run_command(&cmd))
+ cmd.no_stdout = quiet;
+ rc = run_command(&cmd);
+ if (!pgm->trust_exit_code && rc == 0)
+ o->found_changes = 1;
+ else if (pgm->trust_exit_code && rc == 0)
+ ; /* nothing */
+ else if (pgm->trust_exit_code && rc == 1)
+ o->found_changes = 1;
+ else
die(_("external diff died, stopping at %s"), name);
remove_tempfile();
@@ -4510,7 +4544,7 @@ static void fill_metainfo(struct strbuf *msg,
}
}
-static void run_diff_cmd(const char *pgm,
+static void run_diff_cmd(const struct external_diff *pgm,
const char *name,
const char *other,
const char *attr_path,
@@ -4528,8 +4562,8 @@ static void run_diff_cmd(const char *pgm,
if (o->flags.allow_external || !o->ignore_driver_algorithm)
drv = userdiff_find_by_path(o->repo->index, attr_path);
- if (o->flags.allow_external && drv && drv->external)
- pgm = drv->external;
+ if (o->flags.allow_external && drv && drv->external.cmd)
+ pgm = &drv->external;
if (msg) {
/*
@@ -4555,6 +4589,7 @@ static void run_diff_cmd(const char *pgm,
o, complete_rewrite);
} else {
fprintf(o->file, "* Unmerged path %s\n", name);
+ o->found_changes = 1;
}
}
@@ -4564,7 +4599,7 @@ static void diff_fill_oid_info(struct diff_filespec *one, struct index_state *is
if (!one->oid_valid) {
struct stat st;
if (one->is_stdin) {
- oidclr(&one->oid);
+ oidclr(&one->oid, the_repository->hash_algo);
return;
}
if (lstat(one->path, &st) < 0)
@@ -4574,7 +4609,7 @@ static void diff_fill_oid_info(struct diff_filespec *one, struct index_state *is
}
}
else
- oidclr(&one->oid);
+ oidclr(&one->oid, the_repository->hash_algo);
}
static void strip_prefix(int prefix_length, const char **namep, const char **otherp)
@@ -4594,7 +4629,7 @@ static void strip_prefix(int prefix_length, const char **namep, const char **oth
static void run_diff(struct diff_filepair *p, struct diff_options *o)
{
- const char *pgm = external_diff();
+ const struct external_diff *pgm = external_diff();
struct strbuf msg;
struct diff_filespec *one = p->one;
struct diff_filespec *two = p->two;
@@ -4921,12 +4956,20 @@ void diff_setup_done(struct diff_options *options)
options->flags.exit_with_status = 1;
}
+ /*
+ * External diffs could declare non-identical contents equal
+ * (think diff --ignore-space-change).
+ */
+ if (options->flags.allow_external && options->flags.exit_with_status)
+ options->flags.diff_from_contents = 1;
+
options->diff_path_counter = 0;
if (options->flags.follow_renames)
diff_check_follow_pathspec(&options->pathspec, 1);
- if (!options->use_color || external_diff())
+ if (!options->use_color ||
+ (options->flags.allow_external && external_diff()))
options->color_moved = 0;
if (options->filter_not) {
@@ -5370,8 +5413,8 @@ static int diff_opt_default_prefix(const struct option *opt,
BUG_ON_OPT_NEG(unset);
BUG_ON_OPT_ARG(optarg);
- diff_src_prefix = "a/";
- diff_dst_prefix = "b/";
+ FREE_AND_NULL(diff_src_prefix);
+ FREE_AND_NULL(diff_dst_prefix);
diff_set_default_prefix(options);
return 0;
}
@@ -6401,7 +6444,7 @@ static int diff_get_patch_id(struct diff_options *options, struct object_id *oid
the_hash_algo->init_fn(&ctx);
memset(&data, 0, sizeof(struct patch_id_t));
data.ctx = &ctx;
- oidclr(oid);
+ oidclr(oid, the_repository->hash_algo);
for (i = 0; i < q->nr; i++) {
xpparam_t xpp;
@@ -6646,8 +6689,10 @@ static void diff_flush_patch_all_file_pairs(struct diff_options *o)
static void diff_free_file(struct diff_options *options)
{
- if (options->close_file)
+ if (options->close_file && options->file) {
fclose(options->file);
+ options->file = NULL;
+ }
}
static void diff_free_ignore_regex(struct diff_options *options)
@@ -6658,7 +6703,9 @@ static void diff_free_ignore_regex(struct diff_options *options)
regfree(options->ignore_regex[i]);
free(options->ignore_regex[i]);
}
- free(options->ignore_regex);
+
+ FREE_AND_NULL(options->ignore_regex);
+ options->ignore_regex_nr = 0;
}
void diff_free(struct diff_options *options)
@@ -7232,7 +7279,7 @@ size_t fill_textconv(struct repository *r,
if (!driver) {
if (!DIFF_FILE_VALID(df)) {
- *outbuf = "";
+ *outbuf = (char *) "";
return 0;
}
if (diff_populate_filespec(r, df, NULL))
diff --git a/diff.h b/diff.h
index 66bd8aeb29..9901c8ca8c 100644
--- a/diff.h
+++ b/diff.h
@@ -4,7 +4,7 @@
#ifndef DIFF_H
#define DIFF_H
-#include "hash-ll.h"
+#include "hash.h"
#include "pathspec.h"
#include "strbuf.h"
diff --git a/diffcore-break.c b/diffcore-break.c
index 49ba38aa7c..831b66b5c3 100644
--- a/diffcore-break.c
+++ b/diffcore-break.c
@@ -1,6 +1,9 @@
/*
* Copyright (C) 2005 Junio C Hamano
*/
+
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "diffcore.h"
#include "hash.h"
diff --git a/diffcore-rename.c b/diffcore-rename.c
index 5a6e2bcac7..fab45b10d7 100644
--- a/diffcore-rename.c
+++ b/diffcore-rename.c
@@ -2,6 +2,9 @@
*
* Copyright (C) 2005 Junio C Hamano
*/
+
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "diff.h"
#include "diffcore.h"
@@ -406,7 +409,7 @@ static const char *get_highest_rename_path(struct strintmap *counts)
return highest_destination_dir;
}
-static char *UNKNOWN_DIR = "/"; /* placeholder -- short, illegal directory */
+static const char *UNKNOWN_DIR = "/"; /* placeholder -- short, illegal directory */
static int dir_rename_already_determinable(struct strintmap *counts)
{
@@ -429,8 +432,8 @@ static int dir_rename_already_determinable(struct strintmap *counts)
}
static void increment_count(struct dir_rename_info *info,
- char *old_dir,
- char *new_dir)
+ const char *old_dir,
+ const char *new_dir)
{
struct strintmap *counts;
struct strmap_entry *e;
@@ -1422,7 +1425,7 @@ void diffcore_rename_extended(struct diff_options *options,
strcmp(options->single_follow, p->two->path))
continue; /* not interested */
else if (!options->flags.rename_empty &&
- is_empty_blob_oid(&p->two->oid))
+ is_empty_blob_oid(&p->two->oid, the_repository->hash_algo))
continue;
else if (add_rename_dst(p) < 0) {
warning("skipping rename detection, detected"
@@ -1432,7 +1435,7 @@ void diffcore_rename_extended(struct diff_options *options,
}
}
else if (!options->flags.rename_empty &&
- is_empty_blob_oid(&p->one->oid))
+ is_empty_blob_oid(&p->one->oid, the_repository->hash_algo))
continue;
else if (!DIFF_PAIR_UNMERGED(p) && !DIFF_FILE_VALID(p->two)) {
/*
diff --git a/diffcore.h b/diffcore.h
index 5ffe4ec788..1701ed50b9 100644
--- a/diffcore.h
+++ b/diffcore.h
@@ -4,7 +4,7 @@
#ifndef DIFFCORE_H
#define DIFFCORE_H
-#include "hash-ll.h"
+#include "hash.h"
struct diff_options;
struct mem_pool;
diff --git a/dir.c b/dir.c
index 20ebe4cba2..5a23376bda 100644
--- a/dir.c
+++ b/dir.c
@@ -5,6 +5,9 @@
* Copyright (C) Linus Torvalds, 2005-2006
* Junio Hamano, 2005-2006
*/
+
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "abspath.h"
#include "config.h"
@@ -30,6 +33,13 @@
#include "symlinks.h"
#include "trace2.h"
#include "tree.h"
+#include "hex.h"
+
+ /*
+ * The maximum size of a pattern/exclude file. If the file exceeds this size
+ * we will ignore it.
+ */
+#define PATTERN_MAX_FILE_SIZE (100 * 1024 * 1024)
/*
* Tells read_directory_recursive how a file or directory should be treated.
@@ -85,7 +95,7 @@ int count_slashes(const char *s)
return cnt;
}
-int fspathcmp(const char *a, const char *b)
+int git_fspathcmp(const char *a, const char *b)
{
return ignore_case ? strcasecmp(a, b) : strcmp(a, b);
}
@@ -95,11 +105,23 @@ int fspatheq(const char *a, const char *b)
return !fspathcmp(a, b);
}
-int fspathncmp(const char *a, const char *b, size_t count)
+int git_fspathncmp(const char *a, const char *b, size_t count)
{
return ignore_case ? strncasecmp(a, b, count) : strncmp(a, b, count);
}
+int paths_collide(const char *a, const char *b)
+{
+ size_t len_a = strlen(a), len_b = strlen(b);
+
+ if (len_a == len_b)
+ return fspatheq(a, b);
+
+ if (len_a < len_b)
+ return is_dir_sep(b[len_a]) && !fspathncmp(a, b, len_a);
+ return is_dir_sep(a[len_b]) && !fspathncmp(a, b, len_b);
+}
+
unsigned int fspathhash(const char *str)
{
return ignore_case ? strihash(str) : strhash(str);
@@ -714,6 +736,17 @@ static char *dup_and_filter_pattern(const char *pattern)
return result;
}
+static void clear_pattern_entry_hashmap(struct hashmap *map)
+{
+ struct hashmap_iter iter;
+ struct pattern_entry *entry;
+
+ hashmap_for_each_entry(map, &iter, entry, ent) {
+ free(entry->pattern);
+ }
+ hashmap_clear_and_free(map, struct pattern_entry, ent);
+}
+
static void add_pattern_to_hashsets(struct pattern_list *pl, struct path_pattern *given)
{
struct pattern_entry *translated;
@@ -787,6 +820,8 @@ static void add_pattern_to_hashsets(struct pattern_list *pl, struct path_pattern
if (given->patternlen > 2 &&
!strcmp(given->pattern + given->patternlen - 2, "/*")) {
+ struct pattern_entry *old;
+
if (!(given->flags & PATTERN_FLAG_NEGATIVE)) {
/* Not a cone pattern. */
warning(_("unrecognized pattern: '%s'"), given->pattern);
@@ -812,7 +847,11 @@ static void add_pattern_to_hashsets(struct pattern_list *pl, struct path_pattern
}
hashmap_add(&pl->parent_hashmap, &translated->ent);
- hashmap_remove(&pl->recursive_hashmap, &translated->ent, &data);
+ old = hashmap_remove_entry(&pl->recursive_hashmap, translated, ent, &data);
+ if (old) {
+ free(old->pattern);
+ free(old);
+ }
free(data);
return;
}
@@ -843,8 +882,8 @@ static void add_pattern_to_hashsets(struct pattern_list *pl, struct path_pattern
clear_hashmaps:
warning(_("disabling cone pattern matching"));
- hashmap_clear_and_free(&pl->parent_hashmap, struct pattern_entry, ent);
- hashmap_clear_and_free(&pl->recursive_hashmap, struct pattern_entry, ent);
+ clear_pattern_entry_hashmap(&pl->recursive_hashmap);
+ clear_pattern_entry_hashmap(&pl->parent_hashmap);
pl->use_cone_patterns = 0;
}
@@ -896,12 +935,7 @@ void add_pattern(const char *string, const char *base,
int nowildcardlen;
parse_path_pattern(&string, &patternlen, &flags, &nowildcardlen);
- if (flags & PATTERN_FLAG_MUSTBEDIR) {
- FLEXPTR_ALLOC_MEM(pattern, pattern, string, patternlen);
- } else {
- pattern = xmalloc(sizeof(*pattern));
- pattern->pattern = string;
- }
+ FLEX_ALLOC_MEM(pattern, pattern, string, patternlen);
pattern->patternlen = patternlen;
pattern->nowildcardlen = nowildcardlen;
pattern->base = base;
@@ -943,9 +977,8 @@ void clear_pattern_list(struct pattern_list *pl)
for (i = 0; i < pl->nr; i++)
free(pl->patterns[i]);
free(pl->patterns);
- free(pl->filebuf);
- hashmap_clear_and_free(&pl->recursive_hashmap, struct pattern_entry, ent);
- hashmap_clear_and_free(&pl->parent_hashmap, struct pattern_entry, ent);
+ clear_pattern_entry_hashmap(&pl->recursive_hashmap);
+ clear_pattern_entry_hashmap(&pl->parent_hashmap);
memset(pl, 0, sizeof(*pl));
}
@@ -1136,7 +1169,14 @@ static int add_patterns(const char *fname, const char *base, int baselen,
}
}
+ if (size > PATTERN_MAX_FILE_SIZE) {
+ warning("ignoring excessively large pattern file: %s", fname);
+ free(buf);
+ return -1;
+ }
+
add_patterns_from_buffer(buf, size, base, baselen, pl);
+ free(buf);
return 0;
}
@@ -1144,16 +1184,15 @@ static int add_patterns_from_buffer(char *buf, size_t size,
const char *base, int baselen,
struct pattern_list *pl)
{
+ char *orig = buf;
int i, lineno = 1;
char *entry;
hashmap_init(&pl->recursive_hashmap, pl_hashmap_cmp, NULL, 0);
hashmap_init(&pl->parent_hashmap, pl_hashmap_cmp, NULL, 0);
- pl->filebuf = buf;
-
if (skip_utf8_bom(&buf, size))
- size -= buf - pl->filebuf;
+ size -= buf - orig;
entry = buf;
@@ -1192,7 +1231,15 @@ int add_patterns_from_blob_to_list(
if (r != 1)
return r;
+ if (size > PATTERN_MAX_FILE_SIZE) {
+ warning("ignoring excessively large pattern blob: %s",
+ oid_to_hex(oid));
+ free(buf);
+ return -1;
+ }
+
add_patterns_from_buffer(buf, size, base, baselen, pl);
+ free(buf);
return 0;
}
@@ -1643,7 +1690,7 @@ static void prep_exclude(struct dir_struct *dir,
}
/* Try to read per-directory file */
- oidclr(&oid_stat.oid);
+ oidclr(&oid_stat.oid, the_repository->hash_algo);
oid_stat.valid = 0;
if (dir->exclude_per_dir &&
/*
@@ -3306,7 +3353,8 @@ static int remove_dir_recurse(struct strbuf *path, int flag, int *kept_up)
struct object_id submodule_head;
if ((flag & REMOVE_DIR_KEEP_NESTED_GIT) &&
- !resolve_gitlink_ref(path->buf, "HEAD", &submodule_head)) {
+ !repo_resolve_gitlink_ref(the_repository, path->buf,
+ "HEAD", &submodule_head)) {
/* Do not descend and nuke a nested git work tree. */
if (kept_up)
*kept_up = 1;
@@ -3749,7 +3797,7 @@ static void read_oid(size_t pos, void *cb)
rd->data = rd->end + 1;
return;
}
- oidread(&ud->exclude_oid, rd->data);
+ oidread(&ud->exclude_oid, rd->data, the_repository->hash_algo);
rd->data += the_hash_algo->rawsz;
}
@@ -3757,7 +3805,7 @@ static void load_oid_stat(struct oid_stat *oid_stat, const unsigned char *data,
const unsigned char *sha1)
{
stat_data_from_disk(&oid_stat->stat, data);
- oidread(&oid_stat->oid, sha1);
+ oidread(&oid_stat->oid, sha1, the_repository->hash_algo);
oid_stat->valid = 1;
}
diff --git a/dir.h b/dir.h
index 45a7b9ec5f..a3a2f00f5d 100644
--- a/dir.h
+++ b/dir.h
@@ -1,7 +1,7 @@
#ifndef DIR_H
#define DIR_H
-#include "hash-ll.h"
+#include "hash.h"
#include "hashmap.h"
#include "pathspec.h"
#include "statinfo.h"
@@ -62,7 +62,6 @@ struct path_pattern {
*/
struct pattern_list *pl;
- const char *pattern;
int patternlen;
int nowildcardlen;
const char *base;
@@ -74,6 +73,8 @@ struct path_pattern {
* and from -1 decrementing for patterns from CLI args.
*/
int srcpos;
+
+ char pattern[FLEX_ARRAY];
};
/* used for hashmaps for cone patterns */
@@ -94,9 +95,6 @@ struct pattern_list {
int nr;
int alloc;
- /* remember pointer to exclude file contents so we can free() */
- char *filebuf;
-
/* origin of list, e.g. path to filename, or descriptive string */
const char *src;
@@ -543,12 +541,19 @@ int remove_dir_recursively(struct strbuf *path, int flag);
*/
int remove_path(const char *path);
-int fspathcmp(const char *a, const char *b);
+int git_fspathcmp(const char *a, const char *b);
int fspatheq(const char *a, const char *b);
-int fspathncmp(const char *a, const char *b, size_t count);
+int git_fspathncmp(const char *a, const char *b, size_t count);
unsigned int fspathhash(const char *str);
/*
+ * Reports whether paths collide. This may be because the paths differ only in
+ * case on a case-sensitive filesystem, or that one path refers to a symlink
+ * that collides with one of the parent directories of the other.
+ */
+int paths_collide(const char *a, const char *b);
+
+/*
* The prefix part of pattern must not contains wildcards.
*/
struct pathspec_item;
diff --git a/entry.c b/entry.c
index f918a3a78e..e7ed440ce2 100644
--- a/entry.c
+++ b/entry.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "object-store-ll.h"
#include "dir.h"
@@ -167,6 +169,11 @@ static int remove_available_paths(struct string_list_item *item, void *cb_data)
return !available;
}
+static int string_is_not_null(struct string_list_item *item, void *data UNUSED)
+{
+ return !!item->string;
+}
+
int finish_delayed_checkout(struct checkout *state, int show_progress)
{
int errs = 0;
@@ -189,7 +196,7 @@ int finish_delayed_checkout(struct checkout *state, int show_progress)
if (!async_query_available_blobs(filter->string, &available_paths)) {
/* Filter reported an error */
errs = 1;
- filter->string = "";
+ filter->string = NULL;
continue;
}
if (available_paths.nr <= 0) {
@@ -199,7 +206,7 @@ int finish_delayed_checkout(struct checkout *state, int show_progress)
* filter from the list (see
* "string_list_remove_empty_items" call below).
*/
- filter->string = "";
+ filter->string = NULL;
continue;
}
@@ -225,7 +232,7 @@ int finish_delayed_checkout(struct checkout *state, int show_progress)
* Do not ask the filter for available blobs,
* again, as the filter is likely buggy.
*/
- filter->string = "";
+ filter->string = NULL;
continue;
}
ce = index_file_exists(state->istate, path->string,
@@ -239,7 +246,8 @@ int finish_delayed_checkout(struct checkout *state, int show_progress)
errs = 1;
}
}
- string_list_remove_empty_items(&dco->filters, 0);
+
+ filter_string_list(&dco->filters, 0, string_is_not_null, NULL);
}
stop_progress(&progress);
string_list_clear(&dco->filters, 0);
@@ -460,7 +468,7 @@ static void mark_colliding_entries(const struct checkout *state,
continue;
if ((trust_ino && !match_stat_data(&dup->ce_stat_data, st)) ||
- (!trust_ino && !fspathcmp(ce->name, dup->name))) {
+ paths_collide(ce->name, dup->name)) {
dup->ce_flags |= CE_MATCHED;
break;
}
@@ -547,6 +555,20 @@ int checkout_entry_ca(struct cache_entry *ce, struct conv_attrs *ca,
/* If it is a gitlink, leave it alone! */
if (S_ISGITLINK(ce->ce_mode))
return 0;
+ /*
+ * We must avoid replacing submodules' leading
+ * directories with symbolic links, lest recursive
+ * clones can write into arbitrary locations.
+ *
+ * Technically, this logic is not limited
+ * to recursive clones, or for that matter to
+ * submodules' paths colliding with symbolic links'
+ * paths. Yet it strikes a balance in favor of
+ * simplicity, and if paths are colliding, we might
+ * just as well keep the directories during a clone.
+ */
+ if (state->clone && S_ISLNK(ce->ce_mode))
+ return 0;
remove_subtree(&path);
} else if (unlink(path.buf))
return error_errno("unable to unlink old '%s'", path.buf);
diff --git a/environment.c b/environment.c
index a73ba9c12c..5cea2c9f54 100644
--- a/environment.c
+++ b/environment.c
@@ -7,6 +7,9 @@
* even if you might want to know where the git directory etc
* are.
*/
+
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "abspath.h"
#include "branch.h"
@@ -42,12 +45,12 @@ int is_bare_repository_cfg = -1; /* unspecified */
int warn_ambiguous_refs = 1;
int warn_on_object_refname_ambiguity = 1;
int repository_format_precious_objects;
-const char *git_commit_encoding;
-const char *git_log_output_encoding;
+char *git_commit_encoding;
+char *git_log_output_encoding;
char *apply_default_whitespace;
char *apply_default_ignorewhitespace;
-const char *git_attributes_file;
-const char *git_hooks_path;
+char *git_attributes_file;
+char *git_hooks_path;
int zlib_compression_level = Z_BEST_SPEED;
int pack_compression_level = Z_DEFAULT_COMPRESSION;
int fsync_object_files = -1;
@@ -58,13 +61,13 @@ size_t packed_git_window_size = DEFAULT_PACKED_GIT_WINDOW_SIZE;
size_t packed_git_limit = DEFAULT_PACKED_GIT_LIMIT;
size_t delta_base_cache_limit = 96 * 1024 * 1024;
unsigned long big_file_threshold = 512 * 1024 * 1024;
-const char *editor_program;
-const char *askpass_program;
-const char *excludes_file;
+char *editor_program;
+char *askpass_program;
+char *excludes_file;
enum auto_crlf auto_crlf = AUTO_CRLF_FALSE;
enum eol core_eol = EOL_UNSET;
int global_conv_flags_eol = CONV_EOL_RNDTRP_WARN;
-const char *check_roundtrip_encoding = "SHIFT-JIS";
+char *check_roundtrip_encoding;
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;
diff --git a/environment.h b/environment.h
index 05fd94d7be..e9f01d4d11 100644
--- a/environment.h
+++ b/environment.h
@@ -58,6 +58,13 @@ const char *getenv_safe(struct strvec *argv, const char *name);
#define GIT_ATTR_SOURCE_ENVIRONMENT "GIT_ATTR_SOURCE"
/*
+ * Environment variable used to propagate the --no-advice global option to the
+ * advice_enabled() helper, even when run in a subprocess.
+ * This is an internal variable that should not be set by the user.
+ */
+#define GIT_ADVICE_ENVIRONMENT "GIT_ADVICE"
+
+/*
* Environment variable used in handshaking the wire protocol.
* Contains a colon ':' separated list of keys with optional values
* 'key[=value]'. Presence of unknown keys and values must be
@@ -124,8 +131,8 @@ extern int warn_ambiguous_refs;
extern int warn_on_object_refname_ambiguity;
extern char *apply_default_whitespace;
extern char *apply_default_ignorewhitespace;
-extern const char *git_attributes_file;
-extern const char *git_hooks_path;
+extern char *git_attributes_file;
+extern char *git_hooks_path;
extern int zlib_compression_level;
extern int pack_compression_level;
extern size_t packed_git_window_size;
@@ -217,12 +224,12 @@ int odb_pack_keep(const char *name);
const char *get_log_output_encoding(void);
const char *get_commit_output_encoding(void);
-extern const char *git_commit_encoding;
-extern const char *git_log_output_encoding;
+extern char *git_commit_encoding;
+extern char *git_log_output_encoding;
-extern const char *editor_program;
-extern const char *askpass_program;
-extern const char *excludes_file;
+extern char *editor_program;
+extern char *askpass_program;
+extern char *excludes_file;
/*
* Should we print an ellipsis after an abbreviated SHA-1 value
diff --git a/ewah/bitmap.c b/ewah/bitmap.c
index ac7e0af622..55928dada8 100644
--- a/ewah/bitmap.c
+++ b/ewah/bitmap.c
@@ -138,6 +138,49 @@ void bitmap_or(struct bitmap *self, const struct bitmap *other)
self->words[i] |= other->words[i];
}
+int ewah_bitmap_is_subset(struct ewah_bitmap *self, struct bitmap *other)
+{
+ struct ewah_iterator it;
+ eword_t word;
+ size_t i;
+
+ ewah_iterator_init(&it, self);
+
+ for (i = 0; i < other->word_alloc; i++) {
+ if (!ewah_iterator_next(&word, &it)) {
+ /*
+ * If we reached the end of `self`, and haven't
+ * rejected `self` as a possible subset of
+ * `other` yet, then we are done and `self` is
+ * indeed a subset of `other`.
+ */
+ return 1;
+ }
+ if (word & ~other->words[i]) {
+ /*
+ * Otherwise, compare the next two pairs of
+ * words. If the word from `self` has bit(s) not
+ * in the word from `other`, `self` is not a
+ * subset of `other`.
+ */
+ return 0;
+ }
+ }
+
+ /*
+ * If we got to this point, there may be zero or more words
+ * remaining in `self`, with no remaining words left in `other`.
+ * If there are any bits set in the remaining word(s) in `self`,
+ * then `self` is not a subset of `other`.
+ */
+ while (ewah_iterator_next(&word, &it))
+ if (word)
+ return 0;
+
+ /* `self` is definitely a subset of `other` */
+ return 1;
+}
+
void bitmap_or_ewah(struct bitmap *self, struct ewah_bitmap *other)
{
size_t original_size = self->word_alloc;
@@ -169,6 +212,20 @@ size_t bitmap_popcount(struct bitmap *self)
return count;
}
+size_t ewah_bitmap_popcount(struct ewah_bitmap *self)
+{
+ struct ewah_iterator it;
+ eword_t word;
+ size_t count = 0;
+
+ ewah_iterator_init(&it, self);
+
+ while (ewah_iterator_next(&word, &it))
+ count += ewah_bit_popcount64(word);
+
+ return count;
+}
+
int bitmap_is_empty(struct bitmap *self)
{
size_t i;
@@ -204,6 +261,25 @@ int bitmap_equals(struct bitmap *self, struct bitmap *other)
return 1;
}
+int bitmap_equals_ewah(struct bitmap *self, struct ewah_bitmap *other)
+{
+ struct ewah_iterator it;
+ eword_t word;
+ size_t i = 0;
+
+ ewah_iterator_init(&it, other);
+
+ while (ewah_iterator_next(&word, &it))
+ if (word != (i < self->word_alloc ? self->words[i++] : 0))
+ return 0;
+
+ for (; i < self->word_alloc; i++)
+ if (self->words[i])
+ return 0;
+
+ return 1;
+}
+
int bitmap_is_subset(struct bitmap *self, struct bitmap *other)
{
size_t common_size, i;
diff --git a/ewah/ewok.h b/ewah/ewok.h
index c11d76c6f3..5e357e2493 100644
--- a/ewah/ewok.h
+++ b/ewah/ewok.h
@@ -179,7 +179,14 @@ void bitmap_unset(struct bitmap *self, size_t pos);
int bitmap_get(struct bitmap *self, size_t pos);
void bitmap_free(struct bitmap *self);
int bitmap_equals(struct bitmap *self, struct bitmap *other);
+int bitmap_equals_ewah(struct bitmap *self, struct ewah_bitmap *other);
+
+/*
+ * Both `bitmap_is_subset()` and `ewah_bitmap_is_subset()` return 1 if the set
+ * of bits in 'self' are a subset of the bits in 'other'. Returns 0 otherwise.
+ */
int bitmap_is_subset(struct bitmap *self, struct bitmap *other);
+int ewah_bitmap_is_subset(struct ewah_bitmap *self, struct bitmap *other);
struct ewah_bitmap * bitmap_to_ewah(struct bitmap *bitmap);
struct bitmap *ewah_to_bitmap(struct ewah_bitmap *ewah);
@@ -189,6 +196,7 @@ void bitmap_or_ewah(struct bitmap *self, struct ewah_bitmap *other);
void bitmap_or(struct bitmap *self, const struct bitmap *other);
size_t bitmap_popcount(struct bitmap *self);
+size_t ewah_bitmap_popcount(struct ewah_bitmap *self);
int bitmap_is_empty(struct bitmap *self);
#endif
diff --git a/fetch-pack.c b/fetch-pack.c
index 091f9a80a9..732511604b 100644
--- a/fetch-pack.c
+++ b/fetch-pack.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "repository.h"
#include "config.h"
@@ -290,7 +292,8 @@ static void mark_tips(struct fetch_negotiator *negotiator,
int i;
if (!negotiation_tips) {
- for_each_rawref(rev_list_insert_ref_oid, negotiator);
+ refs_for_each_rawref(get_main_ref_store(the_repository),
+ rev_list_insert_ref_oid, negotiator);
return;
}
@@ -732,11 +735,6 @@ static void mark_alternate_complete(struct fetch_negotiator *negotiator UNUSED,
mark_complete(&obj->oid);
}
-struct loose_object_iter {
- struct oidset *loose_object_set;
- struct ref *refs;
-};
-
/*
* Mark recent commits available locally and reachable from a local ref as
* COMPLETE.
@@ -793,7 +791,8 @@ static void mark_complete_and_common_ref(struct fetch_negotiator *negotiator,
*/
trace2_region_enter("fetch-pack", "mark_complete_local_refs", NULL);
if (!args->deepen) {
- for_each_rawref(mark_complete_oid, NULL);
+ refs_for_each_rawref(get_main_ref_store(the_repository),
+ mark_complete_oid, NULL);
for_each_cached_alternate(NULL, mark_alternate_complete);
commit_list_sort_by_date(&complete);
if (cutoff)
@@ -957,12 +956,7 @@ static int get_pack(struct fetch_pack_args *args,
strvec_push(&cmd.args, alternate_shallow_file);
}
- if (fetch_fsck_objects >= 0
- ? fetch_fsck_objects
- : transfer_fsck_objects >= 0
- ? transfer_fsck_objects
- : 0)
- fsck_objects = 1;
+ fsck_objects = fetch_pack_fsck_objects();
if (do_keep || args->from_promisor || index_pack_args || fsck_objects) {
if (pack_lockfiles || fsck_objects)
@@ -1041,8 +1035,10 @@ static int get_pack(struct fetch_pack_args *args,
if (!is_well_formed)
die(_("fetch-pack: invalid index-pack output"));
- if (pack_lockfile)
+ if (pack_lockfiles && pack_lockfile)
string_list_append_nodup(pack_lockfiles, pack_lockfile);
+ else
+ free(pack_lockfile);
parse_gitmodules_oids(cmd.out, gitmodules_oids);
close(cmd.out);
}
@@ -1863,13 +1859,13 @@ static int fetch_pack_config_cb(const char *var, const char *value,
const char *msg_id;
if (strcmp(var, "fetch.fsck.skiplist") == 0) {
- const char *path;
+ char *path ;
if (git_config_pathname(&path, var, value))
return 1;
strbuf_addf(&fsck_msg_types, "%cskiplist=%s",
fsck_msg_types.len ? ',' : '=', path);
- free((char *)path);
+ free(path);
return 0;
}
@@ -2049,6 +2045,16 @@ static const struct object_id *iterate_ref_map(void *cb_data)
return &ref->old_oid;
}
+int fetch_pack_fsck_objects(void)
+{
+ fetch_pack_setup();
+ if (fetch_fsck_objects >= 0)
+ return fetch_fsck_objects;
+ if (transfer_fsck_objects >= 0)
+ return transfer_fsck_objects;
+ return 0;
+}
+
struct ref *fetch_pack(struct fetch_pack_args *args,
int fd[],
const struct ref *ref,
diff --git a/fetch-pack.h b/fetch-pack.h
index 6775d26517..b5c579cdae 100644
--- a/fetch-pack.h
+++ b/fetch-pack.h
@@ -101,4 +101,9 @@ void negotiate_using_fetch(const struct oid_array *negotiation_tips,
*/
int report_unmatched_refs(struct ref **sought, int nr_sought);
+/*
+ * Return true if checks for broken objects in received pack are required.
+ */
+int fetch_pack_fsck_objects(void);
+
#endif
diff --git a/fmt-merge-msg.c b/fmt-merge-msg.c
index ae201e21db..6acb37b480 100644
--- a/fmt-merge-msg.c
+++ b/fmt-merge-msg.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "config.h"
#include "environment.h"
@@ -447,7 +449,7 @@ static void fmt_merge_msg_title(struct strbuf *out,
const char *current_branch)
{
int i = 0;
- char *sep = "";
+ const char *sep = "";
strbuf_addstr(out, "Merge ");
for (i = 0; i < srcs.nr; i++) {
@@ -661,7 +663,9 @@ int fmt_merge_msg(struct strbuf *in, struct strbuf *out,
/* learn the commit that we merge into and the current branch name */
current_branch = current_branch_to_free =
- resolve_refdup("HEAD", RESOLVE_REF_READING, &head_oid, NULL);
+ refs_resolve_refdup(get_main_ref_store(the_repository),
+ "HEAD", RESOLVE_REF_READING, &head_oid,
+ NULL);
if (!current_branch)
die("No current branch");
diff --git a/fsck.c b/fsck.c
index 78af29d264..eea7145470 100644
--- a/fsck.c
+++ b/fsck.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "date.h"
#include "dir.h"
@@ -203,7 +205,8 @@ void fsck_set_msg_types(struct fsck_options *options, const char *values)
if (!strcmp(buf, "skiplist")) {
if (equal == len)
die("skiplist requires a path");
- oidset_parse_file(&options->skiplist, buf + equal + 1);
+ oidset_parse_file(&options->skiplist, buf + equal + 1,
+ the_repository->hash_algo);
buf += len + 1;
continue;
}
@@ -1179,7 +1182,7 @@ int fsck_object(struct object *obj, void *data, unsigned long size,
}
int fsck_buffer(const struct object_id *oid, enum object_type type,
- void *data, unsigned long size,
+ const void *data, unsigned long size,
struct fsck_options *options)
{
if (type == OBJ_BLOB)
@@ -1274,13 +1277,13 @@ int git_fsck_config(const char *var, const char *value,
const char *msg_id;
if (strcmp(var, "fsck.skiplist") == 0) {
- const char *path;
+ char *path;
struct strbuf sb = STRBUF_INIT;
if (git_config_pathname(&path, var, value))
return 1;
strbuf_addf(&sb, "skiplist=%s", path);
- free((char *)path);
+ free(path);
fsck_set_msg_types(options, sb.buf);
strbuf_release(&sb);
return 0;
diff --git a/fsck.h b/fsck.h
index e3adf9d911..6085a384f6 100644
--- a/fsck.h
+++ b/fsck.h
@@ -190,7 +190,7 @@ int fsck_object(struct object *obj, void *data, unsigned long size,
* struct.
*/
int fsck_buffer(const struct object_id *oid, enum object_type,
- void *data, unsigned long size,
+ const void *data, unsigned long size,
struct fsck_options *options);
/*
diff --git a/fsmonitor-ipc.c b/fsmonitor-ipc.c
index 45471b5b74..f1b1631111 100644
--- a/fsmonitor-ipc.c
+++ b/fsmonitor-ipc.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "gettext.h"
#include "simple-ipc.h"
diff --git a/fsmonitor-settings.c b/fsmonitor-settings.c
index a6a9e6bc19..e818583420 100644
--- a/fsmonitor-settings.c
+++ b/fsmonitor-settings.c
@@ -103,6 +103,7 @@ static struct fsmonitor_settings *alloc_settings(void)
static void lookup_fsmonitor_settings(struct repository *r)
{
const char *const_str;
+ char *to_free = NULL;
int bool_value;
if (r->settings.fsmonitor)
@@ -129,8 +130,9 @@ static void lookup_fsmonitor_settings(struct repository *r)
break;
case -1: /* config value set to an arbitrary string */
- if (repo_config_get_pathname(r, "core.fsmonitor", &const_str))
+ if (repo_config_get_pathname(r, "core.fsmonitor", &to_free))
return; /* should not happen */
+ const_str = to_free;
break;
default: /* should not happen */
@@ -141,6 +143,7 @@ static void lookup_fsmonitor_settings(struct repository *r)
fsm_settings__set_hook(r, const_str);
else
fsm_settings__set_disabled(r);
+ free(to_free);
}
enum fsmonitor_mode fsm_settings__get_mode(struct repository *r)
diff --git a/git-compat-util.h b/git-compat-util.h
index ca7678a379..71b4d23f03 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -506,6 +506,14 @@ static inline int git_offset_1st_component(const char *path)
#define offset_1st_component git_offset_1st_component
#endif
+#ifndef fspathcmp
+#define fspathcmp git_fspathcmp
+#endif
+
+#ifndef fspathncmp
+#define fspathncmp git_fspathncmp
+#endif
+
#ifndef is_valid_path
#define is_valid_path(path) 1
#endif
diff --git a/git-gui/README.md b/git-gui/README.md
index b460b649a8..948e9250d5 100644
--- a/git-gui/README.md
+++ b/git-gui/README.md
@@ -42,8 +42,8 @@ You probably need to have root/admin permissions to install.
# Contributing
-The project is currently maintained by Pratyush Yadav over at
-https://github.com/prati0100/git-gui. Even though the project is hosted at
+The project is currently maintained by Johannes Sixt at
+https://github.com/j6t/git-gui. Even though the project is hosted at
GitHub, the development does not happen over GitHub Issues and Pull Requests.
Instead, an email based workflow is used. The Git mailing list
[git@vger.kernel.org](mailto:git@vger.kernel.org) is where the patches are
diff --git a/git-gui/git-gui.sh b/git-gui/git-gui.sh
index 507fb2b682..8fe7538e72 100755
--- a/git-gui/git-gui.sh
+++ b/git-gui/git-gui.sh
@@ -2301,7 +2301,7 @@ proc do_quit {{rc {1}}} {
#
set save [gitdir GITGUI_MSG]
if {$GITGUI_BCK_exists && ![$ui_comm edit modified]} {
- file rename -force [gitdir GITGUI_BCK] $save
+ catch { file rename -force [gitdir GITGUI_BCK] $save }
set GITGUI_BCK_exists 0
} elseif {[$ui_comm edit modified]} {
set msg [string trim [$ui_comm get 0.0 end]]
diff --git a/git-gui/po/fr.po b/git-gui/po/fr.po
index 0aff18691d..878df65399 100644
--- a/git-gui/po/fr.po
+++ b/git-gui/po/fr.po
@@ -1646,7 +1646,7 @@ msgstr "Dépôt Git (sous projet)"
#: lib/diff.tcl:222
msgid "* Binary file (not showing content)."
-msgstr "* Fichier binaire (pas d'apperçu du contenu)."
+msgstr "* Fichier binaire (pas d'aperçu du contenu)."
#: lib/diff.tcl:227
#, tcl-format
diff --git a/git-gui/po/sv.po b/git-gui/po/sv.po
index 1b4ad8368e..de65c18584 100644
--- a/git-gui/po/sv.po
+++ b/git-gui/po/sv.po
@@ -3,14 +3,14 @@
# This file is distributed under the same license as the git-gui package.
#
# Mikael Magnusson <mikachu@gmail.com>, 2008.
-# Peter Krefting <peter@softwolves.pp.se>, 2007-2008, 2015.
+# Peter Krefting <peter@softwolves.pp.se>, 2007-2023.
#
msgid ""
msgstr ""
-"Project-Id-Version: sv\n"
+"Project-Id-Version: git-gui 0.21.0\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2015-03-27 10:15+0100\n"
-"PO-Revision-Date: 2015-03-27 10:24+0100\n"
+"POT-Creation-Date: 2023-10-26 21:17+0100\n"
+"PO-Revision-Date: 2023-10-26 21:23+0100\n"
"Last-Translator: Peter Krefting <peter@softwolves.pp.se>\n"
"Language-Team: Swedish <tp-sv@listor.tp-sv.se>\n"
"Language: sv\n"
@@ -18,35 +18,35 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-"X-Generator: Gtranslator 2.91.6\n"
+"X-Generator: Gtranslator 3.38.0\n"
-#: git-gui.sh:861
+#: git-gui.sh:884
#, tcl-format
msgid "Invalid font specified in %s:"
msgstr "Ogiltigt teckensnitt angivet i %s:"
-#: git-gui.sh:915
+#: git-gui.sh:939
msgid "Main Font"
msgstr "Huvudteckensnitt"
-#: git-gui.sh:916
+#: git-gui.sh:940
msgid "Diff/Console Font"
msgstr "Diff/konsolteckensnitt"
-#: git-gui.sh:931 git-gui.sh:945 git-gui.sh:958 git-gui.sh:1048
-#: git-gui.sh:1067 git-gui.sh:3125
+#: git-gui.sh:955 git-gui.sh:969 git-gui.sh:982 git-gui.sh:1072 git-gui.sh:1091
+#: git-gui.sh:3233
msgid "git-gui: fatal error"
msgstr "git-gui: ödesdigert fel"
-#: git-gui.sh:932
+#: git-gui.sh:956
msgid "Cannot find git in PATH."
msgstr "Hittar inte git i PATH."
-#: git-gui.sh:959
+#: git-gui.sh:983
msgid "Cannot parse Git version string:"
msgstr "Kan inte tolka versionssträng från Git:"
-#: git-gui.sh:984
+#: git-gui.sh:1008
#, tcl-format
msgid ""
"Git version cannot be determined.\n"
@@ -59,53 +59,53 @@ msgid ""
msgstr ""
"Kan inte avgöra Gits version.\n"
"\n"
-"%s säger att dess version är \"%s\".\n"
+"%s säger att dess version är ”%s”.\n"
"\n"
"%s kräver minst Git 1.5.0 eller senare.\n"
"\n"
-"Anta att \"%s\" är version 1.5.0?\n"
+"Anta att ”%s” är version 1.5.0?\n"
-#: git-gui.sh:1281
+#: git-gui.sh:1302
msgid "Git directory not found:"
msgstr "Git-katalogen hittades inte:"
-#: git-gui.sh:1315
+#: git-gui.sh:1332
msgid "Cannot move to top of working directory:"
msgstr "Kan inte gå till början på arbetskatalogen:"
-#: git-gui.sh:1323
+#: git-gui.sh:1340
msgid "Cannot use bare repository:"
msgstr "Kan inte använda naket arkiv:"
-#: git-gui.sh:1331
+#: git-gui.sh:1348
msgid "No working directory"
msgstr "Ingen arbetskatalog"
-#: git-gui.sh:1503 lib/checkout_op.tcl:306
+#: git-gui.sh:1523 lib/checkout_op.tcl:306
msgid "Refreshing file status..."
msgstr "Uppdaterar filstatus..."
-#: git-gui.sh:1563
+#: git-gui.sh:1567
msgid "Scanning for modified files ..."
msgstr "Söker efter ändrade filer..."
-#: git-gui.sh:1639
+#: git-gui.sh:1651
msgid "Calling prepare-commit-msg hook..."
msgstr ""
"Anropar kroken för förberedelse av incheckningsmeddelande (prepare-commit-"
"msg)..."
-#: git-gui.sh:1656
+#: git-gui.sh:1668
msgid "Commit declined by prepare-commit-msg hook."
msgstr ""
"Incheckningen avvisades av kroken för förberedelse av incheckningsmeddelande "
"(prepare-commit-msg)."
-#: git-gui.sh:1814 lib/browser.tcl:252
+#: git-gui.sh:1826 lib/browser.tcl:252
msgid "Ready."
msgstr "Klar."
-#: git-gui.sh:1978
+#: git-gui.sh:1990
#, tcl-format
msgid ""
"Display limit (gui.maxfilesdisplayed = %s) reached, not showing all %s files."
@@ -113,524 +113,811 @@ msgstr ""
"Visningsgräns (gui.maxfilesdisplayed = %s) nådd, visare inte samtliga %s "
"filer."
-#: git-gui.sh:2101
+#: git-gui.sh:2113
msgid "Unmodified"
msgstr "Oförändrade"
-#: git-gui.sh:2103
+#: git-gui.sh:2115
msgid "Modified, not staged"
msgstr "Förändrade, ej köade"
-#: git-gui.sh:2104 git-gui.sh:2116
+#: git-gui.sh:2116 git-gui.sh:2128
msgid "Staged for commit"
msgstr "Köade för incheckning"
-#: git-gui.sh:2105 git-gui.sh:2117
+#: git-gui.sh:2117 git-gui.sh:2129
msgid "Portions staged for commit"
msgstr "Delar köade för incheckning"
-#: git-gui.sh:2106 git-gui.sh:2118
+#: git-gui.sh:2118 git-gui.sh:2130
msgid "Staged for commit, missing"
msgstr "Köade för incheckning, saknade"
-#: git-gui.sh:2108
+#: git-gui.sh:2120
msgid "File type changed, not staged"
msgstr "Filtyp ändrad, ej köade"
-#: git-gui.sh:2109 git-gui.sh:2110
+#: git-gui.sh:2121 git-gui.sh:2122
msgid "File type changed, old type staged for commit"
msgstr "Filtyp ändrad, gammal typ köade för incheckning"
-#: git-gui.sh:2111
+#: git-gui.sh:2123
msgid "File type changed, staged"
msgstr "Filtyp ändrad, köade"
-#: git-gui.sh:2112
+#: git-gui.sh:2124
msgid "File type change staged, modification not staged"
msgstr "Filtypsändringar köade, innehållsändringar ej köade"
-#: git-gui.sh:2113
+#: git-gui.sh:2125
msgid "File type change staged, file missing"
msgstr "Filtypsändringar köade, fil saknas"
-#: git-gui.sh:2115
+#: git-gui.sh:2127
msgid "Untracked, not staged"
msgstr "Ej spårade, ej köade"
-#: git-gui.sh:2120
+#: git-gui.sh:2132
msgid "Missing"
msgstr "Saknade"
-#: git-gui.sh:2121
+#: git-gui.sh:2133
msgid "Staged for removal"
msgstr "Köade för borttagning"
-#: git-gui.sh:2122
+#: git-gui.sh:2134
msgid "Staged for removal, still present"
msgstr "Köade för borttagning, fortfarande närvarande"
-#: git-gui.sh:2124 git-gui.sh:2125 git-gui.sh:2126 git-gui.sh:2127
-#: git-gui.sh:2128 git-gui.sh:2129
+#: git-gui.sh:2136 git-gui.sh:2137 git-gui.sh:2138 git-gui.sh:2139
+#: git-gui.sh:2140 git-gui.sh:2141
msgid "Requires merge resolution"
msgstr "Kräver konflikthantering efter sammanslagning"
-#: git-gui.sh:2164
-msgid "Starting gitk... please wait..."
-msgstr "Startar gitk... vänta..."
-
-#: git-gui.sh:2176
+#: git-gui.sh:2186
msgid "Couldn't find gitk in PATH"
msgstr "Hittade inte gitk i PATH."
-#: git-gui.sh:2235
+#: git-gui.sh:2233 git-gui.sh:2269
+#, tcl-format
+msgid "Starting %s... please wait..."
+msgstr "Startar %s... vänta..."
+
+#: git-gui.sh:2248
msgid "Couldn't find git gui in PATH"
msgstr "Hittade inte git gui i PATH."
-#: git-gui.sh:2654 lib/choose_repository.tcl:41
+#: git-gui.sh:2751 lib/choose_repository.tcl:53
msgid "Repository"
msgstr "Arkiv"
-#: git-gui.sh:2655
+#: git-gui.sh:2752
msgid "Edit"
msgstr "Redigera"
-#: git-gui.sh:2657 lib/choose_rev.tcl:567
+#: git-gui.sh:2754 lib/choose_rev.tcl:567
msgid "Branch"
msgstr "Gren"
-#: git-gui.sh:2660 lib/choose_rev.tcl:554
+#: git-gui.sh:2757 lib/choose_rev.tcl:554
msgid "Commit@@noun"
msgstr "Incheckning"
-#: git-gui.sh:2663 lib/merge.tcl:123 lib/merge.tcl:152 lib/merge.tcl:170
+#: git-gui.sh:2760 lib/merge.tcl:127 lib/merge.tcl:174
msgid "Merge"
msgstr "Slå ihop"
-#: git-gui.sh:2664 lib/choose_rev.tcl:563
+#: git-gui.sh:2761 lib/choose_rev.tcl:563
msgid "Remote"
msgstr "Fjärrarkiv"
-#: git-gui.sh:2667
+#: git-gui.sh:2764
msgid "Tools"
msgstr "Verktyg"
-#: git-gui.sh:2676
+#: git-gui.sh:2773
msgid "Explore Working Copy"
msgstr "Utforska arbetskopia"
-#: git-gui.sh:2682
+#: git-gui.sh:2788
msgid "Git Bash"
msgstr "Git Bash"
-#: git-gui.sh:2692
+#: git-gui.sh:2797
msgid "Browse Current Branch's Files"
msgstr "Bläddra i grenens filer"
-#: git-gui.sh:2696
+#: git-gui.sh:2801
msgid "Browse Branch Files..."
msgstr "Bläddra filer på gren..."
-#: git-gui.sh:2701
+#: git-gui.sh:2806
msgid "Visualize Current Branch's History"
msgstr "Visualisera grenens historik"
-#: git-gui.sh:2705
+#: git-gui.sh:2810
msgid "Visualize All Branch History"
msgstr "Visualisera alla grenars historik"
-#: git-gui.sh:2712
+#: git-gui.sh:2817
#, tcl-format
msgid "Browse %s's Files"
msgstr "Bläddra i filer för %s"
-#: git-gui.sh:2714
+#: git-gui.sh:2819
#, tcl-format
msgid "Visualize %s's History"
msgstr "Visualisera historik för %s"
-#: git-gui.sh:2719 lib/database.tcl:40 lib/database.tcl:66
+#: git-gui.sh:2824 lib/database.tcl:40
msgid "Database Statistics"
msgstr "Databasstatistik"
-#: git-gui.sh:2722 lib/database.tcl:33
+#: git-gui.sh:2827 lib/database.tcl:33
msgid "Compress Database"
msgstr "Komprimera databas"
-#: git-gui.sh:2725
+#: git-gui.sh:2830
msgid "Verify Database"
msgstr "Verifiera databas"
-#: git-gui.sh:2732 git-gui.sh:2736 git-gui.sh:2740 lib/shortcut.tcl:8
-#: lib/shortcut.tcl:40 lib/shortcut.tcl:72
+#: git-gui.sh:2837 git-gui.sh:2841 git-gui.sh:2845
msgid "Create Desktop Icon"
msgstr "Skapa skrivbordsikon"
-#: git-gui.sh:2748 lib/choose_repository.tcl:193 lib/choose_repository.tcl:201
+#: git-gui.sh:2853 lib/choose_repository.tcl:206 lib/choose_repository.tcl:214
msgid "Quit"
msgstr "Avsluta"
-#: git-gui.sh:2756
+#: git-gui.sh:2861
msgid "Undo"
msgstr "Ångra"
-#: git-gui.sh:2759
+#: git-gui.sh:2864
msgid "Redo"
msgstr "Gör om"
-#: git-gui.sh:2763 git-gui.sh:3368
+#: git-gui.sh:2868 git-gui.sh:3493
msgid "Cut"
msgstr "Klipp ut"
-#: git-gui.sh:2766 git-gui.sh:3371 git-gui.sh:3445 git-gui.sh:3530
+#: git-gui.sh:2871 git-gui.sh:3496 git-gui.sh:3572 git-gui.sh:3665
#: lib/console.tcl:69
msgid "Copy"
msgstr "Kopiera"
-#: git-gui.sh:2769 git-gui.sh:3374
+#: git-gui.sh:2874 git-gui.sh:3499
msgid "Paste"
msgstr "Klistra in"
-#: git-gui.sh:2772 git-gui.sh:3377 lib/remote_branch_delete.tcl:39
-#: lib/branch_delete.tcl:28
+#: git-gui.sh:2877 git-gui.sh:3502 lib/branch_delete.tcl:28
+#: lib/remote_branch_delete.tcl:39
msgid "Delete"
msgstr "Ta bort"
-#: git-gui.sh:2776 git-gui.sh:3381 git-gui.sh:3534 lib/console.tcl:71
+#: git-gui.sh:2881 git-gui.sh:3506 git-gui.sh:3669 lib/console.tcl:71
msgid "Select All"
msgstr "Markera alla"
-#: git-gui.sh:2785
+#: git-gui.sh:2890
msgid "Create..."
msgstr "Skapa..."
-#: git-gui.sh:2791
+#: git-gui.sh:2896
msgid "Checkout..."
msgstr "Checka ut..."
-#: git-gui.sh:2797
+#: git-gui.sh:2902
msgid "Rename..."
msgstr "Byt namn..."
-#: git-gui.sh:2802
+#: git-gui.sh:2907
msgid "Delete..."
msgstr "Ta bort..."
-#: git-gui.sh:2807
+#: git-gui.sh:2912
msgid "Reset..."
msgstr "Återställ..."
-#: git-gui.sh:2817
+#: git-gui.sh:2922
msgid "Done"
msgstr "Färdig"
-#: git-gui.sh:2819
+#: git-gui.sh:2924
msgid "Commit@@verb"
msgstr "Checka in"
-#: git-gui.sh:2828 git-gui.sh:3309
-msgid "New Commit"
-msgstr "Ny incheckning"
-
-#: git-gui.sh:2836 git-gui.sh:3316
+#: git-gui.sh:2933 git-gui.sh:3432
msgid "Amend Last Commit"
msgstr "Lägg till föregående incheckning"
-#: git-gui.sh:2846 git-gui.sh:3270 lib/remote_branch_delete.tcl:101
+#: git-gui.sh:2943 git-gui.sh:3393 lib/remote_branch_delete.tcl:101
msgid "Rescan"
msgstr "Sök på nytt"
-#: git-gui.sh:2852
+#: git-gui.sh:2949
msgid "Stage To Commit"
msgstr "Köa för incheckning"
-#: git-gui.sh:2858
+#: git-gui.sh:2955
msgid "Stage Changed Files To Commit"
msgstr "Köa ändrade filer för incheckning"
-#: git-gui.sh:2864
+#: git-gui.sh:2961
msgid "Unstage From Commit"
msgstr "Ta bort från incheckningskö"
-#: git-gui.sh:2870 lib/index.tcl:442
+#: git-gui.sh:2967 lib/index.tcl:521
msgid "Revert Changes"
msgstr "Återställ ändringar"
-#: git-gui.sh:2878 git-gui.sh:3581 git-gui.sh:3612
+#: git-gui.sh:2975 git-gui.sh:3732 git-gui.sh:3763
msgid "Show Less Context"
msgstr "Visa mindre sammanhang"
-#: git-gui.sh:2882 git-gui.sh:3585 git-gui.sh:3616
+#: git-gui.sh:2979 git-gui.sh:3736 git-gui.sh:3767
msgid "Show More Context"
msgstr "Visa mer sammanhang"
-#: git-gui.sh:2889 git-gui.sh:3283 git-gui.sh:3392
+#: git-gui.sh:2986 git-gui.sh:3406 git-gui.sh:3517
msgid "Sign Off"
msgstr "Skriv under"
-#: git-gui.sh:2905
+#: git-gui.sh:3002
msgid "Local Merge..."
msgstr "Lokal sammanslagning..."
-#: git-gui.sh:2910
+#: git-gui.sh:3007
msgid "Abort Merge..."
msgstr "Avbryt sammanslagning..."
-#: git-gui.sh:2922 git-gui.sh:2950
+#: git-gui.sh:3019 git-gui.sh:3047
msgid "Add..."
msgstr "Lägg till..."
-#: git-gui.sh:2926
+#: git-gui.sh:3023
msgid "Push..."
msgstr "Sänd..."
-#: git-gui.sh:2930
+#: git-gui.sh:3027
msgid "Delete Branch..."
msgstr "Ta bort gren..."
-#: git-gui.sh:2940 git-gui.sh:3563
+#: git-gui.sh:3037 git-gui.sh:3698
msgid "Options..."
msgstr "Alternativ..."
-#: git-gui.sh:2951
+#: git-gui.sh:3048
msgid "Remove..."
msgstr "Ta bort..."
-#: git-gui.sh:2960 lib/choose_repository.tcl:55
+#: git-gui.sh:3057 lib/choose_repository.tcl:67
msgid "Help"
msgstr "Hjälp"
-#: git-gui.sh:2964 git-gui.sh:2968 lib/choose_repository.tcl:49
-#: lib/choose_repository.tcl:58 lib/about.tcl:14
+#: git-gui.sh:3061 git-gui.sh:3065 lib/about.tcl:14
+#: lib/choose_repository.tcl:61 lib/choose_repository.tcl:70
#, tcl-format
msgid "About %s"
msgstr "Om %s"
-#: git-gui.sh:2992
+#: git-gui.sh:3085
msgid "Online Documentation"
msgstr "Webbdokumentation"
-#: git-gui.sh:2995 lib/choose_repository.tcl:52 lib/choose_repository.tcl:61
+#: git-gui.sh:3088 lib/choose_repository.tcl:64 lib/choose_repository.tcl:73
msgid "Show SSH Key"
msgstr "Visa SSH-nyckel"
-#: git-gui.sh:3014 git-gui.sh:3146
+#: git-gui.sh:3118 git-gui.sh:3250
+msgid "usage:"
+msgstr "användning:"
+
+#: git-gui.sh:3122 git-gui.sh:3254
msgid "Usage"
msgstr "Användning"
-#: git-gui.sh:3095 lib/blame.tcl:573
+#: git-gui.sh:3203 lib/blame.tcl:576
msgid "Error"
msgstr "Fel"
-#: git-gui.sh:3126
+#: git-gui.sh:3234
#, tcl-format
msgid "fatal: cannot stat path %s: No such file or directory"
msgstr ""
"ödesdigert: kunde inte ta status på sökvägen %s: Fil eller katalog saknas"
-#: git-gui.sh:3159
+#: git-gui.sh:3267
msgid "Current Branch:"
msgstr "Aktuell gren:"
-#: git-gui.sh:3185
-msgid "Staged Changes (Will Commit)"
-msgstr "Köade ändringar (kommer att checkas in)"
-
-#: git-gui.sh:3205
+#: git-gui.sh:3292
msgid "Unstaged Changes"
msgstr "Oköade ändringar"
-#: git-gui.sh:3276
+#: git-gui.sh:3314
+msgid "Staged Changes (Will Commit)"
+msgstr "Köade ändringar (kommer att checkas in)"
+
+#: git-gui.sh:3399
msgid "Stage Changed"
msgstr "Köa ändrade"
-#: git-gui.sh:3295 lib/transport.tcl:137 lib/transport.tcl:229
+#: git-gui.sh:3418 lib/transport.tcl:137
msgid "Push"
msgstr "Sänd"
-#: git-gui.sh:3330
+#: git-gui.sh:3445
msgid "Initial Commit Message:"
msgstr "Inledande incheckningsmeddelande:"
-#: git-gui.sh:3331
+#: git-gui.sh:3446
msgid "Amended Commit Message:"
msgstr "Utökat incheckningsmeddelande:"
-#: git-gui.sh:3332
+#: git-gui.sh:3447
msgid "Amended Initial Commit Message:"
msgstr "Utökat inledande incheckningsmeddelande:"
-#: git-gui.sh:3333
+#: git-gui.sh:3448
msgid "Amended Merge Commit Message:"
msgstr "Utökat incheckningsmeddelande för sammanslagning:"
-#: git-gui.sh:3334
+#: git-gui.sh:3449
msgid "Merge Commit Message:"
msgstr "Incheckningsmeddelande för sammanslagning:"
-#: git-gui.sh:3335
+#: git-gui.sh:3450
msgid "Commit Message:"
msgstr "Incheckningsmeddelande:"
-#: git-gui.sh:3384 git-gui.sh:3538 lib/console.tcl:73
+#: git-gui.sh:3509 git-gui.sh:3673 lib/console.tcl:73
msgid "Copy All"
msgstr "Kopiera alla"
-#: git-gui.sh:3408 lib/blame.tcl:105
+#: git-gui.sh:3533 lib/blame.tcl:106
msgid "File:"
msgstr "Fil:"
-#: git-gui.sh:3526
+#: git-gui.sh:3581 lib/choose_repository.tcl:1054
+msgid "Open"
+msgstr "Öppna"
+
+#: git-gui.sh:3661
msgid "Refresh"
msgstr "Uppdatera"
-#: git-gui.sh:3547
+#: git-gui.sh:3682
msgid "Decrease Font Size"
msgstr "Minska teckensnittsstorlek"
-#: git-gui.sh:3551
+#: git-gui.sh:3686
msgid "Increase Font Size"
msgstr "Öka teckensnittsstorlek"
-#: git-gui.sh:3559 lib/blame.tcl:294
+#: git-gui.sh:3694 lib/blame.tcl:296
msgid "Encoding"
msgstr "Teckenkodning"
-#: git-gui.sh:3570
+#: git-gui.sh:3705
msgid "Apply/Reverse Hunk"
msgstr "Använd/återställ del"
-#: git-gui.sh:3575
+#: git-gui.sh:3710
msgid "Apply/Reverse Line"
msgstr "Använd/återställ rad"
-#: git-gui.sh:3594
+#: git-gui.sh:3716 git-gui.sh:3826 git-gui.sh:3837
+msgid "Revert Hunk"
+msgstr "Återställ del"
+
+#: git-gui.sh:3721 git-gui.sh:3833 git-gui.sh:3844
+msgid "Revert Line"
+msgstr "Återställ rad"
+
+#: git-gui.sh:3726 git-gui.sh:3823
+msgid "Undo Last Revert"
+msgstr "Ångra senaste återställning"
+
+#: git-gui.sh:3745
msgid "Run Merge Tool"
msgstr "Starta verktyg för sammanslagning"
-#: git-gui.sh:3599
+#: git-gui.sh:3750
msgid "Use Remote Version"
msgstr "Använd versionen från fjärrarkivet"
-#: git-gui.sh:3603
+#: git-gui.sh:3754
msgid "Use Local Version"
msgstr "Använd lokala versionen"
-#: git-gui.sh:3607
+#: git-gui.sh:3758
msgid "Revert To Base"
msgstr "Återställ till basversionen"
-#: git-gui.sh:3625
+#: git-gui.sh:3776
msgid "Visualize These Changes In The Submodule"
msgstr "Visualisera ändringarna i undermodulen"
-#: git-gui.sh:3629
+#: git-gui.sh:3780
msgid "Visualize Current Branch History In The Submodule"
msgstr "Visualisera grenens historik i undermodulen"
-#: git-gui.sh:3633
+#: git-gui.sh:3784
msgid "Visualize All Branch History In The Submodule"
msgstr "Visualisera alla grenars historik i undermodulen"
-#: git-gui.sh:3638
+#: git-gui.sh:3789
msgid "Start git gui In The Submodule"
msgstr "Starta git gui i undermodulen"
-#: git-gui.sh:3673
+#: git-gui.sh:3825
msgid "Unstage Hunk From Commit"
msgstr "Ta bort del ur incheckningskö"
-#: git-gui.sh:3675
+#: git-gui.sh:3829
msgid "Unstage Lines From Commit"
msgstr "Ta bort rader ur incheckningskö"
-#: git-gui.sh:3677
+#: git-gui.sh:3830 git-gui.sh:3841
+msgid "Revert Lines"
+msgstr "Återställ rader"
+
+#: git-gui.sh:3832
msgid "Unstage Line From Commit"
msgstr "Ta bort rad ur incheckningskö"
-#: git-gui.sh:3680
+#: git-gui.sh:3836
msgid "Stage Hunk For Commit"
msgstr "Ställ del i incheckningskö"
-#: git-gui.sh:3682
+#: git-gui.sh:3840
msgid "Stage Lines For Commit"
msgstr "Ställ rader i incheckningskö"
-#: git-gui.sh:3684
+#: git-gui.sh:3843
msgid "Stage Line For Commit"
msgstr "Ställ rad i incheckningskö"
-#: git-gui.sh:3709
+#: git-gui.sh:3893
msgid "Initializing..."
msgstr "Initierar..."
-#: git-gui.sh:3852
+#: lib/about.tcl:26
+msgid "git-gui - a graphical user interface for Git."
+msgstr "git-gui - ett grafiskt användargränssnitt för Git."
+
+#: lib/blame.tcl:74
#, tcl-format
-msgid ""
-"Possible environment issues exist.\n"
-"\n"
-"The following environment variables are probably\n"
-"going to be ignored by any Git subprocess run\n"
-"by %s:\n"
-"\n"
-msgstr ""
-"Det finns möjliga problem med miljövariabler.\n"
-"\n"
-"Följande miljövariabler kommer troligen att\n"
-"ignoreras av alla Git-underprocesser som körs\n"
-"av %s:\n"
-"\n"
+msgid "%s (%s): File Viewer"
+msgstr "%s (%s): Filvisare"
+
+#: lib/blame.tcl:80
+msgid "Commit:"
+msgstr "Incheckning:"
+
+#: lib/blame.tcl:282
+msgid "Copy Commit"
+msgstr "Kopiera incheckning"
+
+#: lib/blame.tcl:286
+msgid "Find Text..."
+msgstr "Sök text..."
+
+#: lib/blame.tcl:290
+msgid "Goto Line..."
+msgstr "Gå till rad..."
+
+#: lib/blame.tcl:299
+msgid "Do Full Copy Detection"
+msgstr "Gör full kopieringsigenkänning"
+
+#: lib/blame.tcl:303
+msgid "Show History Context"
+msgstr "Visa historiksammanhang"
+
+#: lib/blame.tcl:306
+msgid "Blame Parent Commit"
+msgstr "Klandra föräldraincheckning"
+
+#: lib/blame.tcl:469
+#, tcl-format
+msgid "Reading %s..."
+msgstr "Läser %s..."
+
+#: lib/blame.tcl:597
+msgid "Loading copy/move tracking annotations..."
+msgstr "Läser annoteringar för kopiering/flyttning..."
+
+#: lib/blame.tcl:614
+msgid "lines annotated"
+msgstr "rader annoterade"
+
+#: lib/blame.tcl:816
+msgid "Loading original location annotations..."
+msgstr "Läser in annotering av originalplacering..."
+
+#: lib/blame.tcl:819
+msgid "Annotation complete."
+msgstr "Annotering fullbordad."
+
+#: lib/blame.tcl:850
+msgid "Busy"
+msgstr "Upptagen"
+
+#: lib/blame.tcl:851
+msgid "Annotation process is already running."
+msgstr "Annoteringsprocess körs redan."
+
+#: lib/blame.tcl:890
+msgid "Running thorough copy detection..."
+msgstr "Kör grundlig kopieringsigenkänning..."
+
+#: lib/blame.tcl:958
+msgid "Loading annotation..."
+msgstr "Läser in annotering..."
+
+#: lib/blame.tcl:1011
+msgid "Author:"
+msgstr "Författare:"
+
+#: lib/blame.tcl:1015
+msgid "Committer:"
+msgstr "Incheckare:"
+
+#: lib/blame.tcl:1020
+msgid "Original File:"
+msgstr "Ursprunglig fil:"
+
+#: lib/blame.tcl:1068
+msgid "Cannot find HEAD commit:"
+msgstr "Hittar inte incheckning för HEAD:"
+
+#: lib/blame.tcl:1123
+msgid "Cannot find parent commit:"
+msgstr "Hittar inte föräldraincheckning:"
+
+#: lib/blame.tcl:1138
+msgid "Unable to display parent"
+msgstr "Kan inte visa förälder"
+
+#: lib/blame.tcl:1139 lib/diff.tcl:345
+msgid "Error loading diff:"
+msgstr "Fel vid inläsning av differens:"
+
+#: lib/blame.tcl:1280
+msgid "Originally By:"
+msgstr "Ursprungligen av:"
+
+#: lib/blame.tcl:1286
+msgid "In File:"
+msgstr "I filen:"
+
+#: lib/blame.tcl:1291
+msgid "Copied Or Moved Here By:"
+msgstr "Kopierad eller flyttad hit av:"
+
+#: lib/branch_checkout.tcl:16
+#, tcl-format
+msgid "%s (%s): Checkout Branch"
+msgstr "%s (%s): Checka ut gren"
+
+#: lib/branch_checkout.tcl:21
+msgid "Checkout Branch"
+msgstr "Checka ut gren"
+
+#: lib/branch_checkout.tcl:26
+msgid "Checkout"
+msgstr "Checka ut"
+
+#: lib/branch_checkout.tcl:30 lib/branch_create.tcl:37 lib/branch_delete.tcl:34
+#: lib/branch_rename.tcl:32 lib/browser.tcl:292 lib/checkout_op.tcl:580
+#: lib/choose_font.tcl:45 lib/merge.tcl:178 lib/option.tcl:127
+#: lib/remote_add.tcl:34 lib/remote_branch_delete.tcl:43 lib/tools_dlg.tcl:41
+#: lib/tools_dlg.tcl:202 lib/tools_dlg.tcl:345 lib/transport.tcl:141
+msgid "Cancel"
+msgstr "Avbryt"
+
+#: lib/branch_checkout.tcl:35 lib/browser.tcl:297 lib/tools_dlg.tcl:321
+msgid "Revision"
+msgstr "Revision"
+
+#: lib/branch_checkout.tcl:39 lib/branch_create.tcl:69 lib/option.tcl:310
+msgid "Options"
+msgstr "Alternativ"
+
+#: lib/branch_checkout.tcl:42 lib/branch_create.tcl:92
+msgid "Fetch Tracking Branch"
+msgstr "Hämta spårande gren"
+
+#: lib/branch_checkout.tcl:47
+msgid "Detach From Local Branch"
+msgstr "Koppla bort från lokal gren"
+
+#: lib/branch_create.tcl:23
+#, tcl-format
+msgid "%s (%s): Create Branch"
+msgstr "%s (%s): Skapa gren"
+
+#: lib/branch_create.tcl:28
+msgid "Create New Branch"
+msgstr "Skapa ny gren"
+
+#: lib/branch_create.tcl:33 lib/choose_repository.tcl:386
+msgid "Create"
+msgstr "Skapa"
+
+#: lib/branch_create.tcl:42
+msgid "Branch Name"
+msgstr "Namn på gren"
+
+#: lib/branch_create.tcl:44 lib/remote_add.tcl:41 lib/tools_dlg.tcl:51
+msgid "Name:"
+msgstr "Namn:"
+
+#: lib/branch_create.tcl:57
+msgid "Match Tracking Branch Name"
+msgstr "Använd namn på spårad gren"
+
+#: lib/branch_create.tcl:66
+msgid "Starting Revision"
+msgstr "Inledande revision"
+
+#: lib/branch_create.tcl:72
+msgid "Update Existing Branch:"
+msgstr "Uppdatera befintlig gren:"
+
+#: lib/branch_create.tcl:75
+msgid "No"
+msgstr "Nej"
+
+#: lib/branch_create.tcl:80
+msgid "Fast Forward Only"
+msgstr "Endast snabbspolning"
+
+#: lib/branch_create.tcl:85 lib/checkout_op.tcl:572
+msgid "Reset"
+msgstr "Återställ"
+
+#: lib/branch_create.tcl:97
+msgid "Checkout After Creation"
+msgstr "Checka ut när skapad"
+
+#: lib/branch_create.tcl:132
+msgid "Please select a tracking branch."
+msgstr "Välj en gren att spåra."
+
+#: lib/branch_create.tcl:141
+#, tcl-format
+msgid "Tracking branch %s is not a branch in the remote repository."
+msgstr "Den spårade grenen %s är inte en gren i fjärrarkivet."
+
+#: lib/branch_create.tcl:154 lib/branch_rename.tcl:92
+msgid "Please supply a branch name."
+msgstr "Ange ett namn för grenen."
+
+#: lib/branch_create.tcl:165 lib/branch_rename.tcl:112
+#, tcl-format
+msgid "'%s' is not an acceptable branch name."
+msgstr "”%s” kan inte användas som namn på grenen."
+
+#: lib/branch_delete.tcl:16
+#, tcl-format
+msgid "%s (%s): Delete Branch"
+msgstr "%s (%s): Ta bort gren"
+
+#: lib/branch_delete.tcl:21
+msgid "Delete Local Branch"
+msgstr "Ta bort lokal gren"
+
+#: lib/branch_delete.tcl:39
+msgid "Local Branches"
+msgstr "Lokala grenar"
+
+#: lib/branch_delete.tcl:51
+msgid "Delete Only If Merged Into"
+msgstr "Ta bara bort om sammanslagen med"
+
+#: lib/branch_delete.tcl:53 lib/remote_branch_delete.tcl:120
+msgid "Always (Do not perform merge checks)"
+msgstr "Alltid (utför inte sammanslagningstest)"
-#: git-gui.sh:3881
+#: lib/branch_delete.tcl:103
+#, tcl-format
+msgid "The following branches are not completely merged into %s:"
+msgstr "Följande grenar är inte till fullo sammanslagna med %s:"
+
+#: lib/branch_delete.tcl:115 lib/remote_branch_delete.tcl:218
msgid ""
+"Recovering deleted branches is difficult.\n"
"\n"
-"This is due to a known issue with the\n"
-"Tcl binary distributed by Cygwin."
+"Delete the selected branches?"
msgstr ""
+"Det kan vara svårt att återställa borttagna grenar.\n"
"\n"
-"Detta beror på ett känt problem med\n"
-"Tcl-binären som följer med Cygwin."
+"Ta bort de valda grenarna?"
-#: git-gui.sh:3886
+#: lib/branch_delete.tcl:131
+#, tcl-format
+msgid " - %s:"
+msgstr " - %s:"
+
+#: lib/branch_delete.tcl:141
#, tcl-format
msgid ""
-"\n"
-"\n"
-"A good replacement for %s\n"
-"is placing values for the user.name and\n"
-"user.email settings into your personal\n"
-"~/.gitconfig file.\n"
+"Failed to delete branches:\n"
+"%s"
msgstr ""
-"\n"
-"\n"
-"Du kan ersätta %s\n"
-"med att lägga in värden för inställningarna\n"
-"user.name och user.email i din personliga\n"
-"~/.gitconfig-fil.\n"
+"Kunde inte ta bort grenar:\n"
+"%s"
-#: lib/line.tcl:17
-msgid "Goto Line:"
-msgstr "Gå till rad:"
+#: lib/branch_rename.tcl:15
+#, tcl-format
+msgid "%s (%s): Rename Branch"
+msgstr "%s (%s): Byt namn på gren"
-#: lib/line.tcl:23
-msgid "Go"
-msgstr "Gå"
+#: lib/branch_rename.tcl:23
+msgid "Rename Branch"
+msgstr "Byt namn på gren"
-#: lib/console.tcl:59
-msgid "Working... please wait..."
-msgstr "Arbetar... vänta..."
+#: lib/branch_rename.tcl:28
+msgid "Rename"
+msgstr "Byt namn"
-#: lib/console.tcl:81 lib/checkout_op.tcl:146 lib/sshkey.tcl:55
-#: lib/database.tcl:30
-msgid "Close"
-msgstr "Stäng"
+#: lib/branch_rename.tcl:38
+msgid "Branch:"
+msgstr "Gren:"
-#: lib/console.tcl:186
-msgid "Success"
-msgstr "Lyckades"
+#: lib/branch_rename.tcl:46
+msgid "New Name:"
+msgstr "Nytt namn:"
-#: lib/console.tcl:200
-msgid "Error: Command Failed"
-msgstr "Fel: Kommando misslyckades"
+#: lib/branch_rename.tcl:81
+msgid "Please select a branch to rename."
+msgstr "Välj en gren att byta namn på."
+
+#: lib/branch_rename.tcl:102 lib/checkout_op.tcl:202
+#, tcl-format
+msgid "Branch '%s' already exists."
+msgstr "Grenen ”%s” finns redan."
+
+#: lib/branch_rename.tcl:123
+#, tcl-format
+msgid "Failed to rename '%s'."
+msgstr "Kunde inte byta namn på ”%s”."
+
+#: lib/browser.tcl:17
+msgid "Starting..."
+msgstr "Startar..."
+
+#: lib/browser.tcl:27
+#, tcl-format
+msgid "%s (%s): File Browser"
+msgstr "%s (%s): Filbläddrare"
+
+#: lib/browser.tcl:132 lib/browser.tcl:149
+#, tcl-format
+msgid "Loading %s..."
+msgstr "Läser %s..."
+
+#: lib/browser.tcl:193
+msgid "[Up To Parent]"
+msgstr "[Upp till förälder]"
+
+#: lib/browser.tcl:275
+#, tcl-format
+msgid "%s (%s): Browse Branch Files"
+msgstr "%s (%s): Bläddra filer på grenen"
+
+#: lib/browser.tcl:282
+msgid "Browse Branch Files"
+msgstr "Bläddra filer på grenen"
+
+#: lib/browser.tcl:288 lib/choose_repository.tcl:401
+#: lib/choose_repository.tcl:488 lib/choose_repository.tcl:497
+#: lib/choose_repository.tcl:1069
+msgid "Browse"
+msgstr "Bläddra"
#: lib/checkout_op.tcl:85
#, tcl-format
@@ -642,21 +929,21 @@ msgstr "Hämtar %s från %s"
msgid "fatal: Cannot resolve %s"
msgstr "ödesdigert: Kunde inte slå upp %s"
+#: lib/checkout_op.tcl:146 lib/console.tcl:81 lib/database.tcl:30
+#: lib/sshkey.tcl:58
+msgid "Close"
+msgstr "Stäng"
+
#: lib/checkout_op.tcl:175
#, tcl-format
msgid "Branch '%s' does not exist."
-msgstr "Grenen \"%s\" finns inte."
+msgstr "Grenen ”%s” finns inte."
#: lib/checkout_op.tcl:194
#, tcl-format
msgid "Failed to configure simplified git-pull for '%s'."
msgstr "Kunde inte konfigurera förenklad git-pull för '%s'."
-#: lib/checkout_op.tcl:202 lib/branch_rename.tcl:102
-#, tcl-format
-msgid "Branch '%s' already exists."
-msgstr "Grenen \"%s\" finns redan."
-
#: lib/checkout_op.tcl:229
#, tcl-format
msgid ""
@@ -665,7 +952,7 @@ msgid ""
"It cannot fast-forward to %s.\n"
"A merge is required."
msgstr ""
-"Grenen \"%s\" finns redan.\n"
+"Grenen ”%s” finns redan.\n"
"\n"
"Den kan inte snabbspolas till %s.\n"
"En sammanslagning krävs."
@@ -673,12 +960,12 @@ msgstr ""
#: lib/checkout_op.tcl:243
#, tcl-format
msgid "Merge strategy '%s' not supported."
-msgstr "Sammanslagningsstrategin \"%s\" stöds inte."
+msgstr "Sammanslagningsstrategin ”%s” stöds inte."
#: lib/checkout_op.tcl:262
#, tcl-format
msgid "Failed to update '%s'."
-msgstr "Misslyckades med att uppdatera \"%s\"."
+msgstr "Misslyckades med att uppdatera ”%s”."
#: lib/checkout_op.tcl:274
msgid "Staging area (index) is already locked."
@@ -703,27 +990,27 @@ msgstr ""
#: lib/checkout_op.tcl:345
#, tcl-format
msgid "Updating working directory to '%s'..."
-msgstr "Uppdaterar arbetskatalogen till \"%s\"..."
+msgstr "Uppdaterar arbetskatalogen till ”%s”..."
#: lib/checkout_op.tcl:346
msgid "files checked out"
msgstr "filer utcheckade"
-#: lib/checkout_op.tcl:376
+#: lib/checkout_op.tcl:377
#, tcl-format
msgid "Aborted checkout of '%s' (file level merging is required)."
-msgstr "Avbryter utcheckning av \"%s\" (sammanslagning på filnivå krävs)."
+msgstr "Avbryter utcheckning av ”%s” (sammanslagning på filnivå krävs)."
-#: lib/checkout_op.tcl:377
+#: lib/checkout_op.tcl:378
msgid "File level merge required."
msgstr "Sammanslagning på filnivå krävs."
-#: lib/checkout_op.tcl:381
+#: lib/checkout_op.tcl:382
#, tcl-format
msgid "Staying on branch '%s'."
-msgstr "Stannar på grenen \"%s\"."
+msgstr "Stannar på grenen ”%s”."
-#: lib/checkout_op.tcl:452
+#: lib/checkout_op.tcl:453
msgid ""
"You are no longer on a local branch.\n"
"\n"
@@ -732,47 +1019,33 @@ msgid ""
msgstr ""
"Du är inte längre på en lokal gren.\n"
"\n"
-"Om du ville vara på en gren skapar du en nu, baserad på \"Denna frånkopplade "
-"utcheckning\"."
+"Om du ville vara på en gren skapar du en nu, baserad på ”Denna frånkopplade "
+"utcheckning”."
-#: lib/checkout_op.tcl:503 lib/checkout_op.tcl:507
+#: lib/checkout_op.tcl:504 lib/checkout_op.tcl:508
#, tcl-format
msgid "Checked out '%s'."
-msgstr "Checkade ut \"%s\"."
+msgstr "Checkade ut ”%s”."
-#: lib/checkout_op.tcl:535
+#: lib/checkout_op.tcl:536
#, tcl-format
msgid "Resetting '%s' to '%s' will lose the following commits:"
-msgstr ""
-"Om du återställer \"%s\" till \"%s\" går följande incheckningar förlorade:"
+msgstr "Om du återställer ”%s” till ”%s” går följande incheckningar förlorade:"
-#: lib/checkout_op.tcl:557
+#: lib/checkout_op.tcl:558
msgid "Recovering lost commits may not be easy."
msgstr "Det kanske inte är så enkelt att återskapa förlorade incheckningar."
-#: lib/checkout_op.tcl:562
+#: lib/checkout_op.tcl:563
#, tcl-format
msgid "Reset '%s'?"
-msgstr "Återställa \"%s\"?"
+msgstr "Återställa ”%s”?"
-#: lib/checkout_op.tcl:567 lib/merge.tcl:166 lib/tools_dlg.tcl:336
+#: lib/checkout_op.tcl:568 lib/merge.tcl:170 lib/tools_dlg.tcl:336
msgid "Visualize"
msgstr "Visualisera"
-#: lib/checkout_op.tcl:571 lib/branch_create.tcl:85
-msgid "Reset"
-msgstr "Återställ"
-
-#: lib/checkout_op.tcl:579 lib/transport.tcl:141 lib/remote_add.tcl:34
-#: lib/browser.tcl:292 lib/merge.tcl:174 lib/branch_checkout.tcl:30
-#: lib/choose_font.tcl:45 lib/option.tcl:127 lib/tools_dlg.tcl:41
-#: lib/tools_dlg.tcl:202 lib/tools_dlg.tcl:345 lib/branch_rename.tcl:32
-#: lib/remote_branch_delete.tcl:43 lib/branch_create.tcl:37
-#: lib/branch_delete.tcl:34
-msgid "Cancel"
-msgstr "Avbryt"
-
-#: lib/checkout_op.tcl:635
+#: lib/checkout_op.tcl:636
#, tcl-format
msgid ""
"Failed to set current branch.\n"
@@ -789,440 +1062,577 @@ msgstr ""
"\n"
"Detta skulle inte ha hänt. %s kommer nu stängas och ge upp."
-#: lib/transport.tcl:6 lib/remote_add.tcl:132
-#, tcl-format
-msgid "fetch %s"
-msgstr "hämta %s"
+#: lib/choose_font.tcl:41
+msgid "Select"
+msgstr "Välj"
-#: lib/transport.tcl:7
-#, tcl-format
-msgid "Fetching new changes from %s"
-msgstr "Hämtar nya ändringar från %s"
+#: lib/choose_font.tcl:55
+msgid "Font Family"
+msgstr "Teckensnittsfamilj"
-#: lib/transport.tcl:18
-#, tcl-format
-msgid "remote prune %s"
-msgstr "fjärrborttagning %s"
+#: lib/choose_font.tcl:76
+msgid "Font Size"
+msgstr "Storlek"
-#: lib/transport.tcl:19
-#, tcl-format
-msgid "Pruning tracking branches deleted from %s"
-msgstr "Tar bort spårande grenar som tagits bort från %s"
+#: lib/choose_font.tcl:93
+msgid "Font Example"
+msgstr "Exempel"
-#: lib/transport.tcl:25
-msgid "fetch all remotes"
-msgstr "hämta alla fjärrarkiv"
+#: lib/choose_font.tcl:105
+msgid ""
+"This is example text.\n"
+"If you like this text, it can be your font."
+msgstr ""
+"Detta är en exempeltext.\n"
+"Om du tycker om den här texten kan den vara ditt teckensnitt."
-#: lib/transport.tcl:26
-msgid "Fetching new changes from all remotes"
-msgstr "Hämtar nya ändringar från alla fjärrarkiv"
+#: lib/choose_repository.tcl:45
+msgid "Git Gui"
+msgstr "Git Gui"
-#: lib/transport.tcl:40
-msgid "remote prune all remotes"
-msgstr "rensa alla fjärrarkiv"
+#: lib/choose_repository.tcl:104 lib/choose_repository.tcl:391
+msgid "Create New Repository"
+msgstr "Skapa nytt arkiv"
-#: lib/transport.tcl:41
-msgid "Pruning tracking branches deleted from all remotes"
-msgstr "Rensar spårande grenar som tagits bort, från alla fjärrarkiv"
+#: lib/choose_repository.tcl:110
+msgid "New..."
+msgstr "Nytt..."
-#: lib/transport.tcl:54 lib/transport.tcl:92 lib/transport.tcl:110
-#: lib/remote_add.tcl:162
-#, tcl-format
-msgid "push %s"
-msgstr "sänd %s"
+#: lib/choose_repository.tcl:117 lib/choose_repository.tcl:475
+msgid "Clone Existing Repository"
+msgstr "Klona befintligt arkiv"
-#: lib/transport.tcl:55
+#: lib/choose_repository.tcl:128
+msgid "Clone..."
+msgstr "Klona..."
+
+#: lib/choose_repository.tcl:135 lib/choose_repository.tcl:1059
+msgid "Open Existing Repository"
+msgstr "Öppna befintligt arkiv"
+
+#: lib/choose_repository.tcl:141
+msgid "Open..."
+msgstr "Öppna..."
+
+#: lib/choose_repository.tcl:154
+msgid "Recent Repositories"
+msgstr "Senaste arkiven"
+
+#: lib/choose_repository.tcl:164
+msgid "Open Recent Repository:"
+msgstr "Öppna tidigare arkiv:"
+
+#: lib/choose_repository.tcl:328 lib/choose_repository.tcl:335
+#: lib/choose_repository.tcl:342
#, tcl-format
-msgid "Pushing changes to %s"
-msgstr "Sänder ändringar till %s"
+msgid "Failed to create repository %s:"
+msgstr "Kunde inte skapa arkivet %s:"
-#: lib/transport.tcl:93
+#: lib/choose_repository.tcl:396
+msgid "Directory:"
+msgstr "Katalog:"
+
+#: lib/choose_repository.tcl:426 lib/choose_repository.tcl:552
+#: lib/choose_repository.tcl:1093
+msgid "Git Repository"
+msgstr "Gitarkiv"
+
+#: lib/choose_repository.tcl:451
#, tcl-format
-msgid "Mirroring to %s"
-msgstr "Speglar till %s"
+msgid "Directory %s already exists."
+msgstr "Katalogen %s finns redan."
-#: lib/transport.tcl:111
+#: lib/choose_repository.tcl:455
#, tcl-format
-msgid "Pushing %s %s to %s"
-msgstr "Sänder %s %s till %s"
+msgid "File %s already exists."
+msgstr "Filen %s finns redan."
-#: lib/transport.tcl:132
-msgid "Push Branches"
-msgstr "Sänd grenar"
+#: lib/choose_repository.tcl:470
+msgid "Clone"
+msgstr "Klona"
-#: lib/transport.tcl:147
-msgid "Source Branches"
-msgstr "Källgrenar"
+#: lib/choose_repository.tcl:483
+msgid "Source Location:"
+msgstr "Plats för källkod:"
-#: lib/transport.tcl:162
-msgid "Destination Repository"
-msgstr "Destinationsarkiv"
+#: lib/choose_repository.tcl:492
+msgid "Target Directory:"
+msgstr "Målkatalog:"
-#: lib/transport.tcl:165 lib/remote_branch_delete.tcl:51
-msgid "Remote:"
-msgstr "Fjärrarkiv:"
+#: lib/choose_repository.tcl:502
+msgid "Clone Type:"
+msgstr "Typ av klon:"
-#: lib/transport.tcl:187 lib/remote_branch_delete.tcl:72
-msgid "Arbitrary Location:"
-msgstr "Godtycklig plats:"
+#: lib/choose_repository.tcl:507
+msgid "Standard (Fast, Semi-Redundant, Hardlinks)"
+msgstr "Standard (snabb, semiredundant, hårda länkar)"
-#: lib/transport.tcl:205
-msgid "Transfer Options"
-msgstr "Överföringsalternativ"
+#: lib/choose_repository.tcl:512
+msgid "Full Copy (Slower, Redundant Backup)"
+msgstr "Full kopia (långsammare, redundant säkerhetskopia)"
-#: lib/transport.tcl:207
-msgid "Force overwrite existing branch (may discard changes)"
-msgstr "Tvinga överskrivning av befintlig gren (kan kasta bort ändringar)"
+#: lib/choose_repository.tcl:517
+msgid "Shared (Fastest, Not Recommended, No Backup)"
+msgstr "Delad (snabbast, rekommenderas ej, ingen säkerhetskopia)"
-#: lib/transport.tcl:211
-msgid "Use thin pack (for slow network connections)"
-msgstr "Använd tunt paket (för långsamma nätverksanslutningar)"
+#: lib/choose_repository.tcl:524
+msgid "Recursively clone submodules too"
+msgstr "Klona även rekursivt undermoduler"
-#: lib/transport.tcl:215
-msgid "Include tags"
-msgstr "Ta med taggar"
+#: lib/choose_repository.tcl:558 lib/choose_repository.tcl:605
+#: lib/choose_repository.tcl:744 lib/choose_repository.tcl:818
+#: lib/choose_repository.tcl:1099 lib/choose_repository.tcl:1107
+#, tcl-format
+msgid "Not a Git repository: %s"
+msgstr "Inte ett Gitarkiv: %s"
-#: lib/remote_add.tcl:20
-msgid "Add Remote"
-msgstr "Lägg till fjärrarkiv"
+#: lib/choose_repository.tcl:594
+msgid "Standard only available for local repository."
+msgstr "Standard är endast tillgängligt för lokala arkiv."
-#: lib/remote_add.tcl:25
-msgid "Add New Remote"
-msgstr "Lägg till nytt fjärrarkiv"
+#: lib/choose_repository.tcl:598
+msgid "Shared only available for local repository."
+msgstr "Delat är endast tillgängligt för lokala arkiv."
-#: lib/remote_add.tcl:30 lib/tools_dlg.tcl:37
-msgid "Add"
-msgstr "Lägg till"
+#: lib/choose_repository.tcl:613
+#, tcl-format
+msgid "Location %s already exists."
+msgstr "Platsen %s finns redan."
-#: lib/remote_add.tcl:39
-msgid "Remote Details"
-msgstr "Detaljer för fjärrarkiv"
+#: lib/choose_repository.tcl:624
+msgid "Failed to configure origin"
+msgstr "Kunde inte konfigurera ursprung"
-#: lib/remote_add.tcl:41 lib/tools_dlg.tcl:51 lib/branch_create.tcl:44
-msgid "Name:"
-msgstr "Namn:"
+#: lib/choose_repository.tcl:636
+msgid "Counting objects"
+msgstr "Räknar objekt"
-#: lib/remote_add.tcl:50
-msgid "Location:"
-msgstr "Plats:"
+#: lib/choose_repository.tcl:637
+msgid "buckets"
+msgstr "hinkar"
-#: lib/remote_add.tcl:60
-msgid "Further Action"
-msgstr "Ytterligare åtgärd"
+#: lib/choose_repository.tcl:657
+#, tcl-format
+msgid "Unable to copy objects/info/alternates: %s"
+msgstr "Kunde inte kopiera objekt/info/alternativ: %s"
-#: lib/remote_add.tcl:63
-msgid "Fetch Immediately"
-msgstr "Hämta omedelbart"
+#: lib/choose_repository.tcl:694
+#, tcl-format
+msgid "Nothing to clone from %s."
+msgstr "Ingenting att klona från %s."
-#: lib/remote_add.tcl:69
-msgid "Initialize Remote Repository and Push"
-msgstr "Initiera fjärrarkiv och sänd till"
+#: lib/choose_repository.tcl:696 lib/choose_repository.tcl:916
+#: lib/choose_repository.tcl:928
+msgid "The 'master' branch has not been initialized."
+msgstr "Grenen ”master” har inte initierats."
-#: lib/remote_add.tcl:75
-msgid "Do Nothing Else Now"
-msgstr "Gör ingent mer nu"
+#: lib/choose_repository.tcl:709
+msgid "Hardlinks are unavailable. Falling back to copying."
+msgstr "Hårda länkar är inte tillgängliga. Faller tillbaka på kopiering."
-#: lib/remote_add.tcl:100
-msgid "Please supply a remote name."
-msgstr "Ange ett namn för fjärrarkivet."
+#: lib/choose_repository.tcl:723
+#, tcl-format
+msgid "Cloning from %s"
+msgstr "Klonar från %s"
-#: lib/remote_add.tcl:113
+#: lib/choose_repository.tcl:754
+msgid "Copying objects"
+msgstr "Kopierar objekt"
+
+#: lib/choose_repository.tcl:755
+msgid "KiB"
+msgstr "KiB"
+
+#: lib/choose_repository.tcl:779
#, tcl-format
-msgid "'%s' is not an acceptable remote name."
-msgstr "\"%s\" kan inte användas som namn på fjärrarkivet."
+msgid "Unable to copy object: %s"
+msgstr "Kunde inte kopiera objekt: %s"
-#: lib/remote_add.tcl:124
+#: lib/choose_repository.tcl:791
+msgid "Linking objects"
+msgstr "Länkar objekt"
+
+#: lib/choose_repository.tcl:792
+msgid "objects"
+msgstr "objekt"
+
+#: lib/choose_repository.tcl:800
#, tcl-format
-msgid "Failed to add remote '%s' of location '%s'."
-msgstr "Kunde inte lägga till fjärrarkivet \"%s\" på platsen \"%s\"."
+msgid "Unable to hardlink object: %s"
+msgstr "Kunde inte hårdlänka objekt: %s"
-#: lib/remote_add.tcl:133
+#: lib/choose_repository.tcl:857
+msgid "Cannot fetch branches and objects. See console output for details."
+msgstr "Kunde inte hämta grenar och objekt. Se konsolutdata för detaljer."
+
+#: lib/choose_repository.tcl:868
+msgid "Cannot fetch tags. See console output for details."
+msgstr "Kunde inte hämta taggar. Se konsolutdata för detaljer."
+
+#: lib/choose_repository.tcl:892
+msgid "Cannot determine HEAD. See console output for details."
+msgstr "Kunde inte avgöra HEAD. Se konsolutdata för detaljer."
+
+#: lib/choose_repository.tcl:901
#, tcl-format
-msgid "Fetching the %s"
-msgstr "Hämtar %s"
+msgid "Unable to cleanup %s"
+msgstr "Kunde inte städa upp %s"
-#: lib/remote_add.tcl:156
+#: lib/choose_repository.tcl:907
+msgid "Clone failed."
+msgstr "Kloning misslyckades."
+
+#: lib/choose_repository.tcl:914
+msgid "No default branch obtained."
+msgstr "Hämtade ingen standardgren."
+
+#: lib/choose_repository.tcl:925
#, tcl-format
-msgid "Do not know how to initialize repository at location '%s'."
-msgstr "Vet inte hur arkivet på platsen \"%s\" skall initieras."
+msgid "Cannot resolve %s as a commit."
+msgstr "Kunde inte slå upp %s till någon incheckning."
-#: lib/remote_add.tcl:163
+#: lib/choose_repository.tcl:952
+msgid "Creating working directory"
+msgstr "Skapar arbetskatalog"
+
+#: lib/choose_repository.tcl:953 lib/index.tcl:77 lib/index.tcl:146
+#: lib/index.tcl:220 lib/index.tcl:589
+msgid "files"
+msgstr "filer"
+
+#: lib/choose_repository.tcl:982
+msgid "Initial file checkout failed."
+msgstr "Inledande filutcheckning misslyckades."
+
+#: lib/choose_repository.tcl:1026
+msgid "Cloning submodules"
+msgstr "Klonar undermoduler"
+
+#: lib/choose_repository.tcl:1041
+msgid "Cannot clone submodules."
+msgstr "Kan inte klona undermoduler."
+
+#: lib/choose_repository.tcl:1064
+msgid "Repository:"
+msgstr "Arkiv:"
+
+#: lib/choose_repository.tcl:1113
#, tcl-format
-msgid "Setting up the %s (at %s)"
-msgstr "Konfigurerar %s (på %s)"
+msgid "Failed to open repository %s:"
+msgstr "Kunde inte öppna arkivet %s:"
-#: lib/browser.tcl:17
-msgid "Starting..."
-msgstr "Startar..."
+#: lib/choose_rev.tcl:52
+msgid "This Detached Checkout"
+msgstr "Denna frånkopplade utcheckning"
-#: lib/browser.tcl:27
-msgid "File Browser"
-msgstr "Filbläddrare"
+#: lib/choose_rev.tcl:60
+msgid "Revision Expression:"
+msgstr "Revisionsuttryck:"
-#: lib/browser.tcl:132 lib/browser.tcl:149
+#: lib/choose_rev.tcl:72
+msgid "Local Branch"
+msgstr "Lokal gren"
+
+#: lib/choose_rev.tcl:77
+msgid "Tracking Branch"
+msgstr "Spårande gren"
+
+#: lib/choose_rev.tcl:82 lib/choose_rev.tcl:544
+msgid "Tag"
+msgstr "Tagg"
+
+#: lib/choose_rev.tcl:321
#, tcl-format
-msgid "Loading %s..."
-msgstr "Läser %s..."
+msgid "Invalid revision: %s"
+msgstr "Ogiltig revision: %s"
-#: lib/browser.tcl:193
-msgid "[Up To Parent]"
-msgstr "[Upp till förälder]"
+#: lib/choose_rev.tcl:342
+msgid "No revision selected."
+msgstr "Ingen revision vald."
-#: lib/browser.tcl:275 lib/browser.tcl:282
-msgid "Browse Branch Files"
-msgstr "Bläddra filer på grenen"
+#: lib/choose_rev.tcl:350
+msgid "Revision expression is empty."
+msgstr "Revisionsuttrycket är tomt."
-#: lib/browser.tcl:288 lib/choose_repository.tcl:422
-#: lib/choose_repository.tcl:509 lib/choose_repository.tcl:518
-#: lib/choose_repository.tcl:1074
-msgid "Browse"
-msgstr "Bläddra"
+#: lib/choose_rev.tcl:537
+msgid "Updated"
+msgstr "Uppdaterad"
-#: lib/browser.tcl:297 lib/branch_checkout.tcl:35 lib/tools_dlg.tcl:321
-msgid "Revision"
-msgstr "Revision"
+#: lib/choose_rev.tcl:565
+msgid "URL"
+msgstr "Webbadress"
-#: lib/merge.tcl:13
+#: lib/commit.tcl:9
msgid ""
-"Cannot merge while amending.\n"
+"There is nothing to amend.\n"
"\n"
-"You must finish amending this commit before starting any type of merge.\n"
+"You are about to create the initial commit. There is no commit before this "
+"to amend.\n"
msgstr ""
-"Kan inte slå ihop vid utökning.\n"
+"Det finns ingenting att utöka.\n"
"\n"
-"Du måste göra färdig utökningen av incheckningen innan du påbörjar någon "
-"slags sammanslagning.\n"
+"Du håller på att skapa den inledande incheckningen. Det finns ingen tidigare "
+"incheckning att utöka.\n"
-#: lib/merge.tcl:27
+#: lib/commit.tcl:18
+msgid ""
+"Cannot amend while merging.\n"
+"\n"
+"You are currently in the middle of a merge that has not been fully "
+"completed. You cannot amend the prior commit unless you first abort the "
+"current merge activity.\n"
+msgstr ""
+"Kan inte utöka vid sammanslagning.\n"
+"\n"
+"Du är i mitten av en sammanslagning som inte är fullbordad. Du kan inte "
+"utöka tidigare incheckningar om du inte först avbryter den pågående "
+"sammanslagningen.\n"
+
+#: lib/commit.tcl:56
+msgid "Error loading commit data for amend:"
+msgstr "Fel vid inläsning av incheckningsdata för utökning:"
+
+#: lib/commit.tcl:83
+msgid "Unable to obtain your identity:"
+msgstr "Kunde inte hämta din identitet:"
+
+#: lib/commit.tcl:88
+msgid "Invalid GIT_COMMITTER_IDENT:"
+msgstr "Felaktig GIT_COMMITTER_IDENT:"
+
+#: lib/commit.tcl:138
+#, tcl-format
+msgid "warning: Tcl does not support encoding '%s'."
+msgstr "varning: Tcl stöder inte teckenkodningen ”%s”."
+
+#: lib/commit.tcl:158
msgid ""
"Last scanned state does not match repository state.\n"
"\n"
"Another Git program has modified this repository since the last scan. A "
-"rescan must be performed before a merge can be performed.\n"
+"rescan must be performed before another commit can be created.\n"
"\n"
"The rescan will be automatically started now.\n"
msgstr ""
"Det senaste inlästa tillståndet motsvarar inte tillståndet i arkivet.\n"
"\n"
"Ett annat Git-program har ändrat arkivet sedan senaste avsökningen. Du måste "
-"utföra en ny sökning innan du kan utföra en sammanslagning.\n"
+"utföra en ny sökning innan du kan göra en ny incheckning.\n"
"\n"
"Sökningen kommer att startas automatiskt nu.\n"
-#: lib/merge.tcl:45
+#: lib/commit.tcl:182
#, tcl-format
msgid ""
-"You are in the middle of a conflicted merge.\n"
-"\n"
-"File %s has merge conflicts.\n"
+"Unmerged files cannot be committed.\n"
"\n"
-"You must resolve them, stage the file, and commit to complete the current "
-"merge. Only then can you begin another merge.\n"
+"File %s has merge conflicts. You must resolve them and stage the file "
+"before committing.\n"
msgstr ""
-"Du är mitt i en sammanslagning med konflikter.\n"
-"\n"
-"Filen %s har sammanslagningskonflikter.\n"
+"Osammanslagna filer kan inte checkas in.\n"
"\n"
-"Du måste lösa dem, köa filen och checka in för att fullborda den aktuella "
-"sammanslagningen. När du gjort det kan du påbörja en ny sammanslagning.\n"
+"Filen %s har sammanslagningskonflikter. Du måste lösa dem och köa filen "
+"innan du checkar in den.\n"
-#: lib/merge.tcl:55
+#: lib/commit.tcl:190
#, tcl-format
msgid ""
-"You are in the middle of a change.\n"
-"\n"
-"File %s is modified.\n"
+"Unknown file state %s detected.\n"
"\n"
-"You should complete the current commit before starting a merge. Doing so "
-"will help you abort a failed merge, should the need arise.\n"
+"File %s cannot be committed by this program.\n"
msgstr ""
-"Du är mitt i en ändring.\n"
-"\n"
-"Filen %s har ändringar.\n"
+"Okänd filstatus %s upptäckt.\n"
"\n"
-"Du bör fullborda den aktuella incheckningen innan du påbörjar en "
-"sammanslagning. Om du gör det blir det enklare att avbryta en misslyckad "
-"sammanslagning, om det skulle vara nödvändigt.\n"
-
-#: lib/merge.tcl:108
-#, tcl-format
-msgid "%s of %s"
-msgstr "%s av %s"
-
-#: lib/merge.tcl:122
-#, tcl-format
-msgid "Merging %s and %s..."
-msgstr "Slår ihop %s och %s..."
-
-#: lib/merge.tcl:133
-msgid "Merge completed successfully."
-msgstr "Sammanslagningen avslutades framgångsrikt."
-
-#: lib/merge.tcl:135
-msgid "Merge failed. Conflict resolution is required."
-msgstr "Sammanslagningen misslyckades. Du måste lösa konflikterna."
-
-#: lib/merge.tcl:160
-#, tcl-format
-msgid "Merge Into %s"
-msgstr "Slå ihop i %s"
-
-#: lib/merge.tcl:179
-msgid "Revision To Merge"
-msgstr "Revisioner att slå ihop"
+"Filen %s kan inte checkas in av programmet.\n"
-#: lib/merge.tcl:214
+#: lib/commit.tcl:198
msgid ""
-"Cannot abort while amending.\n"
+"No changes to commit.\n"
"\n"
-"You must finish amending this commit.\n"
+"You must stage at least 1 file before you can commit.\n"
msgstr ""
-"Kan inte avbryta vid utökning.\n"
+"Inga ändringar att checka in.\n"
"\n"
-"Du måste göra dig färdig med att utöka incheckningen.\n"
+"Du måste köa åtminstone en fil innan du kan checka in.\n"
-#: lib/merge.tcl:224
+#: lib/commit.tcl:213
msgid ""
-"Abort merge?\n"
+"Please supply a commit message.\n"
"\n"
-"Aborting the current merge will cause *ALL* uncommitted changes to be lost.\n"
+"A good commit message has the following format:\n"
"\n"
-"Continue with aborting the current merge?"
+"- First line: Describe in one sentence what you did.\n"
+"- Second line: Blank\n"
+"- Remaining lines: Describe why this change is good.\n"
msgstr ""
-"Avbryt sammanslagning?\n"
+"Ange ett incheckningsmeddelande.\n"
"\n"
-"Om du avbryter sammanslagningen kommer *ALLA* ej incheckade ändringar att gå "
-"förlorade.\n"
+"Ett bra incheckningsmeddelande har följande format:\n"
"\n"
-"Gå vidare med att avbryta den aktuella sammanslagningen?"
+"- Första raden: Beskriv i en mening vad du gjorde.\n"
+"- Andra raden: Tom\n"
+"- Följande rader: Beskriv varför det här är en bra ändring.\n"
+
+#: lib/commit.tcl:244
+msgid "Calling pre-commit hook..."
+msgstr "Anropar kroken före incheckning (pre-commit)..."
+
+#: lib/commit.tcl:259
+msgid "Commit declined by pre-commit hook."
+msgstr "Incheckningen avvisades av kroken före incheckning (pre-commit)."
-#: lib/merge.tcl:230
+#: lib/commit.tcl:278
msgid ""
-"Reset changes?\n"
-"\n"
-"Resetting the changes will cause *ALL* uncommitted changes to be lost.\n"
-"\n"
-"Continue with resetting the current changes?"
+"You are about to commit on a detached head. This is a potentially dangerous "
+"thing to do because if you switch to another branch you will lose your "
+"changes and it can be difficult to retrieve them later from the reflog. You "
+"should probably cancel this commit and create a new branch to continue.\n"
+" \n"
+" Do you really want to proceed with your Commit?"
msgstr ""
-"Återställ ändringar?\n"
-"\n"
-"Om du återställer ändringarna kommer *ALLA* ej incheckade ändringar att gå "
-"förlorade.\n"
-"\n"
-"Gå vidare med att återställa de aktuella ändringarna?"
+"Du är på väg att checka in på ett frånkopplat huvud. Det kan potentiellt "
+"vara farligt, eftersom du kommer förlora dina ändringar om du växlar till en "
+"annan gren och det kan vara svårt att hämta dem senare från ref-loggen. Du "
+"bör troligen avbryta incheckningen och skapa en ny gren för att fortsätta.\n"
+" \n"
+" Vill du verkligen fortsätta checka in?"
-#: lib/merge.tcl:241
-msgid "Aborting"
-msgstr "Avbryter"
+#: lib/commit.tcl:299
+msgid "Calling commit-msg hook..."
+msgstr "Anropar kroken för incheckningsmeddelande (commit-msg)..."
-#: lib/merge.tcl:241
-msgid "files reset"
-msgstr "filer återställda"
+#: lib/commit.tcl:314
+msgid "Commit declined by commit-msg hook."
+msgstr "Incheckning avvisad av kroken för incheckningsmeddelande (commit-msg)."
-#: lib/merge.tcl:269
-msgid "Abort failed."
-msgstr "Misslyckades avbryta."
+#: lib/commit.tcl:327
+msgid "Committing changes..."
+msgstr "Checkar in ändringar..."
-#: lib/merge.tcl:271
-msgid "Abort completed. Ready."
-msgstr "Avbrytning fullbordad. Redo."
+#: lib/commit.tcl:344
+msgid "write-tree failed:"
+msgstr "write-tree misslyckades:"
-#: lib/tools.tcl:75
-#, tcl-format
-msgid "Running %s requires a selected file."
-msgstr "För att starta %s måste du välja en fil."
+#: lib/commit.tcl:345 lib/commit.tcl:395 lib/commit.tcl:422
+msgid "Commit failed."
+msgstr "Incheckningen misslyckades."
-#: lib/tools.tcl:91
+#: lib/commit.tcl:362
#, tcl-format
-msgid "Are you sure you want to run %1$s on file \"%2$s\"?"
-msgstr "Är du säker på att du vill starta %1$s med filen \"%2$s\"?"
+msgid "Commit %s appears to be corrupt"
+msgstr "Incheckningen %s verkar vara trasig"
-#: lib/tools.tcl:95
-#, tcl-format
-msgid "Are you sure you want to run %s?"
-msgstr "Är du säker på att du vill starta %s?"
+#: lib/commit.tcl:367
+msgid ""
+"No changes to commit.\n"
+"\n"
+"No files were modified by this commit and it was not a merge commit.\n"
+"\n"
+"A rescan will be automatically started now.\n"
+msgstr ""
+"Inga ändringar att checka in.\n"
+"\n"
+"Inga filer ändrades av incheckningen och det var inte en sammanslagning.\n"
+"\n"
+"En sökning kommer att startas automatiskt nu.\n"
-#: lib/tools.tcl:116
-#, tcl-format
-msgid "Tool: %s"
-msgstr "Verktyg: %s"
+#: lib/commit.tcl:374
+msgid "No changes to commit."
+msgstr "Inga ändringar att checka in."
-#: lib/tools.tcl:117
-#, tcl-format
-msgid "Running: %s"
-msgstr "Exekverar: %s"
+#: lib/commit.tcl:394
+msgid "commit-tree failed:"
+msgstr "commit-tree misslyckades:"
-#: lib/tools.tcl:155
-#, tcl-format
-msgid "Tool completed successfully: %s"
-msgstr "Verktyget avslutades framgångsrikt: %s"
+#: lib/commit.tcl:421
+msgid "update-ref failed:"
+msgstr "update-ref misslyckades:"
-#: lib/tools.tcl:157
+#: lib/commit.tcl:515
#, tcl-format
-msgid "Tool failed: %s"
-msgstr "Verktyget misslyckades: %s"
+msgid "Created commit %s: %s"
+msgstr "Skapade incheckningen %s: %s"
-#: lib/branch_checkout.tcl:16 lib/branch_checkout.tcl:21
-msgid "Checkout Branch"
-msgstr "Checka ut gren"
+#: lib/console.tcl:59
+msgid "Working... please wait..."
+msgstr "Arbetar... vänta..."
-#: lib/branch_checkout.tcl:26
-msgid "Checkout"
-msgstr "Checka ut"
+#: lib/console.tcl:186
+msgid "Success"
+msgstr "Lyckades"
-#: lib/branch_checkout.tcl:39 lib/option.tcl:310 lib/branch_create.tcl:69
-msgid "Options"
-msgstr "Alternativ"
+#: lib/console.tcl:200
+msgid "Error: Command Failed"
+msgstr "Fel: Kommando misslyckades"
-#: lib/branch_checkout.tcl:42 lib/branch_create.tcl:92
-msgid "Fetch Tracking Branch"
-msgstr "Hämta spårande gren"
+#: lib/database.tcl:42
+msgid "Number of loose objects"
+msgstr "Antal lösa objekt"
-#: lib/branch_checkout.tcl:47
-msgid "Detach From Local Branch"
-msgstr "Koppla bort från lokal gren"
+#: lib/database.tcl:43
+msgid "Disk space used by loose objects"
+msgstr "Diskutrymme använt av lösa objekt"
-#: lib/spellcheck.tcl:57
-msgid "Unsupported spell checker"
-msgstr "Stavningskontrollprogrammet stöds inte"
+#: lib/database.tcl:44
+msgid "Number of packed objects"
+msgstr "Antal packade objekt"
-#: lib/spellcheck.tcl:65
-msgid "Spell checking is unavailable"
-msgstr "Stavningskontroll är ej tillgänglig"
+#: lib/database.tcl:45
+msgid "Number of packs"
+msgstr "Antal paket"
-#: lib/spellcheck.tcl:68
-msgid "Invalid spell checking configuration"
-msgstr "Ogiltig inställning för stavningskontroll"
+#: lib/database.tcl:46
+msgid "Disk space used by packed objects"
+msgstr "Diskutrymme använt av packade objekt"
-#: lib/spellcheck.tcl:70
-#, tcl-format
-msgid "Reverting dictionary to %s."
-msgstr "Återställer ordlistan till %s."
+#: lib/database.tcl:47
+msgid "Packed objects waiting for pruning"
+msgstr "Packade objekt som väntar på städning"
-#: lib/spellcheck.tcl:73
-msgid "Spell checker silently failed on startup"
-msgstr "Stavningskontroll misslyckades tyst vid start"
+#: lib/database.tcl:48
+msgid "Garbage files"
+msgstr "Skräpfiler"
-#: lib/spellcheck.tcl:80
-msgid "Unrecognized spell checker"
-msgstr "Stavningskontrollprogrammet känns inte igen"
+#: lib/database.tcl:57 lib/option.tcl:182 lib/option.tcl:197 lib/option.tcl:220
+#: lib/option.tcl:282
+#, tcl-format
+msgid "%s:"
+msgstr "%s:"
-#: lib/spellcheck.tcl:186
-msgid "No Suggestions"
-msgstr "Inga förslag"
+#: lib/database.tcl:66
+#, tcl-format
+msgid "%s (%s): Database Statistics"
+msgstr "%s (%s): Databasstatistik"
-#: lib/spellcheck.tcl:388
-msgid "Unexpected EOF from spell checker"
-msgstr "Oväntat filslut från stavningskontroll"
+#: lib/database.tcl:72
+msgid "Compressing the object database"
+msgstr "Komprimerar objektdatabasen"
-#: lib/spellcheck.tcl:392
-msgid "Spell Checker Failed"
-msgstr "Stavningskontroll misslyckades"
+#: lib/database.tcl:83
+msgid "Verifying the object database with fsck-objects"
+msgstr "Verifierar objektdatabasen med fsck-objects"
-#: lib/status_bar.tcl:87
+#: lib/database.tcl:107
#, tcl-format
-msgid "%s ... %*i of %*i %s (%3i%%)"
-msgstr "%s... %*i av %*i %s (%3i%%)"
+msgid ""
+"This repository currently has approximately %i loose objects.\n"
+"\n"
+"To maintain optimal performance it is strongly recommended that you compress "
+"the database.\n"
+"\n"
+"Compress the database now?"
+msgstr ""
+"Arkivet har för närvarande omkring %i lösa objekt.\n"
+"\n"
+"För att bibehålla optimal prestanda rekommenderas det å det bestämdaste att "
+"du komprimerar databasen.\n"
+"\n"
+"Komprimera databasen nu?"
+
+#: lib/date.tcl:25
+#, tcl-format
+msgid "Invalid date from Git: %s"
+msgstr "Ogiltigt datum från Git: %s"
#: lib/diff.tcl:77
#, tcl-format
@@ -1252,7 +1662,7 @@ msgstr ""
msgid "Loading diff of %s..."
msgstr "Läser differens för %s..."
-#: lib/diff.tcl:140
+#: lib/diff.tcl:143
msgid ""
"LOCAL: deleted\n"
"REMOTE:\n"
@@ -1260,7 +1670,7 @@ msgstr ""
"LOKAL: borttagen\n"
"FJÄRR:\n"
-#: lib/diff.tcl:145
+#: lib/diff.tcl:148
msgid ""
"REMOTE: deleted\n"
"LOCAL:\n"
@@ -1268,32 +1678,32 @@ msgstr ""
"FJÄRR: borttagen\n"
"LOKAL:\n"
-#: lib/diff.tcl:152
+#: lib/diff.tcl:155
msgid "LOCAL:\n"
msgstr "LOKAL:\n"
-#: lib/diff.tcl:155
+#: lib/diff.tcl:158
msgid "REMOTE:\n"
msgstr "FJÄRR:\n"
-#: lib/diff.tcl:217 lib/diff.tcl:355
+#: lib/diff.tcl:220 lib/diff.tcl:344
#, tcl-format
msgid "Unable to display %s"
msgstr "Kan inte visa %s"
-#: lib/diff.tcl:218
+#: lib/diff.tcl:221
msgid "Error loading file:"
msgstr "Fel vid läsning av fil:"
-#: lib/diff.tcl:225
+#: lib/diff.tcl:227
msgid "Git Repository (subproject)"
msgstr "Gitarkiv (underprojekt)"
-#: lib/diff.tcl:237
+#: lib/diff.tcl:239
msgid "* Binary file (not showing content)."
msgstr "* Binärfil (visar inte innehållet)."
-#: lib/diff.tcl:242
+#: lib/diff.tcl:244
#, tcl-format
msgid ""
"* Untracked file is %d bytes.\n"
@@ -1302,7 +1712,7 @@ msgstr ""
"* Den ospårade filen är %d byte.\n"
"* Visar endast inledande %d byte.\n"
-#: lib/diff.tcl:248
+#: lib/diff.tcl:250
#, tcl-format
msgid ""
"\n"
@@ -1313,75 +1723,444 @@ msgstr ""
"* Den ospårade filen klipptes här av %s.\n"
"* För att se hela filen, använd ett externt redigeringsprogram.\n"
-#: lib/diff.tcl:356 lib/blame.tcl:1128
-msgid "Error loading diff:"
-msgstr "Fel vid inläsning av differens:"
-
-#: lib/diff.tcl:578
+#: lib/diff.tcl:583
msgid "Failed to unstage selected hunk."
msgstr "Kunde inte ta bort den valda delen från kön."
-#: lib/diff.tcl:585
+#: lib/diff.tcl:591
+msgid "Failed to revert selected hunk."
+msgstr "Kunde inte återställa den valda delen."
+
+#: lib/diff.tcl:594
msgid "Failed to stage selected hunk."
msgstr "Kunde inte lägga till den valda delen till kön."
-#: lib/diff.tcl:664
+#: lib/diff.tcl:687
msgid "Failed to unstage selected line."
msgstr "Kunde inte ta bort den valda raden från kön."
-#: lib/diff.tcl:672
+#: lib/diff.tcl:696
+msgid "Failed to revert selected line."
+msgstr "Kunde inte återställa den valda raden."
+
+#: lib/diff.tcl:700
msgid "Failed to stage selected line."
msgstr "Kunde inte lägga till den valda raden till kön."
-#: lib/remote.tcl:200
-msgid "Push to"
-msgstr "Sänd till"
+#: lib/diff.tcl:889
+msgid "Failed to undo last revert."
+msgstr "Kunde inte ångra den senaste återställningen."
-#: lib/remote.tcl:218
-msgid "Remove Remote"
-msgstr "Ta bort fjärrarkiv"
+#: lib/encoding.tcl:443
+msgid "Default"
+msgstr "Standard"
-#: lib/remote.tcl:223
-msgid "Prune from"
-msgstr "Ta bort från"
+#: lib/encoding.tcl:448
+#, tcl-format
+msgid "System (%s)"
+msgstr "Systemets (%s)"
-#: lib/remote.tcl:228
-msgid "Fetch from"
-msgstr "Hämta från"
+#: lib/encoding.tcl:459 lib/encoding.tcl:465
+msgid "Other"
+msgstr "Annan"
-#: lib/choose_font.tcl:41
-msgid "Select"
-msgstr "Välj"
+#: lib/error.tcl:20
+#, tcl-format
+msgid "%s: error"
+msgstr "%s: fel"
-#: lib/choose_font.tcl:55
-msgid "Font Family"
-msgstr "Teckensnittsfamilj"
+#: lib/error.tcl:36
+#, tcl-format
+msgid "%s: warning"
+msgstr "%s: varning"
-#: lib/choose_font.tcl:76
-msgid "Font Size"
-msgstr "Storlek"
+#: lib/error.tcl:80
+#, tcl-format
+msgid "%s hook failed:"
+msgstr "%s-krok misslyckades:"
-#: lib/choose_font.tcl:93
-msgid "Font Example"
-msgstr "Exempel"
+#: lib/error.tcl:96
+msgid "You must correct the above errors before committing."
+msgstr "Du måste rätta till felen ovan innan du checkar in."
-#: lib/choose_font.tcl:105
+#: lib/error.tcl:116
+#, tcl-format
+msgid "%s (%s): error"
+msgstr "%s (%s): fel"
+
+#: lib/index.tcl:6
+msgid "Unable to unlock the index."
+msgstr "Kunde inte låsa upp indexet."
+
+#: lib/index.tcl:30
+msgid "Index Error"
+msgstr "Indexfel"
+
+#: lib/index.tcl:32
msgid ""
-"This is example text.\n"
-"If you like this text, it can be your font."
+"Updating the Git index failed. A rescan will be automatically started to "
+"resynchronize git-gui."
msgstr ""
-"Detta är en exempeltext.\n"
-"Om du tycker om den här texten kan den vara ditt teckensnitt."
+"Misslyckades med att uppdatera Gitindexet. En omsökning kommer att startas "
+"automatiskt för att synkronisera om git-gui."
+
+#: lib/index.tcl:43
+msgid "Continue"
+msgstr "Fortsätt"
+
+#: lib/index.tcl:46
+msgid "Unlock Index"
+msgstr "Lås upp index"
+
+#: lib/index.tcl:326
+msgid "Unstaging selected files from commit"
+msgstr "Tar bort valda filer från incheckningskön"
+
+#: lib/index.tcl:330
+#, tcl-format
+msgid "Unstaging %s from commit"
+msgstr "Tar bort %s från incheckningskön"
+
+#: lib/index.tcl:369
+msgid "Ready to commit."
+msgstr "Redo att checka in."
+
+#: lib/index.tcl:378
+msgid "Adding selected files"
+msgstr "Lägger till valda filer"
+
+#: lib/index.tcl:382
+#, tcl-format
+msgid "Adding %s"
+msgstr "Lägger till %s"
+
+#: lib/index.tcl:412
+#, tcl-format
+msgid "Stage %d untracked files?"
+msgstr "Köa %d ospårade filer?"
+
+#: lib/index.tcl:420
+msgid "Adding all changed files"
+msgstr "Lägger till alla ändrade filer"
+
+#: lib/index.tcl:503
+#, tcl-format
+msgid "Revert changes in file %s?"
+msgstr "Återställ ändringarna i filen %s?"
+
+#: lib/index.tcl:508
+#, tcl-format
+msgid "Revert changes in these %i files?"
+msgstr "Återställ ändringarna i dessa %i filer?"
+
+#: lib/index.tcl:517
+msgid "Any unstaged changes will be permanently lost by the revert."
+msgstr ""
+"Alla oköade ändringar kommer permanent gå förlorade vid återställningen."
+
+#: lib/index.tcl:520 lib/index.tcl:564
+msgid "Do Nothing"
+msgstr "Gör ingenting"
+
+#: lib/index.tcl:546
+#, tcl-format
+msgid "Delete untracked file %s?"
+msgstr "Ta bort den ospårade filen %s?"
+
+#: lib/index.tcl:551
+#, tcl-format
+msgid "Delete these %i untracked files?"
+msgstr "Ta bort dessa %i ospårade filer?"
+
+#: lib/index.tcl:561
+msgid "Files will be permanently deleted."
+msgstr "Filerna kommer tas bort permanent."
+
+#: lib/index.tcl:565
+msgid "Delete Files"
+msgstr "Ta bort filer"
+
+#: lib/index.tcl:588
+msgid "Deleting"
+msgstr "Tar bort"
+
+#: lib/index.tcl:667
+msgid "Encountered errors deleting files:\n"
+msgstr "Fel uppstod vid borttagning av filer:\n"
+
+#: lib/index.tcl:676
+#, tcl-format
+msgid "None of the %d selected files could be deleted."
+msgstr "Ingen av de %d valda filerna kunde tas bort."
+
+#: lib/index.tcl:681
+#, tcl-format
+msgid "%d of the %d selected files could not be deleted."
+msgstr "%d av de %d valda filerna kunde inte tas bort."
+
+#: lib/index.tcl:728
+msgid "Reverting selected files"
+msgstr "Återställer valda filer"
+
+#: lib/index.tcl:732
+#, tcl-format
+msgid "Reverting %s"
+msgstr "Återställer %s"
+
+#: lib/line.tcl:17
+msgid "Goto Line:"
+msgstr "Gå till rad:"
+
+#: lib/line.tcl:23
+msgid "Go"
+msgstr "Gå"
+
+#: lib/merge.tcl:13
+msgid ""
+"Cannot merge while amending.\n"
+"\n"
+"You must finish amending this commit before starting any type of merge.\n"
+msgstr ""
+"Kan inte slå ihop vid utökning.\n"
+"\n"
+"Du måste göra färdig utökningen av incheckningen innan du påbörjar någon "
+"slags sammanslagning.\n"
+
+#: lib/merge.tcl:27
+msgid ""
+"Last scanned state does not match repository state.\n"
+"\n"
+"Another Git program has modified this repository since the last scan. A "
+"rescan must be performed before a merge can be performed.\n"
+"\n"
+"The rescan will be automatically started now.\n"
+msgstr ""
+"Det senaste inlästa tillståndet motsvarar inte tillståndet i arkivet.\n"
+"\n"
+"Ett annat Git-program har ändrat arkivet sedan senaste avsökningen. Du måste "
+"utföra en ny sökning innan du kan utföra en sammanslagning.\n"
+"\n"
+"Sökningen kommer att startas automatiskt nu.\n"
+
+#: lib/merge.tcl:45
+#, tcl-format
+msgid ""
+"You are in the middle of a conflicted merge.\n"
+"\n"
+"File %s has merge conflicts.\n"
+"\n"
+"You must resolve them, stage the file, and commit to complete the current "
+"merge. Only then can you begin another merge.\n"
+msgstr ""
+"Du är mitt i en sammanslagning med konflikter.\n"
+"\n"
+"Filen %s har sammanslagningskonflikter.\n"
+"\n"
+"Du måste lösa dem, köa filen och checka in för att fullborda den aktuella "
+"sammanslagningen. När du gjort det kan du påbörja en ny sammanslagning.\n"
+
+#: lib/merge.tcl:55
+#, tcl-format
+msgid ""
+"You are in the middle of a change.\n"
+"\n"
+"File %s is modified.\n"
+"\n"
+"You should complete the current commit before starting a merge. Doing so "
+"will help you abort a failed merge, should the need arise.\n"
+msgstr ""
+"Du är mitt i en ändring.\n"
+"\n"
+"Filen %s har ändringar.\n"
+"\n"
+"Du bör fullborda den aktuella incheckningen innan du påbörjar en "
+"sammanslagning. Om du gör det blir det enklare att avbryta en misslyckad "
+"sammanslagning, om det skulle vara nödvändigt.\n"
+
+#: lib/merge.tcl:108
+#, tcl-format
+msgid "%s of %s"
+msgstr "%s av %s"
+
+#: lib/merge.tcl:126
+#, tcl-format
+msgid "Merging %s and %s..."
+msgstr "Slår ihop %s och %s..."
+
+#: lib/merge.tcl:137
+msgid "Merge completed successfully."
+msgstr "Sammanslagningen avslutades framgångsrikt."
+
+#: lib/merge.tcl:139
+msgid "Merge failed. Conflict resolution is required."
+msgstr "Sammanslagningen misslyckades. Du måste lösa konflikterna."
+
+#: lib/merge.tcl:156
+#, tcl-format
+msgid "%s (%s): Merge"
+msgstr "%s (%s): Sammanslagning"
+
+#: lib/merge.tcl:164
+#, tcl-format
+msgid "Merge Into %s"
+msgstr "Slå ihop i %s"
+
+#: lib/merge.tcl:183
+msgid "Revision To Merge"
+msgstr "Revisioner att slå ihop"
+
+#: lib/merge.tcl:218
+msgid ""
+"Cannot abort while amending.\n"
+"\n"
+"You must finish amending this commit.\n"
+msgstr ""
+"Kan inte avbryta vid utökning.\n"
+"\n"
+"Du måste göra dig färdig med att utöka incheckningen.\n"
+
+#: lib/merge.tcl:228
+msgid ""
+"Abort merge?\n"
+"\n"
+"Aborting the current merge will cause *ALL* uncommitted changes to be lost.\n"
+"\n"
+"Continue with aborting the current merge?"
+msgstr ""
+"Avbryt sammanslagning?\n"
+"\n"
+"Om du avbryter sammanslagningen kommer *ALLA* ej incheckade ändringar att gå "
+"förlorade.\n"
+"\n"
+"Gå vidare med att avbryta den aktuella sammanslagningen?"
+
+#: lib/merge.tcl:234
+msgid ""
+"Reset changes?\n"
+"\n"
+"Resetting the changes will cause *ALL* uncommitted changes to be lost.\n"
+"\n"
+"Continue with resetting the current changes?"
+msgstr ""
+"Återställ ändringar?\n"
+"\n"
+"Om du återställer ändringarna kommer *ALLA* ej incheckade ändringar att gå "
+"förlorade.\n"
+"\n"
+"Gå vidare med att återställa de aktuella ändringarna?"
+
+#: lib/merge.tcl:246
+msgid "Aborting"
+msgstr "Avbryter"
+
+#: lib/merge.tcl:247
+msgid "files reset"
+msgstr "filer återställda"
+
+#: lib/merge.tcl:277
+msgid "Abort failed."
+msgstr "Misslyckades avbryta."
+
+#: lib/merge.tcl:279
+msgid "Abort completed. Ready."
+msgstr "Avbrytning fullbordad. Redo."
+
+#: lib/mergetool.tcl:8
+msgid "Force resolution to the base version?"
+msgstr "Tvinga lösning att använda basversionen?"
+
+#: lib/mergetool.tcl:9
+msgid "Force resolution to this branch?"
+msgstr "Tvinga lösning att använda den aktuella grenen?"
+
+#: lib/mergetool.tcl:10
+msgid "Force resolution to the other branch?"
+msgstr "Tvinga lösning att använda den andra grenen?"
+
+#: lib/mergetool.tcl:14
+#, tcl-format
+msgid ""
+"Note that the diff shows only conflicting changes.\n"
+"\n"
+"%s will be overwritten.\n"
+"\n"
+"This operation can be undone only by restarting the merge."
+msgstr ""
+"Observera att diffen endast visar de ändringar som står i konflikt.\n"
+"\n"
+"%s kommer att skrivas över.\n"
+"\n"
+"Du måste starta om sammanslagningen för att göra den här operationen ogjord."
+
+#: lib/mergetool.tcl:45
+#, tcl-format
+msgid "File %s seems to have unresolved conflicts, still stage?"
+msgstr "Filen %s verkar innehålla olösta konflikter. Vill du köa ändå?"
+
+#: lib/mergetool.tcl:60
+#, tcl-format
+msgid "Adding resolution for %s"
+msgstr "Lägger till lösning för %s"
+
+#: lib/mergetool.tcl:141
+msgid "Cannot resolve deletion or link conflicts using a tool"
+msgstr "Kan inte lösa borttagnings- eller länkkonflikter med ett verktyg"
+
+#: lib/mergetool.tcl:146
+msgid "Conflict file does not exist"
+msgstr "Konfliktfil existerar inte"
+
+#: lib/mergetool.tcl:246
+#, tcl-format
+msgid "Not a GUI merge tool: '%s'"
+msgstr "Inte ett grafiskt verktyg för sammanslagning: %s"
+
+#: lib/mergetool.tcl:275
+#, tcl-format
+msgid "Unsupported merge tool '%s'"
+msgstr "Verktyget ”%s” för sammanslagning stöds inte"
+
+#: lib/mergetool.tcl:310
+msgid "Merge tool is already running, terminate it?"
+msgstr "Verktyget för sammanslagning körs redan. Vill du avsluta det?"
+
+#: lib/mergetool.tcl:330
+#, tcl-format
+msgid ""
+"Error retrieving versions:\n"
+"%s"
+msgstr ""
+"Fel vid hämtning av versioner:\n"
+"%s"
+
+#: lib/mergetool.tcl:350
+#, tcl-format
+msgid ""
+"Could not start the merge tool:\n"
+"\n"
+"%s"
+msgstr ""
+"Kunde inte starta verktyg för sammanslagning:\n"
+"\n"
+"%s"
+
+#: lib/mergetool.tcl:354
+msgid "Running merge tool..."
+msgstr "Kör verktyg för sammanslagning..."
+
+#: lib/mergetool.tcl:382 lib/mergetool.tcl:390
+msgid "Merge tool failed."
+msgstr "Verktyget för sammanslagning misslyckades."
#: lib/option.tcl:11
#, tcl-format
msgid "Invalid global encoding '%s'"
-msgstr "Den globala teckenkodningen \"%s\" är ogiltig"
+msgstr "Den globala teckenkodningen ”%s” är ogiltig"
#: lib/option.tcl:19
#, tcl-format
msgid "Invalid repo encoding '%s'"
-msgstr "Arkivets teckenkodning \"%s\" är ogiltig"
+msgstr "Arkivets teckenkodning ”%s” är ogiltig"
#: lib/option.tcl:119
msgid "Restore Defaults"
@@ -1521,239 +2300,89 @@ msgstr "Inställningar"
msgid "Failed to completely save options:"
msgstr "Misslyckades med att helt spara alternativ:"
-#: lib/mergetool.tcl:8
-msgid "Force resolution to the base version?"
-msgstr "Tvinga lösning att använda basversionen?"
-
-#: lib/mergetool.tcl:9
-msgid "Force resolution to this branch?"
-msgstr "Tvinga lösning att använda den aktuella grenen?"
+#: lib/remote_add.tcl:20
+#, tcl-format
+msgid "%s (%s): Add Remote"
+msgstr "%s (%s): Lägg till fjärrarkiv"
-#: lib/mergetool.tcl:10
-msgid "Force resolution to the other branch?"
-msgstr "Tvinga lösning att använda den andra grenen?"
+#: lib/remote_add.tcl:25
+msgid "Add New Remote"
+msgstr "Lägg till nytt fjärrarkiv"
-#: lib/mergetool.tcl:14
-#, tcl-format
-msgid ""
-"Note that the diff shows only conflicting changes.\n"
-"\n"
-"%s will be overwritten.\n"
-"\n"
-"This operation can be undone only by restarting the merge."
-msgstr ""
-"Observera att diffen endast visar de ändringar som står i konflikt.\n"
-"\n"
-"%s kommer att skrivas över.\n"
-"\n"
-"Du måste starta om sammanslagningen för att göra den här operationen ogjord."
+#: lib/remote_add.tcl:30 lib/tools_dlg.tcl:37
+msgid "Add"
+msgstr "Lägg till"
-#: lib/mergetool.tcl:45
-#, tcl-format
-msgid "File %s seems to have unresolved conflicts, still stage?"
-msgstr "Filen %s verkar innehålla olösta konflikter. Vill du köa ändå?"
+#: lib/remote_add.tcl:39
+msgid "Remote Details"
+msgstr "Detaljer för fjärrarkiv"
-#: lib/mergetool.tcl:60
-#, tcl-format
-msgid "Adding resolution for %s"
-msgstr "Lägger till lösning för %s"
+#: lib/remote_add.tcl:50
+msgid "Location:"
+msgstr "Plats:"
-#: lib/mergetool.tcl:141
-msgid "Cannot resolve deletion or link conflicts using a tool"
-msgstr "Kan inte lösa borttagnings- eller länkkonflikter med ett verktyg"
+#: lib/remote_add.tcl:60
+msgid "Further Action"
+msgstr "Ytterligare åtgärd"
-#: lib/mergetool.tcl:146
-msgid "Conflict file does not exist"
-msgstr "Konfliktfil existerar inte"
+#: lib/remote_add.tcl:63
+msgid "Fetch Immediately"
+msgstr "Hämta omedelbart"
-#: lib/mergetool.tcl:246
-#, tcl-format
-msgid "Not a GUI merge tool: '%s'"
-msgstr "Inte ett grafiskt verktyg för sammanslagning: %s"
+#: lib/remote_add.tcl:69
+msgid "Initialize Remote Repository and Push"
+msgstr "Initiera fjärrarkiv och sänd till"
-#: lib/mergetool.tcl:275
-#, tcl-format
-msgid "Unsupported merge tool '%s'"
-msgstr "Verktyget \"%s\" för sammanslagning stöds inte"
+#: lib/remote_add.tcl:75
+msgid "Do Nothing Else Now"
+msgstr "Gör ingent mer nu"
-#: lib/mergetool.tcl:310
-msgid "Merge tool is already running, terminate it?"
-msgstr "Verktyget för sammanslagning körs redan. Vill du avsluta det?"
+#: lib/remote_add.tcl:100
+msgid "Please supply a remote name."
+msgstr "Ange ett namn för fjärrarkivet."
-#: lib/mergetool.tcl:330
+#: lib/remote_add.tcl:113
#, tcl-format
-msgid ""
-"Error retrieving versions:\n"
-"%s"
-msgstr ""
-"Fel vid hämtning av versioner:\n"
-"%s"
+msgid "'%s' is not an acceptable remote name."
+msgstr "”%s” kan inte användas som namn på fjärrarkivet."
-#: lib/mergetool.tcl:350
+#: lib/remote_add.tcl:124
#, tcl-format
-msgid ""
-"Could not start the merge tool:\n"
-"\n"
-"%s"
-msgstr ""
-"Kunde inte starta verktyg för sammanslagning:\n"
-"\n"
-"%s"
-
-#: lib/mergetool.tcl:354
-msgid "Running merge tool..."
-msgstr "Kör verktyg för sammanslagning..."
-
-#: lib/mergetool.tcl:382 lib/mergetool.tcl:390
-msgid "Merge tool failed."
-msgstr "Verktyget för sammanslagning misslyckades."
-
-#: lib/tools_dlg.tcl:22
-msgid "Add Tool"
-msgstr "Lägg till verktyg"
-
-#: lib/tools_dlg.tcl:28
-msgid "Add New Tool Command"
-msgstr "Lägg till nytt verktygskommando"
-
-#: lib/tools_dlg.tcl:34
-msgid "Add globally"
-msgstr "Lägg till globalt"
-
-#: lib/tools_dlg.tcl:46
-msgid "Tool Details"
-msgstr "Detaljer för verktyg"
-
-#: lib/tools_dlg.tcl:49
-msgid "Use '/' separators to create a submenu tree:"
-msgstr "Använd \"/\"-avdelare för att skapa ett undermenyträd:"
-
-#: lib/tools_dlg.tcl:60
-msgid "Command:"
-msgstr "Kommando:"
-
-#: lib/tools_dlg.tcl:71
-msgid "Show a dialog before running"
-msgstr "Visa dialog innan programmet startas"
-
-#: lib/tools_dlg.tcl:77
-msgid "Ask the user to select a revision (sets $REVISION)"
-msgstr "Be användaren välja en version (sätter $REVISION)"
-
-#: lib/tools_dlg.tcl:82
-msgid "Ask the user for additional arguments (sets $ARGS)"
-msgstr "Be användaren om ytterligare parametrar (sätter $ARGS)"
-
-#: lib/tools_dlg.tcl:89
-msgid "Don't show the command output window"
-msgstr "Visa inte kommandots utdatafönster"
-
-#: lib/tools_dlg.tcl:94
-msgid "Run only if a diff is selected ($FILENAME not empty)"
-msgstr "Kör endast om en diff har markerats ($FILENAME är inte tomt)"
-
-#: lib/tools_dlg.tcl:118
-msgid "Please supply a name for the tool."
-msgstr "Ange ett namn för verktyget."
+msgid "Failed to add remote '%s' of location '%s'."
+msgstr "Kunde inte lägga till fjärrarkivet ”%s” på platsen ”%s”."
-#: lib/tools_dlg.tcl:126
+#: lib/remote_add.tcl:132 lib/transport.tcl:6
#, tcl-format
-msgid "Tool '%s' already exists."
-msgstr "Verktyget \"%s\" finns redan."
+msgid "fetch %s"
+msgstr "hämta %s"
-#: lib/tools_dlg.tcl:148
+#: lib/remote_add.tcl:133
#, tcl-format
-msgid ""
-"Could not add tool:\n"
-"%s"
-msgstr ""
-"Kunde inte lägga till verktyget:\n"
-"%s"
-
-#: lib/tools_dlg.tcl:187
-msgid "Remove Tool"
-msgstr "Ta bort verktyg"
-
-#: lib/tools_dlg.tcl:193
-msgid "Remove Tool Commands"
-msgstr "Ta bort verktygskommandon"
-
-#: lib/tools_dlg.tcl:198
-msgid "Remove"
-msgstr "Ta bort"
-
-#: lib/tools_dlg.tcl:231
-msgid "(Blue denotes repository-local tools)"
-msgstr "(Blått anger verktyg lokala för arkivet)"
+msgid "Fetching the %s"
+msgstr "Hämtar %s"
-#: lib/tools_dlg.tcl:292
+#: lib/remote_add.tcl:156
#, tcl-format
-msgid "Run Command: %s"
-msgstr "Kör kommandot: %s"
-
-#: lib/tools_dlg.tcl:306
-msgid "Arguments"
-msgstr "Argument"
-
-#: lib/tools_dlg.tcl:341
-msgid "OK"
-msgstr "OK"
-
-#: lib/search.tcl:48
-msgid "Find:"
-msgstr "Sök:"
-
-#: lib/search.tcl:50
-msgid "Next"
-msgstr "Nästa"
-
-#: lib/search.tcl:51
-msgid "Prev"
-msgstr "Föreg"
-
-#: lib/search.tcl:52
-msgid "RegExp"
-msgstr "Reg.uttr."
-
-#: lib/search.tcl:54
-msgid "Case"
-msgstr "Skiftläge"
-
-#: lib/branch_rename.tcl:15 lib/branch_rename.tcl:23
-msgid "Rename Branch"
-msgstr "Byt namn på gren"
-
-#: lib/branch_rename.tcl:28
-msgid "Rename"
-msgstr "Byt namn"
-
-#: lib/branch_rename.tcl:38
-msgid "Branch:"
-msgstr "Gren:"
-
-#: lib/branch_rename.tcl:46
-msgid "New Name:"
-msgstr "Nytt namn:"
-
-#: lib/branch_rename.tcl:81
-msgid "Please select a branch to rename."
-msgstr "Välj en gren att byta namn på."
+msgid "Do not know how to initialize repository at location '%s'."
+msgstr "Vet inte hur arkivet på platsen ”%s” skall initieras."
-#: lib/branch_rename.tcl:92 lib/branch_create.tcl:154
-msgid "Please supply a branch name."
-msgstr "Ange ett namn för grenen."
+#: lib/remote_add.tcl:162 lib/transport.tcl:54 lib/transport.tcl:92
+#: lib/transport.tcl:110
+#, tcl-format
+msgid "push %s"
+msgstr "sänd %s"
-#: lib/branch_rename.tcl:112 lib/branch_create.tcl:165
+#: lib/remote_add.tcl:163
#, tcl-format
-msgid "'%s' is not an acceptable branch name."
-msgstr "\"%s\" kan inte användas som namn på grenen."
+msgid "Setting up the %s (at %s)"
+msgstr "Konfigurerar %s (på %s)"
-#: lib/branch_rename.tcl:123
+#: lib/remote_branch_delete.tcl:29
#, tcl-format
-msgid "Failed to rename '%s'."
-msgstr "Kunde inte byta namn på \"%s\"."
+msgid "%s (%s): Delete Branch Remotely"
+msgstr "%s (%s): Ta bort gren från fjärrarkiv"
-#: lib/remote_branch_delete.tcl:29 lib/remote_branch_delete.tcl:34
+#: lib/remote_branch_delete.tcl:34
msgid "Delete Branch Remotely"
msgstr "Ta bort gren från fjärrarkiv"
@@ -1761,6 +2390,14 @@ msgstr "Ta bort gren från fjärrarkiv"
msgid "From Repository"
msgstr "Från arkiv"
+#: lib/remote_branch_delete.tcl:51 lib/transport.tcl:165
+msgid "Remote:"
+msgstr "Fjärrarkiv:"
+
+#: lib/remote_branch_delete.tcl:72 lib/transport.tcl:187
+msgid "Arbitrary Location:"
+msgstr "Godtycklig plats:"
+
#: lib/remote_branch_delete.tcl:88
msgid "Branches"
msgstr "Grenar"
@@ -1773,13 +2410,9 @@ msgstr "Ta endast bort om"
msgid "Merged Into:"
msgstr "Sammanslagen i:"
-#: lib/remote_branch_delete.tcl:120 lib/branch_delete.tcl:53
-msgid "Always (Do not perform merge checks)"
-msgstr "Alltid (utför inte sammanslagningstest)"
-
#: lib/remote_branch_delete.tcl:153
msgid "A branch is required for 'Merged Into'."
-msgstr "En gren krävs för \"Sammanslagen i\"."
+msgstr "En gren krävs för ”Sammanslagen i”."
#: lib/remote_branch_delete.tcl:185
#, tcl-format
@@ -1805,16 +2438,6 @@ msgstr ""
msgid "Please select one or more branches to delete."
msgstr "Välj en eller flera grenar att ta bort."
-#: lib/remote_branch_delete.tcl:218 lib/branch_delete.tcl:115
-msgid ""
-"Recovering deleted branches is difficult.\n"
-"\n"
-"Delete the selected branches?"
-msgstr ""
-"Det kan vara svårt att återställa borttagna grenar.\n"
-"\n"
-"Ta bort de valda grenarna?"
-
#: lib/remote_branch_delete.tcl:227
#, tcl-format
msgid "Deleting branches from %s"
@@ -1829,384 +2452,122 @@ msgstr "Inget arkiv markerat."
msgid "Scanning %s..."
msgstr "Söker %s..."
-#: lib/choose_repository.tcl:33
-msgid "Git Gui"
-msgstr "Git Gui"
-
-#: lib/choose_repository.tcl:92 lib/choose_repository.tcl:412
-msgid "Create New Repository"
-msgstr "Skapa nytt arkiv"
-
-#: lib/choose_repository.tcl:98
-msgid "New..."
-msgstr "Nytt..."
-
-#: lib/choose_repository.tcl:105 lib/choose_repository.tcl:496
-msgid "Clone Existing Repository"
-msgstr "Klona befintligt arkiv"
-
-#: lib/choose_repository.tcl:116
-msgid "Clone..."
-msgstr "Klona..."
-
-#: lib/choose_repository.tcl:123 lib/choose_repository.tcl:1064
-msgid "Open Existing Repository"
-msgstr "Öppna befintligt arkiv"
-
-#: lib/choose_repository.tcl:129
-msgid "Open..."
-msgstr "Öppna..."
-
-#: lib/choose_repository.tcl:142
-msgid "Recent Repositories"
-msgstr "Senaste arkiven"
-
-#: lib/choose_repository.tcl:148
-msgid "Open Recent Repository:"
-msgstr "Öppna tidigare arkiv:"
-
-#: lib/choose_repository.tcl:316 lib/choose_repository.tcl:323
-#: lib/choose_repository.tcl:330
-#, tcl-format
-msgid "Failed to create repository %s:"
-msgstr "Kunde inte skapa arkivet %s:"
-
-#: lib/choose_repository.tcl:407 lib/branch_create.tcl:33
-msgid "Create"
-msgstr "Skapa"
-
-#: lib/choose_repository.tcl:417
-msgid "Directory:"
-msgstr "Katalog:"
-
-#: lib/choose_repository.tcl:447 lib/choose_repository.tcl:573
-#: lib/choose_repository.tcl:1098
-msgid "Git Repository"
-msgstr "Gitarkiv"
-
-#: lib/choose_repository.tcl:472
-#, tcl-format
-msgid "Directory %s already exists."
-msgstr "Katalogen %s finns redan."
-
-#: lib/choose_repository.tcl:476
-#, tcl-format
-msgid "File %s already exists."
-msgstr "Filen %s finns redan."
-
-#: lib/choose_repository.tcl:491
-msgid "Clone"
-msgstr "Klona"
-
-#: lib/choose_repository.tcl:504
-msgid "Source Location:"
-msgstr "Plats för källkod:"
-
-#: lib/choose_repository.tcl:513
-msgid "Target Directory:"
-msgstr "Målkatalog:"
-
-#: lib/choose_repository.tcl:523
-msgid "Clone Type:"
-msgstr "Typ av klon:"
-
-#: lib/choose_repository.tcl:528
-msgid "Standard (Fast, Semi-Redundant, Hardlinks)"
-msgstr "Standard (snabb, semiredundant, hårda länkar)"
-
-#: lib/choose_repository.tcl:533
-msgid "Full Copy (Slower, Redundant Backup)"
-msgstr "Full kopia (långsammare, redundant säkerhetskopia)"
-
-#: lib/choose_repository.tcl:538
-msgid "Shared (Fastest, Not Recommended, No Backup)"
-msgstr "Delad (snabbast, rekommenderas ej, ingen säkerhetskopia)"
-
-#: lib/choose_repository.tcl:545
-msgid "Recursively clone submodules too"
-msgstr "Klona även rekursivt undermoduler"
-
-#: lib/choose_repository.tcl:579 lib/choose_repository.tcl:626
-#: lib/choose_repository.tcl:772 lib/choose_repository.tcl:842
-#: lib/choose_repository.tcl:1104 lib/choose_repository.tcl:1112
-#, tcl-format
-msgid "Not a Git repository: %s"
-msgstr "Inte ett Gitarkiv: %s"
-
-#: lib/choose_repository.tcl:615
-msgid "Standard only available for local repository."
-msgstr "Standard är endast tillgängligt för lokala arkiv."
-
-#: lib/choose_repository.tcl:619
-msgid "Shared only available for local repository."
-msgstr "Delat är endast tillgängligt för lokala arkiv."
-
-#: lib/choose_repository.tcl:640
-#, tcl-format
-msgid "Location %s already exists."
-msgstr "Platsen %s finns redan."
-
-#: lib/choose_repository.tcl:651
-msgid "Failed to configure origin"
-msgstr "Kunde inte konfigurera ursprung"
-
-#: lib/choose_repository.tcl:663
-msgid "Counting objects"
-msgstr "Räknar objekt"
-
-#: lib/choose_repository.tcl:664
-msgid "buckets"
-msgstr "hinkar"
-
-#: lib/choose_repository.tcl:688
-#, tcl-format
-msgid "Unable to copy objects/info/alternates: %s"
-msgstr "Kunde inte kopiera objekt/info/alternativ: %s"
-
-#: lib/choose_repository.tcl:724
-#, tcl-format
-msgid "Nothing to clone from %s."
-msgstr "Ingenting att klona från %s."
-
-#: lib/choose_repository.tcl:726 lib/choose_repository.tcl:940
-#: lib/choose_repository.tcl:952
-msgid "The 'master' branch has not been initialized."
-msgstr "Grenen \"master\" har inte initierats."
-
-#: lib/choose_repository.tcl:739
-msgid "Hardlinks are unavailable. Falling back to copying."
-msgstr "Hårda länkar är inte tillgängliga. Faller tillbaka på kopiering."
-
-#: lib/choose_repository.tcl:751
-#, tcl-format
-msgid "Cloning from %s"
-msgstr "Klonar från %s"
-
-#: lib/choose_repository.tcl:782
-msgid "Copying objects"
-msgstr "Kopierar objekt"
-
-#: lib/choose_repository.tcl:783
-msgid "KiB"
-msgstr "KiB"
-
-#: lib/choose_repository.tcl:807
-#, tcl-format
-msgid "Unable to copy object: %s"
-msgstr "Kunde inte kopiera objekt: %s"
-
-#: lib/choose_repository.tcl:817
-msgid "Linking objects"
-msgstr "Länkar objekt"
-
-#: lib/choose_repository.tcl:818
-msgid "objects"
-msgstr "objekt"
-
-#: lib/choose_repository.tcl:826
-#, tcl-format
-msgid "Unable to hardlink object: %s"
-msgstr "Kunde inte hårdlänka objekt: %s"
-
-#: lib/choose_repository.tcl:881
-msgid "Cannot fetch branches and objects. See console output for details."
-msgstr "Kunde inte hämta grenar och objekt. Se konsolutdata för detaljer."
-
-#: lib/choose_repository.tcl:892
-msgid "Cannot fetch tags. See console output for details."
-msgstr "Kunde inte hämta taggar. Se konsolutdata för detaljer."
-
-#: lib/choose_repository.tcl:916
-msgid "Cannot determine HEAD. See console output for details."
-msgstr "Kunde inte avgöra HEAD. Se konsolutdata för detaljer."
-
-#: lib/choose_repository.tcl:925
-#, tcl-format
-msgid "Unable to cleanup %s"
-msgstr "Kunde inte städa upp %s"
-
-#: lib/choose_repository.tcl:931
-msgid "Clone failed."
-msgstr "Kloning misslyckades."
+#: lib/remote.tcl:200
+msgid "Push to"
+msgstr "Sänd till"
-#: lib/choose_repository.tcl:938
-msgid "No default branch obtained."
-msgstr "Hämtade ingen standardgren."
+#: lib/remote.tcl:218
+msgid "Remove Remote"
+msgstr "Ta bort fjärrarkiv"
-#: lib/choose_repository.tcl:949
-#, tcl-format
-msgid "Cannot resolve %s as a commit."
-msgstr "Kunde inte slå upp %s till någon incheckning."
+#: lib/remote.tcl:223
+msgid "Prune from"
+msgstr "Ta bort från"
-#: lib/choose_repository.tcl:961
-msgid "Creating working directory"
-msgstr "Skapar arbetskatalog"
+#: lib/remote.tcl:228
+msgid "Fetch from"
+msgstr "Hämta från"
-#: lib/choose_repository.tcl:962 lib/index.tcl:70 lib/index.tcl:136
-#: lib/index.tcl:207
-msgid "files"
-msgstr "filer"
+#: lib/remote.tcl:249 lib/remote.tcl:253 lib/remote.tcl:258 lib/remote.tcl:264
+msgid "All"
+msgstr "Alla"
-#: lib/choose_repository.tcl:981
-msgid "Cannot clone submodules."
-msgstr "Kan inte klona undermoduler."
+#: lib/search.tcl:48
+msgid "Find:"
+msgstr "Sök:"
-#: lib/choose_repository.tcl:990
-msgid "Cloning submodules"
-msgstr "Klonar undermoduler"
+#: lib/search.tcl:50
+msgid "Next"
+msgstr "Nästa"
-#: lib/choose_repository.tcl:1015
-msgid "Initial file checkout failed."
-msgstr "Inledande filutcheckning misslyckades."
+#: lib/search.tcl:51
+msgid "Prev"
+msgstr "Föreg"
-#: lib/choose_repository.tcl:1059
-msgid "Open"
-msgstr "Öppna"
+#: lib/search.tcl:52
+msgid "RegExp"
+msgstr "Reg.uttr."
-#: lib/choose_repository.tcl:1069
-msgid "Repository:"
-msgstr "Arkiv:"
+#: lib/search.tcl:54
+msgid "Case"
+msgstr "Skiftläge"
-#: lib/choose_repository.tcl:1118
+#: lib/shortcut.tcl:8 lib/shortcut.tcl:40 lib/shortcut.tcl:72
#, tcl-format
-msgid "Failed to open repository %s:"
-msgstr "Kunde inte öppna arkivet %s:"
-
-#: lib/about.tcl:26
-msgid "git-gui - a graphical user interface for Git."
-msgstr "git-gui - ett grafiskt användargränssnitt för Git."
-
-#: lib/blame.tcl:73
-msgid "File Viewer"
-msgstr "Filvisare"
-
-#: lib/blame.tcl:79
-msgid "Commit:"
-msgstr "Incheckning:"
-
-#: lib/blame.tcl:280
-msgid "Copy Commit"
-msgstr "Kopiera incheckning"
+msgid "%s (%s): Create Desktop Icon"
+msgstr "%s (%s): Skapa skrivbordsikon"
-#: lib/blame.tcl:284
-msgid "Find Text..."
-msgstr "Sök text..."
+#: lib/shortcut.tcl:24 lib/shortcut.tcl:62
+msgid "Cannot write shortcut:"
+msgstr "Kan inte skriva genväg:"
-#: lib/blame.tcl:288
-msgid "Goto Line..."
-msgstr "Gå till rad..."
+#: lib/shortcut.tcl:137
+msgid "Cannot write icon:"
+msgstr "Kan inte skriva ikon:"
-#: lib/blame.tcl:297
-msgid "Do Full Copy Detection"
-msgstr "Gör full kopieringsigenkänning"
+#: lib/spellcheck.tcl:57
+msgid "Unsupported spell checker"
+msgstr "Stavningskontrollprogrammet stöds inte"
-#: lib/blame.tcl:301
-msgid "Show History Context"
-msgstr "Visa historiksammanhang"
+#: lib/spellcheck.tcl:65
+msgid "Spell checking is unavailable"
+msgstr "Stavningskontroll är ej tillgänglig"
-#: lib/blame.tcl:304
-msgid "Blame Parent Commit"
-msgstr "Klandra föräldraincheckning"
+#: lib/spellcheck.tcl:68
+msgid "Invalid spell checking configuration"
+msgstr "Ogiltig inställning för stavningskontroll"
-#: lib/blame.tcl:466
+#: lib/spellcheck.tcl:70
#, tcl-format
-msgid "Reading %s..."
-msgstr "Läser %s..."
-
-#: lib/blame.tcl:594
-msgid "Loading copy/move tracking annotations..."
-msgstr "Läser annoteringar för kopiering/flyttning..."
-
-#: lib/blame.tcl:614
-msgid "lines annotated"
-msgstr "rader annoterade"
-
-#: lib/blame.tcl:806
-msgid "Loading original location annotations..."
-msgstr "Läser in annotering av originalplacering..."
-
-#: lib/blame.tcl:809
-msgid "Annotation complete."
-msgstr "Annotering fullbordad."
-
-#: lib/blame.tcl:839
-msgid "Busy"
-msgstr "Upptagen"
-
-#: lib/blame.tcl:840
-msgid "Annotation process is already running."
-msgstr "Annoteringsprocess körs redan."
-
-#: lib/blame.tcl:879
-msgid "Running thorough copy detection..."
-msgstr "Kör grundlig kopieringsigenkänning..."
-
-#: lib/blame.tcl:947
-msgid "Loading annotation..."
-msgstr "Läser in annotering..."
-
-#: lib/blame.tcl:1000
-msgid "Author:"
-msgstr "Författare:"
-
-#: lib/blame.tcl:1004
-msgid "Committer:"
-msgstr "Incheckare:"
-
-#: lib/blame.tcl:1009
-msgid "Original File:"
-msgstr "Ursprunglig fil:"
-
-#: lib/blame.tcl:1057
-msgid "Cannot find HEAD commit:"
-msgstr "Hittar inte incheckning för HEAD:"
+msgid "Reverting dictionary to %s."
+msgstr "Återställer ordlistan till %s."
-#: lib/blame.tcl:1112
-msgid "Cannot find parent commit:"
-msgstr "Hittar inte föräldraincheckning:"
+#: lib/spellcheck.tcl:73
+msgid "Spell checker silently failed on startup"
+msgstr "Stavningskontroll misslyckades tyst vid start"
-#: lib/blame.tcl:1127
-msgid "Unable to display parent"
-msgstr "Kan inte visa förälder"
+#: lib/spellcheck.tcl:80
+msgid "Unrecognized spell checker"
+msgstr "Stavningskontrollprogrammet känns inte igen"
-#: lib/blame.tcl:1269
-msgid "Originally By:"
-msgstr "Ursprungligen av:"
+#: lib/spellcheck.tcl:186
+msgid "No Suggestions"
+msgstr "Inga förslag"
-#: lib/blame.tcl:1275
-msgid "In File:"
-msgstr "I filen:"
+#: lib/spellcheck.tcl:388
+msgid "Unexpected EOF from spell checker"
+msgstr "Oväntat filslut från stavningskontroll"
-#: lib/blame.tcl:1280
-msgid "Copied Or Moved Here By:"
-msgstr "Kopierad eller flyttad hit av:"
+#: lib/spellcheck.tcl:392
+msgid "Spell Checker Failed"
+msgstr "Stavningskontroll misslyckades"
-#: lib/sshkey.tcl:31
+#: lib/sshkey.tcl:34
msgid "No keys found."
msgstr "Inga nycklar hittades."
-#: lib/sshkey.tcl:34
+#: lib/sshkey.tcl:37
#, tcl-format
msgid "Found a public key in: %s"
msgstr "Hittade öppen nyckel i: %s"
-#: lib/sshkey.tcl:40
+#: lib/sshkey.tcl:43
msgid "Generate Key"
msgstr "Skapa nyckel"
-#: lib/sshkey.tcl:58
+#: lib/sshkey.tcl:61
msgid "Copy To Clipboard"
msgstr "Kopiera till Urklipp"
-#: lib/sshkey.tcl:72
+#: lib/sshkey.tcl:75
msgid "Your OpenSSH Public Key"
msgstr "Din öppna OpenSSH-nyckel"
-#: lib/sshkey.tcl:80
+#: lib/sshkey.tcl:83
msgid "Generating..."
msgstr "Skapar..."
-#: lib/sshkey.tcl:86
+#: lib/sshkey.tcl:89
#, tcl-format
msgid ""
"Could not start ssh-keygen:\n"
@@ -2217,520 +2578,232 @@ msgstr ""
"\n"
"%s"
-#: lib/sshkey.tcl:113
+#: lib/sshkey.tcl:116
msgid "Generation failed."
msgstr "Misslyckades med att skapa."
-#: lib/sshkey.tcl:120
+#: lib/sshkey.tcl:123
msgid "Generation succeeded, but no keys found."
msgstr "Lyckades skapa nyckeln, men hittar inte någon nyckel."
-#: lib/sshkey.tcl:123
+#: lib/sshkey.tcl:126
#, tcl-format
msgid "Your key is in: %s"
msgstr "Din nyckel finns i: %s"
-#: lib/branch_create.tcl:23
-msgid "Create Branch"
-msgstr "Skapa gren"
-
-#: lib/branch_create.tcl:28
-msgid "Create New Branch"
-msgstr "Skapa ny gren"
-
-#: lib/branch_create.tcl:42
-msgid "Branch Name"
-msgstr "Namn på gren"
-
-#: lib/branch_create.tcl:57
-msgid "Match Tracking Branch Name"
-msgstr "Använd namn på spårad gren"
-
-#: lib/branch_create.tcl:66
-msgid "Starting Revision"
-msgstr "Inledande revision"
-
-#: lib/branch_create.tcl:72
-msgid "Update Existing Branch:"
-msgstr "Uppdatera befintlig gren:"
-
-#: lib/branch_create.tcl:75
-msgid "No"
-msgstr "Nej"
-
-#: lib/branch_create.tcl:80
-msgid "Fast Forward Only"
-msgstr "Endast snabbspolning"
-
-#: lib/branch_create.tcl:97
-msgid "Checkout After Creation"
-msgstr "Checka ut när skapad"
-
-#: lib/branch_create.tcl:132
-msgid "Please select a tracking branch."
-msgstr "Välj en gren att spåra."
-
-#: lib/branch_create.tcl:141
+#: lib/status_bar.tcl:263
#, tcl-format
-msgid "Tracking branch %s is not a branch in the remote repository."
-msgstr "Den spårade grenen %s är inte en gren i fjärrarkivet."
-
-#: lib/shortcut.tcl:21 lib/shortcut.tcl:62
-msgid "Cannot write shortcut:"
-msgstr "Kan inte skriva genväg:"
-
-#: lib/shortcut.tcl:137
-msgid "Cannot write icon:"
-msgstr "Kan inte skriva ikon:"
-
-#: lib/choose_rev.tcl:52
-msgid "This Detached Checkout"
-msgstr "Denna frånkopplade utcheckning"
-
-#: lib/choose_rev.tcl:60
-msgid "Revision Expression:"
-msgstr "Revisionsuttryck:"
-
-#: lib/choose_rev.tcl:72
-msgid "Local Branch"
-msgstr "Lokal gren"
-
-#: lib/choose_rev.tcl:77
-msgid "Tracking Branch"
-msgstr "Spårande gren"
-
-#: lib/choose_rev.tcl:82 lib/choose_rev.tcl:544
-msgid "Tag"
-msgstr "Tagg"
+msgid "%s ... %*i of %*i %s (%3i%%)"
+msgstr "%s... %*i av %*i %s (%3i%%)"
-#: lib/choose_rev.tcl:321
+#: lib/tools_dlg.tcl:22
#, tcl-format
-msgid "Invalid revision: %s"
-msgstr "Ogiltig revision: %s"
+msgid "%s (%s): Add Tool"
+msgstr "%s (%s): Lägg till verktyg"
-#: lib/choose_rev.tcl:342
-msgid "No revision selected."
-msgstr "Ingen revision vald."
+#: lib/tools_dlg.tcl:28
+msgid "Add New Tool Command"
+msgstr "Lägg till nytt verktygskommando"
-#: lib/choose_rev.tcl:350
-msgid "Revision expression is empty."
-msgstr "Revisionsuttrycket är tomt."
+#: lib/tools_dlg.tcl:34
+msgid "Add globally"
+msgstr "Lägg till globalt"
-#: lib/choose_rev.tcl:537
-msgid "Updated"
-msgstr "Uppdaterad"
+#: lib/tools_dlg.tcl:46
+msgid "Tool Details"
+msgstr "Detaljer för verktyg"
-#: lib/choose_rev.tcl:565
-msgid "URL"
-msgstr "Webbadress"
+#: lib/tools_dlg.tcl:49
+msgid "Use '/' separators to create a submenu tree:"
+msgstr "Använd ”/”-avdelare för att skapa ett undermenyträd:"
-#: lib/commit.tcl:9
-msgid ""
-"There is nothing to amend.\n"
-"\n"
-"You are about to create the initial commit. There is no commit before this "
-"to amend.\n"
-msgstr ""
-"Det finns ingenting att utöka.\n"
-"\n"
-"Du håller på att skapa den inledande incheckningen. Det finns ingen tidigare "
-"incheckning att utöka.\n"
+#: lib/tools_dlg.tcl:60
+msgid "Command:"
+msgstr "Kommando:"
-#: lib/commit.tcl:18
-msgid ""
-"Cannot amend while merging.\n"
-"\n"
-"You are currently in the middle of a merge that has not been fully "
-"completed. You cannot amend the prior commit unless you first abort the "
-"current merge activity.\n"
-msgstr ""
-"Kan inte utöka vid sammanslagning.\n"
-"\n"
-"Du är i mitten av en sammanslagning som inte är fullbordad. Du kan inte "
-"utöka tidigare incheckningar om du inte först avbryter den pågående "
-"sammanslagningen.\n"
+#: lib/tools_dlg.tcl:71
+msgid "Show a dialog before running"
+msgstr "Visa dialog innan programmet startas"
-#: lib/commit.tcl:48
-msgid "Error loading commit data for amend:"
-msgstr "Fel vid inläsning av incheckningsdata för utökning:"
+#: lib/tools_dlg.tcl:77
+msgid "Ask the user to select a revision (sets $REVISION)"
+msgstr "Be användaren välja en version (sätter $REVISION)"
-#: lib/commit.tcl:75
-msgid "Unable to obtain your identity:"
-msgstr "Kunde inte hämta din identitet:"
+#: lib/tools_dlg.tcl:82
+msgid "Ask the user for additional arguments (sets $ARGS)"
+msgstr "Be användaren om ytterligare parametrar (sätter $ARGS)"
-#: lib/commit.tcl:80
-msgid "Invalid GIT_COMMITTER_IDENT:"
-msgstr "Felaktig GIT_COMMITTER_IDENT:"
+#: lib/tools_dlg.tcl:89
+msgid "Don't show the command output window"
+msgstr "Visa inte kommandots utdatafönster"
-#: lib/commit.tcl:129
-#, tcl-format
-msgid "warning: Tcl does not support encoding '%s'."
-msgstr "varning: Tcl stöder inte teckenkodningen \"%s\"."
+#: lib/tools_dlg.tcl:94
+msgid "Run only if a diff is selected ($FILENAME not empty)"
+msgstr "Kör endast om en diff har markerats ($FILENAME är inte tomt)"
-#: lib/commit.tcl:149
-msgid ""
-"Last scanned state does not match repository state.\n"
-"\n"
-"Another Git program has modified this repository since the last scan. A "
-"rescan must be performed before another commit can be created.\n"
-"\n"
-"The rescan will be automatically started now.\n"
-msgstr ""
-"Det senaste inlästa tillståndet motsvarar inte tillståndet i arkivet.\n"
-"\n"
-"Ett annat Git-program har ändrat arkivet sedan senaste avsökningen. Du måste "
-"utföra en ny sökning innan du kan göra en ny incheckning.\n"
-"\n"
-"Sökningen kommer att startas automatiskt nu.\n"
+#: lib/tools_dlg.tcl:118
+msgid "Please supply a name for the tool."
+msgstr "Ange ett namn för verktyget."
-#: lib/commit.tcl:173
+#: lib/tools_dlg.tcl:126
#, tcl-format
-msgid ""
-"Unmerged files cannot be committed.\n"
-"\n"
-"File %s has merge conflicts. You must resolve them and stage the file "
-"before committing.\n"
-msgstr ""
-"Osammanslagna filer kan inte checkas in.\n"
-"\n"
-"Filen %s har sammanslagningskonflikter. Du måste lösa dem och köa filen "
-"innan du checkar in den.\n"
+msgid "Tool '%s' already exists."
+msgstr "Verktyget ”%s” finns redan."
-#: lib/commit.tcl:181
+#: lib/tools_dlg.tcl:148
#, tcl-format
msgid ""
-"Unknown file state %s detected.\n"
-"\n"
-"File %s cannot be committed by this program.\n"
-msgstr ""
-"Okänd filstatus %s upptäckt.\n"
-"\n"
-"Filen %s kan inte checkas in av programmet.\n"
-
-#: lib/commit.tcl:189
-msgid ""
-"No changes to commit.\n"
-"\n"
-"You must stage at least 1 file before you can commit.\n"
-msgstr ""
-"Inga ändringar att checka in.\n"
-"\n"
-"Du måste köa åtminstone en fil innan du kan checka in.\n"
-
-#: lib/commit.tcl:204
-msgid ""
-"Please supply a commit message.\n"
-"\n"
-"A good commit message has the following format:\n"
-"\n"
-"- First line: Describe in one sentence what you did.\n"
-"- Second line: Blank\n"
-"- Remaining lines: Describe why this change is good.\n"
-msgstr ""
-"Ange ett incheckningsmeddelande.\n"
-"\n"
-"Ett bra incheckningsmeddelande har följande format:\n"
-"\n"
-"- Första raden: Beskriv i en mening vad du gjorde.\n"
-"- Andra raden: Tom\n"
-"- Följande rader: Beskriv varför det här är en bra ändring.\n"
-
-#: lib/commit.tcl:235
-msgid "Calling pre-commit hook..."
-msgstr "Anropar kroken före incheckning (pre-commit)..."
-
-#: lib/commit.tcl:250
-msgid "Commit declined by pre-commit hook."
-msgstr "Incheckningen avvisades av kroken före incheckning (pre-commit)."
-
-#: lib/commit.tcl:269
-msgid ""
-"You are about to commit on a detached head. This is a potentially dangerous "
-"thing to do because if you switch to another branch you will lose your "
-"changes and it can be difficult to retrieve them later from the reflog. You "
-"should probably cancel this commit and create a new branch to continue.\n"
-" \n"
-" Do you really want to proceed with your Commit?"
+"Could not add tool:\n"
+"%s"
msgstr ""
-"Du är på väg att checka in på ett frånkopplat huvud. Det kan potentiellt "
-"vara farligt, eftersom du kommer förlora dina ändringar om du växlar till en "
-"annan gren och det kan vara svårt att hämta dem senare från ref-loggen. Du "
-"bör troligen avbryta incheckningen och skapa en ny gren för att fortsätta.\n"
-" \n"
-" Vill du verkligen fortsätta checka in?"
-
-#: lib/commit.tcl:290
-msgid "Calling commit-msg hook..."
-msgstr "Anropar kroken för incheckningsmeddelande (commit-msg)..."
-
-#: lib/commit.tcl:305
-msgid "Commit declined by commit-msg hook."
-msgstr "Incheckning avvisad av kroken för incheckningsmeddelande (commit-msg)."
-
-#: lib/commit.tcl:318
-msgid "Committing changes..."
-msgstr "Checkar in ändringar..."
-
-#: lib/commit.tcl:334
-msgid "write-tree failed:"
-msgstr "write-tree misslyckades:"
-
-#: lib/commit.tcl:335 lib/commit.tcl:379 lib/commit.tcl:400
-msgid "Commit failed."
-msgstr "Incheckningen misslyckades."
+"Kunde inte lägga till verktyget:\n"
+"%s"
-#: lib/commit.tcl:352
+#: lib/tools_dlg.tcl:187
#, tcl-format
-msgid "Commit %s appears to be corrupt"
-msgstr "Incheckningen %s verkar vara trasig"
+msgid "%s (%s): Remove Tool"
+msgstr "%s (%s): Ta bort verktyg"
-#: lib/commit.tcl:357
-msgid ""
-"No changes to commit.\n"
-"\n"
-"No files were modified by this commit and it was not a merge commit.\n"
-"\n"
-"A rescan will be automatically started now.\n"
-msgstr ""
-"Inga ändringar att checka in.\n"
-"\n"
-"Inga filer ändrades av incheckningen och det var inte en sammanslagning.\n"
-"\n"
-"En sökning kommer att startas automatiskt nu.\n"
-
-#: lib/commit.tcl:364
-msgid "No changes to commit."
-msgstr "Inga ändringar att checka in."
+#: lib/tools_dlg.tcl:193
+msgid "Remove Tool Commands"
+msgstr "Ta bort verktygskommandon"
-#: lib/commit.tcl:378
-msgid "commit-tree failed:"
-msgstr "commit-tree misslyckades:"
+#: lib/tools_dlg.tcl:198
+msgid "Remove"
+msgstr "Ta bort"
-#: lib/commit.tcl:399
-msgid "update-ref failed:"
-msgstr "update-ref misslyckades:"
+#: lib/tools_dlg.tcl:231
+msgid "(Blue denotes repository-local tools)"
+msgstr "(Blått anger verktyg lokala för arkivet)"
-#: lib/commit.tcl:492
+#: lib/tools_dlg.tcl:283
#, tcl-format
-msgid "Created commit %s: %s"
-msgstr "Skapade incheckningen %s: %s"
+msgid "%s (%s):"
+msgstr "%s (%s):"
-#: lib/branch_delete.tcl:16
-msgid "Delete Branch"
-msgstr "Ta bort gren"
-
-#: lib/branch_delete.tcl:21
-msgid "Delete Local Branch"
-msgstr "Ta bort lokal gren"
+#: lib/tools_dlg.tcl:292
+#, tcl-format
+msgid "Run Command: %s"
+msgstr "Kör kommandot: %s"
-#: lib/branch_delete.tcl:39
-msgid "Local Branches"
-msgstr "Lokala grenar"
+#: lib/tools_dlg.tcl:306
+msgid "Arguments"
+msgstr "Argument"
-#: lib/branch_delete.tcl:51
-msgid "Delete Only If Merged Into"
-msgstr "Ta bara bort om sammanslagen med"
+#: lib/tools_dlg.tcl:341
+msgid "OK"
+msgstr "OK"
-#: lib/branch_delete.tcl:103
+#: lib/tools.tcl:76
#, tcl-format
-msgid "The following branches are not completely merged into %s:"
-msgstr "Följande grenar är inte till fullo sammanslagna med %s:"
+msgid "Running %s requires a selected file."
+msgstr "För att starta %s måste du välja en fil."
-#: lib/branch_delete.tcl:141
+#: lib/tools.tcl:92
#, tcl-format
-msgid ""
-"Failed to delete branches:\n"
-"%s"
-msgstr ""
-"Kunde inte ta bort grenar:\n"
-"%s"
-
-#: lib/index.tcl:6
-msgid "Unable to unlock the index."
-msgstr "Kunde inte låsa upp indexet."
-
-#: lib/index.tcl:17
-msgid "Index Error"
-msgstr "Indexfel"
-
-#: lib/index.tcl:19
-msgid ""
-"Updating the Git index failed. A rescan will be automatically started to "
-"resynchronize git-gui."
-msgstr ""
-"Misslyckades med att uppdatera Gitindexet. En omsökning kommer att startas "
-"automatiskt för att synkronisera om git-gui."
-
-#: lib/index.tcl:30
-msgid "Continue"
-msgstr "Fortsätt"
-
-#: lib/index.tcl:33
-msgid "Unlock Index"
-msgstr "Lås upp index"
+msgid "Are you sure you want to run %1$s on file \"%2$s\"?"
+msgstr "Är du säker på att du vill starta %1$s med filen ”%2$s”?"
-#: lib/index.tcl:298
+#: lib/tools.tcl:96
#, tcl-format
-msgid "Unstaging %s from commit"
-msgstr "Tar bort %s för incheckningskön"
-
-#: lib/index.tcl:337
-msgid "Ready to commit."
-msgstr "Redo att checka in."
+msgid "Are you sure you want to run %s?"
+msgstr "Är du säker på att du vill starta %s?"
-#: lib/index.tcl:350
+#: lib/tools.tcl:118
#, tcl-format
-msgid "Adding %s"
-msgstr "Lägger till %s"
+msgid "Tool: %s"
+msgstr "Verktyg: %s"
-#: lib/index.tcl:380
+#: lib/tools.tcl:119
#, tcl-format
-msgid "Stage %d untracked files?"
-msgstr "Köa %d ospårade filer?"
+msgid "Running: %s"
+msgstr "Exekverar: %s"
-#: lib/index.tcl:428
+#: lib/tools.tcl:158
#, tcl-format
-msgid "Revert changes in file %s?"
-msgstr "Återställ ändringarna i filen %s?"
+msgid "Tool completed successfully: %s"
+msgstr "Verktyget avslutades framgångsrikt: %s"
-#: lib/index.tcl:430
+#: lib/tools.tcl:160
#, tcl-format
-msgid "Revert changes in these %i files?"
-msgstr "Återställ ändringarna i dessa %i filer?"
-
-#: lib/index.tcl:438
-msgid "Any unstaged changes will be permanently lost by the revert."
-msgstr ""
-"Alla oköade ändringar kommer permanent gå förlorade vid återställningen."
-
-#: lib/index.tcl:441
-msgid "Do Nothing"
-msgstr "Gör ingenting"
-
-#: lib/index.tcl:459
-msgid "Reverting selected files"
-msgstr "Återställer valda filer"
+msgid "Tool failed: %s"
+msgstr "Verktyget misslyckades: %s"
-#: lib/index.tcl:463
+#: lib/transport.tcl:7
#, tcl-format
-msgid "Reverting %s"
-msgstr "Återställer %s"
-
-#: lib/encoding.tcl:443
-msgid "Default"
-msgstr "Standard"
+msgid "Fetching new changes from %s"
+msgstr "Hämtar nya ändringar från %s"
-#: lib/encoding.tcl:448
+#: lib/transport.tcl:18
#, tcl-format
-msgid "System (%s)"
-msgstr "Systemets (%s)"
-
-#: lib/encoding.tcl:459 lib/encoding.tcl:465
-msgid "Other"
-msgstr "Annan"
+msgid "remote prune %s"
+msgstr "fjärrborttagning %s"
-#: lib/date.tcl:25
+#: lib/transport.tcl:19
#, tcl-format
-msgid "Invalid date from Git: %s"
-msgstr "Ogiltigt datum från Git: %s"
-
-#: lib/database.tcl:42
-msgid "Number of loose objects"
-msgstr "Antal lösa objekt"
-
-#: lib/database.tcl:43
-msgid "Disk space used by loose objects"
-msgstr "Diskutrymme använt av lösa objekt"
-
-#: lib/database.tcl:44
-msgid "Number of packed objects"
-msgstr "Antal packade objekt"
-
-#: lib/database.tcl:45
-msgid "Number of packs"
-msgstr "Antal paket"
-
-#: lib/database.tcl:46
-msgid "Disk space used by packed objects"
-msgstr "Diskutrymme använt av packade objekt"
+msgid "Pruning tracking branches deleted from %s"
+msgstr "Tar bort spårande grenar som tagits bort från %s"
-#: lib/database.tcl:47
-msgid "Packed objects waiting for pruning"
-msgstr "Packade objekt som väntar på städning"
+#: lib/transport.tcl:25
+msgid "fetch all remotes"
+msgstr "hämta alla fjärrarkiv"
-#: lib/database.tcl:48
-msgid "Garbage files"
-msgstr "Skräpfiler"
+#: lib/transport.tcl:26
+msgid "Fetching new changes from all remotes"
+msgstr "Hämtar nya ändringar från alla fjärrarkiv"
-#: lib/database.tcl:72
-msgid "Compressing the object database"
-msgstr "Komprimerar objektdatabasen"
+#: lib/transport.tcl:40
+msgid "remote prune all remotes"
+msgstr "rensa alla fjärrarkiv"
-#: lib/database.tcl:83
-msgid "Verifying the object database with fsck-objects"
-msgstr "Verifierar objektdatabasen med fsck-objects"
+#: lib/transport.tcl:41
+msgid "Pruning tracking branches deleted from all remotes"
+msgstr "Rensar spårande grenar som tagits bort, från alla fjärrarkiv"
-#: lib/database.tcl:107
+#: lib/transport.tcl:55
#, tcl-format
-msgid ""
-"This repository currently has approximately %i loose objects.\n"
-"\n"
-"To maintain optimal performance it is strongly recommended that you compress "
-"the database.\n"
-"\n"
-"Compress the database now?"
-msgstr ""
-"Arkivet har för närvarande omkring %i lösa objekt.\n"
-"\n"
-"För att bibehålla optimal prestanda rekommenderas det å det bestämdaste att "
-"du komprimerar databasen.\n"
-"\n"
-"Komprimera databasen nu?"
-
-#: lib/error.tcl:20 lib/error.tcl:116
-msgid "error"
-msgstr "fel"
-
-#: lib/error.tcl:36
-msgid "warning"
-msgstr "varning"
-
-#: lib/error.tcl:96
-msgid "You must correct the above errors before committing."
-msgstr "Du måste rätta till felen ovan innan du checkar in."
+msgid "Pushing changes to %s"
+msgstr "Sänder ändringar till %s"
-#~ msgid "Displaying only %s of %s files."
-#~ msgstr "Visar endast %s av %s filer."
+#: lib/transport.tcl:93
+#, tcl-format
+msgid "Mirroring to %s"
+msgstr "Speglar till %s"
-#~ msgid "Case-Sensitive"
-#~ msgstr "Skilj på VERSALER/gemener"
+#: lib/transport.tcl:111
+#, tcl-format
+msgid "Pushing %s %s to %s"
+msgstr "Sänder %s %s till %s"
-#~ msgid "Cannot use funny .git directory:"
-#~ msgstr "Kan inte använda underlig .git-katalog:"
+#: lib/transport.tcl:132
+msgid "Push Branches"
+msgstr "Sänd grenar"
-#~ msgid "Preferences..."
-#~ msgstr "Inställningar..."
+#: lib/transport.tcl:147
+msgid "Source Branches"
+msgstr "Källgrenar"
-#~ msgid "Always (Do not perform merge test.)"
-#~ msgstr "Alltid (utför inte sammanslagningstest)."
+#: lib/transport.tcl:162
+msgid "Destination Repository"
+msgstr "Destinationsarkiv"
-#~ msgid "URL:"
-#~ msgstr "Webbadress:"
+#: lib/transport.tcl:205
+msgid "Transfer Options"
+msgstr "Överföringsalternativ"
-#~ msgid "Delete Remote Branch"
-#~ msgstr "Ta bort fjärrgren"
+#: lib/transport.tcl:207
+msgid "Force overwrite existing branch (may discard changes)"
+msgstr "Tvinga överskrivning av befintlig gren (kan kasta bort ändringar)"
-#~ msgid ""
-#~ "Unable to start gitk:\n"
-#~ "\n"
-#~ "%s does not exist"
-#~ msgstr ""
-#~ "Kan inte starta gitk:\n"
-#~ "\n"
-#~ "%s finns inte"
+#: lib/transport.tcl:211
+msgid "Use thin pack (for slow network connections)"
+msgstr "Använd tunt paket (för långsamma nätverksanslutningar)"
-#~ msgid "Apple"
-#~ msgstr "Äpple"
+#: lib/transport.tcl:215
+msgid "Include tags"
+msgstr "Ta med taggar"
-#~ msgid "Not connected to aspell"
-#~ msgstr "Inte ansluten till aspell"
+#: lib/transport.tcl:229
+#, tcl-format
+msgid "%s (%s): Push"
+msgstr "%s (%s): Sänd"
diff --git a/git-p4.py b/git-p4.py
index 28ab12c72b..f1ab31d540 100755
--- a/git-p4.py
+++ b/git-p4.py
@@ -3253,17 +3253,19 @@ class P4Sync(Command, P4UserMap):
if self.stream_have_file_info:
if "depotFile" in self.stream_file:
f = self.stream_file["depotFile"]
- # force a failure in fast-import, else an empty
- # commit will be made
- self.gitStream.write("\n")
- self.gitStream.write("die-now\n")
- self.gitStream.close()
- # ignore errors, but make sure it exits first
- self.importProcess.wait()
- if f:
- die("Error from p4 print for %s: %s" % (f, err))
- else:
- die("Error from p4 print: %s" % err)
+ try:
+ # force a failure in fast-import, else an empty
+ # commit will be made
+ self.gitStream.write("\n")
+ self.gitStream.write("die-now\n")
+ self.gitStream.close()
+ # ignore errors, but make sure it exits first
+ self.importProcess.wait()
+ finally:
+ if f:
+ die("Error from p4 print for %s: %s" % (f, err))
+ else:
+ die("Error from p4 print: %s" % err)
if 'depotFile' in marshalled and self.stream_have_file_info:
# start of a new file - output the old one first
diff --git a/git-send-email.perl b/git-send-email.perl
index 821b2b3a13..72044e5ef3 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -1664,9 +1664,11 @@ EOF
$smtp->code =~ /250|200/ or die sprintf(__("Failed to send %s\n"), $subject).$smtp->message;
}
if ($quiet) {
- printf($dry_run ? __("Dry-Sent %s\n") : __("Sent %s\n"), $subject);
+ printf($dry_run ? __("Dry-Sent %s") : __("Sent %s"), $subject);
+ print "\n";
} else {
- print($dry_run ? __("Dry-OK. Log says:\n") : __("OK. Log says:\n"));
+ print($dry_run ? __("Dry-OK. Log says:") : __("OK. Log says:"));
+ print "\n";
if (!defined $sendmail_cmd && !file_name_is_absolute($smtp_server)) {
print "Server: $smtp_server\n";
print "MAIL FROM:<$raw_from>\n";
@@ -1686,10 +1688,11 @@ EOF
print $header, "\n";
if ($smtp) {
print __("Result: "), $smtp->code, ' ',
- ($smtp->message =~ /\n([^\n]+\n)$/s), "\n";
+ ($smtp->message =~ /\n([^\n]+\n)$/s);
} else {
- print __("Result: OK\n");
+ print __("Result: OK");
}
+ print "\n";
}
return 1;
@@ -1844,9 +1847,9 @@ sub pre_process_file {
$what, $_) unless $quiet;
next;
}
- push @cc, $c;
+ push @cc, $sc;
printf(__("(body) Adding cc: %s from line '%s'\n"),
- $c, $_) unless $quiet;
+ $sc, $_) unless $quiet;
}
}
close $fh;
diff --git a/git.c b/git.c
index 654d615a18..e35af9b0e5 100644
--- a/git.c
+++ b/git.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "builtin.h"
#include "config.h"
#include "environment.h"
@@ -36,9 +38,10 @@ struct cmd_struct {
const char git_usage_string[] =
N_("git [-v | --version] [-h | --help] [-C <path>] [-c <name>=<value>]\n"
" [--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]\n"
- " [-p | --paginate | -P | --no-pager] [--no-replace-objects] [--bare]\n"
- " [--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]\n"
- " [--config-env=<name>=<envvar>] <command> [<args>]");
+ " [-p | --paginate | -P | --no-pager] [--no-replace-objects] [--no-lazy-fetch]\n"
+ " [--no-optional-locks] [--no-advice] [--bare] [--git-dir=<path>]\n"
+ " [--work-tree=<path>] [--namespace=<name>] [--config-env=<name>=<envvar>]\n"
+ " <command> [<args>]");
const char git_more_info_string[] =
N_("'git help -a' and 'git help -g' list available subcommands and some\n"
@@ -337,6 +340,10 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
setenv(GIT_ATTR_SOURCE_ENVIRONMENT, cmd, 1);
if (envchanged)
*envchanged = 1;
+ } else if (!strcmp(cmd, "--no-advice")) {
+ setenv(GIT_ADVICE_ENVIRONMENT, "0", 1);
+ if (envchanged)
+ *envchanged = 1;
} else {
fprintf(stderr, _("unknown option: %s\n"), cmd);
usage(git_usage_string);
@@ -589,6 +596,7 @@ static struct cmd_struct commands[] = {
{ "rebase", cmd_rebase, RUN_SETUP | NEED_WORK_TREE },
{ "receive-pack", cmd_receive_pack },
{ "reflog", cmd_reflog, RUN_SETUP },
+ { "refs", cmd_refs, RUN_SETUP },
{ "remote", cmd_remote, RUN_SETUP },
{ "remote-ext", cmd_remote_ext, NO_PARSEOPT },
{ "remote-fd", cmd_remote_fd, NO_PARSEOPT },
diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index ccd14e0e30..b09a8d0523 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -8326,10 +8326,10 @@ XML
my %co = %{$commitlist[$i]};
my $commit = $co{'id'};
# we read 150, we always show 30 and the ones more recent than 48 hours
- if (($i >= 20) && ((time - $co{'author_epoch'}) > 48*60*60)) {
+ if (($i >= 20) && ((time - $co{'committer_epoch'}) > 48*60*60)) {
last;
}
- my %cd = parse_date($co{'author_epoch'}, $co{'author_tz'});
+ my %cd = parse_date($co{'committer_epoch'}, $co{'committer_tz'});
# get list of changed files
open my $fd, "-|", git_cmd(), "diff-tree", '-r', @diff_opts,
diff --git a/gpg-interface.c b/gpg-interface.c
index 1ff94266d2..5c824aeb25 100644
--- a/gpg-interface.c
+++ b/gpg-interface.c
@@ -27,7 +27,9 @@ static void gpg_interface_lazy_init(void)
}
static char *configured_signing_key;
-static const char *ssh_default_key_command, *ssh_allowed_signers, *ssh_revocation_file;
+static char *ssh_default_key_command;
+static char *ssh_allowed_signers;
+static char *ssh_revocation_file;
static enum signature_trust_level configured_min_trust_level = TRUST_UNDEFINED;
struct gpg_format {
@@ -725,7 +727,7 @@ static int git_gpg_config(const char *var, const char *value,
void *cb UNUSED)
{
struct gpg_format *fmt = NULL;
- char *fmtname = NULL;
+ const char *fmtname = NULL;
char *trust;
int ret;
@@ -781,7 +783,7 @@ static int git_gpg_config(const char *var, const char *value,
if (fmtname) {
fmt = get_format_by_name(fmtname);
- return git_config_string(&fmt->program, var, value);
+ return git_config_string((char **) &fmt->program, var, value);
}
return 0;
diff --git a/hash-ll.h b/hash-ll.h
deleted file mode 100644
index 2cfde63ae1..0000000000
--- a/hash-ll.h
+++ /dev/null
@@ -1,310 +0,0 @@
-#ifndef HASH_LL_H
-#define HASH_LL_H
-
-#if defined(SHA1_APPLE)
-#include <CommonCrypto/CommonDigest.h>
-#elif defined(SHA1_OPENSSL)
-# include <openssl/sha.h>
-# if defined(OPENSSL_API_LEVEL) && OPENSSL_API_LEVEL >= 3
-# define SHA1_NEEDS_CLONE_HELPER
-# include "sha1/openssl.h"
-# endif
-#elif defined(SHA1_DC)
-#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>
-# if defined(OPENSSL_API_LEVEL) && OPENSSL_API_LEVEL >= 3
-# define SHA256_NEEDS_CLONE_HELPER
-# include "sha256/openssl.h"
-# endif
-#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
-
-#ifdef platform_SHA1_Clone
-#define git_SHA1_Clone platform_SHA1_Clone
-#endif
-
-#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
-
-#ifndef SHA1_NEEDS_CLONE_HELPER
-static inline void git_SHA1_Clone(git_SHA_CTX *dst, const git_SHA_CTX *src)
-{
- memcpy(dst, src, sizeof(*dst));
-}
-#endif
-
-#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_HASH_ANY 020000
-
-#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 9f0f95e2b9..5f808caa51 100644
--- a/hash-lookup.c
+++ b/hash-lookup.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "hash.h"
#include "hash-lookup.h"
@@ -112,7 +114,8 @@ int bsearch_hash(const unsigned char *hash, const uint32_t *fanout_nbo,
while (lo < hi) {
unsigned mi = lo + (hi - lo) / 2;
- int cmp = hashcmp(table + mi * stride, hash);
+ int cmp = hashcmp(table + mi * stride, hash,
+ the_repository->hash_algo);
if (!cmp) {
if (result)
diff --git a/hash.h b/hash.h
index e064807c17..72ffbc862e 100644
--- a/hash.h
+++ b/hash.h
@@ -1,107 +1,369 @@
#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>
+# if defined(OPENSSL_API_LEVEL) && OPENSSL_API_LEVEL >= 3
+# define SHA1_NEEDS_CLONE_HELPER
+# include "sha1/openssl.h"
+# endif
+#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>
+# if defined(OPENSSL_API_LEVEL) && OPENSSL_API_LEVEL >= 3
+# define SHA256_NEEDS_CLONE_HELPER
+# include "sha256/openssl.h"
+# endif
+#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
+
+#ifdef platform_SHA1_Clone
+#define git_SHA1_Clone platform_SHA1_Clone
+#endif
-#define the_hash_algo the_repository->hash_algo
+#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
-static inline int hashcmp(const unsigned char *sha1, const unsigned char *sha2)
+#ifdef SHA1_MAX_BLOCK_SIZE
+#include "compat/sha1-chunked.h"
+#undef git_SHA1_Update
+#define git_SHA1_Update git_SHA1_Update_Chunked
+#endif
+
+#ifndef SHA1_NEEDS_CLONE_HELPER
+static inline void git_SHA1_Clone(git_SHA_CTX *dst, const git_SHA_CTX *src)
{
- return hashcmp_algop(sha1, sha2, the_hash_algo);
+ memcpy(dst, src, sizeof(*dst));
}
+#endif
-static inline int oidcmp(const struct object_id *oid1, const struct object_id *oid2)
+#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_HASH_ANY 020000
+
+#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.
+ */
+};
+
+#ifdef USE_THE_REPOSITORY_VARIABLE
+# include "repository.h"
+# define the_hash_algo the_repository->hash_algo
+#endif
+
+/* 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)
{
- const struct git_hash_algo *algop;
- if (!oid1->algo)
- algop = the_hash_algo;
- else
- algop = &hash_algos[oid1->algo];
- return hashcmp_algop(oid1->hash, oid2->hash, algop);
+ return p - hash_algos;
}
-static inline int hasheq(const unsigned char *sha1, const unsigned char *sha2)
+const struct object_id *null_oid(void);
+
+static inline int hashcmp(const unsigned char *sha1, const unsigned char *sha2, const struct git_hash_algo *algop)
{
- return hasheq_algop(sha1, sha2, the_hash_algo);
+ /*
+ * 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 oideq(const struct object_id *oid1, const struct object_id *oid2)
+static inline int hasheq(const unsigned char *sha1, const unsigned char *sha2, const struct git_hash_algo *algop)
{
- const struct git_hash_algo *algop;
- if (!oid1->algo)
- algop = the_hash_algo;
- else
- algop = &hash_algos[oid1->algo];
- return hasheq_algop(oid1->hash, oid2->hash, 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 is_null_oid(const struct object_id *oid)
+static inline void hashcpy(unsigned char *sha_dst, const unsigned char *sha_src,
+ const struct git_hash_algo *algop)
{
- return oideq(oid, null_oid());
+ memcpy(sha_dst, sha_src, algop->rawsz);
}
-static inline void hashcpy(unsigned char *sha_dst, const unsigned char *sha_src)
+static inline void hashclr(unsigned char *hash, const struct git_hash_algo *algop)
{
- memcpy(sha_dst, sha_src, the_hash_algo->rawsz);
+ memset(hash, 0, algop->rawsz);
}
-/* 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)
+static inline int oidcmp(const struct object_id *oid1, const struct object_id *oid2)
{
- size_t hashsz;
+ return memcmp(oid1->hash, oid2->hash, GIT_MAX_RAWSZ);
+}
- if (!src->algo)
- hashsz = the_hash_algo->rawsz;
- else
- hashsz = hash_algos[src->algo].rawsz;
+static inline int oideq(const struct object_id *oid1, const struct object_id *oid2)
+{
+ return !memcmp(oid1->hash, oid2->hash, GIT_MAX_RAWSZ);
+}
- memcpy(dst->hash, src->hash, hashsz);
- memset(dst->hash + hashsz, 0, GIT_MAX_RAWSZ - hashsz);
+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 void hashclr(unsigned char *hash)
+static inline void oidread(struct object_id *oid, const unsigned char *hash,
+ const struct git_hash_algo *algop)
{
- memset(hash, 0, the_hash_algo->rawsz);
+ memcpy(oid->hash, hash, algop->rawsz);
+ if (algop->rawsz < GIT_MAX_RAWSZ)
+ memset(oid->hash + algop->rawsz, 0, GIT_MAX_RAWSZ - algop->rawsz);
+ oid->algo = hash_algo_by_ptr(algop);
}
-static inline void oidclr(struct object_id *oid)
+static inline void oidclr(struct object_id *oid,
+ const struct git_hash_algo *algop)
{
memset(oid->hash, 0, GIT_MAX_RAWSZ);
- oid->algo = hash_algo_by_ptr(the_hash_algo);
+ oid->algo = hash_algo_by_ptr(algop);
}
-static inline void oidread_algop(struct object_id *oid, const unsigned char *hash, const struct git_hash_algo *algop)
+static inline struct object_id *oiddup(const struct object_id *src)
{
- memcpy(oid->hash, hash, algop->rawsz);
- oid->algo = hash_algo_by_ptr(algop);
+ struct object_id *dst = xmalloc(sizeof(struct object_id));
+ oidcpy(dst, src);
+ return dst;
}
-static inline void oidread(struct object_id *oid, const unsigned char *hash)
+static inline void oid_set_algo(struct object_id *oid, const struct git_hash_algo *algop)
{
- oidread_algop(oid, hash, the_hash_algo);
+ oid->algo = hash_algo_by_ptr(algop);
}
-static inline int is_empty_blob_sha1(const unsigned char *sha1)
+/*
+ * 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)
{
- return hasheq(sha1, the_hash_algo->empty_blob->hash);
+ /*
+ * 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;
}
-static inline int is_empty_blob_oid(const struct object_id *oid)
+static inline int is_null_oid(const struct object_id *oid)
{
- return oideq(oid, the_hash_algo->empty_blob);
+ static const unsigned char null_hash[GIT_MAX_RAWSZ];
+ return !memcmp(oid->hash, null_hash, GIT_MAX_RAWSZ);
}
-static inline int is_empty_tree_sha1(const unsigned char *sha1)
+const char *empty_tree_oid_hex(const struct git_hash_algo *algop);
+
+static inline int is_empty_blob_oid(const struct object_id *oid,
+ const struct git_hash_algo *algop)
{
- return hasheq(sha1, the_hash_algo->empty_tree->hash);
+ return oideq(oid, algop->empty_blob);
}
-static inline int is_empty_tree_oid(const struct object_id *oid)
+static inline int is_empty_tree_oid(const struct object_id *oid,
+ const struct git_hash_algo *algop)
{
- return oideq(oid, the_hash_algo->empty_tree);
+ return oideq(oid, algop->empty_tree);
}
#endif
diff --git a/help.c b/help.c
index 2dbe57b413..a6b4d3b1eb 100644
--- a/help.c
+++ b/help.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "config.h"
#include "builtin.h"
@@ -15,6 +17,10 @@
#include "prompt.h"
#include "fsmonitor-ipc.h"
+#ifndef NO_CURL
+#include "git-curl-compat.h" /* For LIBCURL_VERSION only */
+#endif
+
struct category_description {
uint32_t category;
const char *desc;
@@ -157,7 +163,7 @@ void add_cmdname(struct cmdnames *cmds, const char *name, int len)
cmds->names[cmds->cnt++] = ent;
}
-static void clean_cmdnames(struct cmdnames *cmds)
+void cmdnames_release(struct cmdnames *cmds)
{
int i;
for (i = 0; i < cmds->cnt; ++i)
@@ -359,8 +365,8 @@ void list_all_main_cmds(struct string_list *list)
for (i = 0; i < main_cmds.cnt; i++)
string_list_append(list, main_cmds.names[i]->name);
- clean_cmdnames(&main_cmds);
- clean_cmdnames(&other_cmds);
+ cmdnames_release(&main_cmds);
+ cmdnames_release(&other_cmds);
}
void list_all_other_cmds(struct string_list *list)
@@ -375,8 +381,8 @@ void list_all_other_cmds(struct string_list *list)
for (i = 0; i < other_cmds.cnt; i++)
string_list_append(list, other_cmds.names[i]->name);
- clean_cmdnames(&main_cmds);
- clean_cmdnames(&other_cmds);
+ cmdnames_release(&main_cmds);
+ cmdnames_release(&other_cmds);
}
void list_cmds_by_category(struct string_list *list,
@@ -689,7 +695,7 @@ const char *help_unknown_cmd(const char *cmd)
if (autocorrect && n == 1 && SIMILAR_ENOUGH(best_similarity)) {
const char *assumed = main_cmds.names[0]->name;
main_cmds.names[0] = NULL;
- clean_cmdnames(&main_cmds);
+ cmdnames_release(&main_cmds);
fprintf_ln(stderr,
_("WARNING: You called a Git command named '%s', "
"which does not exist."),
@@ -757,6 +763,15 @@ void get_version_info(struct strbuf *buf, int show_build_options)
if (fsmonitor_ipc__is_supported())
strbuf_addstr(buf, "feature: fsmonitor--daemon\n");
+#if defined LIBCURL_VERSION
+ strbuf_addf(buf, "libcurl: %s\n", LIBCURL_VERSION);
+#endif
+#if defined OPENSSL_VERSION_TEXT
+ strbuf_addf(buf, "OpenSSL: %s\n", OPENSSL_VERSION_TEXT);
+#endif
+#if defined ZLIB_VERSION
+ strbuf_addf(buf, "zlib: %s\n", ZLIB_VERSION);
+#endif
}
}
@@ -800,7 +815,7 @@ static int append_similar_ref(const char *refname,
if (starts_with(refname, "refs/remotes/") &&
!strcmp(branch, cb->base_ref))
string_list_append_nodup(cb->similar_refs,
- shorten_unambiguous_ref(refname, 1));
+ refs_shorten_unambiguous_ref(get_main_ref_store(the_repository), refname, 1));
return 0;
}
@@ -811,7 +826,8 @@ static struct string_list guess_refs(const char *ref)
ref_cb.base_ref = ref;
ref_cb.similar_refs = &similar_refs;
- for_each_ref(append_similar_ref, &ref_cb);
+ refs_for_each_ref(get_main_ref_store(the_repository),
+ append_similar_ref, &ref_cb);
return similar_refs;
}
diff --git a/help.h b/help.h
index af073a7a02..e716ee27ea 100644
--- a/help.h
+++ b/help.h
@@ -13,6 +13,8 @@ struct cmdnames {
} **names;
};
+void cmdnames_release(struct cmdnames *cmds);
+
static inline void mput_char(char c, unsigned int num)
{
while (num--)
diff --git a/hex.c b/hex.c
index d42262bdca..5ca78a7744 100644
--- a/hex.c
+++ b/hex.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "hash.h"
#include "hex.h"
@@ -25,8 +27,12 @@ int get_oid_hex_algop(const char *hex, struct object_id *oid,
const struct git_hash_algo *algop)
{
int ret = get_hash_hex_algop(hex, oid->hash, algop);
- if (!ret)
+ if (!ret) {
oid_set_algo(oid, algop);
+ if (algop->rawsz != GIT_MAX_RAWSZ)
+ memset(oid->hash + algop->rawsz, 0,
+ GIT_MAX_RAWSZ - algop->rawsz);
+ }
return ret;
}
diff --git a/hex.h b/hex.h
index e0b83f776f..e9ccb54065 100644
--- a/hex.h
+++ b/hex.h
@@ -1,7 +1,7 @@
#ifndef HEX_H
#define HEX_H
-#include "hash-ll.h"
+#include "hash.h"
#include "hex-ll.h"
/*
@@ -13,10 +13,6 @@
* input, so it is safe to pass this function an arbitrary
* null-terminated string.
*/
-int get_hash_hex(const char *hex, unsigned char *hash);
-int get_oid_hex(const char *hex, struct object_id *oid);
-
-/* Like get_oid_hex, but for an arbitrary hash algorithm. */
int get_oid_hex_algop(const char *hex, struct object_id *oid, const struct git_hash_algo *algop);
/*
@@ -35,7 +31,6 @@ int get_oid_hex_algop(const char *hex, struct object_id *oid, const struct git_h
char *hash_to_hex_algop_r(char *buffer, const unsigned char *hash, const struct git_hash_algo *);
char *oid_to_hex_r(char *out, const struct object_id *oid);
char *hash_to_hex_algop(const unsigned char *hash, const struct git_hash_algo *); /* static buffer result! */
-char *hash_to_hex(const unsigned char *hash); /* same static buffer */
char *oid_to_hex(const struct object_id *oid); /* same static buffer */
/*
@@ -45,13 +40,9 @@ char *oid_to_hex(const struct object_id *oid); /* same static buffer */
* other invalid character. end is only updated on success; otherwise, it is
* unmodified.
*/
-int parse_oid_hex(const char *hex, struct object_id *oid, const char **end);
-
-/* Like parse_oid_hex, but for an arbitrary hash algorithm. */
int parse_oid_hex_algop(const char *hex, struct object_id *oid, const char **end,
const struct git_hash_algo *algo);
-
/*
* These functions work like get_oid_hex and parse_oid_hex, but they will parse
* a hex value for any algorithm. The algorithm is detected based on the length
@@ -61,4 +52,19 @@ int parse_oid_hex_algop(const char *hex, struct object_id *oid, const char **end
int get_oid_hex_any(const char *hex, struct object_id *oid);
int parse_oid_hex_any(const char *hex, struct object_id *oid, const char **end);
-#endif
+#ifdef USE_THE_REPOSITORY_VARIABLE
+
+/* Like get_oid_hex_algop, but for `the_hash_algo`. */
+int get_hash_hex(const char *hex, unsigned char *hash);
+int get_oid_hex(const char *hex, struct object_id *oid);
+
+/* Like parse_oid_hex_algop, but uses `the_hash_algo`. */
+int parse_oid_hex(const char *hex, struct object_id *oid, const char **end);
+
+/*
+ * Same as `hash_to_hex_algop()`, but uses `the_hash_algo`.
+ */
+char *hash_to_hex(const unsigned char *hash);
+
+#endif /* USE_THE_REPOSITORY_VARIABLE */
+#endif /* HEX_H */
diff --git a/hook.c b/hook.c
index f6306d72b3..7e90787bca 100644
--- a/hook.c
+++ b/hook.c
@@ -7,25 +7,31 @@
#include "run-command.h"
#include "config.h"
#include "strbuf.h"
+#include "environment.h"
+#include "setup.h"
const char *find_hook(const char *name)
{
static struct strbuf path = STRBUF_INIT;
+ int found_hook;
+
strbuf_reset(&path);
strbuf_git_path(&path, "hooks/%s", name);
- if (access(path.buf, X_OK) < 0) {
+ found_hook = access(path.buf, X_OK) >= 0;
+#ifdef STRIP_EXTENSION
+ if (!found_hook) {
int err = errno;
-#ifdef STRIP_EXTENSION
strbuf_addstr(&path, STRIP_EXTENSION);
- if (access(path.buf, X_OK) >= 0)
- return path.buf;
- if (errno == EACCES)
- err = errno;
+ found_hook = access(path.buf, X_OK) >= 0;
+ if (!found_hook)
+ errno = err;
+ }
#endif
- if (err == EACCES && advice_enabled(ADVICE_IGNORED_HOOK)) {
+ if (!found_hook) {
+ if (errno == EACCES && advice_enabled(ADVICE_IGNORED_HOOK)) {
static struct string_list advise_given = STRING_LIST_INIT_DUP;
if (!string_list_lookup(&advise_given, name)) {
diff --git a/hook.h b/hook.h
index 19ab9a5806..6511525aeb 100644
--- a/hook.h
+++ b/hook.h
@@ -86,5 +86,6 @@ int run_hooks(const char *hook_name);
* argument. These things will be used as positional arguments to the
* hook. This function behaves like the old run_hook_le() API.
*/
+LAST_ARG_MUST_BE_NULL
int run_hooks_l(const char *hook_name, ...);
#endif
diff --git a/http-backend.c b/http-backend.c
index 1ed1e29d07..461424972b 100644
--- a/http-backend.c
+++ b/http-backend.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "config.h"
#include "environment.h"
@@ -559,7 +561,8 @@ static void get_info_refs(struct strbuf *hdr, char *arg UNUSED)
} else {
select_getanyfile(hdr);
- for_each_namespaced_ref(NULL, show_text_ref, &buf);
+ refs_for_each_namespaced_ref(get_main_ref_store(the_repository),
+ NULL, show_text_ref, &buf);
send_strbuf(hdr, "text/plain", &buf);
}
strbuf_release(&buf);
@@ -571,9 +574,10 @@ static int show_head_ref(const char *refname, const struct object_id *oid,
struct strbuf *buf = cb_data;
if (flag & REF_ISSYMREF) {
- const char *target = resolve_ref_unsafe(refname,
- RESOLVE_REF_READING,
- NULL, NULL);
+ const char *target = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
+ refname,
+ RESOLVE_REF_READING,
+ NULL, NULL);
if (target)
strbuf_addf(buf, "ref: %s\n", strip_namespace(target));
@@ -589,7 +593,8 @@ static void get_head(struct strbuf *hdr, char *arg UNUSED)
struct strbuf buf = STRBUF_INIT;
select_getanyfile(hdr);
- head_ref_namespaced(show_head_ref, &buf);
+ refs_head_ref_namespaced(get_main_ref_store(the_repository),
+ show_head_ref, &buf);
send_strbuf(hdr, "text/plain", &buf);
strbuf_release(&buf);
}
@@ -750,7 +755,7 @@ static int bad_request(struct strbuf *hdr, const struct service_cmd *c)
int cmd_main(int argc UNUSED, const char **argv UNUSED)
{
- char *method = getenv("REQUEST_METHOD");
+ const char *method = getenv("REQUEST_METHOD");
const char *proto_header;
char *dir;
struct service_cmd *cmd = NULL;
diff --git a/http-fetch.c b/http-fetch.c
index bec94988bb..d460bb1837 100644
--- a/http-fetch.c
+++ b/http-fetch.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "config.h"
#include "gettext.h"
@@ -127,8 +129,12 @@ int cmd_main(int argc, const char **argv)
} else if (skip_prefix(argv[arg], "--packfile=", &p)) {
const char *end;
+ if (nongit)
+ die(_("not a git repository"));
+
packfile = 1;
- if (parse_oid_hex(p, &packfile_hash, &end) || *end)
+ if (parse_oid_hex_algop(p, &packfile_hash, &end,
+ the_repository->hash_algo) || *end)
die(_("argument to --packfile must be a valid hash (got '%s')"), p);
} else if (skip_prefix(argv[arg], "--index-pack-arg=", &p)) {
strvec_push(&index_pack_args, p);
diff --git a/http-push.c b/http-push.c
index 1fe51226fd..7315a694aa 100644
--- a/http-push.c
+++ b/http-push.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "environment.h"
#include "hex.h"
@@ -1016,6 +1018,7 @@ static void remote_ls(const char *path, int flags,
/* extract hex from sharded "xx/x{38}" filename */
static int get_oid_hex_from_objpath(const char *path, struct object_id *oid)
{
+ memset(oid->hash, 0, GIT_MAX_RAWSZ);
oid->algo = hash_algo_by_ptr(the_hash_algo);
if (strlen(path) != the_hash_algo->hexsz + 1)
@@ -1552,7 +1555,7 @@ static void fetch_symref(const char *path, char **symref, struct object_id *oid)
free(url);
FREE_AND_NULL(*symref);
- oidclr(oid);
+ oidclr(oid, the_repository->hash_algo);
if (buffer.len == 0)
return;
diff --git a/http-walker.c b/http-walker.c
index b395ef1327..e417a7f51c 100644
--- a/http-walker.c
+++ b/http-walker.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "repository.h"
#include "hex.h"
@@ -152,7 +154,7 @@ static void prefetch(struct walker *walker, unsigned char *sha1)
newreq = xmalloc(sizeof(*newreq));
newreq->walker = walker;
- oidread(&newreq->oid, sha1);
+ oidread(&newreq->oid, sha1, the_repository->hash_algo);
newreq->repo = data->alt;
newreq->state = WAITING;
newreq->req = NULL;
@@ -485,7 +487,7 @@ static int fetch_object(struct walker *walker, unsigned char *hash)
list_for_each(pos, head) {
obj_req = list_entry(pos, struct object_request, node);
- if (hasheq(obj_req->oid.hash, hash))
+ if (hasheq(obj_req->oid.hash, hash, the_repository->hash_algo))
break;
}
if (!obj_req)
diff --git a/http.c b/http.c
index 3d80bd6116..623ed23489 100644
--- a/http.c
+++ b/http.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "git-curl-compat.h"
#include "hex.h"
@@ -38,11 +40,11 @@ char curl_errorstr[CURL_ERROR_SIZE];
static int curl_ssl_verify = -1;
static int curl_ssl_try;
-static const char *curl_http_version = NULL;
-static const char *ssl_cert;
-static const char *ssl_cert_type;
-static const char *ssl_cipherlist;
-static const char *ssl_version;
+static char *curl_http_version;
+static char *ssl_cert;
+static char *ssl_cert_type;
+static char *ssl_cipherlist;
+static char *ssl_version;
static struct {
const char *name;
long ssl_version;
@@ -59,23 +61,23 @@ static struct {
{ "tlsv1.3", CURL_SSLVERSION_TLSv1_3 },
#endif
};
-static const char *ssl_key;
-static const char *ssl_key_type;
-static const char *ssl_capath;
-static const char *curl_no_proxy;
+static char *ssl_key;
+static char *ssl_key_type;
+static char *ssl_capath;
+static char *curl_no_proxy;
#ifdef GIT_CURL_HAVE_CURLOPT_PINNEDPUBLICKEY
-static const char *ssl_pinnedkey;
+static char *ssl_pinnedkey;
#endif
-static const char *ssl_cainfo;
+static char *ssl_cainfo;
static long curl_low_speed_limit = -1;
static long curl_low_speed_time = -1;
static int curl_ftp_no_epsv;
-static const char *curl_http_proxy;
-static const char *http_proxy_authmethod;
+static char *curl_http_proxy;
+static char *http_proxy_authmethod;
-static const char *http_proxy_ssl_cert;
-static const char *http_proxy_ssl_key;
-static const char *http_proxy_ssl_ca_info;
+static char *http_proxy_ssl_cert;
+static char *http_proxy_ssl_key;
+static char *http_proxy_ssl_ca_info;
static struct credential proxy_cert_auth = CREDENTIAL_INIT;
static int proxy_ssl_cert_password_required;
@@ -95,7 +97,7 @@ static struct {
*/
};
#ifdef CURLGSSAPI_DELEGATION_FLAG
-static const char *curl_deleg;
+static char *curl_deleg;
static struct {
const char *name;
long curl_deleg_param;
@@ -106,13 +108,20 @@ static struct {
};
#endif
+enum proactive_auth {
+ PROACTIVE_AUTH_NONE = 0,
+ PROACTIVE_AUTH_IF_CREDENTIALS,
+ PROACTIVE_AUTH_AUTO,
+ PROACTIVE_AUTH_BASIC,
+};
+
static struct credential proxy_auth = CREDENTIAL_INIT;
static const char *curl_proxyuserpwd;
-static const char *curl_cookie_file;
+static char *curl_cookie_file;
static int curl_save_cookies;
struct credential http_auth = CREDENTIAL_INIT;
-static int http_proactive_auth;
-static const char *user_agent;
+static enum proactive_auth http_proactive_auth;
+static char *user_agent;
static int curl_empty_auth = -1;
enum http_follow_config http_follow_config = HTTP_FOLLOW_INITIAL;
@@ -128,7 +137,6 @@ static unsigned long empty_auth_useless =
| CURLAUTH_DIGEST;
static struct curl_slist *pragma_header;
-static struct curl_slist *no_pragma_header;
static struct string_list extra_http_headers = STRING_LIST_INIT_DUP;
static struct curl_slist *host_resolutions;
@@ -147,6 +155,12 @@ static int http_schannel_check_revoke = 1;
*/
static int http_schannel_use_ssl_cainfo;
+static int always_auth_proactively(void)
+{
+ return http_proactive_auth != PROACTIVE_AUTH_NONE &&
+ http_proactive_auth != PROACTIVE_AUTH_IF_CREDENTIALS;
+}
+
size_t fread_buffer(char *ptr, size_t eltsize, size_t nmemb, void *buffer_)
{
size_t size = eltsize * nmemb;
@@ -299,6 +313,11 @@ size_t fwrite_null(char *ptr UNUSED, size_t eltsize UNUSED, size_t nmemb,
return nmemb;
}
+static struct curl_slist *object_request_headers(void)
+{
+ return curl_slist_append(http_copy_default_headers(), "Pragma:");
+}
+
static void closedown_active_slot(struct active_request_slot *slot)
{
active_requests--;
@@ -533,6 +552,20 @@ static int http_options(const char *var, const char *value,
return 0;
}
+ if (!strcmp("http.proactiveauth", var)) {
+ if (!value)
+ return config_error_nonbool(var);
+ if (!strcmp(value, "auto"))
+ http_proactive_auth = PROACTIVE_AUTH_AUTO;
+ else if (!strcmp(value, "basic"))
+ http_proactive_auth = PROACTIVE_AUTH_BASIC;
+ else if (!strcmp(value, "none"))
+ http_proactive_auth = PROACTIVE_AUTH_NONE;
+ else
+ warning(_("Unknown value for http.proactiveauth"));
+ return 0;
+ }
+
/* Fall back on the default ones */
return git_default_config(var, value, ctx, data);
}
@@ -557,42 +590,78 @@ static int curl_empty_auth_enabled(void)
return 0;
}
+struct curl_slist *http_append_auth_header(const struct credential *c,
+ struct curl_slist *headers)
+{
+ if (c->authtype && c->credential) {
+ struct strbuf auth = STRBUF_INIT;
+ strbuf_addf(&auth, "Authorization: %s %s",
+ c->authtype, c->credential);
+ headers = curl_slist_append(headers, auth.buf);
+ strbuf_release(&auth);
+ }
+ return headers;
+}
+
static void init_curl_http_auth(CURL *result)
{
- if (!http_auth.username || !*http_auth.username) {
- if (curl_empty_auth_enabled())
+ if ((!http_auth.username || !*http_auth.username) &&
+ (!http_auth.credential || !*http_auth.credential)) {
+ int empty_auth = curl_empty_auth_enabled();
+ if ((empty_auth != -1 && !always_auth_proactively()) || empty_auth == 1) {
curl_easy_setopt(result, CURLOPT_USERPWD, ":");
- return;
+ return;
+ } else if (!always_auth_proactively()) {
+ return;
+ } else if (http_proactive_auth == PROACTIVE_AUTH_BASIC) {
+ strvec_push(&http_auth.wwwauth_headers, "Basic");
+ }
}
- credential_fill(&http_auth);
+ credential_fill(&http_auth, 1);
- curl_easy_setopt(result, CURLOPT_USERNAME, http_auth.username);
- curl_easy_setopt(result, CURLOPT_PASSWORD, http_auth.password);
+ if (http_auth.password) {
+ if (always_auth_proactively()) {
+ /*
+ * We got a credential without an authtype and we don't
+ * know what's available. Since our only two options at
+ * the moment are auto (which defaults to basic) and
+ * basic, use basic for now.
+ */
+ curl_easy_setopt(result, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
+ }
+ curl_easy_setopt(result, CURLOPT_USERNAME, http_auth.username);
+ curl_easy_setopt(result, CURLOPT_PASSWORD, http_auth.password);
+ }
}
/* *var must be free-able */
-static void var_override(const char **var, char *value)
+static void var_override(char **var, char *value)
{
if (value) {
- free((void *)*var);
+ free(*var);
*var = xstrdup(value);
}
}
static void set_proxyauth_name_password(CURL *result)
{
+ if (proxy_auth.password) {
curl_easy_setopt(result, CURLOPT_PROXYUSERNAME,
proxy_auth.username);
curl_easy_setopt(result, CURLOPT_PROXYPASSWORD,
proxy_auth.password);
+ } else if (proxy_auth.authtype && proxy_auth.credential) {
+ curl_easy_setopt(result, CURLOPT_PROXYHEADER,
+ http_append_auth_header(&proxy_auth, NULL));
+ }
}
static void init_curl_proxy_auth(CURL *result)
{
if (proxy_auth.username) {
- if (!proxy_auth.password)
- credential_fill(&proxy_auth);
+ if (!proxy_auth.password && !proxy_auth.credential)
+ credential_fill(&proxy_auth, 1);
set_proxyauth_name_password(result);
}
@@ -626,7 +695,7 @@ static int has_cert_password(void)
cert_auth.host = xstrdup("");
cert_auth.username = xstrdup("");
cert_auth.path = xstrdup(ssl_cert);
- credential_fill(&cert_auth);
+ credential_fill(&cert_auth, 0);
}
return 1;
}
@@ -641,7 +710,7 @@ static int has_proxy_cert_password(void)
proxy_cert_auth.host = xstrdup("");
proxy_cert_auth.username = xstrdup("");
proxy_cert_auth.path = xstrdup(http_proxy_ssl_cert);
- credential_fill(&proxy_cert_auth);
+ credential_fill(&proxy_cert_auth, 0);
}
return 1;
}
@@ -1023,7 +1092,7 @@ static CURL *get_curl_handle(void)
#endif
}
- if (http_proactive_auth)
+ if (http_proactive_auth != PROACTIVE_AUTH_NONE)
init_curl_http_auth(result);
if (getenv("GIT_SSL_VERSION"))
@@ -1208,11 +1277,13 @@ static CURL *get_curl_handle(void)
return result;
}
-static void set_from_env(const char **var, const char *envname)
+static void set_from_env(char **var, const char *envname)
{
const char *val = getenv(envname);
- if (val)
- *var = val;
+ if (val) {
+ FREE_AND_NULL(*var);
+ *var = xstrdup(val);
+ }
}
void http_init(struct remote *remote, const char *url, int proactive_auth)
@@ -1265,7 +1336,8 @@ void http_init(struct remote *remote, const char *url, int proactive_auth)
if (curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK)
die("curl_global_init failed");
- http_proactive_auth = proactive_auth;
+ if (proactive_auth && http_proactive_auth == PROACTIVE_AUTH_NONE)
+ http_proactive_auth = PROACTIVE_AUTH_IF_CREDENTIALS;
if (remote && remote->http_proxy)
curl_http_proxy = xstrdup(remote->http_proxy);
@@ -1275,8 +1347,6 @@ void http_init(struct remote *remote, const char *url, int proactive_auth)
pragma_header = curl_slist_append(http_copy_default_headers(),
"Pragma: no-cache");
- no_pragma_header = curl_slist_append(http_copy_default_headers(),
- "Pragma:");
{
char *http_max_requests = getenv("GIT_HTTP_MAX_REQUESTS");
@@ -1360,9 +1430,6 @@ void http_cleanup(void)
curl_slist_free_all(pragma_header);
pragma_header = NULL;
- curl_slist_free_all(no_pragma_header);
- no_pragma_header = NULL;
-
curl_slist_free_all(host_resolutions);
host_resolutions = NULL;
@@ -1442,7 +1509,16 @@ struct active_request_slot *get_active_slot(void)
slot->finished = NULL;
slot->callback_data = NULL;
slot->callback_func = NULL;
+
+ if (curl_cookie_file && !strcmp(curl_cookie_file, "-")) {
+ warning(_("refusing to read cookies from http.cookiefile '-'"));
+ FREE_AND_NULL(curl_cookie_file);
+ }
curl_easy_setopt(slot->curl, CURLOPT_COOKIEFILE, curl_cookie_file);
+ if (curl_save_cookies && (!curl_cookie_file || !curl_cookie_file[0])) {
+ curl_save_cookies = 0;
+ warning(_("ignoring http.savecookies for empty http.cookiefile"));
+ }
if (curl_save_cookies)
curl_easy_setopt(slot->curl, CURLOPT_COOKIEJAR, curl_cookie_file);
curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, pragma_header);
@@ -1470,7 +1546,7 @@ struct active_request_slot *get_active_slot(void)
curl_easy_setopt(slot->curl, CURLOPT_IPRESOLVE, git_curl_ipresolve);
curl_easy_setopt(slot->curl, CURLOPT_HTTPAUTH, http_auth_methods);
- if (http_auth.password || curl_empty_auth_enabled())
+ if (http_auth.password || http_auth.credential || curl_empty_auth_enabled())
init_curl_http_auth(slot->curl);
return slot;
@@ -1759,8 +1835,15 @@ static int handle_curl_result(struct slot_results *results)
} else if (missing_target(results))
return HTTP_MISSING_TARGET;
else if (results->http_code == 401) {
- if (http_auth.username && http_auth.password) {
+ if ((http_auth.username && http_auth.password) ||\
+ (http_auth.authtype && http_auth.credential)) {
+ if (http_auth.multistage) {
+ credential_clear_secrets(&http_auth);
+ return HTTP_REAUTH;
+ }
credential_reject(&http_auth);
+ if (always_auth_proactively())
+ http_proactive_auth = PROACTIVE_AUTH_NONE;
return HTTP_NOAUTH;
} else {
http_auth_methods &= ~CURLAUTH_GSSNEGOTIATE;
@@ -1947,7 +2030,7 @@ static void write_accept_language(struct strbuf *buf)
/* add '*' */
REALLOC_ARRAY(language_tags, num_langs + 1);
- language_tags[num_langs++] = "*"; /* it's OK; this won't be freed */
+ language_tags[num_langs++] = xstrdup("*");
/* compute decimal_places */
for (max_q = 1, decimal_places = 0;
@@ -1977,8 +2060,7 @@ static void write_accept_language(struct strbuf *buf)
}
}
- /* free language tags -- last one is a static '*' */
- for (i = 0; i < num_langs - 1; i++)
+ for (i = 0; i < num_langs; i++)
free(language_tags[i]);
free(language_tags);
}
@@ -2067,11 +2149,15 @@ static int http_request(const char *url,
/* Add additional headers here */
if (options && options->extra_headers) {
const struct string_list_item *item;
- for_each_string_list_item(item, options->extra_headers) {
- headers = curl_slist_append(headers, item->string);
+ if (options && options->extra_headers) {
+ for_each_string_list_item(item, options->extra_headers) {
+ headers = curl_slist_append(headers, item->string);
+ }
}
}
+ headers = http_append_auth_header(&http_auth, headers);
+
curl_easy_setopt(slot->curl, CURLOPT_URL, url);
curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(slot->curl, CURLOPT_ENCODING, "");
@@ -2153,7 +2239,13 @@ static int http_request_reauth(const char *url,
void *result, int target,
struct http_get_options *options)
{
- int ret = http_request(url, result, target, options);
+ int i = 3;
+ int ret;
+
+ if (always_auth_proactively())
+ credential_fill(&http_auth, 1);
+
+ ret = http_request(url, result, target, options);
if (ret != HTTP_OK && ret != HTTP_REAUTH)
return ret;
@@ -2166,35 +2258,35 @@ static int http_request_reauth(const char *url,
}
}
- if (ret != HTTP_REAUTH)
- return ret;
-
- /*
- * The previous request may have put cruft into our output stream; we
- * should clear it out before making our next request.
- */
- switch (target) {
- case HTTP_REQUEST_STRBUF:
- strbuf_reset(result);
- break;
- case HTTP_REQUEST_FILE:
- if (fflush(result)) {
- error_errno("unable to flush a file");
- return HTTP_START_FAILED;
- }
- rewind(result);
- if (ftruncate(fileno(result), 0) < 0) {
- error_errno("unable to truncate a file");
- return HTTP_START_FAILED;
+ while (ret == HTTP_REAUTH && --i) {
+ /*
+ * The previous request may have put cruft into our output stream; we
+ * should clear it out before making our next request.
+ */
+ switch (target) {
+ case HTTP_REQUEST_STRBUF:
+ strbuf_reset(result);
+ break;
+ case HTTP_REQUEST_FILE:
+ if (fflush(result)) {
+ error_errno("unable to flush a file");
+ return HTTP_START_FAILED;
+ }
+ rewind(result);
+ if (ftruncate(fileno(result), 0) < 0) {
+ error_errno("unable to truncate a file");
+ return HTTP_START_FAILED;
+ }
+ break;
+ default:
+ BUG("Unknown http_request target");
}
- break;
- default:
- BUG("Unknown http_request target");
- }
- credential_fill(&http_auth);
+ credential_fill(&http_auth, 1);
- return http_request(url, result, target, options);
+ ret = http_request(url, result, target, options);
+ }
+ return ret;
}
int http_get_strbuf(const char *url,
@@ -2371,6 +2463,7 @@ void release_http_pack_request(struct http_pack_request *preq)
}
preq->slot = NULL;
strbuf_release(&preq->tmpfile);
+ curl_slist_free_all(preq->headers);
free(preq->url);
free(preq);
}
@@ -2455,11 +2548,11 @@ struct http_pack_request *new_direct_http_pack_request(
}
preq->slot = get_active_slot();
+ preq->headers = object_request_headers();
curl_easy_setopt(preq->slot->curl, CURLOPT_WRITEDATA, preq->packfile);
curl_easy_setopt(preq->slot->curl, CURLOPT_WRITEFUNCTION, fwrite);
curl_easy_setopt(preq->slot->curl, CURLOPT_URL, preq->url);
- curl_easy_setopt(preq->slot->curl, CURLOPT_HTTPHEADER,
- no_pragma_header);
+ curl_easy_setopt(preq->slot->curl, CURLOPT_HTTPHEADER, preq->headers);
/*
* If there is data present from a previous transfer attempt,
@@ -2625,13 +2718,14 @@ struct http_object_request *new_http_object_request(const char *base_url,
}
freq->slot = get_active_slot();
+ freq->headers = object_request_headers();
curl_easy_setopt(freq->slot->curl, CURLOPT_WRITEDATA, freq);
curl_easy_setopt(freq->slot->curl, CURLOPT_FAILONERROR, 0);
curl_easy_setopt(freq->slot->curl, CURLOPT_WRITEFUNCTION, fwrite_sha1_file);
curl_easy_setopt(freq->slot->curl, CURLOPT_ERRORBUFFER, freq->errorstr);
curl_easy_setopt(freq->slot->curl, CURLOPT_URL, freq->url);
- curl_easy_setopt(freq->slot->curl, CURLOPT_HTTPHEADER, no_pragma_header);
+ curl_easy_setopt(freq->slot->curl, CURLOPT_HTTPHEADER, freq->headers);
/*
* If we have successfully processed data from a previous fetch
@@ -2719,5 +2813,6 @@ void release_http_object_request(struct http_object_request *freq)
release_active_slot(freq->slot);
freq->slot = NULL;
}
+ curl_slist_free_all(freq->headers);
strbuf_release(&freq->tmpfile);
}
diff --git a/http.h b/http.h
index 3af19a8bf5..a516ca4a9a 100644
--- a/http.h
+++ b/http.h
@@ -175,6 +175,9 @@ int http_get_file(const char *url, const char *filename,
int http_fetch_ref(const char *base, struct ref *ref);
+struct curl_slist *http_append_auth_header(const struct credential *c,
+ struct curl_slist *headers);
+
/* Helpers for fetching packs */
int http_get_info_packs(const char *base_url,
struct packed_git **packs_head);
@@ -196,6 +199,7 @@ struct http_pack_request {
FILE *packfile;
struct strbuf tmpfile;
struct active_request_slot *slot;
+ struct curl_slist *headers;
};
struct http_pack_request *new_http_pack_request(
@@ -229,6 +233,7 @@ struct http_object_request {
int zret;
int rename;
struct active_request_slot *slot;
+ struct curl_slist *headers;
};
struct http_object_request *new_http_object_request(
diff --git a/ident.c b/ident.c
index cc7afdbf81..caf41fb2a9 100644
--- a/ident.c
+++ b/ident.c
@@ -46,9 +46,9 @@ static struct passwd *xgetpwuid_self(int *is_bogus)
pw = getpwuid(getuid());
if (!pw) {
static struct passwd fallback;
- fallback.pw_name = "unknown";
+ fallback.pw_name = (char *) "unknown";
#ifndef NO_GECOS_IN_PWENT
- fallback.pw_gecos = "Unknown";
+ fallback.pw_gecos = (char *) "Unknown";
#endif
pw = &fallback;
if (is_bogus)
diff --git a/imap-send.c b/imap-send.c
index 0afd088d8a..01404e5047 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -69,21 +69,16 @@ static void imap_warn(const char *, ...);
static char *next_arg(char **);
struct imap_server_conf {
- const char *name;
- const char *tunnel;
- const char *host;
+ char *tunnel;
+ char *host;
int port;
- const char *folder;
- const char *user;
- const char *pass;
+ char *folder;
+ char *user;
+ char *pass;
int use_ssl;
int ssl_verify;
int use_html;
- const char *auth_method;
-};
-
-static struct imap_server_conf server = {
- .ssl_verify = 1,
+ char *auth_method;
};
struct imap_socket {
@@ -110,6 +105,7 @@ struct imap {
};
struct imap_store {
+ const struct imap_server_conf *cfg;
/* currently open mailbox */
const char *name; /* foreign! maybe preset? */
int uidvalidity;
@@ -194,8 +190,8 @@ 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 UNUSED,
- int use_tls_only UNUSED,
- int verify UNUSED)
+ const struct imap_server_conf *cfg,
+ int use_tls_only UNUSED)
{
fprintf(stderr, "SSL requested but SSL support not compiled in\n");
return -1;
@@ -250,7 +246,9 @@ 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,
+ const struct imap_server_conf *cfg,
+ int use_tls_only)
{
#if (OPENSSL_VERSION_NUMBER >= 0x10000000L)
const SSL_METHOD *meth;
@@ -279,7 +277,7 @@ static int ssl_socket_connect(struct imap_socket *sock, int use_tls_only, int ve
if (use_tls_only)
SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
- if (verify)
+ if (cfg->ssl_verify)
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
if (!SSL_CTX_set_default_verify_paths(ctx)) {
@@ -306,9 +304,9 @@ static int ssl_socket_connect(struct imap_socket *sock, int use_tls_only, int ve
* OpenSSL does not document this function, but the implementation
* returns 1 on success, 0 on failure after calling SSLerr().
*/
- ret = SSL_set_tlsext_host_name(sock->ssl, server.host);
+ ret = SSL_set_tlsext_host_name(sock->ssl, cfg->host);
if (ret != 1)
- warning("SSL_set_tlsext_host_name(%s) failed.", server.host);
+ warning("SSL_set_tlsext_host_name(%s) failed.", cfg->host);
#endif
ret = SSL_connect(sock->ssl);
@@ -317,12 +315,12 @@ static int ssl_socket_connect(struct imap_socket *sock, int use_tls_only, int ve
return -1;
}
- if (verify) {
+ if (cfg->ssl_verify) {
/* make sure the hostname matches that of the certificate */
cert = SSL_get_peer_certificate(sock->ssl);
if (!cert)
return error("unable to get peer certificate.");
- if (verify_hostname(cert, server.host) < 0)
+ if (verify_hostname(cert, cfg->host) < 0)
return -1;
}
@@ -895,7 +893,7 @@ static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
int ret;
char *response;
- response = cram(prompt, server.user, server.pass);
+ response = cram(prompt, ctx->cfg->user, ctx->cfg->pass);
ret = socket_write(&ctx->imap->buf.sock, response, strlen(response));
if (ret != strlen(response))
@@ -917,7 +915,7 @@ static void server_fill_credential(struct imap_server_conf *srvc, struct credent
cred->username = xstrdup_or_null(srvc->user);
cred->password = xstrdup_or_null(srvc->pass);
- credential_fill(cred);
+ credential_fill(cred, 1);
if (!srvc->user)
srvc->user = xstrdup(cred->username);
@@ -935,6 +933,7 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
CALLOC_ARRAY(ctx, 1);
+ ctx->cfg = srvc;
ctx->imap = CALLOC_ARRAY(imap, 1);
imap->buf.sock.fd[0] = imap->buf.sock.fd[1] = -1;
imap->in_progress_append = &imap->in_progress;
@@ -1035,7 +1034,7 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
imap->buf.sock.fd[1] = dup(s);
if (srvc->use_ssl &&
- ssl_socket_connect(&imap->buf.sock, 0, srvc->ssl_verify)) {
+ ssl_socket_connect(&imap->buf.sock, srvc, 0)) {
close(s);
goto bail;
}
@@ -1068,8 +1067,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,
- srvc->ssl_verify))
+ if (ssl_socket_connect(&imap->buf.sock, srvc, 1))
goto bail;
/* capabilities may have changed, so get the new capabilities */
if (imap_exec(ctx, NULL, "CAPABILITY") != RESP_OK)
@@ -1215,9 +1213,9 @@ static int imap_store_msg(struct imap_store *ctx, struct strbuf *msg)
static void wrap_in_html(struct strbuf *msg)
{
struct strbuf buf = STRBUF_INIT;
- static char *content_type = "Content-Type: text/html;\n";
- static char *pre_open = "<pre>\n";
- static char *pre_close = "</pre>\n";
+ static const char *content_type = "Content-Type: text/html;\n";
+ static const char *pre_open = "<pre>\n";
+ static const char *pre_close = "</pre>\n";
const char *body = strstr(msg->buf, "\n\n");
if (!body)
@@ -1299,24 +1297,30 @@ static int split_msg(struct strbuf *all_msgs, struct strbuf *msg, int *ofs)
static int git_imap_config(const char *var, const char *val,
const struct config_context *ctx, void *cb)
{
-
- if (!strcmp("imap.sslverify", var))
- server.ssl_verify = git_config_bool(var, val);
- else if (!strcmp("imap.preformattedhtml", var))
- server.use_html = git_config_bool(var, val);
- else if (!strcmp("imap.folder", var))
- return git_config_string(&server.folder, var, val);
- else if (!strcmp("imap.user", var))
- return git_config_string(&server.user, var, val);
- else if (!strcmp("imap.pass", var))
- return git_config_string(&server.pass, var, val);
- else if (!strcmp("imap.tunnel", var))
- return git_config_string(&server.tunnel, var, val);
- else if (!strcmp("imap.authmethod", var))
- return git_config_string(&server.auth_method, var, val);
- else if (!strcmp("imap.port", var))
- server.port = git_config_int(var, val, ctx->kvi);
- else if (!strcmp("imap.host", var)) {
+ struct imap_server_conf *cfg = cb;
+
+ if (!strcmp("imap.sslverify", var)) {
+ cfg->ssl_verify = git_config_bool(var, val);
+ } else if (!strcmp("imap.preformattedhtml", var)) {
+ cfg->use_html = git_config_bool(var, val);
+ } else if (!strcmp("imap.folder", var)) {
+ FREE_AND_NULL(cfg->folder);
+ return git_config_string(&cfg->folder, var, val);
+ } else if (!strcmp("imap.user", var)) {
+ FREE_AND_NULL(cfg->folder);
+ return git_config_string(&cfg->user, var, val);
+ } else if (!strcmp("imap.pass", var)) {
+ FREE_AND_NULL(cfg->folder);
+ return git_config_string(&cfg->pass, var, val);
+ } else if (!strcmp("imap.tunnel", var)) {
+ FREE_AND_NULL(cfg->folder);
+ return git_config_string(&cfg->tunnel, var, val);
+ } else if (!strcmp("imap.authmethod", var)) {
+ FREE_AND_NULL(cfg->folder);
+ return git_config_string(&cfg->auth_method, var, val);
+ } else if (!strcmp("imap.port", var)) {
+ cfg->port = git_config_int(var, val, ctx->kvi);
+ } else if (!strcmp("imap.host", var)) {
if (!val) {
return config_error_nonbool(var);
} else {
@@ -1324,14 +1328,15 @@ static int git_imap_config(const char *var, const char *val,
val += 5;
else if (starts_with(val, "imaps:")) {
val += 6;
- server.use_ssl = 1;
+ cfg->use_ssl = 1;
}
if (starts_with(val, "//"))
val += 2;
- server.host = xstrdup(val);
+ cfg->host = xstrdup(val);
}
- } else
+ } else {
return git_default_config(var, val, ctx, cb);
+ }
return 0;
}
@@ -1497,12 +1502,16 @@ static int curl_append_msgs_to_imap(struct imap_server_conf *server,
int cmd_main(int argc, const char **argv)
{
+ struct imap_server_conf server = {
+ .ssl_verify = 1,
+ };
struct strbuf all_msgs = STRBUF_INIT;
int total;
int nongit_ok;
+ int ret;
setup_git_directory_gently(&nongit_ok);
- git_config(git_imap_config, NULL);
+ git_config(git_imap_config, &server);
argc = parse_options(argc, (const char **)argv, "", imap_send_options, imap_send_usage, 0);
@@ -1526,42 +1535,56 @@ int cmd_main(int argc, const char **argv)
if (!server.folder) {
fprintf(stderr, "no imap store specified\n");
- return 1;
+ ret = 1;
+ goto out;
}
if (!server.host) {
if (!server.tunnel) {
fprintf(stderr, "no imap host specified\n");
- return 1;
+ ret = 1;
+ goto out;
}
- server.host = "tunnel";
+ server.host = xstrdup("tunnel");
}
/* read the messages */
if (strbuf_read(&all_msgs, 0, 0) < 0) {
error_errno(_("could not read from stdin"));
- return 1;
+ ret = 1;
+ goto out;
}
if (all_msgs.len == 0) {
fprintf(stderr, "nothing to send\n");
- return 1;
+ ret = 1;
+ goto out;
}
total = count_messages(&all_msgs);
if (!total) {
fprintf(stderr, "no messages to send\n");
- return 1;
+ ret = 1;
+ goto out;
}
/* write it to the imap server */
if (server.tunnel)
- return append_msgs_to_imap(&server, &all_msgs, total);
-
+ ret = append_msgs_to_imap(&server, &all_msgs, total);
#ifdef USE_CURL_FOR_IMAP_SEND
- if (use_curl)
- return curl_append_msgs_to_imap(&server, &all_msgs, total);
+ else if (use_curl)
+ ret = curl_append_msgs_to_imap(&server, &all_msgs, total);
#endif
-
- return append_msgs_to_imap(&server, &all_msgs, total);
+ else
+ ret = append_msgs_to_imap(&server, &all_msgs, total);
+
+out:
+ free(server.tunnel);
+ free(server.host);
+ free(server.folder);
+ free(server.user);
+ free(server.pass);
+ free(server.auth_method);
+ strbuf_release(&all_msgs);
+ return ret;
}
diff --git a/json-writer.c b/json-writer.c
index 005c820aa4..25b9201f9c 100644
--- a/json-writer.c
+++ b/json-writer.c
@@ -46,10 +46,7 @@ static void append_quoted_string(struct strbuf *out, const char *in)
static void indent_pretty(struct json_writer *jw)
{
- int k;
-
- for (k = 0; k < jw->open_stack.len; k++)
- strbuf_addstr(&jw->json, " ");
+ strbuf_addstrings(&jw->json, " ", jw->open_stack.len);
}
/*
diff --git a/line-log.c b/line-log.c
index 8ff6ccb772..67c80b39a0 100644
--- a/line-log.c
+++ b/line-log.c
@@ -899,14 +899,12 @@ static void print_line(const char *prefix, char first,
static char *output_prefix(struct diff_options *opt)
{
- char *prefix = "";
-
if (opt->output_prefix) {
struct strbuf *sb = opt->output_prefix(opt, opt->output_prefix_data);
- prefix = sb->buf;
+ return sb->buf;
+ } else {
+ return xstrdup("");
}
-
- return prefix;
}
static void dump_diff_hacky_one(struct rev_info *rev, struct line_log_data *range)
@@ -927,7 +925,7 @@ static void dump_diff_hacky_one(struct rev_info *rev, struct line_log_data *rang
const char *c_context = diff_get_color(opt->use_color, DIFF_CONTEXT);
if (!pair || !diff)
- return;
+ goto out;
if (pair->one->oid_valid)
fill_line_ends(rev->diffopt.repo, pair->one, &p_lines, &p_ends);
@@ -1002,8 +1000,10 @@ static void dump_diff_hacky_one(struct rev_info *rev, struct line_log_data *rang
c_context, c_reset, opt->file);
}
+out:
free(p_ends);
free(t_ends);
+ free(prefix);
}
/*
@@ -1012,7 +1012,11 @@ static void dump_diff_hacky_one(struct rev_info *rev, struct line_log_data *rang
*/
static void dump_diff_hacky(struct rev_info *rev, struct line_log_data *range)
{
- fprintf(rev->diffopt.file, "%s\n", output_prefix(&rev->diffopt));
+ char *prefix = output_prefix(&rev->diffopt);
+
+ fprintf(rev->diffopt.file, "%s\n", prefix);
+ free(prefix);
+
while (range) {
dump_diff_hacky_one(rev, range);
range = range->next;
@@ -1032,6 +1036,7 @@ static int process_diff_filepair(struct rev_info *rev,
struct range_set tmp;
struct diff_ranges diff;
mmfile_t file_parent, file_target;
+ char *parent_data_to_free = NULL;
assert(pair->two->path);
while (rg) {
@@ -1056,7 +1061,7 @@ static int process_diff_filepair(struct rev_info *rev,
file_parent.ptr = pair->one->data;
file_parent.size = pair->one->size;
} else {
- file_parent.ptr = "";
+ file_parent.ptr = parent_data_to_free = xstrdup("");
file_parent.size = 0;
}
@@ -1075,6 +1080,7 @@ static int process_diff_filepair(struct rev_info *rev,
diff_ranges_release(&diff);
+ free(parent_data_to_free);
return ((*diff_out)->parent.nr > 0);
}
diff --git a/line-range.c b/line-range.c
index 60f0e5ada8..b99f0d9895 100644
--- a/line-range.c
+++ b/line-range.c
@@ -234,6 +234,8 @@ static const char *parse_range_funcname(
}
regfree(&regexp);
+ if (xecfg)
+ xdiff_clear_find_func(xecfg);
free(xecfg);
free(pattern);
diff --git a/list-objects-filter-options.c b/list-objects-filter-options.c
index c5f363ca6f..00611107d2 100644
--- a/list-objects-filter-options.c
+++ b/list-objects-filter-options.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "config.h"
#include "gettext.h"
diff --git a/list-objects-filter.c b/list-objects-filter.c
index 4346f8da45..dc598a081b 100644
--- a/list-objects-filter.c
+++ b/list-objects-filter.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "dir.h"
#include "gettext.h"
@@ -542,6 +544,8 @@ static void filter_sparse_oid__init(
filter->filter_data = d;
filter->filter_object_fn = filter_sparse;
filter->free_fn = filter_sparse_free;
+
+ object_context_release(&oc);
}
/*
diff --git a/list-objects.c b/list-objects.c
index 11ad8be411..985d008799 100644
--- a/list-objects.c
+++ b/list-objects.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "tag.h"
#include "commit.h"
diff --git a/log-tree.c b/log-tree.c
index 16031b44e7..52feec4356 100644
--- a/log-tree.c
+++ b/log-tree.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "commit-reach.h"
#include "config.h"
@@ -232,8 +234,10 @@ void load_ref_decorations(struct decoration_filter *filter, int flags)
}
decoration_loaded = 1;
decoration_flags = flags;
- for_each_ref(add_ref_decoration, filter);
- head_ref(add_ref_decoration, filter);
+ refs_for_each_ref(get_main_ref_store(the_repository),
+ add_ref_decoration, filter);
+ refs_head_ref(get_main_ref_store(the_repository),
+ add_ref_decoration, filter);
for_each_commit_graft(add_graft_decoration, filter);
}
}
@@ -277,7 +281,8 @@ static const struct name_decoration *current_pointed_by_HEAD(const struct name_d
return NULL;
/* Now resolve and find the matching current branch */
- branch_name = resolve_ref_unsafe("HEAD", 0, NULL, &rru_flags);
+ branch_name = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
+ "HEAD", 0, NULL, &rru_flags);
if (!branch_name || !(rru_flags & REF_ISSYMREF))
return NULL;
@@ -670,6 +675,51 @@ static void next_commentary_block(struct rev_info *opt, struct strbuf *sb)
opt->shown_dashes = 1;
}
+static void show_diff_of_diff(struct rev_info *opt)
+{
+ if (!cmit_fmt_is_mail(opt->commit_format))
+ return;
+
+ if (opt->idiff_oid1) {
+ struct diff_queue_struct dq;
+
+ memcpy(&dq, &diff_queued_diff, sizeof(diff_queued_diff));
+ DIFF_QUEUE_CLEAR(&diff_queued_diff);
+
+ fprintf_ln(opt->diffopt.file, "\n%s", opt->idiff_title);
+ show_interdiff(opt->idiff_oid1, opt->idiff_oid2, 2,
+ &opt->diffopt);
+
+ memcpy(&diff_queued_diff, &dq, sizeof(diff_queued_diff));
+ }
+
+ if (opt->rdiff1) {
+ struct diff_queue_struct dq;
+ struct diff_options opts;
+ struct range_diff_options range_diff_opts = {
+ .creation_factor = opt->creation_factor,
+ .dual_color = 1,
+ .diffopt = &opts
+ };
+
+ memcpy(&dq, &diff_queued_diff, sizeof(diff_queued_diff));
+ DIFF_QUEUE_CLEAR(&diff_queued_diff);
+
+ fprintf_ln(opt->diffopt.file, "\n%s", opt->rdiff_title);
+ /*
+ * Pass minimum required diff-options to range-diff; others
+ * can be added later if deemed desirable.
+ */
+ repo_diff_setup(the_repository, &opts);
+ opts.file = opt->diffopt.file;
+ opts.use_color = opt->diffopt.use_color;
+ diff_setup_done(&opts);
+ show_range_diff(opt->rdiff1, opt->rdiff2, &range_diff_opts);
+
+ memcpy(&diff_queued_diff, &dq, sizeof(diff_queued_diff));
+ }
+}
+
void show_log(struct rev_info *opt)
{
struct strbuf msgbuf = STRBUF_INIT;
@@ -853,47 +903,6 @@ void show_log(struct rev_info *opt)
strbuf_release(&msgbuf);
free(ctx.notes_message);
free(ctx.after_subject);
-
- if (cmit_fmt_is_mail(ctx.fmt) && opt->idiff_oid1) {
- struct diff_queue_struct dq;
-
- memcpy(&dq, &diff_queued_diff, sizeof(diff_queued_diff));
- DIFF_QUEUE_CLEAR(&diff_queued_diff);
-
- next_commentary_block(opt, NULL);
- fprintf_ln(opt->diffopt.file, "%s", opt->idiff_title);
- show_interdiff(opt->idiff_oid1, opt->idiff_oid2, 2,
- &opt->diffopt);
-
- memcpy(&diff_queued_diff, &dq, sizeof(diff_queued_diff));
- }
-
- if (cmit_fmt_is_mail(ctx.fmt) && opt->rdiff1) {
- struct diff_queue_struct dq;
- struct diff_options opts;
- struct range_diff_options range_diff_opts = {
- .creation_factor = opt->creation_factor,
- .dual_color = 1,
- .diffopt = &opts
- };
-
- memcpy(&dq, &diff_queued_diff, sizeof(diff_queued_diff));
- DIFF_QUEUE_CLEAR(&diff_queued_diff);
-
- next_commentary_block(opt, NULL);
- fprintf_ln(opt->diffopt.file, "%s", opt->rdiff_title);
- /*
- * Pass minimum required diff-options to range-diff; others
- * can be added later if deemed desirable.
- */
- repo_diff_setup(the_repository, &opts);
- opts.file = opt->diffopt.file;
- opts.use_color = opt->diffopt.use_color;
- diff_setup_done(&opts);
- show_range_diff(opt->rdiff1, opt->rdiff2, &range_diff_opts);
-
- memcpy(&diff_queued_diff, &dq, sizeof(diff_queued_diff));
- }
}
int log_tree_diff_flush(struct rev_info *opt)
@@ -1044,6 +1053,7 @@ static int do_remerge_diff(struct rev_info *opt,
log_tree_diff_flush(opt);
/* Cleanup */
+ free_commit_list(bases);
cleanup_additional_headers(&opt->diffopt);
strbuf_release(&parent1_desc);
strbuf_release(&parent2_desc);
@@ -1162,9 +1172,12 @@ int log_tree_commit(struct rev_info *opt, struct commit *commit)
}
if (opt->track_linear && !opt->linear && opt->reverse_output_stage)
fprintf(opt->diffopt.file, "\n%s\n", opt->break_bar);
+ if (shown)
+ show_diff_of_diff(opt);
opt->loginfo = NULL;
maybe_flush_or_die(opt->diffopt.file, "stdout");
opt->diffopt.no_free = no_free;
+
diff_free(&opt->diffopt);
return shown;
}
diff --git a/loose.c b/loose.c
index f6faa6216a..a8bf772172 100644
--- a/loose.c
+++ b/loose.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "hash.h"
#include "path.h"
diff --git a/loose.h b/loose.h
index 2c2957072c..28512306e5 100644
--- a/loose.h
+++ b/loose.h
@@ -3,6 +3,8 @@
#include "khash.h"
+struct repository;
+
struct loose_object_map {
kh_oid_map_t *to_compat;
kh_oid_map_t *to_storage;
diff --git a/ls-refs.c b/ls-refs.c
index 819cbefee3..2dd925b43d 100644
--- a/ls-refs.c
+++ b/ls-refs.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "environment.h"
#include "gettext.h"
@@ -95,9 +97,11 @@ static int send_ref(const char *refname, const struct object_id *oid,
strbuf_addf(&data->buf, "unborn %s", refname_nons);
if (data->symrefs && flag & REF_ISSYMREF) {
struct object_id unused;
- const char *symref_target = resolve_ref_unsafe(refname, 0,
- &unused,
- &flag);
+ const char *symref_target = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
+ refname,
+ 0,
+ &unused,
+ &flag);
if (!symref_target)
die("'%s' is a symref but it is not?", refname);
@@ -108,7 +112,7 @@ static int send_ref(const char *refname, const struct object_id *oid,
if (data->peel && oid) {
struct object_id peeled;
- if (!peel_iterated_oid(oid, &peeled))
+ if (!peel_iterated_oid(the_repository, oid, &peeled))
strbuf_addf(&data->buf, " peeled:%s", oid_to_hex(&peeled));
}
@@ -126,7 +130,7 @@ static void send_possibly_unborn_head(struct ls_refs_data *data)
int oid_is_null;
strbuf_addf(&namespaced, "%sHEAD", get_git_namespace());
- if (!resolve_ref_unsafe(namespaced.buf, 0, &oid, &flag))
+ if (!refs_resolve_ref_unsafe(get_main_ref_store(the_repository), namespaced.buf, 0, &oid, &flag))
return; /* bad ref */
oid_is_null = is_null_oid(&oid);
if (!oid_is_null ||
diff --git a/mailmap.c b/mailmap.c
index 3d6a5e9400..2d0212f444 100644
--- a/mailmap.c
+++ b/mailmap.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "environment.h"
#include "string-list.h"
@@ -6,8 +8,8 @@
#include "object-store-ll.h"
#include "setup.h"
-const char *git_mailmap_file;
-const char *git_mailmap_blob;
+char *git_mailmap_file;
+char *git_mailmap_blob;
struct mailmap_info {
char *name;
@@ -216,7 +218,7 @@ int read_mailmap(struct string_list *map)
map->cmp = namemap_cmp;
if (!git_mailmap_blob && is_bare_repository())
- git_mailmap_blob = "HEAD:.mailmap";
+ git_mailmap_blob = xstrdup("HEAD:.mailmap");
if (!startup_info->have_repository || !is_bare_repository())
err |= read_mailmap_file(map, ".mailmap",
diff --git a/mailmap.h b/mailmap.h
index 0f8fd2c586..cbda9bc5e0 100644
--- a/mailmap.h
+++ b/mailmap.h
@@ -3,8 +3,8 @@
struct string_list;
-extern const char *git_mailmap_file;
-extern const char *git_mailmap_blob;
+extern char *git_mailmap_file;
+extern char *git_mailmap_blob;
int read_mailmap(struct string_list *map);
void clear_mailmap(struct string_list *map);
diff --git a/match-trees.c b/match-trees.c
index 3412b6a140..f17c74d483 100644
--- a/match-trees.c
+++ b/match-trees.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "hex.h"
#include "match-trees.h"
@@ -229,7 +231,7 @@ static int splice_tree(const struct object_id *oid1, const char *prefix,
oid_to_hex(oid1));
if (*subpath) {
struct object_id tree_oid;
- oidread(&tree_oid, rewrite_here);
+ oidread(&tree_oid, rewrite_here, the_repository->hash_algo);
status = splice_tree(&tree_oid, subpath, oid2, &subtree);
if (status)
return status;
@@ -237,7 +239,7 @@ static int splice_tree(const struct object_id *oid1, const char *prefix,
} else {
rewrite_with = oid2;
}
- hashcpy(rewrite_here, rewrite_with->hash);
+ hashcpy(rewrite_here, rewrite_with->hash, the_repository->hash_algo);
status = write_object_file(buf, sz, OBJ_TREE, result);
free(buf);
return status;
diff --git a/mem-pool.h b/mem-pool.h
index d1c66413ec..321d86a63c 100644
--- a/mem-pool.h
+++ b/mem-pool.h
@@ -50,6 +50,7 @@ char *mem_pool_strndup(struct mem_pool *pool, const char *str, size_t len);
/*
* Allocate memory from the memory pool and format a string into it.
*/
+__attribute__((format (printf, 2, 3)))
char *mem_pool_strfmt(struct mem_pool *pool, const char *fmt, ...);
/*
diff --git a/merge-blobs.c b/merge-blobs.c
index 2f659fd014..0ad0390fea 100644
--- a/merge-blobs.c
+++ b/merge-blobs.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "merge-ll.h"
#include "blob.h"
diff --git a/merge-ll.c b/merge-ll.c
index bf1077ae09..180c19df67 100644
--- a/merge-ll.c
+++ b/merge-ll.c
@@ -29,7 +29,7 @@ struct ll_merge_driver {
const char *name;
const char *description;
ll_merge_fn fn;
- const char *recursive;
+ char *recursive;
struct ll_merge_driver *next;
char *cmdline;
};
@@ -268,7 +268,7 @@ static enum ll_merge_result ll_ext_merge(const struct ll_merge_driver *fn,
* merge.default and merge.driver configuration items
*/
static struct ll_merge_driver *ll_user_merge, **ll_user_merge_tail;
-static const char *default_ll_merge;
+static char *default_ll_merge;
static int read_merge_config(const char *var, const char *value,
const struct config_context *ctx UNUSED,
@@ -304,8 +304,13 @@ static int read_merge_config(const char *var, const char *value,
ll_user_merge_tail = &(fn->next);
}
- if (!strcmp("name", key))
- return git_config_string(&fn->description, var, value);
+ if (!strcmp("name", key)) {
+ /*
+ * The description is leaking, but that's okay as we want to
+ * keep around the merge drivers anyway.
+ */
+ return git_config_string((char **) &fn->description, var, value);
+ }
if (!strcmp("driver", key)) {
if (!value)
diff --git a/merge-ort-wrappers.c b/merge-ort-wrappers.c
index 4acedf3c33..d6f6135996 100644
--- a/merge-ort-wrappers.c
+++ b/merge-ort-wrappers.c
@@ -48,7 +48,7 @@ int merge_ort_nonrecursive(struct merge_options *opt,
int merge_ort_recursive(struct merge_options *opt,
struct commit *side1,
struct commit *side2,
- struct commit_list *merge_bases,
+ const struct commit_list *merge_bases,
struct commit **result)
{
struct tree *head = repo_get_commit_tree(opt->repo, side1);
diff --git a/merge-ort-wrappers.h b/merge-ort-wrappers.h
index 0c4c57adbb..90af1f69c5 100644
--- a/merge-ort-wrappers.h
+++ b/merge-ort-wrappers.h
@@ -19,7 +19,7 @@ int merge_ort_nonrecursive(struct merge_options *opt,
int merge_ort_recursive(struct merge_options *opt,
struct commit *h1,
struct commit *h2,
- struct commit_list *ancestors,
+ const struct commit_list *ancestors,
struct commit **result);
#endif
diff --git a/merge-ort.c b/merge-ort.c
index eaede6cead..e9d01ac7f7 100644
--- a/merge-ort.c
+++ b/merge-ort.c
@@ -14,6 +14,8 @@
* "cale", "peedy", or "ins" instead of "ort"?)
*/
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "merge-ort.h"
@@ -543,17 +545,35 @@ enum conflict_and_info_types {
CONFLICT_SUBMODULE_HISTORY_NOT_AVAILABLE,
CONFLICT_SUBMODULE_MAY_HAVE_REWINDS,
CONFLICT_SUBMODULE_NULL_MERGE_BASE,
- CONFLICT_SUBMODULE_CORRUPT,
+
+ /* INSERT NEW ENTRIES HERE */
+
+ /*
+ * Keep this entry after all regular conflict and info types; only
+ * errors (failures causing immediate abort of the merge) should
+ * come after this.
+ */
+ NB_REGULAR_CONFLICT_TYPES,
+
+ /*
+ * Something is seriously wrong; cannot even perform merge;
+ * Keep this group _last_ other than NB_TOTAL_TYPES
+ */
+ ERROR_SUBMODULE_CORRUPT,
+ ERROR_THREEWAY_CONTENT_MERGE_FAILED,
+ ERROR_OBJECT_WRITE_FAILED,
+ ERROR_OBJECT_READ_FAILED,
+ ERROR_OBJECT_NOT_A_BLOB,
/* Keep this entry _last_ in the list */
- NB_CONFLICT_TYPES,
+ NB_TOTAL_TYPES,
};
/*
* Short description of conflict type, relied upon by external tools.
*
* We can add more entries, but DO NOT change any of these strings. Also,
- * Order MUST match conflict_info_and_types.
+ * please ensure the order matches what is used in conflict_info_and_types.
*/
static const char *type_short_descriptions[] = {
/*** "Simple" conflicts and informational messages ***/
@@ -597,8 +617,18 @@ static const char *type_short_descriptions[] = {
"CONFLICT (submodule may have rewinds)",
[CONFLICT_SUBMODULE_NULL_MERGE_BASE] =
"CONFLICT (submodule lacks merge base)",
- [CONFLICT_SUBMODULE_CORRUPT] =
- "CONFLICT (submodule corrupt)"
+
+ /* Something is seriously wrong; cannot even perform merge */
+ [ERROR_SUBMODULE_CORRUPT] =
+ "ERROR (submodule corrupt)",
+ [ERROR_THREEWAY_CONTENT_MERGE_FAILED] =
+ "ERROR (three-way content merge failed)",
+ [ERROR_OBJECT_WRITE_FAILED] =
+ "ERROR (object write failed)",
+ [ERROR_OBJECT_READ_FAILED] =
+ "ERROR (object read failed)",
+ [ERROR_OBJECT_NOT_A_BLOB] =
+ "ERROR (object is not a blob)",
};
struct logical_conflict_info {
@@ -762,7 +792,8 @@ static void path_msg(struct merge_options *opt,
/* Sanity checks */
assert(omittable_hint ==
- !starts_with(type_short_descriptions[type], "CONFLICT") ||
+ (!starts_with(type_short_descriptions[type], "CONFLICT") &&
+ !starts_with(type_short_descriptions[type], "ERROR")) ||
type == CONFLICT_DIR_RENAME_SUGGESTED);
if (opt->record_conflict_msgs_as_headers && omittable_hint)
return; /* Do not record mere hints in headers */
@@ -1817,9 +1848,9 @@ static int merge_submodule(struct merge_options *opt,
/* check whether both changes are forward */
ret2 = repo_in_merge_bases(&subrepo, commit_o, commit_a);
if (ret2 < 0) {
- path_msg(opt, CONFLICT_SUBMODULE_CORRUPT, 0,
+ path_msg(opt, ERROR_SUBMODULE_CORRUPT, 0,
path, NULL, NULL, NULL,
- _("Failed to merge submodule %s "
+ _("error: failed to merge submodule %s "
"(repository corrupt)"),
path);
ret = -1;
@@ -1828,9 +1859,9 @@ static int merge_submodule(struct merge_options *opt,
if (ret2 > 0)
ret2 = repo_in_merge_bases(&subrepo, commit_o, commit_b);
if (ret2 < 0) {
- path_msg(opt, CONFLICT_SUBMODULE_CORRUPT, 0,
+ path_msg(opt, ERROR_SUBMODULE_CORRUPT, 0,
path, NULL, NULL, NULL,
- _("Failed to merge submodule %s "
+ _("error: failed to merge submodule %s "
"(repository corrupt)"),
path);
ret = -1;
@@ -1848,9 +1879,9 @@ static int merge_submodule(struct merge_options *opt,
/* Case #1: a is contained in b or vice versa */
ret2 = repo_in_merge_bases(&subrepo, commit_a, commit_b);
if (ret2 < 0) {
- path_msg(opt, CONFLICT_SUBMODULE_CORRUPT, 0,
+ path_msg(opt, ERROR_SUBMODULE_CORRUPT, 0,
path, NULL, NULL, NULL,
- _("Failed to merge submodule %s "
+ _("error: failed to merge submodule %s "
"(repository corrupt)"),
path);
ret = -1;
@@ -1867,9 +1898,9 @@ static int merge_submodule(struct merge_options *opt,
}
ret2 = repo_in_merge_bases(&subrepo, commit_b, commit_a);
if (ret2 < 0) {
- path_msg(opt, CONFLICT_SUBMODULE_CORRUPT, 0,
+ path_msg(opt, ERROR_SUBMODULE_CORRUPT, 0,
path, NULL, NULL, NULL,
- _("Failed to merge submodule %s "
+ _("error: failed to merge submodule %s "
"(repository corrupt)"),
path);
ret = -1;
@@ -1901,9 +1932,9 @@ static int merge_submodule(struct merge_options *opt,
&merges);
switch (parent_count) {
case -1:
- path_msg(opt, CONFLICT_SUBMODULE_CORRUPT, 0,
+ path_msg(opt, ERROR_SUBMODULE_CORRUPT, 0,
path, NULL, NULL, NULL,
- _("Failed to merge submodule %s "
+ _("error: failed to merge submodule %s "
"(repository corrupt)"),
path);
ret = -1;
@@ -2109,7 +2140,7 @@ static int handle_content_merge(struct merge_options *opt,
* merges, which happens for example with rename/rename(2to1) and
* rename/add conflicts.
*/
- unsigned clean = 1;
+ int clean = 1;
/*
* handle_content_merge() needs both files to be of the same type, i.e.
@@ -2173,18 +2204,28 @@ static int handle_content_merge(struct merge_options *opt,
pathnames, extra_marker_size,
&result_buf);
- if ((merge_status < 0) || !result_buf.ptr)
- ret = error(_("failed to execute internal merge"));
+ if ((merge_status < 0) || !result_buf.ptr) {
+ path_msg(opt, ERROR_THREEWAY_CONTENT_MERGE_FAILED, 0,
+ pathnames[0], pathnames[1], pathnames[2], NULL,
+ _("error: failed to execute internal merge for %s"),
+ path);
+ ret = -1;
+ }
if (!ret &&
write_object_file(result_buf.ptr, result_buf.size,
- OBJ_BLOB, &result->oid))
- ret = error(_("unable to add %s to database"), path);
-
+ OBJ_BLOB, &result->oid)) {
+ path_msg(opt, ERROR_OBJECT_WRITE_FAILED, 0,
+ pathnames[0], pathnames[1], pathnames[2], NULL,
+ _("error: unable to add %s to database"), path);
+ ret = -1;
+ }
free(result_buf.ptr);
+
if (ret)
return -1;
- clean &= (merge_status == 0);
+ if (merge_status > 0)
+ clean = 0;
path_msg(opt, INFO_AUTO_MERGING, 1, path, NULL, NULL, NULL,
_("Auto-merging %s"), path);
} else if (S_ISGITLINK(a->mode)) {
@@ -2192,6 +2233,8 @@ static int handle_content_merge(struct merge_options *opt,
clean = merge_submodule(opt, pathnames[0],
two_way ? null_oid() : &o->oid,
&a->oid, &b->oid, &result->oid);
+ if (clean < 0)
+ return -1;
if (opt->priv->call_depth && two_way && !clean) {
result->mode = o->mode;
oidcpy(&result->oid, &o->oid);
@@ -3557,18 +3600,27 @@ static int sort_dirs_next_to_their_children(const char *one, const char *two)
return c1 - c2;
}
-static int read_oid_strbuf(const struct object_id *oid,
- struct strbuf *dst)
+static int read_oid_strbuf(struct merge_options *opt,
+ const struct object_id *oid,
+ struct strbuf *dst,
+ const char *path)
{
void *buf;
enum object_type type;
unsigned long size;
buf = repo_read_object_file(the_repository, oid, &type, &size);
- if (!buf)
- return error(_("cannot read object %s"), oid_to_hex(oid));
+ if (!buf) {
+ path_msg(opt, ERROR_OBJECT_READ_FAILED, 0,
+ path, NULL, NULL, NULL,
+ _("error: cannot read object %s"), oid_to_hex(oid));
+ return -1;
+ }
if (type != OBJ_BLOB) {
free(buf);
- return error(_("object %s is not a blob"), oid_to_hex(oid));
+ path_msg(opt, ERROR_OBJECT_NOT_A_BLOB, 0,
+ path, NULL, NULL, NULL,
+ _("error: object %s is not a blob"), oid_to_hex(oid));
+ return -1;
}
strbuf_attach(dst, buf, size, size + 1);
return 0;
@@ -3592,8 +3644,8 @@ static int blob_unchanged(struct merge_options *opt,
if (oideq(&base->oid, &side->oid))
return 1;
- if (read_oid_strbuf(&base->oid, &basebuf) ||
- read_oid_strbuf(&side->oid, &sidebuf))
+ if (read_oid_strbuf(opt, &base->oid, &basebuf, path) ||
+ read_oid_strbuf(opt, &side->oid, &sidebuf, path))
goto error_return;
/*
* Note: binary | is used so that both renormalizations are
@@ -4643,6 +4695,7 @@ void merge_display_update_messages(struct merge_options *opt,
struct hashmap_iter iter;
struct strmap_entry *e;
struct string_list olist = STRING_LIST_INIT_NODUP;
+ FILE *o = stdout;
if (opt->record_conflict_msgs_as_headers)
BUG("Either display conflict messages or record them as headers, not both");
@@ -4659,6 +4712,10 @@ void merge_display_update_messages(struct merge_options *opt,
}
string_list_sort(&olist);
+ /* Print to stderr if we hit errors rather than just conflicts */
+ if (result->clean < 0)
+ o = stderr;
+
/* Iterate over the items, printing them */
for (int path_nr = 0; path_nr < olist.nr; ++path_nr) {
struct string_list *conflicts = olist.items[path_nr].util;
@@ -4666,25 +4723,31 @@ void merge_display_update_messages(struct merge_options *opt,
struct logical_conflict_info *info =
conflicts->items[i].util;
+ /* On failure, ignore regular conflict types */
+ if (result->clean < 0 &&
+ info->type < NB_REGULAR_CONFLICT_TYPES)
+ continue;
+
if (detailed) {
- printf("%lu", (unsigned long)info->paths.nr);
- putchar('\0');
+ fprintf(o, "%lu", (unsigned long)info->paths.nr);
+ fputc('\0', o);
for (int n = 0; n < info->paths.nr; n++) {
- fputs(info->paths.v[n], stdout);
- putchar('\0');
+ fputs(info->paths.v[n], o);
+ fputc('\0', o);
}
- fputs(type_short_descriptions[info->type],
- stdout);
- putchar('\0');
+ fputs(type_short_descriptions[info->type], o);
+ fputc('\0', o);
}
- puts(conflicts->items[i].string);
+ fputs(conflicts->items[i].string, o);
+ fputc('\n', o);
if (detailed)
- putchar('\0');
+ fputc('\0', o);
}
}
string_list_clear(&olist, 0);
- print_submodule_conflict_suggestion(&opti->conflicted_submodules);
+ if (result->clean >= 0)
+ print_submodule_conflict_suggestion(&opti->conflicted_submodules);
/* Also include needed rename limit adjustment now */
diff_warn_rename_limit("merge.renamelimit",
@@ -5000,6 +5063,26 @@ static void merge_check_renames_reusable(struct merge_result *result,
/*** Function Grouping: merge_incore_*() and their internal variants ***/
+static void move_opt_priv_to_result_priv(struct merge_options *opt,
+ struct merge_result *result)
+{
+ /*
+ * opt->priv and result->priv are a bit weird. opt->priv contains
+ * information that we can re-use in subsequent merge operations to
+ * enable our cached renames optimization. The best way to provide
+ * that to subsequent merges is putting it in result->priv.
+ * However, putting it directly there would mean retrofitting lots
+ * of functions in this file to also take a merge_result pointer,
+ * which is ugly and annoying. So, we just make sure at the end of
+ * the merge (the outer merge if there are internal recursive ones)
+ * to move it.
+ */
+ assert(opt->priv && !result->priv);
+ result->priv = opt->priv;
+ result->_properly_initialized = RESULT_INITIALIZED;
+ opt->priv = NULL;
+}
+
/*
* Originally from merge_trees_internal(); heavily adapted, though.
*/
@@ -5030,6 +5113,7 @@ redo:
oid_to_hex(&side1->object.oid),
oid_to_hex(&side2->object.oid));
result->clean = -1;
+ move_opt_priv_to_result_priv(opt, result);
return;
}
trace2_region_leave("merge", "collect_merge_info", opt->repo);
@@ -5060,22 +5144,20 @@ redo:
/* existence of conflicted entries implies unclean */
result->clean &= strmap_empty(&opt->priv->conflicted);
}
- if (!opt->priv->call_depth) {
- result->priv = opt->priv;
- result->_properly_initialized = RESULT_INITIALIZED;
- opt->priv = NULL;
- }
+ if (!opt->priv->call_depth || result->clean < 0)
+ move_opt_priv_to_result_priv(opt, result);
}
/*
* Originally from merge_recursive_internal(); somewhat adapted, though.
*/
static void merge_ort_internal(struct merge_options *opt,
- struct commit_list *merge_bases,
+ const struct commit_list *_merge_bases,
struct commit *h1,
struct commit *h2,
struct merge_result *result)
{
+ struct commit_list *merge_bases = copy_commit_list(_merge_bases);
struct commit *next;
struct commit *merged_merge_bases;
const char *ancestor_name;
@@ -5085,7 +5167,7 @@ static void merge_ort_internal(struct merge_options *opt,
if (repo_get_merge_bases(the_repository, h1, h2,
&merge_bases) < 0) {
result->clean = -1;
- return;
+ goto out;
}
/* See merge-ort.h:merge_incore_recursive() declaration NOTE */
merge_bases = reverse_commit_list(merge_bases);
@@ -5129,7 +5211,7 @@ static void merge_ort_internal(struct merge_options *opt,
opt->branch2 = "Temporary merge branch 2";
merge_ort_internal(opt, NULL, prev, next, result);
if (result->clean < 0)
- return;
+ goto out;
opt->branch1 = saved_b1;
opt->branch2 = saved_b2;
opt->priv->call_depth--;
@@ -5152,6 +5234,9 @@ static void merge_ort_internal(struct merge_options *opt,
result);
strbuf_release(&merge_base_abbrev);
opt->ancestor = NULL; /* avoid accidental re-use of opt->ancestor */
+
+out:
+ free_commit_list(merge_bases);
}
void merge_incore_nonrecursive(struct merge_options *opt,
@@ -5181,7 +5266,7 @@ void merge_incore_nonrecursive(struct merge_options *opt,
}
void merge_incore_recursive(struct merge_options *opt,
- struct commit_list *merge_bases,
+ const struct commit_list *merge_bases,
struct commit *side1,
struct commit *side2,
struct merge_result *result)
diff --git a/merge-ort.h b/merge-ort.h
index ce56ec1a78..82f2b3222d 100644
--- a/merge-ort.h
+++ b/merge-ort.h
@@ -2,7 +2,7 @@
#define MERGE_ORT_H
#include "merge-recursive.h"
-#include "hash-ll.h"
+#include "hash.h"
struct commit;
struct tree;
@@ -59,7 +59,7 @@ struct merge_result {
* first", 2006-08-09)
*/
void merge_incore_recursive(struct merge_options *opt,
- struct commit_list *merge_bases,
+ const struct commit_list *merge_bases,
struct commit *side1,
struct commit *side2,
struct merge_result *result);
diff --git a/merge-recursive.c b/merge-recursive.c
index 8ff29ed09e..5cc638066a 100644
--- a/merge-recursive.c
+++ b/merge-recursive.c
@@ -3,6 +3,9 @@
* Fredrik Kuivinen.
* The thieves were Alex Riesen and Johannes Schindelin, in June/July 2006
*/
+
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "merge-recursive.h"
@@ -239,7 +242,8 @@ enum rename_type {
struct stage_data {
struct diff_filespec stages[4]; /* mostly for oid & mode; maybe path */
struct rename_conflict_info *rename_conflict_info;
- unsigned processed:1;
+ unsigned processed:1,
+ rename_conflict_info_owned:1;
};
struct rename {
@@ -308,6 +312,7 @@ static inline void setup_rename_conflict_info(enum rename_type rename_type,
ci->ren1->dst_entry->processed = 0;
ci->ren1->dst_entry->rename_conflict_info = ci;
+ ci->ren1->dst_entry->rename_conflict_info_owned = 1;
if (ren2) {
ci->ren2->dst_entry->rename_conflict_info = ci;
}
@@ -3055,6 +3060,10 @@ static void final_cleanup_rename(struct string_list *rename)
for (i = 0; i < rename->nr; i++) {
re = rename->items[i].util;
diff_free_filepair(re->pair);
+ if (re->src_entry->rename_conflict_info_owned)
+ FREE_AND_NULL(re->src_entry->rename_conflict_info);
+ if (re->dst_entry->rename_conflict_info_owned)
+ FREE_AND_NULL(re->dst_entry->rename_conflict_info);
}
string_list_clear(rename, 1);
free(rename);
@@ -3627,15 +3636,16 @@ static int merge_trees_internal(struct merge_options *opt,
static int merge_recursive_internal(struct merge_options *opt,
struct commit *h1,
struct commit *h2,
- struct commit_list *merge_bases,
+ const struct commit_list *_merge_bases,
struct commit **result)
{
+ struct commit_list *merge_bases = copy_commit_list(_merge_bases);
struct commit_list *iter;
struct commit *merged_merge_bases;
struct tree *result_tree;
- int clean;
const char *ancestor_name;
struct strbuf merge_base_abbrev = STRBUF_INIT;
+ int ret;
if (show(opt, 4)) {
output(opt, 4, _("Merging:"));
@@ -3645,8 +3655,10 @@ static int merge_recursive_internal(struct merge_options *opt,
if (!merge_bases) {
if (repo_get_merge_bases(the_repository, h1, h2,
- &merge_bases) < 0)
- return -1;
+ &merge_bases) < 0) {
+ ret = -1;
+ goto out;
+ }
merge_bases = reverse_commit_list(merge_bases);
}
@@ -3696,14 +3708,18 @@ static int merge_recursive_internal(struct merge_options *opt,
opt->branch1 = "Temporary merge branch 1";
opt->branch2 = "Temporary merge branch 2";
if (merge_recursive_internal(opt, merged_merge_bases, iter->item,
- NULL, &merged_merge_bases) < 0)
- return -1;
+ NULL, &merged_merge_bases) < 0) {
+ ret = -1;
+ goto out;
+ }
opt->branch1 = saved_b1;
opt->branch2 = saved_b2;
opt->priv->call_depth--;
- if (!merged_merge_bases)
- return err(opt, _("merge returned no commit"));
+ if (!merged_merge_bases) {
+ ret = err(opt, _("merge returned no commit"));
+ goto out;
+ }
}
/*
@@ -3720,17 +3736,16 @@ static int merge_recursive_internal(struct merge_options *opt,
repo_read_index(opt->repo);
opt->ancestor = ancestor_name;
- clean = merge_trees_internal(opt,
- repo_get_commit_tree(opt->repo, h1),
- repo_get_commit_tree(opt->repo, h2),
- repo_get_commit_tree(opt->repo,
- merged_merge_bases),
- &result_tree);
- strbuf_release(&merge_base_abbrev);
+ ret = merge_trees_internal(opt,
+ repo_get_commit_tree(opt->repo, h1),
+ repo_get_commit_tree(opt->repo, h2),
+ repo_get_commit_tree(opt->repo,
+ merged_merge_bases),
+ &result_tree);
opt->ancestor = NULL; /* avoid accidental re-use of opt->ancestor */
- if (clean < 0) {
+ if (ret < 0) {
flush_output(opt);
- return clean;
+ goto out;
}
if (opt->priv->call_depth) {
@@ -3739,7 +3754,11 @@ static int merge_recursive_internal(struct merge_options *opt,
commit_list_insert(h1, &(*result)->parents);
commit_list_insert(h2, &(*result)->parents->next);
}
- return clean;
+
+out:
+ strbuf_release(&merge_base_abbrev);
+ free_commit_list(merge_bases);
+ return ret;
}
static int merge_start(struct merge_options *opt, struct tree *head)
@@ -3794,6 +3813,9 @@ static void merge_finalize(struct merge_options *opt)
if (show(opt, 2))
diff_warn_rename_limit("merge.renamelimit",
opt->priv->needed_rename_limit, 0);
+ hashmap_clear_and_free(&opt->priv->current_file_dir_set,
+ struct path_hashmap_entry, e);
+ string_list_clear(&opt->priv->df_conflict_file_set, 0);
FREE_AND_NULL(opt->priv);
}
@@ -3818,7 +3840,7 @@ int merge_trees(struct merge_options *opt,
int merge_recursive(struct merge_options *opt,
struct commit *h1,
struct commit *h2,
- struct commit_list *merge_bases,
+ const struct commit_list *merge_bases,
struct commit **result)
{
int clean;
@@ -3860,7 +3882,7 @@ int merge_recursive_generic(struct merge_options *opt,
const struct object_id *head,
const struct object_id *merge,
int num_merge_bases,
- const struct object_id **merge_bases,
+ const struct object_id *merge_bases,
struct commit **result)
{
int clean;
@@ -3873,10 +3895,10 @@ int merge_recursive_generic(struct merge_options *opt,
int i;
for (i = 0; i < num_merge_bases; ++i) {
struct commit *base;
- if (!(base = get_ref(opt->repo, merge_bases[i],
- oid_to_hex(merge_bases[i]))))
+ if (!(base = get_ref(opt->repo, &merge_bases[i],
+ oid_to_hex(&merge_bases[i]))))
return err(opt, _("Could not parse object '%s'"),
- oid_to_hex(merge_bases[i]));
+ oid_to_hex(&merge_bases[i]));
commit_list_insert(base, &ca);
}
if (num_merge_bases == 1)
@@ -3886,6 +3908,7 @@ int merge_recursive_generic(struct merge_options *opt,
repo_hold_locked_index(opt->repo, &lock, LOCK_DIE_ON_ERROR);
clean = merge_recursive(opt, head_commit, next_commit, ca,
result);
+ free_commit_list(ca);
if (clean < 0) {
rollback_lock_file(&lock);
return clean;
diff --git a/merge-recursive.h b/merge-recursive.h
index e67d38c303..3136c7cc2d 100644
--- a/merge-recursive.h
+++ b/merge-recursive.h
@@ -104,7 +104,7 @@ int merge_trees(struct merge_options *opt,
int merge_recursive(struct merge_options *opt,
struct commit *h1,
struct commit *h2,
- struct commit_list *merge_bases,
+ const struct commit_list *merge_bases,
struct commit **result);
/*
@@ -123,7 +123,7 @@ int merge_recursive_generic(struct merge_options *opt,
const struct object_id *head,
const struct object_id *merge,
int num_merge_bases,
- const struct object_id **merge_bases,
+ const struct object_id *merge_bases,
struct commit **result);
#endif
diff --git a/merge.c b/merge.c
index 752a937fa9..fe3efa4b24 100644
--- a/merge.c
+++ b/merge.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "gettext.h"
#include "hash.h"
diff --git a/mergetools/vimdiff b/mergetools/vimdiff
index 97e376329b..f8ad6b35d4 100644
--- a/mergetools/vimdiff
+++ b/mergetools/vimdiff
@@ -72,7 +72,6 @@ gen_cmd_aux () {
nested=0
nested_min=100
-
# Step 1:
#
# Increase/decrease "start"/"end" indices respectively to get rid of
@@ -87,7 +86,7 @@ gen_cmd_aux () {
IFS=#
for c in $(echo "$LAYOUT" | sed 's:.:&#:g')
do
- if test "$c" = " "
+ if test -z "$c" || test "$c" = " "
then
continue
fi
@@ -326,7 +325,7 @@ gen_cmd () {
fi
# If this is a single window diff with all the buffers
- if ! echo "$tab" | grep ",\|/" >/dev/null
+ if ! echo "$tab" | grep -E ",|/" >/dev/null
then
CMD="$CMD | silent execute 'bufdo diffthis'"
fi
diff --git a/midx-write.c b/midx-write.c
index 65e69d2de7..478b42e720 100644
--- a/midx-write.c
+++ b/midx-write.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "abspath.h"
#include "config.h"
@@ -100,6 +102,32 @@ struct write_midx_context {
struct string_list *to_include;
};
+static int should_include_pack(const struct write_midx_context *ctx,
+ const char *file_name)
+{
+ /*
+ * Note that at most one of ctx->m and ctx->to_include are set,
+ * so we are testing midx_contains_pack() and
+ * string_list_has_string() independently (guarded by the
+ * appropriate NULL checks).
+ *
+ * We could support passing to_include while reusing an existing
+ * MIDX, but don't currently since the reuse process drags
+ * forward all packs from an existing MIDX (without checking
+ * whether or not they appear in the to_include list).
+ *
+ * If we added support for that, these next two conditional
+ * should be performed independently (likely checking
+ * to_include before the existing MIDX).
+ */
+ if (ctx->m && midx_contains_pack(ctx->m, file_name))
+ return 0;
+ else if (ctx->to_include &&
+ !string_list_has_string(ctx->to_include, file_name))
+ return 0;
+ return 1;
+}
+
static void add_pack_to_midx(const char *full_path, size_t full_path_len,
const char *file_name, void *data)
{
@@ -108,29 +136,11 @@ static void add_pack_to_midx(const char *full_path, size_t full_path_len,
if (ends_with(file_name, ".idx")) {
display_progress(ctx->progress, ++ctx->pack_paths_checked);
- /*
- * Note that at most one of ctx->m and ctx->to_include are set,
- * so we are testing midx_contains_pack() and
- * string_list_has_string() independently (guarded by the
- * appropriate NULL checks).
- *
- * We could support passing to_include while reusing an existing
- * MIDX, but don't currently since the reuse process drags
- * forward all packs from an existing MIDX (without checking
- * whether or not they appear in the to_include list).
- *
- * If we added support for that, these next two conditional
- * should be performed independently (likely checking
- * to_include before the existing MIDX).
- */
- if (ctx->m && midx_contains_pack(ctx->m, file_name))
- return;
- else if (ctx->to_include &&
- !string_list_has_string(ctx->to_include, file_name))
+
+ if (!should_include_pack(ctx, file_name))
return;
ALLOC_GROW(ctx->info, ctx->nr + 1, ctx->alloc);
-
p = add_packed_git(full_path, full_path_len, 0);
if (!p) {
warning(_("failed to add packfile '%s'"),
@@ -299,21 +309,16 @@ static void midx_fanout_add_pack_fanout(struct midx_fanout *fanout,
* Copy only the de-duplicated entries (selected by most-recent modified time
* of a packfile containing the object).
*/
-static struct pack_midx_entry *get_sorted_entries(struct multi_pack_index *m,
- struct pack_info *info,
- uint32_t nr_packs,
- size_t *nr_objects,
- int preferred_pack)
+static void compute_sorted_entries(struct write_midx_context *ctx,
+ uint32_t start_pack)
{
uint32_t cur_fanout, cur_pack, cur_object;
size_t alloc_objects, total_objects = 0;
struct midx_fanout fanout = { 0 };
- struct pack_midx_entry *deduplicated_entries = NULL;
- uint32_t start_pack = m ? m->num_packs : 0;
- for (cur_pack = start_pack; cur_pack < nr_packs; cur_pack++)
+ for (cur_pack = start_pack; cur_pack < ctx->nr; cur_pack++)
total_objects = st_add(total_objects,
- info[cur_pack].p->num_objects);
+ ctx->info[cur_pack].p->num_objects);
/*
* As we de-duplicate by fanout value, we expect the fanout
@@ -323,26 +328,26 @@ static struct pack_midx_entry *get_sorted_entries(struct multi_pack_index *m,
alloc_objects = fanout.alloc = total_objects > 3200 ? total_objects / 200 : 16;
ALLOC_ARRAY(fanout.entries, fanout.alloc);
- ALLOC_ARRAY(deduplicated_entries, alloc_objects);
- *nr_objects = 0;
+ ALLOC_ARRAY(ctx->entries, alloc_objects);
+ ctx->entries_nr = 0;
for (cur_fanout = 0; cur_fanout < 256; cur_fanout++) {
fanout.nr = 0;
- if (m)
- midx_fanout_add_midx_fanout(&fanout, m, cur_fanout,
- preferred_pack);
+ if (ctx->m)
+ midx_fanout_add_midx_fanout(&fanout, ctx->m, cur_fanout,
+ ctx->preferred_pack_idx);
- for (cur_pack = start_pack; cur_pack < nr_packs; cur_pack++) {
- int preferred = cur_pack == preferred_pack;
+ for (cur_pack = start_pack; cur_pack < ctx->nr; cur_pack++) {
+ int preferred = cur_pack == ctx->preferred_pack_idx;
midx_fanout_add_pack_fanout(&fanout,
- info, cur_pack,
+ ctx->info, cur_pack,
preferred, cur_fanout);
}
- if (-1 < preferred_pack && preferred_pack < start_pack)
- midx_fanout_add_pack_fanout(&fanout, info,
- preferred_pack, 1,
+ if (-1 < ctx->preferred_pack_idx && ctx->preferred_pack_idx < start_pack)
+ midx_fanout_add_pack_fanout(&fanout, ctx->info,
+ ctx->preferred_pack_idx, 1,
cur_fanout);
midx_fanout_sort(&fanout);
@@ -356,17 +361,16 @@ static struct pack_midx_entry *get_sorted_entries(struct multi_pack_index *m,
&fanout.entries[cur_object].oid))
continue;
- ALLOC_GROW(deduplicated_entries, st_add(*nr_objects, 1),
+ ALLOC_GROW(ctx->entries, st_add(ctx->entries_nr, 1),
alloc_objects);
- memcpy(&deduplicated_entries[*nr_objects],
+ memcpy(&ctx->entries[ctx->entries_nr],
&fanout.entries[cur_object],
sizeof(struct pack_midx_entry));
- (*nr_objects)++;
+ ctx->entries_nr++;
}
}
free(fanout.entries);
- return deduplicated_entries;
}
static int write_midx_pack_names(struct hashfile *f, void *data)
@@ -664,7 +668,7 @@ static int add_ref_to_pending(const char *refname,
return 0;
}
- if (!peel_iterated_oid(oid, &peeled))
+ if (!peel_iterated_oid(the_repository, oid, &peeled))
oid = &peeled;
object = parse_object_or_die(oid, refname);
@@ -755,7 +759,8 @@ static struct commit **find_commits_for_midx_bitmap(uint32_t *indexed_commits_nr
read_refs_snapshot(refs_snapshot, &revs);
} else {
setup_revisions(0, NULL, &revs, NULL);
- for_each_ref(add_ref_to_pending, &revs);
+ refs_for_each_ref(get_main_ref_store(the_repository),
+ add_ref_to_pending, &revs);
}
/*
@@ -798,6 +803,7 @@ static int write_midx_bitmap(const char *midx_name,
{
int ret, i;
uint16_t options = 0;
+ struct bitmap_writer writer;
struct pack_idx_entry **index;
char *bitmap_name = xstrfmt("%s-%s.bitmap", midx_name,
hash_to_hex(midx_hash));
@@ -819,8 +825,10 @@ static int write_midx_bitmap(const char *midx_name,
for (i = 0; i < pdata->nr_objects; i++)
index[i] = &pdata->objects[i].idx;
- bitmap_writer_show_progress(flags & MIDX_PROGRESS);
- bitmap_writer_build_type_index(pdata, index, pdata->nr_objects);
+ bitmap_writer_init(&writer, the_repository);
+ bitmap_writer_show_progress(&writer, flags & MIDX_PROGRESS);
+ bitmap_writer_build_type_index(&writer, pdata, index,
+ pdata->nr_objects);
/*
* bitmap_writer_finish expects objects in lex order, but pack_order
@@ -838,17 +846,19 @@ static int write_midx_bitmap(const char *midx_name,
for (i = 0; i < pdata->nr_objects; i++)
index[pack_order[i]] = &pdata->objects[i].idx;
- bitmap_writer_select_commits(commits, commits_nr, -1);
- ret = bitmap_writer_build(pdata);
+ bitmap_writer_select_commits(&writer, commits, commits_nr);
+ ret = bitmap_writer_build(&writer, pdata);
if (ret < 0)
goto cleanup;
- bitmap_writer_set_checksum(midx_hash);
- bitmap_writer_finish(index, pdata->nr_objects, bitmap_name, options);
+ bitmap_writer_set_checksum(&writer, midx_hash);
+ bitmap_writer_finish(&writer, index, pdata->nr_objects, bitmap_name,
+ options);
cleanup:
free(index);
free(bitmap_name);
+ bitmap_writer_free(&writer);
trace2_region_leave("midx", "write_midx_bitmap", the_repository);
@@ -880,6 +890,40 @@ cleanup:
return result;
}
+static int fill_packs_from_midx(struct write_midx_context *ctx,
+ const char *preferred_pack_name, uint32_t flags)
+{
+ uint32_t i;
+
+ for (i = 0; i < ctx->m->num_packs; i++) {
+ ALLOC_GROW(ctx->info, ctx->nr + 1, ctx->alloc);
+
+ if (flags & MIDX_WRITE_REV_INDEX || preferred_pack_name) {
+ /*
+ * If generating a reverse index, need to have
+ * packed_git's loaded to compare their
+ * mtimes and object count.
+ *
+ *
+ * If a preferred pack is specified, need to
+ * have packed_git's loaded to ensure the chosen
+ * preferred pack has a non-zero object count.
+ */
+ if (prepare_midx_pack(the_repository, ctx->m, i))
+ return error(_("could not load pack"));
+
+ if (open_pack_index(ctx->m->packs[i]))
+ die(_("could not open index for %s"),
+ ctx->m->packs[i]->pack_name);
+ }
+
+ fill_pack_info(&ctx->info[ctx->nr++], ctx->m->packs[i],
+ ctx->m->pack_names[i], i);
+ }
+
+ return 0;
+}
+
static int write_midx_internal(const char *object_dir,
struct string_list *packs_to_include,
struct string_list *packs_to_drop,
@@ -889,7 +933,7 @@ static int write_midx_internal(const char *object_dir,
{
struct strbuf midx_name = STRBUF_INIT;
unsigned char midx_hash[GIT_MAX_RAWSZ];
- uint32_t i;
+ uint32_t i, start_pack;
struct hashfile *f = NULL;
struct lock_file lk;
struct write_midx_context ctx = { 0 };
@@ -925,32 +969,14 @@ static int write_midx_internal(const char *object_dir,
ctx.info = NULL;
ALLOC_ARRAY(ctx.info, ctx.alloc);
- if (ctx.m) {
- for (i = 0; i < ctx.m->num_packs; i++) {
- ALLOC_GROW(ctx.info, ctx.nr + 1, ctx.alloc);
-
- if (flags & MIDX_WRITE_REV_INDEX) {
- /*
- * If generating a reverse index, need to have
- * packed_git's loaded to compare their
- * mtimes and object count.
- */
- if (prepare_midx_pack(the_repository, ctx.m, i)) {
- error(_("could not load pack"));
- result = 1;
- goto cleanup;
- }
-
- if (open_pack_index(ctx.m->packs[i]))
- die(_("could not open index for %s"),
- ctx.m->packs[i]->pack_name);
- }
-
- fill_pack_info(&ctx.info[ctx.nr++], ctx.m->packs[i],
- ctx.m->pack_names[i], i);
- }
+ if (ctx.m && fill_packs_from_midx(&ctx, preferred_pack_name,
+ flags) < 0) {
+ result = 1;
+ goto cleanup;
}
+ start_pack = ctx.nr;
+
ctx.pack_paths_checked = 0;
if (flags & MIDX_PROGRESS)
ctx.progress = start_delayed_progress(_("Adding packfiles to multi-pack-index"), 0);
@@ -1048,8 +1074,7 @@ static int write_midx_internal(const char *object_dir,
}
}
- ctx.entries = get_sorted_entries(ctx.m, ctx.info, ctx.nr, &ctx.entries_nr,
- ctx.preferred_pack_idx);
+ compute_sorted_entries(&ctx, start_pack);
ctx.large_offsets_needed = 0;
for (i = 0; i < ctx.entries_nr; i++) {
diff --git a/midx.c b/midx.c
index 6f07de3688..3992b05465 100644
--- a/midx.c
+++ b/midx.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "config.h"
#include "dir.h"
@@ -25,13 +27,15 @@ const unsigned char *get_midx_checksum(struct multi_pack_index *m)
void get_midx_filename(struct strbuf *out, const char *object_dir)
{
- strbuf_addf(out, "%s/pack/multi-pack-index", object_dir);
+ get_midx_filename_ext(out, object_dir, NULL, NULL);
}
-void get_midx_rev_filename(struct strbuf *out, struct multi_pack_index *m)
+void get_midx_filename_ext(struct strbuf *out, const char *object_dir,
+ const unsigned char *hash, const char *ext)
{
- get_midx_filename(out, m->object_dir);
- strbuf_addf(out, "-%s.rev", hash_to_hex(get_midx_checksum(m)));
+ strbuf_addf(out, "%s/pack/multi-pack-index", object_dir);
+ if (ext)
+ strbuf_addf(out, "-%s.%s", hash_to_hex(hash), ext);
}
static int midx_read_oid_fanout(const unsigned char *chunk_start,
@@ -302,7 +306,8 @@ struct object_id *nth_midxed_object_oid(struct object_id *oid,
if (n >= m->num_objects)
return NULL;
- oidread(oid, m->chunk_oid_lookup + st_mult(m->hash_len, n));
+ oidread(oid, m->chunk_oid_lookup + st_mult(m->hash_len, n),
+ the_repository->hash_algo);
return oid;
}
diff --git a/midx.h b/midx.h
index dc477dff44..8554f2d616 100644
--- a/midx.h
+++ b/midx.h
@@ -74,9 +74,13 @@ struct multi_pack_index {
#define MIDX_WRITE_BITMAP_HASH_CACHE (1 << 3)
#define MIDX_WRITE_BITMAP_LOOKUP_TABLE (1 << 4)
+#define MIDX_EXT_REV "rev"
+#define MIDX_EXT_BITMAP "bitmap"
+
const unsigned char *get_midx_checksum(struct multi_pack_index *m);
void get_midx_filename(struct strbuf *out, const char *object_dir);
-void get_midx_rev_filename(struct strbuf *out, struct multi_pack_index *m);
+void get_midx_filename_ext(struct strbuf *out, const char *object_dir,
+ const unsigned char *hash, const char *ext);
struct multi_pack_index *load_multi_pack_index(const char *object_dir, int local);
int prepare_midx_pack(struct repository *r, struct multi_pack_index *m, uint32_t pack_int_id);
diff --git a/negotiator/default.c b/negotiator/default.c
index 9a5b696327..e3fa5c3324 100644
--- a/negotiator/default.c
+++ b/negotiator/default.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "default.h"
#include "../commit.h"
@@ -192,6 +194,7 @@ void default_negotiator_init(struct fetch_negotiator *negotiator)
ns->rev_list.compare = compare_commits_by_commit_date;
if (marked)
- for_each_ref(clear_marks, NULL);
+ refs_for_each_ref(get_main_ref_store(the_repository),
+ clear_marks, NULL);
marked = 1;
}
diff --git a/negotiator/skipping.c b/negotiator/skipping.c
index 5b91520430..f109928ad0 100644
--- a/negotiator/skipping.c
+++ b/negotiator/skipping.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "skipping.h"
#include "../commit.h"
@@ -261,6 +263,7 @@ void skipping_negotiator_init(struct fetch_negotiator *negotiator)
data->rev_list.compare = compare;
if (marked)
- for_each_ref(clear_marks, NULL);
+ refs_for_each_ref(get_main_ref_store(the_repository),
+ clear_marks, NULL);
marked = 1;
}
diff --git a/notes-cache.c b/notes-cache.c
index 0e1d5b1ac7..ecfdf6e43b 100644
--- a/notes-cache.c
+++ b/notes-cache.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "notes-cache.h"
#include "object-store-ll.h"
@@ -17,7 +19,7 @@ static int notes_cache_match_validity(struct repository *r,
struct strbuf msg = STRBUF_INIT;
int ret;
- if (read_ref(ref, &oid) < 0)
+ if (refs_read_ref(get_main_ref_store(the_repository), ref, &oid) < 0)
return 0;
commit = lookup_commit_reference_gently(r, &oid, 1);
@@ -66,8 +68,8 @@ int notes_cache_write(struct notes_cache *c)
if (commit_tree(c->validity, strlen(c->validity), &tree_oid, NULL,
&commit_oid, NULL, NULL) < 0)
return -1;
- if (update_ref("update notes cache", c->tree.update_ref, &commit_oid,
- NULL, 0, UPDATE_REFS_QUIET_ON_ERR) < 0)
+ if (refs_update_ref(get_main_ref_store(the_repository), "update notes cache", c->tree.update_ref, &commit_oid,
+ NULL, 0, UPDATE_REFS_QUIET_ON_ERR) < 0)
return -1;
return 0;
diff --git a/notes-merge.c b/notes-merge.c
index 51282934ae..dadbbabf86 100644
--- a/notes-merge.c
+++ b/notes-merge.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "advice.h"
#include "commit.h"
@@ -240,7 +242,7 @@ static void diff_tree_local(struct notes_merge_options *o,
* (will be overwritten by following addition)
*/
if (oideq(&mp->local, &uninitialized))
- oidclr(&mp->local);
+ oidclr(&mp->local, the_repository->hash_algo);
} else if (is_null_oid(&p->one->oid)) { /* addition */
/*
* Either this is a true addition (1), or it is part
@@ -556,13 +558,13 @@ int notes_merge(struct notes_merge_options *o,
assert(o->local_ref && o->remote_ref);
assert(!strcmp(o->local_ref, local_tree->ref));
- oidclr(result_oid);
+ oidclr(result_oid, the_repository->hash_algo);
trace_printf("notes_merge(o->local_ref = %s, o->remote_ref = %s)\n",
o->local_ref, o->remote_ref);
/* Dereference o->local_ref into local_sha1 */
- if (read_ref_full(o->local_ref, 0, &local_oid, NULL))
+ if (refs_read_ref_full(get_main_ref_store(the_repository), o->local_ref, 0, &local_oid, NULL))
die("Failed to resolve local notes ref '%s'", o->local_ref);
else if (!check_refname_format(o->local_ref, 0) &&
is_null_oid(&local_oid))
@@ -579,7 +581,7 @@ int notes_merge(struct notes_merge_options *o,
* unborn ref, perform the merge using an empty notes tree.
*/
if (!check_refname_format(o->remote_ref, 0)) {
- oidclr(&remote_oid);
+ oidclr(&remote_oid, the_repository->hash_algo);
remote = NULL;
} else {
die("Failed to resolve remote notes ref '%s'",
@@ -661,6 +663,7 @@ int notes_merge(struct notes_merge_options *o,
commit_list_insert(local, &parents);
create_notes_commit(o->repo, local_tree, parents, o->commit_msg.buf,
o->commit_msg.len, result_oid);
+ free_commit_list(parents);
}
found_result:
diff --git a/notes-utils.c b/notes-utils.c
index 6197a5a455..ac66b82dd3 100644
--- a/notes-utils.c
+++ b/notes-utils.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "config.h"
#include "commit.h"
@@ -9,10 +11,11 @@
void create_notes_commit(struct repository *r,
struct notes_tree *t,
- struct commit_list *parents,
+ const struct commit_list *parents,
const char *msg, size_t msg_len,
struct object_id *result_oid)
{
+ struct commit_list *parents_to_free = NULL;
struct object_id tree_oid;
assert(t->initialized);
@@ -23,11 +26,12 @@ void create_notes_commit(struct repository *r,
if (!parents) {
/* Deduce parent commit from t->ref */
struct object_id parent_oid;
- if (!read_ref(t->ref, &parent_oid)) {
+ if (!refs_read_ref(get_main_ref_store(the_repository), t->ref, &parent_oid)) {
struct commit *parent = lookup_commit(r, &parent_oid);
if (repo_parse_commit(r, parent))
die("Failed to find/parse commit %s", t->ref);
- commit_list_insert(parent, &parents);
+ commit_list_insert(parent, &parents_to_free);
+ parents = parents_to_free;
}
/* else: t->ref points to nothing, assume root/orphan commit */
}
@@ -35,6 +39,8 @@ void create_notes_commit(struct repository *r,
if (commit_tree(msg, msg_len, &tree_oid, parents, result_oid, NULL,
NULL))
die("Failed to commit notes tree to database");
+
+ free_commit_list(parents_to_free);
}
void commit_notes(struct repository *r, struct notes_tree *t, const char *msg)
@@ -55,8 +61,9 @@ void commit_notes(struct repository *r, struct notes_tree *t, const char *msg)
create_notes_commit(r, t, NULL, buf.buf, buf.len, &commit_oid);
strbuf_insertstr(&buf, 0, "notes: ");
- update_ref(buf.buf, t->update_ref, &commit_oid, NULL, 0,
- UPDATE_REFS_DIE_ON_ERR);
+ refs_update_ref(get_main_ref_store(the_repository), buf.buf,
+ t->update_ref, &commit_oid, NULL, 0,
+ UPDATE_REFS_DIE_ON_ERR);
strbuf_release(&buf);
}
@@ -186,6 +193,7 @@ void finish_copy_notes_for_rewrite(struct repository *r,
for (i = 0; c->trees[i]; i++) {
commit_notes(r, c->trees[i], msg);
free_notes(c->trees[i]);
+ free(c->trees[i]);
}
free(c->trees);
free(c);
diff --git a/notes-utils.h b/notes-utils.h
index d9b3c09eaf..c54b1fe141 100644
--- a/notes-utils.h
+++ b/notes-utils.h
@@ -20,7 +20,7 @@ struct repository;
*/
void create_notes_commit(struct repository *r,
struct notes_tree *t,
- struct commit_list *parents,
+ const struct commit_list *parents,
const char *msg, size_t msg_len,
struct object_id *result_oid);
diff --git a/notes.c b/notes.c
index fed1eda80c..1ba6412aae 100644
--- a/notes.c
+++ b/notes.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "config.h"
#include "environment.h"
@@ -149,7 +151,7 @@ static struct leaf_node *note_tree_find(struct notes_tree *t,
void **p = note_tree_search(t, &tree, &n, key_sha1);
if (GET_PTR_TYPE(*p) == PTR_TYPE_NOTE) {
struct leaf_node *l = (struct leaf_node *) CLR_PTR_TYPE(*p);
- if (hasheq(key_sha1, l->key_oid.hash))
+ if (hasheq(key_sha1, l->key_oid.hash, the_repository->hash_algo))
return l;
}
return NULL;
@@ -353,7 +355,7 @@ static void add_non_note(struct notes_tree *t, char *path,
n->next = NULL;
n->path = path;
n->mode = mode;
- oidread(&n->oid, sha1);
+ oidread(&n->oid, sha1, the_repository->hash_algo);
t->prev_non_note = n;
if (!t->first_non_note) {
@@ -427,6 +429,8 @@ static void load_subtree(struct notes_tree *t, struct leaf_node *subtree,
hashsz - prefix_len))
goto handle_non_note; /* entry.path is not a SHA1 */
+ memset(object_oid.hash + hashsz, 0, GIT_MAX_RAWSZ - hashsz);
+
type = PTR_TYPE_NOTE;
} else if (path_len == 2) {
/* This is potentially an internal node */
@@ -945,7 +949,8 @@ void string_list_add_refs_by_glob(struct string_list *list, const char *glob)
{
assert(list->strdup_strings);
if (has_glob_specials(glob)) {
- for_each_glob_ref(string_list_add_one_ref, glob, list);
+ refs_for_each_glob_ref(get_main_ref_store(the_repository),
+ string_list_add_one_ref, glob, list);
} else {
struct object_id oid;
if (repo_get_oid(the_repository, glob, &oid))
@@ -1029,13 +1034,13 @@ void init_notes(struct notes_tree *t, const char *notes_ref,
if (flags & NOTES_INIT_EMPTY ||
repo_get_oid_treeish(the_repository, notes_ref, &object_oid))
return;
- if (flags & NOTES_INIT_WRITABLE && read_ref(notes_ref, &object_oid))
+ if (flags & NOTES_INIT_WRITABLE && refs_read_ref(get_main_ref_store(the_repository), notes_ref, &object_oid))
die("Cannot use notes ref %s", notes_ref);
if (get_tree_entry(the_repository, &object_oid, "", &oid, &mode))
die("Failed to read notes tree referenced by %s (%s)",
notes_ref, oid_to_hex(&object_oid));
- oidclr(&root_tree.key_oid);
+ oidclr(&root_tree.key_oid, the_repository->hash_algo);
oidcpy(&root_tree.val_oid, &oid);
load_subtree(t, &root_tree, t->root, 0);
}
@@ -1059,6 +1064,12 @@ void init_display_notes(struct display_notes_opt *opt)
{
memset(opt, 0, sizeof(*opt));
opt->use_default_notes = -1;
+ string_list_init_dup(&opt->extra_notes_refs);
+}
+
+void release_display_notes(struct display_notes_opt *opt)
+{
+ string_list_clear(&opt->extra_notes_refs, 0);
}
void enable_default_display_notes(struct display_notes_opt *opt, int *show_notes)
@@ -1072,19 +1083,15 @@ void enable_ref_display_notes(struct display_notes_opt *opt, int *show_notes,
struct strbuf buf = STRBUF_INIT;
strbuf_addstr(&buf, ref);
expand_notes_ref(&buf);
- string_list_append(&opt->extra_notes_refs,
- strbuf_detach(&buf, NULL));
+ string_list_append_nodup(&opt->extra_notes_refs,
+ strbuf_detach(&buf, NULL));
*show_notes = 1;
}
void disable_display_notes(struct display_notes_opt *opt, int *show_notes)
{
opt->use_default_notes = -1;
- /* we have been strdup'ing ourselves, so trick
- * string_list into free()ing strings */
- opt->extra_notes_refs.strdup_strings = 1;
string_list_clear(&opt->extra_notes_refs, 0);
- opt->extra_notes_refs.strdup_strings = 0;
*show_notes = 0;
}
@@ -1145,8 +1152,8 @@ int remove_note(struct notes_tree *t, const unsigned char *object_sha1)
if (!t)
t = &default_notes_tree;
assert(t->initialized);
- oidread(&l.key_oid, object_sha1);
- oidclr(&l.val_oid);
+ oidread(&l.key_oid, object_sha1, the_repository->hash_algo);
+ oidclr(&l.val_oid, the_repository->hash_algo);
note_tree_remove(t, t->root, 0, &l);
if (is_null_oid(&l.val_oid)) /* no note was removed */
return 1;
@@ -1216,11 +1223,16 @@ void prune_notes(struct notes_tree *t, int flags)
for_each_note(t, 0, prune_notes_helper, &l);
while (l) {
+ struct note_delete_list *next;
+
if (flags & NOTES_PRUNE_VERBOSE)
printf("%s\n", hash_to_hex(l->sha1));
if (!(flags & NOTES_PRUNE_DRYRUN))
remove_note(t, l->sha1);
- l = l->next;
+
+ next = l->next;
+ free(l);
+ l = next;
}
}
diff --git a/notes.h b/notes.h
index 064fd7143a..235216944b 100644
--- a/notes.h
+++ b/notes.h
@@ -276,6 +276,11 @@ struct display_notes_opt {
void init_display_notes(struct display_notes_opt *opt);
/*
+ * Release resources acquired by the display_notes_opt.
+ */
+void release_display_notes(struct display_notes_opt *opt);
+
+/*
* This family of functions enables or disables the display of notes. In
* particular, 'enable_default_display_notes' will display the default notes,
* 'enable_ref_display_notes' will display the notes ref 'ref' and
diff --git a/object-file-convert.c b/object-file-convert.c
index 4f6189095b..3887d6d57b 100644
--- a/object-file-convert.c
+++ b/object-file-convert.c
@@ -1,9 +1,11 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "gettext.h"
#include "strbuf.h"
#include "hex.h"
#include "repository.h"
-#include "hash-ll.h"
+#include "hash.h"
#include "hash.h"
#include "object.h"
#include "loose.h"
@@ -56,7 +58,7 @@ static int decode_tree_entry_raw(struct object_id *oid, const char **path,
return -1;
*len = strlen(*path) + 1;
- oidread_algop(oid, (const unsigned char *)*path + *len, algo);
+ oidread(oid, (const unsigned char *)*path + *len, algo);
return 0;
}
diff --git a/object-file.c b/object-file.c
index 610b1f465c..065103be3e 100644
--- a/object-file.c
+++ b/object-file.c
@@ -6,6 +6,9 @@
* This handles basic git object files - packing, unpacking,
* creation etc.
*/
+
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "abspath.h"
#include "config.h"
@@ -227,16 +230,10 @@ const struct object_id *null_oid(void)
return the_hash_algo->null_oid;
}
-const char *empty_tree_oid_hex(void)
-{
- static char buf[GIT_MAX_HEXSZ + 1];
- return oid_to_hex_r(buf, the_hash_algo->empty_tree);
-}
-
-const char *empty_blob_oid_hex(void)
+const char *empty_tree_oid_hex(const struct git_hash_algo *algop)
{
static char buf[GIT_MAX_HEXSZ + 1];
- return oid_to_hex_r(buf, the_hash_algo->empty_blob);
+ return oid_to_hex_r(buf, algop->empty_tree);
}
int hash_algo_by_name(const char *name)
@@ -277,7 +274,7 @@ int hash_algo_by_length(int len)
static struct cached_object {
struct object_id oid;
enum object_type type;
- void *buf;
+ const void *buf;
unsigned long size;
} *cached_objects;
static int cached_object_nr, cached_object_alloc;
@@ -1446,7 +1443,7 @@ static int loose_object_info(struct repository *r,
int allow_unknown = flags & OBJECT_INFO_ALLOW_UNKNOWN_TYPE;
if (oi->delta_base_oid)
- oidclr(oi->delta_base_oid);
+ oidclr(oi->delta_base_oid, the_repository->hash_algo);
/*
* If we don't care about type or size, then we don't
@@ -1580,7 +1577,7 @@ static int do_oid_object_info_extended(struct repository *r,
if (oi->disk_sizep)
*(oi->disk_sizep) = 0;
if (oi->delta_base_oid)
- oidclr(oi->delta_base_oid);
+ oidclr(oi->delta_base_oid, the_repository->hash_algo);
if (oi->type_name)
strbuf_addstr(oi->type_name, type_name(co->type));
if (oi->contentp)
@@ -1711,9 +1708,9 @@ static int oid_object_info_convert(struct repository *r,
ret = convert_object_file(&outbuf,
the_hash_algo, input_algo,
content, size, type, !do_die);
+ free(content);
if (ret == -1)
return -1;
- free(content);
size = outbuf.len;
content = strbuf_detach(&outbuf, NULL);
}
@@ -1778,6 +1775,7 @@ int pretend_object_file(void *buf, unsigned long len, enum object_type type,
struct object_id *oid)
{
struct cached_object *co;
+ char *co_buf;
hash_object_file(the_hash_algo, buf, len, type, oid);
if (repo_has_object_file_with_flags(the_repository, oid, OBJECT_INFO_QUICK | OBJECT_INFO_SKIP_FETCH_OBJECT) ||
@@ -1787,8 +1785,9 @@ int pretend_object_file(void *buf, unsigned long len, enum object_type type,
co = &cached_objects[cached_object_nr++];
co->size = len;
co->type = type;
- co->buf = xmalloc(len);
- memcpy(co->buf, buf, len);
+ co_buf = xmalloc(len);
+ memcpy(co_buf, buf, len);
+ co->buf = co_buf;
oidcpy(&co->oid, oid);
return 0;
}
@@ -2482,12 +2481,13 @@ static int hash_format_check_report(struct fsck_options *opts UNUSED,
}
static int index_mem(struct index_state *istate,
- struct object_id *oid, void *buf, size_t size,
+ struct object_id *oid,
+ const void *buf, size_t size,
enum object_type type,
const char *path, unsigned flags)
{
+ struct strbuf nbuf = STRBUF_INIT;
int ret = 0;
- int re_allocated = 0;
int write_object = flags & HASH_WRITE_OBJECT;
if (!type)
@@ -2497,11 +2497,10 @@ static int index_mem(struct index_state *istate,
* Convert blobs to git internal format
*/
if ((type == OBJ_BLOB) && path) {
- struct strbuf nbuf = STRBUF_INIT;
if (convert_to_git(istate, path, buf, size, &nbuf,
get_conv_flags(flags))) {
- buf = strbuf_detach(&nbuf, &size);
- re_allocated = 1;
+ buf = nbuf.buf;
+ size = nbuf.len;
}
}
if (flags & HASH_FORMAT_CHECK) {
@@ -2518,8 +2517,8 @@ static int index_mem(struct index_state *istate,
ret = write_object_file(buf, size, type, oid);
else
hash_object_file(the_hash_algo, buf, size, type, oid);
- if (re_allocated)
- free(buf);
+
+ strbuf_release(&nbuf);
return ret;
}
@@ -2669,7 +2668,7 @@ int index_path(struct index_state *istate, struct object_id *oid,
strbuf_release(&sb);
break;
case S_IFDIR:
- return resolve_gitlink_ref(path, "HEAD", oid);
+ return repo_resolve_gitlink_ref(the_repository, path, "HEAD", oid);
default:
return error(_("%s: unsupported file type"), path);
}
@@ -2743,6 +2742,8 @@ int for_each_file_in_obj_subdir(unsigned int subdir_nr,
!hex_to_bytes(oid.hash + 1, de->d_name,
the_hash_algo->rawsz - 1)) {
oid_set_algo(&oid, the_hash_algo);
+ memset(oid.hash + the_hash_algo->rawsz, 0,
+ GIT_MAX_RAWSZ - the_hash_algo->rawsz);
if (obj_cb) {
r = obj_cb(&oid, path->buf, data);
if (r)
diff --git a/object-name.c b/object-name.c
index 523af6f64f..527b853ac4 100644
--- a/object-name.c
+++ b/object-name.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "object-name.h"
#include "advice.h"
@@ -837,7 +839,7 @@ int repo_find_unique_abbrev_r(struct repository *r, char *hex,
}
oid_to_hex_r(hex, oid);
- if (len == hexsz || !len)
+ if (len >= hexsz || !len)
return hexsz;
mad.repo = r;
@@ -1757,6 +1759,11 @@ int strbuf_check_branch_ref(struct strbuf *sb, const char *name)
return check_refname_format(sb->buf, 0);
}
+void object_context_release(struct object_context *ctx)
+{
+ free(ctx->path);
+}
+
/*
* This is like "get_oid_basic()", except it allows "object ID expressions",
* notably "xyz^" for "parent of xyz"
@@ -1764,7 +1771,9 @@ int strbuf_check_branch_ref(struct strbuf *sb, const char *name)
int repo_get_oid(struct repository *r, const char *name, struct object_id *oid)
{
struct object_context unused;
- return get_oid_with_context(r, name, 0, oid, &unused);
+ int ret = get_oid_with_context(r, name, 0, oid, &unused);
+ object_context_release(&unused);
+ return ret;
}
/*
@@ -1802,8 +1811,10 @@ int repo_get_oid_committish(struct repository *r,
struct object_id *oid)
{
struct object_context unused;
- return get_oid_with_context(r, name, GET_OID_COMMITTISH,
- oid, &unused);
+ int ret = get_oid_with_context(r, name, GET_OID_COMMITTISH,
+ oid, &unused);
+ object_context_release(&unused);
+ return ret;
}
int repo_get_oid_treeish(struct repository *r,
@@ -1811,8 +1822,10 @@ int repo_get_oid_treeish(struct repository *r,
struct object_id *oid)
{
struct object_context unused;
- return get_oid_with_context(r, name, GET_OID_TREEISH,
- oid, &unused);
+ int ret = get_oid_with_context(r, name, GET_OID_TREEISH,
+ oid, &unused);
+ object_context_release(&unused);
+ return ret;
}
int repo_get_oid_commit(struct repository *r,
@@ -1820,8 +1833,10 @@ int repo_get_oid_commit(struct repository *r,
struct object_id *oid)
{
struct object_context unused;
- return get_oid_with_context(r, name, GET_OID_COMMIT,
- oid, &unused);
+ int ret = get_oid_with_context(r, name, GET_OID_COMMIT,
+ oid, &unused);
+ object_context_release(&unused);
+ return ret;
}
int repo_get_oid_tree(struct repository *r,
@@ -1829,8 +1844,10 @@ int repo_get_oid_tree(struct repository *r,
struct object_id *oid)
{
struct object_context unused;
- return get_oid_with_context(r, name, GET_OID_TREE,
- oid, &unused);
+ int ret = get_oid_with_context(r, name, GET_OID_TREE,
+ oid, &unused);
+ object_context_release(&unused);
+ return ret;
}
int repo_get_oid_blob(struct repository *r,
@@ -1838,8 +1855,10 @@ int repo_get_oid_blob(struct repository *r,
struct object_id *oid)
{
struct object_context unused;
- return get_oid_with_context(r, name, GET_OID_BLOB,
- oid, &unused);
+ int ret = get_oid_with_context(r, name, GET_OID_BLOB,
+ oid, &unused);
+ object_context_release(&unused);
+ return ret;
}
/* Must be called only when object_name:filename doesn't exist. */
@@ -2117,6 +2136,7 @@ void maybe_die_on_misspelt_object_name(struct repository *r,
struct object_id oid;
get_oid_with_context_1(r, name, GET_OID_ONLY_TO_DIE | GET_OID_QUIETLY,
prefix, &oid, &oc);
+ object_context_release(&oc);
}
enum get_oid_result get_oid_with_context(struct repository *repo,
diff --git a/object-name.h b/object-name.h
index 064ddc97d1..8dba4a47a4 100644
--- a/object-name.h
+++ b/object-name.h
@@ -22,6 +22,8 @@ struct object_context {
char *path;
};
+void object_context_release(struct object_context *ctx);
+
/*
* Return an abbreviated sha1 unique within this repository's object database.
* The result will be at least `len` characters long, and will be NUL
diff --git a/object.c b/object.c
index 51e384828e..0c0fcb76c0 100644
--- a/object.c
+++ b/object.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "gettext.h"
#include "hex.h"
@@ -207,6 +209,29 @@ struct object *lookup_object_by_type(struct repository *r,
}
}
+enum peel_status peel_object(struct repository *r,
+ const struct object_id *name,
+ struct object_id *oid)
+{
+ struct object *o = lookup_unknown_object(r, name);
+
+ if (o->type == OBJ_NONE) {
+ int type = oid_object_info(r, name, NULL);
+ if (type < 0 || !object_as_type(o, type, 0))
+ return PEEL_INVALID;
+ }
+
+ if (o->type != OBJ_TAG)
+ return PEEL_NON_TAG;
+
+ o = deref_tag_noverify(r, o);
+ if (!o)
+ return PEEL_INVALID;
+
+ oidcpy(oid, &o->oid);
+ return PEEL_PEELED;
+}
+
struct object *parse_object_buffer(struct repository *r, const struct object_id *oid, enum object_type type, unsigned long size, void *buffer, int *eaten_p)
{
struct object *obj;
diff --git a/object.h b/object.h
index 9293e703cc..05691486eb 100644
--- a/object.h
+++ b/object.h
@@ -1,7 +1,7 @@
#ifndef OBJECT_H
#define OBJECT_H
-#include "hash-ll.h"
+#include "hash.h"
struct buffer_slab;
struct repository;
@@ -62,7 +62,7 @@ void object_array_init(struct object_array *array);
/*
* object flag allocation:
- * revision.h: 0---------10 15 23------27
+ * revision.h: 0---------10 15 23------27
* fetch-pack.c: 01 67
* negotiator/default.c: 2--5
* walker.c: 0-2
@@ -75,12 +75,14 @@ void object_array_init(struct object_array *array);
* commit-reach.c: 16-----19
* sha1-name.c: 20
* list-objects-filter.c: 21
+ * bloom.c: 2122
* builtin/fsck.c: 0--3
* builtin/gc.c: 0
* builtin/index-pack.c: 2021
* reflog.c: 10--12
* builtin/show-branch.c: 0-------------------------------------------26
* builtin/unpack-objects.c: 2021
+ * pack-bitmap.h: 2122
*/
#define FLAG_BITS 28
@@ -256,6 +258,41 @@ struct object *lookup_unknown_object(struct repository *r, const struct object_i
struct object *lookup_object_by_type(struct repository *r, const struct object_id *oid,
enum object_type type);
+enum peel_status {
+ /* object was peeled successfully: */
+ PEEL_PEELED = 0,
+
+ /*
+ * object cannot be peeled because the named object (or an
+ * object referred to by a tag in the peel chain), does not
+ * exist.
+ */
+ PEEL_INVALID = -1,
+
+ /* object cannot be peeled because it is not a tag: */
+ PEEL_NON_TAG = -2,
+
+ /* ref_entry contains no peeled value because it is a symref: */
+ PEEL_IS_SYMREF = -3,
+
+ /*
+ * ref_entry cannot be peeled because it is broken (i.e., the
+ * symbolic reference cannot even be resolved to an object
+ * name):
+ */
+ PEEL_BROKEN = -4
+};
+
+/*
+ * Peel the named object; i.e., if the object is a tag, resolve the
+ * tag recursively until a non-tag is found. If successful, store the
+ * result to oid and return PEEL_PEELED. If the object is not a tag
+ * or is not valid, return PEEL_NON_TAG or PEEL_INVALID, respectively,
+ * and leave oid unchanged.
+ */
+enum peel_status peel_object(struct repository *r,
+ const struct object_id *name, struct object_id *oid);
+
struct object_list *object_list_insert(struct object *item,
struct object_list **list_p);
diff --git a/oid-array.c b/oid-array.c
index 1f36651754..9cac974395 100644
--- a/oid-array.c
+++ b/oid-array.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "oid-array.h"
#include "hash-lookup.h"
diff --git a/oidmap.h b/oidmap.h
index 05c673eb7c..fad412827a 100644
--- a/oidmap.h
+++ b/oidmap.h
@@ -1,7 +1,7 @@
#ifndef OIDMAP_H
#define OIDMAP_H
-#include "hash-ll.h"
+#include "hash.h"
#include "hashmap.h"
/*
diff --git a/oidset.c b/oidset.c
index 91d1385910..8d36aef8dc 100644
--- a/oidset.c
+++ b/oidset.c
@@ -48,12 +48,14 @@ void oidset_clear(struct oidset *set)
oidset_init(set, 0);
}
-void oidset_parse_file(struct oidset *set, const char *path)
+void oidset_parse_file(struct oidset *set, const char *path,
+ const struct git_hash_algo *algop)
{
- oidset_parse_file_carefully(set, path, NULL, NULL);
+ oidset_parse_file_carefully(set, path, algop, NULL, NULL);
}
void oidset_parse_file_carefully(struct oidset *set, const char *path,
+ const struct git_hash_algo *algop,
oidset_parse_tweak_fn fn, void *cbdata)
{
FILE *fp;
@@ -79,7 +81,7 @@ void oidset_parse_file_carefully(struct oidset *set, const char *path,
if (!sb.len)
continue;
- if (parse_oid_hex(sb.buf, &oid, &p) || *p != '\0')
+ if (parse_oid_hex_algop(sb.buf, &oid, &p, algop) || *p != '\0')
die("invalid object name: %s", sb.buf);
if (fn && fn(&oid, cbdata))
continue;
diff --git a/oidset.h b/oidset.h
index 262f4256d6..0106b6f278 100644
--- a/oidset.h
+++ b/oidset.h
@@ -80,7 +80,8 @@ void oidset_clear(struct oidset *set);
* are allowed. Leading whitespace and empty or white-space only lines are
* ignored.
*/
-void oidset_parse_file(struct oidset *set, const char *path);
+void oidset_parse_file(struct oidset *set, const char *path,
+ const struct git_hash_algo *algop);
/*
* Similar to the above, but with a callback which can (1) return non-zero to
@@ -89,6 +90,7 @@ void oidset_parse_file(struct oidset *set, const char *path);
*/
typedef int (*oidset_parse_tweak_fn)(struct object_id *, void *);
void oidset_parse_file_carefully(struct oidset *set, const char *path,
+ const struct git_hash_algo *algop,
oidset_parse_tweak_fn fn, void *cbdata);
struct oidset_iter {
diff --git a/oidtree.c b/oidtree.c
index daef175dc7..92d03b52db 100644
--- a/oidtree.c
+++ b/oidtree.c
@@ -42,7 +42,7 @@ void oidtree_insert(struct oidtree *ot, const struct object_id *oid)
* Clear the padding and copy the result in separate steps to
* respect the 4-byte alignment needed by struct object_id.
*/
- oidcpy_with_padding(&k, oid);
+ oidcpy(&k, oid);
memcpy(on->k, &k, sizeof(k));
/*
@@ -60,7 +60,7 @@ int oidtree_contains(struct oidtree *ot, const struct object_id *oid)
struct object_id k;
size_t klen = sizeof(k);
- oidcpy_with_padding(&k, oid);
+ oidcpy(&k, oid);
if (oid->algo == GIT_HASH_UNKNOWN)
klen -= sizeof(oid->algo);
diff --git a/oidtree.h b/oidtree.h
index 55c83513fd..77898f510a 100644
--- a/oidtree.h
+++ b/oidtree.h
@@ -2,7 +2,7 @@
#define OIDTREE_H
#include "cbtree.h"
-#include "hash-ll.h"
+#include "hash.h"
#include "mem-pool.h"
struct oidtree {
diff --git a/oss-fuzz/fuzz-commit-graph.c b/oss-fuzz/fuzz-commit-graph.c
index 2992079dd9..fbb77fec19 100644
--- a/oss-fuzz/fuzz-commit-graph.c
+++ b/oss-fuzz/fuzz-commit-graph.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "commit-graph.h"
#include "repository.h"
@@ -11,15 +13,17 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
{
struct commit_graph *g;
- initialize_the_repository();
+ initialize_repository(the_repository);
+
/*
* Initialize the_repository with commit-graph settings that would
* normally be read from the repository's gitdir. We want to avoid
* touching the disk to keep the individual fuzz-test cases as fast as
* possible.
*/
+ repo_set_hash_algo(the_repository, GIT_HASH_SHA1);
the_repository->settings.commit_graph_generation_version = 2;
- the_repository->settings.commit_graph_read_changed_paths = 1;
+ the_repository->settings.commit_graph_changed_paths_version = 1;
g = parse_commit_graph(&the_repository->settings, (void *)data, size);
repo_clear(the_repository);
free_commit_graph(g);
diff --git a/pack-bitmap-write.c b/pack-bitmap-write.c
index c6c8f94cc5..bf96c80898 100644
--- a/pack-bitmap-write.c
+++ b/pack-bitmap-write.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "environment.h"
#include "gettext.h"
@@ -17,6 +19,12 @@
#include "trace2.h"
#include "tree.h"
#include "tree-walk.h"
+#include "pseudo-merge.h"
+#include "oid-array.h"
+#include "config.h"
+#include "alloc.h"
+#include "refs.h"
+#include "strmap.h"
struct bitmapped_commit {
struct commit *commit;
@@ -25,45 +33,82 @@ struct bitmapped_commit {
int flags;
int xor_offset;
uint32_t commit_pos;
+ unsigned pseudo_merge : 1;
};
-struct bitmap_writer {
- struct ewah_bitmap *commits;
- struct ewah_bitmap *trees;
- struct ewah_bitmap *blobs;
- struct ewah_bitmap *tags;
+static inline int bitmap_writer_nr_selected_commits(struct bitmap_writer *writer)
+{
+ return writer->selected_nr - writer->pseudo_merges_nr;
+}
- kh_oid_map_t *bitmaps;
- struct packing_data *to_pack;
+void bitmap_writer_init(struct bitmap_writer *writer, struct repository *r)
+{
+ memset(writer, 0, sizeof(struct bitmap_writer));
+ if (writer->bitmaps)
+ BUG("bitmap writer already initialized");
+ writer->bitmaps = kh_init_oid_map();
+ writer->pseudo_merge_commits = kh_init_oid_map();
- struct bitmapped_commit *selected;
- unsigned int selected_nr, selected_alloc;
+ string_list_init_dup(&writer->pseudo_merge_groups);
- struct progress *progress;
- int show_progress;
- unsigned char pack_checksum[GIT_MAX_RAWSZ];
-};
+ load_pseudo_merges_from_config(&writer->pseudo_merge_groups);
+}
+
+static void free_pseudo_merge_commit_idx(struct pseudo_merge_commit_idx *idx)
+{
+ if (!idx)
+ return;
+ free(idx->pseudo_merge);
+ free(idx);
+}
+
+void bitmap_writer_free(struct bitmap_writer *writer)
+{
+ uint32_t i;
+ struct pseudo_merge_commit_idx *idx;
+
+ if (!writer)
+ return;
+
+ ewah_free(writer->commits);
+ ewah_free(writer->trees);
+ ewah_free(writer->blobs);
+ ewah_free(writer->tags);
+
+ kh_destroy_oid_map(writer->bitmaps);
-static struct bitmap_writer writer;
+ kh_foreach_value(writer->pseudo_merge_commits, idx,
+ free_pseudo_merge_commit_idx(idx));
+ kh_destroy_oid_map(writer->pseudo_merge_commits);
-void bitmap_writer_show_progress(int show)
+ for (i = 0; i < writer->selected_nr; i++) {
+ struct bitmapped_commit *bc = &writer->selected[i];
+ if (bc->write_as != bc->bitmap)
+ ewah_free(bc->write_as);
+ ewah_free(bc->bitmap);
+ }
+ free(writer->selected);
+}
+
+void bitmap_writer_show_progress(struct bitmap_writer *writer, int show)
{
- writer.show_progress = show;
+ writer->show_progress = show;
}
/**
* Build the initial type index for the packfile or multi-pack-index
*/
-void bitmap_writer_build_type_index(struct packing_data *to_pack,
+void bitmap_writer_build_type_index(struct bitmap_writer *writer,
+ struct packing_data *to_pack,
struct pack_idx_entry **index,
uint32_t index_nr)
{
uint32_t i;
- writer.commits = ewah_new();
- writer.trees = ewah_new();
- writer.blobs = ewah_new();
- writer.tags = ewah_new();
+ writer->commits = ewah_new();
+ writer->trees = ewah_new();
+ writer->blobs = ewah_new();
+ writer->tags = ewah_new();
ALLOC_ARRAY(to_pack->in_pack_pos, to_pack->nr_objects);
for (i = 0; i < index_nr; ++i) {
@@ -88,19 +133,19 @@ void bitmap_writer_build_type_index(struct packing_data *to_pack,
switch (real_type) {
case OBJ_COMMIT:
- ewah_set(writer.commits, i);
+ ewah_set(writer->commits, i);
break;
case OBJ_TREE:
- ewah_set(writer.trees, i);
+ ewah_set(writer->trees, i);
break;
case OBJ_BLOB:
- ewah_set(writer.blobs, i);
+ ewah_set(writer->blobs, i);
break;
case OBJ_TAG:
- ewah_set(writer.tags, i);
+ ewah_set(writer->tags, i);
break;
default:
@@ -111,27 +156,49 @@ void bitmap_writer_build_type_index(struct packing_data *to_pack,
}
}
+int bitmap_writer_has_bitmapped_object_id(struct bitmap_writer *writer,
+ const struct object_id *oid)
+{
+ return kh_get_oid_map(writer->bitmaps, *oid) != kh_end(writer->bitmaps);
+}
+
/**
* Compute the actual bitmaps
*/
-static inline void push_bitmapped_commit(struct commit *commit)
+void bitmap_writer_push_commit(struct bitmap_writer *writer,
+ struct commit *commit, unsigned pseudo_merge)
{
- if (writer.selected_nr >= writer.selected_alloc) {
- writer.selected_alloc = (writer.selected_alloc + 32) * 2;
- REALLOC_ARRAY(writer.selected, writer.selected_alloc);
+ if (writer->selected_nr >= writer->selected_alloc) {
+ writer->selected_alloc = (writer->selected_alloc + 32) * 2;
+ REALLOC_ARRAY(writer->selected, writer->selected_alloc);
+ }
+
+ if (!pseudo_merge) {
+ int hash_ret;
+ khiter_t hash_pos = kh_put_oid_map(writer->bitmaps,
+ commit->object.oid,
+ &hash_ret);
+
+ if (!hash_ret)
+ die(_("duplicate entry when writing bitmap index: %s"),
+ oid_to_hex(&commit->object.oid));
+ kh_value(writer->bitmaps, hash_pos) = NULL;
}
- writer.selected[writer.selected_nr].commit = commit;
- writer.selected[writer.selected_nr].bitmap = NULL;
- writer.selected[writer.selected_nr].flags = 0;
+ writer->selected[writer->selected_nr].commit = commit;
+ writer->selected[writer->selected_nr].bitmap = NULL;
+ writer->selected[writer->selected_nr].write_as = NULL;
+ writer->selected[writer->selected_nr].flags = 0;
+ writer->selected[writer->selected_nr].pseudo_merge = pseudo_merge;
- writer.selected_nr++;
+ writer->selected_nr++;
}
-static uint32_t find_object_pos(const struct object_id *oid, int *found)
+static uint32_t find_object_pos(struct bitmap_writer *writer,
+ const struct object_id *oid, int *found)
{
- struct object_entry *entry = packlist_find(writer.to_pack, oid);
+ struct object_entry *entry = packlist_find(writer->to_pack, oid);
if (!entry) {
if (found)
@@ -143,30 +210,34 @@ static uint32_t find_object_pos(const struct object_id *oid, int *found)
if (found)
*found = 1;
- return oe_in_pack_pos(writer.to_pack, entry);
+ return oe_in_pack_pos(writer->to_pack, entry);
}
-static void compute_xor_offsets(void)
+static void compute_xor_offsets(struct bitmap_writer *writer)
{
static const int MAX_XOR_OFFSET_SEARCH = 10;
int i, next = 0;
- while (next < writer.selected_nr) {
- struct bitmapped_commit *stored = &writer.selected[next];
-
+ while (next < writer->selected_nr) {
+ struct bitmapped_commit *stored = &writer->selected[next];
int best_offset = 0;
struct ewah_bitmap *best_bitmap = stored->bitmap;
struct ewah_bitmap *test_xor;
+ if (stored->pseudo_merge)
+ goto next;
+
for (i = 1; i <= MAX_XOR_OFFSET_SEARCH; ++i) {
int curr = next - i;
if (curr < 0)
break;
+ if (writer->selected[curr].pseudo_merge)
+ continue;
test_xor = ewah_pool_new();
- ewah_xor(writer.selected[curr].bitmap, stored->bitmap, test_xor);
+ ewah_xor(writer->selected[curr].bitmap, stored->bitmap, test_xor);
if (test_xor->buffer_size < best_bitmap->buffer_size) {
if (best_bitmap != stored->bitmap)
@@ -179,6 +250,7 @@ static void compute_xor_offsets(void)
}
}
+next:
stored->xor_offset = best_offset;
stored->write_as = best_bitmap;
@@ -191,7 +263,8 @@ struct bb_commit {
struct bitmap *commit_mask;
struct bitmap *bitmap;
unsigned selected:1,
- maximal:1;
+ maximal:1,
+ pseudo_merge:1;
unsigned idx; /* within selected array */
};
@@ -229,17 +302,18 @@ static void bitmap_builder_init(struct bitmap_builder *bb,
revs.first_parent_only = 1;
for (i = 0; i < writer->selected_nr; i++) {
- struct commit *c = writer->selected[i].commit;
- struct bb_commit *ent = bb_data_at(&bb->data, c);
+ struct bitmapped_commit *bc = &writer->selected[i];
+ struct bb_commit *ent = bb_data_at(&bb->data, bc->commit);
ent->selected = 1;
ent->maximal = 1;
+ ent->pseudo_merge = bc->pseudo_merge;
ent->idx = i;
ent->commit_mask = bitmap_new();
bitmap_set(ent->commit_mask, i);
- add_pending_object(&revs, &c->object, "");
+ add_pending_object(&revs, &bc->commit->object, "");
}
if (prepare_revision_walk(&revs))
@@ -348,7 +422,8 @@ static void bitmap_builder_clear(struct bitmap_builder *bb)
bb->commits_nr = bb->commits_alloc = 0;
}
-static int fill_bitmap_tree(struct bitmap *bitmap,
+static int fill_bitmap_tree(struct bitmap_writer *writer,
+ struct bitmap *bitmap,
struct tree *tree)
{
int found;
@@ -360,7 +435,7 @@ static int fill_bitmap_tree(struct bitmap *bitmap,
* If our bit is already set, then there is nothing to do. Both this
* tree and all of its children will be set.
*/
- pos = find_object_pos(&tree->object.oid, &found);
+ pos = find_object_pos(writer, &tree->object.oid, &found);
if (!found)
return -1;
if (bitmap_get(bitmap, pos))
@@ -375,12 +450,12 @@ static int fill_bitmap_tree(struct bitmap *bitmap,
while (tree_entry(&desc, &entry)) {
switch (object_type(entry.mode)) {
case OBJ_TREE:
- if (fill_bitmap_tree(bitmap,
+ if (fill_bitmap_tree(writer, bitmap,
lookup_tree(the_repository, &entry.oid)) < 0)
return -1;
break;
case OBJ_BLOB:
- pos = find_object_pos(&entry.oid, &found);
+ pos = find_object_pos(writer, &entry.oid, &found);
if (!found)
return -1;
bitmap_set(bitmap, pos);
@@ -396,8 +471,10 @@ static int fill_bitmap_tree(struct bitmap *bitmap,
}
static int reused_bitmaps_nr;
+static int reused_pseudo_merge_bitmaps_nr;
-static int fill_bitmap_commit(struct bb_commit *ent,
+static int fill_bitmap_commit(struct bitmap_writer *writer,
+ struct bb_commit *ent,
struct commit *commit,
struct prio_queue *queue,
struct prio_queue *tree_queue,
@@ -416,8 +493,13 @@ static int fill_bitmap_commit(struct bb_commit *ent,
struct commit *c = prio_queue_get(queue);
if (old_bitmap && mapping) {
- struct ewah_bitmap *old = bitmap_for_commit(old_bitmap, c);
+ struct ewah_bitmap *old;
struct bitmap *remapped = bitmap_new();
+
+ if (commit->object.flags & BITMAP_PSEUDO_MERGE)
+ old = pseudo_merge_bitmap_for_commit(old_bitmap, c);
+ else
+ old = bitmap_for_commit(old_bitmap, c);
/*
* If this commit has an old bitmap, then translate that
* bitmap and add its bits to this one. No need to walk
@@ -426,7 +508,10 @@ static int fill_bitmap_commit(struct bb_commit *ent,
if (old && !rebuild_bitmap(mapping, old, remapped)) {
bitmap_or(ent->bitmap, remapped);
bitmap_free(remapped);
- reused_bitmaps_nr++;
+ if (commit->object.flags & BITMAP_PSEUDO_MERGE)
+ reused_pseudo_merge_bitmaps_nr++;
+ else
+ reused_bitmaps_nr++;
continue;
}
bitmap_free(remapped);
@@ -436,15 +521,18 @@ static int fill_bitmap_commit(struct bb_commit *ent,
* Mark ourselves and queue our tree. The commit
* walk ensures we cover all parents.
*/
- pos = find_object_pos(&c->object.oid, &found);
- if (!found)
- return -1;
- bitmap_set(ent->bitmap, pos);
- prio_queue_put(tree_queue,
- repo_get_commit_tree(the_repository, c));
+ if (!(c->object.flags & BITMAP_PSEUDO_MERGE)) {
+ pos = find_object_pos(writer, &c->object.oid, &found);
+ if (!found)
+ return -1;
+ bitmap_set(ent->bitmap, pos);
+ prio_queue_put(tree_queue,
+ repo_get_commit_tree(the_repository, c));
+ }
for (p = c->parents; p; p = p->next) {
- pos = find_object_pos(&p->item->object.oid, &found);
+ pos = find_object_pos(writer, &p->item->object.oid,
+ &found);
if (!found)
return -1;
if (!bitmap_get(ent->bitmap, pos)) {
@@ -455,29 +543,34 @@ static int fill_bitmap_commit(struct bb_commit *ent,
}
while (tree_queue->nr) {
- if (fill_bitmap_tree(ent->bitmap,
+ if (fill_bitmap_tree(writer, ent->bitmap,
prio_queue_get(tree_queue)) < 0)
return -1;
}
return 0;
}
-static void store_selected(struct bb_commit *ent, struct commit *commit)
+static void store_selected(struct bitmap_writer *writer,
+ struct bb_commit *ent, struct commit *commit)
{
- struct bitmapped_commit *stored = &writer.selected[ent->idx];
+ struct bitmapped_commit *stored = &writer->selected[ent->idx];
khiter_t hash_pos;
- int hash_ret;
stored->bitmap = bitmap_to_ewah(ent->bitmap);
- hash_pos = kh_put_oid_map(writer.bitmaps, commit->object.oid, &hash_ret);
- if (hash_ret == 0)
- die("Duplicate entry when writing index: %s",
+ if (ent->pseudo_merge)
+ return;
+
+ hash_pos = kh_get_oid_map(writer->bitmaps, commit->object.oid);
+ if (hash_pos == kh_end(writer->bitmaps))
+ die(_("attempted to store non-selected commit: '%s'"),
oid_to_hex(&commit->object.oid));
- kh_value(writer.bitmaps, hash_pos) = stored;
+
+ kh_value(writer->bitmaps, hash_pos) = stored;
}
-int bitmap_writer_build(struct packing_data *to_pack)
+int bitmap_writer_build(struct bitmap_writer *writer,
+ struct packing_data *to_pack)
{
struct bitmap_builder bb;
size_t i;
@@ -488,11 +581,11 @@ int bitmap_writer_build(struct packing_data *to_pack)
uint32_t *mapping;
int closed = 1; /* until proven otherwise */
- writer.bitmaps = kh_init_oid_map();
- writer.to_pack = to_pack;
+ writer->to_pack = to_pack;
- if (writer.show_progress)
- writer.progress = start_progress("Building bitmaps", writer.selected_nr);
+ if (writer->show_progress)
+ writer->progress = start_progress("Building bitmaps",
+ writer->selected_nr);
trace2_region_enter("pack-bitmap-write", "building_bitmaps_total",
the_repository);
@@ -502,23 +595,23 @@ int bitmap_writer_build(struct packing_data *to_pack)
else
mapping = NULL;
- bitmap_builder_init(&bb, &writer, old_bitmap);
+ bitmap_builder_init(&bb, writer, old_bitmap);
for (i = bb.commits_nr; i > 0; i--) {
struct commit *commit = bb.commits[i-1];
struct bb_commit *ent = bb_data_at(&bb.data, commit);
struct commit *child;
int reused = 0;
- if (fill_bitmap_commit(ent, commit, &queue, &tree_queue,
+ if (fill_bitmap_commit(writer, ent, commit, &queue, &tree_queue,
old_bitmap, mapping) < 0) {
closed = 0;
break;
}
if (ent->selected) {
- store_selected(ent, commit);
+ store_selected(writer, ent, commit);
nr_stored++;
- display_progress(writer.progress, nr_stored);
+ display_progress(writer->progress, nr_stored);
}
while ((child = pop_commit(&ent->reverse_edges))) {
@@ -548,11 +641,14 @@ int bitmap_writer_build(struct packing_data *to_pack)
the_repository);
trace2_data_intmax("pack-bitmap-write", the_repository,
"building_bitmaps_reused", reused_bitmaps_nr);
+ trace2_data_intmax("pack-bitmap-write", the_repository,
+ "building_bitmaps_pseudo_merge_reused",
+ reused_pseudo_merge_bitmaps_nr);
- stop_progress(&writer.progress);
+ stop_progress(&writer->progress);
if (closed)
- compute_xor_offsets();
+ compute_xor_offsets(writer);
return closed ? 0 : -1;
}
@@ -590,9 +686,9 @@ static int date_compare(const void *_a, const void *_b)
return (long)b->date - (long)a->date;
}
-void bitmap_writer_select_commits(struct commit **indexed_commits,
- unsigned int indexed_commits_nr,
- int max_bitmaps)
+void bitmap_writer_select_commits(struct bitmap_writer *writer,
+ struct commit **indexed_commits,
+ unsigned int indexed_commits_nr)
{
unsigned int i = 0, j, next;
@@ -600,12 +696,12 @@ void bitmap_writer_select_commits(struct commit **indexed_commits,
if (indexed_commits_nr < 100) {
for (i = 0; i < indexed_commits_nr; ++i)
- push_bitmapped_commit(indexed_commits[i]);
+ bitmap_writer_push_commit(writer, indexed_commits[i], 0);
return;
}
- if (writer.show_progress)
- writer.progress = start_progress("Selecting bitmap commits", 0);
+ if (writer->show_progress)
+ writer->progress = start_progress("Selecting bitmap commits", 0);
for (;;) {
struct commit *chosen = NULL;
@@ -615,11 +711,6 @@ void bitmap_writer_select_commits(struct commit **indexed_commits,
if (i + next >= indexed_commits_nr)
break;
- if (max_bitmaps > 0 && writer.selected_nr >= max_bitmaps) {
- writer.selected_nr = max_bitmaps;
- break;
- }
-
if (next == 0) {
chosen = indexed_commits[i];
} else {
@@ -638,13 +729,15 @@ void bitmap_writer_select_commits(struct commit **indexed_commits,
}
}
- push_bitmapped_commit(chosen);
+ bitmap_writer_push_commit(writer, chosen, 0);
i += next + 1;
- display_progress(writer.progress, i);
+ display_progress(writer->progress, i);
}
- stop_progress(&writer.progress);
+ stop_progress(&writer->progress);
+
+ select_pseudo_merges(writer, indexed_commits, indexed_commits_nr);
}
@@ -670,19 +763,21 @@ static const struct object_id *oid_access(size_t pos, const void *table)
return &index[pos]->oid;
}
-static void write_selected_commits_v1(struct hashfile *f,
- uint32_t *commit_positions,
- off_t *offsets)
+static void write_selected_commits_v1(struct bitmap_writer *writer,
+ struct hashfile *f, off_t *offsets)
{
int i;
- for (i = 0; i < writer.selected_nr; ++i) {
- struct bitmapped_commit *stored = &writer.selected[i];
+ for (i = 0; i < bitmap_writer_nr_selected_commits(writer); ++i) {
+ struct bitmapped_commit *stored = &writer->selected[i];
+ if (stored->pseudo_merge)
+ BUG("unexpected pseudo-merge among selected: %s",
+ oid_to_hex(&stored->commit->object.oid));
if (offsets)
offsets[i] = hashfile_total(f);
- hashwrite_be32(f, commit_positions[i]);
+ hashwrite_be32(f, stored->commit_pos);
hashwrite_u8(f, stored->xor_offset);
hashwrite_u8(f, stored->flags);
@@ -690,31 +785,154 @@ static void write_selected_commits_v1(struct hashfile *f,
}
}
+static void write_pseudo_merges(struct bitmap_writer *writer,
+ struct hashfile *f)
+{
+ struct oid_array commits = OID_ARRAY_INIT;
+ struct bitmap **commits_bitmap = NULL;
+ off_t *pseudo_merge_ofs = NULL;
+ off_t start, table_start, next_ext;
+
+ uint32_t base = bitmap_writer_nr_selected_commits(writer);
+ size_t i, j = 0;
+
+ CALLOC_ARRAY(commits_bitmap, writer->pseudo_merges_nr);
+ CALLOC_ARRAY(pseudo_merge_ofs, writer->pseudo_merges_nr);
+
+ for (i = 0; i < writer->pseudo_merges_nr; i++) {
+ struct bitmapped_commit *merge = &writer->selected[base + i];
+ struct commit_list *p;
+
+ if (!merge->pseudo_merge)
+ BUG("found non-pseudo merge commit at %"PRIuMAX, (uintmax_t)i);
+
+ commits_bitmap[i] = bitmap_new();
+
+ for (p = merge->commit->parents; p; p = p->next)
+ bitmap_set(commits_bitmap[i],
+ find_object_pos(writer, &p->item->object.oid,
+ NULL));
+ }
+
+ start = hashfile_total(f);
+
+ for (i = 0; i < writer->pseudo_merges_nr; i++) {
+ struct ewah_bitmap *commits_ewah = bitmap_to_ewah(commits_bitmap[i]);
+
+ pseudo_merge_ofs[i] = hashfile_total(f);
+
+ dump_bitmap(f, commits_ewah);
+ dump_bitmap(f, writer->selected[base+i].write_as);
+
+ ewah_free(commits_ewah);
+ }
+
+ next_ext = st_add(hashfile_total(f),
+ st_mult(kh_size(writer->pseudo_merge_commits),
+ sizeof(uint64_t)));
+
+ table_start = hashfile_total(f);
+
+ commits.alloc = kh_size(writer->pseudo_merge_commits);
+ CALLOC_ARRAY(commits.oid, commits.alloc);
+
+ for (i = kh_begin(writer->pseudo_merge_commits); i != kh_end(writer->pseudo_merge_commits); i++) {
+ if (!kh_exist(writer->pseudo_merge_commits, i))
+ continue;
+ oid_array_append(&commits, &kh_key(writer->pseudo_merge_commits, i));
+ }
+
+ oid_array_sort(&commits);
+
+ /* write lookup table (non-extended) */
+ for (i = 0; i < commits.nr; i++) {
+ int hash_pos;
+ struct pseudo_merge_commit_idx *c;
+
+ hash_pos = kh_get_oid_map(writer->pseudo_merge_commits,
+ commits.oid[i]);
+ if (hash_pos == kh_end(writer->pseudo_merge_commits))
+ BUG("could not find pseudo-merge commit %s",
+ oid_to_hex(&commits.oid[i]));
+
+ c = kh_value(writer->pseudo_merge_commits, hash_pos);
+
+ hashwrite_be32(f, find_object_pos(writer, &commits.oid[i],
+ NULL));
+ if (c->nr == 1)
+ hashwrite_be64(f, pseudo_merge_ofs[c->pseudo_merge[0]]);
+ else if (c->nr > 1) {
+ if (next_ext & ((uint64_t)1<<63))
+ die(_("too many pseudo-merges"));
+ hashwrite_be64(f, next_ext | ((uint64_t)1<<63));
+ next_ext = st_add3(next_ext,
+ sizeof(uint32_t),
+ st_mult(c->nr, sizeof(uint64_t)));
+ } else
+ BUG("expected commit '%s' to have at least one "
+ "pseudo-merge", oid_to_hex(&commits.oid[i]));
+ }
+
+ /* write lookup table (extended) */
+ for (i = 0; i < commits.nr; i++) {
+ int hash_pos;
+ struct pseudo_merge_commit_idx *c;
+
+ hash_pos = kh_get_oid_map(writer->pseudo_merge_commits,
+ commits.oid[i]);
+ if (hash_pos == kh_end(writer->pseudo_merge_commits))
+ BUG("could not find pseudo-merge commit %s",
+ oid_to_hex(&commits.oid[i]));
+
+ c = kh_value(writer->pseudo_merge_commits, hash_pos);
+ if (c->nr == 1)
+ continue;
+
+ hashwrite_be32(f, c->nr);
+ for (j = 0; j < c->nr; j++)
+ hashwrite_be64(f, pseudo_merge_ofs[c->pseudo_merge[j]]);
+ }
+
+ /* write positions for all pseudo merges */
+ for (i = 0; i < writer->pseudo_merges_nr; i++)
+ hashwrite_be64(f, pseudo_merge_ofs[i]);
+
+ hashwrite_be32(f, writer->pseudo_merges_nr);
+ hashwrite_be32(f, kh_size(writer->pseudo_merge_commits));
+ hashwrite_be64(f, table_start - start);
+ hashwrite_be64(f, hashfile_total(f) - start + sizeof(uint64_t));
+
+ for (i = 0; i < writer->pseudo_merges_nr; i++)
+ bitmap_free(commits_bitmap[i]);
+
+ free(pseudo_merge_ofs);
+ free(commits_bitmap);
+}
+
static int table_cmp(const void *_va, const void *_vb, void *_data)
{
- uint32_t *commit_positions = _data;
- uint32_t a = commit_positions[*(uint32_t *)_va];
- uint32_t b = commit_positions[*(uint32_t *)_vb];
+ struct bitmap_writer *writer = _data;
+ struct bitmapped_commit *a = &writer->selected[*(uint32_t *)_va];
+ struct bitmapped_commit *b = &writer->selected[*(uint32_t *)_vb];
- if (a > b)
- return 1;
- else if (a < b)
+ if (a->commit_pos < b->commit_pos)
return -1;
+ else if (a->commit_pos > b->commit_pos)
+ return 1;
return 0;
}
-static void write_lookup_table(struct hashfile *f,
- uint32_t *commit_positions,
+static void write_lookup_table(struct bitmap_writer *writer, struct hashfile *f,
off_t *offsets)
{
uint32_t i;
uint32_t *table, *table_inv;
- ALLOC_ARRAY(table, writer.selected_nr);
- ALLOC_ARRAY(table_inv, writer.selected_nr);
+ ALLOC_ARRAY(table, bitmap_writer_nr_selected_commits(writer));
+ ALLOC_ARRAY(table_inv, bitmap_writer_nr_selected_commits(writer));
- for (i = 0; i < writer.selected_nr; i++)
+ for (i = 0; i < bitmap_writer_nr_selected_commits(writer); i++)
table[i] = i;
/*
@@ -722,17 +940,17 @@ static void write_lookup_table(struct hashfile *f,
* bitmap corresponds to j'th bitmapped commit (among the selected
* commits) in lex order of OIDs.
*/
- QSORT_S(table, writer.selected_nr, table_cmp, commit_positions);
+ QSORT_S(table, bitmap_writer_nr_selected_commits(writer), table_cmp, writer);
/* table_inv helps us discover that relationship (i'th bitmap
* to j'th commit by j = table_inv[i])
*/
- for (i = 0; i < writer.selected_nr; i++)
+ for (i = 0; i < bitmap_writer_nr_selected_commits(writer); i++)
table_inv[table[i]] = i;
trace2_region_enter("pack-bitmap-write", "writing_lookup_table", the_repository);
- for (i = 0; i < writer.selected_nr; i++) {
- struct bitmapped_commit *selected = &writer.selected[table[i]];
+ for (i = 0; i < bitmap_writer_nr_selected_commits(writer); i++) {
+ struct bitmapped_commit *selected = &writer->selected[table[i]];
uint32_t xor_offset = selected->xor_offset;
uint32_t xor_row;
@@ -753,7 +971,7 @@ static void write_lookup_table(struct hashfile *f,
xor_row = 0xffffffff;
}
- hashwrite_be32(f, commit_positions[table[i]]);
+ hashwrite_be32(f, writer->selected[table[i]].commit_pos);
hashwrite_be64(f, (uint64_t)offsets[table[i]]);
hashwrite_be32(f, xor_row);
}
@@ -775,12 +993,14 @@ static void write_hash_cache(struct hashfile *f,
}
}
-void bitmap_writer_set_checksum(const unsigned char *sha1)
+void bitmap_writer_set_checksum(struct bitmap_writer *writer,
+ const unsigned char *sha1)
{
- hashcpy(writer.pack_checksum, sha1);
+ hashcpy(writer->pack_checksum, sha1, the_repository->hash_algo);
}
-void bitmap_writer_finish(struct pack_idx_entry **index,
+void bitmap_writer_finish(struct bitmap_writer *writer,
+ struct pack_idx_entry **index,
uint32_t index_nr,
const char *filename,
uint16_t options)
@@ -789,7 +1009,6 @@ void bitmap_writer_finish(struct pack_idx_entry **index,
static uint16_t flags = BITMAP_OPT_FULL_DAG;
struct strbuf tmp_file = STRBUF_INIT;
struct hashfile *f;
- uint32_t *commit_positions = NULL;
off_t *offsets = NULL;
uint32_t i;
@@ -797,39 +1016,43 @@ void bitmap_writer_finish(struct pack_idx_entry **index,
int fd = odb_mkstemp(&tmp_file, "pack/tmp_bitmap_XXXXXX");
+ if (writer->pseudo_merges_nr)
+ options |= BITMAP_OPT_PSEUDO_MERGES;
+
f = hashfd(fd, tmp_file.buf);
memcpy(header.magic, BITMAP_IDX_SIGNATURE, sizeof(BITMAP_IDX_SIGNATURE));
header.version = htons(default_version);
header.options = htons(flags | options);
- header.entry_count = htonl(writer.selected_nr);
- hashcpy(header.checksum, writer.pack_checksum);
+ header.entry_count = htonl(bitmap_writer_nr_selected_commits(writer));
+ hashcpy(header.checksum, writer->pack_checksum, the_repository->hash_algo);
hashwrite(f, &header, sizeof(header) - GIT_MAX_RAWSZ + the_hash_algo->rawsz);
- dump_bitmap(f, writer.commits);
- dump_bitmap(f, writer.trees);
- dump_bitmap(f, writer.blobs);
- dump_bitmap(f, writer.tags);
+ dump_bitmap(f, writer->commits);
+ dump_bitmap(f, writer->trees);
+ dump_bitmap(f, writer->blobs);
+ dump_bitmap(f, writer->tags);
if (options & BITMAP_OPT_LOOKUP_TABLE)
CALLOC_ARRAY(offsets, index_nr);
- ALLOC_ARRAY(commit_positions, writer.selected_nr);
-
- for (i = 0; i < writer.selected_nr; i++) {
- struct bitmapped_commit *stored = &writer.selected[i];
- int commit_pos = oid_pos(&stored->commit->object.oid, index, index_nr, oid_access);
+ for (i = 0; i < bitmap_writer_nr_selected_commits(writer); i++) {
+ struct bitmapped_commit *stored = &writer->selected[i];
+ int commit_pos = oid_pos(&stored->commit->object.oid, index,
+ index_nr, oid_access);
if (commit_pos < 0)
BUG(_("trying to write commit not in index"));
-
- commit_positions[i] = commit_pos;
+ stored->commit_pos = commit_pos;
}
- write_selected_commits_v1(f, commit_positions, offsets);
+ write_selected_commits_v1(writer, f, offsets);
+
+ if (options & BITMAP_OPT_PSEUDO_MERGES)
+ write_pseudo_merges(writer, f);
if (options & BITMAP_OPT_LOOKUP_TABLE)
- write_lookup_table(f, commit_positions, offsets);
+ write_lookup_table(writer, f, offsets);
if (options & BITMAP_OPT_HASH_CACHE)
write_hash_cache(f, index, index_nr);
@@ -844,6 +1067,5 @@ void bitmap_writer_finish(struct pack_idx_entry **index,
die_errno("unable to rename temporary bitmap file to '%s'", filename);
strbuf_release(&tmp_file);
- free(commit_positions);
free(offsets);
}
diff --git a/pack-bitmap.c b/pack-bitmap.c
index 35c5ef9d3c..2e657a2aa4 100644
--- a/pack-bitmap.c
+++ b/pack-bitmap.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "commit.h"
#include "gettext.h"
@@ -20,6 +22,7 @@
#include "list-objects-filter-options.h"
#include "midx.h"
#include "config.h"
+#include "pseudo-merge.h"
/*
* An entry on the bitmap index, representing the bitmap for a given
@@ -86,6 +89,9 @@ struct bitmap_index {
*/
unsigned char *table_lookup;
+ /* This contains the pseudo-merge cache within 'map' (if found). */
+ struct pseudo_merge_map pseudo_merges;
+
/*
* Extended index.
*
@@ -110,6 +116,13 @@ struct bitmap_index {
unsigned int version;
};
+static int pseudo_merges_satisfied_nr;
+static int pseudo_merges_cascades_nr;
+static int existing_bitmaps_hits_nr;
+static int existing_bitmaps_misses_nr;
+static int roots_with_bitmaps_nr;
+static int roots_without_bitmaps_nr;
+
static struct ewah_bitmap *lookup_stored_bitmap(struct stored_bitmap *st)
{
struct ewah_bitmap *parent;
@@ -129,17 +142,13 @@ static struct ewah_bitmap *lookup_stored_bitmap(struct stored_bitmap *st)
return composed;
}
-/*
- * Read a bitmap from the current read position on the mmaped
- * index, and increase the read position accordingly
- */
-static struct ewah_bitmap *read_bitmap_1(struct bitmap_index *index)
+struct ewah_bitmap *read_bitmap(const unsigned char *map,
+ size_t map_size, size_t *map_pos)
{
struct ewah_bitmap *b = ewah_pool_new();
- ssize_t bitmap_size = ewah_read_mmap(b,
- index->map + index->map_pos,
- index->map_size - index->map_pos);
+ ssize_t bitmap_size = ewah_read_mmap(b, map + *map_pos,
+ map_size - *map_pos);
if (bitmap_size < 0) {
error(_("failed to load bitmap index (corrupted?)"));
@@ -147,10 +156,20 @@ static struct ewah_bitmap *read_bitmap_1(struct bitmap_index *index)
return NULL;
}
- index->map_pos += bitmap_size;
+ *map_pos += bitmap_size;
+
return b;
}
+/*
+ * Read a bitmap from the current read position on the mmaped
+ * index, and increase the read position accordingly
+ */
+static struct ewah_bitmap *read_bitmap_1(struct bitmap_index *index)
+{
+ return read_bitmap(index->map, index->map_size, &index->map_pos);
+}
+
static uint32_t bitmap_num_objects(struct bitmap_index *index)
{
if (index->midx)
@@ -199,6 +218,46 @@ static int load_bitmap_header(struct bitmap_index *index)
index->table_lookup = (void *)(index_end - table_size);
index_end -= table_size;
}
+
+ if (flags & BITMAP_OPT_PSEUDO_MERGES) {
+ unsigned char *pseudo_merge_ofs;
+ size_t table_size;
+ uint32_t i;
+
+ if (sizeof(table_size) > index_end - index->map - header_size)
+ return error(_("corrupted bitmap index file (too short to fit pseudo-merge table header)"));
+
+ table_size = get_be64(index_end - 8);
+ if (table_size > index_end - index->map - header_size)
+ return error(_("corrupted bitmap index file (too short to fit pseudo-merge table)"));
+
+ if (git_env_bool("GIT_TEST_USE_PSEUDO_MERGES", 1)) {
+ const unsigned char *ext = (index_end - table_size);
+
+ index->pseudo_merges.map = index->map;
+ index->pseudo_merges.map_size = index->map_size;
+ index->pseudo_merges.commits = ext + get_be64(index_end - 16);
+ index->pseudo_merges.commits_nr = get_be32(index_end - 20);
+ index->pseudo_merges.nr = get_be32(index_end - 24);
+
+ if (st_add(st_mult(index->pseudo_merges.nr,
+ sizeof(uint64_t)),
+ 24) > table_size)
+ return error(_("corrupted bitmap index file, pseudo-merge table too short"));
+
+ CALLOC_ARRAY(index->pseudo_merges.v,
+ index->pseudo_merges.nr);
+
+ pseudo_merge_ofs = index_end - 24 -
+ (index->pseudo_merges.nr * sizeof(uint64_t));
+ for (i = 0; i < index->pseudo_merges.nr; i++) {
+ index->pseudo_merges.v[i].at = get_be64(pseudo_merge_ofs);
+ pseudo_merge_ofs += sizeof(uint64_t);
+ }
+ }
+
+ index_end -= table_size;
+ }
}
index->entry_count = ntohl(header->entry_count);
@@ -309,9 +368,8 @@ static int load_bitmap_entries_v1(struct bitmap_index *index)
char *midx_bitmap_filename(struct multi_pack_index *midx)
{
struct strbuf buf = STRBUF_INIT;
-
- get_midx_filename(&buf, midx->object_dir);
- strbuf_addf(&buf, "-%s.bitmap", hash_to_hex(get_midx_checksum(midx)));
+ get_midx_filename_ext(&buf, midx->object_dir, get_midx_checksum(midx),
+ MIDX_EXT_BITMAP);
return strbuf_detach(&buf, NULL);
}
@@ -368,7 +426,8 @@ static int open_midx_bitmap_1(struct bitmap_index *bitmap_git,
if (load_bitmap_header(bitmap_git) < 0)
goto cleanup;
- if (!hasheq(get_midx_checksum(bitmap_git->midx), bitmap_git->checksum)) {
+ if (!hasheq(get_midx_checksum(bitmap_git->midx), bitmap_git->checksum,
+ the_repository->hash_algo)) {
error(_("checksum doesn't match in MIDX and bitmap"));
goto cleanup;
}
@@ -961,6 +1020,22 @@ static void show_commit(struct commit *commit UNUSED,
{
}
+static unsigned apply_pseudo_merges_for_commit_1(struct bitmap_index *bitmap_git,
+ struct bitmap *result,
+ struct commit *commit,
+ uint32_t commit_pos)
+{
+ int ret;
+
+ ret = apply_pseudo_merges_for_commit(&bitmap_git->pseudo_merges,
+ result, commit, commit_pos);
+
+ if (ret)
+ pseudo_merges_satisfied_nr += ret;
+
+ return ret;
+}
+
static int add_to_include_set(struct bitmap_index *bitmap_git,
struct include_data *data,
struct commit *commit,
@@ -976,11 +1051,19 @@ static int add_to_include_set(struct bitmap_index *bitmap_git,
partial = bitmap_for_commit(bitmap_git, commit);
if (partial) {
+ existing_bitmaps_hits_nr++;
+
bitmap_or_ewah(data->base, partial);
return 0;
}
+ existing_bitmaps_misses_nr++;
+
bitmap_set(data->base, bitmap_pos);
+ if (apply_pseudo_merges_for_commit_1(bitmap_git, data->base, commit,
+ bitmap_pos))
+ return 0;
+
return 1;
}
@@ -1031,8 +1114,12 @@ static int add_commit_to_bitmap(struct bitmap_index *bitmap_git,
{
struct ewah_bitmap *or_with = bitmap_for_commit(bitmap_git, commit);
- if (!or_with)
+ if (!or_with) {
+ existing_bitmaps_misses_nr++;
return 0;
+ }
+
+ existing_bitmaps_hits_nr++;
if (!*base)
*base = ewah_to_bitmap(or_with);
@@ -1106,6 +1193,20 @@ static void show_boundary_object(struct object *object UNUSED,
BUG("should not be called");
}
+static unsigned cascade_pseudo_merges_1(struct bitmap_index *bitmap_git,
+ struct bitmap *result,
+ struct bitmap *roots)
+{
+ int ret = cascade_pseudo_merges(&bitmap_git->pseudo_merges,
+ result, roots);
+ if (ret) {
+ pseudo_merges_cascades_nr++;
+ pseudo_merges_satisfied_nr += ret;
+ }
+
+ return ret;
+}
+
static struct bitmap *find_boundary_objects(struct bitmap_index *bitmap_git,
struct rev_info *revs,
struct object_list *roots)
@@ -1115,6 +1216,7 @@ static struct bitmap *find_boundary_objects(struct bitmap_index *bitmap_git,
unsigned int i;
unsigned int tmp_blobs, tmp_trees, tmp_tags;
int any_missing = 0;
+ int existing_bitmaps = 0;
cb.bitmap_git = bitmap_git;
cb.base = bitmap_new();
@@ -1122,6 +1224,25 @@ static struct bitmap *find_boundary_objects(struct bitmap_index *bitmap_git,
revs->ignore_missing_links = 1;
+ if (bitmap_git->pseudo_merges.nr) {
+ struct bitmap *roots_bitmap = bitmap_new();
+ struct object_list *objects = NULL;
+
+ for (objects = roots; objects; objects = objects->next) {
+ struct object *object = objects->item;
+ int pos;
+
+ pos = bitmap_position(bitmap_git, &object->oid);
+ if (pos < 0)
+ continue;
+
+ bitmap_set(roots_bitmap, pos);
+ }
+
+ if (!cascade_pseudo_merges_1(bitmap_git, cb.base, roots_bitmap))
+ bitmap_free(roots_bitmap);
+ }
+
/*
* OR in any existing reachability bitmaps among `roots` into
* `cb.base`.
@@ -1133,8 +1254,10 @@ static struct bitmap *find_boundary_objects(struct bitmap_index *bitmap_git,
continue;
if (add_commit_to_bitmap(bitmap_git, &cb.base,
- (struct commit *)object))
+ (struct commit *)object)) {
+ existing_bitmaps = 1;
continue;
+ }
any_missing = 1;
}
@@ -1142,6 +1265,9 @@ static struct bitmap *find_boundary_objects(struct bitmap_index *bitmap_git,
if (!any_missing)
goto cleanup;
+ if (existing_bitmaps)
+ cascade_pseudo_merges_1(bitmap_git, cb.base, NULL);
+
tmp_blobs = revs->blob_objects;
tmp_trees = revs->tree_objects;
tmp_tags = revs->blob_objects;
@@ -1197,6 +1323,44 @@ cleanup:
return cb.base;
}
+struct ewah_bitmap *pseudo_merge_bitmap_for_commit(struct bitmap_index *bitmap_git,
+ struct commit *commit)
+{
+ struct commit_list *p;
+ struct bitmap *parents;
+ struct pseudo_merge *match = NULL;
+
+ if (!bitmap_git->pseudo_merges.nr)
+ return NULL;
+
+ parents = bitmap_new();
+
+ for (p = commit->parents; p; p = p->next) {
+ int pos = bitmap_position(bitmap_git, &p->item->object.oid);
+ if (pos < 0 || pos >= bitmap_num_objects(bitmap_git))
+ goto done;
+
+ bitmap_set(parents, pos);
+ }
+
+ match = pseudo_merge_for_parents(&bitmap_git->pseudo_merges,
+ parents);
+
+done:
+ bitmap_free(parents);
+ if (match)
+ return pseudo_merge_bitmap(&bitmap_git->pseudo_merges, match);
+
+ return NULL;
+}
+
+static void unsatisfy_all_pseudo_merges(struct bitmap_index *bitmap_git)
+{
+ uint32_t i;
+ for (i = 0; i < bitmap_git->pseudo_merges.nr; i++)
+ bitmap_git->pseudo_merges.v[i].satisfied = 0;
+}
+
static struct bitmap *find_objects(struct bitmap_index *bitmap_git,
struct rev_info *revs,
struct object_list *roots,
@@ -1204,9 +1368,32 @@ static struct bitmap *find_objects(struct bitmap_index *bitmap_git,
{
struct bitmap *base = NULL;
int needs_walk = 0;
+ unsigned existing_bitmaps = 0;
struct object_list *not_mapped = NULL;
+ unsatisfy_all_pseudo_merges(bitmap_git);
+
+ if (bitmap_git->pseudo_merges.nr) {
+ struct bitmap *roots_bitmap = bitmap_new();
+ struct object_list *objects = NULL;
+
+ for (objects = roots; objects; objects = objects->next) {
+ struct object *object = objects->item;
+ int pos;
+
+ pos = bitmap_position(bitmap_git, &object->oid);
+ if (pos < 0)
+ continue;
+
+ bitmap_set(roots_bitmap, pos);
+ }
+
+ base = bitmap_new();
+ if (!cascade_pseudo_merges_1(bitmap_git, base, roots_bitmap))
+ bitmap_free(roots_bitmap);
+ }
+
/*
* Go through all the roots for the walk. The ones that have bitmaps
* on the bitmap index will be `or`ed together to form an initial
@@ -1217,11 +1404,21 @@ static struct bitmap *find_objects(struct bitmap_index *bitmap_git,
*/
while (roots) {
struct object *object = roots->item;
+
roots = roots->next;
+ if (base) {
+ int pos = bitmap_position(bitmap_git, &object->oid);
+ if (pos > 0 && bitmap_get(base, pos)) {
+ object->flags |= SEEN;
+ continue;
+ }
+ }
+
if (object->type == OBJ_COMMIT &&
add_commit_to_bitmap(bitmap_git, &base, (struct commit *)object)) {
object->flags |= SEEN;
+ existing_bitmaps = 1;
continue;
}
@@ -1237,6 +1434,9 @@ static struct bitmap *find_objects(struct bitmap_index *bitmap_git,
roots = not_mapped;
+ if (existing_bitmaps)
+ cascade_pseudo_merges_1(bitmap_git, base, NULL);
+
/*
* Let's iterate through all the roots that don't have bitmaps to
* check if we can determine them to be reachable from the existing
@@ -1257,8 +1457,12 @@ static struct bitmap *find_objects(struct bitmap_index *bitmap_git,
object->flags &= ~UNINTERESTING;
add_pending_object(revs, object, "");
needs_walk = 1;
+
+ roots_without_bitmaps_nr++;
} else {
object->flags |= SEEN;
+
+ roots_with_bitmaps_nr++;
}
}
@@ -1821,6 +2025,19 @@ struct bitmap_index *prepare_bitmap_walk(struct rev_info *revs,
object_list_free(&wants);
object_list_free(&haves);
+ trace2_data_intmax("bitmap", the_repository, "pseudo_merges_satisfied",
+ pseudo_merges_satisfied_nr);
+ trace2_data_intmax("bitmap", the_repository, "pseudo_merges_cascades",
+ pseudo_merges_cascades_nr);
+ trace2_data_intmax("bitmap", the_repository, "bitmap/hits",
+ existing_bitmaps_hits_nr);
+ trace2_data_intmax("bitmap", the_repository, "bitmap/misses",
+ existing_bitmaps_misses_nr);
+ trace2_data_intmax("bitmap", the_repository, "bitmap/roots_with_bitmap",
+ roots_with_bitmaps_nr);
+ trace2_data_intmax("bitmap", the_repository, "bitmap/roots_without_bitmap",
+ roots_without_bitmaps_nr);
+
return bitmap_git;
cleanup:
@@ -2074,6 +2291,7 @@ void reuse_partial_packfile_from_bitmap(struct bitmap_index *bitmap_git,
QSORT(packs, packs_nr, bitmapped_pack_cmp);
} else {
struct packed_git *pack;
+ uint32_t pack_int_id;
if (bitmap_is_midx(bitmap_git)) {
uint32_t preferred_pack_pos;
@@ -2084,12 +2302,24 @@ void reuse_partial_packfile_from_bitmap(struct bitmap_index *bitmap_git,
}
pack = bitmap_git->midx->packs[preferred_pack_pos];
+ pack_int_id = preferred_pack_pos;
} else {
pack = bitmap_git->pack;
+ /*
+ * Any value for 'pack_int_id' will do here. When we
+ * process the pack via try_partial_reuse(), we won't
+ * use the `pack_int_id` field since we have a non-MIDX
+ * bitmap.
+ *
+ * Use '-1' as a sentinel value to make it clear
+ * that we do not expect to read this field.
+ */
+ pack_int_id = -1;
}
ALLOC_GROW(packs, packs_nr + 1, packs_alloc);
packs[packs_nr].p = pack;
+ packs[packs_nr].pack_int_id = pack_int_id;
packs[packs_nr].bitmap_nr = pack->num_objects;
packs[packs_nr].bitmap_pos = 0;
@@ -2398,6 +2628,132 @@ cleanup:
return 0;
}
+static void bit_pos_to_object_id(struct bitmap_index *bitmap_git,
+ uint32_t bit_pos,
+ struct object_id *oid)
+{
+ uint32_t index_pos;
+
+ if (bitmap_is_midx(bitmap_git))
+ index_pos = pack_pos_to_midx(bitmap_git->midx, bit_pos);
+ else
+ index_pos = pack_pos_to_index(bitmap_git->pack, bit_pos);
+
+ nth_bitmap_object_oid(bitmap_git, oid, index_pos);
+}
+
+int test_bitmap_pseudo_merges(struct repository *r)
+{
+ struct bitmap_index *bitmap_git;
+ uint32_t i;
+
+ bitmap_git = prepare_bitmap_git(r);
+ if (!bitmap_git || !bitmap_git->pseudo_merges.nr)
+ goto cleanup;
+
+ for (i = 0; i < bitmap_git->pseudo_merges.nr; i++) {
+ struct pseudo_merge *merge;
+ struct ewah_bitmap *commits_bitmap, *merge_bitmap;
+
+ merge = use_pseudo_merge(&bitmap_git->pseudo_merges,
+ &bitmap_git->pseudo_merges.v[i]);
+ commits_bitmap = merge->commits;
+ merge_bitmap = pseudo_merge_bitmap(&bitmap_git->pseudo_merges,
+ merge);
+
+ printf("at=%"PRIuMAX", commits=%"PRIuMAX", objects=%"PRIuMAX"\n",
+ (uintmax_t)merge->at,
+ (uintmax_t)ewah_bitmap_popcount(commits_bitmap),
+ (uintmax_t)ewah_bitmap_popcount(merge_bitmap));
+ }
+
+cleanup:
+ free_bitmap_index(bitmap_git);
+ return 0;
+}
+
+static void dump_ewah_object_ids(struct bitmap_index *bitmap_git,
+ struct ewah_bitmap *bitmap)
+
+{
+ struct ewah_iterator it;
+ eword_t word;
+ uint32_t pos = 0;
+
+ ewah_iterator_init(&it, bitmap);
+
+ while (ewah_iterator_next(&word, &it)) {
+ struct object_id oid;
+ uint32_t offset;
+
+ for (offset = 0; offset < BITS_IN_EWORD; offset++) {
+ if (!(word >> offset))
+ break;
+
+ offset += ewah_bit_ctz64(word >> offset);
+
+ bit_pos_to_object_id(bitmap_git, pos + offset, &oid);
+ printf("%s\n", oid_to_hex(&oid));
+ }
+ pos += BITS_IN_EWORD;
+ }
+}
+
+int test_bitmap_pseudo_merge_commits(struct repository *r, uint32_t n)
+{
+ struct bitmap_index *bitmap_git;
+ struct pseudo_merge *merge;
+ int ret = 0;
+
+ bitmap_git = prepare_bitmap_git(r);
+ if (!bitmap_git || !bitmap_git->pseudo_merges.nr)
+ goto cleanup;
+
+ if (n >= bitmap_git->pseudo_merges.nr) {
+ ret = error(_("pseudo-merge index out of range "
+ "(%"PRIu32" >= %"PRIuMAX")"),
+ n, (uintmax_t)bitmap_git->pseudo_merges.nr);
+ goto cleanup;
+ }
+
+ merge = use_pseudo_merge(&bitmap_git->pseudo_merges,
+ &bitmap_git->pseudo_merges.v[n]);
+ dump_ewah_object_ids(bitmap_git, merge->commits);
+
+cleanup:
+ free_bitmap_index(bitmap_git);
+ return ret;
+}
+
+int test_bitmap_pseudo_merge_objects(struct repository *r, uint32_t n)
+{
+ struct bitmap_index *bitmap_git;
+ struct pseudo_merge *merge;
+ int ret = 0;
+
+ bitmap_git = prepare_bitmap_git(r);
+ if (!bitmap_git || !bitmap_git->pseudo_merges.nr)
+ goto cleanup;
+
+ if (n >= bitmap_git->pseudo_merges.nr) {
+ ret = error(_("pseudo-merge index out of range "
+ "(%"PRIu32" >= %"PRIuMAX")"),
+ n, (uintmax_t)bitmap_git->pseudo_merges.nr);
+ goto cleanup;
+ }
+
+ merge = use_pseudo_merge(&bitmap_git->pseudo_merges,
+ &bitmap_git->pseudo_merges.v[n]);
+
+ dump_ewah_object_ids(bitmap_git,
+ pseudo_merge_bitmap(&bitmap_git->pseudo_merges,
+ merge));
+
+cleanup:
+ free_bitmap_index(bitmap_git);
+ return ret;
+}
+
int rebuild_bitmap(const uint32_t *reposition,
struct ewah_bitmap *source,
struct bitmap *dest)
@@ -2504,6 +2860,7 @@ void free_bitmap_index(struct bitmap_index *b)
*/
close_midx_revindex(b->midx);
}
+ free_pseudo_merge_map(&b->pseudo_merges);
free(b);
}
diff --git a/pack-bitmap.h b/pack-bitmap.h
index c7dea13217..1171e6d989 100644
--- a/pack-bitmap.h
+++ b/pack-bitmap.h
@@ -21,6 +21,7 @@ struct bitmap_disk_header {
unsigned char checksum[GIT_MAX_RAWSZ];
};
+#define BITMAP_PSEUDO_MERGE (1u<<21)
#define NEEDS_BITMAP (1u<<22)
/*
@@ -36,6 +37,7 @@ enum pack_bitmap_opts {
BITMAP_OPT_FULL_DAG = 0x1,
BITMAP_OPT_HASH_CACHE = 0x4,
BITMAP_OPT_LOOKUP_TABLE = 0x10,
+ BITMAP_OPT_PSEUDO_MERGES = 0x20,
};
enum pack_bitmap_flags {
@@ -71,6 +73,9 @@ 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);
+int test_bitmap_pseudo_merges(struct repository *r);
+int test_bitmap_pseudo_merge_commits(struct repository *r, uint32_t n);
+int test_bitmap_pseudo_merge_objects(struct repository *r, uint32_t n);
#define GIT_TEST_PACK_USE_BITMAP_BOUNDARY_TRAVERSAL \
"GIT_TEST_PACK_USE_BITMAP_BOUNDARY_TRAVERSAL"
@@ -97,11 +102,39 @@ int bitmap_has_oid_in_uninteresting(struct bitmap_index *, const struct object_i
off_t get_disk_usage_from_bitmap(struct bitmap_index *, struct rev_info *);
-void bitmap_writer_show_progress(int show);
-void bitmap_writer_set_checksum(const unsigned char *sha1);
-void bitmap_writer_build_type_index(struct packing_data *to_pack,
+struct bitmap_writer {
+ struct ewah_bitmap *commits;
+ struct ewah_bitmap *trees;
+ struct ewah_bitmap *blobs;
+ struct ewah_bitmap *tags;
+
+ kh_oid_map_t *bitmaps;
+ struct packing_data *to_pack;
+
+ struct bitmapped_commit *selected;
+ unsigned int selected_nr, selected_alloc;
+
+ struct string_list pseudo_merge_groups;
+ kh_oid_map_t *pseudo_merge_commits; /* oid -> pseudo merge(s) */
+ uint32_t pseudo_merges_nr;
+
+ struct progress *progress;
+ int show_progress;
+ unsigned char pack_checksum[GIT_MAX_RAWSZ];
+};
+
+void bitmap_writer_init(struct bitmap_writer *writer, struct repository *r);
+void bitmap_writer_show_progress(struct bitmap_writer *writer, int show);
+void bitmap_writer_set_checksum(struct bitmap_writer *writer,
+ const unsigned char *sha1);
+void bitmap_writer_build_type_index(struct bitmap_writer *writer,
+ struct packing_data *to_pack,
struct pack_idx_entry **index,
uint32_t index_nr);
+int bitmap_writer_has_bitmapped_object_id(struct bitmap_writer *writer,
+ const struct object_id *oid);
+void bitmap_writer_push_commit(struct bitmap_writer *writer,
+ struct commit *commit, unsigned pseudo_merge);
uint32_t *create_bitmap_mapping(struct bitmap_index *bitmap_git,
struct packing_data *mapping);
int rebuild_bitmap(const uint32_t *reposition,
@@ -109,13 +142,19 @@ int rebuild_bitmap(const uint32_t *reposition,
struct bitmap *dest);
struct ewah_bitmap *bitmap_for_commit(struct bitmap_index *bitmap_git,
struct commit *commit);
-void bitmap_writer_select_commits(struct commit **indexed_commits,
- unsigned int indexed_commits_nr, int max_bitmaps);
-int bitmap_writer_build(struct packing_data *to_pack);
-void bitmap_writer_finish(struct pack_idx_entry **index,
+struct ewah_bitmap *pseudo_merge_bitmap_for_commit(struct bitmap_index *bitmap_git,
+ struct commit *commit);
+void bitmap_writer_select_commits(struct bitmap_writer *writer,
+ struct commit **indexed_commits,
+ unsigned int indexed_commits_nr);
+int bitmap_writer_build(struct bitmap_writer *writer,
+ struct packing_data *to_pack);
+void bitmap_writer_finish(struct bitmap_writer *writer,
+ struct pack_idx_entry **index,
uint32_t index_nr,
const char *filename,
uint16_t options);
+void bitmap_writer_free(struct bitmap_writer *writer);
char *midx_bitmap_filename(struct multi_pack_index *midx);
char *pack_bitmap_filename(struct packed_git *p);
@@ -126,4 +165,6 @@ int bitmap_is_preferred_refname(struct repository *r, const char *refname);
int verify_bitmap_files(struct repository *r);
+struct ewah_bitmap *read_bitmap(const unsigned char *map,
+ size_t map_size, size_t *map_pos);
#endif
diff --git a/pack-check.c b/pack-check.c
index 25104d5b14..e883dae3f2 100644
--- a/pack-check.c
+++ b/pack-check.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "environment.h"
#include "hex.h"
@@ -78,10 +80,11 @@ static int verify_packfile(struct repository *r,
} while (offset < pack_sig_ofs);
r->hash_algo->final_fn(hash, &ctx);
pack_sig = use_pack(p, w_curs, pack_sig_ofs, NULL);
- if (!hasheq(hash, pack_sig))
+ if (!hasheq(hash, pack_sig, the_repository->hash_algo))
err = error("%s pack checksum mismatch",
p->pack_name);
- if (!hasheq(index_base + index_size - r->hash_algo->hexsz, pack_sig))
+ if (!hasheq(index_base + index_size - r->hash_algo->hexsz, pack_sig,
+ the_repository->hash_algo))
err = error("%s pack checksum does not match its index",
p->pack_name);
unuse_pack(w_curs);
diff --git a/pack-revindex.c b/pack-revindex.c
index a7624d8be8..22d3c23464 100644
--- a/pack-revindex.c
+++ b/pack-revindex.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "gettext.h"
#include "pack-revindex.h"
@@ -381,7 +383,8 @@ int load_midx_revindex(struct multi_pack_index *m)
trace2_data_string("load_midx_revindex", the_repository,
"source", "rev");
- get_midx_rev_filename(&revindex_name, m);
+ get_midx_filename_ext(&revindex_name, m->object_dir,
+ get_midx_checksum(m), MIDX_EXT_REV);
ret = load_revindex_from_disk(revindex_name.buf,
m->num_objects,
@@ -526,6 +529,9 @@ static int midx_key_to_pack_pos(struct multi_pack_index *m,
{
uint32_t *found;
+ if (key->pack >= m->num_packs)
+ BUG("MIDX pack lookup out of bounds (%"PRIu32" >= %"PRIu32")",
+ key->pack, m->num_packs);
/*
* The preferred pack sorts first, so determine its identifier by
* looking at the first object in pseudo-pack order.
diff --git a/pack-write.c b/pack-write.c
index 80ecfa544c..d07f03d0ab 100644
--- a/pack-write.c
+++ b/pack-write.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "environment.h"
#include "gettext.h"
@@ -428,7 +430,8 @@ void fixup_pack_header_footer(int pack_fd,
if (partial_pack_offset == 0) {
unsigned char hash[GIT_MAX_RAWSZ];
the_hash_algo->final_fn(hash, &old_hash_ctx);
- if (!hasheq(hash, partial_pack_hash))
+ if (!hasheq(hash, partial_pack_hash,
+ the_repository->hash_algo))
die("Unexpected checksum for %s "
"(disk corruption?)", pack_name);
/*
diff --git a/packfile.c b/packfile.c
index d4df7fdeea..813584646f 100644
--- a/packfile.c
+++ b/packfile.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "environment.h"
#include "gettext.h"
@@ -242,7 +244,7 @@ struct packed_git *parse_pack_index(unsigned char *sha1, const char *idx_path)
struct packed_git *p = alloc_packed_git(alloc);
memcpy(p->pack_name, path, alloc); /* includes NUL */
- hashcpy(p->hash, sha1);
+ hashcpy(p->hash, sha1, the_repository->hash_algo);
if (check_packed_git_idx(idx_path, p)) {
free(p);
return NULL;
@@ -596,7 +598,7 @@ static int open_packed_git_1(struct packed_git *p)
if (read_result != hashsz)
return error("packfile %s signature is unavailable", p->pack_name);
idx_hash = ((unsigned char *)p->index_data) + p->index_size - hashsz * 2;
- if (!hasheq(hash, idx_hash))
+ if (!hasheq(hash, idx_hash, the_repository->hash_algo))
return error("packfile %s does not match index", p->pack_name);
return 0;
}
@@ -751,7 +753,7 @@ struct packed_git *add_packed_git(const char *path, size_t path_len, int local)
p->mtime = st.st_mtime;
if (path_len < the_hash_algo->hexsz ||
get_hash_hex(path + path_len - the_hash_algo->hexsz, p->hash))
- hashclr(p->hash);
+ hashclr(p->hash, the_repository->hash_algo);
return p;
}
@@ -1251,7 +1253,7 @@ static int get_delta_base_oid(struct packed_git *p,
{
if (type == OBJ_REF_DELTA) {
unsigned char *base = use_pack(p, w_curs, curpos, NULL);
- oidread(oid, base);
+ oidread(oid, base, the_repository->hash_algo);
return 0;
} else if (type == OBJ_OFS_DELTA) {
uint32_t base_pos;
@@ -1593,7 +1595,7 @@ int packed_object_info(struct repository *r, struct packed_git *p,
goto out;
}
} else
- oidclr(oi->delta_base_oid);
+ oidclr(oi->delta_base_oid, the_repository->hash_algo);
}
oi->whence = in_delta_base_cache(p, obj_offset) ? OI_DBCACHED :
@@ -1917,10 +1919,12 @@ int nth_packed_object_id(struct object_id *oid,
return -1;
index += 4 * 256;
if (p->index_version == 1) {
- oidread(oid, index + st_add(st_mult(hashsz + 4, n), 4));
+ oidread(oid, index + st_add(st_mult(hashsz + 4, n), 4),
+ the_repository->hash_algo);
} else {
index += 8;
- oidread(oid, index + st_mult(hashsz, n));
+ oidread(oid, index + st_mult(hashsz, n),
+ the_repository->hash_algo);
}
return 0;
}
@@ -1971,7 +1975,7 @@ off_t find_pack_entry_one(const unsigned char *sha1,
return 0;
}
- hashcpy(oid.hash, sha1);
+ hashcpy(oid.hash, sha1, the_repository->hash_algo);
if (bsearch_pack(&oid, p, &result))
return nth_packed_object_offset(p, result);
return 0;
diff --git a/packfile.h b/packfile.h
index 28c8fd3e39..eb18ec15db 100644
--- a/packfile.h
+++ b/packfile.h
@@ -101,6 +101,8 @@ int close_pack_fd(struct packed_git *p);
uint32_t get_pack_fanout(struct packed_git *p, uint32_t value);
+struct raw_object_store;
+
unsigned char *use_pack(struct packed_git *, struct pack_window **, off_t, unsigned long *);
void close_pack_windows(struct packed_git *);
void close_pack(struct packed_git *);
diff --git a/pager.c b/pager.c
index b8822a9381..be6f4ee59f 100644
--- a/pager.c
+++ b/pager.c
@@ -13,7 +13,7 @@ int pager_use_color = 1;
#endif
static struct child_process pager_process;
-static const char *pager_program;
+static char *pager_program;
/* Is the value coming back from term_columns() just a guess? */
static int term_columns_guessed;
@@ -137,7 +137,7 @@ void setup_pager(void)
pager_process.in = -1;
strvec_push(&pager_process.env, "GIT_PAGER_IN_USE");
if (start_command(&pager_process))
- return;
+ die("unable to execute pager '%s'", pager);
/* original process continues, but writes to the pipe */
dup2(pager_process.in, 1);
diff --git a/parallel-checkout.c b/parallel-checkout.c
index b5a714c711..08b960aac8 100644
--- a/parallel-checkout.c
+++ b/parallel-checkout.c
@@ -429,13 +429,7 @@ static void send_one_item(int fd, struct parallel_checkout_item *pc_item)
fixed_portion->ident = pc_item->ca.ident;
fixed_portion->name_len = name_len;
fixed_portion->working_tree_encoding_len = working_tree_encoding_len;
- /*
- * We pad the unused bytes in the hash array because, otherwise,
- * Valgrind would complain about passing uninitialized bytes to a
- * write() syscall. The warning doesn't represent any real risk here,
- * but it could hinder the detection of actual errors.
- */
- oidcpy_with_padding(&fixed_portion->oid, &pc_item->ce->oid);
+ oidcpy(&fixed_portion->oid, &pc_item->ce->oid);
variant = data + sizeof(*fixed_portion);
if (working_tree_encoding_len) {
diff --git a/parse-options-cb.c b/parse-options-cb.c
index bdc7fae497..166d35e0eb 100644
--- a/parse-options-cb.c
+++ b/parse-options-cb.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "parse-options.h"
#include "branch.h"
@@ -7,6 +9,7 @@
#include "environment.h"
#include "gettext.h"
#include "object-name.h"
+#include "setup.h"
#include "string-list.h"
#include "strvec.h"
#include "oid-array.h"
@@ -29,8 +32,6 @@ int parse_opt_abbrev_cb(const struct option *opt, const char *arg, int unset)
opt->long_name);
if (v && v < MINIMUM_ABBREV)
v = MINIMUM_ABBREV;
- else if (v > the_hash_algo->hexsz)
- v = the_hash_algo->hexsz;
}
*(int *)(opt->value) = v;
return 0;
diff --git a/parse-options.h b/parse-options.h
index bd62e20268..ae15342390 100644
--- a/parse-options.h
+++ b/parse-options.h
@@ -355,7 +355,7 @@ struct option {
.type = OPTION_ALIAS, \
.short_name = (s), \
.long_name = (l), \
- .value = (source_long_name), \
+ .value = (char *)(source_long_name), \
}
#define OPT_SUBCOMMAND_F(l, v, fn, f) { \
diff --git a/parse.c b/parse.c
index 42d691a0fb..7a60a4f816 100644
--- a/parse.c
+++ b/parse.c
@@ -125,6 +125,35 @@ int git_parse_ssize_t(const char *value, ssize_t *ret)
return 1;
}
+int git_parse_double(const char *value, double *ret)
+{
+ char *end;
+ double val;
+ uintmax_t factor;
+
+ if (!value || !*value) {
+ errno = EINVAL;
+ return 0;
+ }
+
+ errno = 0;
+ val = strtod(value, &end);
+ if (errno == ERANGE)
+ return 0;
+ if (end == value) {
+ errno = EINVAL;
+ return 0;
+ }
+ factor = get_unit_factor(end);
+ if (!factor) {
+ errno = EINVAL;
+ return 0;
+ }
+ val *= factor;
+ *ret = val;
+ return 1;
+}
+
int git_parse_maybe_bool_text(const char *value)
{
if (!value)
diff --git a/parse.h b/parse.h
index 07d2193d69..6bb9a54d9a 100644
--- a/parse.h
+++ b/parse.h
@@ -6,6 +6,7 @@ int git_parse_ssize_t(const char *, ssize_t *);
int git_parse_ulong(const char *, unsigned long *);
int git_parse_int(const char *value, int *ret);
int git_parse_int64(const char *value, int64_t *ret);
+int git_parse_double(const char *value, double *ret);
/**
* Same as `git_config_bool`, except that it returns -1 on error rather
diff --git a/path.c b/path.c
index 67229edb9c..19f7684f38 100644
--- a/path.c
+++ b/path.c
@@ -1,11 +1,13 @@
/*
* Utilities for paths and pathnames
*/
+
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "abspath.h"
#include "environment.h"
#include "gettext.h"
-#include "hex.h"
#include "repository.h"
#include "strbuf.h"
#include "string-list.h"
@@ -647,58 +649,6 @@ void strbuf_git_common_path(struct strbuf *sb,
va_end(args);
}
-int validate_headref(const char *path)
-{
- struct stat st;
- char buffer[256];
- const char *refname;
- struct object_id oid;
- int fd;
- ssize_t len;
-
- if (lstat(path, &st) < 0)
- return -1;
-
- /* Make sure it is a "refs/.." symlink */
- if (S_ISLNK(st.st_mode)) {
- len = readlink(path, buffer, sizeof(buffer)-1);
- if (len >= 5 && !memcmp("refs/", buffer, 5))
- return 0;
- return -1;
- }
-
- /*
- * Anything else, just open it and try to see if it is a symbolic ref.
- */
- fd = open(path, O_RDONLY);
- if (fd < 0)
- return -1;
- len = read_in_full(fd, buffer, sizeof(buffer)-1);
- close(fd);
-
- if (len < 0)
- return -1;
- buffer[len] = '\0';
-
- /*
- * Is it a symbolic ref?
- */
- if (skip_prefix(buffer, "ref:", &refname)) {
- while (isspace(*refname))
- refname++;
- if (starts_with(refname, "refs/"))
- return 0;
- }
-
- /*
- * Is this a detached HEAD?
- */
- if (!get_oid_hex(buffer, &oid))
- return 0;
-
- return -1;
-}
-
static struct passwd *getpw_str(const char *username, size_t len)
{
struct passwd *pw;
@@ -829,6 +779,7 @@ const char *enter_repo(const char *path, int strict)
if (!suffix[i])
return NULL;
gitfile = read_gitfile(used_path.buf);
+ die_upon_dubious_ownership(gitfile, NULL, used_path.buf);
if (gitfile) {
strbuf_reset(&used_path);
strbuf_addstr(&used_path, gitfile);
@@ -839,6 +790,7 @@ const char *enter_repo(const char *path, int strict)
}
else {
const char *gitfile = read_gitfile(path);
+ die_upon_dubious_ownership(gitfile, NULL, path);
if (gitfile)
path = gitfile;
if (chdir(path))
diff --git a/path.h b/path.h
index ea96487b29..a6f0b70692 100644
--- a/path.h
+++ b/path.h
@@ -4,6 +4,7 @@
struct repository;
struct strbuf;
struct string_list;
+struct worktree;
/*
* The result to all functions which return statically allocated memory may be
@@ -82,6 +83,14 @@ const char *git_path(const char *fmt, ...)
__attribute__((format (printf, 1, 2)));
/*
+ * Similar to git_path() but can produce paths for a specified
+ * worktree instead of current one
+ */
+const char *worktree_git_path(const struct worktree *wt,
+ const char *fmt, ...)
+ __attribute__((format (printf, 2, 3)));
+
+/*
* Return a path into the main repository's (the_repository) git directory.
*/
char *git_pathdup(const char *fmt, ...)
@@ -173,7 +182,6 @@ const char *git_path_fetch_head(struct repository *r);
const char *git_path_shallow(struct repository *r);
int ends_with_path_components(const char *path, const char *components);
-int validate_headref(const char *ref);
int calc_shared_perm(int mode);
int adjust_shared_perm(const char *path);
diff --git a/pathspec.c b/pathspec.c
index 2133b9fe60..fe1f0f41af 100644
--- a/pathspec.c
+++ b/pathspec.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "abspath.h"
#include "parse.h"
diff --git a/pathspec.h b/pathspec.h
index fec4399bbc..de537cff3c 100644
--- a/pathspec.h
+++ b/pathspec.h
@@ -23,7 +23,7 @@ struct index_state;
#define PATHSPEC_ONESTAR 1 /* the pathspec pattern satisfies GFNM_ONESTAR */
/**
- * See glossary-context.txt for the syntax of pathspec.
+ * See glossary-content.txt for the syntax of pathspec.
* In memory, a pathspec set is represented by "struct pathspec" and is
* prepared by parse_pathspec().
*/
diff --git a/perl/Git.pm b/perl/Git.pm
index 03bf570bf4..aebfe0c6e0 100644
--- a/perl/Git.pm
+++ b/perl/Git.pm
@@ -418,7 +418,7 @@ argument is required if you want to see the command name in the error message,
and it is the fourth value returned by C<command_bidi_pipe()>. The call idiom
is:
- my ($pid, $in, $out, $ctx) = $r->command_bidi_pipe('cat-file --batch-check');
+ my ($pid, $in, $out, $ctx) = $r->command_bidi_pipe(qw(cat-file --batch-check));
print $out "000000000\n";
while (<$in>) { ... }
$r->command_close_bidi_pipe($pid, $in, $out, $ctx);
@@ -431,7 +431,7 @@ C<PIPE_IN> and C<PIPE_OUT> may be C<undef> if they have been closed prior to
calling this function. This may be useful in a query-response type of
commands where caller first writes a query and later reads response, eg:
- my ($pid, $in, $out, $ctx) = $r->command_bidi_pipe('cat-file --batch-check');
+ my ($pid, $in, $out, $ctx) = $r->command_bidi_pipe(qw(cat-file --batch-check));
print $out "000000000\n";
close $out;
while (<$in>) { ... }
diff --git a/pretty.c b/pretty.c
index 7ead078998..44222fb83c 100644
--- a/pretty.c
+++ b/pretty.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "config.h"
#include "commit.h"
@@ -62,7 +64,7 @@ static int git_pretty_formats_config(const char *var, const char *value,
{
struct cmt_fmt_map *commit_format = NULL;
const char *name;
- const char *fmt;
+ char *fmt;
int i;
if (!skip_prefix(var, "pretty.", &name))
@@ -93,13 +95,17 @@ static int git_pretty_formats_config(const char *var, const char *value,
if (git_config_string(&fmt, var, value))
return -1;
- if (skip_prefix(fmt, "format:", &fmt))
+ if (skip_prefix(fmt, "format:", &commit_format->user_format)) {
commit_format->is_tformat = 0;
- else if (skip_prefix(fmt, "tformat:", &fmt) || strchr(fmt, '%'))
+ } else if (skip_prefix(fmt, "tformat:", &commit_format->user_format)) {
commit_format->is_tformat = 1;
- else
+ } else if (strchr(fmt, '%')) {
+ commit_format->is_tformat = 1;
+ commit_format->user_format = fmt;
+ } else {
commit_format->is_alias = 1;
- commit_format->user_format = fmt;
+ commit_format->user_format = fmt;
+ }
return 0;
}
@@ -1321,7 +1327,7 @@ int format_set_trailers_options(struct process_trailer_options *opts,
static size_t parse_describe_args(const char *start, struct strvec *args)
{
struct {
- char *name;
+ const char *name;
enum {
DESCRIBE_ARG_BOOL,
DESCRIBE_ARG_INTEGER,
@@ -1580,8 +1586,8 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
case 'D':
{
const struct decoration_options opts = {
- .prefix = "",
- .suffix = ""
+ .prefix = (char *) "",
+ .suffix = (char *) "",
};
format_decorations(sb, commit, c->auto_color, &opts);
diff --git a/progress.c b/progress.c
index c83cb60bf1..0d44c18edc 100644
--- a/progress.c
+++ b/progress.c
@@ -9,6 +9,8 @@
*/
#define GIT_TEST_PROGRESS_ONLY
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "pager.h"
#include "progress.h"
diff --git a/promisor-remote.c b/promisor-remote.c
index ac3aa1e365..317e1b127f 100644
--- a/promisor-remote.c
+++ b/promisor-remote.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "gettext.h"
#include "hex.h"
@@ -8,6 +10,7 @@
#include "transport.h"
#include "strvec.h"
#include "packfile.h"
+#include "environment.h"
struct promisor_remote_config {
struct promisor_remote *promisors;
@@ -22,6 +25,16 @@ static int fetch_objects(struct repository *repo,
struct child_process child = CHILD_PROCESS_INIT;
int i;
FILE *child_in;
+ int quiet;
+
+ if (git_env_bool(NO_LAZY_FETCH_ENVIRONMENT, 0)) {
+ static int warning_shown;
+ if (!warning_shown) {
+ warning_shown = 1;
+ warning(_("lazy fetching disabled; some objects may not be available"));
+ }
+ return -1;
+ }
child.git_cmd = 1;
child.in = -1;
@@ -31,6 +44,8 @@ static int fetch_objects(struct repository *repo,
"fetch", remote_name, "--no-tags",
"--no-write-fetch-head", "--recurse-submodules=no",
"--filter=blob:none", "--stdin", NULL);
+ if (!git_config_get_bool("promisor.quiet", &quiet) && quiet)
+ strvec_push(&child.args, "--quiet");
if (start_command(&child))
die(_("promisor-remote: unable to fork off fetch subprocess"));
child_in = xfdopen(child.in, "w");
diff --git a/promisor-remote.h b/promisor-remote.h
index 2cb9eda9ea..88cb599c39 100644
--- a/promisor-remote.h
+++ b/promisor-remote.h
@@ -13,7 +13,7 @@ struct object_id;
*/
struct promisor_remote {
struct promisor_remote *next;
- const char *partial_clone_filter;
+ char *partial_clone_filter;
const char name[FLEX_ARRAY];
};
diff --git a/protocol-caps.c b/protocol-caps.c
index 75f4cbb0a7..855f279c2f 100644
--- a/protocol-caps.c
+++ b/protocol-caps.c
@@ -3,10 +3,11 @@
#include "gettext.h"
#include "hex.h"
#include "pkt-line.h"
-#include "hash-ll.h"
+#include "hash.h"
#include "hex.h"
#include "object.h"
#include "object-store-ll.h"
+#include "repository.h"
#include "string-list.h"
#include "strbuf.h"
@@ -52,7 +53,7 @@ static void send_info(struct repository *r, struct packet_writer *writer,
struct object_id oid;
unsigned long object_size;
- if (get_oid_hex(oid_str, &oid) < 0) {
+ if (get_oid_hex_algop(oid_str, &oid, r->hash_algo) < 0) {
packet_writer_error(
writer,
"object-info: protocol error, expected to get oid, not '%s'",
diff --git a/pseudo-merge.c b/pseudo-merge.c
new file mode 100644
index 0000000000..f0fde13c47
--- /dev/null
+++ b/pseudo-merge.c
@@ -0,0 +1,759 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
+#include "git-compat-util.h"
+#include "pseudo-merge.h"
+#include "date.h"
+#include "oid-array.h"
+#include "strbuf.h"
+#include "config.h"
+#include "string-list.h"
+#include "refs.h"
+#include "pack-bitmap.h"
+#include "commit.h"
+#include "alloc.h"
+#include "progress.h"
+#include "hex.h"
+
+#define DEFAULT_PSEUDO_MERGE_DECAY 1.0
+#define DEFAULT_PSEUDO_MERGE_MAX_MERGES 64
+#define DEFAULT_PSEUDO_MERGE_SAMPLE_RATE 1
+#define DEFAULT_PSEUDO_MERGE_THRESHOLD approxidate("1.week.ago")
+#define DEFAULT_PSEUDO_MERGE_STABLE_THRESHOLD approxidate("1.month.ago")
+#define DEFAULT_PSEUDO_MERGE_STABLE_SIZE 512
+
+static double gitexp(double base, int exp)
+{
+ double result = 1;
+ while (1) {
+ if (exp % 2)
+ result *= base;
+ exp >>= 1;
+ if (!exp)
+ break;
+ base *= base;
+ }
+ return result;
+}
+
+static uint32_t pseudo_merge_group_size(const struct pseudo_merge_group *group,
+ const struct pseudo_merge_matches *matches,
+ uint32_t i)
+{
+ double C = 0.0f;
+ uint32_t n;
+
+ /*
+ * The size of pseudo-merge groups decays according to a power series,
+ * which looks like:
+ *
+ * f(n) = C * n^-k
+ *
+ * , where 'n' is the n-th pseudo-merge group, 'f(n)' is its size, 'k'
+ * is the decay rate, and 'C' is a scaling value.
+ *
+ * The value of C depends on the number of groups, decay rate, and total
+ * number of commits. It is computed such that if there are M and N
+ * total groups and commits, respectively, that:
+ *
+ * N = f(0) + f(1) + ... f(M-1)
+ *
+ * Rearranging to isolate C, we get:
+ *
+ * N = \sum_{n=1}^M C / n^k
+ *
+ * N / C = \sum_{n=1}^M n^-k
+ *
+ * C = N / \sum_{n=1}^M n^-k
+ *
+ * For example, if we have a decay rate of 'k' being equal to 1.5, 'N'
+ * total commits equal to 10,000, and 'M' being equal to 6 groups, then
+ * the (rounded) group sizes are:
+ *
+ * { 5469, 1934, 1053, 684, 489, 372 }
+ *
+ * increasing the number of total groups, say to 10, scales the group
+ * sizes appropriately:
+ *
+ * { 5012, 1772, 964, 626, 448, 341, 271, 221, 186, 158 }
+ */
+ for (n = 0; n < group->max_merges; n++)
+ C += 1.0 / gitexp(n + 1, group->decay);
+ C = matches->unstable_nr / C;
+
+ return (uint32_t)((C / gitexp(i + 1, group->decay)) + 0.5);
+}
+
+static void pseudo_merge_group_init(struct pseudo_merge_group *group)
+{
+ memset(group, 0, sizeof(struct pseudo_merge_group));
+
+ strmap_init_with_options(&group->matches, NULL, 0);
+
+ group->decay = DEFAULT_PSEUDO_MERGE_DECAY;
+ group->max_merges = DEFAULT_PSEUDO_MERGE_MAX_MERGES;
+ group->sample_rate = DEFAULT_PSEUDO_MERGE_SAMPLE_RATE;
+ group->threshold = DEFAULT_PSEUDO_MERGE_THRESHOLD;
+ group->stable_threshold = DEFAULT_PSEUDO_MERGE_STABLE_THRESHOLD;
+ group->stable_size = DEFAULT_PSEUDO_MERGE_STABLE_SIZE;
+}
+
+static int pseudo_merge_config(const char *var, const char *value,
+ const struct config_context *ctx,
+ void *cb_data)
+{
+ struct string_list *list = cb_data;
+ struct string_list_item *item;
+ struct pseudo_merge_group *group;
+ struct strbuf buf = STRBUF_INIT;
+ const char *sub, *key;
+ size_t sub_len;
+ int ret = 0;
+
+ if (parse_config_key(var, "bitmappseudomerge", &sub, &sub_len, &key))
+ goto done;
+
+ if (!sub_len)
+ goto done;
+
+ strbuf_add(&buf, sub, sub_len);
+
+ item = string_list_lookup(list, buf.buf);
+ if (!item) {
+ item = string_list_insert(list, buf.buf);
+
+ item->util = xmalloc(sizeof(struct pseudo_merge_group));
+ pseudo_merge_group_init(item->util);
+ }
+
+ group = item->util;
+
+ if (!strcmp(key, "pattern")) {
+ struct strbuf re = STRBUF_INIT;
+
+ free(group->pattern);
+ if (*value != '^')
+ strbuf_addch(&re, '^');
+ strbuf_addstr(&re, value);
+
+ group->pattern = xcalloc(1, sizeof(regex_t));
+ if (regcomp(group->pattern, re.buf, REG_EXTENDED))
+ die(_("failed to load pseudo-merge regex for %s: '%s'"),
+ sub, re.buf);
+
+ strbuf_release(&re);
+ } else if (!strcmp(key, "decay")) {
+ group->decay = git_config_double(var, value, ctx->kvi);
+ if (group->decay < 0) {
+ warning(_("%s must be non-negative, using default"), var);
+ group->decay = DEFAULT_PSEUDO_MERGE_DECAY;
+ }
+ } else if (!strcmp(key, "samplerate")) {
+ group->sample_rate = git_config_double(var, value, ctx->kvi);
+ if (!(0 <= group->sample_rate && group->sample_rate <= 1)) {
+ warning(_("%s must be between 0 and 1, using default"), var);
+ group->sample_rate = DEFAULT_PSEUDO_MERGE_SAMPLE_RATE;
+ }
+ } else if (!strcmp(key, "threshold")) {
+ if (git_config_expiry_date(&group->threshold, var, value)) {
+ ret = -1;
+ goto done;
+ }
+ } else if (!strcmp(key, "maxmerges")) {
+ group->max_merges = git_config_int(var, value, ctx->kvi);
+ if (group->max_merges < 0) {
+ warning(_("%s must be non-negative, using default"), var);
+ group->max_merges = DEFAULT_PSEUDO_MERGE_MAX_MERGES;
+ }
+ } else if (!strcmp(key, "stablethreshold")) {
+ if (git_config_expiry_date(&group->stable_threshold, var, value)) {
+ ret = -1;
+ goto done;
+ }
+ } else if (!strcmp(key, "stablesize")) {
+ group->stable_size = git_config_int(var, value, ctx->kvi);
+ if (group->stable_size <= 0) {
+ warning(_("%s must be positive, using default"), var);
+ group->stable_size = DEFAULT_PSEUDO_MERGE_STABLE_SIZE;
+ }
+ }
+
+done:
+ strbuf_release(&buf);
+
+ return ret;
+}
+
+void load_pseudo_merges_from_config(struct string_list *list)
+{
+ struct string_list_item *item;
+
+ git_config(pseudo_merge_config, list);
+
+ for_each_string_list_item(item, list) {
+ struct pseudo_merge_group *group = item->util;
+ if (!group->pattern)
+ die(_("pseudo-merge group '%s' missing required pattern"),
+ item->string);
+ if (group->threshold < group->stable_threshold)
+ die(_("pseudo-merge group '%s' has unstable threshold "
+ "before stable one"), item->string);
+ }
+}
+
+static int find_pseudo_merge_group_for_ref(const char *refname,
+ const struct object_id *oid,
+ int flags UNUSED,
+ void *_data)
+{
+ struct bitmap_writer *writer = _data;
+ struct object_id peeled;
+ struct commit *c;
+ uint32_t i;
+ int has_bitmap;
+
+ if (!peel_iterated_oid(the_repository, oid, &peeled))
+ oid = &peeled;
+
+ c = lookup_commit(the_repository, oid);
+ if (!c)
+ return 0;
+
+ has_bitmap = bitmap_writer_has_bitmapped_object_id(writer, oid);
+
+ for (i = 0; i < writer->pseudo_merge_groups.nr; i++) {
+ struct pseudo_merge_group *group;
+ struct pseudo_merge_matches *matches;
+ struct strbuf group_name = STRBUF_INIT;
+ regmatch_t captures[16];
+ size_t j;
+
+ group = writer->pseudo_merge_groups.items[i].util;
+ if (regexec(group->pattern, refname, ARRAY_SIZE(captures),
+ captures, 0))
+ continue;
+
+ if (captures[ARRAY_SIZE(captures) - 1].rm_so != -1)
+ warning(_("pseudo-merge regex from config has too many capture "
+ "groups (max=%"PRIuMAX")"),
+ (uintmax_t)ARRAY_SIZE(captures) - 2);
+
+ for (j = !!group->pattern->re_nsub; j < ARRAY_SIZE(captures); j++) {
+ regmatch_t *match = &captures[j];
+ if (match->rm_so == -1)
+ continue;
+
+ if (group_name.len)
+ strbuf_addch(&group_name, '-');
+
+ strbuf_add(&group_name, refname + match->rm_so,
+ match->rm_eo - match->rm_so);
+ }
+
+ matches = strmap_get(&group->matches, group_name.buf);
+ if (!matches) {
+ matches = xcalloc(1, sizeof(*matches));
+ strmap_put(&group->matches, strbuf_detach(&group_name, NULL),
+ matches);
+ }
+
+ if (c->date <= group->stable_threshold) {
+ ALLOC_GROW(matches->stable, matches->stable_nr + 1,
+ matches->stable_alloc);
+ matches->stable[matches->stable_nr++] = c;
+ } else if (c->date <= group->threshold && !has_bitmap) {
+ ALLOC_GROW(matches->unstable, matches->unstable_nr + 1,
+ matches->unstable_alloc);
+ matches->unstable[matches->unstable_nr++] = c;
+ }
+
+ strbuf_release(&group_name);
+ }
+
+ return 0;
+}
+
+static struct commit *push_pseudo_merge(struct pseudo_merge_group *group)
+{
+ struct commit *merge;
+
+ ALLOC_GROW(group->merges, group->merges_nr + 1, group->merges_alloc);
+
+ merge = alloc_commit_node(the_repository);
+ merge->object.parsed = 1;
+ merge->object.flags |= BITMAP_PSEUDO_MERGE;
+
+ group->merges[group->merges_nr++] = merge;
+
+ return merge;
+}
+
+static struct pseudo_merge_commit_idx *pseudo_merge_idx(kh_oid_map_t *pseudo_merge_commits,
+ const struct object_id *oid)
+
+{
+ struct pseudo_merge_commit_idx *pmc;
+ int hash_ret;
+ khiter_t hash_pos = kh_put_oid_map(pseudo_merge_commits, *oid,
+ &hash_ret);
+
+ if (hash_ret) {
+ CALLOC_ARRAY(pmc, 1);
+ kh_value(pseudo_merge_commits, hash_pos) = pmc;
+ } else {
+ pmc = kh_value(pseudo_merge_commits, hash_pos);
+ }
+
+ return pmc;
+}
+
+#define MIN_PSEUDO_MERGE_SIZE 8
+
+static void select_pseudo_merges_1(struct bitmap_writer *writer,
+ struct pseudo_merge_group *group,
+ struct pseudo_merge_matches *matches)
+{
+ uint32_t i, j;
+ uint32_t stable_merges_nr;
+
+ if (!matches->stable_nr && !matches->unstable_nr)
+ return; /* all tips in this group already have bitmaps */
+
+ stable_merges_nr = matches->stable_nr / group->stable_size;
+ if (matches->stable_nr % group->stable_size)
+ stable_merges_nr++;
+
+ /* make stable_merges_nr pseudo merges for stable commits */
+ for (i = 0, j = 0; i < stable_merges_nr; i++) {
+ struct commit *merge;
+ struct commit_list **p;
+
+ merge = push_pseudo_merge(group);
+ p = &merge->parents;
+
+ /*
+ * For each pseudo-merge created above, add parents to the
+ * allocated commit node from the stable set of commits
+ * (un-bitmapped, newer than the stable threshold).
+ */
+ do {
+ struct commit *c;
+ struct pseudo_merge_commit_idx *pmc;
+
+ if (j >= matches->stable_nr)
+ break;
+
+ c = matches->stable[j++];
+ /*
+ * Here and below, make sure that we keep our mapping of
+ * commits -> pseudo-merge(s) which include the key'd
+ * commit up-to-date.
+ */
+ pmc = pseudo_merge_idx(writer->pseudo_merge_commits,
+ &c->object.oid);
+
+ ALLOC_GROW(pmc->pseudo_merge, pmc->nr + 1, pmc->alloc);
+
+ pmc->pseudo_merge[pmc->nr++] = writer->pseudo_merges_nr;
+ p = commit_list_append(c, p);
+ } while (j % group->stable_size);
+
+ bitmap_writer_push_commit(writer, merge, 1);
+ writer->pseudo_merges_nr++;
+ }
+
+ /* make up to group->max_merges pseudo merges for unstable commits */
+ for (i = 0, j = 0; i < group->max_merges; i++) {
+ struct commit *merge;
+ struct commit_list **p;
+ uint32_t size, end;
+
+ merge = push_pseudo_merge(group);
+ p = &merge->parents;
+
+ size = pseudo_merge_group_size(group, matches, i);
+ end = size < MIN_PSEUDO_MERGE_SIZE ? matches->unstable_nr : j + size;
+
+ /*
+ * For each pseudo-merge commit created above, add parents to
+ * the allocated commit node from the unstable set of commits
+ * (newer than the stable threshold).
+ *
+ * Account for the sample rate, since not every candidate from
+ * the set of stable commits will be included as a pseudo-merge
+ * parent.
+ */
+ for (; j < end && j < matches->unstable_nr; j++) {
+ struct commit *c = matches->unstable[j];
+ struct pseudo_merge_commit_idx *pmc;
+
+ if (j % (uint32_t)(1.0 / group->sample_rate))
+ continue;
+
+ pmc = pseudo_merge_idx(writer->pseudo_merge_commits,
+ &c->object.oid);
+
+ ALLOC_GROW(pmc->pseudo_merge, pmc->nr + 1, pmc->alloc);
+
+ pmc->pseudo_merge[pmc->nr++] = writer->pseudo_merges_nr;
+ p = commit_list_append(c, p);
+ }
+
+ bitmap_writer_push_commit(writer, merge, 1);
+ writer->pseudo_merges_nr++;
+ if (end >= matches->unstable_nr)
+ break;
+ }
+}
+
+static int commit_date_cmp(const void *va, const void *vb)
+{
+ timestamp_t a = (*(const struct commit **)va)->date;
+ timestamp_t b = (*(const struct commit **)vb)->date;
+
+ if (a < b)
+ return -1;
+ else if (a > b)
+ return 1;
+ return 0;
+}
+
+static void sort_pseudo_merge_matches(struct pseudo_merge_matches *matches)
+{
+ QSORT(matches->stable, matches->stable_nr, commit_date_cmp);
+ QSORT(matches->unstable, matches->unstable_nr, commit_date_cmp);
+}
+
+void select_pseudo_merges(struct bitmap_writer *writer,
+ struct commit **commits, size_t commits_nr)
+{
+ struct progress *progress = NULL;
+ uint32_t i;
+
+ if (!writer->pseudo_merge_groups.nr)
+ return;
+
+ if (writer->show_progress)
+ progress = start_progress("Selecting pseudo-merge commits",
+ writer->pseudo_merge_groups.nr);
+
+ refs_for_each_ref(get_main_ref_store(the_repository),
+ find_pseudo_merge_group_for_ref, writer);
+
+ for (i = 0; i < writer->pseudo_merge_groups.nr; i++) {
+ struct pseudo_merge_group *group;
+ struct hashmap_iter iter;
+ struct strmap_entry *e;
+
+ group = writer->pseudo_merge_groups.items[i].util;
+ strmap_for_each_entry(&group->matches, &iter, e) {
+ struct pseudo_merge_matches *matches = e->value;
+
+ sort_pseudo_merge_matches(matches);
+
+ select_pseudo_merges_1(writer, group, matches);
+ }
+
+ display_progress(progress, i + 1);
+ }
+
+ stop_progress(&progress);
+}
+
+void free_pseudo_merge_map(struct pseudo_merge_map *pm)
+{
+ uint32_t i;
+ for (i = 0; i < pm->nr; i++) {
+ ewah_pool_free(pm->v[i].commits);
+ ewah_pool_free(pm->v[i].bitmap);
+ }
+ free(pm->v);
+}
+
+struct pseudo_merge_commit_ext {
+ uint32_t nr;
+ const unsigned char *ptr;
+};
+
+static int pseudo_merge_ext_at(const struct pseudo_merge_map *pm,
+ struct pseudo_merge_commit_ext *ext, size_t at)
+{
+ if (at >= pm->map_size)
+ return error(_("extended pseudo-merge read out-of-bounds "
+ "(%"PRIuMAX" >= %"PRIuMAX")"),
+ (uintmax_t)at, (uintmax_t)pm->map_size);
+ if (at + 4 >= pm->map_size)
+ return error(_("extended pseudo-merge entry is too short "
+ "(%"PRIuMAX" >= %"PRIuMAX")"),
+ (uintmax_t)(at + 4), (uintmax_t)pm->map_size);
+
+ ext->nr = get_be32(pm->map + at);
+ ext->ptr = pm->map + at + sizeof(uint32_t);
+
+ return 0;
+}
+
+struct ewah_bitmap *pseudo_merge_bitmap(const struct pseudo_merge_map *pm,
+ struct pseudo_merge *merge)
+{
+ if (!merge->loaded_commits)
+ BUG("cannot use unloaded pseudo-merge bitmap");
+
+ if (!merge->loaded_bitmap) {
+ size_t at = merge->bitmap_at;
+
+ merge->bitmap = read_bitmap(pm->map, pm->map_size, &at);
+ merge->loaded_bitmap = 1;
+ }
+
+ return merge->bitmap;
+}
+
+struct pseudo_merge *use_pseudo_merge(const struct pseudo_merge_map *pm,
+ struct pseudo_merge *merge)
+{
+ if (!merge->loaded_commits) {
+ size_t pos = merge->at;
+
+ merge->commits = read_bitmap(pm->map, pm->map_size, &pos);
+ merge->bitmap_at = pos;
+ merge->loaded_commits = 1;
+ }
+ return merge;
+}
+
+static struct pseudo_merge *pseudo_merge_at(const struct pseudo_merge_map *pm,
+ struct object_id *oid,
+ size_t want)
+{
+ size_t lo = 0;
+ size_t hi = pm->nr;
+
+ while (lo < hi) {
+ size_t mi = lo + (hi - lo) / 2;
+ size_t got = pm->v[mi].at;
+
+ if (got == want)
+ return use_pseudo_merge(pm, &pm->v[mi]);
+ else if (got < want)
+ hi = mi;
+ else
+ lo = mi + 1;
+ }
+
+ warning(_("could not find pseudo-merge for commit %s at offset %"PRIuMAX),
+ oid_to_hex(oid), (uintmax_t)want);
+
+ return NULL;
+}
+
+struct pseudo_merge_commit {
+ uint32_t commit_pos;
+ uint64_t pseudo_merge_ofs;
+};
+
+#define PSEUDO_MERGE_COMMIT_RAWSZ (sizeof(uint32_t)+sizeof(uint64_t))
+
+static void read_pseudo_merge_commit_at(struct pseudo_merge_commit *merge,
+ const unsigned char *at)
+{
+ merge->commit_pos = get_be32(at);
+ merge->pseudo_merge_ofs = get_be64(at + sizeof(uint32_t));
+}
+
+static int nth_pseudo_merge_ext(const struct pseudo_merge_map *pm,
+ struct pseudo_merge_commit_ext *ext,
+ struct pseudo_merge_commit *merge,
+ uint32_t n)
+{
+ size_t ofs;
+
+ if (n >= ext->nr)
+ return error(_("extended pseudo-merge lookup out-of-bounds "
+ "(%"PRIu32" >= %"PRIu32")"), n, ext->nr);
+
+ ofs = get_be64(ext->ptr + st_mult(n, sizeof(uint64_t)));
+ if (ofs >= pm->map_size)
+ return error(_("out-of-bounds read: (%"PRIuMAX" >= %"PRIuMAX")"),
+ (uintmax_t)ofs, (uintmax_t)pm->map_size);
+
+ read_pseudo_merge_commit_at(merge, pm->map + ofs);
+
+ return 0;
+}
+
+static unsigned apply_pseudo_merge(const struct pseudo_merge_map *pm,
+ struct pseudo_merge *merge,
+ struct bitmap *result,
+ struct bitmap *roots)
+{
+ if (merge->satisfied)
+ return 0;
+
+ if (!ewah_bitmap_is_subset(merge->commits, roots ? roots : result))
+ return 0;
+
+ bitmap_or_ewah(result, pseudo_merge_bitmap(pm, merge));
+ if (roots)
+ bitmap_or_ewah(roots, pseudo_merge_bitmap(pm, merge));
+ merge->satisfied = 1;
+
+ return 1;
+}
+
+static int pseudo_merge_commit_cmp(const void *va, const void *vb)
+{
+ struct pseudo_merge_commit merge;
+ uint32_t key = *(uint32_t*)va;
+
+ read_pseudo_merge_commit_at(&merge, vb);
+
+ if (key < merge.commit_pos)
+ return -1;
+ if (key > merge.commit_pos)
+ return 1;
+ return 0;
+}
+
+static struct pseudo_merge_commit *find_pseudo_merge(const struct pseudo_merge_map *pm,
+ uint32_t pos)
+{
+ if (!pm->commits_nr)
+ return NULL;
+
+ return bsearch(&pos, pm->commits, pm->commits_nr,
+ PSEUDO_MERGE_COMMIT_RAWSZ, pseudo_merge_commit_cmp);
+}
+
+int apply_pseudo_merges_for_commit(const struct pseudo_merge_map *pm,
+ struct bitmap *result,
+ struct commit *commit, uint32_t commit_pos)
+{
+ struct pseudo_merge *merge;
+ struct pseudo_merge_commit *merge_commit;
+ int ret = 0;
+
+ merge_commit = find_pseudo_merge(pm, commit_pos);
+ if (!merge_commit)
+ return 0;
+
+ if (merge_commit->pseudo_merge_ofs & ((uint64_t)1<<63)) {
+ struct pseudo_merge_commit_ext ext = { 0 };
+ off_t ofs = merge_commit->pseudo_merge_ofs & ~((uint64_t)1<<63);
+ uint32_t i;
+
+ if (pseudo_merge_ext_at(pm, &ext, ofs) < -1) {
+ warning(_("could not read extended pseudo-merge table "
+ "for commit %s"),
+ oid_to_hex(&commit->object.oid));
+ return ret;
+ }
+
+ for (i = 0; i < ext.nr; i++) {
+ if (nth_pseudo_merge_ext(pm, &ext, merge_commit, i) < 0)
+ return ret;
+
+ merge = pseudo_merge_at(pm, &commit->object.oid,
+ merge_commit->pseudo_merge_ofs);
+
+ if (!merge)
+ return ret;
+
+ if (apply_pseudo_merge(pm, merge, result, NULL))
+ ret++;
+ }
+ } else {
+ merge = pseudo_merge_at(pm, &commit->object.oid,
+ merge_commit->pseudo_merge_ofs);
+
+ if (!merge)
+ return ret;
+
+ if (apply_pseudo_merge(pm, merge, result, NULL))
+ ret++;
+ }
+
+ if (ret)
+ cascade_pseudo_merges(pm, result, NULL);
+
+ return ret;
+}
+
+int cascade_pseudo_merges(const struct pseudo_merge_map *pm,
+ struct bitmap *result,
+ struct bitmap *roots)
+{
+ unsigned any_satisfied;
+ int ret = 0;
+
+ do {
+ struct pseudo_merge *merge;
+ uint32_t i;
+
+ any_satisfied = 0;
+
+ for (i = 0; i < pm->nr; i++) {
+ merge = use_pseudo_merge(pm, &pm->v[i]);
+ if (apply_pseudo_merge(pm, merge, result, roots)) {
+ any_satisfied |= 1;
+ ret++;
+ }
+ }
+ } while (any_satisfied);
+
+ return ret;
+}
+
+struct pseudo_merge *pseudo_merge_for_parents(const struct pseudo_merge_map *pm,
+ struct bitmap *parents)
+{
+ struct pseudo_merge *match = NULL;
+ size_t i;
+
+ if (!pm->nr)
+ return NULL;
+
+ /*
+ * NOTE: this loop is quadratic in the worst-case (where no
+ * matching pseudo-merge bitmaps are found), but in practice
+ * this is OK for a few reasons:
+ *
+ * - Rejecting pseudo-merge bitmaps that do not match the
+ * given commit is done quickly (i.e. `bitmap_equals_ewah()`
+ * returns early when we know the two bitmaps aren't equal.
+ *
+ * - Already matched pseudo-merge bitmaps (which we track with
+ * the `->satisfied` bit here) are skipped as potential
+ * candidates.
+ *
+ * - The number of pseudo-merges should be small (in the
+ * hundreds for most repositories).
+ *
+ * If in the future this semi-quadratic behavior does become a
+ * problem, another approach would be to keep track of which
+ * pseudo-merges are still "viable" after enumerating the
+ * pseudo-merge commit's parents:
+ *
+ * - A pseudo-merge bitmap becomes non-viable when the bit(s)
+ * corresponding to one or more parent(s) of the given
+ * commit are not set in a candidate pseudo-merge's commits
+ * bitmap.
+ *
+ * - After processing all bits, enumerate the remaining set of
+ * viable pseudo-merge bitmaps, and check that their
+ * popcount() matches the number of parents in the given
+ * commit.
+ */
+ for (i = 0; i < pm->nr; i++) {
+ struct pseudo_merge *candidate = use_pseudo_merge(pm, &pm->v[i]);
+ if (!candidate || candidate->satisfied)
+ continue;
+ if (!bitmap_equals_ewah(parents, candidate->commits))
+ continue;
+
+ match = candidate;
+ match->satisfied = 1;
+ break;
+ }
+
+ return match;
+}
diff --git a/pseudo-merge.h b/pseudo-merge.h
new file mode 100644
index 0000000000..2aca01d056
--- /dev/null
+++ b/pseudo-merge.h
@@ -0,0 +1,216 @@
+#ifndef PSEUDO_MERGE_H
+#define PSEUDO_MERGE_H
+
+#include "git-compat-util.h"
+#include "strmap.h"
+#include "khash.h"
+#include "ewah/ewok.h"
+
+struct commit;
+struct string_list;
+struct bitmap_index;
+struct bitmap_writer;
+
+/*
+ * A pseudo-merge group tracks the set of non-bitmapped reference tips
+ * that match the given pattern.
+ *
+ * Within those matches, they are further segmented by separating
+ * consecutive capture groups with '-' dash character capture groups
+ * with '-' dash characters.
+ *
+ * Those groups are then ordered by committer date and partitioned
+ * into individual pseudo-merge(s) according to the decay, max_merges,
+ * sample_rate, and threshold parameters.
+ */
+struct pseudo_merge_group {
+ regex_t *pattern;
+
+ /* capture group(s) -> struct pseudo_merge_matches */
+ struct strmap matches;
+
+ /*
+ * The individual pseudo-merge(s) that are generated from the
+ * above array of matches, partitioned according to the below
+ * parameters.
+ */
+ struct commit **merges;
+ size_t merges_nr;
+ size_t merges_alloc;
+
+ /*
+ * Pseudo-merge grouping parameters. See git-config(1) for
+ * more information.
+ */
+ double decay;
+ int max_merges;
+ double sample_rate;
+ int stable_size;
+ timestamp_t threshold;
+ timestamp_t stable_threshold;
+};
+
+struct pseudo_merge_matches {
+ struct commit **stable;
+ struct commit **unstable;
+ size_t stable_nr, stable_alloc;
+ size_t unstable_nr, unstable_alloc;
+};
+
+/*
+ * Read the repository's configuration:
+ *
+ * - bitmapPseudoMerge.<name>.pattern
+ * - bitmapPseudoMerge.<name>.decay
+ * - bitmapPseudoMerge.<name>.sampleRate
+ * - bitmapPseudoMerge.<name>.threshold
+ * - bitmapPseudoMerge.<name>.maxMerges
+ * - bitmapPseudoMerge.<name>.stableThreshold
+ * - bitmapPseudoMerge.<name>.stableSize
+ *
+ * and populates the given `list` with pseudo-merge groups. String
+ * entry keys are the pseudo-merge group names, and the values are
+ * pointers to the pseudo_merge_group structure itself.
+ */
+void load_pseudo_merges_from_config(struct string_list *list);
+
+/*
+ * A pseudo-merge commit index (pseudo_merge_commit_idx) maps a
+ * particular (non-pseudo-merge) commit to the list of pseudo-merge(s)
+ * it appears in.
+ */
+struct pseudo_merge_commit_idx {
+ uint32_t *pseudo_merge;
+ size_t nr, alloc;
+};
+
+/*
+ * Selects pseudo-merges from a list of commits, populating the given
+ * string_list of pseudo-merge groups.
+ *
+ * Populates the pseudo_merge_commits map with a commit_idx
+ * corresponding to each commit in the list. Counts the total number
+ * of pseudo-merges generated.
+ *
+ * Optionally shows a progress meter.
+ */
+void select_pseudo_merges(struct bitmap_writer *writer,
+ struct commit **commits, size_t commits_nr);
+
+/*
+ * Represents a serialized view of a file containing pseudo-merge(s)
+ * (see Documentation/technical/bitmap-format.txt for a specification
+ * of the format).
+ */
+struct pseudo_merge_map {
+ /*
+ * An array of pseudo-merge(s), lazily loaded from the .bitmap
+ * file.
+ */
+ struct pseudo_merge *v;
+ size_t nr;
+ size_t commits_nr;
+
+ /*
+ * Pointers into a memory-mapped view of the .bitmap file:
+ *
+ * - map: the beginning of the .bitmap file
+ * - commits: the beginning of the pseudo-merge commit index
+ * - map_size: the size of the .bitmap file
+ */
+ const unsigned char *map;
+ const unsigned char *commits;
+
+ size_t map_size;
+};
+
+/*
+ * An individual pseudo-merge, storing a pair of lazily-loaded
+ * bitmaps:
+ *
+ * - commits: the set of commit(s) that are part of the pseudo-merge
+ * - bitmap: the set of object(s) reachable from the above set of
+ * commits.
+ *
+ * The `at` and `bitmap_at` fields are used to store the locations of
+ * each of the above bitmaps in the .bitmap file.
+ */
+struct pseudo_merge {
+ struct ewah_bitmap *commits;
+ struct ewah_bitmap *bitmap;
+
+ off_t at;
+ off_t bitmap_at;
+
+ /*
+ * `satisfied` indicates whether the given pseudo-merge has been
+ * used.
+ *
+ * `loaded_commits` and `loaded_bitmap` indicate whether the
+ * respective bitmaps have been loaded and read from the
+ * .bitmap file.
+ */
+ unsigned satisfied : 1,
+ loaded_commits : 1,
+ loaded_bitmap : 1;
+};
+
+/*
+ * Frees the given pseudo-merge map, releasing any memory held by (a)
+ * parsed EWAH bitmaps, or (b) the array of pseudo-merges itself. Does
+ * not free the memory-mapped view of the .bitmap file.
+ */
+void free_pseudo_merge_map(struct pseudo_merge_map *pm);
+
+/*
+ * Loads the bitmap corresponding to the given pseudo-merge from the
+ * map, if it has not already been loaded.
+ */
+struct ewah_bitmap *pseudo_merge_bitmap(const struct pseudo_merge_map *pm,
+ struct pseudo_merge *merge);
+
+/*
+ * Loads the pseudo-merge and its commits bitmap from the given
+ * pseudo-merge map, if it has not already been loaded.
+ */
+struct pseudo_merge *use_pseudo_merge(const struct pseudo_merge_map *pm,
+ struct pseudo_merge *merge);
+
+/*
+ * Applies pseudo-merge(s) containing the given commit to the bitmap
+ * "result".
+ *
+ * If any pseudo-merge(s) were satisfied, returns the number
+ * satisfied, otherwise returns 0. If any were satisfied, the
+ * remaining unsatisfied pseudo-merges are cascaded (see below).
+ */
+int apply_pseudo_merges_for_commit(const struct pseudo_merge_map *pm,
+ struct bitmap *result,
+ struct commit *commit, uint32_t commit_pos);
+
+/*
+ * Applies pseudo-merge(s) which are satisfied according to the
+ * current bitmap in result (or roots, see below). If any
+ * pseudo-merges were satisfied, repeat the process over unsatisfied
+ * pseudo-merge commits until no more pseudo-merges are satisfied.
+ *
+ * Result is the bitmap to which the pseudo-merge(s) are applied.
+ * Roots (if given) is a bitmap of the traversal tip(s) for either
+ * side of a reachability traversal.
+ *
+ * Roots may given instead of a populated results bitmap at the
+ * beginning of a traversal on either side where the reachability
+ * closure over tips is not yet known.
+ */
+int cascade_pseudo_merges(const struct pseudo_merge_map *pm,
+ struct bitmap *result,
+ struct bitmap *roots);
+
+/*
+ * Returns a pseudo-merge which contains the exact set of commits
+ * listed in the "parents" bitamp, or NULL if none could be found.
+ */
+struct pseudo_merge *pseudo_merge_for_parents(const struct pseudo_merge_map *pm,
+ struct bitmap *parents);
+
+#endif
diff --git a/range-diff.c b/range-diff.c
index c45b6d849c..5f01605550 100644
--- a/range-diff.c
+++ b/range-diff.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "environment.h"
#include "gettext.h"
diff --git a/range-diff.h b/range-diff.h
index 04ffe217be..2f69f6a434 100644
--- a/range-diff.h
+++ b/range-diff.h
@@ -6,6 +6,12 @@
#define RANGE_DIFF_CREATION_FACTOR_DEFAULT 60
+/*
+ * A much higher value than the default, when we KNOW we are comparing
+ * the same series (e.g., used when format-patch calls range-diff).
+ */
+#define CREATION_FACTOR_FOR_THE_SAME_SERIES 999
+
struct range_diff_options {
int creation_factor;
unsigned dual_color:1;
diff --git a/reachable.c b/reachable.c
index 3b85add243..46613a6bb6 100644
--- a/reachable.c
+++ b/reachable.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "gettext.h"
#include "hex.h"
@@ -363,10 +365,11 @@ void mark_reachable_objects(struct rev_info *revs, int mark_reflog,
add_index_objects_to_pending(revs, 0);
/* Add all external refs */
- for_each_ref(add_one_ref, revs);
+ refs_for_each_ref(get_main_ref_store(the_repository), add_one_ref,
+ revs);
/* detached HEAD is not included in the list above */
- head_ref(add_one_ref, revs);
+ refs_head_ref(get_main_ref_store(the_repository), add_one_ref, revs);
other_head_refs(add_one_ref, revs);
/* rebase autostash and orig-head */
diff --git a/read-cache-ll.h b/read-cache-ll.h
index 09414afd04..e0e39607ef 100644
--- a/read-cache-ll.h
+++ b/read-cache-ll.h
@@ -1,7 +1,7 @@
#ifndef READ_CACHE_LL_H
#define READ_CACHE_LL_H
-#include "hash-ll.h"
+#include "hash.h"
#include "hashmap.h"
#include "statinfo.h"
diff --git a/read-cache.c b/read-cache.c
index e1723ad796..48bf24f87c 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -3,6 +3,9 @@
*
* Copyright (C) Linus Torvalds, 2005
*/
+
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "bulk-checkin.h"
#include "config.h"
@@ -271,7 +274,8 @@ static int ce_compare_gitlink(const struct cache_entry *ce)
*
* If so, we consider it always to match.
*/
- if (resolve_gitlink_ref(ce->name, "HEAD", &oid) < 0)
+ if (repo_resolve_gitlink_ref(the_repository, ce->name,
+ "HEAD", &oid) < 0)
return 0;
return !oideq(&oid, &ce->oid);
}
@@ -336,7 +340,7 @@ static int ce_match_stat_basic(const struct cache_entry *ce, struct stat *st)
/* Racily smudged entry? */
if (!ce->ce_stat_data.sd_size) {
- if (!is_empty_blob_sha1(ce->oid.hash))
+ if (!is_empty_blob_oid(&ce->oid, the_repository->hash_algo))
changed |= DATA_CHANGED;
}
@@ -711,7 +715,7 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st,
namelen = strlen(path);
if (S_ISDIR(st_mode)) {
- if (resolve_gitlink_ref(path, "HEAD", &oid) < 0)
+ if (repo_resolve_gitlink_ref(the_repository, path, "HEAD", &oid) < 0)
return error(_("'%s' does not have a commit checked out"), path);
while (namelen && path[namelen-1] == '/')
namelen--;
@@ -1116,19 +1120,32 @@ static int has_dir_name(struct index_state *istate,
istate->cache[istate->cache_nr - 1]->name,
&len_eq_last);
if (cmp_last > 0) {
- if (len_eq_last == 0) {
+ if (name[len_eq_last] != '/') {
/*
* The entry sorts AFTER the last one in the
- * index and their paths have no common prefix,
- * so there cannot be a F/D conflict.
+ * index.
+ *
+ * If there were a conflict with "file", then our
+ * name would start with "file/" and the last index
+ * entry would start with "file" but not "file/".
+ *
+ * The next character after common prefix is
+ * not '/', so there can be no conflict.
*/
return retval;
} else {
/*
* The entry sorts AFTER the last one in the
- * index, but has a common prefix. Fall through
- * to the loop below to disect the entry's path
- * and see where the difference is.
+ * index, and the next character after common
+ * prefix is '/'.
+ *
+ * Either the last index entry is a file in
+ * conflict with this entry, or it has a name
+ * which sorts between this entry and the
+ * potential conflicting file.
+ *
+ * In both cases, we fall through to the loop
+ * below and let the regular search code handle it.
*/
}
} else if (cmp_last == 0) {
@@ -1152,53 +1169,6 @@ static int has_dir_name(struct index_state *istate,
}
len = slash - name;
- if (cmp_last > 0) {
- /*
- * (len + 1) is a directory boundary (including
- * the trailing slash). And since the loop is
- * decrementing "slash", the first iteration is
- * the longest directory prefix; subsequent
- * iterations consider parent directories.
- */
-
- if (len + 1 <= len_eq_last) {
- /*
- * The directory prefix (including the trailing
- * slash) also appears as a prefix in the last
- * entry, so the remainder cannot collide (because
- * strcmp said the whole path was greater).
- *
- * EQ: last: xxx/A
- * this: xxx/B
- *
- * LT: last: xxx/file_A
- * this: xxx/file_B
- */
- return retval;
- }
-
- if (len > len_eq_last) {
- /*
- * This part of the directory prefix (excluding
- * the trailing slash) is longer than the known
- * equal portions, so this sub-directory cannot
- * collide with a file.
- *
- * GT: last: xxxA
- * this: xxxB/file
- */
- return retval;
- }
-
- /*
- * This is a possible collision. Fall through and
- * let the regular search code handle it.
- *
- * last: xxx
- * this: xxx/file
- */
- }
-
pos = index_name_stage_pos(istate, name, len, stage, EXPAND_SPARSE);
if (pos >= 0) {
/*
@@ -1761,14 +1731,14 @@ static int verify_hdr(const struct cache_header *hdr, unsigned long size)
end = (unsigned char *)hdr + size;
start = end - the_hash_algo->rawsz;
- oidread(&oid, start);
+ oidread(&oid, start, the_repository->hash_algo);
if (oideq(&oid, null_oid()))
return 0;
the_hash_algo->init_fn(&c);
the_hash_algo->update_fn(&c, hdr, size - the_hash_algo->rawsz);
the_hash_algo->final_fn(hash, &c);
- if (!hasheq(hash, start))
+ if (!hasheq(hash, start, the_repository->hash_algo))
return error(_("bad index file sha1 signature"));
return 0;
}
@@ -1909,7 +1879,8 @@ static struct cache_entry *create_from_disk(struct mem_pool *ce_mem_pool,
ce->ce_flags = flags & ~CE_NAMEMASK;
ce->ce_namelen = len;
ce->index = 0;
- oidread(&ce->oid, (const unsigned char *)ondisk + offsetof(struct ondisk_cache_entry, data));
+ oidread(&ce->oid, (const unsigned char *)ondisk + offsetof(struct ondisk_cache_entry, data),
+ the_repository->hash_algo);
if (expand_name_field) {
if (copy_len)
@@ -2282,7 +2253,8 @@ int do_read_index(struct index_state *istate, const char *path, int must_exist)
if (verify_hdr(hdr, mmap_size) < 0)
goto unmap;
- oidread(&istate->oid, (const unsigned char *)hdr + mmap_size - the_hash_algo->rawsz);
+ oidread(&istate->oid, (const unsigned char *)hdr + mmap_size - the_hash_algo->rawsz,
+ the_repository->hash_algo);
istate->version = ntohl(hdr->hdr_version);
istate->cache_nr = ntohl(hdr->hdr_entries);
istate->cache_alloc = alloc_nr(istate->cache_nr);
@@ -2674,7 +2646,7 @@ static void copy_cache_entry_to_ondisk(struct ondisk_cache_entry *ondisk,
ondisk->uid = htonl(ce->ce_stat_data.sd_uid);
ondisk->gid = htonl(ce->ce_stat_data.sd_gid);
ondisk->size = htonl(ce->ce_stat_data.sd_size);
- hashcpy(ondisk->data, ce->oid.hash);
+ hashcpy(ondisk->data, ce->oid.hash, the_repository->hash_algo);
flags = ce->ce_flags & ~CE_NAMEMASK;
flags |= (ce_namelen(ce) >= CE_NAMEMASK ? CE_NAMEMASK : ce_namelen(ce));
@@ -2763,7 +2735,7 @@ static int verify_index_from(const struct index_state *istate, const char *path)
if (n != the_hash_algo->rawsz)
goto out;
- if (!hasheq(istate->oid.hash, hash))
+ if (!hasheq(istate->oid.hash, hash, the_repository->hash_algo))
goto out;
close(fd);
@@ -3636,7 +3608,7 @@ static size_t read_eoie_extension(const char *mmap, size_t mmap_size)
src_offset += extsize;
}
the_hash_algo->final_fn(hash, &c);
- if (!hasheq(hash, (const unsigned char *)index))
+ if (!hasheq(hash, (const unsigned char *)index, the_repository->hash_algo))
return 0;
/* Validate that the extension offsets returned us back to the eoie extension. */
diff --git a/rebase-interactive.c b/rebase-interactive.c
index c343e16fcd..cbeb864147 100644
--- a/rebase-interactive.c
+++ b/rebase-interactive.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "commit.h"
#include "editor.h"
@@ -101,9 +103,10 @@ void append_todo_help(int command_count,
strbuf_add_commented_lines(buf, msg, strlen(msg), comment_line_str);
}
-int edit_todo_list(struct repository *r, struct todo_list *todo_list,
- struct todo_list *new_todo, const char *shortrevisions,
- const char *shortonto, unsigned flags)
+int edit_todo_list(struct repository *r, struct replay_opts *opts,
+ struct todo_list *todo_list, struct todo_list *new_todo,
+ const char *shortrevisions, const char *shortonto,
+ unsigned flags)
{
const char *todo_file = rebase_path_todo(),
*todo_backup = rebase_path_todo_backup();
@@ -114,7 +117,9 @@ int edit_todo_list(struct repository *r, struct todo_list *todo_list,
* it. If there is an error, we do not return, because the user
* might want to fix it in the first place. */
if (!initial)
- incorrect = todo_list_parse_insn_buffer(r, todo_list->buf.buf, todo_list) |
+ incorrect = todo_list_parse_insn_buffer(r, opts,
+ todo_list->buf.buf,
+ todo_list) |
file_exists(rebase_path_dropped());
if (todo_list_write_to_file(r, todo_list, todo_file, shortrevisions, shortonto,
@@ -134,13 +139,13 @@ int edit_todo_list(struct repository *r, struct todo_list *todo_list,
if (initial && new_todo->buf.len == 0)
return -3;
- if (todo_list_parse_insn_buffer(r, new_todo->buf.buf, new_todo)) {
+ if (todo_list_parse_insn_buffer(r, opts, new_todo->buf.buf, new_todo)) {
fprintf(stderr, _(edit_todo_list_advice));
return -4;
}
if (incorrect) {
- if (todo_list_check_against_backup(r, new_todo)) {
+ if (todo_list_check_against_backup(r, opts, new_todo)) {
write_file(rebase_path_dropped(), "%s", "");
return -4;
}
@@ -228,13 +233,15 @@ leave_check:
return res;
}
-int todo_list_check_against_backup(struct repository *r, struct todo_list *todo_list)
+int todo_list_check_against_backup(struct repository *r,
+ struct replay_opts *opts,
+ struct todo_list *todo_list)
{
struct todo_list backup = TODO_LIST_INIT;
int res = 0;
if (strbuf_read_file(&backup.buf, rebase_path_todo_backup(), 0) > 0) {
- todo_list_parse_insn_buffer(r, backup.buf.buf, &backup);
+ todo_list_parse_insn_buffer(r, opts, backup.buf.buf, &backup);
res = todo_list_check(&backup, todo_list);
}
diff --git a/rebase-interactive.h b/rebase-interactive.h
index 7239c60f79..8e5b181b33 100644
--- a/rebase-interactive.h
+++ b/rebase-interactive.h
@@ -3,17 +3,20 @@
struct strbuf;
struct repository;
+struct replay_opts;
struct todo_list;
void append_todo_help(int command_count,
const char *shortrevisions, const char *shortonto,
struct strbuf *buf);
-int edit_todo_list(struct repository *r, struct todo_list *todo_list,
- struct todo_list *new_todo, const char *shortrevisions,
- const char *shortonto, unsigned flags);
+int edit_todo_list(struct repository *r, struct replay_opts *opts,
+ struct todo_list *todo_list, struct todo_list *new_todo,
+ const char *shortrevisions, const char *shortonto,
+ unsigned flags);
int todo_list_check(struct todo_list *old_todo, struct todo_list *new_todo);
int todo_list_check_against_backup(struct repository *r,
+ struct replay_opts *opts,
struct todo_list *todo_list);
#endif
diff --git a/ref-filter.c b/ref-filter.c
index 41f639bc2f..8c5e673fc0 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "environment.h"
#include "gettext.h"
@@ -895,7 +897,9 @@ static int head_atom_parser(struct ref_format *format UNUSED,
{
if (arg)
return err_no_arg(err, "HEAD");
- atom->u.head = resolve_refdup("HEAD", RESOLVE_REF_READING, NULL, NULL);
+ atom->u.head = refs_resolve_refdup(get_main_ref_store(the_repository),
+ "HEAD", RESOLVE_REF_READING, NULL,
+ NULL);
return 0;
}
@@ -2135,7 +2139,9 @@ static const char *rstrip_ref_components(const char *refname, int len)
static const char *show_ref(struct refname_atom *atom, const char *refname)
{
if (atom->option == R_SHORT)
- return shorten_unambiguous_ref(refname, warn_ambiguous_refs);
+ return refs_shorten_unambiguous_ref(get_main_ref_store(the_repository),
+ refname,
+ warn_ambiguous_refs);
else if (atom->option == R_LSTRIP)
return lstrip_ref_components(refname, atom->lstrip);
else if (atom->option == R_RSTRIP)
@@ -2338,8 +2344,10 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err)
CALLOC_ARRAY(ref->value, used_atom_cnt);
if (need_symref && (ref->flag & REF_ISSYMREF) && !ref->symref) {
- ref->symref = resolve_refdup(ref->refname, RESOLVE_REF_READING,
- NULL, NULL);
+ ref->symref = refs_resolve_refdup(get_main_ref_store(the_repository),
+ ref->refname,
+ RESOLVE_REF_READING,
+ NULL, NULL);
if (!ref->symref)
ref->symref = xstrdup("");
}
@@ -2514,7 +2522,7 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err)
* If it is a tag object, see if we use the peeled value. If we do,
* grab the peeled OID.
*/
- if (need_tagged && peel_iterated_oid(&obj->oid, &oi_deref.oid))
+ if (need_tagged && peel_iterated_oid(the_repository, &obj->oid, &oi_deref.oid))
die("bad tag");
return get_object(ref, 1, &obj, &oi_deref, err);
@@ -2640,7 +2648,8 @@ static int for_each_fullref_in_pattern(struct ref_filter *filter,
* prefixes like "refs/heads/" etc. are stripped off,
* so 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),
+ "", NULL, cb, cb_data);
}
if (filter->ignore_case) {
@@ -2649,7 +2658,8 @@ static int for_each_fullref_in_pattern(struct ref_filter *filter,
* so just return everything and let the caller
* sort it out.
*/
- return for_each_fullref_in("", cb, cb_data);
+ return refs_for_each_fullref_in(get_main_ref_store(the_repository),
+ "", NULL, cb, cb_data);
}
if (!filter->name_patterns[0]) {
@@ -3062,11 +3072,17 @@ static int do_filter_refs(struct ref_filter *filter, unsigned int type, each_ref
* of filter_ref_kind().
*/
if (filter->kind == FILTER_REFS_BRANCHES)
- ret = for_each_fullref_in("refs/heads/", fn, cb_data);
+ ret = refs_for_each_fullref_in(get_main_ref_store(the_repository),
+ "refs/heads/", NULL,
+ fn, cb_data);
else if (filter->kind == FILTER_REFS_REMOTES)
- ret = for_each_fullref_in("refs/remotes/", fn, cb_data);
+ ret = refs_for_each_fullref_in(get_main_ref_store(the_repository),
+ "refs/remotes/", NULL,
+ fn, cb_data);
else if (filter->kind == FILTER_REFS_TAGS)
- ret = for_each_fullref_in("refs/tags/", fn, cb_data);
+ ret = refs_for_each_fullref_in(get_main_ref_store(the_repository),
+ "refs/tags/", NULL, fn,
+ cb_data);
else if (filter->kind & FILTER_REFS_REGULAR)
ret = for_each_fullref_in_pattern(filter, fn, cb_data);
@@ -3076,7 +3092,8 @@ static int do_filter_refs(struct ref_filter *filter, unsigned int type, each_ref
*/
if (!ret && !(filter->kind & FILTER_REFS_ROOT_REFS) &&
(filter->kind & FILTER_REFS_DETACHED_HEAD))
- head_ref(fn, cb_data);
+ refs_head_ref(get_main_ref_store(the_repository), fn,
+ cb_data);
}
clear_contains_cache(&filter->internal.contains_cache);
diff --git a/reflog-walk.c b/reflog-walk.c
index 66484f4f32..c7070b13b0 100644
--- a/reflog-walk.c
+++ b/reflog-walk.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "commit.h"
#include "refs.h"
@@ -67,24 +69,32 @@ static struct complete_reflogs *read_complete_reflog(const char *ref)
struct complete_reflogs *reflogs =
xcalloc(1, sizeof(struct complete_reflogs));
reflogs->ref = xstrdup(ref);
- for_each_reflog_ent(ref, read_one_reflog, reflogs);
+ refs_for_each_reflog_ent(get_main_ref_store(the_repository), ref,
+ read_one_reflog, reflogs);
if (reflogs->nr == 0) {
const char *name;
void *name_to_free;
- name = name_to_free = resolve_refdup(ref, RESOLVE_REF_READING,
- NULL, NULL);
+ name = name_to_free = refs_resolve_refdup(get_main_ref_store(the_repository),
+ ref,
+ RESOLVE_REF_READING,
+ NULL, NULL);
if (name) {
- for_each_reflog_ent(name, read_one_reflog, reflogs);
+ refs_for_each_reflog_ent(get_main_ref_store(the_repository),
+ name, read_one_reflog,
+ reflogs);
free(name_to_free);
}
}
if (reflogs->nr == 0) {
char *refname = xstrfmt("refs/%s", ref);
- for_each_reflog_ent(refname, read_one_reflog, reflogs);
+ refs_for_each_reflog_ent(get_main_ref_store(the_repository),
+ refname, read_one_reflog, reflogs);
if (reflogs->nr == 0) {
free(refname);
refname = xstrfmt("refs/heads/%s", ref);
- for_each_reflog_ent(refname, read_one_reflog, reflogs);
+ refs_for_each_reflog_ent(get_main_ref_store(the_repository),
+ refname, read_one_reflog,
+ reflogs);
}
free(refname);
}
@@ -174,7 +184,8 @@ int add_reflog_for_walk(struct reflog_walk_info *info,
else {
if (*branch == '\0') {
free(branch);
- branch = resolve_refdup("HEAD", 0, NULL, NULL);
+ branch = refs_resolve_refdup(get_main_ref_store(the_repository),
+ "HEAD", 0, NULL, NULL);
if (!branch)
die("no current branch");
@@ -182,8 +193,8 @@ int add_reflog_for_walk(struct reflog_walk_info *info,
reflogs = read_complete_reflog(branch);
if (!reflogs || reflogs->nr == 0) {
char *b;
- int ret = dwim_log(branch, strlen(branch),
- NULL, &b);
+ int ret = repo_dwim_log(the_repository, branch, strlen(branch),
+ NULL, &b);
if (ret > 1)
free(b);
else if (ret == 1) {
@@ -236,7 +247,9 @@ void get_reflog_selector(struct strbuf *sb,
if (shorten) {
if (!commit_reflog->reflogs->short_ref)
commit_reflog->reflogs->short_ref
- = shorten_unambiguous_ref(commit_reflog->reflogs->ref, 0);
+ = refs_shorten_unambiguous_ref(get_main_ref_store(the_repository),
+ commit_reflog->reflogs->ref,
+ 0);
printed_ref = commit_reflog->reflogs->short_ref;
} else {
printed_ref = commit_reflog->reflogs->ref;
diff --git a/reflog.c b/reflog.c
index 647f3ca398..5ca944529b 100644
--- a/reflog.c
+++ b/reflog.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "gettext.h"
#include "object-store-ll.h"
@@ -343,7 +345,8 @@ void reflog_expiry_prepare(const char *refname,
case UE_ALWAYS:
return;
case UE_HEAD:
- for_each_ref(push_tip_to_list, &cb->tips);
+ refs_for_each_ref(get_main_ref_store(the_repository),
+ push_tip_to_list, &cb->tips);
for (elem = cb->tips; elem; elem = elem->next)
commit_list_insert(elem->item, &cb->mark_list);
break;
@@ -408,7 +411,7 @@ int reflog_delete(const char *rev, enum expire_reflog_flags flags, int verbose)
if (!spec)
return error(_("not a reflog: %s"), rev);
- if (!dwim_log(rev, spec - rev, NULL, &ref)) {
+ if (!repo_dwim_log(the_repository, rev, spec - rev, NULL, &ref)) {
status |= error(_("no reflog for '%s'"), rev);
goto cleanup;
}
@@ -416,19 +419,22 @@ int reflog_delete(const char *rev, enum expire_reflog_flags flags, int verbose)
recno = strtoul(spec + 2, &ep, 10);
if (*ep == '}') {
cmd.recno = -recno;
- for_each_reflog_ent(ref, count_reflog_ent, &cmd);
+ refs_for_each_reflog_ent(get_main_ref_store(the_repository),
+ ref, count_reflog_ent, &cmd);
} else {
cmd.expire_total = approxidate(spec + 2);
- for_each_reflog_ent(ref, count_reflog_ent, &cmd);
+ refs_for_each_reflog_ent(get_main_ref_store(the_repository),
+ ref, count_reflog_ent, &cmd);
cmd.expire_total = 0;
}
cb.cmd = cmd;
- status |= reflog_expire(ref, flags,
- reflog_expiry_prepare,
- should_prune_fn,
- reflog_expiry_cleanup,
- &cb);
+ status |= refs_reflog_expire(get_main_ref_store(the_repository), ref,
+ flags,
+ reflog_expiry_prepare,
+ should_prune_fn,
+ reflog_expiry_cleanup,
+ &cb);
cleanup:
free(ref);
diff --git a/refs.c b/refs.c
index 9abd9e5b86..bb90a18875 100644
--- a/refs.c
+++ b/refs.c
@@ -2,11 +2,13 @@
* The backend-independent part of the reference module.
*/
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "advice.h"
#include "config.h"
#include "environment.h"
-#include "hashmap.h"
+#include "strmap.h"
#include "gettext.h"
#include "hex.h"
#include "lockfile.h"
@@ -19,7 +21,6 @@
#include "object-store-ll.h"
#include "object.h"
#include "path.h"
-#include "tag.h"
#include "submodule.h"
#include "worktree.h"
#include "strvec.h"
@@ -38,14 +39,15 @@ static const struct ref_storage_be *refs_backends[] = {
[REF_STORAGE_FORMAT_REFTABLE] = &refs_be_reftable,
};
-static const struct ref_storage_be *find_ref_storage_backend(unsigned int ref_storage_format)
+static const struct ref_storage_be *find_ref_storage_backend(
+ enum ref_storage_format ref_storage_format)
{
if (ref_storage_format < ARRAY_SIZE(refs_backends))
return refs_backends[ref_storage_format];
return NULL;
}
-unsigned int ref_storage_format_by_name(const char *name)
+enum ref_storage_format ref_storage_format_by_name(const char *name)
{
for (unsigned int i = 0; i < ARRAY_SIZE(refs_backends); i++)
if (refs_backends[i] && !strcmp(refs_backends[i]->name, name))
@@ -53,7 +55,7 @@ unsigned int ref_storage_format_by_name(const char *name)
return REF_STORAGE_FORMAT_UNKNOWN;
}
-const char *ref_storage_format_to_name(unsigned int ref_storage_format)
+const char *ref_storage_format_to_name(enum ref_storage_format ref_storage_format)
{
const struct ref_storage_be *be = find_ref_storage_backend(ref_storage_format);
if (!be)
@@ -159,7 +161,7 @@ void update_ref_namespace(enum ref_namespace namespace, char *ref)
{
struct ref_namespace_info *info = &ref_namespace[namespace];
if (info->ref_updated)
- free(info->ref);
+ free((char *)info->ref);
info->ref = ref;
info->ref_updated = 1;
}
@@ -384,14 +386,6 @@ char *refs_resolve_refdup(struct ref_store *refs,
return xstrdup_or_null(result);
}
-char *resolve_refdup(const char *refname, int resolve_flags,
- struct object_id *oid, int *flags)
-{
- return refs_resolve_refdup(get_main_ref_store(the_repository),
- refname, resolve_flags,
- oid, flags);
-}
-
/* The argument to for_each_filter_refs */
struct for_each_ref_filter {
const char *pattern;
@@ -400,19 +394,18 @@ struct for_each_ref_filter {
void *cb_data;
};
-int read_ref_full(const char *refname, int resolve_flags, struct object_id *oid, int *flags)
+int refs_read_ref_full(struct ref_store *refs, const char *refname,
+ int resolve_flags, struct object_id *oid, int *flags)
{
- struct ref_store *refs = get_main_ref_store(the_repository);
-
if (refs_resolve_ref_unsafe(refs, refname, resolve_flags,
oid, flags))
return 0;
return -1;
}
-int read_ref(const char *refname, struct object_id *oid)
+int refs_read_ref(struct ref_store *refs, const char *refname, struct object_id *oid)
{
- return read_ref_full(refname, RESOLVE_REF_READING, oid, NULL);
+ return refs_read_ref_full(refs, refname, RESOLVE_REF_READING, oid, NULL);
}
int refs_ref_exists(struct ref_store *refs, const char *refname)
@@ -421,11 +414,6 @@ int refs_ref_exists(struct ref_store *refs, const char *refname)
NULL, NULL);
}
-int ref_exists(const char *refname)
-{
- return refs_ref_exists(get_main_ref_store(the_repository), refname);
-}
-
static int for_each_filter_refs(const char *refname,
const struct object_id *oid,
int flags, void *data)
@@ -439,28 +427,8 @@ static int for_each_filter_refs(const char *refname,
return filter->fn(refname, oid, flags, filter->cb_data);
}
-enum peel_status peel_object(const struct object_id *name, struct object_id *oid)
-{
- struct object *o = lookup_unknown_object(the_repository, name);
-
- if (o->type == OBJ_NONE) {
- int type = oid_object_info(the_repository, name, NULL);
- if (type < 0 || !object_as_type(o, type, 0))
- return PEEL_INVALID;
- }
-
- if (o->type != OBJ_TAG)
- return PEEL_NON_TAG;
-
- o = deref_tag_noverify(o);
- if (!o)
- return PEEL_INVALID;
-
- oidcpy(oid, &o->oid);
- return PEEL_PEELED;
-}
-
struct warn_if_dangling_data {
+ struct ref_store *refs;
FILE *fp;
const char *refname;
const struct string_list *refnames;
@@ -477,7 +445,7 @@ static int warn_if_dangling_symref(const char *refname,
if (!(flags & REF_ISSYMREF))
return 0;
- resolves_to = resolve_ref_unsafe(refname, 0, NULL, NULL);
+ resolves_to = refs_resolve_ref_unsafe(d->refs, refname, 0, NULL, NULL);
if (!resolves_to
|| (d->refname
? strcmp(resolves_to, d->refname)
@@ -490,26 +458,28 @@ static int warn_if_dangling_symref(const char *refname,
return 0;
}
-void warn_dangling_symref(FILE *fp, const char *msg_fmt, const char *refname)
+void refs_warn_dangling_symref(struct ref_store *refs, FILE *fp,
+ const char *msg_fmt, const char *refname)
{
- struct warn_if_dangling_data data;
-
- data.fp = fp;
- data.refname = refname;
- data.refnames = NULL;
- data.msg_fmt = msg_fmt;
- for_each_rawref(warn_if_dangling_symref, &data);
+ struct warn_if_dangling_data data = {
+ .refs = refs,
+ .fp = fp,
+ .refname = refname,
+ .msg_fmt = msg_fmt,
+ };
+ refs_for_each_rawref(refs, warn_if_dangling_symref, &data);
}
-void warn_dangling_symrefs(FILE *fp, const char *msg_fmt, const struct string_list *refnames)
+void refs_warn_dangling_symrefs(struct ref_store *refs, FILE *fp,
+ const char *msg_fmt, const struct string_list *refnames)
{
- struct warn_if_dangling_data data;
-
- data.fp = fp;
- data.refname = NULL;
- data.refnames = refnames;
- data.msg_fmt = msg_fmt;
- for_each_rawref(warn_if_dangling_symref, &data);
+ struct warn_if_dangling_data data = {
+ .refs = refs,
+ .fp = fp,
+ .refnames = refnames,
+ .msg_fmt = msg_fmt,
+ };
+ refs_for_each_rawref(refs, warn_if_dangling_symref, &data);
}
int refs_for_each_tag_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data)
@@ -517,32 +487,17 @@ int refs_for_each_tag_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data)
return refs_for_each_ref_in(refs, "refs/tags/", fn, cb_data);
}
-int for_each_tag_ref(each_ref_fn fn, void *cb_data)
-{
- return refs_for_each_tag_ref(get_main_ref_store(the_repository), fn, cb_data);
-}
-
int refs_for_each_branch_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data)
{
return refs_for_each_ref_in(refs, "refs/heads/", fn, cb_data);
}
-int for_each_branch_ref(each_ref_fn fn, void *cb_data)
-{
- return refs_for_each_branch_ref(get_main_ref_store(the_repository), fn, cb_data);
-}
-
int refs_for_each_remote_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data)
{
return refs_for_each_ref_in(refs, "refs/remotes/", fn, cb_data);
}
-int for_each_remote_ref(each_ref_fn fn, void *cb_data)
-{
- return refs_for_each_remote_ref(get_main_ref_store(the_repository), fn, cb_data);
-}
-
-int head_ref_namespaced(each_ref_fn fn, void *cb_data)
+int refs_head_ref_namespaced(struct ref_store *refs, each_ref_fn fn, void *cb_data)
{
struct strbuf buf = STRBUF_INIT;
int ret = 0;
@@ -550,7 +505,7 @@ int head_ref_namespaced(each_ref_fn fn, void *cb_data)
int flag;
strbuf_addf(&buf, "%sHEAD", get_git_namespace());
- if (!read_ref_full(buf.buf, RESOLVE_REF_READING, &oid, &flag))
+ if (!refs_read_ref_full(refs, buf.buf, RESOLVE_REF_READING, &oid, &flag))
ret = fn(buf.buf, &oid, flag, cb_data);
strbuf_release(&buf);
@@ -583,8 +538,8 @@ void normalize_glob_ref(struct string_list_item *item, const char *prefix,
strbuf_release(&normalized_pattern);
}
-int for_each_glob_ref_in(each_ref_fn fn, const char *pattern,
- const char *prefix, void *cb_data)
+int refs_for_each_glob_ref_in(struct ref_store *refs, each_ref_fn fn,
+ const char *pattern, const char *prefix, void *cb_data)
{
struct strbuf real_pattern = STRBUF_INIT;
struct for_each_ref_filter filter;
@@ -607,15 +562,16 @@ 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(for_each_filter_refs, &filter);
+ ret = refs_for_each_ref(refs, for_each_filter_refs, &filter);
strbuf_release(&real_pattern);
return ret;
}
-int for_each_glob_ref(each_ref_fn fn, const char *pattern, void *cb_data)
+int refs_for_each_glob_ref(struct ref_store *refs, each_ref_fn fn,
+ const char *pattern, void *cb_data)
{
- return for_each_glob_ref_in(fn, pattern, NULL, cb_data);
+ return refs_for_each_glob_ref_in(refs, fn, pattern, NULL, cb_data);
}
const char *prettify_refname(const char *name)
@@ -711,16 +667,6 @@ char *repo_default_branch_name(struct repository *r, int quiet)
return ret;
}
-const char *git_default_branch_name(int quiet)
-{
- static char *ret;
-
- if (!ret)
- ret = repo_default_branch_name(the_repository, quiet);
-
- return ret;
-}
-
/*
* *string and *len will only be substituted, and *string returned (for
* later free()ing) if the string passed in is a magic short-hand form
@@ -832,11 +778,6 @@ int repo_dwim_log(struct repository *r, const char *str, int len,
return logs_found;
}
-int dwim_log(const char *str, int len, struct object_id *oid, char **log)
-{
- return repo_dwim_log(the_repository, str, len, oid, log);
-}
-
int is_per_worktree_ref(const char *refname)
{
return starts_with(refname, "refs/worktree/") ||
@@ -975,7 +916,7 @@ int refs_delete_ref(struct ref_store *refs, const char *msg,
transaction = ref_store_transaction_begin(refs, &err);
if (!transaction ||
ref_transaction_delete(transaction, refname, old_oid,
- flags, msg, &err) ||
+ NULL, flags, msg, &err) ||
ref_transaction_commit(transaction, &err)) {
error("%s", err.buf);
ref_transaction_free(transaction);
@@ -987,13 +928,6 @@ int refs_delete_ref(struct ref_store *refs, const char *msg,
return 0;
}
-int delete_ref(const char *msg, const char *refname,
- const struct object_id *old_oid, unsigned int flags)
-{
- return refs_delete_ref(get_main_ref_store(the_repository), msg, refname,
- old_oid, flags);
-}
-
static void copy_reflog_msg(struct strbuf *sb, const char *msg)
{
char c;
@@ -1186,11 +1120,6 @@ struct ref_transaction *ref_store_transaction_begin(struct ref_store *refs,
return tr;
}
-struct ref_transaction *ref_transaction_begin(struct strbuf *err)
-{
- return ref_store_transaction_begin(get_main_ref_store(the_repository), err);
-}
-
void ref_transaction_free(struct ref_transaction *transaction)
{
size_t i;
@@ -1213,6 +1142,8 @@ void ref_transaction_free(struct ref_transaction *transaction)
for (i = 0; i < transaction->nr; i++) {
free(transaction->updates[i]->msg);
+ free((char *)transaction->updates[i]->new_target);
+ free((char *)transaction->updates[i]->old_target);
free(transaction->updates[i]);
}
free(transaction->updates);
@@ -1224,6 +1155,7 @@ struct ref_update *ref_transaction_add_update(
const char *refname, unsigned int flags,
const struct object_id *new_oid,
const struct object_id *old_oid,
+ const char *new_target, const char *old_target,
const char *msg)
{
struct ref_update *update;
@@ -1231,16 +1163,24 @@ struct ref_update *ref_transaction_add_update(
if (transaction->state != REF_TRANSACTION_OPEN)
BUG("update called for transaction that is not open");
+ if (old_oid && old_target)
+ BUG("only one of old_oid and old_target should be non NULL");
+ if (new_oid && new_target)
+ BUG("only one of new_oid and new_target should be non NULL");
+
FLEX_ALLOC_STR(update, refname, refname);
ALLOC_GROW(transaction->updates, transaction->nr + 1, transaction->alloc);
transaction->updates[transaction->nr++] = update;
update->flags = flags;
- if (flags & REF_HAVE_NEW)
+ update->new_target = xstrdup_or_null(new_target);
+ update->old_target = xstrdup_or_null(old_target);
+ if ((flags & REF_HAVE_NEW) && new_oid)
oidcpy(&update->new_oid, new_oid);
- if (flags & REF_HAVE_OLD)
+ if ((flags & REF_HAVE_OLD) && old_oid)
oidcpy(&update->old_oid, old_oid);
+
update->msg = normalize_reflog_message(msg);
return update;
}
@@ -1249,11 +1189,19 @@ int ref_transaction_update(struct ref_transaction *transaction,
const char *refname,
const struct object_id *new_oid,
const struct object_id *old_oid,
+ const char *new_target,
+ const char *old_target,
unsigned int flags, const char *msg,
struct strbuf *err)
{
assert(err);
+ if ((flags & REF_FORCE_CREATE_REFLOG) &&
+ (flags & REF_SKIP_CREATE_REFLOG)) {
+ strbuf_addstr(err, _("refusing to force and skip creation of reflog"));
+ return -1;
+ }
+
if (!(flags & REF_SKIP_REFNAME_VERIFICATION) &&
((new_oid && !is_null_oid(new_oid)) ?
check_refname_format(refname, REFNAME_ALLOW_ONELEVEL) :
@@ -1281,49 +1229,68 @@ int ref_transaction_update(struct ref_transaction *transaction,
flags &= REF_TRANSACTION_UPDATE_ALLOWED_FLAGS;
flags |= (new_oid ? REF_HAVE_NEW : 0) | (old_oid ? REF_HAVE_OLD : 0);
+ flags |= (new_target ? REF_HAVE_NEW : 0) | (old_target ? REF_HAVE_OLD : 0);
ref_transaction_add_update(transaction, refname, flags,
- new_oid, old_oid, msg);
+ new_oid, old_oid, new_target,
+ old_target, msg);
return 0;
}
int ref_transaction_create(struct ref_transaction *transaction,
const char *refname,
const struct object_id *new_oid,
+ const char *new_target,
unsigned int flags, const char *msg,
struct strbuf *err)
{
- if (!new_oid || is_null_oid(new_oid)) {
- strbuf_addf(err, "'%s' has a null OID", refname);
+ if (new_oid && new_target)
+ BUG("create called with both new_oid and new_target set");
+ if ((!new_oid || is_null_oid(new_oid)) && !new_target) {
+ strbuf_addf(err, "'%s' has neither a valid OID nor a target", refname);
return 1;
}
return ref_transaction_update(transaction, refname, new_oid,
- null_oid(), flags, msg, err);
+ null_oid(), new_target, NULL, flags,
+ msg, err);
}
int ref_transaction_delete(struct ref_transaction *transaction,
const char *refname,
const struct object_id *old_oid,
- unsigned int flags, const char *msg,
+ const char *old_target,
+ unsigned int flags,
+ const char *msg,
struct strbuf *err)
{
if (old_oid && is_null_oid(old_oid))
BUG("delete called with old_oid set to zeros");
+ if (old_oid && old_target)
+ BUG("delete called with both old_oid and old_target set");
+ if (old_target && !(flags & REF_NO_DEREF))
+ BUG("delete cannot operate on symrefs with deref mode");
return ref_transaction_update(transaction, refname,
null_oid(), old_oid,
- flags, msg, err);
+ NULL, old_target, flags,
+ msg, err);
}
int ref_transaction_verify(struct ref_transaction *transaction,
const char *refname,
const struct object_id *old_oid,
+ const char *old_target,
unsigned int flags,
struct strbuf *err)
{
- if (!old_oid)
- BUG("verify called with old_oid set to NULL");
+ if (!old_target && !old_oid)
+ BUG("verify called with old_oid and old_target set to NULL");
+ if (old_oid && old_target)
+ BUG("verify called with both old_oid and old_target set");
+ if (old_target && !(flags & REF_NO_DEREF))
+ BUG("verify cannot operate on symrefs with deref mode");
return ref_transaction_update(transaction, refname,
NULL, old_oid,
+ NULL, old_target,
flags, NULL, err);
}
@@ -1338,8 +1305,8 @@ int refs_update_ref(struct ref_store *refs, const char *msg,
t = ref_store_transaction_begin(refs, &err);
if (!t ||
- ref_transaction_update(t, refname, new_oid, old_oid, flags, msg,
- &err) ||
+ ref_transaction_update(t, refname, new_oid, old_oid, NULL, NULL,
+ flags, msg, &err) ||
ref_transaction_commit(t, &err)) {
ret = 1;
ref_transaction_free(t);
@@ -1366,15 +1333,6 @@ int refs_update_ref(struct ref_store *refs, const char *msg,
return 0;
}
-int update_ref(const char *msg, const char *refname,
- const struct object_id *new_oid,
- const struct object_id *old_oid,
- unsigned int flags, enum action_on_err onerr)
-{
- return refs_update_ref(get_main_ref_store(the_repository), msg, refname, new_oid,
- old_oid, flags, onerr);
-}
-
/*
* Check that the string refname matches a rule of the form
* "{prefix}%.*s{suffix}". So "foo/bar/baz" would match the rule
@@ -1476,12 +1434,6 @@ char *refs_shorten_unambiguous_ref(struct ref_store *refs,
return xstrdup(refname);
}
-char *shorten_unambiguous_ref(const char *refname, int strict)
-{
- return refs_shorten_unambiguous_ref(get_main_ref_store(the_repository),
- refname, strict);
-}
-
int parse_hide_refs_config(const char *var, const char *value, const char *section,
struct strvec *hide_refs)
{
@@ -1600,11 +1552,6 @@ int refs_head_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data)
return 0;
}
-int head_ref(each_ref_fn fn, void *cb_data)
-{
- return refs_head_ref(get_main_ref_store(the_repository), fn, cb_data);
-}
-
struct ref_iterator *refs_ref_iterator_begin(
struct ref_store *refs,
const char *prefix,
@@ -1636,53 +1583,12 @@ struct ref_iterator *refs_ref_iterator_begin(
return iter;
}
-/*
- * Call fn for each reference in the specified submodule for which the
- * refname begins with prefix. If trim is non-zero, then trim that
- * many characters off the beginning of each refname before passing
- * the refname to fn. flags can be DO_FOR_EACH_INCLUDE_BROKEN to
- * include broken references in the iteration. If fn ever returns a
- * non-zero value, stop the iteration and return that value;
- * otherwise, return 0.
- */
-static int do_for_each_repo_ref(struct repository *r, const char *prefix,
- each_repo_ref_fn fn, int trim, int flags,
- void *cb_data)
-{
- struct ref_iterator *iter;
- struct ref_store *refs = get_main_ref_store(r);
-
- if (!refs)
- return 0;
-
- iter = refs_ref_iterator_begin(refs, prefix, NULL, trim, flags);
-
- return do_for_each_repo_ref_iterator(r, iter, fn, cb_data);
-}
-
-struct do_for_each_ref_help {
- each_ref_fn *fn;
- void *cb_data;
-};
-
-static int do_for_each_ref_helper(struct repository *r UNUSED,
- const char *refname,
- const struct object_id *oid,
- int flags,
- void *cb_data)
-{
- struct do_for_each_ref_help *hp = cb_data;
-
- return hp->fn(refname, oid, flags, hp->cb_data);
-}
-
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)
{
struct ref_iterator *iter;
- struct do_for_each_ref_help hp = { fn, cb_data };
if (!refs)
return 0;
@@ -1690,8 +1596,7 @@ static int do_for_each_ref(struct ref_store *refs, const char *prefix,
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);
+ return do_for_each_ref_iterator(iter, fn, cb_data);
}
int refs_for_each_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data)
@@ -1699,28 +1604,12 @@ int refs_for_each_ref(struct ref_store *refs, each_ref_fn fn, void *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)
-{
- return refs_for_each_ref(get_main_ref_store(the_repository), fn, 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, NULL, fn, strlen(prefix), 0, cb_data);
}
-int for_each_ref_in(const char *prefix, each_ref_fn fn, void *cb_data)
-{
- return refs_for_each_ref_in(get_main_ref_store(the_repository), prefix, fn, 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, 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)
@@ -1728,22 +1617,22 @@ int refs_for_each_fullref_in(struct ref_store *refs, const char *prefix,
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)
+int refs_for_each_replace_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data)
{
const char *git_replace_ref_base = ref_namespace[NAMESPACE_REPLACE].ref;
- return do_for_each_repo_ref(r, git_replace_ref_base, fn,
- strlen(git_replace_ref_base),
- DO_FOR_EACH_INCLUDE_BROKEN, cb_data);
+ return do_for_each_ref(refs, git_replace_ref_base, NULL, fn,
+ strlen(git_replace_ref_base),
+ DO_FOR_EACH_INCLUDE_BROKEN, cb_data);
}
-int for_each_namespaced_ref(const char **exclude_patterns,
- each_ref_fn fn, void *cb_data)
+int refs_for_each_namespaced_ref(struct ref_store *refs,
+ 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, exclude_patterns, fn, 0, 0, cb_data);
+ ret = do_for_each_ref(refs, buf.buf, exclude_patterns, fn, 0, 0, cb_data);
strbuf_release(&buf);
return ret;
}
@@ -1754,11 +1643,6 @@ int refs_for_each_rawref(struct ref_store *refs, each_ref_fn fn, void *cb_data)
DO_FOR_EACH_INCLUDE_BROKEN, cb_data);
}
-int for_each_rawref(each_ref_fn fn, void *cb_data)
-{
- return refs_for_each_rawref(get_main_ref_store(the_repository), fn, cb_data);
-}
-
int refs_for_each_include_root_refs(struct ref_store *refs, each_ref_fn fn,
void *cb_data)
{
@@ -1954,7 +1838,7 @@ const char *refs_resolve_ref_unsafe(struct ref_store *refs,
failure_errno != ENOTDIR)
return NULL;
- oidclr(oid);
+ oidclr(oid, the_repository->hash_algo);
if (*flags & REF_BAD_NAME)
*flags |= REF_ISBROKEN;
return refname;
@@ -1964,7 +1848,7 @@ const char *refs_resolve_ref_unsafe(struct ref_store *refs,
if (!(read_flags & REF_ISSYMREF)) {
if (*flags & REF_BAD_NAME) {
- oidclr(oid);
+ oidclr(oid, the_repository->hash_algo);
*flags |= REF_ISBROKEN;
}
return refname;
@@ -1972,7 +1856,7 @@ const char *refs_resolve_ref_unsafe(struct ref_store *refs,
refname = sb_refname.buf;
if (resolve_flags & RESOLVE_REF_NO_RECURSE) {
- oidclr(oid);
+ oidclr(oid, the_repository->hash_algo);
return refname;
}
if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) {
@@ -1988,26 +1872,24 @@ const char *refs_resolve_ref_unsafe(struct ref_store *refs,
}
/* backend functions */
-int refs_init_db(struct ref_store *refs, int flags, struct strbuf *err)
+int ref_store_create_on_disk(struct ref_store *refs, int flags, struct strbuf *err)
{
- return refs->be->init_db(refs, flags, err);
+ return refs->be->create_on_disk(refs, flags, err);
}
-const char *resolve_ref_unsafe(const char *refname, int resolve_flags,
- struct object_id *oid, int *flags)
+int ref_store_remove_on_disk(struct ref_store *refs, struct strbuf *err)
{
- return refs_resolve_ref_unsafe(get_main_ref_store(the_repository), refname,
- resolve_flags, oid, flags);
+ return refs->be->remove_on_disk(refs, err);
}
-int resolve_gitlink_ref(const char *submodule, const char *refname,
- struct object_id *oid)
+int repo_resolve_gitlink_ref(struct repository *r,
+ const char *submodule, const char *refname,
+ struct object_id *oid)
{
struct ref_store *refs;
int flags;
- refs = get_submodule_ref_store(submodule);
-
+ refs = repo_get_submodule_ref_store(r, submodule);
if (!refs)
return -1;
@@ -2017,80 +1899,36 @@ int resolve_gitlink_ref(const char *submodule, const char *refname,
return 0;
}
-struct ref_store_hash_entry
-{
- struct hashmap_entry ent;
-
- struct ref_store *refs;
-
- /* NUL-terminated identifier of the ref store: */
- char name[FLEX_ARRAY];
-};
-
-static int ref_store_hash_cmp(const void *cmp_data UNUSED,
- const struct hashmap_entry *eptr,
- const struct hashmap_entry *entry_or_key,
- const void *keydata)
-{
- const struct ref_store_hash_entry *e1, *e2;
- const char *name;
-
- e1 = container_of(eptr, const struct ref_store_hash_entry, ent);
- e2 = container_of(entry_or_key, const struct ref_store_hash_entry, ent);
- name = keydata ? keydata : e2->name;
-
- return strcmp(e1->name, name);
-}
-
-static struct ref_store_hash_entry *alloc_ref_store_hash_entry(
- const char *name, struct ref_store *refs)
-{
- struct ref_store_hash_entry *entry;
-
- FLEX_ALLOC_STR(entry, name, name);
- hashmap_entry_init(&entry->ent, strhash(name));
- entry->refs = refs;
- return entry;
-}
-
-/* A hashmap of ref_stores, stored by submodule name: */
-static struct hashmap submodule_ref_stores;
-
-/* A hashmap of ref_stores, stored by worktree id: */
-static struct hashmap worktree_ref_stores;
-
/*
* Look up a ref store by name. If that ref_store hasn't been
* registered yet, return NULL.
*/
-static struct ref_store *lookup_ref_store_map(struct hashmap *map,
+static struct ref_store *lookup_ref_store_map(struct strmap *map,
const char *name)
{
- struct ref_store_hash_entry *entry;
- unsigned int hash;
+ struct strmap_entry *entry;
- if (!map->tablesize)
+ if (!map->map.tablesize)
/* It's initialized on demand in register_ref_store(). */
return NULL;
- hash = strhash(name);
- entry = hashmap_get_entry_from_hash(map, hash, name,
- struct ref_store_hash_entry, ent);
- return entry ? entry->refs : NULL;
+ entry = strmap_get_entry(map, name);
+ return entry ? entry->value : NULL;
}
/*
* Create, record, and return a ref_store instance for the specified
- * gitdir.
+ * gitdir using the given ref storage format.
*/
static struct ref_store *ref_store_init(struct repository *repo,
+ enum ref_storage_format format,
const char *gitdir,
unsigned int flags)
{
const struct ref_storage_be *be;
struct ref_store *refs;
- be = find_ref_storage_backend(repo->ref_storage_format);
+ be = find_ref_storage_backend(format);
if (!be)
BUG("reference backend is unknown");
@@ -2098,6 +1936,12 @@ static struct ref_store *ref_store_init(struct repository *repo,
return refs;
}
+void ref_store_release(struct ref_store *ref_store)
+{
+ ref_store->be->release(ref_store);
+ free(ref_store->gitdir);
+}
+
struct ref_store *get_main_ref_store(struct repository *r)
{
if (r->refs_private)
@@ -2106,7 +1950,8 @@ struct ref_store *get_main_ref_store(struct repository *r)
if (!r->gitdir)
BUG("attempting to get main_ref_store outside of repository");
- r->refs_private = ref_store_init(r, r->gitdir, REF_STORE_ALL_CAPS);
+ r->refs_private = ref_store_init(r, r->ref_storage_format,
+ r->gitdir, REF_STORE_ALL_CAPS);
r->refs_private = maybe_debug_wrap_ref_store(r->gitdir, r->refs_private);
return r->refs_private;
}
@@ -2115,22 +1960,19 @@ struct ref_store *get_main_ref_store(struct repository *r)
* Associate a ref store with a name. It is a fatal error to call this
* function twice for the same name.
*/
-static void register_ref_store_map(struct hashmap *map,
+static void register_ref_store_map(struct strmap *map,
const char *type,
struct ref_store *refs,
const char *name)
{
- struct ref_store_hash_entry *entry;
-
- if (!map->tablesize)
- hashmap_init(map, ref_store_hash_cmp, NULL, 0);
-
- entry = alloc_ref_store_hash_entry(name, refs);
- if (hashmap_put(map, &entry->ent))
+ if (!map->map.tablesize)
+ strmap_init(map);
+ if (strmap_put(map, name, refs))
BUG("%s ref_store '%s' initialized twice", type, name);
}
-struct ref_store *get_submodule_ref_store(const char *submodule)
+struct ref_store *repo_get_submodule_ref_store(struct repository *repo,
+ const char *submodule)
{
struct strbuf submodule_sb = STRBUF_INIT;
struct ref_store *refs;
@@ -2151,7 +1993,7 @@ struct ref_store *get_submodule_ref_store(const char *submodule)
/* We need to strip off one or more trailing slashes */
submodule = to_free = xmemdupz(submodule, len);
- refs = lookup_ref_store_map(&submodule_ref_stores, submodule);
+ refs = lookup_ref_store_map(&repo->submodule_ref_stores, submodule);
if (refs)
goto done;
@@ -2163,20 +2005,16 @@ struct ref_store *get_submodule_ref_store(const char *submodule)
goto done;
subrepo = xmalloc(sizeof(*subrepo));
- /*
- * NEEDSWORK: Make get_submodule_ref_store() work with arbitrary
- * superprojects other than the_repository. This probably should be
- * done by making it take a struct repository * parameter instead of a
- * submodule path.
- */
- if (repo_submodule_init(subrepo, the_repository, submodule,
+
+ if (repo_submodule_init(subrepo, repo, submodule,
null_oid())) {
free(subrepo);
goto done;
}
- refs = ref_store_init(subrepo, submodule_sb.buf,
+ refs = ref_store_init(subrepo, the_repository->ref_storage_format,
+ submodule_sb.buf,
REF_STORE_READ | REF_STORE_ODB);
- register_ref_store_map(&submodule_ref_stores, "submodule",
+ register_ref_store_map(&repo->submodule_ref_stores, "submodule",
refs, submodule);
done:
@@ -2192,25 +2030,29 @@ struct ref_store *get_worktree_ref_store(const struct worktree *wt)
const char *id;
if (wt->is_current)
- return get_main_ref_store(the_repository);
+ return get_main_ref_store(wt->repo);
id = wt->id ? wt->id : "/";
- refs = lookup_ref_store_map(&worktree_ref_stores, id);
+ refs = lookup_ref_store_map(&wt->repo->worktree_ref_stores, id);
if (refs)
return refs;
- if (wt->id)
- refs = ref_store_init(the_repository,
- git_common_path("worktrees/%s", wt->id),
- REF_STORE_ALL_CAPS);
- else
- refs = ref_store_init(the_repository,
- get_git_common_dir(),
- REF_STORE_ALL_CAPS);
+ if (wt->id) {
+ struct strbuf common_path = STRBUF_INIT;
+ strbuf_git_common_path(&common_path, wt->repo,
+ "worktrees/%s", wt->id);
+ refs = ref_store_init(wt->repo, wt->repo->ref_storage_format,
+ common_path.buf, REF_STORE_ALL_CAPS);
+ strbuf_release(&common_path);
+ } else {
+ refs = ref_store_init(wt->repo, the_repository->ref_storage_format,
+ wt->repo->commondir, REF_STORE_ALL_CAPS);
+ }
if (refs)
- register_ref_store_map(&worktree_ref_stores, "worktree",
- refs, id);
+ register_ref_store_map(&wt->repo->worktree_ref_stores,
+ "worktree", refs, id);
+
return refs;
}
@@ -2228,36 +2070,37 @@ int refs_pack_refs(struct ref_store *refs, struct pack_refs_opts *opts)
return refs->be->pack_refs(refs, opts);
}
-int peel_iterated_oid(const struct object_id *base, struct object_id *peeled)
+int peel_iterated_oid(struct repository *r, const struct object_id *base, struct object_id *peeled)
{
if (current_ref_iter &&
(current_ref_iter->oid == base ||
oideq(current_ref_iter->oid, base)))
return ref_iterator_peel(current_ref_iter, peeled);
- return peel_object(base, peeled) ? -1 : 0;
+ return peel_object(r, base, peeled) ? -1 : 0;
}
-int refs_create_symref(struct ref_store *refs,
- const char *ref_target,
- const char *refs_heads_master,
- const char *logmsg)
+int refs_update_symref(struct ref_store *refs, const char *ref,
+ const char *target, const char *logmsg)
{
- char *msg;
- int retval;
+ struct ref_transaction *transaction;
+ struct strbuf err = STRBUF_INIT;
+ int ret = 0;
- msg = normalize_reflog_message(logmsg);
- retval = refs->be->create_symref(refs, ref_target, refs_heads_master,
- msg);
- free(msg);
- return retval;
-}
+ transaction = ref_store_transaction_begin(refs, &err);
+ if (!transaction ||
+ ref_transaction_update(transaction, ref, NULL, NULL,
+ target, NULL, REF_NO_DEREF,
+ logmsg, &err) ||
+ ref_transaction_commit(transaction, &err)) {
+ ret = error("%s", err.buf);
+ }
-int create_symref(const char *ref_target, const char *refs_heads_master,
- const char *logmsg)
-{
- return refs_create_symref(get_main_ref_store(the_repository), ref_target,
- refs_heads_master, logmsg);
+ strbuf_release(&err);
+ if (transaction)
+ ref_transaction_free(transaction);
+
+ return ret;
}
int ref_update_reject_duplicates(struct string_list *refnames,
@@ -2310,10 +2153,22 @@ static int run_transaction_hook(struct ref_transaction *transaction,
struct ref_update *update = transaction->updates[i];
strbuf_reset(&buf);
- strbuf_addf(&buf, "%s %s %s\n",
- oid_to_hex(&update->old_oid),
- oid_to_hex(&update->new_oid),
- update->refname);
+
+ if (!(update->flags & REF_HAVE_OLD))
+ strbuf_addf(&buf, "%s ", oid_to_hex(null_oid()));
+ else if (update->old_target)
+ strbuf_addf(&buf, "ref:%s ", update->old_target);
+ else
+ strbuf_addf(&buf, "%s ", oid_to_hex(&update->old_oid));
+
+ if (!(update->flags & REF_HAVE_NEW))
+ strbuf_addf(&buf, "%s ", oid_to_hex(null_oid()));
+ else if (update->new_target)
+ strbuf_addf(&buf, "ref:%s ", update->new_target);
+ else
+ strbuf_addf(&buf, "%s ", oid_to_hex(&update->new_oid));
+
+ strbuf_addf(&buf, "%s\n", update->refname);
if (write_in_full(proc.in, buf.buf, buf.len) < 0) {
if (errno != EPIPE) {
@@ -2532,8 +2387,7 @@ struct do_for_each_reflog_help {
void *cb_data;
};
-static int do_for_each_reflog_helper(struct repository *r UNUSED,
- const char *refname,
+static int do_for_each_reflog_helper(const char *refname,
const struct object_id *oid UNUSED,
int flags,
void *cb_data)
@@ -2549,13 +2403,7 @@ int refs_for_each_reflog(struct ref_store *refs, each_reflog_fn fn, void *cb_dat
iter = refs->be->reflog_iterator_begin(refs);
- return do_for_each_repo_ref_iterator(the_repository, iter,
- do_for_each_reflog_helper, &hp);
-}
-
-int for_each_reflog(each_reflog_fn fn, void *cb_data)
-{
- return refs_for_each_reflog(get_main_ref_store(the_repository), fn, cb_data);
+ return do_for_each_ref_iterator(iter, do_for_each_reflog_helper, &hp);
}
int refs_for_each_reflog_ent_reverse(struct ref_store *refs,
@@ -2567,58 +2415,28 @@ int refs_for_each_reflog_ent_reverse(struct ref_store *refs,
fn, cb_data);
}
-int for_each_reflog_ent_reverse(const char *refname, each_reflog_ent_fn fn,
- void *cb_data)
-{
- return refs_for_each_reflog_ent_reverse(get_main_ref_store(the_repository),
- refname, fn, cb_data);
-}
-
int refs_for_each_reflog_ent(struct ref_store *refs, const char *refname,
each_reflog_ent_fn fn, void *cb_data)
{
return refs->be->for_each_reflog_ent(refs, refname, fn, cb_data);
}
-int for_each_reflog_ent(const char *refname, each_reflog_ent_fn fn,
- void *cb_data)
-{
- return refs_for_each_reflog_ent(get_main_ref_store(the_repository), refname,
- fn, cb_data);
-}
-
int refs_reflog_exists(struct ref_store *refs, const char *refname)
{
return refs->be->reflog_exists(refs, refname);
}
-int reflog_exists(const char *refname)
-{
- return refs_reflog_exists(get_main_ref_store(the_repository), refname);
-}
-
int refs_create_reflog(struct ref_store *refs, const char *refname,
struct strbuf *err)
{
return refs->be->create_reflog(refs, refname, err);
}
-int safe_create_reflog(const char *refname, struct strbuf *err)
-{
- return refs_create_reflog(get_main_ref_store(the_repository), refname,
- err);
-}
-
int refs_delete_reflog(struct ref_store *refs, const char *refname)
{
return refs->be->delete_reflog(refs, refname);
}
-int delete_reflog(const char *refname)
-{
- return refs_delete_reflog(get_main_ref_store(the_repository), refname);
-}
-
int refs_reflog_expire(struct ref_store *refs,
const char *refname,
unsigned int flags,
@@ -2632,19 +2450,6 @@ int refs_reflog_expire(struct ref_store *refs,
cleanup_fn, policy_cb_data);
}
-int reflog_expire(const char *refname,
- unsigned int flags,
- reflog_expiry_prepare_fn prepare_fn,
- reflog_expiry_should_prune_fn should_prune_fn,
- reflog_expiry_cleanup_fn cleanup_fn,
- void *policy_cb_data)
-{
- return refs_reflog_expire(get_main_ref_store(the_repository),
- refname, flags,
- prepare_fn, should_prune_fn,
- cleanup_fn, policy_cb_data);
-}
-
int initial_ref_transaction_commit(struct ref_transaction *transaction,
struct strbuf *err)
{
@@ -2696,7 +2501,7 @@ int refs_delete_refs(struct ref_store *refs, const char *logmsg,
for_each_string_list_item(item, refnames) {
ret = ref_transaction_delete(transaction, item->string,
- NULL, flags, msg, &err);
+ NULL, NULL, flags, msg, &err);
if (ret) {
warning(_("could not delete reference %s: %s"),
item->string, err.buf);
@@ -2723,12 +2528,6 @@ out:
return ret;
}
-int delete_refs(const char *msg, struct string_list *refnames,
- unsigned int flags)
-{
- return refs_delete_refs(get_main_ref_store(the_repository), msg, refnames, flags);
-}
-
int refs_rename_ref(struct ref_store *refs, const char *oldref,
const char *newref, const char *logmsg)
{
@@ -2741,11 +2540,6 @@ int refs_rename_ref(struct ref_store *refs, const char *oldref,
return retval;
}
-int rename_ref(const char *oldref, const char *newref, const char *logmsg)
-{
- return refs_rename_ref(get_main_ref_store(the_repository), oldref, newref, logmsg);
-}
-
int refs_copy_existing_ref(struct ref_store *refs, const char *oldref,
const char *newref, const char *logmsg)
{
@@ -2758,7 +2552,352 @@ int refs_copy_existing_ref(struct ref_store *refs, const char *oldref,
return retval;
}
-int copy_existing_ref(const char *oldref, const char *newref, const char *logmsg)
+const char *ref_update_original_update_refname(struct ref_update *update)
+{
+ while (update->parent_update)
+ update = update->parent_update;
+
+ return update->refname;
+}
+
+int ref_update_has_null_new_value(struct ref_update *update)
+{
+ return !update->new_target && is_null_oid(&update->new_oid);
+}
+
+int ref_update_check_old_target(const char *referent, struct ref_update *update,
+ struct strbuf *err)
+{
+ if (!update->old_target)
+ BUG("called without old_target set");
+
+ if (!strcmp(referent, update->old_target))
+ return 0;
+
+ if (!strcmp(referent, ""))
+ strbuf_addf(err, "verifying symref target: '%s': "
+ "reference is missing but expected %s",
+ ref_update_original_update_refname(update),
+ update->old_target);
+ else
+ strbuf_addf(err, "verifying symref target: '%s': "
+ "is at %s but expected %s",
+ ref_update_original_update_refname(update),
+ referent, update->old_target);
+ return -1;
+}
+
+struct migration_data {
+ struct ref_store *old_refs;
+ struct ref_transaction *transaction;
+ struct strbuf *errbuf;
+};
+
+static int migrate_one_ref(const char *refname, const struct object_id *oid,
+ int flags, void *cb_data)
+{
+ struct migration_data *data = cb_data;
+ struct strbuf symref_target = STRBUF_INIT;
+ int ret;
+
+ if (flags & REF_ISSYMREF) {
+ ret = refs_read_symbolic_ref(data->old_refs, refname, &symref_target);
+ if (ret < 0)
+ goto done;
+
+ ret = ref_transaction_update(data->transaction, refname, NULL, null_oid(),
+ symref_target.buf, NULL,
+ REF_SKIP_CREATE_REFLOG | REF_NO_DEREF, NULL, data->errbuf);
+ if (ret < 0)
+ goto done;
+ } else {
+ ret = ref_transaction_create(data->transaction, refname, oid, NULL,
+ REF_SKIP_CREATE_REFLOG | REF_SKIP_OID_VERIFICATION,
+ NULL, data->errbuf);
+ if (ret < 0)
+ goto done;
+ }
+
+done:
+ strbuf_release(&symref_target);
+ return ret;
+}
+
+static int move_files(const char *from_path, const char *to_path, struct strbuf *errbuf)
+{
+ struct strbuf from_buf = STRBUF_INIT, to_buf = STRBUF_INIT;
+ size_t from_len, to_len;
+ DIR *from_dir;
+ int ret;
+
+ from_dir = opendir(from_path);
+ if (!from_dir) {
+ strbuf_addf(errbuf, "could not open source directory '%s': %s",
+ from_path, strerror(errno));
+ ret = -1;
+ goto done;
+ }
+
+ strbuf_addstr(&from_buf, from_path);
+ strbuf_complete(&from_buf, '/');
+ from_len = from_buf.len;
+
+ strbuf_addstr(&to_buf, to_path);
+ strbuf_complete(&to_buf, '/');
+ to_len = to_buf.len;
+
+ while (1) {
+ struct dirent *ent;
+
+ errno = 0;
+ ent = readdir(from_dir);
+ if (!ent)
+ break;
+
+ if (!strcmp(ent->d_name, ".") ||
+ !strcmp(ent->d_name, ".."))
+ continue;
+
+ strbuf_setlen(&from_buf, from_len);
+ strbuf_addstr(&from_buf, ent->d_name);
+
+ strbuf_setlen(&to_buf, to_len);
+ strbuf_addstr(&to_buf, ent->d_name);
+
+ ret = rename(from_buf.buf, to_buf.buf);
+ if (ret < 0) {
+ strbuf_addf(errbuf, "could not link file '%s' to '%s': %s",
+ from_buf.buf, to_buf.buf, strerror(errno));
+ goto done;
+ }
+ }
+
+ if (errno) {
+ strbuf_addf(errbuf, "could not read entry from directory '%s': %s",
+ from_path, strerror(errno));
+ ret = -1;
+ goto done;
+ }
+
+ ret = 0;
+
+done:
+ strbuf_release(&from_buf);
+ strbuf_release(&to_buf);
+ if (from_dir)
+ closedir(from_dir);
+ return ret;
+}
+
+static int count_reflogs(const char *reflog UNUSED, void *payload)
+{
+ size_t *reflog_count = payload;
+ (*reflog_count)++;
+ return 0;
+}
+
+static int has_worktrees(void)
{
- return refs_copy_existing_ref(get_main_ref_store(the_repository), oldref, newref, logmsg);
+ struct worktree **worktrees = get_worktrees();
+ int ret = 0;
+ size_t i;
+
+ for (i = 0; worktrees[i]; i++) {
+ if (is_main_worktree(worktrees[i]))
+ continue;
+ ret = 1;
+ }
+
+ free_worktrees(worktrees);
+ return ret;
}
+
+int repo_migrate_ref_storage_format(struct repository *repo,
+ enum ref_storage_format format,
+ unsigned int flags,
+ struct strbuf *errbuf)
+{
+ struct ref_store *old_refs = NULL, *new_refs = NULL;
+ struct ref_transaction *transaction = NULL;
+ struct strbuf new_gitdir = STRBUF_INIT;
+ struct migration_data data;
+ size_t reflog_count = 0;
+ int did_migrate_refs = 0;
+ int ret;
+
+ if (repo->ref_storage_format == format) {
+ strbuf_addstr(errbuf, "current and new ref storage format are equal");
+ ret = -1;
+ goto done;
+ }
+
+ old_refs = get_main_ref_store(repo);
+
+ /*
+ * We do not have any interfaces that would allow us to write many
+ * reflog entries. Once we have them we can remove this restriction.
+ */
+ if (refs_for_each_reflog(old_refs, count_reflogs, &reflog_count) < 0) {
+ strbuf_addstr(errbuf, "cannot count reflogs");
+ ret = -1;
+ goto done;
+ }
+ if (reflog_count) {
+ strbuf_addstr(errbuf, "migrating reflogs is not supported yet");
+ ret = -1;
+ goto done;
+ }
+
+ /*
+ * Worktrees complicate the migration because every worktree has a
+ * separate ref storage. While it should be feasible to implement, this
+ * is pushed out to a future iteration.
+ *
+ * TODO: we should really be passing the caller-provided repository to
+ * `has_worktrees()`, but our worktree subsystem doesn't yet support
+ * that.
+ */
+ if (has_worktrees()) {
+ strbuf_addstr(errbuf, "migrating repositories with worktrees is not supported yet");
+ ret = -1;
+ goto done;
+ }
+
+ /*
+ * The overall logic looks like this:
+ *
+ * 1. Set up a new temporary directory and initialize it with the new
+ * format. This is where all refs will be migrated into.
+ *
+ * 2. Enumerate all refs and write them into the new ref storage.
+ * This operation is safe as we do not yet modify the main
+ * repository.
+ *
+ * 3. If we're in dry-run mode then we are done and can hand over the
+ * directory to the caller for inspection. If not, we now start
+ * with the destructive part.
+ *
+ * 4. Delete the old ref storage from disk. As we have a copy of refs
+ * in the new ref storage it's okay(ish) if we now get interrupted
+ * as there is an equivalent copy of all refs available.
+ *
+ * 5. Move the new ref storage files into place.
+ *
+ * 6. Change the repository format to the new ref format.
+ */
+ strbuf_addf(&new_gitdir, "%s/%s", old_refs->gitdir, "ref_migration.XXXXXX");
+ if (!mkdtemp(new_gitdir.buf)) {
+ strbuf_addf(errbuf, "cannot create migration directory: %s",
+ strerror(errno));
+ ret = -1;
+ goto done;
+ }
+
+ new_refs = ref_store_init(repo, format, new_gitdir.buf,
+ REF_STORE_ALL_CAPS);
+ ret = ref_store_create_on_disk(new_refs, 0, errbuf);
+ if (ret < 0)
+ goto done;
+
+ transaction = ref_store_transaction_begin(new_refs, errbuf);
+ if (!transaction)
+ goto done;
+
+ data.old_refs = old_refs;
+ data.transaction = transaction;
+ data.errbuf = errbuf;
+
+ /*
+ * We need to use the internal `do_for_each_ref()` here so that we can
+ * also include broken refs and symrefs. These would otherwise be
+ * skipped silently.
+ *
+ * Ideally, we would do this call while locking the old ref storage
+ * such that there cannot be any concurrent modifications. We do not
+ * have the infra for that though, and the "files" backend does not
+ * allow for a central lock due to its design. It's thus on the user to
+ * ensure that there are no concurrent writes.
+ */
+ ret = do_for_each_ref(old_refs, "", NULL, migrate_one_ref, 0,
+ DO_FOR_EACH_INCLUDE_ROOT_REFS | DO_FOR_EACH_INCLUDE_BROKEN,
+ &data);
+ if (ret < 0)
+ goto done;
+
+ /*
+ * TODO: we might want to migrate to `initial_ref_transaction_commit()`
+ * here, which is more efficient for the files backend because it would
+ * write new refs into the packed-refs file directly. At this point,
+ * the files backend doesn't handle pseudo-refs and symrefs correctly
+ * though, so this requires some more work.
+ */
+ ret = ref_transaction_commit(transaction, errbuf);
+ if (ret < 0)
+ goto done;
+ did_migrate_refs = 1;
+
+ if (flags & REPO_MIGRATE_REF_STORAGE_FORMAT_DRYRUN) {
+ printf(_("Finished dry-run migration of refs, "
+ "the result can be found at '%s'\n"), new_gitdir.buf);
+ ret = 0;
+ goto done;
+ }
+
+ /*
+ * Until now we were in the non-destructive phase, where we only
+ * populated the new ref store. From hereon though we are about
+ * to get hands by deleting the old ref store and then moving
+ * the new one into place.
+ *
+ * Assuming that there were no concurrent writes, the new ref
+ * store should have all information. So if we fail from hereon
+ * we may be in an in-between state, but it would still be able
+ * to recover by manually moving remaining files from the
+ * temporary migration directory into place.
+ */
+ ret = ref_store_remove_on_disk(old_refs, errbuf);
+ if (ret < 0)
+ goto done;
+
+ ret = move_files(new_gitdir.buf, old_refs->gitdir, errbuf);
+ if (ret < 0)
+ goto done;
+
+ if (rmdir(new_gitdir.buf) < 0)
+ warning_errno(_("could not remove temporary migration directory '%s'"),
+ new_gitdir.buf);
+
+ /*
+ * We have migrated the repository, so we now need to adjust the
+ * repository format so that clients will use the new ref store.
+ * We also need to swap out the repository's main ref store.
+ */
+ initialize_repository_version(hash_algo_by_ptr(repo->hash_algo), format, 1);
+
+ free(new_refs->gitdir);
+ new_refs->gitdir = xstrdup(old_refs->gitdir);
+ repo->refs_private = new_refs;
+ ref_store_release(old_refs);
+
+ ret = 0;
+
+done:
+ if (ret && did_migrate_refs) {
+ strbuf_complete(errbuf, '\n');
+ strbuf_addf(errbuf, _("migrated refs can be found at '%s'"),
+ new_gitdir.buf);
+ }
+
+ if (ret && new_refs)
+ ref_store_release(new_refs);
+ ref_transaction_free(transaction);
+ strbuf_release(&new_gitdir);
+ return ret;
+}
+
+int ref_update_expects_existing_old_ref(struct ref_update *update)
+{
+ return (update->flags & REF_HAVE_OLD) &&
+ (!is_null_oid(&update->old_oid) || update->old_target);
+}
+
diff --git a/refs.h b/refs.h
index dc4358727f..b3e39bc257 100644
--- a/refs.h
+++ b/refs.h
@@ -2,17 +2,17 @@
#define REFS_H
#include "commit.h"
+#include "repository.h"
struct object_id;
struct ref_store;
-struct repository;
struct strbuf;
struct string_list;
struct string_list_item;
struct worktree;
-unsigned int ref_storage_format_by_name(const char *name);
-const char *ref_storage_format_to_name(unsigned int ref_storage_format);
+enum ref_storage_format ref_storage_format_by_name(const char *name);
+const char *ref_storage_format_to_name(enum ref_storage_format ref_storage_format);
/*
* Resolve a reference, recursively following symbolic refererences.
@@ -72,18 +72,14 @@ const char *refs_resolve_ref_unsafe(struct ref_store *refs,
struct object_id *oid,
int *flags);
-const char *resolve_ref_unsafe(const char *refname, int resolve_flags,
- struct object_id *oid, int *flags);
-
char *refs_resolve_refdup(struct ref_store *refs,
const char *refname, int resolve_flags,
struct object_id *oid, int *flags);
-char *resolve_refdup(const char *refname, int resolve_flags,
- struct object_id *oid, int *flags);
-int read_ref_full(const char *refname, int resolve_flags,
- struct object_id *oid, int *flags);
-int read_ref(const char *refname, struct object_id *oid);
+int refs_read_ref_full(struct ref_store *refs, const char *refname,
+ int resolve_flags, struct object_id *oid, int *flags);
+
+int refs_read_ref(struct ref_store *refs, const char *refname, struct object_id *oid);
int refs_read_symbolic_ref(struct ref_store *ref_store, const char *refname,
struct strbuf *referent);
@@ -114,27 +110,36 @@ int refs_verify_refname_available(struct ref_store *refs,
int refs_ref_exists(struct ref_store *refs, const char *refname);
-int ref_exists(const char *refname);
-
int should_autocreate_reflog(const char *refname);
int is_branch(const char *refname);
-#define REFS_INIT_DB_IS_WORKTREE (1 << 0)
+#define REF_STORE_CREATE_ON_DISK_IS_WORKTREE (1 << 0)
-int refs_init_db(struct ref_store *refs, int flags, struct strbuf *err);
+int ref_store_create_on_disk(struct ref_store *refs, int flags, struct strbuf *err);
+
+/*
+ * Release all memory and resources associated with the ref store.
+ */
+void ref_store_release(struct ref_store *ref_store);
+
+/*
+ * Remove the ref store from disk. This deletes all associated data.
+ */
+int ref_store_remove_on_disk(struct ref_store *refs, struct strbuf *err);
/*
* Return the peeled value of the oid currently being iterated via
* for_each_ref(), etc. This is equivalent to calling:
*
- * peel_object(oid, &peeled);
+ * peel_object(r, oid, &peeled);
*
* with the "oid" value given to the each_ref_fn callback, except
* that some ref storage may be able to answer the query without
* actually loading the object in memory.
*/
-int peel_iterated_oid(const struct object_id *base, struct object_id *peeled);
+int peel_iterated_oid(struct repository *r,
+ const struct object_id *base, struct object_id *peeled);
/**
* Resolve refname in the nested "gitlink" repository in the specified
@@ -142,8 +147,9 @@ int peel_iterated_oid(const struct object_id *base, struct object_id *peeled);
* successful, return 0 and set oid to the name of the object;
* otherwise, return a non-zero value.
*/
-int resolve_gitlink_ref(const char *submodule, const char *refname,
- struct object_id *oid);
+int repo_resolve_gitlink_ref(struct repository *r,
+ const char *submodule, const char *refname,
+ struct object_id *oid);
/*
* Return true iff abbrev_name is a possible abbreviation for
@@ -163,15 +169,12 @@ int expand_ref(struct repository *r, const char *str, int len, struct object_id
int repo_dwim_ref(struct repository *r, const char *str, int len,
struct object_id *oid, char **ref, int nonfatal_dangling_mark);
int repo_dwim_log(struct repository *r, const char *str, int len, struct object_id *oid, char **ref);
-int dwim_log(const char *str, int len, struct object_id *oid, char **ref);
/*
* Retrieves the default branch name for newly-initialized repositories.
*
- * The return value of `repo_default_branch_name()` is an allocated string. The
- * return value of `git_default_branch_name()` is a singleton.
+ * The return value is an allocated string.
*/
-const char *git_default_branch_name(int quiet);
char *repo_default_branch_name(struct repository *r, int quiet);
/*
@@ -299,16 +302,6 @@ typedef int each_ref_fn(const char *refname,
const struct object_id *oid, int flags, void *cb_data);
/*
- * The same as each_ref_fn, but also with a repository argument that
- * contains the repository associated with the callback.
- */
-typedef int each_repo_ref_fn(struct repository *r,
- const char *refname,
- const struct object_id *oid,
- int flags,
- void *cb_data);
-
-/*
* The following functions invoke the specified callback function for
* each reference indicated. If the function ever returns a nonzero
* value, stop the iteration and return that value. Please note that
@@ -329,18 +322,8 @@ int refs_for_each_branch_ref(struct ref_store *refs,
each_ref_fn fn, void *cb_data);
int refs_for_each_remote_ref(struct ref_store *refs,
each_ref_fn fn, void *cb_data);
-
-/* just iterates the head ref. */
-int head_ref(each_ref_fn fn, void *cb_data);
-
-/* iterates all refs. */
-int for_each_ref(each_ref_fn fn, void *cb_data);
-
-/**
- * iterates all refs which have a defined prefix and strips that prefix from
- * the passed variable refname.
- */
-int for_each_ref_in(const char *prefix, each_ref_fn fn, void *cb_data);
+int refs_for_each_replace_ref(struct ref_store *refs,
+ each_ref_fn fn, void *cb_data);
/*
* references matching any pattern in "exclude_patterns" are omitted from the
@@ -349,7 +332,6 @@ 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);
/**
* iterate all refs in "patterns" by partitioning patterns into disjoint sets
@@ -366,31 +348,25 @@ int refs_for_each_fullref_in_prefixes(struct ref_store *refs,
const char **exclude_patterns,
each_ref_fn fn, void *cb_data);
-/**
- * iterate refs from the respective area.
- */
-int for_each_tag_ref(each_ref_fn fn, void *cb_data);
-int for_each_branch_ref(each_ref_fn fn, void *cb_data);
-int for_each_remote_ref(each_ref_fn fn, void *cb_data);
-int for_each_replace_ref(struct repository *r, each_repo_ref_fn fn, void *cb_data);
-
/* iterates all refs that match the specified glob pattern. */
-int for_each_glob_ref(each_ref_fn fn, const char *pattern, void *cb_data);
+int refs_for_each_glob_ref(struct ref_store *refs, each_ref_fn fn,
+ const char *pattern, void *cb_data);
+
+int refs_for_each_glob_ref_in(struct ref_store *refs, each_ref_fn fn,
+ const char *pattern, const char *prefix, void *cb_data);
-int for_each_glob_ref_in(each_ref_fn fn, const char *pattern,
- const char *prefix, void *cb_data);
+int refs_head_ref_namespaced(struct ref_store *refs, each_ref_fn fn, void *cb_data);
-int head_ref_namespaced(each_ref_fn fn, void *cb_data);
/*
* references matching any pattern in "exclude_patterns" are omitted from the
* result set on a best-effort basis.
*/
-int for_each_namespaced_ref(const char **exclude_patterns,
- each_ref_fn fn, void *cb_data);
+int refs_for_each_namespaced_ref(struct ref_store *refs,
+ 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);
-int for_each_rawref(each_ref_fn fn, void *cb_data);
/*
* Iterates over all refs including root refs, i.e. pseudorefs and HEAD.
@@ -415,9 +391,10 @@ static inline const char *has_glob_specials(const char *pattern)
return strpbrk(pattern, "?*[");
}
-void warn_dangling_symref(FILE *fp, const char *msg_fmt, const char *refname);
-void warn_dangling_symrefs(FILE *fp, const char *msg_fmt,
- const struct string_list *refnames);
+void refs_warn_dangling_symref(struct ref_store *refs, FILE *fp,
+ const char *msg_fmt, const char *refname);
+void refs_warn_dangling_symrefs(struct ref_store *refs, FILE *fp,
+ const char *msg_fmt, const struct string_list *refnames);
/*
* Flags for controlling behaviour of pack_refs()
@@ -446,7 +423,6 @@ int refs_pack_refs(struct ref_store *refs, struct pack_refs_opts *opts);
*/
int refs_create_reflog(struct ref_store *refs, const char *refname,
struct strbuf *err);
-int safe_create_reflog(const char *refname, struct strbuf *err);
/**
* Reads log for the value of ref during at_time (in which case "cnt" should be
@@ -470,7 +446,6 @@ int read_ref_at(struct ref_store *refs,
/** Check if a particular reflog exists */
int refs_reflog_exists(struct ref_store *refs, const char *refname);
-int reflog_exists(const char *refname);
/*
* Delete the specified reference. If old_oid is non-NULL, then
@@ -484,8 +459,6 @@ int refs_delete_ref(struct ref_store *refs, const char *msg,
const char *refname,
const struct object_id *old_oid,
unsigned int flags);
-int delete_ref(const char *msg, const char *refname,
- const struct object_id *old_oid, unsigned int flags);
/*
* Delete the specified references. If there are any problems, emit
@@ -495,12 +468,9 @@ int delete_ref(const char *msg, const char *refname,
*/
int refs_delete_refs(struct ref_store *refs, const char *msg,
struct string_list *refnames, unsigned int flags);
-int delete_refs(const char *msg, struct string_list *refnames,
- unsigned int flags);
/** Delete a reflog */
int refs_delete_reflog(struct ref_store *refs, const char *refname);
-int delete_reflog(const char *refname);
/*
* Callback to process a reflog entry found by the iteration functions (see
@@ -546,17 +516,7 @@ int refs_for_each_reflog_ent_reverse(struct ref_store *refs,
void *cb_data);
/*
- * Iterate over reflog entries in the log for `refname` in the main ref store.
- */
-
-/* oldest entry first */
-int for_each_reflog_ent(const char *refname, each_reflog_ent_fn fn, void *cb_data);
-
-/* youngest entry first */
-int for_each_reflog_ent_reverse(const char *refname, each_reflog_ent_fn fn, void *cb_data);
-
-/*
- * The signature for the callback function for the {refs_,}for_each_reflog()
+ * The signature for the callback function for the refs_for_each_reflog()
* functions below. The memory pointed to by the refname argument is only
* guaranteed to be valid for the duration of a single callback invocation.
*/
@@ -567,7 +527,6 @@ typedef int each_reflog_fn(const char *refname, void *cb_data);
* and returns the value. Reflog file order is unspecified.
*/
int refs_for_each_reflog(struct ref_store *refs, each_reflog_fn fn, void *cb_data);
-int for_each_reflog(each_reflog_fn fn, void *cb_data);
#define REFNAME_ALLOW_ONELEVEL 1
#define REFNAME_REFSPEC_PATTERN 2
@@ -592,23 +551,17 @@ const char *prettify_refname(const char *refname);
char *refs_shorten_unambiguous_ref(struct ref_store *refs,
const char *refname, int strict);
-char *shorten_unambiguous_ref(const char *refname, int strict);
/** rename ref, return 0 on success **/
int refs_rename_ref(struct ref_store *refs, const char *oldref,
const char *newref, const char *logmsg);
-int rename_ref(const char *oldref, const char *newref,
- const char *logmsg);
/** copy ref, return 0 on success **/
int refs_copy_existing_ref(struct ref_store *refs, const char *oldref,
const char *newref, const char *logmsg);
-int copy_existing_ref(const char *oldref, const char *newref,
- const char *logmsg);
-int refs_create_symref(struct ref_store *refs, const char *refname,
+int refs_update_symref(struct ref_store *refs, const char *refname,
const char *target, const char *logmsg);
-int create_symref(const char *refname, const char *target, const char *logmsg);
enum action_on_err {
UPDATE_REFS_MSG_ON_ERR,
@@ -622,7 +575,6 @@ enum action_on_err {
*/
struct ref_transaction *ref_store_transaction_begin(struct ref_store *refs,
struct strbuf *err);
-struct ref_transaction *ref_transaction_begin(struct strbuf *err);
/*
* Reference transaction updates
@@ -648,6 +600,16 @@ struct ref_transaction *ref_transaction_begin(struct strbuf *err);
* before the update. A copy of this value is made in the
* transaction.
*
+ * new_target -- the target reference that the reference will be
+ * updated to point to. If the reference is a regular reference,
+ * it will be converted to a symbolic reference. Cannot be set
+ * together with `new_oid`. A copy of this value is made in the
+ * transaction.
+ *
+ * old_target -- the reference that the reference must be pointing to.
+ * Canont be set together with `old_oid`. A copy of this value is
+ * made in the transaction.
+ *
* flags -- flags affecting the update, passed to
* update_ref_lock(). Possible flags: REF_NO_DEREF,
* REF_FORCE_CREATE_REFLOG. See those constants for more
@@ -697,12 +659,18 @@ struct ref_transaction *ref_transaction_begin(struct strbuf *err);
#define REF_SKIP_REFNAME_VERIFICATION (1 << 11)
/*
+ * Skip creation of a reflog entry, even if it would have otherwise been
+ * created.
+ */
+#define REF_SKIP_CREATE_REFLOG (1 << 12)
+
+/*
* Bitmask of all of the flags that are allowed to be passed in to
* ref_transaction_update() and friends:
*/
#define REF_TRANSACTION_UPDATE_ALLOWED_FLAGS \
(REF_NO_DEREF | REF_FORCE_CREATE_REFLOG | REF_SKIP_OID_VERIFICATION | \
- REF_SKIP_REFNAME_VERIFICATION)
+ REF_SKIP_REFNAME_VERIFICATION | REF_SKIP_CREATE_REFLOG)
/*
* Add a reference update to transaction. `new_oid` is the value that
@@ -713,7 +681,11 @@ struct ref_transaction *ref_transaction_begin(struct strbuf *err);
* beforehand. The old value is checked after the lock is taken to
* prevent races. If the old value doesn't agree with old_oid, the
* whole transaction fails. If old_oid is NULL, then the previous
- * value is not checked.
+ * value is not checked. If `old_target` is not NULL, treat the reference
+ * as a symbolic ref and validate that its target before the update is
+ * `old_target`. If the `new_target` is not NULL, then the reference
+ * will be updated to a symbolic ref which targets `new_target`.
+ * Together, these allow us to update between regular refs and symrefs.
*
* See the above comment "Reference transaction updates" for more
* information.
@@ -722,6 +694,8 @@ int ref_transaction_update(struct ref_transaction *transaction,
const char *refname,
const struct object_id *new_oid,
const struct object_id *old_oid,
+ const char *new_target,
+ const char *old_target,
unsigned int flags, const char *msg,
struct strbuf *err);
@@ -737,6 +711,7 @@ int ref_transaction_update(struct ref_transaction *transaction,
int ref_transaction_create(struct ref_transaction *transaction,
const char *refname,
const struct object_id *new_oid,
+ const char *new_target,
unsigned int flags, const char *msg,
struct strbuf *err);
@@ -751,7 +726,9 @@ int ref_transaction_create(struct ref_transaction *transaction,
int ref_transaction_delete(struct ref_transaction *transaction,
const char *refname,
const struct object_id *old_oid,
- unsigned int flags, const char *msg,
+ const char *old_target,
+ unsigned int flags,
+ const char *msg,
struct strbuf *err);
/*
@@ -765,6 +742,7 @@ int ref_transaction_delete(struct ref_transaction *transaction,
int ref_transaction_verify(struct ref_transaction *transaction,
const char *refname,
const struct object_id *old_oid,
+ const char *old_target,
unsigned int flags,
struct strbuf *err);
@@ -853,9 +831,6 @@ void ref_transaction_free(struct ref_transaction *transaction);
int refs_update_ref(struct ref_store *refs, const char *msg, const char *refname,
const struct object_id *new_oid, const struct object_id *old_oid,
unsigned int flags, enum action_on_err onerr);
-int update_ref(const char *msg, const char *refname,
- const struct object_id *new_oid, const struct object_id *old_oid,
- unsigned int flags, enum action_on_err onerr);
int parse_hide_refs_config(const char *var, const char *value, const char *,
struct strvec *);
@@ -913,7 +888,7 @@ enum expire_reflog_flags {
/*
* The following interface is used for reflog expiration. The caller
- * calls reflog_expire(), supplying it with three callback functions,
+ * calls refs_reflog_expire(), supplying it with three callback functions,
* of the following types. The callback functions define the
* expiration policy that is desired.
*
@@ -950,12 +925,6 @@ int refs_reflog_expire(struct ref_store *refs,
reflog_expiry_should_prune_fn should_prune_fn,
reflog_expiry_cleanup_fn cleanup_fn,
void *policy_cb_data);
-int reflog_expire(const char *refname,
- unsigned int flags,
- reflog_expiry_prepare_fn prepare_fn,
- reflog_expiry_should_prune_fn should_prune_fn,
- reflog_expiry_cleanup_fn cleanup_fn,
- void *policy_cb_data);
struct ref_store *get_main_ref_store(struct repository *r);
@@ -1003,7 +972,8 @@ struct ref_store *get_main_ref_store(struct repository *r);
* For backwards compatibility, submodule=="" is treated the same as
* submodule==NULL.
*/
-struct ref_store *get_submodule_ref_store(const char *submodule);
+struct ref_store *repo_get_submodule_ref_store(struct repository *repo,
+ const char *submodule);
struct ref_store *get_worktree_ref_store(const struct worktree *wt);
/*
@@ -1013,7 +983,7 @@ struct ref_store *get_worktree_ref_store(const struct worktree *wt);
*/
struct ref_namespace_info {
- char *ref;
+ const char *ref;
enum decoration_type decoration;
/*
@@ -1098,4 +1068,229 @@ int is_root_ref(const char *refname);
*/
int is_pseudo_ref(const char *refname);
+/*
+ * The following flags can be passed to `repo_migrate_ref_storage_format()`:
+ *
+ * - REPO_MIGRATE_REF_STORAGE_FORMAT_DRYRUN: perform a dry-run migration
+ * without touching the main repository. The result will be written into a
+ * temporary ref storage directory.
+ */
+#define REPO_MIGRATE_REF_STORAGE_FORMAT_DRYRUN (1 << 0)
+
+/*
+ * Migrate the ref storage format used by the repository to the
+ * specified one.
+ */
+int repo_migrate_ref_storage_format(struct repository *repo,
+ enum ref_storage_format format,
+ unsigned int flags,
+ struct strbuf *err);
+
+/*
+ * The following functions have been removed in Git v2.46 in favor of functions
+ * that receive a `ref_store` as parameter. The intent of this section is
+ * merely to help patch authors of in-flight series to have a reference what
+ * they should be migrating to. The section will be removed in Git v2.47.
+ */
+#if 0
+static char *resolve_refdup(const char *refname, int resolve_flags,
+ struct object_id *oid, int *flags)
+{
+ return refs_resolve_refdup(get_main_ref_store(the_repository),
+ refname, resolve_flags,
+ oid, flags);
+}
+
+static int read_ref_full(const char *refname, int resolve_flags,
+ struct object_id *oid, int *flags)
+{
+ return refs_read_ref_full(get_main_ref_store(the_repository), refname,
+ resolve_flags, oid, flags);
+}
+
+static int read_ref(const char *refname, struct object_id *oid)
+{
+ return refs_read_ref(get_main_ref_store(the_repository), refname, oid);
+}
+
+static int ref_exists(const char *refname)
+{
+ return refs_ref_exists(get_main_ref_store(the_repository), refname);
+}
+
+static int for_each_tag_ref(each_ref_fn fn, void *cb_data)
+{
+ return refs_for_each_tag_ref(get_main_ref_store(the_repository), fn, cb_data);
+}
+
+static int for_each_branch_ref(each_ref_fn fn, void *cb_data)
+{
+ return refs_for_each_branch_ref(get_main_ref_store(the_repository), fn, cb_data);
+}
+
+static int for_each_remote_ref(each_ref_fn fn, void *cb_data)
+{
+ return refs_for_each_remote_ref(get_main_ref_store(the_repository), fn, cb_data);
+}
+
+static int head_ref_namespaced(each_ref_fn fn, void *cb_data)
+{
+ return refs_head_ref_namespaced(get_main_ref_store(the_repository),
+ fn, cb_data);
+}
+
+static int for_each_glob_ref_in(each_ref_fn fn, const char *pattern,
+ const char *prefix, void *cb_data)
+{
+ return refs_for_each_glob_ref_in(get_main_ref_store(the_repository),
+ fn, pattern, prefix, cb_data);
+}
+
+static int for_each_glob_ref(each_ref_fn fn, const char *pattern, void *cb_data)
+{
+ return refs_for_each_glob_ref(get_main_ref_store(the_repository),
+ fn, pattern, cb_data);
+}
+
+static int delete_ref(const char *msg, const char *refname,
+ const struct object_id *old_oid, unsigned int flags)
+{
+ return refs_delete_ref(get_main_ref_store(the_repository), msg, refname,
+ old_oid, flags);
+}
+
+static struct ref_transaction *ref_transaction_begin(struct strbuf *err)
+{
+ return ref_store_transaction_begin(get_main_ref_store(the_repository), err);
+}
+
+static int update_ref(const char *msg, const char *refname,
+ const struct object_id *new_oid,
+ const struct object_id *old_oid,
+ unsigned int flags, enum action_on_err onerr)
+{
+ return refs_update_ref(get_main_ref_store(the_repository), msg, refname, new_oid,
+ old_oid, flags, onerr);
+}
+
+static char *shorten_unambiguous_ref(const char *refname, int strict)
+{
+ return refs_shorten_unambiguous_ref(get_main_ref_store(the_repository),
+ refname, strict);
+}
+
+static int head_ref(each_ref_fn fn, void *cb_data)
+{
+ return refs_head_ref(get_main_ref_store(the_repository), fn, cb_data);
+}
+
+static int for_each_ref(each_ref_fn fn, void *cb_data)
+{
+ return refs_for_each_ref(get_main_ref_store(the_repository), fn, cb_data);
+}
+
+static int for_each_ref_in(const char *prefix, each_ref_fn fn, void *cb_data)
+{
+ return refs_for_each_ref_in(get_main_ref_store(the_repository), prefix, fn, cb_data);
+}
+
+static int for_each_fullref_in(const char *prefix,
+ const char **exclude_patterns,
+ each_ref_fn fn, void *cb_data)
+{
+ return refs_for_each_fullref_in(get_main_ref_store(the_repository),
+ prefix, exclude_patterns, fn, cb_data);
+}
+
+static int for_each_namespaced_ref(const char **exclude_patterns,
+ each_ref_fn fn, void *cb_data)
+{
+ return refs_for_each_namespaced_ref(get_main_ref_store(the_repository),
+ exclude_patterns, fn, cb_data);
+}
+
+static int for_each_rawref(each_ref_fn fn, void *cb_data)
+{
+ return refs_for_each_rawref(get_main_ref_store(the_repository), fn, cb_data);
+}
+
+static const char *resolve_ref_unsafe(const char *refname, int resolve_flags,
+ struct object_id *oid, int *flags)
+{
+ return refs_resolve_ref_unsafe(get_main_ref_store(the_repository), refname,
+ resolve_flags, oid, flags);
+}
+
+static int create_symref(const char *ref_target, const char *refs_heads_master,
+ const char *logmsg)
+{
+ return refs_create_symref(get_main_ref_store(the_repository), ref_target,
+ refs_heads_master, logmsg);
+}
+
+static int for_each_reflog(each_reflog_fn fn, void *cb_data)
+{
+ return refs_for_each_reflog(get_main_ref_store(the_repository), fn, cb_data);
+}
+
+static int for_each_reflog_ent_reverse(const char *refname, each_reflog_ent_fn fn,
+ void *cb_data)
+{
+ return refs_for_each_reflog_ent_reverse(get_main_ref_store(the_repository),
+ refname, fn, cb_data);
+}
+
+static int for_each_reflog_ent(const char *refname, each_reflog_ent_fn fn,
+ void *cb_data)
+{
+ return refs_for_each_reflog_ent(get_main_ref_store(the_repository), refname,
+ fn, cb_data);
+}
+
+static int reflog_exists(const char *refname)
+{
+ return refs_reflog_exists(get_main_ref_store(the_repository), refname);
+}
+
+static int safe_create_reflog(const char *refname, struct strbuf *err)
+{
+ return refs_create_reflog(get_main_ref_store(the_repository), refname,
+ err);
+}
+
+static int delete_reflog(const char *refname)
+{
+ return refs_delete_reflog(get_main_ref_store(the_repository), refname);
+}
+
+static int reflog_expire(const char *refname,
+ unsigned int flags,
+ reflog_expiry_prepare_fn prepare_fn,
+ reflog_expiry_should_prune_fn should_prune_fn,
+ reflog_expiry_cleanup_fn cleanup_fn,
+ void *policy_cb_data)
+{
+ return refs_reflog_expire(get_main_ref_store(the_repository),
+ refname, flags,
+ prepare_fn, should_prune_fn,
+ cleanup_fn, policy_cb_data);
+}
+
+static int delete_refs(const char *msg, struct string_list *refnames,
+ unsigned int flags)
+{
+ return refs_delete_refs(get_main_ref_store(the_repository), msg, refnames, flags);
+}
+
+static int rename_ref(const char *oldref, const char *newref, const char *logmsg)
+{
+ return refs_rename_ref(get_main_ref_store(the_repository), oldref, newref, logmsg);
+}
+
+static int copy_existing_ref(const char *oldref, const char *newref, const char *logmsg)
+{
+ return refs_copy_existing_ref(get_main_ref_store(the_repository), oldref, newref, logmsg);
+}
+#endif
+
#endif /* REFS_H */
diff --git a/refs/debug.c b/refs/debug.c
index c7531b17f0..547d9245b9 100644
--- a/refs/debug.c
+++ b/refs/debug.c
@@ -33,11 +33,18 @@ struct ref_store *maybe_debug_wrap_ref_store(const char *gitdir, struct ref_stor
return (struct ref_store *)res;
}
-static int debug_init_db(struct ref_store *refs, int flags, struct strbuf *err)
+static void debug_release(struct ref_store *refs)
{
struct debug_ref_store *drefs = (struct debug_ref_store *)refs;
- int res = drefs->refs->be->init_db(drefs->refs, flags, err);
- trace_printf_key(&trace_refs, "init_db: %d\n", res);
+ drefs->refs->be->release(drefs->refs);
+ trace_printf_key(&trace_refs, "release\n");
+}
+
+static int debug_create_on_disk(struct ref_store *refs, int flags, struct strbuf *err)
+{
+ struct debug_ref_store *drefs = (struct debug_ref_store *)refs;
+ int res = drefs->refs->be->create_on_disk(drefs->refs, flags, err);
+ trace_printf_key(&trace_refs, "create_on_disk: %d\n", res);
return res;
}
@@ -131,18 +138,6 @@ static int debug_pack_refs(struct ref_store *ref_store, struct pack_refs_opts *o
return res;
}
-static int debug_create_symref(struct ref_store *ref_store,
- const char *ref_name, const char *target,
- const char *logmsg)
-{
- struct debug_ref_store *drefs = (struct debug_ref_store *)ref_store;
- int res = drefs->refs->be->create_symref(drefs->refs, ref_name, target,
- logmsg);
- trace_printf_key(&trace_refs, "create_symref: %s -> %s \"%s\": %d\n", ref_name,
- target, logmsg, res);
- return res;
-}
-
static int debug_rename_ref(struct ref_store *ref_store, const char *oldref,
const char *newref, const char *logmsg)
{
@@ -427,7 +422,8 @@ static int debug_reflog_expire(struct ref_store *ref_store, const char *refname,
struct ref_storage_be refs_be_debug = {
.name = "debug",
.init = NULL,
- .init_db = debug_init_db,
+ .release = debug_release,
+ .create_on_disk = debug_create_on_disk,
/*
* None of these should be NULL. If the "files" backend (in
@@ -441,7 +437,6 @@ struct ref_storage_be refs_be_debug = {
.initial_transaction_commit = debug_initial_transaction_commit,
.pack_refs = debug_pack_refs,
- .create_symref = debug_create_symref,
.rename_ref = debug_rename_ref,
.copy_ref = debug_copy_ref,
diff --git a/refs/files-backend.c b/refs/files-backend.c
index 6f9a631592..aa52d9be7c 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "../git-compat-util.h"
#include "../copy.h"
#include "../environment.h"
@@ -89,9 +91,9 @@ static void clear_loose_ref_cache(struct files_ref_store *refs)
* Create a new submodule ref cache and add it to the internal
* set of caches.
*/
-static struct ref_store *files_ref_store_create(struct repository *repo,
- const char *gitdir,
- unsigned int flags)
+static struct ref_store *files_ref_store_init(struct repository *repo,
+ const char *gitdir,
+ unsigned int flags)
{
struct files_ref_store *refs = xcalloc(1, sizeof(*refs));
struct ref_store *ref_store = (struct ref_store *)refs;
@@ -102,7 +104,7 @@ static struct ref_store *files_ref_store_create(struct repository *repo,
get_common_dir_noenv(&sb, gitdir);
refs->gitcommondir = strbuf_detach(&sb, NULL);
refs->packed_ref_store =
- packed_ref_store_create(repo, refs->gitcommondir, flags);
+ packed_ref_store_init(repo, refs->gitcommondir, flags);
chdir_notify_reparent("files-backend $GIT_DIR", &refs->base.gitdir);
chdir_notify_reparent("files-backend $GIT_COMMONDIR",
@@ -149,6 +151,14 @@ static struct files_ref_store *files_downcast(struct ref_store *ref_store,
return refs;
}
+static void files_ref_store_release(struct ref_store *ref_store)
+{
+ struct files_ref_store *refs = files_downcast(ref_store, 0, "release");
+ free_ref_cache(refs->loose);
+ free(refs->gitcommondir);
+ ref_store_release(refs->packed_ref_store);
+}
+
static void files_reflog_path(struct files_ref_store *refs,
struct strbuf *sb,
const char *refname)
@@ -238,7 +248,7 @@ static void loose_fill_ref_dir_regular_file(struct files_ref_store *refs,
if (!refs_resolve_ref_unsafe(&refs->base, refname, RESOLVE_REF_READING,
&oid, &flag)) {
- oidclr(&oid);
+ oidclr(&oid, the_repository->hash_algo);
flag |= REF_ISBROKEN;
} else if (is_null_oid(&oid)) {
/*
@@ -255,7 +265,7 @@ static void loose_fill_ref_dir_regular_file(struct files_ref_store *refs,
if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) {
if (!refname_is_safe(refname))
die("loose refname is dangerous: %s", refname);
- oidclr(&oid);
+ oidclr(&oid, the_repository->hash_algo);
flag |= REF_BAD_NAME | REF_ISBROKEN;
}
add_entry_to_dir(dir, create_ref_entry(refname, &oid, flag));
@@ -315,19 +325,15 @@ static void loose_fill_ref_dir(struct ref_store *ref_store,
add_per_worktree_entries_to_dir(dir, dirname);
}
-/*
- * Add pseudorefs to the ref dir by parsing the directory for any files
- * which follow the pseudoref syntax.
- */
-static void add_pseudoref_and_head_entries(struct ref_store *ref_store,
- struct ref_dir *dir,
- const char *dirname)
+static int for_each_root_ref(struct files_ref_store *refs,
+ int (*cb)(const char *refname, void *cb_data),
+ void *cb_data)
{
- struct files_ref_store *refs =
- files_downcast(ref_store, REF_STORE_READ, "fill_ref_dir");
struct strbuf path = STRBUF_INIT, refname = STRBUF_INIT;
+ const char *dirname = refs->loose->root->name;
struct dirent *de;
size_t dirnamelen;
+ int ret;
DIR *d;
files_ref_path(refs, &path, dirname);
@@ -335,7 +341,7 @@ static void add_pseudoref_and_head_entries(struct ref_store *ref_store,
d = opendir(path.buf);
if (!d) {
strbuf_release(&path);
- return;
+ return -1;
}
strbuf_addstr(&refname, dirname);
@@ -351,14 +357,49 @@ static void add_pseudoref_and_head_entries(struct ref_store *ref_store,
strbuf_addstr(&refname, de->d_name);
dtype = get_dtype(de, &path, 1);
- if (dtype == DT_REG && is_root_ref(de->d_name))
- loose_fill_ref_dir_regular_file(refs, refname.buf, dir);
+ if (dtype == DT_REG && is_root_ref(de->d_name)) {
+ ret = cb(refname.buf, cb_data);
+ if (ret)
+ goto done;
+ }
strbuf_setlen(&refname, dirnamelen);
}
+
+ ret = 0;
+
+done:
strbuf_release(&refname);
strbuf_release(&path);
closedir(d);
+ return ret;
+}
+
+struct fill_root_ref_data {
+ struct files_ref_store *refs;
+ struct ref_dir *dir;
+};
+
+static int fill_root_ref(const char *refname, void *cb_data)
+{
+ struct fill_root_ref_data *data = cb_data;
+ loose_fill_ref_dir_regular_file(data->refs, refname, data->dir);
+ return 0;
+}
+
+/*
+ * Add root refs to the ref dir by parsing the directory for any files which
+ * follow the root ref syntax.
+ */
+static void add_root_refs(struct files_ref_store *refs,
+ struct ref_dir *dir)
+{
+ struct fill_root_ref_data data = {
+ .refs = refs,
+ .dir = dir,
+ };
+
+ for_each_root_ref(refs, fill_root_ref, &data);
}
static struct ref_cache *get_loose_ref_cache(struct files_ref_store *refs,
@@ -380,8 +421,7 @@ static struct ref_cache *get_loose_ref_cache(struct files_ref_store *refs,
dir = get_ref_dir(refs->loose->root);
if (flags & DO_FOR_EACH_INCLUDE_ROOT_REFS)
- add_pseudoref_and_head_entries(dir->cache->ref_store, dir,
- refs->loose->root->name);
+ add_root_refs(refs, dir);
/*
* Add an incomplete entry for "refs/" (to be filled
@@ -793,8 +833,10 @@ retry:
*/
if (refs_verify_refname_available(
refs->packed_ref_store, refname,
- extras, NULL, err))
+ extras, NULL, err)) {
+ ret = TRANSACTION_NAME_CONFLICT;
goto error_return;
+ }
}
ret = 0;
@@ -1110,7 +1152,7 @@ static struct ref_lock *lock_ref_oid_basic(struct files_ref_store *refs,
if (!refs_resolve_ref_unsafe(&refs->base, lock->ref_name, 0,
&lock->old_oid, NULL))
- oidclr(&lock->old_oid);
+ oidclr(&lock->old_oid, the_repository->hash_algo);
goto out;
error_return:
@@ -1197,7 +1239,7 @@ static void prune_ref(struct files_ref_store *refs, struct ref_to_prune *r)
ref_transaction_add_update(
transaction, r->name,
REF_NO_DEREF | REF_HAVE_NEW | REF_HAVE_OLD | REF_IS_PRUNING,
- null_oid(), &r->oid, NULL);
+ null_oid(), &r->oid, NULL, NULL, NULL);
if (ref_transaction_commit(transaction, &err))
goto cleanup;
@@ -1228,7 +1270,8 @@ static void prune_refs(struct files_ref_store *refs, struct ref_to_prune **refs_
/*
* Return true if the specified reference should be packed.
*/
-static int should_pack_ref(const char *refname,
+static int should_pack_ref(struct files_ref_store *refs,
+ const char *refname,
const struct object_id *oid, unsigned int ref_flags,
struct pack_refs_opts *opts)
{
@@ -1244,7 +1287,7 @@ static int should_pack_ref(const char *refname,
return 0;
/* Do not pack broken refs: */
- if (!ref_resolves_to_object(refname, the_repository, oid, ref_flags))
+ if (!ref_resolves_to_object(refname, refs->base.repo, oid, ref_flags))
return 0;
if (ref_excluded(opts->exclusions, refname))
@@ -1276,14 +1319,14 @@ static int files_pack_refs(struct ref_store *ref_store,
packed_refs_lock(refs->packed_ref_store, LOCK_DIE_ON_ERROR, &err);
iter = cache_ref_iterator_begin(get_loose_ref_cache(refs, 0), NULL,
- the_repository, 0);
+ refs->base.repo, 0);
while ((ok = ref_iterator_advance(iter)) == ITER_OK) {
/*
* If the loose reference can be packed, add an entry
* 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, opts))
+ if (!should_pack_ref(refs, iter->refname, iter->oid, iter->flags, opts))
continue;
/*
@@ -1291,7 +1334,7 @@ static int files_pack_refs(struct ref_store *ref_store,
* packed-refs transaction:
*/
if (ref_transaction_update(transaction, iter->refname,
- iter->oid, NULL,
+ iter->oid, NULL, NULL, NULL,
REF_NO_DEREF, NULL, &err))
die("failure preparing to create packed reference %s: %s",
iter->refname, err.buf);
@@ -1380,7 +1423,8 @@ static int rename_tmp_log(struct files_ref_store *refs, const char *newrefname)
return ret;
}
-static int write_ref_to_lockfile(struct ref_lock *lock,
+static int write_ref_to_lockfile(struct files_ref_store *refs,
+ struct ref_lock *lock,
const struct object_id *oid,
int skip_oid_verification, struct strbuf *err);
static int commit_ref_update(struct files_ref_store *refs,
@@ -1528,7 +1572,7 @@ static int files_copy_or_rename_ref(struct ref_store *ref_store,
}
oidcpy(&lock->old_oid, &orig_oid);
- if (write_ref_to_lockfile(lock, &orig_oid, 0, &err) ||
+ if (write_ref_to_lockfile(refs, lock, &orig_oid, 0, &err) ||
commit_ref_update(refs, lock, &orig_oid, logmsg, &err)) {
error("unable to write current sha1 into %s: %s", newrefname, err.buf);
strbuf_release(&err);
@@ -1548,7 +1592,7 @@ static int files_copy_or_rename_ref(struct ref_store *ref_store,
flag = log_all_ref_updates;
log_all_ref_updates = LOG_REFS_NONE;
- if (write_ref_to_lockfile(lock, &orig_oid, 0, &err) ||
+ if (write_ref_to_lockfile(refs, lock, &orig_oid, 0, &err) ||
commit_ref_update(refs, lock, &orig_oid, NULL, &err)) {
error("unable to write current sha1 into %s: %s", oldrefname, err.buf);
strbuf_release(&err);
@@ -1740,6 +1784,9 @@ static int files_log_ref_write(struct files_ref_store *refs,
{
int logfd, result;
+ if (flags & REF_SKIP_CREATE_REFLOG)
+ return 0;
+
if (log_all_ref_updates == LOG_REFS_UNSET)
log_all_ref_updates = is_bare_repository() ? LOG_REFS_NONE : LOG_REFS_NORMAL;
@@ -1782,7 +1829,8 @@ static int files_log_ref_write(struct files_ref_store *refs,
* Write oid into the open lockfile, then close the lockfile. On
* errors, rollback the lockfile, fill in *err and return -1.
*/
-static int write_ref_to_lockfile(struct ref_lock *lock,
+static int write_ref_to_lockfile(struct files_ref_store *refs,
+ struct ref_lock *lock,
const struct object_id *oid,
int skip_oid_verification, struct strbuf *err)
{
@@ -1791,7 +1839,7 @@ static int write_ref_to_lockfile(struct ref_lock *lock,
int fd;
if (!skip_oid_verification) {
- o = parse_object(the_repository, oid);
+ o = parse_object(refs->base.repo, oid);
if (!o) {
strbuf_addf(
err,
@@ -1810,7 +1858,7 @@ static int write_ref_to_lockfile(struct ref_lock *lock,
}
}
fd = get_lock_file_fd(&lock->lk);
- if (write_in_full(fd, oid_to_hex(oid), the_hash_algo->hexsz) < 0 ||
+ if (write_in_full(fd, oid_to_hex(oid), refs->base.repo->hash_algo->hexsz) < 0 ||
write_in_full(fd, &term, 1) < 0 ||
fsync_component(FSYNC_COMPONENT_REFERENCE, get_lock_file_fd(&lock->lk)) < 0 ||
close_ref_gently(lock) < 0) {
@@ -1902,66 +1950,23 @@ static int create_ref_symlink(struct ref_lock *lock, const char *target)
return ret;
}
-static void update_symref_reflog(struct files_ref_store *refs,
- struct ref_lock *lock, const char *refname,
- const char *target, const char *logmsg)
-{
- struct strbuf err = STRBUF_INIT;
- struct object_id new_oid;
-
- if (logmsg &&
- refs_resolve_ref_unsafe(&refs->base, target,
- RESOLVE_REF_READING, &new_oid, NULL) &&
- files_log_ref_write(refs, refname, &lock->old_oid,
- &new_oid, logmsg, 0, &err)) {
- error("%s", err.buf);
- strbuf_release(&err);
- }
-}
-
-static int create_symref_locked(struct files_ref_store *refs,
- struct ref_lock *lock, const char *refname,
- const char *target, const char *logmsg)
+static int create_symref_lock(struct files_ref_store *refs,
+ struct ref_lock *lock, const char *refname,
+ const char *target, struct strbuf *err)
{
- if (prefer_symlink_refs && !create_ref_symlink(lock, target)) {
- update_symref_reflog(refs, lock, refname, target, logmsg);
- return 0;
+ if (!fdopen_lock_file(&lock->lk, "w")) {
+ strbuf_addf(err, "unable to fdopen %s: %s",
+ get_lock_file_path(&lock->lk), strerror(errno));
+ return -1;
}
- if (!fdopen_lock_file(&lock->lk, "w"))
- return error("unable to fdopen %s: %s",
+ if (fprintf(get_lock_file_fp(&lock->lk), "ref: %s\n", target) < 0) {
+ strbuf_addf(err, "unable to write to %s: %s",
get_lock_file_path(&lock->lk), strerror(errno));
-
- update_symref_reflog(refs, lock, refname, target, logmsg);
-
- /* no error check; commit_ref will check ferror */
- fprintf(get_lock_file_fp(&lock->lk), "ref: %s\n", target);
- if (commit_ref(lock) < 0)
- return error("unable to write symref for %s: %s", refname,
- strerror(errno));
- return 0;
-}
-
-static int files_create_symref(struct ref_store *ref_store,
- const char *refname, const char *target,
- const char *logmsg)
-{
- struct files_ref_store *refs =
- files_downcast(ref_store, REF_STORE_WRITE, "create_symref");
- struct strbuf err = STRBUF_INIT;
- struct ref_lock *lock;
- int ret;
-
- lock = lock_ref_oid_basic(refs, refname, &err);
- if (!lock) {
- error("%s", err.buf);
- strbuf_release(&err);
return -1;
}
- ret = create_symref_locked(refs, lock, refname, target, logmsg);
- unlock_ref(lock);
- return ret;
+ return 0;
}
static int files_reflog_exists(struct ref_store *ref_store,
@@ -2283,6 +2288,7 @@ static int split_head_update(struct ref_update *update,
struct ref_update *new_update;
if ((update->flags & REF_LOG_ONLY) ||
+ (update->flags & REF_SKIP_CREATE_REFLOG) ||
(update->flags & REF_IS_PRUNING) ||
(update->flags & REF_UPDATE_VIA_HEAD))
return 0;
@@ -2308,7 +2314,7 @@ static int split_head_update(struct ref_update *update,
transaction, "HEAD",
update->flags | REF_LOG_ONLY | REF_NO_DEREF,
&update->new_oid, &update->old_oid,
- update->msg);
+ NULL, NULL, update->msg);
/*
* Add "HEAD". This insertion is O(N) in the transaction
@@ -2370,8 +2376,9 @@ static int split_symref_update(struct ref_update *update,
new_update = ref_transaction_add_update(
transaction, referent, new_flags,
- &update->new_oid, &update->old_oid,
- update->msg);
+ update->new_target ? NULL : &update->new_oid,
+ update->old_target ? NULL : &update->old_oid,
+ update->new_target, update->old_target, update->msg);
new_update->parent_update = update;
@@ -2400,17 +2407,6 @@ static int split_symref_update(struct ref_update *update,
}
/*
- * Return the refname under which update was originally requested.
- */
-static const char *original_update_refname(struct ref_update *update)
-{
- while (update->parent_update)
- update = update->parent_update;
-
- return update->refname;
-}
-
-/*
* Check whether the REF_HAVE_OLD and old_oid values stored in update
* are consistent with oid, which is the reference's current value. If
* everything is OK, return 0; otherwise, write an error message to
@@ -2426,16 +2422,16 @@ static int check_old_oid(struct ref_update *update, struct object_id *oid,
if (is_null_oid(&update->old_oid))
strbuf_addf(err, "cannot lock ref '%s': "
"reference already exists",
- original_update_refname(update));
+ ref_update_original_update_refname(update));
else if (is_null_oid(oid))
strbuf_addf(err, "cannot lock ref '%s': "
"reference is missing but expected %s",
- original_update_refname(update),
+ ref_update_original_update_refname(update),
oid_to_hex(&update->old_oid));
else
strbuf_addf(err, "cannot lock ref '%s': "
"is at %s but expected %s",
- original_update_refname(update),
+ ref_update_original_update_refname(update),
oid_to_hex(oid),
oid_to_hex(&update->old_oid));
@@ -2463,14 +2459,13 @@ static int lock_ref_for_update(struct files_ref_store *refs,
struct strbuf *err)
{
struct strbuf referent = STRBUF_INIT;
- int mustexist = (update->flags & REF_HAVE_OLD) &&
- !is_null_oid(&update->old_oid);
+ int mustexist = ref_update_expects_existing_old_ref(update);
int ret = 0;
struct ref_lock *lock;
files_assert_main_repository(refs, "lock_ref_for_update");
- if ((update->flags & REF_HAVE_NEW) && is_null_oid(&update->new_oid))
+ if ((update->flags & REF_HAVE_NEW) && ref_update_has_null_new_value(update))
update->flags |= REF_DELETING;
if (head_ref) {
@@ -2489,7 +2484,7 @@ static int lock_ref_for_update(struct files_ref_store *refs,
reason = strbuf_detach(err, NULL);
strbuf_addf(err, "cannot lock ref '%s': %s",
- original_update_refname(update), reason);
+ ref_update_original_update_refname(update), reason);
free(reason);
goto out;
}
@@ -2509,11 +2504,18 @@ static int lock_ref_for_update(struct files_ref_store *refs,
if (update->flags & REF_HAVE_OLD) {
strbuf_addf(err, "cannot lock ref '%s': "
"error reading reference",
- original_update_refname(update));
+ ref_update_original_update_refname(update));
+ ret = TRANSACTION_GENERIC_ERROR;
+ goto out;
+ }
+ }
+
+ if (update->old_target) {
+ if (ref_update_check_old_target(referent.buf, update, err)) {
ret = TRANSACTION_GENERIC_ERROR;
goto out;
}
- } else if (check_old_oid(update, &lock->old_oid, err)) {
+ } else if (check_old_oid(update, &lock->old_oid, err)) {
ret = TRANSACTION_GENERIC_ERROR;
goto out;
}
@@ -2534,7 +2536,19 @@ static int lock_ref_for_update(struct files_ref_store *refs,
} else {
struct ref_update *parent_update;
- if (check_old_oid(update, &lock->old_oid, err)) {
+ /*
+ * Even if the ref is a regular ref, if `old_target` is set, we
+ * fail with an error.
+ */
+ if (update->old_target) {
+ strbuf_addf(err, _("cannot lock ref '%s': "
+ "expected symref with target '%s': "
+ "but is a regular ref"),
+ ref_update_original_update_refname(update),
+ update->old_target);
+ ret = TRANSACTION_GENERIC_ERROR;
+ goto out;
+ } else if (check_old_oid(update, &lock->old_oid, err)) {
ret = TRANSACTION_GENERIC_ERROR;
goto out;
}
@@ -2552,9 +2566,28 @@ static int lock_ref_for_update(struct files_ref_store *refs,
}
}
- if ((update->flags & REF_HAVE_NEW) &&
- !(update->flags & REF_DELETING) &&
- !(update->flags & REF_LOG_ONLY)) {
+ if (update->new_target && !(update->flags & REF_LOG_ONLY)) {
+ if (create_symref_lock(refs, lock, update->refname,
+ update->new_target, err)) {
+ ret = TRANSACTION_GENERIC_ERROR;
+ goto out;
+ }
+
+ if (close_ref_gently(lock)) {
+ strbuf_addf(err, "couldn't close '%s.lock'",
+ update->refname);
+ ret = TRANSACTION_GENERIC_ERROR;
+ goto out;
+ }
+
+ /*
+ * Once we have created the symref lock, the commit
+ * phase of the transaction only needs to commit the lock.
+ */
+ update->flags |= REF_NEEDS_COMMIT;
+ } else if ((update->flags & REF_HAVE_NEW) &&
+ !(update->flags & REF_DELETING) &&
+ !(update->flags & REF_LOG_ONLY)) {
if (!(update->type & REF_ISSYMREF) &&
oideq(&lock->old_oid, &update->new_oid)) {
/*
@@ -2562,7 +2595,7 @@ static int lock_ref_for_update(struct files_ref_store *refs,
* value, so we don't need to write it.
*/
} else if (write_ref_to_lockfile(
- lock, &update->new_oid,
+ refs, lock, &update->new_oid,
update->flags & REF_SKIP_OID_VERIFICATION,
err)) {
char *write_err = strbuf_detach(err, NULL);
@@ -2762,7 +2795,7 @@ static int files_transaction_prepare(struct ref_store *ref_store,
packed_transaction, update->refname,
REF_HAVE_NEW | REF_NO_DEREF,
&update->new_oid, NULL,
- NULL);
+ NULL, NULL, NULL);
}
}
@@ -2817,6 +2850,43 @@ cleanup:
return ret;
}
+static int parse_and_write_reflog(struct files_ref_store *refs,
+ struct ref_update *update,
+ struct ref_lock *lock,
+ struct strbuf *err)
+{
+ if (update->new_target) {
+ /*
+ * We want to get the resolved OID for the target, to ensure
+ * that the correct value is added to the reflog.
+ */
+ if (!refs_resolve_ref_unsafe(&refs->base, update->new_target,
+ RESOLVE_REF_READING,
+ &update->new_oid, NULL)) {
+ /*
+ * TODO: currently we skip creating reflogs for dangling
+ * symref updates. It would be nice to capture this as
+ * zero oid updates however.
+ */
+ return 0;
+ }
+ }
+
+ if (files_log_ref_write(refs, lock->ref_name, &lock->old_oid,
+ &update->new_oid, update->msg, update->flags, err)) {
+ char *old_msg = strbuf_detach(err, NULL);
+
+ strbuf_addf(err, "cannot update the ref '%s': %s",
+ lock->ref_name, old_msg);
+ free(old_msg);
+ unlock_ref(lock);
+ update->backend_data = NULL;
+ return -1;
+ }
+
+ return 0;
+}
+
static int files_transaction_finish(struct ref_store *ref_store,
struct ref_transaction *transaction,
struct strbuf *err)
@@ -2847,23 +2917,20 @@ static int files_transaction_finish(struct ref_store *ref_store,
if (update->flags & REF_NEEDS_COMMIT ||
update->flags & REF_LOG_ONLY) {
- if (files_log_ref_write(refs,
- lock->ref_name,
- &lock->old_oid,
- &update->new_oid,
- update->msg, update->flags,
- err)) {
- char *old_msg = strbuf_detach(err, NULL);
-
- strbuf_addf(err, "cannot update the ref '%s': %s",
- lock->ref_name, old_msg);
- free(old_msg);
- unlock_ref(lock);
- update->backend_data = NULL;
+ if (parse_and_write_reflog(refs, update, lock, err)) {
ret = TRANSACTION_GENERIC_ERROR;
goto cleanup;
}
}
+
+ /*
+ * We try creating a symlink, if that succeeds we continue to the
+ * next update. If not, we try and create a regular symref.
+ */
+ if (update->new_target && prefer_symlink_refs)
+ if (!create_ref_symlink(lock, update->new_target))
+ continue;
+
if (update->flags & REF_NEEDS_COMMIT) {
clear_loose_ref_cache(refs);
if (commit_ref(lock)) {
@@ -3047,7 +3114,7 @@ static int files_initial_transaction_commit(struct ref_store *ref_store,
ref_transaction_add_update(packed_transaction, update->refname,
update->flags & ~REF_HAVE_OLD,
&update->new_oid, &update->old_oid,
- NULL);
+ NULL, NULL, NULL);
}
if (packed_refs_lock(refs->packed_ref_store, 0, err)) {
@@ -3211,7 +3278,7 @@ static int files_reflog_expire(struct ref_store *ref_store,
rollback_lock_file(&reflog_lock);
} else if (update &&
(write_in_full(get_lock_file_fd(&lock->lk),
- oid_to_hex(&cb.last_kept_oid), the_hash_algo->hexsz) < 0 ||
+ oid_to_hex(&cb.last_kept_oid), refs->base.repo->hash_algo->hexsz) < 0 ||
write_str_in_full(get_lock_file_fd(&lock->lk), "\n") < 0 ||
close_ref_gently(lock) < 0)) {
status |= error("couldn't write %s",
@@ -3235,12 +3302,12 @@ static int files_reflog_expire(struct ref_store *ref_store,
return -1;
}
-static int files_init_db(struct ref_store *ref_store,
- int flags,
- struct strbuf *err UNUSED)
+static int files_ref_store_create_on_disk(struct ref_store *ref_store,
+ int flags,
+ struct strbuf *err UNUSED)
{
struct files_ref_store *refs =
- files_downcast(ref_store, REF_STORE_WRITE, "init_db");
+ files_downcast(ref_store, REF_STORE_WRITE, "create");
struct strbuf sb = STRBUF_INIT;
/*
@@ -3263,7 +3330,7 @@ static int files_init_db(struct ref_store *ref_store,
* There is no need to create directories for common refs when creating
* a worktree ref store.
*/
- if (!(flags & REFS_INIT_DB_IS_WORKTREE)) {
+ if (!(flags & REF_STORE_CREATE_ON_DISK_IS_WORKTREE)) {
/*
* Create .git/refs/{heads,tags}
*/
@@ -3280,17 +3347,80 @@ static int files_init_db(struct ref_store *ref_store,
return 0;
}
+struct remove_one_root_ref_data {
+ const char *gitdir;
+ struct strbuf *err;
+};
+
+static int remove_one_root_ref(const char *refname,
+ void *cb_data)
+{
+ struct remove_one_root_ref_data *data = cb_data;
+ struct strbuf buf = STRBUF_INIT;
+ int ret = 0;
+
+ strbuf_addf(&buf, "%s/%s", data->gitdir, refname);
+
+ ret = unlink(buf.buf);
+ if (ret < 0)
+ strbuf_addf(data->err, "could not delete %s: %s\n",
+ refname, strerror(errno));
+
+ strbuf_release(&buf);
+ return ret;
+}
+
+static int files_ref_store_remove_on_disk(struct ref_store *ref_store,
+ struct strbuf *err)
+{
+ struct files_ref_store *refs =
+ files_downcast(ref_store, REF_STORE_WRITE, "remove");
+ struct remove_one_root_ref_data data = {
+ .gitdir = refs->base.gitdir,
+ .err = err,
+ };
+ struct strbuf sb = STRBUF_INIT;
+ int ret = 0;
+
+ strbuf_addf(&sb, "%s/refs", refs->base.gitdir);
+ if (remove_dir_recursively(&sb, 0) < 0) {
+ strbuf_addf(err, "could not delete refs: %s",
+ strerror(errno));
+ ret = -1;
+ }
+ strbuf_reset(&sb);
+
+ strbuf_addf(&sb, "%s/logs", refs->base.gitdir);
+ if (remove_dir_recursively(&sb, 0) < 0) {
+ strbuf_addf(err, "could not delete logs: %s",
+ strerror(errno));
+ ret = -1;
+ }
+ strbuf_reset(&sb);
+
+ if (for_each_root_ref(refs, remove_one_root_ref, &data) < 0)
+ ret = -1;
+
+ if (ref_store_remove_on_disk(refs->packed_ref_store, err) < 0)
+ ret = -1;
+
+ strbuf_release(&sb);
+ return ret;
+}
+
struct ref_storage_be refs_be_files = {
.name = "files",
- .init = files_ref_store_create,
- .init_db = files_init_db,
+ .init = files_ref_store_init,
+ .release = files_ref_store_release,
+ .create_on_disk = files_ref_store_create_on_disk,
+ .remove_on_disk = files_ref_store_remove_on_disk,
+
.transaction_prepare = files_transaction_prepare,
.transaction_finish = files_transaction_finish,
.transaction_abort = files_transaction_abort,
.initial_transaction_commit = files_initial_transaction_commit,
.pack_refs = files_pack_refs,
- .create_symref = files_create_symref,
.rename_ref = files_rename_ref,
.copy_ref = files_copy_ref,
diff --git a/refs/iterator.c b/refs/iterator.c
index 9db8b056d5..d355ebf0d5 100644
--- a/refs/iterator.c
+++ b/refs/iterator.c
@@ -440,15 +440,15 @@ struct ref_iterator *prefix_ref_iterator_begin(struct ref_iterator *iter0,
struct ref_iterator *current_ref_iter = NULL;
-int do_for_each_repo_ref_iterator(struct repository *r, struct ref_iterator *iter,
- each_repo_ref_fn fn, void *cb_data)
+int do_for_each_ref_iterator(struct ref_iterator *iter,
+ each_ref_fn fn, void *cb_data)
{
int retval = 0, ok;
struct ref_iterator *old_ref_iter = current_ref_iter;
current_ref_iter = iter;
while ((ok = ref_iterator_advance(iter)) == ITER_OK) {
- retval = fn(r, iter->refname, iter->oid, iter->flags, cb_data);
+ retval = fn(iter->refname, iter->oid, iter->flags, cb_data);
if (retval) {
/*
* If ref_iterator_abort() returns ITER_ERROR,
diff --git a/refs/packed-backend.c b/refs/packed-backend.c
index 4e826c05ff..a0666407cd 100644
--- a/refs/packed-backend.c
+++ b/refs/packed-backend.c
@@ -1,5 +1,8 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "../git-compat-util.h"
#include "../config.h"
+#include "../dir.h"
#include "../gettext.h"
#include "../hash.h"
#include "../hex.h"
@@ -200,9 +203,14 @@ static int release_snapshot(struct snapshot *snapshot)
}
}
-struct ref_store *packed_ref_store_create(struct repository *repo,
- const char *gitdir,
- unsigned int store_flags)
+static size_t snapshot_hexsz(const struct snapshot *snapshot)
+{
+ return snapshot->refs->base.repo->hash_algo->hexsz;
+}
+
+struct ref_store *packed_ref_store_init(struct repository *repo,
+ const char *gitdir,
+ unsigned int store_flags)
{
struct packed_ref_store *refs = xcalloc(1, sizeof(*refs));
struct ref_store *ref_store = (struct ref_store *)refs;
@@ -252,6 +260,15 @@ static void clear_snapshot(struct packed_ref_store *refs)
}
}
+static void packed_ref_store_release(struct ref_store *ref_store)
+{
+ struct packed_ref_store *refs = packed_downcast(ref_store, 0, "release");
+ clear_snapshot(refs);
+ rollback_lock_file(&refs->lock);
+ delete_tempfile(&refs->tempfile);
+ free(refs->path);
+}
+
static NORETURN void die_unterminated_line(const char *path,
const char *p, size_t len)
{
@@ -280,11 +297,13 @@ struct snapshot_record {
size_t len;
};
-static int cmp_packed_ref_records(const void *v1, const void *v2)
+static int cmp_packed_ref_records(const void *v1, const void *v2,
+ void *cb_data)
{
+ const struct snapshot *snapshot = cb_data;
const struct snapshot_record *e1 = v1, *e2 = v2;
- const char *r1 = e1->start + the_hash_algo->hexsz + 1;
- const char *r2 = e2->start + the_hash_algo->hexsz + 1;
+ const char *r1 = e1->start + snapshot_hexsz(snapshot) + 1;
+ const char *r2 = e2->start + snapshot_hexsz(snapshot) + 1;
while (1) {
if (*r1 == '\n')
@@ -305,9 +324,9 @@ static int cmp_packed_ref_records(const void *v1, const void *v2)
* refname.
*/
static int cmp_record_to_refname(const char *rec, const char *refname,
- int start)
+ int start, const struct snapshot *snapshot)
{
- const char *r1 = rec + the_hash_algo->hexsz + 1;
+ const char *r1 = rec + snapshot_hexsz(snapshot) + 1;
const char *r2 = refname;
while (1) {
@@ -354,7 +373,7 @@ static void sort_snapshot(struct snapshot *snapshot)
if (!eol)
/* The safety check should prevent this. */
BUG("unterminated line found in packed-refs");
- if (eol - pos < the_hash_algo->hexsz + 2)
+ if (eol - pos < snapshot_hexsz(snapshot) + 2)
die_invalid_line(snapshot->refs->path,
pos, eof - pos);
eol++;
@@ -380,7 +399,7 @@ static void sort_snapshot(struct snapshot *snapshot)
if (sorted &&
nr > 1 &&
cmp_packed_ref_records(&records[nr - 2],
- &records[nr - 1]) >= 0)
+ &records[nr - 1], snapshot) >= 0)
sorted = 0;
pos = eol;
@@ -390,7 +409,7 @@ static void sort_snapshot(struct snapshot *snapshot)
goto cleanup;
/* We need to sort the memory. First we sort the records array: */
- QSORT(records, nr, cmp_packed_ref_records);
+ QSORT_S(records, nr, cmp_packed_ref_records, snapshot);
/*
* Allocate a new chunk of memory, and copy the old memory to
@@ -466,7 +485,8 @@ static void verify_buffer_safe(struct snapshot *snapshot)
return;
last_line = find_start_of_record(start, eof - 1);
- if (*(eof - 1) != '\n' || eof - last_line < the_hash_algo->hexsz + 2)
+ if (*(eof - 1) != '\n' ||
+ eof - last_line < snapshot_hexsz(snapshot) + 2)
die_invalid_line(snapshot->refs->path,
last_line, eof - last_line);
}
@@ -561,7 +581,7 @@ static const char *find_reference_location_1(struct snapshot *snapshot,
mid = lo + (hi - lo) / 2;
rec = find_start_of_record(lo, mid);
- cmp = cmp_record_to_refname(rec, refname, start);
+ cmp = cmp_record_to_refname(rec, refname, start, snapshot);
if (cmp < 0) {
lo = find_end_of_record(mid, hi);
} else if (cmp > 0) {
@@ -858,7 +878,7 @@ static int next_record(struct packed_ref_iterator *iter)
iter->base.flags = REF_ISPACKED;
p = iter->pos;
- if (iter->eof - p < the_hash_algo->hexsz + 2 ||
+ if (iter->eof - p < snapshot_hexsz(iter->snapshot) + 2 ||
parse_oid_hex(p, &iter->oid, &p) ||
!isspace(*p++))
die_invalid_line(iter->snapshot->refs->path,
@@ -876,7 +896,7 @@ static int next_record(struct packed_ref_iterator *iter)
if (!refname_is_safe(iter->base.refname))
die("packed refname is dangerous: %s",
iter->base.refname);
- oidclr(&iter->oid);
+ oidclr(&iter->oid, the_repository->hash_algo);
iter->base.flags |= REF_BAD_NAME | REF_ISBROKEN;
}
if (iter->snapshot->peeled == PEELED_FULLY ||
@@ -888,7 +908,7 @@ static int next_record(struct packed_ref_iterator *iter)
if (iter->pos < iter->eof && *iter->pos == '^') {
p = iter->pos + 1;
- if (iter->eof - p < the_hash_algo->hexsz + 1 ||
+ if (iter->eof - p < snapshot_hexsz(iter->snapshot) + 1 ||
parse_oid_hex(p, &iter->peeled, &p) ||
*p++ != '\n')
die_invalid_line(iter->snapshot->refs->path,
@@ -901,13 +921,13 @@ static int next_record(struct packed_ref_iterator *iter)
* we suppress it if the reference is broken:
*/
if ((iter->base.flags & REF_ISBROKEN)) {
- oidclr(&iter->peeled);
+ oidclr(&iter->peeled, the_repository->hash_algo);
iter->base.flags &= ~REF_KNOWS_PEELED;
} else {
iter->base.flags |= REF_KNOWS_PEELED;
}
} else {
- oidclr(&iter->peeled);
+ oidclr(&iter->peeled, the_repository->hash_algo);
}
return ITER_OK;
@@ -944,16 +964,13 @@ static int packed_ref_iterator_peel(struct ref_iterator *ref_iterator,
struct packed_ref_iterator *iter =
(struct packed_ref_iterator *)ref_iterator;
- if (iter->repo != the_repository)
- BUG("peeling for non-the_repository is not supported");
-
if ((iter->base.flags & REF_KNOWS_PEELED)) {
oidcpy(peeled, &iter->peeled);
return is_null_oid(&iter->peeled) ? -1 : 0;
} else if ((iter->base.flags & (REF_ISBROKEN | REF_ISSYMREF))) {
return -1;
} else {
- return peel_object(&iter->oid, peeled) ? -1 : 0;
+ return peel_object(iter->repo, &iter->oid, peeled) ? -1 : 0;
}
}
@@ -1244,14 +1261,27 @@ int packed_refs_is_locked(struct ref_store *ref_store)
static const char PACKED_REFS_HEADER[] =
"# pack-refs with: peeled fully-peeled sorted \n";
-static int packed_init_db(struct ref_store *ref_store UNUSED,
- int flags UNUSED,
- struct strbuf *err UNUSED)
+static int packed_ref_store_create_on_disk(struct ref_store *ref_store UNUSED,
+ int flags UNUSED,
+ struct strbuf *err UNUSED)
{
/* Nothing to do. */
return 0;
}
+static int packed_ref_store_remove_on_disk(struct ref_store *ref_store,
+ struct strbuf *err)
+{
+ struct packed_ref_store *refs = packed_downcast(ref_store, 0, "remove");
+
+ if (remove_path(refs->path) < 0) {
+ strbuf_addstr(err, "could not delete packed-refs");
+ return -1;
+ }
+
+ return 0;
+}
+
/*
* Write the packed refs from the current snapshot to the packed-refs
* tempfile, incorporating any changes from `updates`. `updates` must
@@ -1412,7 +1442,8 @@ static int write_with_updates(struct packed_ref_store *refs,
i++;
} else {
struct object_id peeled;
- int peel_error = peel_object(&update->new_oid,
+ int peel_error = peel_object(refs->base.repo,
+ &update->new_oid,
&peeled);
if (write_packed_entry(out, update->refname,
@@ -1706,15 +1737,17 @@ static struct ref_iterator *packed_reflog_iterator_begin(struct ref_store *ref_s
struct ref_storage_be refs_be_packed = {
.name = "packed",
- .init = packed_ref_store_create,
- .init_db = packed_init_db,
+ .init = packed_ref_store_init,
+ .release = packed_ref_store_release,
+ .create_on_disk = packed_ref_store_create_on_disk,
+ .remove_on_disk = packed_ref_store_remove_on_disk,
+
.transaction_prepare = packed_transaction_prepare,
.transaction_finish = packed_transaction_finish,
.transaction_abort = packed_transaction_abort,
.initial_transaction_commit = packed_initial_transaction_commit,
.pack_refs = packed_pack_refs,
- .create_symref = NULL,
.rename_ref = NULL,
.copy_ref = NULL,
diff --git a/refs/packed-backend.h b/refs/packed-backend.h
index 9dd8a344c3..09437ad13b 100644
--- a/refs/packed-backend.h
+++ b/refs/packed-backend.h
@@ -13,9 +13,9 @@ struct ref_transaction;
* even among packed refs.
*/
-struct ref_store *packed_ref_store_create(struct repository *repo,
- const char *gitdir,
- unsigned int store_flags);
+struct ref_store *packed_ref_store_init(struct repository *repo,
+ const char *gitdir,
+ unsigned int store_flags);
/*
* Lock the packed-refs file for writing. Flags is passed to
diff --git a/refs/ref-cache.c b/refs/ref-cache.c
index 9f9797209a..4ce519bbc8 100644
--- a/refs/ref-cache.c
+++ b/refs/ref-cache.c
@@ -71,6 +71,8 @@ static void free_ref_entry(struct ref_entry *entry)
void free_ref_cache(struct ref_cache *cache)
{
+ if (!cache)
+ return;
free_ref_entry(cache->root);
free(cache);
}
@@ -441,10 +443,7 @@ static int cache_ref_iterator_peel(struct ref_iterator *ref_iterator,
{
struct cache_ref_iterator *iter =
(struct cache_ref_iterator *)ref_iterator;
-
- if (iter->repo != the_repository)
- BUG("peeling for non-the_repository is not supported");
- return peel_object(ref_iterator->oid, peeled) ? -1 : 0;
+ return peel_object(iter->repo, ref_iterator->oid, peeled) ? -1 : 0;
}
static int cache_ref_iterator_abort(struct ref_iterator *ref_iterator)
diff --git a/refs/ref-cache.h b/refs/ref-cache.h
index 95c76e27c8..31ebe24f6c 100644
--- a/refs/ref-cache.h
+++ b/refs/ref-cache.h
@@ -1,7 +1,7 @@
#ifndef REFS_REF_CACHE_H
#define REFS_REF_CACHE_H
-#include "hash-ll.h"
+#include "hash.h"
struct ref_dir;
struct ref_store;
diff --git a/refs/refs-internal.h b/refs/refs-internal.h
index 56641aa57a..fa975d69aa 100644
--- a/refs/refs-internal.h
+++ b/refs/refs-internal.h
@@ -69,40 +69,6 @@ int ref_resolves_to_object(const char *refname,
const struct object_id *oid,
unsigned int flags);
-enum peel_status {
- /* object was peeled successfully: */
- PEEL_PEELED = 0,
-
- /*
- * object cannot be peeled because the named object (or an
- * object referred to by a tag in the peel chain), does not
- * exist.
- */
- PEEL_INVALID = -1,
-
- /* object cannot be peeled because it is not a tag: */
- PEEL_NON_TAG = -2,
-
- /* ref_entry contains no peeled value because it is a symref: */
- PEEL_IS_SYMREF = -3,
-
- /*
- * ref_entry cannot be peeled because it is broken (i.e., the
- * symbolic reference cannot even be resolved to an object
- * name):
- */
- PEEL_BROKEN = -4
-};
-
-/*
- * Peel the named object; i.e., if the object is a tag, resolve the
- * tag recursively until a non-tag is found. If successful, store the
- * result to oid and return PEEL_PEELED. If the object is not a tag
- * or is not valid, return PEEL_NON_TAG or PEEL_INVALID, respectively,
- * and leave oid unchanged.
- */
-enum peel_status peel_object(const struct object_id *name, struct object_id *oid);
-
/**
* Information needed for a single ref update. Set new_oid to the new
* value or to null_oid to delete the ref. To check the old value
@@ -125,6 +91,19 @@ struct ref_update {
struct object_id old_oid;
/*
+ * If set, point the reference to this value. This can also be
+ * used to convert regular references to become symbolic refs.
+ * Cannot be set together with `new_oid`.
+ */
+ const char *new_target;
+
+ /*
+ * If set, check that the reference previously pointed to this
+ * value. Cannot be set together with `old_oid`.
+ */
+ const char *old_target;
+
+ /*
* One or more of REF_NO_DEREF, REF_FORCE_CREATE_REFLOG,
* REF_HAVE_NEW, REF_HAVE_OLD, or backend-specific flags.
*/
@@ -173,6 +152,7 @@ struct ref_update *ref_transaction_add_update(
const char *refname, unsigned int flags,
const struct object_id *new_oid,
const struct object_id *old_oid,
+ const char *new_target, const char *old_target,
const char *msg);
/*
@@ -503,9 +483,8 @@ extern struct ref_iterator *current_ref_iter;
* adapter between the callback style of reference iteration and the
* iterator style.
*/
-int do_for_each_repo_ref_iterator(struct repository *r,
- struct ref_iterator *iter,
- each_repo_ref_fn fn, void *cb_data);
+int do_for_each_ref_iterator(struct ref_iterator *iter,
+ each_ref_fn fn, void *cb_data);
struct ref_store;
@@ -529,10 +508,20 @@ struct ref_store;
typedef struct ref_store *ref_store_init_fn(struct repository *repo,
const char *gitdir,
unsigned int flags);
+/*
+ * Release all memory and resources associated with the ref store.
+ */
+typedef void ref_store_release_fn(struct ref_store *refs);
-typedef int ref_init_db_fn(struct ref_store *refs,
- int flags,
- struct strbuf *err);
+typedef int ref_store_create_on_disk_fn(struct ref_store *refs,
+ int flags,
+ struct strbuf *err);
+
+/*
+ * Remove the reference store from disk.
+ */
+typedef int ref_store_remove_on_disk_fn(struct ref_store *refs,
+ struct strbuf *err);
typedef int ref_transaction_prepare_fn(struct ref_store *refs,
struct ref_transaction *transaction,
@@ -552,10 +541,6 @@ typedef int ref_transaction_commit_fn(struct ref_store *refs,
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,
- const char *logmsg);
typedef int rename_ref_fn(struct ref_store *ref_store,
const char *oldref, const char *newref,
const char *logmsg);
@@ -668,7 +653,9 @@ typedef int read_symbolic_ref_fn(struct ref_store *ref_store, const char *refnam
struct ref_storage_be {
const char *name;
ref_store_init_fn *init;
- ref_init_db_fn *init_db;
+ ref_store_release_fn *release;
+ ref_store_create_on_disk_fn *create_on_disk;
+ ref_store_remove_on_disk_fn *remove_on_disk;
ref_transaction_prepare_fn *transaction_prepare;
ref_transaction_finish_fn *transaction_finish;
@@ -676,7 +663,6 @@ struct ref_storage_be {
ref_transaction_commit_fn *initial_transaction_commit;
pack_refs_fn *pack_refs;
- create_symref_fn *create_symref;
rename_ref_fn *rename_ref;
copy_ref_fn *copy_ref;
@@ -700,7 +686,7 @@ extern struct ref_storage_be refs_be_packed;
/*
* A representation of the reference store for the main repository or
* a submodule. The ref_store instances for submodules are kept in a
- * hash map; see get_submodule_ref_store() for more info.
+ * hash map; see repo_get_submodule_ref_store() for more info.
*/
struct ref_store {
/* The backend describing this ref_store's storage scheme: */
@@ -735,4 +721,31 @@ void base_ref_store_init(struct ref_store *refs, struct repository *repo,
*/
struct ref_store *maybe_debug_wrap_ref_store(const char *gitdir, struct ref_store *store);
+/*
+ * Return the refname under which update was originally requested.
+ */
+const char *ref_update_original_update_refname(struct ref_update *update);
+
+/*
+ * Helper function to check if the new value is null, this
+ * takes into consideration that the update could be a regular
+ * ref or a symbolic ref.
+ */
+int ref_update_has_null_new_value(struct ref_update *update);
+
+/*
+ * Check whether the old_target values stored in update are consistent
+ * with the referent, which is the symbolic reference's current value.
+ * If everything is OK, return 0; otherwise, write an error message to
+ * err and return -1.
+ */
+int ref_update_check_old_target(const char *referent, struct ref_update *update,
+ struct strbuf *err);
+
+/*
+ * Check if the ref must exist, this means that the old_oid or
+ * old_target is non NULL.
+ */
+int ref_update_expects_existing_old_ref(struct ref_update *update);
+
#endif /* REFS_REFS_INTERNAL_H */
diff --git a/refs/reftable-backend.c b/refs/reftable-backend.c
index b6668e7c7e..fbe74c239d 100644
--- a/refs/reftable-backend.c
+++ b/refs/reftable-backend.c
@@ -1,6 +1,10 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "../git-compat-util.h"
#include "../abspath.h"
#include "../chdir-notify.h"
+#include "../config.h"
+#include "../dir.h"
#include "../environment.h"
#include "../gettext.h"
#include "../hash.h"
@@ -15,7 +19,6 @@
#include "../reftable/reftable-record.h"
#include "../reftable/reftable-error.h"
#include "../reftable/reftable-iterator.h"
-#include "../reftable/reftable-merged.h"
#include "../setup.h"
#include "../strmap.h"
#include "parse.h"
@@ -129,7 +132,7 @@ static struct reftable_stack *stack_for(struct reftable_ref_store *store,
store->base.repo->commondir, wtname_buf.buf);
store->err = reftable_new_stack(&stack, wt_dir.buf,
- store->write_options);
+ &store->write_options);
assert(store->err != REFTABLE_API_ERROR);
strmap_put(&store->worktree_stacks, wtname_buf.buf, stack);
}
@@ -172,32 +175,30 @@ static int should_write_log(struct ref_store *refs, const char *refname)
}
}
-static void fill_reftable_log_record(struct reftable_log_record *log)
+static void fill_reftable_log_record(struct reftable_log_record *log, const struct ident_split *split)
{
- const char *info = git_committer_info(0);
- struct ident_split split = {0};
+ const char *tz_begin;
int sign = 1;
- if (split_ident_line(&split, info, strlen(info)))
- BUG("failed splitting committer info");
-
reftable_log_record_release(log);
log->value_type = REFTABLE_LOG_UPDATE;
log->value.update.name =
- xstrndup(split.name_begin, split.name_end - split.name_begin);
+ xstrndup(split->name_begin, split->name_end - split->name_begin);
log->value.update.email =
- xstrndup(split.mail_begin, split.mail_end - split.mail_begin);
- log->value.update.time = atol(split.date_begin);
- if (*split.tz_begin == '-') {
+ xstrndup(split->mail_begin, split->mail_end - split->mail_begin);
+ log->value.update.time = atol(split->date_begin);
+
+ tz_begin = split->tz_begin;
+ if (*tz_begin == '-') {
sign = -1;
- split.tz_begin++;
+ tz_begin++;
}
- if (*split.tz_begin == '+') {
+ if (*tz_begin == '+') {
sign = 1;
- split.tz_begin++;
+ tz_begin++;
}
- log->value.update.tz_offset = sign * atoi(split.tz_begin);
+ log->value.update.tz_offset = sign * atoi(tz_begin);
}
static int read_ref_without_reload(struct reftable_stack *stack,
@@ -218,7 +219,8 @@ static int read_ref_without_reload(struct reftable_stack *stack,
strbuf_addstr(referent, ref.value.symref);
*type |= REF_ISSYMREF;
} else if (reftable_ref_record_val1(&ref)) {
- oidread(oid, reftable_ref_record_val1(&ref));
+ oidread(oid, reftable_ref_record_val1(&ref),
+ the_repository->hash_algo);
} else {
/* We got a tombstone, which should not happen. */
BUG("unhandled reference value type %d", ref.value_type);
@@ -230,6 +232,34 @@ done:
return ret;
}
+static int reftable_be_config(const char *var, const char *value,
+ const struct config_context *ctx,
+ void *_opts)
+{
+ struct reftable_write_options *opts = _opts;
+
+ if (!strcmp(var, "reftable.blocksize")) {
+ unsigned long block_size = git_config_ulong(var, value, ctx->kvi);
+ if (block_size > 16777215)
+ die("reftable block size cannot exceed 16MB");
+ opts->block_size = block_size;
+ } else if (!strcmp(var, "reftable.restartinterval")) {
+ unsigned long restart_interval = git_config_ulong(var, value, ctx->kvi);
+ if (restart_interval > UINT16_MAX)
+ die("reftable block size cannot exceed %u", (unsigned)UINT16_MAX);
+ opts->restart_interval = restart_interval;
+ } else if (!strcmp(var, "reftable.indexobjects")) {
+ opts->skip_index_objects = !git_config_bool(var, value);
+ } else if (!strcmp(var, "reftable.geometricfactor")) {
+ unsigned long factor = git_config_ulong(var, value, ctx->kvi);
+ if (factor > UINT8_MAX)
+ die("reftable geometric factor cannot exceed %u", (unsigned)UINT8_MAX);
+ opts->auto_compaction_factor = factor;
+ }
+
+ return 0;
+}
+
static struct ref_store *reftable_be_init(struct repository *repo,
const char *gitdir,
unsigned int store_flags)
@@ -245,12 +275,24 @@ static struct ref_store *reftable_be_init(struct repository *repo,
base_ref_store_init(&refs->base, repo, gitdir, &refs_be_reftable);
strmap_init(&refs->worktree_stacks);
refs->store_flags = store_flags;
- refs->write_options.block_size = 4096;
+
refs->write_options.hash_id = repo->hash_algo->format_id;
refs->write_options.default_permissions = calc_shared_perm(0666 & ~mask);
refs->write_options.disable_auto_compact =
!git_env_bool("GIT_TEST_REFTABLE_AUTOCOMPACTION", 1);
+ git_config(reftable_be_config, &refs->write_options);
+
+ /*
+ * It is somewhat unfortunate that we have to mirror the default block
+ * size of the reftable library here. But given that the write options
+ * wouldn't be updated by the library here, and given that we require
+ * the proper block size to trim reflog message so that they fit, we
+ * must set up a proper value here.
+ */
+ if (!refs->write_options.block_size)
+ refs->write_options.block_size = 4096;
+
/*
* Set up the main reftable stack that is hosted in GIT_COMMON_DIR.
* This stack contains both the shared and the main worktree refs.
@@ -265,7 +307,7 @@ static struct ref_store *reftable_be_init(struct repository *repo,
}
strbuf_addstr(&path, "/reftable");
refs->err = reftable_new_stack(&refs->main_stack, path.buf,
- refs->write_options);
+ &refs->write_options);
if (refs->err)
goto done;
@@ -282,7 +324,7 @@ static struct ref_store *reftable_be_init(struct repository *repo,
strbuf_addf(&path, "%s/reftable", gitdir);
refs->err = reftable_new_stack(&refs->worktree_stack, path.buf,
- refs->write_options);
+ &refs->write_options);
if (refs->err)
goto done;
}
@@ -295,12 +337,33 @@ done:
return &refs->base;
}
-static int reftable_be_init_db(struct ref_store *ref_store,
- int flags UNUSED,
- struct strbuf *err UNUSED)
+static void reftable_be_release(struct ref_store *ref_store)
+{
+ struct reftable_ref_store *refs = reftable_be_downcast(ref_store, 0, "release");
+ struct strmap_entry *entry;
+ struct hashmap_iter iter;
+
+ if (refs->main_stack) {
+ reftable_stack_destroy(refs->main_stack);
+ refs->main_stack = NULL;
+ }
+
+ if (refs->worktree_stack) {
+ reftable_stack_destroy(refs->worktree_stack);
+ refs->worktree_stack = NULL;
+ }
+
+ strmap_for_each_entry(&refs->worktree_stacks, &iter, entry)
+ reftable_stack_destroy(entry->value);
+ strmap_clear(&refs->worktree_stacks, 0);
+}
+
+static int reftable_be_create_on_disk(struct ref_store *ref_store,
+ int flags UNUSED,
+ struct strbuf *err UNUSED)
{
struct reftable_ref_store *refs =
- reftable_be_downcast(ref_store, REF_STORE_WRITE, "init_db");
+ reftable_be_downcast(ref_store, REF_STORE_WRITE, "create");
struct strbuf sb = STRBUF_INIT;
strbuf_addf(&sb, "%s/reftable", refs->base.gitdir);
@@ -324,6 +387,56 @@ static int reftable_be_init_db(struct ref_store *ref_store,
return 0;
}
+static int reftable_be_remove_on_disk(struct ref_store *ref_store,
+ struct strbuf *err)
+{
+ struct reftable_ref_store *refs =
+ reftable_be_downcast(ref_store, REF_STORE_WRITE, "remove");
+ struct strbuf sb = STRBUF_INIT;
+ int ret = 0;
+
+ /*
+ * Release the ref store such that all stacks are closed. This is
+ * required so that the "tables.list" file is not open anymore, which
+ * would otherwise make it impossible to remove the file on Windows.
+ */
+ reftable_be_release(ref_store);
+
+ strbuf_addf(&sb, "%s/reftable", refs->base.gitdir);
+ if (remove_dir_recursively(&sb, 0) < 0) {
+ strbuf_addf(err, "could not delete reftables: %s",
+ strerror(errno));
+ ret = -1;
+ }
+ strbuf_reset(&sb);
+
+ strbuf_addf(&sb, "%s/HEAD", refs->base.gitdir);
+ if (unlink(sb.buf) < 0) {
+ strbuf_addf(err, "could not delete stub HEAD: %s",
+ strerror(errno));
+ ret = -1;
+ }
+ strbuf_reset(&sb);
+
+ strbuf_addf(&sb, "%s/refs/heads", refs->base.gitdir);
+ if (unlink(sb.buf) < 0) {
+ strbuf_addf(err, "could not delete stub heads: %s",
+ strerror(errno));
+ ret = -1;
+ }
+ strbuf_reset(&sb);
+
+ strbuf_addf(&sb, "%s/refs", refs->base.gitdir);
+ if (rmdir(sb.buf) < 0) {
+ strbuf_addf(err, "could not delete refs directory: %s",
+ strerror(errno));
+ ret = -1;
+ }
+
+ strbuf_release(&sb);
+ return ret;
+}
+
struct reftable_ref_iterator {
struct ref_iterator base;
struct reftable_ref_store *refs;
@@ -373,15 +486,17 @@ static int reftable_ref_iterator_advance(struct ref_iterator *ref_iterator)
switch (iter->ref.value_type) {
case REFTABLE_REF_VAL1:
- oidread(&iter->oid, iter->ref.value.val1);
+ oidread(&iter->oid, iter->ref.value.val1,
+ the_repository->hash_algo);
break;
case REFTABLE_REF_VAL2:
- oidread(&iter->oid, iter->ref.value.val2.value);
+ oidread(&iter->oid, iter->ref.value.val2.value,
+ the_repository->hash_algo);
break;
case REFTABLE_REF_SYMREF:
if (!refs_resolve_ref_unsafe(&iter->refs->base, iter->ref.refname,
RESOLVE_REF_READING, &iter->oid, &flags))
- oidclr(&iter->oid);
+ oidclr(&iter->oid, the_repository->hash_algo);
break;
default:
BUG("unhandled reference value type %d", iter->ref.value_type);
@@ -393,7 +508,7 @@ static int reftable_ref_iterator_advance(struct ref_iterator *ref_iterator)
if (check_refname_format(iter->ref.refname, REFNAME_ALLOW_ONELEVEL)) {
if (!refname_is_safe(iter->ref.refname))
die(_("refname is dangerous: %s"), iter->ref.refname);
- oidclr(&iter->oid);
+ oidclr(&iter->oid, the_repository->hash_algo);
flags |= REF_BAD_NAME | REF_ISBROKEN;
}
@@ -435,7 +550,8 @@ static int reftable_ref_iterator_peel(struct ref_iterator *ref_iterator,
(struct reftable_ref_iterator *)ref_iterator;
if (iter->ref.value_type == REFTABLE_REF_VAL2) {
- oidread(peeled, iter->ref.value.val2.target_value);
+ oidread(peeled, iter->ref.value.val2.target_value,
+ the_repository->hash_algo);
return 0;
}
@@ -463,7 +579,6 @@ static struct reftable_ref_iterator *ref_iterator_for_stack(struct reftable_ref_
const char *prefix,
int flags)
{
- struct reftable_merged_table *merged_table;
struct reftable_ref_iterator *iter;
int ret;
@@ -483,9 +598,8 @@ static struct reftable_ref_iterator *ref_iterator_for_stack(struct reftable_ref_
if (ret)
goto done;
- merged_table = reftable_stack_merged_table(stack);
-
- ret = reftable_merged_table_seek_ref(merged_table, &iter->iter, prefix);
+ reftable_stack_init_ref_iterator(stack, &iter->iter);
+ ret = reftable_iterator_seek_ref(&iter->iter, prefix);
if (ret)
goto done;
@@ -580,16 +694,6 @@ static int reftable_be_read_symbolic_ref(struct ref_store *ref_store,
return ret;
}
-/*
- * Return the refname under which update was originally requested.
- */
-static const char *original_update_refname(struct ref_update *update)
-{
- while (update->parent_update)
- update = update->parent_update;
- return update->refname;
-}
-
struct reftable_transaction_update {
struct ref_update *update;
struct object_id current_oid;
@@ -828,7 +932,7 @@ static int reftable_be_transaction_prepare(struct ref_store *ref_store,
new_update = ref_transaction_add_update(
transaction, "HEAD",
u->flags | REF_LOG_ONLY | REF_NO_DEREF,
- &u->new_oid, &u->old_oid, u->msg);
+ &u->new_oid, &u->old_oid, NULL, NULL, u->msg);
string_list_insert(&affected_refnames, new_update->refname);
}
@@ -836,7 +940,7 @@ static int reftable_be_transaction_prepare(struct ref_store *ref_store,
&current_oid, &referent, &u->type);
if (ret < 0)
goto done;
- if (ret > 0 && (!(u->flags & REF_HAVE_OLD) || is_null_oid(&u->old_oid))) {
+ if (ret > 0 && !ref_update_expects_existing_old_ref(u)) {
/*
* The reference does not exist, and we either have no
* old object ID or expect the reference to not exist.
@@ -855,7 +959,7 @@ static int reftable_be_transaction_prepare(struct ref_store *ref_store,
* There is no need to write the reference deletion
* when the reference in question doesn't exist.
*/
- if (u->flags & REF_HAVE_NEW && !is_null_oid(&u->new_oid)) {
+ if ((u->flags & REF_HAVE_NEW) && !ref_update_has_null_new_value(u)) {
ret = queue_transaction_update(refs, tx_data, u,
&current_oid, err);
if (ret)
@@ -868,7 +972,7 @@ static int reftable_be_transaction_prepare(struct ref_store *ref_store,
/* The reference does not exist, but we expected it to. */
strbuf_addf(err, _("cannot lock ref '%s': "
"unable to resolve reference '%s'"),
- original_update_refname(u), u->refname);
+ ref_update_original_update_refname(u), u->refname);
ret = -1;
goto done;
}
@@ -906,8 +1010,11 @@ static int reftable_be_transaction_prepare(struct ref_store *ref_store,
* intertwined with the locking in files-backend.c.
*/
new_update = ref_transaction_add_update(
- transaction, referent.buf, new_flags,
- &u->new_oid, &u->old_oid, u->msg);
+ transaction, referent.buf, new_flags,
+ u->new_target ? NULL : &u->new_oid,
+ u->old_target ? NULL : &u->old_oid,
+ u->new_target, u->old_target, u->msg);
+
new_update->parent_update = u;
/*
@@ -937,20 +1044,35 @@ static int reftable_be_transaction_prepare(struct ref_store *ref_store,
* individual refs. But the error messages match what the files
* backend returns, which keeps our tests happy.
*/
- if (u->flags & REF_HAVE_OLD && !oideq(&current_oid, &u->old_oid)) {
+ if (u->old_target) {
+ if (!(u->type & REF_ISSYMREF)) {
+ strbuf_addf(err, _("cannot lock ref '%s': "
+ "expected symref with target '%s': "
+ "but is a regular ref"),
+ ref_update_original_update_refname(u),
+ u->old_target);
+ ret = -1;
+ goto done;
+ }
+
+ if (ref_update_check_old_target(referent.buf, u, err)) {
+ ret = -1;
+ goto done;
+ }
+ } else if ((u->flags & REF_HAVE_OLD) && !oideq(&current_oid, &u->old_oid)) {
if (is_null_oid(&u->old_oid))
strbuf_addf(err, _("cannot lock ref '%s': "
- "reference already exists"),
- original_update_refname(u));
+ "reference already exists"),
+ ref_update_original_update_refname(u));
else if (is_null_oid(&current_oid))
strbuf_addf(err, _("cannot lock ref '%s': "
- "reference is missing but expected %s"),
- original_update_refname(u),
+ "reference is missing but expected %s"),
+ ref_update_original_update_refname(u),
oid_to_hex(&u->old_oid));
else
strbuf_addf(err, _("cannot lock ref '%s': "
- "is at %s but expected %s"),
- original_update_refname(u),
+ "is at %s but expected %s"),
+ ref_update_original_update_refname(u),
oid_to_hex(&current_oid),
oid_to_hex(&u->old_oid));
ret = -1;
@@ -1016,13 +1138,17 @@ static int transaction_update_cmp(const void *a, const void *b)
static int write_transaction_table(struct reftable_writer *writer, void *cb_data)
{
struct write_transaction_table_arg *arg = cb_data;
- struct reftable_merged_table *mt =
- reftable_stack_merged_table(arg->stack);
uint64_t ts = reftable_stack_next_update_index(arg->stack);
struct reftable_log_record *logs = NULL;
+ struct ident_split committer_ident = {0};
size_t logs_nr = 0, logs_alloc = 0, i;
+ const char *committer_info;
int ret = 0;
+ committer_info = git_committer_info(0);
+ if (split_ident_line(&committer_ident, committer_info, strlen(committer_info)))
+ BUG("failed splitting committer info");
+
QSORT(arg->updates, arg->updates_nr, transaction_update_cmp);
reftable_writer_set_limits(writer, ts, ts);
@@ -1042,10 +1168,14 @@ static int write_transaction_table(struct reftable_writer *writer, void *cb_data
* - `core.logAllRefUpdates` tells us to create the reflog for
* the given ref.
*/
- if (u->flags & REF_HAVE_NEW && !(u->type & REF_ISSYMREF) && is_null_oid(&u->new_oid)) {
+ if ((u->flags & REF_HAVE_NEW) &&
+ !(u->type & REF_ISSYMREF) &&
+ ref_update_has_null_new_value(u)) {
struct reftable_log_record log = {0};
struct reftable_iterator it = {0};
+ reftable_stack_init_log_iterator(arg->stack, &it);
+
/*
* When deleting refs we also delete all reflog entries
* with them. While it is not strictly required to
@@ -1055,7 +1185,7 @@ static int write_transaction_table(struct reftable_writer *writer, void *cb_data
* Unfortunately, we have no better way than to delete
* all reflog entries one by one.
*/
- ret = reftable_merged_table_seek_log(mt, &it, u->refname);
+ ret = reftable_iterator_seek_log(&it, u->refname);
while (ret == 0) {
struct reftable_log_record *tombstone;
@@ -1079,28 +1209,57 @@ static int write_transaction_table(struct reftable_writer *writer, void *cb_data
if (ret)
goto done;
- } else if (u->flags & REF_HAVE_NEW &&
+ } else if (!(u->flags & REF_SKIP_CREATE_REFLOG) &&
+ (u->flags & REF_HAVE_NEW) &&
(u->flags & REF_FORCE_CREATE_REFLOG ||
should_write_log(&arg->refs->base, u->refname))) {
struct reftable_log_record *log;
+ int create_reflog = 1;
+
+ if (u->new_target) {
+ if (!refs_resolve_ref_unsafe(&arg->refs->base, u->new_target,
+ RESOLVE_REF_READING, &u->new_oid, NULL)) {
+ /*
+ * TODO: currently we skip creating reflogs for dangling
+ * symref updates. It would be nice to capture this as
+ * zero oid updates however.
+ */
+ create_reflog = 0;
+ }
+ }
- ALLOC_GROW(logs, logs_nr + 1, logs_alloc);
- log = &logs[logs_nr++];
- memset(log, 0, sizeof(*log));
-
- fill_reftable_log_record(log);
- log->update_index = ts;
- log->refname = xstrdup(u->refname);
- memcpy(log->value.update.new_hash, u->new_oid.hash, GIT_MAX_RAWSZ);
- memcpy(log->value.update.old_hash, tx_update->current_oid.hash, GIT_MAX_RAWSZ);
- log->value.update.message =
- xstrndup(u->msg, arg->refs->write_options.block_size / 2);
+ if (create_reflog) {
+ ALLOC_GROW(logs, logs_nr + 1, logs_alloc);
+ log = &logs[logs_nr++];
+ memset(log, 0, sizeof(*log));
+
+ fill_reftable_log_record(log, &committer_ident);
+ log->update_index = ts;
+ log->refname = xstrdup(u->refname);
+ memcpy(log->value.update.new_hash,
+ u->new_oid.hash, GIT_MAX_RAWSZ);
+ memcpy(log->value.update.old_hash,
+ tx_update->current_oid.hash, GIT_MAX_RAWSZ);
+ log->value.update.message =
+ xstrndup(u->msg, arg->refs->write_options.block_size / 2);
+ }
}
if (u->flags & REF_LOG_ONLY)
continue;
- if (u->flags & REF_HAVE_NEW && is_null_oid(&u->new_oid)) {
+ if (u->new_target) {
+ struct reftable_ref_record ref = {
+ .refname = (char *)u->refname,
+ .value_type = REFTABLE_REF_SYMREF,
+ .value.symref = (char *)u->new_target,
+ .update_index = ts,
+ };
+
+ ret = reftable_writer_add_ref(writer, &ref);
+ if (ret < 0)
+ goto done;
+ } else if ((u->flags & REF_HAVE_NEW) && ref_update_has_null_new_value(u)) {
struct reftable_ref_record ref = {
.refname = (char *)u->refname,
.update_index = ts,
@@ -1118,7 +1277,7 @@ static int write_transaction_table(struct reftable_writer *writer, void *cb_data
ref.refname = (char *)u->refname;
ref.update_index = ts;
- peel_error = peel_object(&u->new_oid, &peeled);
+ peel_error = peel_object(arg->refs->base.repo, &u->new_oid, &peeled);
if (!peel_error) {
ref.value_type = REFTABLE_REF_VAL2;
memcpy(ref.value.val2.target_value, peeled.hash, GIT_MAX_RAWSZ);
@@ -1226,96 +1385,12 @@ out:
struct write_create_symref_arg {
struct reftable_ref_store *refs;
struct reftable_stack *stack;
+ struct strbuf *err;
const char *refname;
const char *target;
const char *logmsg;
};
-static int write_create_symref_table(struct reftable_writer *writer, void *cb_data)
-{
- struct write_create_symref_arg *create = cb_data;
- uint64_t ts = reftable_stack_next_update_index(create->stack);
- struct reftable_ref_record ref = {
- .refname = (char *)create->refname,
- .value_type = REFTABLE_REF_SYMREF,
- .value.symref = (char *)create->target,
- .update_index = ts,
- };
- struct reftable_log_record log = {0};
- struct object_id new_oid;
- struct object_id old_oid;
- int ret;
-
- reftable_writer_set_limits(writer, ts, ts);
-
- ret = reftable_writer_add_ref(writer, &ref);
- if (ret)
- return ret;
-
- /*
- * Note that it is important to try and resolve the reference before we
- * write the log entry. This is because `should_write_log()` will munge
- * `core.logAllRefUpdates`, which is undesirable when we create a new
- * repository because it would be written into the config. As HEAD will
- * not resolve for new repositories this ordering will ensure that this
- * never happens.
- */
- if (!create->logmsg ||
- !refs_resolve_ref_unsafe(&create->refs->base, create->target,
- RESOLVE_REF_READING, &new_oid, NULL) ||
- !should_write_log(&create->refs->base, create->refname))
- return 0;
-
- fill_reftable_log_record(&log);
- log.refname = xstrdup(create->refname);
- log.update_index = ts;
- log.value.update.message = xstrndup(create->logmsg,
- create->refs->write_options.block_size / 2);
- memcpy(log.value.update.new_hash, new_oid.hash, GIT_MAX_RAWSZ);
- if (refs_resolve_ref_unsafe(&create->refs->base, create->refname,
- RESOLVE_REF_READING, &old_oid, NULL))
- memcpy(log.value.update.old_hash, old_oid.hash, GIT_MAX_RAWSZ);
-
- ret = reftable_writer_add_log(writer, &log);
- reftable_log_record_release(&log);
- return ret;
-}
-
-static int reftable_be_create_symref(struct ref_store *ref_store,
- const char *refname,
- const char *target,
- const char *logmsg)
-{
- struct reftable_ref_store *refs =
- reftable_be_downcast(ref_store, REF_STORE_WRITE, "create_symref");
- struct reftable_stack *stack = stack_for(refs, refname, &refname);
- struct write_create_symref_arg arg = {
- .refs = refs,
- .stack = stack,
- .refname = refname,
- .target = target,
- .logmsg = logmsg,
- };
- int ret;
-
- ret = refs->err;
- if (ret < 0)
- goto done;
-
- ret = reftable_stack_reload(stack);
- if (ret)
- goto done;
-
- ret = reftable_stack_add(stack, &write_create_symref_table, &arg);
-
-done:
- assert(ret != REFTABLE_API_ERROR);
- if (ret)
- error("unable to write symref for %s: %s", refname,
- reftable_error_str(ret));
- return ret;
-}
-
struct write_copy_arg {
struct reftable_ref_store *refs;
struct reftable_stack *stack;
@@ -1329,15 +1404,20 @@ static int write_copy_table(struct reftable_writer *writer, void *cb_data)
{
struct write_copy_arg *arg = cb_data;
uint64_t deletion_ts, creation_ts;
- struct reftable_merged_table *mt = reftable_stack_merged_table(arg->stack);
struct reftable_ref_record old_ref = {0}, refs[2] = {0};
struct reftable_log_record old_log = {0}, *logs = NULL;
struct reftable_iterator it = {0};
struct string_list skip = STRING_LIST_INIT_NODUP;
+ struct ident_split committer_ident = {0};
struct strbuf errbuf = STRBUF_INIT;
size_t logs_nr = 0, logs_alloc = 0, i;
+ const char *committer_info;
int ret;
+ committer_info = git_committer_info(0);
+ if (split_ident_line(&committer_ident, committer_info, strlen(committer_info)))
+ BUG("failed splitting committer info");
+
if (reftable_stack_read_ref(arg->stack, arg->oldname, &old_ref)) {
ret = error(_("refname %s not found"), arg->oldname);
goto done;
@@ -1360,7 +1440,8 @@ static int write_copy_table(struct reftable_writer *writer, void *cb_data)
/*
* Verify that the new refname is available.
*/
- string_list_insert(&skip, arg->oldname);
+ if (arg->delete_old)
+ string_list_insert(&skip, arg->oldname);
ret = refs_verify_refname_available(&arg->refs->base, arg->newname,
NULL, &skip, &errbuf);
if (ret < 0) {
@@ -1386,10 +1467,10 @@ static int write_copy_table(struct reftable_writer *writer, void *cb_data)
* old reference.
*/
refs[0] = old_ref;
- refs[0].refname = (char *)arg->newname;
+ refs[0].refname = xstrdup(arg->newname);
refs[0].update_index = creation_ts;
if (arg->delete_old) {
- refs[1].refname = (char *)arg->oldname;
+ refs[1].refname = xstrdup(arg->oldname);
refs[1].value_type = REFTABLE_REF_DELETION;
refs[1].update_index = deletion_ts;
}
@@ -1411,8 +1492,8 @@ static int write_copy_table(struct reftable_writer *writer, void *cb_data)
ALLOC_GROW(logs, logs_nr + 1, logs_alloc);
memset(&logs[logs_nr], 0, sizeof(logs[logs_nr]));
- fill_reftable_log_record(&logs[logs_nr]);
- logs[logs_nr].refname = (char *)arg->newname;
+ fill_reftable_log_record(&logs[logs_nr], &committer_ident);
+ logs[logs_nr].refname = xstrdup(arg->newname);
logs[logs_nr].update_index = deletion_ts;
logs[logs_nr].value.update.message =
xstrndup(arg->logmsg, arg->refs->write_options.block_size / 2);
@@ -1433,7 +1514,13 @@ static int write_copy_table(struct reftable_writer *writer, void *cb_data)
if (append_head_reflog) {
ALLOC_GROW(logs, logs_nr + 1, logs_alloc);
logs[logs_nr] = logs[logs_nr - 1];
- logs[logs_nr].refname = "HEAD";
+ logs[logs_nr].refname = xstrdup("HEAD");
+ logs[logs_nr].value.update.name =
+ xstrdup(logs[logs_nr].value.update.name);
+ logs[logs_nr].value.update.email =
+ xstrdup(logs[logs_nr].value.update.email);
+ logs[logs_nr].value.update.message =
+ xstrdup(logs[logs_nr].value.update.message);
logs_nr++;
}
}
@@ -1443,8 +1530,8 @@ static int write_copy_table(struct reftable_writer *writer, void *cb_data)
*/
ALLOC_GROW(logs, logs_nr + 1, logs_alloc);
memset(&logs[logs_nr], 0, sizeof(logs[logs_nr]));
- fill_reftable_log_record(&logs[logs_nr]);
- logs[logs_nr].refname = (char *)arg->newname;
+ fill_reftable_log_record(&logs[logs_nr], &committer_ident);
+ logs[logs_nr].refname = xstrdup(arg->newname);
logs[logs_nr].update_index = creation_ts;
logs[logs_nr].value.update.message =
xstrndup(arg->logmsg, arg->refs->write_options.block_size / 2);
@@ -1456,7 +1543,8 @@ static int write_copy_table(struct reftable_writer *writer, void *cb_data)
* copy over all log entries from the old reflog. Last but not least,
* when renaming we also have to delete all the old reflog entries.
*/
- ret = reftable_merged_table_seek_log(mt, &it, arg->oldname);
+ reftable_stack_init_log_iterator(arg->stack, &it);
+ ret = reftable_iterator_seek_log(&it, arg->oldname);
if (ret < 0)
goto done;
@@ -1476,7 +1564,7 @@ static int write_copy_table(struct reftable_writer *writer, void *cb_data)
*/
ALLOC_GROW(logs, logs_nr + 1, logs_alloc);
logs[logs_nr] = old_log;
- logs[logs_nr].refname = (char *)arg->newname;
+ logs[logs_nr].refname = xstrdup(arg->newname);
logs_nr++;
/*
@@ -1485,7 +1573,7 @@ static int write_copy_table(struct reftable_writer *writer, void *cb_data)
if (arg->delete_old) {
ALLOC_GROW(logs, logs_nr + 1, logs_alloc);
memset(&logs[logs_nr], 0, sizeof(logs[logs_nr]));
- logs[logs_nr].refname = (char *)arg->oldname;
+ logs[logs_nr].refname = xstrdup(arg->oldname);
logs[logs_nr].value_type = REFTABLE_LOG_DELETION;
logs[logs_nr].update_index = old_log.update_index;
logs_nr++;
@@ -1508,13 +1596,11 @@ done:
reftable_iterator_destroy(&it);
string_list_clear(&skip, 0);
strbuf_release(&errbuf);
- for (i = 0; i < logs_nr; i++) {
- if (!strcmp(logs[i].refname, "HEAD"))
- continue;
- logs[i].refname = NULL;
+ for (i = 0; i < logs_nr; i++)
reftable_log_record_release(&logs[i]);
- }
free(logs);
+ for (i = 0; i < ARRAY_SIZE(refs); i++)
+ reftable_ref_record_release(&refs[i]);
reftable_ref_record_release(&old_ref);
reftable_log_record_release(&old_log);
return ret;
@@ -1662,7 +1748,6 @@ static struct ref_iterator_vtable reftable_reflog_iterator_vtable = {
static struct reftable_reflog_iterator *reflog_iterator_for_stack(struct reftable_ref_store *refs,
struct reftable_stack *stack)
{
- struct reftable_merged_table *merged_table;
struct reftable_reflog_iterator *iter;
int ret;
@@ -1679,9 +1764,8 @@ static struct reftable_reflog_iterator *reflog_iterator_for_stack(struct reftabl
if (ret < 0)
goto done;
- merged_table = reftable_stack_merged_table(stack);
-
- ret = reftable_merged_table_seek_log(merged_table, &iter->iter, "");
+ reftable_stack_init_log_iterator(stack, &iter->iter);
+ ret = reftable_iterator_seek_log(&iter->iter, "");
if (ret < 0)
goto done;
@@ -1713,8 +1797,8 @@ static int yield_log_record(struct reftable_log_record *log,
struct object_id old_oid, new_oid;
const char *full_committer;
- oidread(&old_oid, log->value.update.old_hash);
- oidread(&new_oid, log->value.update.new_hash);
+ oidread(&old_oid, log->value.update.old_hash, the_repository->hash_algo);
+ oidread(&new_oid, log->value.update.new_hash, the_repository->hash_algo);
/*
* When both the old object ID and the new object ID are null
@@ -1739,7 +1823,6 @@ static int reftable_be_for_each_reflog_ent_reverse(struct ref_store *ref_store,
struct reftable_ref_store *refs =
reftable_be_downcast(ref_store, REF_STORE_READ, "for_each_reflog_ent_reverse");
struct reftable_stack *stack = stack_for(refs, refname, &refname);
- struct reftable_merged_table *mt = NULL;
struct reftable_log_record log = {0};
struct reftable_iterator it = {0};
int ret;
@@ -1747,8 +1830,8 @@ static int reftable_be_for_each_reflog_ent_reverse(struct ref_store *ref_store,
if (refs->err < 0)
return refs->err;
- mt = reftable_stack_merged_table(stack);
- ret = reftable_merged_table_seek_log(mt, &it, refname);
+ reftable_stack_init_log_iterator(stack, &it);
+ ret = reftable_iterator_seek_log(&it, refname);
while (!ret) {
ret = reftable_iterator_next_log(&it, &log);
if (ret < 0)
@@ -1776,7 +1859,6 @@ static int reftable_be_for_each_reflog_ent(struct ref_store *ref_store,
struct reftable_ref_store *refs =
reftable_be_downcast(ref_store, REF_STORE_READ, "for_each_reflog_ent");
struct reftable_stack *stack = stack_for(refs, refname, &refname);
- struct reftable_merged_table *mt = NULL;
struct reftable_log_record *logs = NULL;
struct reftable_iterator it = {0};
size_t logs_alloc = 0, logs_nr = 0, i;
@@ -1785,8 +1867,8 @@ static int reftable_be_for_each_reflog_ent(struct ref_store *ref_store,
if (refs->err < 0)
return refs->err;
- mt = reftable_stack_merged_table(stack);
- ret = reftable_merged_table_seek_log(mt, &it, refname);
+ reftable_stack_init_log_iterator(stack, &it);
+ ret = reftable_iterator_seek_log(&it, refname);
while (!ret) {
struct reftable_log_record log = {0};
@@ -1823,7 +1905,6 @@ static int reftable_be_reflog_exists(struct ref_store *ref_store,
struct reftable_ref_store *refs =
reftable_be_downcast(ref_store, REF_STORE_READ, "reflog_exists");
struct reftable_stack *stack = stack_for(refs, refname, &refname);
- struct reftable_merged_table *mt = reftable_stack_merged_table(stack);
struct reftable_log_record log = {0};
struct reftable_iterator it = {0};
int ret;
@@ -1836,7 +1917,8 @@ static int reftable_be_reflog_exists(struct ref_store *ref_store,
if (ret < 0)
goto done;
- ret = reftable_merged_table_seek_log(mt, &it, refname);
+ reftable_stack_init_log_iterator(stack, &it);
+ ret = reftable_iterator_seek_log(&it, refname);
if (ret < 0)
goto done;
@@ -1934,8 +2016,6 @@ struct write_reflog_delete_arg {
static int write_reflog_delete_table(struct reftable_writer *writer, void *cb_data)
{
struct write_reflog_delete_arg *arg = cb_data;
- struct reftable_merged_table *mt =
- reftable_stack_merged_table(arg->stack);
struct reftable_log_record log = {0}, tombstone = {0};
struct reftable_iterator it = {0};
uint64_t ts = reftable_stack_next_update_index(arg->stack);
@@ -1943,12 +2023,14 @@ static int write_reflog_delete_table(struct reftable_writer *writer, void *cb_da
reftable_writer_set_limits(writer, ts, ts);
+ reftable_stack_init_log_iterator(arg->stack, &it);
+
/*
* In order to delete a table we need to delete all reflog entries one
* by one. This is inefficient, but the reftable format does not have a
* better marker right now.
*/
- ret = reftable_merged_table_seek_log(mt, &it, arg->refname);
+ ret = reftable_iterator_seek_log(&it, arg->refname);
while (ret == 0) {
ret = reftable_iterator_next_log(&it, &log);
if (ret < 0)
@@ -1992,6 +2074,7 @@ static int reftable_be_delete_reflog(struct ref_store *ref_store,
}
struct reflog_expiry_arg {
+ struct reftable_ref_store *refs;
struct reftable_stack *stack;
struct reftable_log_record *records;
struct object_id update_oid;
@@ -2020,7 +2103,7 @@ static int write_reflog_expiry_table(struct reftable_writer *writer, void *cb_da
ref.refname = (char *)arg->refname;
ref.update_index = ts;
- if (!peel_object(&arg->update_oid, &peeled)) {
+ if (!peel_object(arg->refs->base.repo, &arg->update_oid, &peeled)) {
ref.value_type = REFTABLE_REF_VAL2;
memcpy(ref.value.val2.target_value, peeled.hash, GIT_MAX_RAWSZ);
memcpy(ref.value.val2.value, arg->update_oid.hash, GIT_MAX_RAWSZ);
@@ -2084,7 +2167,6 @@ static int reftable_be_reflog_expire(struct ref_store *ref_store,
struct reftable_ref_store *refs =
reftable_be_downcast(ref_store, REF_STORE_WRITE, "reflog_expire");
struct reftable_stack *stack = stack_for(refs, refname, &refname);
- struct reftable_merged_table *mt = reftable_stack_merged_table(stack);
struct reftable_log_record *logs = NULL;
struct reftable_log_record *rewritten = NULL;
struct reftable_ref_record ref_record = {0};
@@ -2103,7 +2185,9 @@ static int reftable_be_reflog_expire(struct ref_store *ref_store,
if (ret < 0)
goto done;
- ret = reftable_merged_table_seek_log(mt, &it, refname);
+ reftable_stack_init_log_iterator(stack, &it);
+
+ ret = reftable_iterator_seek_log(&it, refname);
if (ret < 0)
goto done;
@@ -2115,7 +2199,8 @@ static int reftable_be_reflog_expire(struct ref_store *ref_store,
if (ret < 0)
goto done;
if (reftable_ref_record_val1(&ref_record))
- oidread(&oid, reftable_ref_record_val1(&ref_record));
+ oidread(&oid, reftable_ref_record_val1(&ref_record),
+ the_repository->hash_algo);
prepare_fn(refname, &oid, policy_cb_data);
while (1) {
@@ -2130,8 +2215,10 @@ static int reftable_be_reflog_expire(struct ref_store *ref_store,
break;
}
- oidread(&old_oid, log.value.update.old_hash);
- oidread(&new_oid, log.value.update.new_hash);
+ oidread(&old_oid, log.value.update.old_hash,
+ the_repository->hash_algo);
+ oidread(&new_oid, log.value.update.new_hash,
+ the_repository->hash_algo);
/*
* Skip over the reflog existence marker. We will add it back
@@ -2162,8 +2249,10 @@ static int reftable_be_reflog_expire(struct ref_store *ref_store,
struct object_id old_oid, new_oid;
*dest = logs[i];
- oidread(&old_oid, logs[i].value.update.old_hash);
- oidread(&new_oid, logs[i].value.update.new_hash);
+ oidread(&old_oid, logs[i].value.update.old_hash,
+ the_repository->hash_algo);
+ oidread(&new_oid, logs[i].value.update.new_hash,
+ the_repository->hash_algo);
if (should_prune_fn(&old_oid, &new_oid, logs[i].value.update.email,
(timestamp_t)logs[i].value.update.time,
@@ -2180,8 +2269,9 @@ static int reftable_be_reflog_expire(struct ref_store *ref_store,
if (flags & EXPIRE_REFLOGS_UPDATE_REF && last_hash &&
reftable_ref_record_val1(&ref_record))
- oidread(&arg.update_oid, last_hash);
+ oidread(&arg.update_oid, last_hash, the_repository->hash_algo);
+ arg.refs = refs;
arg.records = rewritten;
arg.len = logs_nr;
arg.stack = stack,
@@ -2216,14 +2306,16 @@ done:
struct ref_storage_be refs_be_reftable = {
.name = "reftable",
.init = reftable_be_init,
- .init_db = reftable_be_init_db,
+ .release = reftable_be_release,
+ .create_on_disk = reftable_be_create_on_disk,
+ .remove_on_disk = reftable_be_remove_on_disk,
+
.transaction_prepare = reftable_be_transaction_prepare,
.transaction_finish = reftable_be_transaction_finish,
.transaction_abort = reftable_be_transaction_abort,
.initial_transaction_commit = reftable_be_initial_transaction_commit,
.pack_refs = reftable_be_pack_refs,
- .create_symref = reftable_be_create_symref,
.rename_ref = reftable_be_rename_ref,
.copy_ref = reftable_be_copy_ref,
diff --git a/refspec.c b/refspec.c
index d60932f4de..ec90ab349a 100644
--- a/refspec.c
+++ b/refspec.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "gettext.h"
#include "hash.h"
@@ -7,19 +9,6 @@
#include "refspec.h"
#include "strbuf.h"
-static struct refspec_item s_tag_refspec = {
- .force = 0,
- .pattern = 1,
- .matching = 0,
- .exact_sha1 = 0,
- .negative = 0,
- .src = "refs/tags/*",
- .dst = "refs/tags/*",
-};
-
-/* See TAG_REFSPEC for the string version */
-const struct refspec_item *tag_refspec = &s_tag_refspec;
-
/*
* Parses the provided refspec 'refspec' and populates the refspec_item 'item'.
* Returns 1 if successful and 0 if the refspec is invalid.
diff --git a/refspec.h b/refspec.h
index 8c0c446993..754be45cee 100644
--- a/refspec.h
+++ b/refspec.h
@@ -2,7 +2,6 @@
#define REFSPEC_H
#define TAG_REFSPEC "refs/tags/*:refs/tags/*"
-extern const struct refspec_item *tag_refspec;
/**
* A struct refspec_item holds the parsed interpretation of a refspec. If it
diff --git a/reftable/basics.c b/reftable/basics.c
index fea711db7e..0058619ca6 100644
--- a/reftable/basics.c
+++ b/reftable/basics.c
@@ -67,9 +67,9 @@ void free_names(char **a)
reftable_free(a);
}
-size_t names_length(char **names)
+size_t names_length(const char **names)
{
- char **p = names;
+ const char **p = names;
while (*p)
p++;
return p - names;
@@ -102,15 +102,12 @@ void parse_names(char *buf, int size, char ***namesp)
*namesp = names;
}
-int names_equal(char **a, char **b)
+int names_equal(const char **a, const char **b)
{
- int i = 0;
- for (; a[i] && b[i]; i++) {
- if (strcmp(a[i], b[i])) {
+ size_t i = 0;
+ for (; a[i] && b[i]; i++)
+ if (strcmp(a[i], b[i]))
return 0;
- }
- }
-
return a[i] == b[i];
}
diff --git a/reftable/basics.h b/reftable/basics.h
index 523ecd5307..c8fec68d4e 100644
--- a/reftable/basics.h
+++ b/reftable/basics.h
@@ -42,10 +42,10 @@ void free_names(char **a);
void parse_names(char *buf, int size, char ***namesp);
/* compares two NULL-terminated arrays of strings. */
-int names_equal(char **a, char **b);
+int names_equal(const char **a, const char **b);
/* returns the array size of a NULL-terminated array of strings. */
-size_t names_length(char **names);
+size_t names_length(const char **names);
/* Allocation routines; they invoke the functions set through
* reftable_set_alloc() */
diff --git a/reftable/basics_test.c b/reftable/basics_test.c
deleted file mode 100644
index 997c4d9e01..0000000000
--- a/reftable/basics_test.c
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
-Copyright 2020 Google LLC
-
-Use of this source code is governed by a BSD-style
-license that can be found in the LICENSE file or at
-https://developers.google.com/open-source/licenses/bsd
-*/
-
-#include "system.h"
-
-#include "basics.h"
-#include "test_framework.h"
-#include "reftable-tests.h"
-
-struct integer_needle_lesseq_args {
- int needle;
- int *haystack;
-};
-
-static int integer_needle_lesseq(size_t i, void *_args)
-{
- struct integer_needle_lesseq_args *args = _args;
- return args->needle <= args->haystack[i];
-}
-
-static void test_binsearch(void)
-{
- int haystack[] = { 2, 4, 6, 8, 10 };
- struct {
- int needle;
- size_t expected_idx;
- } testcases[] = {
- {-9000, 0},
- {-1, 0},
- {0, 0},
- {2, 0},
- {3, 1},
- {4, 1},
- {7, 3},
- {9, 4},
- {10, 4},
- {11, 5},
- {9000, 5},
- };
- size_t i = 0;
-
- for (i = 0; i < ARRAY_SIZE(testcases); i++) {
- struct integer_needle_lesseq_args args = {
- .haystack = haystack,
- .needle = testcases[i].needle,
- };
- size_t idx;
-
- idx = binsearch(ARRAY_SIZE(haystack), &integer_needle_lesseq, &args);
- EXPECT(idx == testcases[i].expected_idx);
- }
-}
-
-static void test_names_length(void)
-{
- char *a[] = { "a", "b", NULL };
- EXPECT(names_length(a) == 2);
-}
-
-static void test_parse_names_normal(void)
-{
- char in[] = "a\nb\n";
- char **out = NULL;
- parse_names(in, strlen(in), &out);
- EXPECT(!strcmp(out[0], "a"));
- EXPECT(!strcmp(out[1], "b"));
- EXPECT(!out[2]);
- free_names(out);
-}
-
-static void test_parse_names_drop_empty(void)
-{
- char in[] = "a\n\n";
- char **out = NULL;
- parse_names(in, strlen(in), &out);
- EXPECT(!strcmp(out[0], "a"));
- EXPECT(!out[1]);
- free_names(out);
-}
-
-static void test_common_prefix(void)
-{
- struct strbuf s1 = STRBUF_INIT;
- struct strbuf s2 = STRBUF_INIT;
- strbuf_addstr(&s1, "abcdef");
- strbuf_addstr(&s2, "abc");
- EXPECT(common_prefix_size(&s1, &s2) == 3);
- strbuf_release(&s1);
- strbuf_release(&s2);
-}
-
-int basics_test_main(int argc, const char *argv[])
-{
- RUN_TEST(test_common_prefix);
- RUN_TEST(test_parse_names_normal);
- RUN_TEST(test_parse_names_drop_empty);
- RUN_TEST(test_binsearch);
- RUN_TEST(test_names_length);
- return 0;
-}
diff --git a/reftable/block.c b/reftable/block.c
index 3e87460cba..00030eee06 100644
--- a/reftable/block.c
+++ b/reftable/block.c
@@ -76,6 +76,10 @@ void block_writer_init(struct block_writer *bw, uint8_t typ, uint8_t *buf,
bw->entries = 0;
bw->restart_len = 0;
bw->last_key.len = 0;
+ if (!bw->zstream) {
+ REFTABLE_CALLOC_ARRAY(bw->zstream, 1);
+ deflateInit(bw->zstream, 9);
+ }
}
uint8_t block_writer_type(struct block_writer *bw)
@@ -139,39 +143,52 @@ int block_writer_finish(struct block_writer *w)
w->next += 2;
put_be24(w->buf + 1 + w->header_off, w->next);
+ /*
+ * Log records are stored zlib-compressed. Note that the compression
+ * also spans over the restart points we have just written.
+ */
if (block_writer_type(w) == BLOCK_TYPE_LOG) {
int block_header_skip = 4 + w->header_off;
- uLongf src_len = w->next - block_header_skip;
- uLongf dest_cap = src_len * 1.001 + 12;
- uint8_t *compressed;
-
- REFTABLE_ALLOC_ARRAY(compressed, dest_cap);
-
- while (1) {
- uLongf out_dest_len = dest_cap;
- int zresult = compress2(compressed, &out_dest_len,
- w->buf + block_header_skip,
- src_len, 9);
- if (zresult == Z_BUF_ERROR && dest_cap < LONG_MAX) {
- dest_cap *= 2;
- compressed =
- reftable_realloc(compressed, dest_cap);
- if (compressed)
- continue;
- }
-
- if (Z_OK != zresult) {
- reftable_free(compressed);
- return REFTABLE_ZLIB_ERROR;
- }
-
- memcpy(w->buf + block_header_skip, compressed,
- out_dest_len);
- w->next = out_dest_len + block_header_skip;
- reftable_free(compressed);
- break;
- }
+ uLongf src_len = w->next - block_header_skip, compressed_len;
+ int ret;
+
+ ret = deflateReset(w->zstream);
+ if (ret != Z_OK)
+ return REFTABLE_ZLIB_ERROR;
+
+ /*
+ * Precompute the upper bound of how many bytes the compressed
+ * data may end up with. Combined with `Z_FINISH`, `deflate()`
+ * is guaranteed to return `Z_STREAM_END`.
+ */
+ compressed_len = deflateBound(w->zstream, src_len);
+ REFTABLE_ALLOC_GROW(w->compressed, compressed_len, w->compressed_cap);
+
+ w->zstream->next_out = w->compressed;
+ w->zstream->avail_out = compressed_len;
+ w->zstream->next_in = w->buf + block_header_skip;
+ w->zstream->avail_in = src_len;
+
+ /*
+ * We want to perform all decompression in a single step, which
+ * is why we can pass Z_FINISH here. As we have precomputed the
+ * deflated buffer's size via `deflateBound()` this function is
+ * guaranteed to succeed according to the zlib documentation.
+ */
+ ret = deflate(w->zstream, Z_FINISH);
+ if (ret != Z_STREAM_END)
+ return REFTABLE_ZLIB_ERROR;
+
+ /*
+ * Overwrite the uncompressed data we have already written and
+ * adjust the `next` pointer to point right after the
+ * compressed data.
+ */
+ memcpy(w->buf + block_header_skip, w->compressed,
+ w->zstream->total_out);
+ w->next = w->zstream->total_out + block_header_skip;
}
+
return w->next;
}
@@ -309,9 +326,9 @@ int block_reader_first_key(const struct block_reader *br, struct strbuf *key)
return 0;
}
-static uint32_t block_reader_restart_offset(const struct block_reader *br, int i)
+static uint32_t block_reader_restart_offset(const struct block_reader *br, size_t idx)
{
- return get_be24(br->restart_bytes + 3 * i);
+ return get_be24(br->restart_bytes + 3 * idx);
}
void block_iter_seek_start(struct block_iter *it, const struct block_reader *br)
@@ -514,7 +531,10 @@ done:
void block_writer_release(struct block_writer *bw)
{
+ deflateEnd(bw->zstream);
+ FREE_AND_NULL(bw->zstream);
FREE_AND_NULL(bw->restarts);
+ FREE_AND_NULL(bw->compressed);
strbuf_release(&bw->last_key);
/* the block is not owned. */
}
diff --git a/reftable/block.h b/reftable/block.h
index ea4384a7e2..1c8f25ee6e 100644
--- a/reftable/block.h
+++ b/reftable/block.h
@@ -18,6 +18,10 @@ https://developers.google.com/open-source/licenses/bsd
* allocation overhead.
*/
struct block_writer {
+ z_stream *zstream;
+ unsigned char *compressed;
+ size_t compressed_cap;
+
uint8_t *buf;
uint32_t block_size;
@@ -25,7 +29,7 @@ struct block_writer {
uint32_t header_off;
/* How often to restart keys. */
- int restart_interval;
+ uint16_t restart_interval;
int hash_size;
/* Offset of next uint8_t to write. */
diff --git a/reftable/block_test.c b/reftable/block_test.c
index 26a9cfbc83..90aecd5a7c 100644
--- a/reftable/block_test.c
+++ b/reftable/block_test.c
@@ -42,7 +42,7 @@ static void test_block_read_write(void)
block_writer_init(&bw, BLOCK_TYPE_REF, block.data, block_size,
header_off, hash_size(GIT_SHA1_FORMAT_ID));
- rec.u.ref.refname = "";
+ rec.u.ref.refname = (char *) "";
rec.u.ref.value_type = REFTABLE_REF_DELETION;
n = block_writer_add(&bw, &rec);
EXPECT(n == REFTABLE_API_ERROR);
diff --git a/reftable/constants.h b/reftable/constants.h
index 5eee72c4c1..f6beb843eb 100644
--- a/reftable/constants.h
+++ b/reftable/constants.h
@@ -17,5 +17,6 @@ https://developers.google.com/open-source/licenses/bsd
#define MAX_RESTARTS ((1 << 16) - 1)
#define DEFAULT_BLOCK_SIZE 4096
+#define DEFAULT_GEOMETRIC_FACTOR 2
#endif
diff --git a/reftable/dump.c b/reftable/dump.c
index 26e0393c7d..dd65d9e8bb 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-ll.h"
+#include "hash.h"
#include "reftable-blocksource.h"
#include "reftable-error.h"
@@ -27,9 +27,9 @@ https://developers.google.com/open-source/licenses/bsd
static int compact_stack(const char *stackdir)
{
struct reftable_stack *stack = NULL;
- struct reftable_write_options cfg = { 0 };
+ struct reftable_write_options opts = { 0 };
- int err = reftable_new_stack(&stack, stackdir, cfg);
+ int err = reftable_new_stack(&stack, stackdir, &opts);
if (err < 0)
goto done;
@@ -48,6 +48,7 @@ static void print_help(void)
printf("usage: dump [-cst] arg\n\n"
"options: \n"
" -c compact\n"
+ " -b dump blocks\n"
" -t dump table\n"
" -s dump stack\n"
" -6 sha256 hash format\n"
@@ -58,6 +59,7 @@ static void print_help(void)
int reftable_dump_main(int argc, char *const *argv)
{
int err = 0;
+ int opt_dump_blocks = 0;
int opt_dump_table = 0;
int opt_dump_stack = 0;
int opt_compact = 0;
@@ -67,6 +69,8 @@ int reftable_dump_main(int argc, char *const *argv)
for (; argc > 1; argv++, argc--)
if (*argv[1] != '-')
break;
+ else if (!strcmp("-b", argv[1]))
+ opt_dump_blocks = 1;
else if (!strcmp("-t", argv[1]))
opt_dump_table = 1;
else if (!strcmp("-6", argv[1]))
@@ -88,7 +92,9 @@ int reftable_dump_main(int argc, char *const *argv)
arg = argv[1];
- if (opt_dump_table) {
+ if (opt_dump_blocks) {
+ err = reftable_reader_print_blocks(arg);
+ } else if (opt_dump_table) {
err = reftable_reader_print_file(arg);
} else if (opt_dump_stack) {
err = reftable_stack_print_directory(arg, opt_hash_id);
diff --git a/reftable/error.c b/reftable/error.c
index cfb7a0fda4..a25f28a43e 100644
--- a/reftable/error.c
+++ b/reftable/error.c
@@ -27,8 +27,6 @@ const char *reftable_error_str(int err)
return "misuse of the reftable API";
case REFTABLE_ZLIB_ERROR:
return "zlib failure";
- case REFTABLE_NAME_CONFLICT:
- return "file/directory conflict";
case REFTABLE_EMPTY_TABLE_ERROR:
return "wrote empty table";
case REFTABLE_REFNAME_ERROR:
diff --git a/reftable/generic.c b/reftable/generic.c
index b9f1c7c18a..28ae26145e 100644
--- a/reftable/generic.c
+++ b/reftable/generic.c
@@ -12,32 +12,66 @@ https://developers.google.com/open-source/licenses/bsd
#include "reftable-iterator.h"
#include "reftable-generic.h"
-int reftable_table_seek_ref(struct reftable_table *tab,
- struct reftable_iterator *it, const char *name)
+void table_init_iter(struct reftable_table *tab,
+ struct reftable_iterator *it,
+ uint8_t typ)
{
- struct reftable_record rec = { .type = BLOCK_TYPE_REF,
- .u.ref = {
- .refname = (char *)name,
- } };
- return tab->ops->seek_record(tab->table_arg, it, &rec);
+
+ tab->ops->init_iter(tab->table_arg, it, typ);
+}
+
+void reftable_table_init_ref_iter(struct reftable_table *tab,
+ struct reftable_iterator *it)
+{
+ table_init_iter(tab, it, BLOCK_TYPE_REF);
+}
+
+void reftable_table_init_log_iter(struct reftable_table *tab,
+ struct reftable_iterator *it)
+{
+ table_init_iter(tab, it, BLOCK_TYPE_LOG);
+}
+
+int reftable_iterator_seek_ref(struct reftable_iterator *it,
+ const char *name)
+{
+ struct reftable_record want = {
+ .type = BLOCK_TYPE_REF,
+ .u.ref = {
+ .refname = (char *)name,
+ },
+ };
+ return it->ops->seek(it->iter_arg, &want);
+}
+
+int reftable_iterator_seek_log_at(struct reftable_iterator *it,
+ const char *name, uint64_t update_index)
+{
+ struct reftable_record want = {
+ .type = BLOCK_TYPE_LOG,
+ .u.log = {
+ .refname = (char *)name,
+ .update_index = update_index,
+ },
+ };
+ return it->ops->seek(it->iter_arg, &want);
}
-int reftable_table_seek_log(struct reftable_table *tab,
- struct reftable_iterator *it, const char *name)
+int reftable_iterator_seek_log(struct reftable_iterator *it,
+ const char *name)
{
- struct reftable_record rec = { .type = BLOCK_TYPE_LOG,
- .u.log = {
- .refname = (char *)name,
- .update_index = ~((uint64_t)0),
- } };
- return tab->ops->seek_record(tab->table_arg, it, &rec);
+ return reftable_iterator_seek_log_at(it, name, ~((uint64_t) 0));
}
int reftable_table_read_ref(struct reftable_table *tab, const char *name,
struct reftable_ref_record *ref)
{
struct reftable_iterator it = { NULL };
- int err = reftable_table_seek_ref(tab, &it, name);
+ int err;
+
+ reftable_table_init_ref_iter(tab, &it);
+
+ err = reftable_iterator_seek_ref(&it, name);
if (err)
goto done;
@@ -62,10 +96,13 @@ int reftable_table_print(struct reftable_table *tab) {
struct reftable_ref_record ref = { NULL };
struct reftable_log_record log = { NULL };
uint32_t hash_id = reftable_table_hash_id(tab);
- int err = reftable_table_seek_ref(tab, &it, "");
- if (err < 0) {
+ int err;
+
+ reftable_table_init_ref_iter(tab, &it);
+
+ err = reftable_iterator_seek_ref(&it, "");
+ if (err < 0)
return err;
- }
while (1) {
err = reftable_iterator_next_ref(&it, &ref);
@@ -80,10 +117,12 @@ int reftable_table_print(struct reftable_table *tab) {
reftable_iterator_destroy(&it);
reftable_ref_record_release(&ref);
- err = reftable_table_seek_log(tab, &it, "");
- if (err < 0) {
+ reftable_table_init_log_iter(tab, &it);
+
+ err = reftable_iterator_seek_log(&it, "");
+ if (err < 0)
return err;
- }
+
while (1) {
err = reftable_iterator_next_log(&it, &log);
if (err > 0) {
@@ -152,11 +191,21 @@ int reftable_iterator_next_log(struct reftable_iterator *it,
return err;
}
+int iterator_seek(struct reftable_iterator *it, struct reftable_record *want)
+{
+ return it->ops->seek(it->iter_arg, want);
+}
+
int iterator_next(struct reftable_iterator *it, struct reftable_record *rec)
{
return it->ops->next(it->iter_arg, rec);
}
+static int empty_iterator_seek(void *arg, struct reftable_record *want)
+{
+ return 0;
+}
+
static int empty_iterator_next(void *arg, struct reftable_record *rec)
{
return 1;
@@ -167,6 +216,7 @@ static void empty_iterator_close(void *arg)
}
static struct reftable_iterator_vtable empty_vtable = {
+ .seek = &empty_iterator_seek,
.next = &empty_iterator_next,
.close = &empty_iterator_close,
};
diff --git a/reftable/generic.h b/reftable/generic.h
index 98886a0640..8341fa570e 100644
--- a/reftable/generic.h
+++ b/reftable/generic.h
@@ -14,19 +14,24 @@ https://developers.google.com/open-source/licenses/bsd
/* generic interface to reftables */
struct reftable_table_vtable {
- int (*seek_record)(void *tab, struct reftable_iterator *it,
- struct reftable_record *);
+ void (*init_iter)(void *tab, struct reftable_iterator *it, uint8_t typ);
uint32_t (*hash_id)(void *tab);
uint64_t (*min_update_index)(void *tab);
uint64_t (*max_update_index)(void *tab);
};
+void table_init_iter(struct reftable_table *tab,
+ struct reftable_iterator *it,
+ uint8_t typ);
+
struct reftable_iterator_vtable {
+ int (*seek)(void *iter_arg, struct reftable_record *want);
int (*next)(void *iter_arg, struct reftable_record *rec);
void (*close)(void *iter_arg);
};
void iterator_set_empty(struct reftable_iterator *it);
+int iterator_seek(struct reftable_iterator *it, struct reftable_record *want);
int iterator_next(struct reftable_iterator *it, struct reftable_record *rec);
#endif
diff --git a/reftable/iter.c b/reftable/iter.c
index aa9ac199b1..fddea31e51 100644
--- a/reftable/iter.c
+++ b/reftable/iter.c
@@ -23,6 +23,13 @@ static void filtering_ref_iterator_close(void *iter_arg)
reftable_iterator_destroy(&fri->it);
}
+static int filtering_ref_iterator_seek(void *iter_arg,
+ struct reftable_record *want)
+{
+ struct filtering_ref_iterator *fri = iter_arg;
+ return iterator_seek(&fri->it, want);
+}
+
static int filtering_ref_iterator_next(void *iter_arg,
struct reftable_record *rec)
{
@@ -38,11 +45,11 @@ static int filtering_ref_iterator_next(void *iter_arg,
if (fri->double_check) {
struct reftable_iterator it = { NULL };
- err = reftable_table_seek_ref(&fri->tab, &it,
- ref->refname);
- if (err == 0) {
+ reftable_table_init_ref_iter(&fri->tab, &it);
+
+ err = reftable_iterator_seek_ref(&it, ref->refname);
+ if (err == 0)
err = reftable_iterator_next_ref(&it, ref);
- }
reftable_iterator_destroy(&it);
@@ -73,6 +80,7 @@ static int filtering_ref_iterator_next(void *iter_arg,
}
static struct reftable_iterator_vtable filtering_ref_iterator_vtable = {
+ .seek = &filtering_ref_iterator_seek,
.next = &filtering_ref_iterator_next,
.close = &filtering_ref_iterator_close,
};
@@ -119,6 +127,12 @@ static int indexed_table_ref_iter_next_block(struct indexed_table_ref_iter *it)
return 0;
}
+static int indexed_table_ref_iter_seek(void *p, struct reftable_record *want)
+{
+ BUG("seeking indexed table is not supported");
+ return -1;
+}
+
static int indexed_table_ref_iter_next(void *p, struct reftable_record *rec)
{
struct indexed_table_ref_iter *it = p;
@@ -175,6 +189,7 @@ int new_indexed_table_ref_iter(struct indexed_table_ref_iter **dest,
}
static struct reftable_iterator_vtable indexed_table_ref_iter_vtable = {
+ .seek = &indexed_table_ref_iter_seek,
.next = &indexed_table_ref_iter_next,
.close = &indexed_table_ref_iter_close,
};
diff --git a/reftable/merged.c b/reftable/merged.c
index f85a24c678..6adce44f4b 100644
--- a/reftable/merged.c
+++ b/reftable/merged.c
@@ -25,34 +25,25 @@ struct merged_subiter {
struct merged_iter {
struct merged_subiter *subiters;
struct merged_iter_pqueue pq;
- uint32_t hash_id;
size_t stack_len;
- uint8_t typ;
int suppress_deletions;
ssize_t advance_index;
};
-static int merged_iter_init(struct merged_iter *mi)
+static void merged_iter_init(struct merged_iter *mi,
+ struct reftable_merged_table *mt,
+ uint8_t typ)
{
- for (size_t i = 0; i < mi->stack_len; i++) {
- struct pq_entry e = {
- .index = i,
- .rec = &mi->subiters[i].rec,
- };
- int err;
-
- reftable_record_init(&mi->subiters[i].rec, mi->typ);
- err = iterator_next(&mi->subiters[i].iter,
- &mi->subiters[i].rec);
- if (err < 0)
- return err;
- if (err > 0)
- continue;
+ memset(mi, 0, sizeof(*mi));
+ mi->advance_index = -1;
+ mi->suppress_deletions = mt->suppress_deletions;
- merged_iter_pqueue_add(&mi->pq, &e);
+ REFTABLE_CALLOC_ARRAY(mi->subiters, mt->stack_len);
+ for (size_t i = 0; i < mt->stack_len; i++) {
+ reftable_record_init(&mi->subiters[i].rec, typ);
+ table_init_iter(&mt->stack[i], &mi->subiters[i].iter, typ);
}
-
- return 0;
+ mi->stack_len = mt->stack_len;
}
static void merged_iter_close(void *p)
@@ -83,6 +74,27 @@ static int merged_iter_advance_subiter(struct merged_iter *mi, size_t idx)
return 0;
}
+static int merged_iter_seek(struct merged_iter *mi, struct reftable_record *want)
+{
+ int err;
+
+ mi->advance_index = -1;
+
+ for (size_t i = 0; i < mi->stack_len; i++) {
+ err = iterator_seek(&mi->subiters[i].iter, want);
+ if (err < 0)
+ return err;
+ if (err > 0)
+ continue;
+
+ err = merged_iter_advance_subiter(mi, i);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
static int merged_iter_next_entry(struct merged_iter *mi,
struct reftable_record *rec)
{
@@ -148,6 +160,11 @@ static int merged_iter_next_entry(struct merged_iter *mi,
return 0;
}
+static int merged_iter_seek_void(void *it, struct reftable_record *want)
+{
+ return merged_iter_seek(it, want);
+}
+
static int merged_iter_next_void(void *p, struct reftable_record *rec)
{
struct merged_iter *mi = p;
@@ -162,6 +179,7 @@ static int merged_iter_next_void(void *p, struct reftable_record *rec)
}
static struct reftable_iterator_vtable merged_iter_vtable = {
+ .seek = merged_iter_seek_void,
.next = &merged_iter_next_void,
.close = &merged_iter_close,
};
@@ -207,19 +225,11 @@ int reftable_new_merged_table(struct reftable_merged_table **dest,
return 0;
}
-/* clears the list of subtable, without affecting the readers themselves. */
-void merged_table_release(struct reftable_merged_table *mt)
-{
- FREE_AND_NULL(mt->stack);
- mt->stack_len = 0;
-}
-
void reftable_merged_table_free(struct reftable_merged_table *mt)
{
- if (!mt) {
+ if (!mt)
return;
- }
- merged_table_release(mt);
+ FREE_AND_NULL(mt->stack);
reftable_free(mt);
}
@@ -235,81 +245,13 @@ reftable_merged_table_min_update_index(struct reftable_merged_table *mt)
return mt->min;
}
-static int reftable_table_seek_record(struct reftable_table *tab,
- struct reftable_iterator *it,
- struct reftable_record *rec)
-{
- return tab->ops->seek_record(tab->table_arg, it, rec);
-}
-
-static int merged_table_seek_record(struct reftable_merged_table *mt,
- struct reftable_iterator *it,
- struct reftable_record *rec)
-{
- struct merged_iter merged = {
- .typ = reftable_record_type(rec),
- .hash_id = mt->hash_id,
- .suppress_deletions = mt->suppress_deletions,
- .advance_index = -1,
- };
- struct merged_iter *p;
- int err;
-
- REFTABLE_CALLOC_ARRAY(merged.subiters, mt->stack_len);
- for (size_t i = 0; i < mt->stack_len; i++) {
- err = reftable_table_seek_record(&mt->stack[i],
- &merged.subiters[merged.stack_len].iter, rec);
- if (err < 0)
- goto out;
- if (!err)
- merged.stack_len++;
- }
-
- err = merged_iter_init(&merged);
- if (err < 0)
- goto out;
-
- p = reftable_malloc(sizeof(struct merged_iter));
- *p = merged;
- iterator_from_merged_iter(it, p);
-
-out:
- if (err < 0)
- merged_iter_close(&merged);
- return err;
-}
-
-int reftable_merged_table_seek_ref(struct reftable_merged_table *mt,
- struct reftable_iterator *it,
- const char *name)
-{
- struct reftable_record rec = {
- .type = BLOCK_TYPE_REF,
- .u.ref = {
- .refname = (char *)name,
- },
- };
- return merged_table_seek_record(mt, it, &rec);
-}
-
-int reftable_merged_table_seek_log_at(struct reftable_merged_table *mt,
- struct reftable_iterator *it,
- const char *name, uint64_t update_index)
-{
- struct reftable_record rec = { .type = BLOCK_TYPE_LOG,
- .u.log = {
- .refname = (char *)name,
- .update_index = update_index,
- } };
- return merged_table_seek_record(mt, it, &rec);
-}
-
-int reftable_merged_table_seek_log(struct reftable_merged_table *mt,
- struct reftable_iterator *it,
- const char *name)
+void merged_table_init_iter(struct reftable_merged_table *mt,
+ struct reftable_iterator *it,
+ uint8_t typ)
{
- uint64_t max = ~((uint64_t)0);
- return reftable_merged_table_seek_log_at(mt, it, name, max);
+ struct merged_iter *mi = reftable_malloc(sizeof(*mi));
+ merged_iter_init(mi, mt, typ);
+ iterator_from_merged_iter(it, mi);
}
uint32_t reftable_merged_table_hash_id(struct reftable_merged_table *mt)
@@ -317,11 +259,11 @@ uint32_t reftable_merged_table_hash_id(struct reftable_merged_table *mt)
return mt->hash_id;
}
-static int reftable_merged_table_seek_void(void *tab,
- struct reftable_iterator *it,
- struct reftable_record *rec)
+static void reftable_merged_table_init_iter_void(void *tab,
+ struct reftable_iterator *it,
+ uint8_t typ)
{
- return merged_table_seek_record(tab, it, rec);
+ merged_table_init_iter(tab, it, typ);
}
static uint32_t reftable_merged_table_hash_id_void(void *tab)
@@ -340,7 +282,7 @@ static uint64_t reftable_merged_table_max_update_index_void(void *tab)
}
static struct reftable_table_vtable merged_table_vtable = {
- .seek_record = reftable_merged_table_seek_void,
+ .init_iter = reftable_merged_table_init_iter_void,
.hash_id = reftable_merged_table_hash_id_void,
.min_update_index = reftable_merged_table_min_update_index_void,
.max_update_index = reftable_merged_table_max_update_index_void,
diff --git a/reftable/merged.h b/reftable/merged.h
index a2571dbc99..2efe571da6 100644
--- a/reftable/merged.h
+++ b/reftable/merged.h
@@ -24,6 +24,10 @@ struct reftable_merged_table {
uint64_t max;
};
-void merged_table_release(struct reftable_merged_table *mt);
+struct reftable_iterator;
+
+void merged_table_init_iter(struct reftable_merged_table *mt,
+ struct reftable_iterator *it,
+ uint8_t typ);
#endif
diff --git a/reftable/merged_test.c b/reftable/merged_test.c
index 530fc82d1c..a9d6661c13 100644
--- a/reftable/merged_test.c
+++ b/reftable/merged_test.c
@@ -12,6 +12,7 @@ https://developers.google.com/open-source/licenses/bsd
#include "basics.h"
#include "blocksource.h"
+#include "constants.h"
#include "reader.h"
#include "record.h"
#include "test_framework.h"
@@ -124,13 +125,13 @@ static void readers_destroy(struct reftable_reader **readers, size_t n)
static void test_merged_between(void)
{
struct reftable_ref_record r1[] = { {
- .refname = "b",
+ .refname = (char *) "b",
.update_index = 1,
.value_type = REFTABLE_REF_VAL1,
.value.val1 = { 1, 2, 3, 0 },
} };
struct reftable_ref_record r2[] = { {
- .refname = "a",
+ .refname = (char *) "a",
.update_index = 2,
.value_type = REFTABLE_REF_DELETION,
} };
@@ -145,7 +146,10 @@ static void test_merged_between(void)
int i;
struct reftable_ref_record ref = { NULL };
struct reftable_iterator it = { NULL };
- int err = reftable_merged_table_seek_ref(mt, &it, "a");
+ int err;
+
+ merged_table_init_iter(mt, &it, BLOCK_TYPE_REF);
+ err = reftable_iterator_seek_ref(&it, "a");
EXPECT_ERR(err);
err = reftable_iterator_next_ref(&it, &ref);
@@ -165,38 +169,38 @@ static void test_merged(void)
{
struct reftable_ref_record r1[] = {
{
- .refname = "a",
+ .refname = (char *) "a",
.update_index = 1,
.value_type = REFTABLE_REF_VAL1,
.value.val1 = { 1 },
},
{
- .refname = "b",
+ .refname = (char *) "b",
.update_index = 1,
.value_type = REFTABLE_REF_VAL1,
.value.val1 = { 1 },
},
{
- .refname = "c",
+ .refname = (char *) "c",
.update_index = 1,
.value_type = REFTABLE_REF_VAL1,
.value.val1 = { 1 },
}
};
struct reftable_ref_record r2[] = { {
- .refname = "a",
+ .refname = (char *) "a",
.update_index = 2,
.value_type = REFTABLE_REF_DELETION,
} };
struct reftable_ref_record r3[] = {
{
- .refname = "c",
+ .refname = (char *) "c",
.update_index = 3,
.value_type = REFTABLE_REF_VAL1,
.value.val1 = { 2 },
},
{
- .refname = "d",
+ .refname = (char *) "d",
.update_index = 3,
.value_type = REFTABLE_REF_VAL1,
.value.val1 = { 1 },
@@ -217,14 +221,15 @@ static void test_merged(void)
struct reftable_reader **readers = NULL;
struct reftable_merged_table *mt =
merged_table_from_records(refs, &bs, &readers, sizes, bufs, 3);
-
struct reftable_iterator it = { NULL };
- int err = reftable_merged_table_seek_ref(mt, &it, "a");
+ int err;
struct reftable_ref_record *out = NULL;
size_t len = 0;
size_t cap = 0;
int i = 0;
+ merged_table_init_iter(mt, &it, BLOCK_TYPE_REF);
+ err = reftable_iterator_seek_ref(&it, "a");
EXPECT_ERR(err);
EXPECT(reftable_merged_table_hash_id(mt) == GIT_SHA1_FORMAT_ID);
EXPECT(reftable_merged_table_min_update_index(mt) == 1);
@@ -291,46 +296,46 @@ static void test_merged_logs(void)
{
struct reftable_log_record r1[] = {
{
- .refname = "a",
+ .refname = (char *) "a",
.update_index = 2,
.value_type = REFTABLE_LOG_UPDATE,
.value.update = {
.old_hash = { 2 },
/* deletion */
- .name = "jane doe",
- .email = "jane@invalid",
- .message = "message2",
+ .name = (char *) "jane doe",
+ .email = (char *) "jane@invalid",
+ .message = (char *) "message2",
}
},
{
- .refname = "a",
+ .refname = (char *) "a",
.update_index = 1,
.value_type = REFTABLE_LOG_UPDATE,
.value.update = {
.old_hash = { 1 },
.new_hash = { 2 },
- .name = "jane doe",
- .email = "jane@invalid",
- .message = "message1",
+ .name = (char *) "jane doe",
+ .email = (char *) "jane@invalid",
+ .message = (char *) "message1",
}
},
};
struct reftable_log_record r2[] = {
{
- .refname = "a",
+ .refname = (char *) "a",
.update_index = 3,
.value_type = REFTABLE_LOG_UPDATE,
.value.update = {
.new_hash = { 3 },
- .name = "jane doe",
- .email = "jane@invalid",
- .message = "message3",
+ .name = (char *) "jane doe",
+ .email = (char *) "jane@invalid",
+ .message = (char *) "message3",
}
},
};
struct reftable_log_record r3[] = {
{
- .refname = "a",
+ .refname = (char *) "a",
.update_index = 2,
.value_type = REFTABLE_LOG_DELETION,
},
@@ -348,14 +353,15 @@ static void test_merged_logs(void)
struct reftable_reader **readers = NULL;
struct reftable_merged_table *mt = merged_table_from_log_records(
logs, &bs, &readers, sizes, bufs, 3);
-
struct reftable_iterator it = { NULL };
- int err = reftable_merged_table_seek_log(mt, &it, "a");
+ int err;
struct reftable_log_record *out = NULL;
size_t len = 0;
size_t cap = 0;
int i = 0;
+ merged_table_init_iter(mt, &it, BLOCK_TYPE_LOG);
+ err = reftable_iterator_seek_log(&it, "a");
EXPECT_ERR(err);
EXPECT(reftable_merged_table_hash_id(mt) == GIT_SHA1_FORMAT_ID);
EXPECT(reftable_merged_table_min_update_index(mt) == 1);
@@ -377,7 +383,8 @@ static void test_merged_logs(void)
GIT_SHA1_RAWSZ));
}
- err = reftable_merged_table_seek_log_at(mt, &it, "a", 2);
+ merged_table_init_iter(mt, &it, BLOCK_TYPE_LOG);
+ err = reftable_iterator_seek_log_at(&it, "a", 2);
EXPECT_ERR(err);
reftable_log_record_release(&out[0]);
err = reftable_iterator_next_log(&it, &out[0]);
@@ -406,7 +413,7 @@ static void test_default_write_opts(void)
reftable_new_writer(&strbuf_add_void, &noop_flush, &buf, &opts);
struct reftable_ref_record rec = {
- .refname = "master",
+ .refname = (char *) "master",
.update_index = 1,
};
int err;
diff --git a/reftable/reader.c b/reftable/reader.c
index 481dff10d4..29c99e2269 100644
--- a/reftable/reader.c
+++ b/reftable/reader.c
@@ -224,8 +224,14 @@ struct table_iter {
struct block_iter bi;
int is_finished;
};
-#define TABLE_ITER_INIT { \
- .bi = BLOCK_ITER_INIT \
+
+static int table_iter_init(struct table_iter *ti, struct reftable_reader *r)
+{
+ struct block_iter bi = BLOCK_ITER_INIT;
+ memset(ti, 0, sizeof(*ti));
+ ti->r = r;
+ ti->bi = bi;
+ return 0;
}
static int table_iter_next_in_block(struct table_iter *ti,
@@ -363,50 +369,23 @@ static int table_iter_next(struct table_iter *ti, struct reftable_record *rec)
}
}
-static int table_iter_next_void(void *ti, struct reftable_record *rec)
-{
- return table_iter_next(ti, rec);
-}
-
-static void table_iter_close_void(void *ti)
-{
- table_iter_close(ti);
-}
-
-static struct reftable_iterator_vtable table_iter_vtable = {
- .next = &table_iter_next_void,
- .close = &table_iter_close_void,
-};
-
-static void iterator_from_table_iter(struct reftable_iterator *it,
- struct table_iter *ti)
-{
- assert(!it->ops);
- it->iter_arg = ti;
- it->ops = &table_iter_vtable;
-}
-
-static int reader_table_iter_at(struct reftable_reader *r,
- struct table_iter *ti, uint64_t off,
- uint8_t typ)
+static int table_iter_seek_to(struct table_iter *ti, uint64_t off, uint8_t typ)
{
int err;
- err = reader_init_block_reader(r, &ti->br, off, typ);
+ err = reader_init_block_reader(ti->r, &ti->br, off, typ);
if (err != 0)
return err;
- ti->r = r;
ti->typ = block_reader_type(&ti->br);
ti->block_off = off;
block_iter_seek_start(&ti->bi, &ti->br);
return 0;
}
-static int reader_start(struct reftable_reader *r, struct table_iter *ti,
- uint8_t typ, int index)
+static int table_iter_seek_start(struct table_iter *ti, uint8_t typ, int index)
{
- struct reftable_reader_offsets *offs = reader_offsets_for(r, typ);
+ struct reftable_reader_offsets *offs = reader_offsets_for(ti->r, typ);
uint64_t off = offs->offset;
if (index) {
off = offs->index_offset;
@@ -416,16 +395,16 @@ static int reader_start(struct reftable_reader *r, struct table_iter *ti,
typ = BLOCK_TYPE_INDEX;
}
- return reader_table_iter_at(r, ti, off, typ);
+ return table_iter_seek_to(ti, off, typ);
}
-static int reader_seek_linear(struct table_iter *ti,
- struct reftable_record *want)
+static int table_iter_seek_linear(struct table_iter *ti,
+ struct reftable_record *want)
{
struct strbuf want_key = STRBUF_INIT;
struct strbuf got_key = STRBUF_INIT;
struct reftable_record rec;
- int err = -1;
+ int err;
reftable_record_init(&rec, reftable_record_type(want));
reftable_record_key(want, &want_key);
@@ -499,9 +478,8 @@ done:
return err;
}
-static int reader_seek_indexed(struct reftable_reader *r,
- struct reftable_iterator *it,
- struct reftable_record *rec)
+static int table_iter_seek_indexed(struct table_iter *ti,
+ struct reftable_record *rec)
{
struct reftable_record want_index = {
.type = BLOCK_TYPE_INDEX, .u.idx = { .last_key = STRBUF_INIT }
@@ -510,15 +488,9 @@ static int reader_seek_indexed(struct reftable_reader *r,
.type = BLOCK_TYPE_INDEX,
.u.idx = { .last_key = STRBUF_INIT },
};
- struct table_iter index_iter = TABLE_ITER_INIT;
- struct table_iter empty = TABLE_ITER_INIT;
- struct table_iter next = TABLE_ITER_INIT;
- int err = 0;
+ int err;
reftable_record_key(rec, &want_index.u.idx.last_key);
- err = reader_start(r, &index_iter, reftable_record_type(rec), 1);
- if (err < 0)
- goto done;
/*
* The index may consist of multiple levels, where each level may have
@@ -526,7 +498,7 @@ static int reader_seek_indexed(struct reftable_reader *r,
* highest layer that identifies the relevant index block as well as
* the record inside that block that corresponds to our wanted key.
*/
- err = reader_seek_linear(&index_iter, &want_index);
+ err = table_iter_seek_linear(ti, &want_index);
if (err < 0)
goto done;
@@ -552,123 +524,113 @@ static int reader_seek_indexed(struct reftable_reader *r,
* all levels of the index only to find out that the key does
* not exist.
*/
- err = table_iter_next(&index_iter, &index_result);
+ err = table_iter_next(ti, &index_result);
if (err != 0)
goto done;
- err = reader_table_iter_at(r, &next, index_result.u.idx.offset,
- 0);
+ err = table_iter_seek_to(ti, index_result.u.idx.offset, 0);
if (err != 0)
goto done;
- err = block_iter_seek_key(&next.bi, &next.br, &want_index.u.idx.last_key);
+ err = block_iter_seek_key(&ti->bi, &ti->br, &want_index.u.idx.last_key);
if (err < 0)
goto done;
- if (next.typ == reftable_record_type(rec)) {
+ if (ti->typ == reftable_record_type(rec)) {
err = 0;
break;
}
- if (next.typ != BLOCK_TYPE_INDEX) {
+ if (ti->typ != BLOCK_TYPE_INDEX) {
err = REFTABLE_FORMAT_ERROR;
- break;
+ goto done;
}
-
- table_iter_close(&index_iter);
- index_iter = next;
- next = empty;
- }
-
- if (err == 0) {
- struct table_iter *malloced = reftable_calloc(1, sizeof(*malloced));
- *malloced = next;
- next = empty;
- iterator_from_table_iter(it, malloced);
}
done:
- table_iter_close(&next);
- table_iter_close(&index_iter);
reftable_record_release(&want_index);
reftable_record_release(&index_result);
return err;
}
-static int reader_seek_internal(struct reftable_reader *r,
- struct reftable_iterator *it,
- struct reftable_record *rec)
+static int table_iter_seek(struct table_iter *ti,
+ struct reftable_record *want)
{
- struct reftable_reader_offsets *offs =
- reader_offsets_for(r, reftable_record_type(rec));
- uint64_t idx = offs->index_offset;
- struct table_iter ti = TABLE_ITER_INIT, *p;
+ uint8_t typ = reftable_record_type(want);
+ struct reftable_reader_offsets *offs = reader_offsets_for(ti->r, typ);
int err;
- if (idx > 0)
- return reader_seek_indexed(r, it, rec);
-
- err = reader_start(r, &ti, reftable_record_type(rec), 0);
+ err = table_iter_seek_start(ti, reftable_record_type(want),
+ !!offs->index_offset);
if (err < 0)
goto out;
- err = reader_seek_linear(&ti, rec);
- if (err < 0)
+ if (offs->index_offset)
+ err = table_iter_seek_indexed(ti, want);
+ else
+ err = table_iter_seek_linear(ti, want);
+ if (err)
goto out;
- REFTABLE_ALLOC_ARRAY(p, 1);
- *p = ti;
- iterator_from_table_iter(it, p);
-
out:
- if (err)
- table_iter_close(&ti);
return err;
}
-static int reader_seek(struct reftable_reader *r, struct reftable_iterator *it,
- struct reftable_record *rec)
+static int table_iter_seek_void(void *ti, struct reftable_record *want)
{
- uint8_t typ = reftable_record_type(rec);
+ return table_iter_seek(ti, want);
+}
- struct reftable_reader_offsets *offs = reader_offsets_for(r, typ);
- if (!offs->is_present) {
- iterator_set_empty(it);
- return 0;
- }
+static int table_iter_next_void(void *ti, struct reftable_record *rec)
+{
+ return table_iter_next(ti, rec);
+}
- return reader_seek_internal(r, it, rec);
+static void table_iter_close_void(void *ti)
+{
+ table_iter_close(ti);
}
-int reftable_reader_seek_ref(struct reftable_reader *r,
- struct reftable_iterator *it, const char *name)
+static struct reftable_iterator_vtable table_iter_vtable = {
+ .seek = &table_iter_seek_void,
+ .next = &table_iter_next_void,
+ .close = &table_iter_close_void,
+};
+
+static void iterator_from_table_iter(struct reftable_iterator *it,
+ struct table_iter *ti)
{
- struct reftable_record rec = {
- .type = BLOCK_TYPE_REF,
- .u.ref = {
- .refname = (char *)name,
- },
- };
- return reader_seek(r, it, &rec);
+ assert(!it->ops);
+ it->iter_arg = ti;
+ it->ops = &table_iter_vtable;
}
-int reftable_reader_seek_log_at(struct reftable_reader *r,
- struct reftable_iterator *it, const char *name,
- uint64_t update_index)
+static void reader_init_iter(struct reftable_reader *r,
+ struct reftable_iterator *it,
+ uint8_t typ)
{
- struct reftable_record rec = { .type = BLOCK_TYPE_LOG,
- .u.log = {
- .refname = (char *)name,
- .update_index = update_index,
- } };
- return reader_seek(r, it, &rec);
+ struct reftable_reader_offsets *offs = reader_offsets_for(r, typ);
+
+ if (offs->is_present) {
+ struct table_iter *ti;
+ REFTABLE_ALLOC_ARRAY(ti, 1);
+ table_iter_init(ti, r);
+ iterator_from_table_iter(it, ti);
+ } else {
+ iterator_set_empty(it);
+ }
}
-int reftable_reader_seek_log(struct reftable_reader *r,
- struct reftable_iterator *it, const char *name)
+void reftable_reader_init_ref_iterator(struct reftable_reader *r,
+ struct reftable_iterator *it)
{
- uint64_t max = ~((uint64_t)0);
- return reftable_reader_seek_log_at(r, it, name, max);
+ reader_init_iter(r, it, BLOCK_TYPE_REF);
+}
+
+void reftable_reader_init_log_iterator(struct reftable_reader *r,
+ struct reftable_iterator *it)
+{
+ reader_init_iter(r, it, BLOCK_TYPE_LOG);
}
void reader_close(struct reftable_reader *r)
@@ -719,7 +681,8 @@ static int reftable_reader_refs_for_indexed(struct reftable_reader *r,
struct indexed_table_ref_iter *itr = NULL;
/* Look through the reverse index. */
- err = reader_seek(r, &oit, &want);
+ reader_init_iter(r, &oit, BLOCK_TYPE_OBJ);
+ err = iterator_seek(&oit, &want);
if (err != 0)
goto done;
@@ -754,15 +717,15 @@ static int reftable_reader_refs_for_unindexed(struct reftable_reader *r,
struct reftable_iterator *it,
uint8_t *oid)
{
- struct table_iter ti_empty = TABLE_ITER_INIT;
- struct table_iter *ti = reftable_calloc(1, sizeof(*ti));
+ struct table_iter *ti;
struct filtering_ref_iterator *filter = NULL;
struct filtering_ref_iterator empty = FILTERING_REF_ITERATOR_INIT;
int oid_len = hash_size(r->hash_id);
int err;
- *ti = ti_empty;
- err = reader_start(r, ti, BLOCK_TYPE_REF, 0);
+ REFTABLE_ALLOC_ARRAY(ti, 1);
+ table_iter_init(ti, r);
+ err = table_iter_seek_start(ti, BLOCK_TYPE_REF, 0);
if (err < 0) {
reftable_free(ti);
return err;
@@ -800,10 +763,11 @@ uint64_t reftable_reader_min_update_index(struct reftable_reader *r)
/* generic table interface. */
-static int reftable_reader_seek_void(void *tab, struct reftable_iterator *it,
- struct reftable_record *rec)
+static void reftable_reader_init_iter_void(void *tab,
+ struct reftable_iterator *it,
+ uint8_t typ)
{
- return reader_seek(tab, it, rec);
+ reader_init_iter(tab, it, typ);
}
static uint32_t reftable_reader_hash_id_void(void *tab)
@@ -822,7 +786,7 @@ static uint64_t reftable_reader_max_update_index_void(void *tab)
}
static struct reftable_table_vtable reader_vtable = {
- .seek_record = reftable_reader_seek_void,
+ .init_iter = reftable_reader_init_iter_void,
.hash_id = reftable_reader_hash_id_void,
.min_update_index = reftable_reader_min_update_index_void,
.max_update_index = reftable_reader_max_update_index_void,
@@ -856,3 +820,68 @@ done:
reftable_reader_free(r);
return err;
}
+
+int reftable_reader_print_blocks(const char *tablename)
+{
+ struct {
+ const char *name;
+ int type;
+ } sections[] = {
+ {
+ .name = "ref",
+ .type = BLOCK_TYPE_REF,
+ },
+ {
+ .name = "obj",
+ .type = BLOCK_TYPE_OBJ,
+ },
+ {
+ .name = "log",
+ .type = BLOCK_TYPE_LOG,
+ },
+ };
+ struct reftable_block_source src = { 0 };
+ struct reftable_reader *r = NULL;
+ struct table_iter ti = { 0 };
+ size_t i;
+ int err;
+
+ err = reftable_block_source_from_file(&src, tablename);
+ if (err < 0)
+ goto done;
+
+ err = reftable_new_reader(&r, &src, tablename);
+ if (err < 0)
+ goto done;
+
+ table_iter_init(&ti, r);
+
+ printf("header:\n");
+ printf(" block_size: %d\n", r->block_size);
+
+ for (i = 0; i < ARRAY_SIZE(sections); i++) {
+ err = table_iter_seek_start(&ti, sections[i].type, 0);
+ if (err < 0)
+ goto done;
+ if (err > 0)
+ continue;
+
+ printf("%s:\n", sections[i].name);
+
+ while (1) {
+ printf(" - length: %u\n", ti.br.block_len);
+ printf(" restarts: %u\n", ti.br.restart_count);
+
+ err = table_iter_next_block(&ti);
+ if (err < 0)
+ goto done;
+ if (err > 0)
+ break;
+ }
+ }
+
+done:
+ reftable_reader_free(r);
+ table_iter_close(&ti);
+ return err;
+}
diff --git a/reftable/readwrite_test.c b/reftable/readwrite_test.c
index a6dbd214c5..f411abfe9c 100644
--- a/reftable/readwrite_test.c
+++ b/reftable/readwrite_test.c
@@ -86,7 +86,7 @@ static void write_table(char ***names, struct strbuf *buf, int N,
log.update_index = update_index;
log.value_type = REFTABLE_LOG_UPDATE;
set_test_hash(log.value.update.new_hash, i);
- log.value.update.message = "message";
+ log.value.update.message = (char *) "message";
n = reftable_writer_add_log(w, &log);
EXPECT(n == 0);
@@ -118,15 +118,15 @@ static void test_log_buffer_size(void)
int err;
int i;
struct reftable_log_record
- log = { .refname = "refs/heads/master",
+ log = { .refname = (char *) "refs/heads/master",
.update_index = 0xa,
.value_type = REFTABLE_LOG_UPDATE,
.value = { .update = {
- .name = "Han-Wen Nienhuys",
- .email = "hanwen@google.com",
+ .name = (char *) "Han-Wen Nienhuys",
+ .email = (char *) "hanwen@google.com",
.tz_offset = 100,
.time = 0x5e430672,
- .message = "commit: 9\n",
+ .message = (char *) "commit: 9\n",
} } };
struct reftable_writer *w =
reftable_new_writer(&strbuf_add_void, &noop_flush, &buf, &opts);
@@ -156,15 +156,15 @@ static void test_log_overflow(void)
};
int err;
struct reftable_log_record log = {
- .refname = "refs/heads/master",
+ .refname = (char *) "refs/heads/master",
.update_index = 0xa,
.value_type = REFTABLE_LOG_UPDATE,
.value = {
.update = {
.old_hash = { 1 },
.new_hash = { 2 },
- .name = "Han-Wen Nienhuys",
- .email = "hanwen@google.com",
+ .name = (char *) "Han-Wen Nienhuys",
+ .email = (char *) "hanwen@google.com",
.tz_offset = 100,
.time = 0x5e430672,
.message = msg,
@@ -239,7 +239,9 @@ static void test_log_write_read(void)
err = init_reader(&rd, &source, "file.log");
EXPECT_ERR(err);
- err = reftable_reader_seek_ref(&rd, &it, names[N - 1]);
+ reftable_reader_init_ref_iterator(&rd, &it);
+
+ err = reftable_iterator_seek_ref(&it, names[N - 1]);
EXPECT_ERR(err);
err = reftable_iterator_next_ref(&it, &ref);
@@ -252,7 +254,9 @@ static void test_log_write_read(void)
reftable_iterator_destroy(&it);
reftable_ref_record_release(&ref);
- err = reftable_reader_seek_log(&rd, &it, "");
+ reftable_reader_init_log_iterator(&rd, &it);
+
+ err = reftable_iterator_seek_log(&it, "");
EXPECT_ERR(err);
i = 0;
@@ -293,14 +297,14 @@ static void test_log_zlib_corruption(void)
char message[100] = { 0 };
int err, i, n;
struct reftable_log_record log = {
- .refname = "refname",
+ .refname = (char *) "refname",
.value_type = REFTABLE_LOG_UPDATE,
.value = {
.update = {
.new_hash = { 1 },
.old_hash = { 2 },
- .name = "My Name",
- .email = "myname@invalid",
+ .name = (char *) "My Name",
+ .email = (char *) "myname@invalid",
.message = message,
},
},
@@ -330,7 +334,8 @@ static void test_log_zlib_corruption(void)
err = init_reader(&rd, &source, "file.log");
EXPECT_ERR(err);
- err = reftable_reader_seek_log(&rd, &it, "refname");
+ reftable_reader_init_log_iterator(&rd, &it);
+ err = reftable_iterator_seek_log(&it, "refname");
EXPECT(err == REFTABLE_ZLIB_ERROR);
reftable_iterator_destroy(&it);
@@ -358,7 +363,8 @@ static void test_table_read_write_sequential(void)
err = init_reader(&rd, &source, "file.ref");
EXPECT_ERR(err);
- err = reftable_reader_seek_ref(&rd, &it, "");
+ reftable_reader_init_ref_iterator(&rd, &it);
+ err = reftable_iterator_seek_ref(&it, "");
EXPECT_ERR(err);
while (1) {
@@ -412,7 +418,8 @@ static void test_table_read_api(void)
err = init_reader(&rd, &source, "file.ref");
EXPECT_ERR(err);
- err = reftable_reader_seek_ref(&rd, &it, names[0]);
+ reftable_reader_init_ref_iterator(&rd, &it);
+ err = reftable_iterator_seek_ref(&it, names[0]);
EXPECT_ERR(err);
err = reftable_iterator_next_log(&it, &log);
@@ -457,7 +464,8 @@ static void test_table_read_write_seek(int index, int hash_id)
}
for (i = 1; i < N; i++) {
- int err = reftable_reader_seek_ref(&rd, &it, names[i]);
+ reftable_reader_init_ref_iterator(&rd, &it);
+ err = reftable_iterator_seek_ref(&it, names[i]);
EXPECT_ERR(err);
err = reftable_iterator_next_ref(&it, &ref);
EXPECT_ERR(err);
@@ -472,7 +480,8 @@ static void test_table_read_write_seek(int index, int hash_id)
strbuf_addstr(&pastLast, names[N - 1]);
strbuf_addstr(&pastLast, "/");
- err = reftable_reader_seek_ref(&rd, &it, pastLast.buf);
+ reftable_reader_init_ref_iterator(&rd, &it);
+ err = reftable_iterator_seek_ref(&it, pastLast.buf);
if (err == 0) {
struct reftable_ref_record ref = { NULL };
int err = reftable_iterator_next_ref(&it, &ref);
@@ -576,7 +585,8 @@ static void test_table_refs_for(int indexed)
rd.obj_offsets.is_present = 0;
}
- err = reftable_reader_seek_ref(&rd, &it, "");
+ reftable_reader_init_ref_iterator(&rd, &it);
+ err = reftable_iterator_seek_ref(&it, "");
EXPECT_ERR(err);
reftable_iterator_destroy(&it);
@@ -639,7 +649,8 @@ static void test_write_empty_table(void)
err = reftable_new_reader(&rd, &source, "filename");
EXPECT_ERR(err);
- err = reftable_reader_seek_ref(rd, &it, "");
+ reftable_reader_init_ref_iterator(rd, &it);
+ err = reftable_iterator_seek_ref(&it, "");
EXPECT_ERR(err);
err = reftable_iterator_next_ref(&it, &rec);
@@ -728,7 +739,7 @@ static void test_write_empty_key(void)
struct reftable_writer *w =
reftable_new_writer(&strbuf_add_void, &noop_flush, &buf, &opts);
struct reftable_ref_record ref = {
- .refname = "",
+ .refname = (char *) "",
.update_index = 1,
.value_type = REFTABLE_REF_DELETION,
};
@@ -752,18 +763,18 @@ static void test_write_key_order(void)
reftable_new_writer(&strbuf_add_void, &noop_flush, &buf, &opts);
struct reftable_ref_record refs[2] = {
{
- .refname = "b",
+ .refname = (char *) "b",
.update_index = 1,
.value_type = REFTABLE_REF_SYMREF,
.value = {
- .symref = "target",
+ .symref = (char *) "target",
},
}, {
- .refname = "a",
+ .refname = (char *) "a",
.update_index = 1,
.value_type = REFTABLE_REF_SYMREF,
.value = {
- .symref = "target",
+ .symref = (char *) "target",
},
}
};
@@ -846,7 +857,8 @@ static void test_write_multiple_indices(void)
* Seeking the log uses the log index now. In case there is any
* confusion regarding indices we would notice here.
*/
- err = reftable_reader_seek_log(reader, &it, "");
+ reftable_reader_init_log_iterator(reader, &it);
+ err = reftable_iterator_seek_log(&it, "");
EXPECT_ERR(err);
reftable_iterator_destroy(&it);
@@ -901,7 +913,8 @@ static void test_write_multi_level_index(void)
/*
* Seeking the last ref should work as expected.
*/
- err = reftable_reader_seek_ref(reader, &it, "refs/heads/199");
+ reftable_reader_init_ref_iterator(reader, &it);
+ err = reftable_iterator_seek_ref(&it, "refs/heads/199");
EXPECT_ERR(err);
reftable_iterator_destroy(&it);
diff --git a/reftable/record.c b/reftable/record.c
index 5506f3e913..a2cba5ef74 100644
--- a/reftable/record.c
+++ b/reftable/record.c
@@ -116,7 +116,7 @@ static int decode_string(struct strbuf *dest, struct string_view in)
return start_len - in.len;
}
-static int encode_string(char *str, struct string_view s)
+static int encode_string(const char *str, struct string_view s)
{
struct string_view start = s;
int l = strlen(str);
@@ -969,9 +969,9 @@ done:
return REFTABLE_FORMAT_ERROR;
}
-static int null_streq(char *a, char *b)
+static int null_streq(const char *a, const char *b)
{
- char *empty = "";
+ const char *empty = "";
if (!a)
a = empty;
diff --git a/reftable/record_test.c b/reftable/record_test.c
deleted file mode 100644
index c158ee79ff..0000000000
--- a/reftable/record_test.c
+++ /dev/null
@@ -1,419 +0,0 @@
-/*
- Copyright 2020 Google LLC
-
- Use of this source code is governed by a BSD-style
- license that can be found in the LICENSE file or at
- https://developers.google.com/open-source/licenses/bsd
-*/
-
-#include "record.h"
-
-#include "system.h"
-#include "basics.h"
-#include "constants.h"
-#include "test_framework.h"
-#include "reftable-tests.h"
-
-static void test_copy(struct reftable_record *rec)
-{
- struct reftable_record copy;
- uint8_t typ;
-
- typ = reftable_record_type(rec);
- reftable_record_init(&copy, typ);
- reftable_record_copy_from(&copy, rec, GIT_SHA1_RAWSZ);
- /* do it twice to catch memory leaks */
- reftable_record_copy_from(&copy, rec, GIT_SHA1_RAWSZ);
- EXPECT(reftable_record_equal(rec, &copy, GIT_SHA1_RAWSZ));
-
- puts("testing print coverage:\n");
- reftable_record_print(&copy, GIT_SHA1_RAWSZ);
-
- reftable_record_release(&copy);
-}
-
-static void test_varint_roundtrip(void)
-{
- uint64_t inputs[] = { 0,
- 1,
- 27,
- 127,
- 128,
- 257,
- 4096,
- ((uint64_t)1 << 63),
- ((uint64_t)1 << 63) + ((uint64_t)1 << 63) - 1 };
- int i = 0;
- for (i = 0; i < ARRAY_SIZE(inputs); i++) {
- uint8_t dest[10];
-
- struct string_view out = {
- .buf = dest,
- .len = sizeof(dest),
- };
- uint64_t in = inputs[i];
- int n = put_var_int(&out, in);
- uint64_t got = 0;
-
- EXPECT(n > 0);
- out.len = n;
- n = get_var_int(&got, &out);
- EXPECT(n > 0);
-
- EXPECT(got == in);
- }
-}
-
-static void test_common_prefix(void)
-{
- struct {
- const char *a, *b;
- int want;
- } cases[] = {
- { "abc", "ab", 2 },
- { "", "abc", 0 },
- { "abc", "abd", 2 },
- { "abc", "pqr", 0 },
- };
-
- int i = 0;
- for (i = 0; i < ARRAY_SIZE(cases); i++) {
- struct strbuf a = STRBUF_INIT;
- struct strbuf b = STRBUF_INIT;
- strbuf_addstr(&a, cases[i].a);
- strbuf_addstr(&b, cases[i].b);
- EXPECT(common_prefix_size(&a, &b) == cases[i].want);
-
- strbuf_release(&a);
- strbuf_release(&b);
- }
-}
-
-static void set_hash(uint8_t *h, int j)
-{
- int i = 0;
- for (i = 0; i < hash_size(GIT_SHA1_FORMAT_ID); i++) {
- h[i] = (j >> i) & 0xff;
- }
-}
-
-static void test_reftable_ref_record_roundtrip(void)
-{
- struct strbuf scratch = STRBUF_INIT;
- int i = 0;
-
- for (i = REFTABLE_REF_DELETION; i < REFTABLE_NR_REF_VALUETYPES; i++) {
- struct reftable_record in = {
- .type = BLOCK_TYPE_REF,
- };
- struct reftable_record out = { .type = BLOCK_TYPE_REF };
- struct strbuf key = STRBUF_INIT;
- uint8_t buffer[1024] = { 0 };
- struct string_view dest = {
- .buf = buffer,
- .len = sizeof(buffer),
- };
- int n, m;
-
- in.u.ref.value_type = i;
- switch (i) {
- case REFTABLE_REF_DELETION:
- break;
- case REFTABLE_REF_VAL1:
- set_hash(in.u.ref.value.val1, 1);
- break;
- case REFTABLE_REF_VAL2:
- set_hash(in.u.ref.value.val2.value, 1);
- set_hash(in.u.ref.value.val2.target_value, 2);
- break;
- case REFTABLE_REF_SYMREF:
- in.u.ref.value.symref = xstrdup("target");
- break;
- }
- in.u.ref.refname = xstrdup("refs/heads/master");
-
- test_copy(&in);
-
- EXPECT(reftable_record_val_type(&in) == i);
-
- reftable_record_key(&in, &key);
- n = reftable_record_encode(&in, dest, GIT_SHA1_RAWSZ);
- EXPECT(n > 0);
-
- /* decode into a non-zero reftable_record to test for leaks. */
- m = reftable_record_decode(&out, key, i, dest, GIT_SHA1_RAWSZ, &scratch);
- EXPECT(n == m);
-
- EXPECT(reftable_ref_record_equal(&in.u.ref, &out.u.ref,
- GIT_SHA1_RAWSZ));
- reftable_record_release(&in);
-
- strbuf_release(&key);
- reftable_record_release(&out);
- }
-
- strbuf_release(&scratch);
-}
-
-static void test_reftable_log_record_equal(void)
-{
- struct reftable_log_record in[2] = {
- {
- .refname = xstrdup("refs/heads/master"),
- .update_index = 42,
- },
- {
- .refname = xstrdup("refs/heads/master"),
- .update_index = 22,
- }
- };
-
- EXPECT(!reftable_log_record_equal(&in[0], &in[1], GIT_SHA1_RAWSZ));
- in[1].update_index = in[0].update_index;
- EXPECT(reftable_log_record_equal(&in[0], &in[1], GIT_SHA1_RAWSZ));
- reftable_log_record_release(&in[0]);
- reftable_log_record_release(&in[1]);
-}
-
-static void test_reftable_log_record_roundtrip(void)
-{
- int i;
- struct reftable_log_record in[] = {
- {
- .refname = xstrdup("refs/heads/master"),
- .update_index = 42,
- .value_type = REFTABLE_LOG_UPDATE,
- .value = {
- .update = {
- .name = xstrdup("han-wen"),
- .email = xstrdup("hanwen@google.com"),
- .message = xstrdup("test"),
- .time = 1577123507,
- .tz_offset = 100,
- },
- }
- },
- {
- .refname = xstrdup("refs/heads/master"),
- .update_index = 22,
- .value_type = REFTABLE_LOG_DELETION,
- },
- {
- .refname = xstrdup("branch"),
- .update_index = 33,
- .value_type = REFTABLE_LOG_UPDATE,
- }
- };
- struct strbuf scratch = STRBUF_INIT;
-
- set_test_hash(in[0].value.update.new_hash, 1);
- set_test_hash(in[0].value.update.old_hash, 2);
- set_test_hash(in[2].value.update.new_hash, 3);
- set_test_hash(in[2].value.update.old_hash, 4);
- for (i = 0; i < ARRAY_SIZE(in); i++) {
- struct reftable_record rec = { .type = BLOCK_TYPE_LOG };
- struct strbuf key = STRBUF_INIT;
- uint8_t buffer[1024] = { 0 };
- struct string_view dest = {
- .buf = buffer,
- .len = sizeof(buffer),
- };
- /* populate out, to check for leaks. */
- struct reftable_record out = {
- .type = BLOCK_TYPE_LOG,
- .u.log = {
- .refname = xstrdup("old name"),
- .value_type = REFTABLE_LOG_UPDATE,
- .value = {
- .update = {
- .name = xstrdup("old name"),
- .email = xstrdup("old@email"),
- .message = xstrdup("old message"),
- },
- },
- },
- };
- int n, m, valtype;
-
- rec.u.log = in[i];
-
- test_copy(&rec);
-
- reftable_record_key(&rec, &key);
-
- n = reftable_record_encode(&rec, dest, GIT_SHA1_RAWSZ);
- EXPECT(n >= 0);
- valtype = reftable_record_val_type(&rec);
- m = reftable_record_decode(&out, key, valtype, dest,
- GIT_SHA1_RAWSZ, &scratch);
- EXPECT(n == m);
-
- EXPECT(reftable_log_record_equal(&in[i], &out.u.log,
- GIT_SHA1_RAWSZ));
- reftable_log_record_release(&in[i]);
- strbuf_release(&key);
- reftable_record_release(&out);
- }
-
- strbuf_release(&scratch);
-}
-
-static void test_u24_roundtrip(void)
-{
- uint32_t in = 0x112233;
- uint8_t dest[3];
- uint32_t out;
- put_be24(dest, in);
- out = get_be24(dest);
- EXPECT(in == out);
-}
-
-static void test_key_roundtrip(void)
-{
- uint8_t buffer[1024] = { 0 };
- struct string_view dest = {
- .buf = buffer,
- .len = sizeof(buffer),
- };
- struct strbuf last_key = STRBUF_INIT;
- struct strbuf key = STRBUF_INIT;
- struct strbuf roundtrip = STRBUF_INIT;
- int restart;
- uint8_t extra;
- int n, m;
- uint8_t rt_extra;
-
- strbuf_addstr(&last_key, "refs/heads/master");
- strbuf_addstr(&key, "refs/tags/bla");
- extra = 6;
- n = reftable_encode_key(&restart, dest, last_key, key, extra);
- EXPECT(!restart);
- EXPECT(n > 0);
-
- strbuf_addstr(&roundtrip, "refs/heads/master");
- m = reftable_decode_key(&roundtrip, &rt_extra, dest);
- EXPECT(n == m);
- EXPECT(0 == strbuf_cmp(&key, &roundtrip));
- EXPECT(rt_extra == extra);
-
- strbuf_release(&last_key);
- strbuf_release(&key);
- strbuf_release(&roundtrip);
-}
-
-static void test_reftable_obj_record_roundtrip(void)
-{
- uint8_t testHash1[GIT_SHA1_RAWSZ] = { 1, 2, 3, 4, 0 };
- uint64_t till9[] = { 1, 2, 3, 4, 500, 600, 700, 800, 9000 };
- struct reftable_obj_record recs[3] = {
- {
- .hash_prefix = testHash1,
- .hash_prefix_len = 5,
- .offsets = till9,
- .offset_len = 3,
- },
- {
- .hash_prefix = testHash1,
- .hash_prefix_len = 5,
- .offsets = till9,
- .offset_len = 9,
- },
- {
- .hash_prefix = testHash1,
- .hash_prefix_len = 5,
- },
- };
- struct strbuf scratch = STRBUF_INIT;
- int i = 0;
-
- for (i = 0; i < ARRAY_SIZE(recs); i++) {
- uint8_t buffer[1024] = { 0 };
- struct string_view dest = {
- .buf = buffer,
- .len = sizeof(buffer),
- };
- struct reftable_record in = {
- .type = BLOCK_TYPE_OBJ,
- .u = {
- .obj = recs[i],
- },
- };
- struct strbuf key = STRBUF_INIT;
- struct reftable_record out = { .type = BLOCK_TYPE_OBJ };
- int n, m;
- uint8_t extra;
-
- test_copy(&in);
- reftable_record_key(&in, &key);
- n = reftable_record_encode(&in, dest, GIT_SHA1_RAWSZ);
- EXPECT(n > 0);
- extra = reftable_record_val_type(&in);
- m = reftable_record_decode(&out, key, extra, dest,
- GIT_SHA1_RAWSZ, &scratch);
- EXPECT(n == m);
-
- EXPECT(reftable_record_equal(&in, &out, GIT_SHA1_RAWSZ));
- strbuf_release(&key);
- reftable_record_release(&out);
- }
-
- strbuf_release(&scratch);
-}
-
-static void test_reftable_index_record_roundtrip(void)
-{
- struct reftable_record in = {
- .type = BLOCK_TYPE_INDEX,
- .u.idx = {
- .offset = 42,
- .last_key = STRBUF_INIT,
- },
- };
- uint8_t buffer[1024] = { 0 };
- struct string_view dest = {
- .buf = buffer,
- .len = sizeof(buffer),
- };
- struct strbuf scratch = STRBUF_INIT;
- struct strbuf key = STRBUF_INIT;
- struct reftable_record out = {
- .type = BLOCK_TYPE_INDEX,
- .u.idx = { .last_key = STRBUF_INIT },
- };
- int n, m;
- uint8_t extra;
-
- strbuf_addstr(&in.u.idx.last_key, "refs/heads/master");
- reftable_record_key(&in, &key);
- test_copy(&in);
-
- EXPECT(0 == strbuf_cmp(&key, &in.u.idx.last_key));
- n = reftable_record_encode(&in, dest, GIT_SHA1_RAWSZ);
- EXPECT(n > 0);
-
- extra = reftable_record_val_type(&in);
- m = reftable_record_decode(&out, key, extra, dest, GIT_SHA1_RAWSZ,
- &scratch);
- EXPECT(m == n);
-
- EXPECT(reftable_record_equal(&in, &out, GIT_SHA1_RAWSZ));
-
- reftable_record_release(&out);
- strbuf_release(&key);
- strbuf_release(&scratch);
- strbuf_release(&in.u.idx.last_key);
-}
-
-int record_test_main(int argc, const char *argv[])
-{
- RUN_TEST(test_reftable_log_record_equal);
- RUN_TEST(test_reftable_log_record_roundtrip);
- RUN_TEST(test_reftable_ref_record_roundtrip);
- RUN_TEST(test_varint_roundtrip);
- RUN_TEST(test_key_roundtrip);
- RUN_TEST(test_common_prefix);
- RUN_TEST(test_reftable_obj_record_roundtrip);
- RUN_TEST(test_reftable_index_record_roundtrip);
- RUN_TEST(test_u24_roundtrip);
- return 0;
-}
diff --git a/reftable/refname.c b/reftable/refname.c
deleted file mode 100644
index bbfde15754..0000000000
--- a/reftable/refname.c
+++ /dev/null
@@ -1,206 +0,0 @@
-/*
- Copyright 2020 Google LLC
-
- Use of this source code is governed by a BSD-style
- 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 "basics.h"
-#include "refname.h"
-#include "reftable-iterator.h"
-
-struct refname_needle_lesseq_args {
- char **haystack;
- const char *needle;
-};
-
-static int refname_needle_lesseq(size_t k, void *_args)
-{
- struct refname_needle_lesseq_args *args = _args;
- return strcmp(args->needle, args->haystack[k]) <= 0;
-}
-
-static int modification_has_ref(struct modification *mod, const char *name)
-{
- struct reftable_ref_record ref = { NULL };
- int err = 0;
-
- if (mod->add_len > 0) {
- struct refname_needle_lesseq_args args = {
- .haystack = mod->add,
- .needle = name,
- };
- size_t idx = binsearch(mod->add_len, refname_needle_lesseq, &args);
- if (idx < mod->add_len && !strcmp(mod->add[idx], name))
- return 0;
- }
-
- if (mod->del_len > 0) {
- struct refname_needle_lesseq_args args = {
- .haystack = mod->del,
- .needle = name,
- };
- size_t idx = binsearch(mod->del_len, refname_needle_lesseq, &args);
- if (idx < mod->del_len && !strcmp(mod->del[idx], name))
- return 1;
- }
-
- err = reftable_table_read_ref(&mod->tab, name, &ref);
- reftable_ref_record_release(&ref);
- return err;
-}
-
-static void modification_release(struct modification *mod)
-{
- /* don't delete the strings themselves; they're owned by ref records.
- */
- FREE_AND_NULL(mod->add);
- FREE_AND_NULL(mod->del);
- mod->add_len = 0;
- mod->del_len = 0;
-}
-
-static int modification_has_ref_with_prefix(struct modification *mod,
- const char *prefix)
-{
- struct reftable_iterator it = { NULL };
- struct reftable_ref_record ref = { NULL };
- int err = 0;
-
- if (mod->add_len > 0) {
- struct refname_needle_lesseq_args args = {
- .haystack = mod->add,
- .needle = prefix,
- };
- size_t idx = binsearch(mod->add_len, refname_needle_lesseq, &args);
- if (idx < mod->add_len &&
- !strncmp(prefix, mod->add[idx], strlen(prefix)))
- goto done;
- }
- err = reftable_table_seek_ref(&mod->tab, &it, prefix);
- if (err)
- goto done;
-
- while (1) {
- err = reftable_iterator_next_ref(&it, &ref);
- if (err)
- goto done;
-
- if (mod->del_len > 0) {
- struct refname_needle_lesseq_args args = {
- .haystack = mod->del,
- .needle = ref.refname,
- };
- size_t idx = binsearch(mod->del_len, refname_needle_lesseq, &args);
- if (idx < mod->del_len &&
- !strcmp(ref.refname, mod->del[idx]))
- continue;
- }
-
- if (strncmp(ref.refname, prefix, strlen(prefix))) {
- err = 1;
- goto done;
- }
- err = 0;
- goto done;
- }
-
-done:
- reftable_ref_record_release(&ref);
- reftable_iterator_destroy(&it);
- return err;
-}
-
-static int validate_refname(const char *name)
-{
- while (1) {
- char *next = strchr(name, '/');
- if (!*name) {
- return REFTABLE_REFNAME_ERROR;
- }
- if (!next) {
- return 0;
- }
- if (next - name == 0 || (next - name == 1 && *name == '.') ||
- (next - name == 2 && name[0] == '.' && name[1] == '.'))
- return REFTABLE_REFNAME_ERROR;
- name = next + 1;
- }
- return 0;
-}
-
-int validate_ref_record_addition(struct reftable_table tab,
- struct reftable_ref_record *recs, size_t sz)
-{
- struct modification mod = {
- .tab = tab,
- .add = reftable_calloc(sz, sizeof(*mod.add)),
- .del = reftable_calloc(sz, sizeof(*mod.del)),
- };
- int i = 0;
- int err = 0;
- for (; i < sz; i++) {
- if (reftable_ref_record_is_deletion(&recs[i])) {
- mod.del[mod.del_len++] = recs[i].refname;
- } else {
- mod.add[mod.add_len++] = recs[i].refname;
- }
- }
-
- err = modification_validate(&mod);
- modification_release(&mod);
- return err;
-}
-
-static void strbuf_trim_component(struct strbuf *sl)
-{
- while (sl->len > 0) {
- int is_slash = (sl->buf[sl->len - 1] == '/');
- strbuf_setlen(sl, sl->len - 1);
- if (is_slash)
- break;
- }
-}
-
-int modification_validate(struct modification *mod)
-{
- struct strbuf slashed = STRBUF_INIT;
- int err = 0;
- int i = 0;
- for (; i < mod->add_len; i++) {
- err = validate_refname(mod->add[i]);
- if (err)
- goto done;
- strbuf_reset(&slashed);
- strbuf_addstr(&slashed, mod->add[i]);
- strbuf_addstr(&slashed, "/");
-
- err = modification_has_ref_with_prefix(mod, slashed.buf);
- if (err == 0) {
- err = REFTABLE_NAME_CONFLICT;
- goto done;
- }
- if (err < 0)
- goto done;
-
- strbuf_reset(&slashed);
- strbuf_addstr(&slashed, mod->add[i]);
- while (slashed.len) {
- strbuf_trim_component(&slashed);
- err = modification_has_ref(mod, slashed.buf);
- if (err == 0) {
- err = REFTABLE_NAME_CONFLICT;
- goto done;
- }
- if (err < 0)
- goto done;
- }
- }
- err = 0;
-done:
- strbuf_release(&slashed);
- return err;
-}
diff --git a/reftable/refname.h b/reftable/refname.h
deleted file mode 100644
index a24b40fcb4..0000000000
--- a/reftable/refname.h
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- Copyright 2020 Google LLC
-
- Use of this source code is governed by a BSD-style
- license that can be found in the LICENSE file or at
- https://developers.google.com/open-source/licenses/bsd
-*/
-#ifndef REFNAME_H
-#define REFNAME_H
-
-#include "reftable-record.h"
-#include "reftable-generic.h"
-
-struct modification {
- struct reftable_table tab;
-
- char **add;
- size_t add_len;
-
- char **del;
- size_t del_len;
-};
-
-int validate_ref_record_addition(struct reftable_table tab,
- struct reftable_ref_record *recs, size_t sz);
-
-int modification_validate(struct modification *mod);
-
-#endif
diff --git a/reftable/refname_test.c b/reftable/refname_test.c
deleted file mode 100644
index b9cc62554e..0000000000
--- a/reftable/refname_test.c
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
-Copyright 2020 Google LLC
-
-Use of this source code is governed by a BSD-style
-license that can be found in the LICENSE file or at
-https://developers.google.com/open-source/licenses/bsd
-*/
-
-#include "basics.h"
-#include "block.h"
-#include "blocksource.h"
-#include "reader.h"
-#include "record.h"
-#include "refname.h"
-#include "reftable-error.h"
-#include "reftable-writer.h"
-#include "system.h"
-
-#include "test_framework.h"
-#include "reftable-tests.h"
-
-struct testcase {
- char *add;
- char *del;
- int error_code;
-};
-
-static void test_conflict(void)
-{
- struct reftable_write_options opts = { 0 };
- struct strbuf buf = STRBUF_INIT;
- struct reftable_writer *w =
- reftable_new_writer(&strbuf_add_void, &noop_flush, &buf, &opts);
- struct reftable_ref_record rec = {
- .refname = "a/b",
- .value_type = REFTABLE_REF_SYMREF,
- .value.symref = "destination", /* make sure it's not a symref.
- */
- .update_index = 1,
- };
- int err;
- int i;
- struct reftable_block_source source = { NULL };
- struct reftable_reader *rd = NULL;
- struct reftable_table tab = { NULL };
- struct testcase cases[] = {
- { "a/b/c", NULL, REFTABLE_NAME_CONFLICT },
- { "b", NULL, 0 },
- { "a", NULL, REFTABLE_NAME_CONFLICT },
- { "a", "a/b", 0 },
-
- { "p/", NULL, REFTABLE_REFNAME_ERROR },
- { "p//q", NULL, REFTABLE_REFNAME_ERROR },
- { "p/./q", NULL, REFTABLE_REFNAME_ERROR },
- { "p/../q", NULL, REFTABLE_REFNAME_ERROR },
-
- { "a/b/c", "a/b", 0 },
- { NULL, "a//b", 0 },
- };
- reftable_writer_set_limits(w, 1, 1);
-
- err = reftable_writer_add_ref(w, &rec);
- EXPECT_ERR(err);
-
- err = reftable_writer_close(w);
- EXPECT_ERR(err);
- reftable_writer_free(w);
-
- block_source_from_strbuf(&source, &buf);
- err = reftable_new_reader(&rd, &source, "filename");
- EXPECT_ERR(err);
-
- reftable_table_from_reader(&tab, rd);
-
- for (i = 0; i < ARRAY_SIZE(cases); i++) {
- struct modification mod = {
- .tab = tab,
- };
-
- if (cases[i].add) {
- mod.add = &cases[i].add;
- mod.add_len = 1;
- }
- if (cases[i].del) {
- mod.del = &cases[i].del;
- mod.del_len = 1;
- }
-
- err = modification_validate(&mod);
- EXPECT(err == cases[i].error_code);
- }
-
- reftable_reader_free(rd);
- strbuf_release(&buf);
-}
-
-int refname_test_main(int argc, const char *argv[])
-{
- RUN_TEST(test_conflict);
- return 0;
-}
diff --git a/reftable/reftable-error.h b/reftable/reftable-error.h
index e9b07c9f36..6368cd9ed9 100644
--- a/reftable/reftable-error.h
+++ b/reftable/reftable-error.h
@@ -48,9 +48,6 @@ enum reftable_error {
/* Wrote a table without blocks. */
REFTABLE_EMPTY_TABLE_ERROR = -8,
- /* Dir/file conflict. */
- REFTABLE_NAME_CONFLICT = -9,
-
/* Invalid ref name. */
REFTABLE_REFNAME_ERROR = -10,
diff --git a/reftable/reftable-generic.h b/reftable/reftable-generic.h
index d239751a77..65670ea093 100644
--- a/reftable/reftable-generic.h
+++ b/reftable/reftable-generic.h
@@ -21,11 +21,11 @@ struct reftable_table {
void *table_arg;
};
-int reftable_table_seek_log(struct reftable_table *tab,
- struct reftable_iterator *it, const char *name);
+void reftable_table_init_ref_iter(struct reftable_table *tab,
+ struct reftable_iterator *it);
-int reftable_table_seek_ref(struct reftable_table *tab,
- struct reftable_iterator *it, const char *name);
+void reftable_table_init_log_iter(struct reftable_table *tab,
+ struct reftable_iterator *it);
/* returns the hash ID from a generic reftable_table */
uint32_t reftable_table_hash_id(struct reftable_table *tab);
diff --git a/reftable/reftable-iterator.h b/reftable/reftable-iterator.h
index d3eee7af35..e3bf688d53 100644
--- a/reftable/reftable-iterator.h
+++ b/reftable/reftable-iterator.h
@@ -21,12 +21,33 @@ struct reftable_iterator {
void *iter_arg;
};
+/*
+ * Position the iterator at the ref record with given name such that the next
+ * call to `next_ref()` would yield the record.
+ */
+int reftable_iterator_seek_ref(struct reftable_iterator *it,
+ const char *name);
+
/* reads the next reftable_ref_record. Returns < 0 for error, 0 for OK and > 0:
* end of iteration.
*/
int reftable_iterator_next_ref(struct reftable_iterator *it,
struct reftable_ref_record *ref);
+/*
+ * Position the iterator at the log record with given name and update index
+ * such that the next call to `next_log()` would yield the record.
+ */
+int reftable_iterator_seek_log_at(struct reftable_iterator *it,
+ const char *name, uint64_t update_index);
+
+/*
+ * Position the iterator at the newest log record with given name such that the
+ * next call to `next_log()` would yield the record.
+ */
+int reftable_iterator_seek_log(struct reftable_iterator *it,
+ const char *name);
+
/* reads the next reftable_log_record. Returns < 0 for error, 0 for OK and > 0:
* end of iteration.
*/
diff --git a/reftable/reftable-merged.h b/reftable/reftable-merged.h
index c91a2d83a2..14d5fc9f05 100644
--- a/reftable/reftable-merged.h
+++ b/reftable/reftable-merged.h
@@ -36,21 +36,6 @@ int reftable_new_merged_table(struct reftable_merged_table **dest,
struct reftable_table *stack, size_t n,
uint32_t hash_id);
-/* returns an iterator positioned just before 'name' */
-int reftable_merged_table_seek_ref(struct reftable_merged_table *mt,
- struct reftable_iterator *it,
- const char *name);
-
-/* returns an iterator for log entry, at given update_index */
-int reftable_merged_table_seek_log_at(struct reftable_merged_table *mt,
- struct reftable_iterator *it,
- const char *name, uint64_t update_index);
-
-/* like reftable_merged_table_seek_log_at but look for the newest entry. */
-int reftable_merged_table_seek_log(struct reftable_merged_table *mt,
- struct reftable_iterator *it,
- const char *name);
-
/* returns the max update_index covered by this merged table. */
uint64_t
reftable_merged_table_max_update_index(struct reftable_merged_table *mt);
diff --git a/reftable/reftable-reader.h b/reftable/reftable-reader.h
index 4a4bc2fdf8..a32f31d648 100644
--- a/reftable/reftable-reader.h
+++ b/reftable/reftable-reader.h
@@ -36,48 +36,17 @@ struct reftable_table;
int reftable_new_reader(struct reftable_reader **pp,
struct reftable_block_source *src, const char *name);
-/* reftable_reader_seek_ref returns an iterator where 'name' would be inserted
- in the table. To seek to the start of the table, use name = "".
-
- example:
-
- struct reftable_reader *r = NULL;
- int err = reftable_new_reader(&r, &src, "filename");
- if (err < 0) { ... }
- struct reftable_iterator it = {0};
- err = reftable_reader_seek_ref(r, &it, "refs/heads/master");
- if (err < 0) { ... }
- struct reftable_ref_record ref = {0};
- while (1) {
- err = reftable_iterator_next_ref(&it, &ref);
- if (err > 0) {
- break;
- }
- if (err < 0) {
- ..error handling..
- }
- ..found..
- }
- reftable_iterator_destroy(&it);
- reftable_ref_record_release(&ref);
-*/
-int reftable_reader_seek_ref(struct reftable_reader *r,
- struct reftable_iterator *it, const char *name);
+/* Initialize a reftable iterator for reading refs. */
+void reftable_reader_init_ref_iterator(struct reftable_reader *r,
+ struct reftable_iterator *it);
+
+/* Initialize a reftable iterator for reading logs. */
+void reftable_reader_init_log_iterator(struct reftable_reader *r,
+ struct reftable_iterator *it);
/* returns the hash ID used in this table. */
uint32_t reftable_reader_hash_id(struct reftable_reader *r);
-/* seek to logs for the given name, older than update_index. To seek to the
- start of the table, use name = "".
-*/
-int reftable_reader_seek_log_at(struct reftable_reader *r,
- struct reftable_iterator *it, const char *name,
- uint64_t update_index);
-
-/* seek to newest log entry for given name. */
-int reftable_reader_seek_log(struct reftable_reader *r,
- struct reftable_iterator *it, const char *name);
-
/* closes and deallocates a reader. */
void reftable_reader_free(struct reftable_reader *);
@@ -97,5 +66,7 @@ void reftable_table_from_reader(struct reftable_table *tab,
/* print table onto stdout for debugging. */
int reftable_reader_print_file(const char *tablename);
+/* print blocks onto stdout for debugging. */
+int reftable_reader_print_blocks(const char *tablename);
#endif
diff --git a/reftable/reftable-record.h b/reftable/reftable-record.h
index 2a2943cd13..ff486eb1f7 100644
--- a/reftable/reftable-record.h
+++ b/reftable/reftable-record.h
@@ -9,7 +9,7 @@ https://developers.google.com/open-source/licenses/bsd
#ifndef REFTABLE_RECORD_H
#define REFTABLE_RECORD_H
-#include "hash-ll.h"
+#include "hash.h"
#include <stdint.h>
/*
diff --git a/reftable/reftable-stack.h b/reftable/reftable-stack.h
index 1b602dda58..09e97c9991 100644
--- a/reftable/reftable-stack.h
+++ b/reftable/reftable-stack.h
@@ -29,7 +29,7 @@ struct reftable_stack;
* stored in 'dir'. Typically, this should be .git/reftables.
*/
int reftable_new_stack(struct reftable_stack **dest, const char *dir,
- struct reftable_write_options config);
+ const struct reftable_write_options *opts);
/* returns the update_index at which a next table should be written. */
uint64_t reftable_stack_next_update_index(struct reftable_stack *st);
@@ -66,6 +66,24 @@ int reftable_stack_add(struct reftable_stack *st,
void *write_arg),
void *write_arg);
+struct reftable_iterator;
+
+/*
+ * Initialize an iterator for the merged tables contained in the stack that can
+ * be used to iterate through refs. The iterator is valid until the next reload
+ * or write.
+ */
+void reftable_stack_init_ref_iterator(struct reftable_stack *st,
+ struct reftable_iterator *it);
+
+/*
+ * Initialize an iterator for the merged tables contained in the stack that can
+ * be used to iterate through logs. The iterator is valid until the next reload
+ * or write.
+ */
+void reftable_stack_init_log_iterator(struct reftable_stack *st,
+ struct reftable_iterator *it);
+
/* returns the merged_table for seeking. This table is valid until the
* next write or reload, and should not be closed or deleted.
*/
diff --git a/reftable/reftable-tests.h b/reftable/reftable-tests.h
index 0019cbcfa4..114cc3d053 100644
--- a/reftable/reftable-tests.h
+++ b/reftable/reftable-tests.h
@@ -14,7 +14,6 @@ int block_test_main(int argc, const char **argv);
int merged_test_main(int argc, const char **argv);
int pq_test_main(int argc, const char **argv);
int record_test_main(int argc, const char **argv);
-int refname_test_main(int argc, const char **argv);
int readwrite_test_main(int argc, const char **argv);
int stack_test_main(int argc, const char **argv);
int tree_test_main(int argc, const char **argv);
diff --git a/reftable/reftable-writer.h b/reftable/reftable-writer.h
index 155bf0bbe2..189b1f4144 100644
--- a/reftable/reftable-writer.h
+++ b/reftable/reftable-writer.h
@@ -28,7 +28,7 @@ struct reftable_write_options {
unsigned skip_index_objects : 1;
/* how often to write complete keys in each block. */
- int restart_interval;
+ uint16_t restart_interval;
/* 4-byte identifier ("sha1", "s256") of the hash.
* Defaults to SHA1 if unset
@@ -38,10 +38,6 @@ struct reftable_write_options {
/* Default mode for creating files. If unset, use 0666 (+umask) */
unsigned int default_permissions;
- /* boolean: do not check ref names for validity or dir/file conflicts.
- */
- unsigned skip_name_check : 1;
-
/* boolean: copy log messages exactly. If unset, check that the message
* is a single line, and add '\n' if missing.
*/
@@ -49,6 +45,12 @@ struct reftable_write_options {
/* boolean: Prevent auto-compaction of tables. */
unsigned disable_auto_compact : 1;
+
+ /*
+ * Geometric sequence factor used by auto-compaction to decide which
+ * tables to compact. Defaults to 2 if unset.
+ */
+ uint8_t auto_compaction_factor;
};
/* reftable_block_stats holds statistics for a single block type */
@@ -92,7 +94,7 @@ struct reftable_stats {
struct reftable_writer *
reftable_new_writer(ssize_t (*writer_func)(void *, const void *, size_t),
int (*flush_func)(void *),
- void *writer_arg, struct reftable_write_options *opts);
+ void *writer_arg, const struct reftable_write_options *opts);
/* Set the range of update indices for the records we will add. When writing a
table into a stack, the min should be at least
diff --git a/reftable/stack.c b/reftable/stack.c
index 80266bcbab..737591125e 100644
--- a/reftable/stack.c
+++ b/reftable/stack.c
@@ -10,10 +10,11 @@ https://developers.google.com/open-source/licenses/bsd
#include "../write-or-die.h"
#include "system.h"
+#include "constants.h"
#include "merged.h"
#include "reader.h"
-#include "refname.h"
#include "reftable-error.h"
+#include "reftable-generic.h"
#include "reftable-record.h"
#include "reftable-merged.h"
#include "writer.h"
@@ -27,8 +28,6 @@ static int stack_write_compact(struct reftable_stack *st,
struct reftable_writer *wr,
size_t first, size_t last,
struct reftable_log_expiry_config *config);
-static int stack_check_addition(struct reftable_stack *st,
- const char *new_tab_name);
static void reftable_addition_close(struct reftable_addition *add);
static int reftable_stack_reload_maybe_reuse(struct reftable_stack *st,
int reuse_open);
@@ -56,15 +55,17 @@ static int reftable_fd_flush(void *arg)
}
int reftable_new_stack(struct reftable_stack **dest, const char *dir,
- struct reftable_write_options config)
+ const struct reftable_write_options *_opts)
{
struct reftable_stack *p = reftable_calloc(1, sizeof(*p));
struct strbuf list_file_name = STRBUF_INIT;
+ struct reftable_write_options opts = {0};
int err = 0;
- if (config.hash_id == 0) {
- config.hash_id = GIT_SHA1_FORMAT_ID;
- }
+ if (_opts)
+ opts = *_opts;
+ if (opts.hash_id == 0)
+ opts.hash_id = GIT_SHA1_FORMAT_ID;
*dest = NULL;
@@ -75,7 +76,7 @@ int reftable_new_stack(struct reftable_stack **dest, const char *dir,
p->list_file = strbuf_detach(&list_file_name, NULL);
p->list_fd = -1;
p->reftable_dir = xstrdup(dir);
- p->config = config;
+ p->opts = opts;
err = reftable_stack_reload_maybe_reuse(p, 1);
if (err < 0) {
@@ -132,6 +133,20 @@ int read_lines(const char *filename, char ***namesp)
return err;
}
+void reftable_stack_init_ref_iterator(struct reftable_stack *st,
+ struct reftable_iterator *it)
+{
+ merged_table_init_iter(reftable_stack_merged_table(st),
+ it, BLOCK_TYPE_REF);
+}
+
+void reftable_stack_init_log_iterator(struct reftable_stack *st,
+ struct reftable_iterator *it)
+{
+ merged_table_init_iter(reftable_stack_merged_table(st),
+ it, BLOCK_TYPE_LOG);
+}
+
struct reftable_merged_table *
reftable_stack_merged_table(struct reftable_stack *st)
{
@@ -206,7 +221,8 @@ static struct reftable_reader **stack_copy_readers(struct reftable_stack *st,
return cur;
}
-static int reftable_stack_reload_once(struct reftable_stack *st, char **names,
+static int reftable_stack_reload_once(struct reftable_stack *st,
+ const char **names,
int reuse_open)
{
size_t cur_len = !st->merged ? 0 : st->merged->stack_len;
@@ -224,7 +240,7 @@ static int reftable_stack_reload_once(struct reftable_stack *st, char **names,
while (*names) {
struct reftable_reader *rd = NULL;
- char *name = *names++;
+ const char *name = *names++;
/* this is linear; we assume compaction keeps the number of
tables under control so this is not quadratic. */
@@ -257,16 +273,14 @@ static int reftable_stack_reload_once(struct reftable_stack *st, char **names,
/* success! */
err = reftable_new_merged_table(&new_merged, new_tables,
- new_readers_len, st->config.hash_id);
+ new_readers_len, st->opts.hash_id);
if (err < 0)
goto done;
new_tables = NULL;
st->readers_len = new_readers_len;
- if (st->merged) {
- merged_table_release(st->merged);
+ if (st->merged)
reftable_merged_table_free(st->merged);
- }
if (st->readers) {
reftable_free(st->readers);
}
@@ -356,7 +370,7 @@ static int reftable_stack_reload_maybe_reuse(struct reftable_stack *st,
goto out;
}
- err = reftable_stack_reload_once(st, names, reuse_open);
+ err = reftable_stack_reload_once(st, (const char **) names, reuse_open);
if (!err)
break;
if (err != REFTABLE_NOT_EXIST_ERROR)
@@ -370,7 +384,8 @@ static int reftable_stack_reload_maybe_reuse(struct reftable_stack *st,
err = read_lines(st->list_file, &names_after);
if (err < 0)
goto out;
- if (names_equal(names_after, names)) {
+ if (names_equal((const char **) names_after,
+ (const char **) names)) {
err = REFTABLE_NOT_EXIST_ERROR;
goto out;
}
@@ -580,8 +595,8 @@ static int reftable_stack_init_addition(struct reftable_addition *add,
}
goto done;
}
- if (st->config.default_permissions) {
- if (chmod(add->lock_file->filename.buf, st->config.default_permissions) < 0) {
+ if (st->opts.default_permissions) {
+ if (chmod(add->lock_file->filename.buf, st->opts.default_permissions) < 0) {
err = REFTABLE_IO_ERROR;
goto done;
}
@@ -680,7 +695,7 @@ int reftable_addition_commit(struct reftable_addition *add)
if (err)
goto done;
- if (!add->stack->config.disable_auto_compact) {
+ if (!add->stack->opts.disable_auto_compact) {
/*
* Auto-compact the stack to keep the number of tables in
* control. It is possible that a concurrent writer is already
@@ -758,9 +773,9 @@ int reftable_addition_add(struct reftable_addition *add,
err = REFTABLE_IO_ERROR;
goto done;
}
- if (add->stack->config.default_permissions) {
+ if (add->stack->opts.default_permissions) {
if (chmod(get_tempfile_path(tab_file),
- add->stack->config.default_permissions)) {
+ add->stack->opts.default_permissions)) {
err = REFTABLE_IO_ERROR;
goto done;
}
@@ -768,7 +783,7 @@ int reftable_addition_add(struct reftable_addition *add,
tab_fd = get_tempfile_fd(tab_file);
wr = reftable_new_writer(reftable_fd_write, reftable_fd_flush, &tab_fd,
- &add->stack->config);
+ &add->stack->opts);
err = write_table(wr, arg);
if (err < 0)
goto done;
@@ -787,10 +802,6 @@ int reftable_addition_add(struct reftable_addition *add,
goto done;
}
- err = stack_check_addition(add->stack, get_tempfile_path(tab_file));
- if (err < 0)
- goto done;
-
if (wr->min_update_index < add->next_update_index) {
err = REFTABLE_API_ERROR;
goto done;
@@ -855,14 +866,14 @@ static int stack_compact_locked(struct reftable_stack *st,
}
tab_fd = get_tempfile_fd(tab_file);
- if (st->config.default_permissions &&
- chmod(get_tempfile_path(tab_file), st->config.default_permissions) < 0) {
+ if (st->opts.default_permissions &&
+ chmod(get_tempfile_path(tab_file), st->opts.default_permissions) < 0) {
err = REFTABLE_IO_ERROR;
goto done;
}
wr = reftable_new_writer(reftable_fd_write, reftable_fd_flush,
- &tab_fd, &st->config);
+ &tab_fd, &st->opts);
err = stack_write_compact(st, wr, first, last, config);
if (err < 0)
goto done;
@@ -910,13 +921,14 @@ static int stack_write_compact(struct reftable_stack *st,
st->readers[last]->max_update_index);
err = reftable_new_merged_table(&mt, subtabs, subtabs_len,
- st->config.hash_id);
+ st->opts.hash_id);
if (err < 0) {
reftable_free(subtabs);
goto done;
}
- err = reftable_merged_table_seek_ref(mt, &it, "");
+ merged_table_init_iter(mt, &it, BLOCK_TYPE_REF);
+ err = reftable_iterator_seek_ref(&it, "");
if (err < 0)
goto done;
@@ -940,7 +952,8 @@ static int stack_write_compact(struct reftable_stack *st,
}
reftable_iterator_destroy(&it);
- err = reftable_merged_table_seek_log(mt, &it, "");
+ merged_table_init_iter(mt, &it, BLOCK_TYPE_LOG);
+ err = reftable_iterator_seek_log(&it, "");
if (err < 0)
goto done;
@@ -974,10 +987,8 @@ static int stack_write_compact(struct reftable_stack *st,
done:
reftable_iterator_destroy(&it);
- if (mt) {
- merged_table_release(mt);
+ if (mt)
reftable_merged_table_free(mt);
- }
reftable_ref_record_release(&ref);
reftable_log_record_release(&log);
st->stats.entries_written += entries;
@@ -1100,9 +1111,9 @@ static int stack_compact_range(struct reftable_stack *st,
goto done;
}
- if (st->config.default_permissions) {
+ if (st->opts.default_permissions) {
if (chmod(get_lock_file_path(&tables_list_lock),
- st->config.default_permissions) < 0) {
+ st->opts.default_permissions) < 0) {
err = REFTABLE_IO_ERROR;
goto done;
}
@@ -1216,12 +1227,16 @@ static int segment_size(struct segment *s)
return s->end - s->start;
}
-struct segment suggest_compaction_segment(uint64_t *sizes, size_t n)
+struct segment suggest_compaction_segment(uint64_t *sizes, size_t n,
+ uint8_t factor)
{
struct segment seg = { 0 };
uint64_t bytes;
size_t i;
+ if (!factor)
+ factor = DEFAULT_GEOMETRIC_FACTOR;
+
/*
* If there are no tables or only a single one then we don't have to
* compact anything. The sequence is geometric by definition already.
@@ -1253,7 +1268,7 @@ struct segment suggest_compaction_segment(uint64_t *sizes, size_t n)
* 64, 32, 16, 8, 4, 3, 1
*/
for (i = n - 1; i > 0; i--) {
- if (sizes[i - 1] < sizes[i] * 2) {
+ if (sizes[i - 1] < sizes[i] * factor) {
seg.end = i + 1;
bytes = sizes[i];
break;
@@ -1279,7 +1294,7 @@ struct segment suggest_compaction_segment(uint64_t *sizes, size_t n)
uint64_t curr = bytes;
bytes += sizes[i - 1];
- if (sizes[i - 1] < curr * 2) {
+ if (sizes[i - 1] < curr * factor) {
seg.start = i - 1;
seg.bytes = bytes;
}
@@ -1292,7 +1307,7 @@ static uint64_t *stack_table_sizes_for_compaction(struct reftable_stack *st)
{
uint64_t *sizes =
reftable_calloc(st->merged->stack_len, sizeof(*sizes));
- int version = (st->config.hash_id == GIT_SHA1_FORMAT_ID) ? 1 : 2;
+ int version = (st->opts.hash_id == GIT_SHA1_FORMAT_ID) ? 1 : 2;
int overhead = header_size(version) - 1;
int i = 0;
for (i = 0; i < st->merged->stack_len; i++) {
@@ -1305,7 +1320,8 @@ int reftable_stack_auto_compact(struct reftable_stack *st)
{
uint64_t *sizes = stack_table_sizes_for_compaction(st);
struct segment seg =
- suggest_compaction_segment(sizes, st->merged->stack_len);
+ suggest_compaction_segment(sizes, st->merged->stack_len,
+ st->opts.auto_compaction_factor);
reftable_free(sizes);
if (segment_size(&seg) > 0)
return stack_compact_range_stats(st, seg.start, seg.end - 1,
@@ -1331,9 +1347,11 @@ int reftable_stack_read_ref(struct reftable_stack *st, const char *refname,
int reftable_stack_read_log(struct reftable_stack *st, const char *refname,
struct reftable_log_record *log)
{
- struct reftable_iterator it = { NULL };
- struct reftable_merged_table *mt = reftable_stack_merged_table(st);
- int err = reftable_merged_table_seek_log(mt, &it, refname);
+ struct reftable_iterator it = {0};
+ int err;
+
+ reftable_stack_init_log_iterator(st, &it);
+ err = reftable_iterator_seek_log(&it, refname);
if (err)
goto done;
@@ -1355,65 +1373,6 @@ done:
return err;
}
-static int stack_check_addition(struct reftable_stack *st,
- const char *new_tab_name)
-{
- int err = 0;
- struct reftable_block_source src = { NULL };
- struct reftable_reader *rd = NULL;
- struct reftable_table tab = { NULL };
- struct reftable_ref_record *refs = NULL;
- struct reftable_iterator it = { NULL };
- int cap = 0;
- int len = 0;
- int i = 0;
-
- if (st->config.skip_name_check)
- return 0;
-
- err = reftable_block_source_from_file(&src, new_tab_name);
- if (err < 0)
- goto done;
-
- err = reftable_new_reader(&rd, &src, new_tab_name);
- if (err < 0)
- goto done;
-
- err = reftable_reader_seek_ref(rd, &it, "");
- if (err > 0) {
- err = 0;
- goto done;
- }
- if (err < 0)
- goto done;
-
- while (1) {
- struct reftable_ref_record ref = { NULL };
- err = reftable_iterator_next_ref(&it, &ref);
- if (err > 0)
- break;
- if (err < 0)
- goto done;
-
- REFTABLE_ALLOC_GROW(refs, len + 1, cap);
- refs[len++] = ref;
- }
-
- reftable_table_from_merged_table(&tab, reftable_stack_merged_table(st));
-
- err = validate_ref_record_addition(tab, refs, len);
-
-done:
- for (i = 0; i < len; i++) {
- reftable_ref_record_release(&refs[i]);
- }
-
- free(refs);
- reftable_iterator_destroy(&it);
- reftable_reader_free(rd);
- return err;
-}
-
static int is_table_name(const char *s)
{
const char *dot = strrchr(s, '.');
@@ -1500,11 +1459,11 @@ done:
int reftable_stack_print_directory(const char *stackdir, uint32_t hash_id)
{
struct reftable_stack *stack = NULL;
- struct reftable_write_options cfg = { .hash_id = hash_id };
+ struct reftable_write_options opts = { .hash_id = hash_id };
struct reftable_merged_table *merged = NULL;
struct reftable_table table = { NULL };
- int err = reftable_new_stack(&stack, stackdir, cfg);
+ int err = reftable_new_stack(&stack, stackdir, &opts);
if (err < 0)
goto done;
diff --git a/reftable/stack.h b/reftable/stack.h
index d43efa4760..5b45cff4f7 100644
--- a/reftable/stack.h
+++ b/reftable/stack.h
@@ -20,7 +20,7 @@ struct reftable_stack {
char *reftable_dir;
- struct reftable_write_options config;
+ struct reftable_write_options opts;
struct reftable_reader **readers;
size_t readers_len;
@@ -35,6 +35,7 @@ struct segment {
uint64_t bytes;
};
-struct segment suggest_compaction_segment(uint64_t *sizes, size_t n);
+struct segment suggest_compaction_segment(uint64_t *sizes, size_t n,
+ uint8_t factor);
#endif
diff --git a/reftable/stack_test.c b/reftable/stack_test.c
index 1df3ffce52..e3c11e6a6e 100644
--- a/reftable/stack_test.c
+++ b/reftable/stack_test.c
@@ -83,7 +83,7 @@ static void test_read_file(void)
char out[1024] = "line1\n\nline2\nline3";
int n, err;
char **names = NULL;
- char *want[] = { "line1", "line2", "line3" };
+ const char *want[] = { "line1", "line2", "line3" };
int i = 0;
EXPECT(fd > 0);
@@ -102,29 +102,6 @@ static void test_read_file(void)
(void) remove(fn);
}
-static void test_parse_names(void)
-{
- char buf[] = "line\n";
- char **names = NULL;
- parse_names(buf, strlen(buf), &names);
-
- EXPECT(NULL != names[0]);
- EXPECT(0 == strcmp(names[0], "line"));
- EXPECT(NULL == names[1]);
- free_names(names);
-}
-
-static void test_names_equal(void)
-{
- char *a[] = { "a", "b", "c", NULL };
- char *b[] = { "a", "b", "d", NULL };
- char *c[] = { "a", "b", NULL };
-
- EXPECT(names_equal(a, a));
- EXPECT(!names_equal(a, b));
- EXPECT(!names_equal(a, c));
-}
-
static int write_test_ref(struct reftable_writer *wr, void *arg)
{
struct reftable_ref_record *ref = arg;
@@ -150,20 +127,20 @@ static void test_reftable_stack_add_one(void)
char *dir = get_tmp_dir(__LINE__);
struct strbuf scratch = STRBUF_INIT;
int mask = umask(002);
- struct reftable_write_options cfg = {
+ struct reftable_write_options opts = {
.default_permissions = 0660,
};
struct reftable_stack *st = NULL;
int err;
struct reftable_ref_record ref = {
- .refname = "HEAD",
+ .refname = (char *) "HEAD",
.update_index = 1,
.value_type = REFTABLE_REF_SYMREF,
- .value.symref = "master",
+ .value.symref = (char *) "master",
};
struct reftable_ref_record dest = { NULL };
struct stat stat_result = { 0 };
- err = reftable_new_stack(&st, dir, cfg);
+ err = reftable_new_stack(&st, dir, &opts);
EXPECT_ERR(err);
err = reftable_stack_add(st, &write_test_ref, &ref);
@@ -186,7 +163,7 @@ static void test_reftable_stack_add_one(void)
strbuf_addstr(&scratch, "/tables.list");
err = stat(scratch.buf, &stat_result);
EXPECT(!err);
- EXPECT((stat_result.st_mode & 0777) == cfg.default_permissions);
+ EXPECT((stat_result.st_mode & 0777) == opts.default_permissions);
strbuf_reset(&scratch);
strbuf_addstr(&scratch, dir);
@@ -195,7 +172,7 @@ static void test_reftable_stack_add_one(void)
strbuf_addstr(&scratch, st->readers[0]->name);
err = stat(scratch.buf, &stat_result);
EXPECT(!err);
- EXPECT((stat_result.st_mode & 0777) == cfg.default_permissions);
+ EXPECT((stat_result.st_mode & 0777) == opts.default_permissions);
#else
(void) stat_result;
#endif
@@ -209,33 +186,33 @@ static void test_reftable_stack_add_one(void)
static void test_reftable_stack_uptodate(void)
{
- struct reftable_write_options cfg = { 0 };
+ struct reftable_write_options opts = { 0 };
struct reftable_stack *st1 = NULL;
struct reftable_stack *st2 = NULL;
char *dir = get_tmp_dir(__LINE__);
int err;
struct reftable_ref_record ref1 = {
- .refname = "HEAD",
+ .refname = (char *) "HEAD",
.update_index = 1,
.value_type = REFTABLE_REF_SYMREF,
- .value.symref = "master",
+ .value.symref = (char *) "master",
};
struct reftable_ref_record ref2 = {
- .refname = "branch2",
+ .refname = (char *) "branch2",
.update_index = 2,
.value_type = REFTABLE_REF_SYMREF,
- .value.symref = "master",
+ .value.symref = (char *) "master",
};
/* simulate multi-process access to the same stack
by creating two stacks for the same directory.
*/
- err = reftable_new_stack(&st1, dir, cfg);
+ err = reftable_new_stack(&st1, dir, &opts);
EXPECT_ERR(err);
- err = reftable_new_stack(&st2, dir, cfg);
+ err = reftable_new_stack(&st2, dir, &opts);
EXPECT_ERR(err);
err = reftable_stack_add(st1, &write_test_ref, &ref1);
@@ -257,22 +234,20 @@ static void test_reftable_stack_uptodate(void)
static void test_reftable_stack_transaction_api(void)
{
char *dir = get_tmp_dir(__LINE__);
-
- struct reftable_write_options cfg = { 0 };
+ struct reftable_write_options opts = { 0 };
struct reftable_stack *st = NULL;
int err;
struct reftable_addition *add = NULL;
struct reftable_ref_record ref = {
- .refname = "HEAD",
+ .refname = (char *) "HEAD",
.update_index = 1,
.value_type = REFTABLE_REF_SYMREF,
- .value.symref = "master",
+ .value.symref = (char *) "master",
};
struct reftable_ref_record dest = { NULL };
-
- err = reftable_new_stack(&st, dir, cfg);
+ err = reftable_new_stack(&st, dir, &opts);
EXPECT_ERR(err);
reftable_addition_destroy(add);
@@ -301,19 +276,19 @@ static void test_reftable_stack_transaction_api(void)
static void test_reftable_stack_transaction_api_performs_auto_compaction(void)
{
char *dir = get_tmp_dir(__LINE__);
- struct reftable_write_options cfg = {0};
+ struct reftable_write_options opts = {0};
struct reftable_addition *add = NULL;
struct reftable_stack *st = NULL;
int i, n = 20, err;
- err = reftable_new_stack(&st, dir, cfg);
+ err = reftable_new_stack(&st, dir, &opts);
EXPECT_ERR(err);
for (i = 0; i <= n; i++) {
struct reftable_ref_record ref = {
.update_index = reftable_stack_next_update_index(st),
.value_type = REFTABLE_REF_SYMREF,
- .value.symref = "master",
+ .value.symref = (char *) "master",
};
char name[100];
@@ -325,7 +300,7 @@ static void test_reftable_stack_transaction_api_performs_auto_compaction(void)
* we can ensure that we indeed honor this setting and have
* better control over when exactly auto compaction runs.
*/
- st->config.disable_auto_compact = i != n;
+ st->opts.disable_auto_compact = i != n;
err = reftable_stack_new_addition(&add, st);
EXPECT_ERR(err);
@@ -356,18 +331,18 @@ static void test_reftable_stack_transaction_api_performs_auto_compaction(void)
static void test_reftable_stack_auto_compaction_fails_gracefully(void)
{
struct reftable_ref_record ref = {
- .refname = "refs/heads/master",
+ .refname = (char *) "refs/heads/master",
.update_index = 1,
.value_type = REFTABLE_REF_VAL1,
.value.val1 = {0x01},
};
- struct reftable_write_options cfg = {0};
+ struct reftable_write_options opts = {0};
struct reftable_stack *st;
struct strbuf table_path = STRBUF_INIT;
char *dir = get_tmp_dir(__LINE__);
int err;
- err = reftable_new_stack(&st, dir, cfg);
+ err = reftable_new_stack(&st, dir, &opts);
EXPECT_ERR(err);
err = reftable_stack_add(st, write_test_ref, &ref);
@@ -396,44 +371,6 @@ static void test_reftable_stack_auto_compaction_fails_gracefully(void)
clear_dir(dir);
}
-static void test_reftable_stack_validate_refname(void)
-{
- struct reftable_write_options cfg = { 0 };
- struct reftable_stack *st = NULL;
- int err;
- char *dir = get_tmp_dir(__LINE__);
-
- int i;
- struct reftable_ref_record ref = {
- .refname = "a/b",
- .update_index = 1,
- .value_type = REFTABLE_REF_SYMREF,
- .value.symref = "master",
- };
- char *additions[] = { "a", "a/b/c" };
-
- err = reftable_new_stack(&st, dir, cfg);
- EXPECT_ERR(err);
-
- err = reftable_stack_add(st, &write_test_ref, &ref);
- EXPECT_ERR(err);
-
- for (i = 0; i < ARRAY_SIZE(additions); i++) {
- struct reftable_ref_record ref = {
- .refname = additions[i],
- .update_index = 1,
- .value_type = REFTABLE_REF_SYMREF,
- .value.symref = "master",
- };
-
- err = reftable_stack_add(st, &write_test_ref, &ref);
- EXPECT(err == REFTABLE_NAME_CONFLICT);
- }
-
- reftable_stack_destroy(st);
- clear_dir(dir);
-}
-
static int write_error(struct reftable_writer *wr, void *arg)
{
return *((int *)arg);
@@ -442,24 +379,23 @@ static int write_error(struct reftable_writer *wr, void *arg)
static void test_reftable_stack_update_index_check(void)
{
char *dir = get_tmp_dir(__LINE__);
-
- struct reftable_write_options cfg = { 0 };
+ struct reftable_write_options opts = { 0 };
struct reftable_stack *st = NULL;
int err;
struct reftable_ref_record ref1 = {
- .refname = "name1",
+ .refname = (char *) "name1",
.update_index = 1,
.value_type = REFTABLE_REF_SYMREF,
- .value.symref = "master",
+ .value.symref = (char *) "master",
};
struct reftable_ref_record ref2 = {
- .refname = "name2",
+ .refname = (char *) "name2",
.update_index = 1,
.value_type = REFTABLE_REF_SYMREF,
- .value.symref = "master",
+ .value.symref = (char *) "master",
};
- err = reftable_new_stack(&st, dir, cfg);
+ err = reftable_new_stack(&st, dir, &opts);
EXPECT_ERR(err);
err = reftable_stack_add(st, &write_test_ref, &ref1);
@@ -474,12 +410,11 @@ static void test_reftable_stack_update_index_check(void)
static void test_reftable_stack_lock_failure(void)
{
char *dir = get_tmp_dir(__LINE__);
-
- struct reftable_write_options cfg = { 0 };
+ struct reftable_write_options opts = { 0 };
struct reftable_stack *st = NULL;
int err, i;
- err = reftable_new_stack(&st, dir, cfg);
+ err = reftable_new_stack(&st, dir, &opts);
EXPECT_ERR(err);
for (i = -1; i != REFTABLE_EMPTY_TABLE_ERROR; i--) {
err = reftable_stack_add(st, &write_error, &i);
@@ -494,7 +429,7 @@ static void test_reftable_stack_add(void)
{
int i = 0;
int err = 0;
- struct reftable_write_options cfg = {
+ struct reftable_write_options opts = {
.exact_log_message = 1,
.default_permissions = 0660,
.disable_auto_compact = 1,
@@ -507,7 +442,7 @@ static void test_reftable_stack_add(void)
struct stat stat_result;
int N = ARRAY_SIZE(refs);
- err = reftable_new_stack(&st, dir, cfg);
+ err = reftable_new_stack(&st, dir, &opts);
EXPECT_ERR(err);
for (i = 0; i < N; i++) {
@@ -566,7 +501,7 @@ static void test_reftable_stack_add(void)
strbuf_addstr(&path, "/tables.list");
err = stat(path.buf, &stat_result);
EXPECT(!err);
- EXPECT((stat_result.st_mode & 0777) == cfg.default_permissions);
+ EXPECT((stat_result.st_mode & 0777) == opts.default_permissions);
strbuf_reset(&path);
strbuf_addstr(&path, dir);
@@ -575,7 +510,7 @@ static void test_reftable_stack_add(void)
strbuf_addstr(&path, st->readers[0]->name);
err = stat(path.buf, &stat_result);
EXPECT(!err);
- EXPECT((stat_result.st_mode & 0777) == cfg.default_permissions);
+ EXPECT((stat_result.st_mode & 0777) == opts.default_permissions);
#else
(void) stat_result;
#endif
@@ -593,13 +528,13 @@ static void test_reftable_stack_add(void)
static void test_reftable_stack_log_normalize(void)
{
int err = 0;
- struct reftable_write_options cfg = {
+ struct reftable_write_options opts = {
0,
};
struct reftable_stack *st = NULL;
char *dir = get_tmp_dir(__LINE__);
struct reftable_log_record input = {
- .refname = "branch",
+ .refname = (char *) "branch",
.update_index = 1,
.value_type = REFTABLE_LOG_UPDATE,
.value = {
@@ -617,14 +552,14 @@ static void test_reftable_stack_log_normalize(void)
.update_index = 1,
};
- err = reftable_new_stack(&st, dir, cfg);
+ err = reftable_new_stack(&st, dir, &opts);
EXPECT_ERR(err);
- input.value.update.message = "one\ntwo";
+ input.value.update.message = (char *) "one\ntwo";
err = reftable_stack_add(st, &write_test_log, &arg);
EXPECT(err == REFTABLE_API_ERROR);
- input.value.update.message = "one";
+ input.value.update.message = (char *) "one";
err = reftable_stack_add(st, &write_test_log, &arg);
EXPECT_ERR(err);
@@ -632,7 +567,7 @@ static void test_reftable_stack_log_normalize(void)
EXPECT_ERR(err);
EXPECT(0 == strcmp(dest.value.update.message, "one\n"));
- input.value.update.message = "two\n";
+ input.value.update.message = (char *) "two\n";
arg.update_index = 2;
err = reftable_stack_add(st, &write_test_log, &arg);
EXPECT_ERR(err);
@@ -650,8 +585,7 @@ static void test_reftable_stack_tombstone(void)
{
int i = 0;
char *dir = get_tmp_dir(__LINE__);
-
- struct reftable_write_options cfg = { 0 };
+ struct reftable_write_options opts = { 0 };
struct reftable_stack *st = NULL;
int err;
struct reftable_ref_record refs[2] = { { NULL } };
@@ -660,8 +594,7 @@ static void test_reftable_stack_tombstone(void)
struct reftable_ref_record dest = { NULL };
struct reftable_log_record log_dest = { NULL };
-
- err = reftable_new_stack(&st, dir, cfg);
+ err = reftable_new_stack(&st, dir, &opts);
EXPECT_ERR(err);
/* even entries add the refs, odd entries delete them. */
@@ -729,35 +662,34 @@ static void test_reftable_stack_tombstone(void)
static void test_reftable_stack_hash_id(void)
{
char *dir = get_tmp_dir(__LINE__);
-
- struct reftable_write_options cfg = { 0 };
+ struct reftable_write_options opts = { 0 };
struct reftable_stack *st = NULL;
int err;
struct reftable_ref_record ref = {
- .refname = "master",
+ .refname = (char *) "master",
.value_type = REFTABLE_REF_SYMREF,
- .value.symref = "target",
+ .value.symref = (char *) "target",
.update_index = 1,
};
- struct reftable_write_options cfg32 = { .hash_id = GIT_SHA256_FORMAT_ID };
+ struct reftable_write_options opts32 = { .hash_id = GIT_SHA256_FORMAT_ID };
struct reftable_stack *st32 = NULL;
- struct reftable_write_options cfg_default = { 0 };
+ struct reftable_write_options opts_default = { 0 };
struct reftable_stack *st_default = NULL;
struct reftable_ref_record dest = { NULL };
- err = reftable_new_stack(&st, dir, cfg);
+ err = reftable_new_stack(&st, dir, &opts);
EXPECT_ERR(err);
err = reftable_stack_add(st, &write_test_ref, &ref);
EXPECT_ERR(err);
/* can't read it with the wrong hash ID. */
- err = reftable_new_stack(&st32, dir, cfg32);
+ err = reftable_new_stack(&st32, dir, &opts32);
EXPECT(err == REFTABLE_FORMAT_ERROR);
- /* check that we can read it back with default config too. */
- err = reftable_new_stack(&st_default, dir, cfg_default);
+ /* check that we can read it back with default opts too. */
+ err = reftable_new_stack(&st_default, dir, &opts_default);
EXPECT_ERR(err);
err = reftable_stack_read_ref(st_default, "master", &dest);
@@ -774,7 +706,7 @@ static void test_suggest_compaction_segment(void)
{
uint64_t sizes[] = { 512, 64, 17, 16, 9, 9, 9, 16, 2, 16 };
struct segment min =
- suggest_compaction_segment(sizes, ARRAY_SIZE(sizes));
+ suggest_compaction_segment(sizes, ARRAY_SIZE(sizes), 2);
EXPECT(min.start == 1);
EXPECT(min.end == 10);
}
@@ -783,15 +715,14 @@ static void test_suggest_compaction_segment_nothing(void)
{
uint64_t sizes[] = { 64, 32, 16, 8, 4, 2 };
struct segment result =
- suggest_compaction_segment(sizes, ARRAY_SIZE(sizes));
+ suggest_compaction_segment(sizes, ARRAY_SIZE(sizes), 2);
EXPECT(result.start == result.end);
}
static void test_reflog_expire(void)
{
char *dir = get_tmp_dir(__LINE__);
-
- struct reftable_write_options cfg = { 0 };
+ struct reftable_write_options opts = { 0 };
struct reftable_stack *st = NULL;
struct reftable_log_record logs[20] = { { NULL } };
int N = ARRAY_SIZE(logs) - 1;
@@ -802,8 +733,7 @@ static void test_reflog_expire(void)
};
struct reftable_log_record log = { NULL };
-
- err = reftable_new_stack(&st, dir, cfg);
+ err = reftable_new_stack(&st, dir, &opts);
EXPECT_ERR(err);
for (i = 1; i <= N; i++) {
@@ -866,21 +796,19 @@ static int write_nothing(struct reftable_writer *wr, void *arg)
static void test_empty_add(void)
{
- struct reftable_write_options cfg = { 0 };
+ struct reftable_write_options opts = { 0 };
struct reftable_stack *st = NULL;
int err;
char *dir = get_tmp_dir(__LINE__);
-
struct reftable_stack *st2 = NULL;
-
- err = reftable_new_stack(&st, dir, cfg);
+ err = reftable_new_stack(&st, dir, &opts);
EXPECT_ERR(err);
err = reftable_stack_add(st, &write_nothing, NULL);
EXPECT_ERR(err);
- err = reftable_new_stack(&st2, dir, cfg);
+ err = reftable_new_stack(&st2, dir, &opts);
EXPECT_ERR(err);
clear_dir(dir);
reftable_stack_destroy(st);
@@ -899,16 +827,15 @@ static int fastlog2(uint64_t sz)
static void test_reftable_stack_auto_compaction(void)
{
- struct reftable_write_options cfg = {
+ struct reftable_write_options opts = {
.disable_auto_compact = 1,
};
struct reftable_stack *st = NULL;
char *dir = get_tmp_dir(__LINE__);
-
int err, i;
int N = 100;
- err = reftable_new_stack(&st, dir, cfg);
+ err = reftable_new_stack(&st, dir, &opts);
EXPECT_ERR(err);
for (i = 0; i < N; i++) {
@@ -917,7 +844,7 @@ static void test_reftable_stack_auto_compaction(void)
.refname = name,
.update_index = reftable_stack_next_update_index(st),
.value_type = REFTABLE_REF_SYMREF,
- .value.symref = "master",
+ .value.symref = (char *) "master",
};
snprintf(name, sizeof(name), "branch%04d", i);
@@ -938,20 +865,20 @@ static void test_reftable_stack_auto_compaction(void)
static void test_reftable_stack_add_performs_auto_compaction(void)
{
- struct reftable_write_options cfg = { 0 };
+ struct reftable_write_options opts = { 0 };
struct reftable_stack *st = NULL;
struct strbuf refname = STRBUF_INIT;
char *dir = get_tmp_dir(__LINE__);
int err, i, n = 20;
- err = reftable_new_stack(&st, dir, cfg);
+ err = reftable_new_stack(&st, dir, &opts);
EXPECT_ERR(err);
for (i = 0; i <= n; i++) {
struct reftable_ref_record ref = {
.update_index = reftable_stack_next_update_index(st),
.value_type = REFTABLE_REF_SYMREF,
- .value.symref = "master",
+ .value.symref = (char *) "master",
};
/*
@@ -959,7 +886,7 @@ static void test_reftable_stack_add_performs_auto_compaction(void)
* we can ensure that we indeed honor this setting and have
* better control over when exactly auto compaction runs.
*/
- st->config.disable_auto_compact = i != n;
+ st->opts.disable_auto_compact = i != n;
strbuf_reset(&refname);
strbuf_addf(&refname, "branch-%04d", i);
@@ -986,14 +913,13 @@ static void test_reftable_stack_add_performs_auto_compaction(void)
static void test_reftable_stack_compaction_concurrent(void)
{
- struct reftable_write_options cfg = { 0 };
+ struct reftable_write_options opts = { 0 };
struct reftable_stack *st1 = NULL, *st2 = NULL;
char *dir = get_tmp_dir(__LINE__);
-
int err, i;
int N = 3;
- err = reftable_new_stack(&st1, dir, cfg);
+ err = reftable_new_stack(&st1, dir, &opts);
EXPECT_ERR(err);
for (i = 0; i < N; i++) {
@@ -1002,7 +928,7 @@ static void test_reftable_stack_compaction_concurrent(void)
.refname = name,
.update_index = reftable_stack_next_update_index(st1),
.value_type = REFTABLE_REF_SYMREF,
- .value.symref = "master",
+ .value.symref = (char *) "master",
};
snprintf(name, sizeof(name), "branch%04d", i);
@@ -1010,7 +936,7 @@ static void test_reftable_stack_compaction_concurrent(void)
EXPECT_ERR(err);
}
- err = reftable_new_stack(&st2, dir, cfg);
+ err = reftable_new_stack(&st2, dir, &opts);
EXPECT_ERR(err);
err = reftable_stack_compact_all(st1, NULL);
@@ -1036,14 +962,13 @@ static void unclean_stack_close(struct reftable_stack *st)
static void test_reftable_stack_compaction_concurrent_clean(void)
{
- struct reftable_write_options cfg = { 0 };
+ struct reftable_write_options opts = { 0 };
struct reftable_stack *st1 = NULL, *st2 = NULL, *st3 = NULL;
char *dir = get_tmp_dir(__LINE__);
-
int err, i;
int N = 3;
- err = reftable_new_stack(&st1, dir, cfg);
+ err = reftable_new_stack(&st1, dir, &opts);
EXPECT_ERR(err);
for (i = 0; i < N; i++) {
@@ -1052,7 +977,7 @@ static void test_reftable_stack_compaction_concurrent_clean(void)
.refname = name,
.update_index = reftable_stack_next_update_index(st1),
.value_type = REFTABLE_REF_SYMREF,
- .value.symref = "master",
+ .value.symref = (char *) "master",
};
snprintf(name, sizeof(name), "branch%04d", i);
@@ -1060,7 +985,7 @@ static void test_reftable_stack_compaction_concurrent_clean(void)
EXPECT_ERR(err);
}
- err = reftable_new_stack(&st2, dir, cfg);
+ err = reftable_new_stack(&st2, dir, &opts);
EXPECT_ERR(err);
err = reftable_stack_compact_all(st1, NULL);
@@ -1069,7 +994,7 @@ static void test_reftable_stack_compaction_concurrent_clean(void)
unclean_stack_close(st1);
unclean_stack_close(st2);
- err = reftable_new_stack(&st3, dir, cfg);
+ err = reftable_new_stack(&st3, dir, &opts);
EXPECT_ERR(err);
err = reftable_stack_clean(st3);
@@ -1086,8 +1011,6 @@ static void test_reftable_stack_compaction_concurrent_clean(void)
int stack_test_main(int argc, const char *argv[])
{
RUN_TEST(test_empty_add);
- RUN_TEST(test_names_equal);
- RUN_TEST(test_parse_names);
RUN_TEST(test_read_file);
RUN_TEST(test_reflog_expire);
RUN_TEST(test_reftable_stack_add);
@@ -1105,7 +1028,6 @@ int stack_test_main(int argc, const char *argv[])
RUN_TEST(test_reftable_stack_auto_compaction_fails_gracefully);
RUN_TEST(test_reftable_stack_update_index_check);
RUN_TEST(test_reftable_stack_uptodate);
- RUN_TEST(test_reftable_stack_validate_refname);
RUN_TEST(test_suggest_compaction_segment);
RUN_TEST(test_suggest_compaction_segment_nothing);
return 0;
diff --git a/reftable/system.h b/reftable/system.h
index 5d8b6dede5..d0cabd5d17 100644
--- a/reftable/system.h
+++ b/reftable/system.h
@@ -15,7 +15,7 @@ https://developers.google.com/open-source/licenses/bsd
#include "lockfile.h"
#include "strbuf.h"
#include "tempfile.h"
-#include "hash-ll.h" /* hash ID, sizes.*/
+#include "hash.h" /* hash ID, sizes.*/
#include "dir.h" /* remove_dir_recursively, for tests.*/
int hash_size(uint32_t id);
diff --git a/reftable/writer.c b/reftable/writer.c
index 1d9ff0fbfa..45b3e9ce1f 100644
--- a/reftable/writer.c
+++ b/reftable/writer.c
@@ -109,7 +109,7 @@ static void writer_reinit_block_writer(struct reftable_writer *w, uint8_t typ)
block_start = header_size(writer_version(w));
}
- strbuf_release(&w->last_key);
+ strbuf_reset(&w->last_key);
block_writer_init(&w->block_writer_data, typ, w->block,
w->opts.block_size, block_start,
hash_size(w->opts.hash_id));
@@ -117,25 +117,26 @@ static void writer_reinit_block_writer(struct reftable_writer *w, uint8_t typ)
w->block_writer->restart_interval = w->opts.restart_interval;
}
-static struct strbuf reftable_empty_strbuf = STRBUF_INIT;
-
struct reftable_writer *
reftable_new_writer(ssize_t (*writer_func)(void *, const void *, size_t),
int (*flush_func)(void *),
- void *writer_arg, struct reftable_write_options *opts)
+ void *writer_arg, const struct reftable_write_options *_opts)
{
struct reftable_writer *wp = reftable_calloc(1, sizeof(*wp));
+ struct reftable_write_options opts = {0};
+
+ if (_opts)
+ opts = *_opts;
+ options_set_defaults(&opts);
+ if (opts.block_size >= (1 << 24))
+ BUG("configured block size exceeds 16MB");
+
strbuf_init(&wp->block_writer_data.last_key, 0);
- options_set_defaults(opts);
- if (opts->block_size >= (1 << 24)) {
- /* TODO - error return? */
- abort();
- }
- wp->last_key = reftable_empty_strbuf;
- REFTABLE_CALLOC_ARRAY(wp->block, opts->block_size);
+ strbuf_init(&wp->last_key, 0);
+ REFTABLE_CALLOC_ARRAY(wp->block, opts.block_size);
wp->write = writer_func;
wp->write_arg = writer_arg;
- wp->opts = *opts;
+ wp->opts = opts;
wp->flush = flush_func;
writer_reinit_block_writer(wp, BLOCK_TYPE_REF);
@@ -149,11 +150,21 @@ void reftable_writer_set_limits(struct reftable_writer *w, uint64_t min,
w->max_update_index = max;
}
+static void writer_release(struct reftable_writer *w)
+{
+ if (w) {
+ reftable_free(w->block);
+ w->block = NULL;
+ block_writer_release(&w->block_writer_data);
+ w->block_writer = NULL;
+ writer_clear_index(w);
+ strbuf_release(&w->last_key);
+ }
+}
+
void reftable_writer_free(struct reftable_writer *w)
{
- if (!w)
- return;
- reftable_free(w->block);
+ writer_release(w);
reftable_free(w);
}
@@ -209,7 +220,8 @@ static int writer_add_record(struct reftable_writer *w,
struct reftable_record *rec)
{
struct strbuf key = STRBUF_INIT;
- int err = -1;
+ int err;
+
reftable_record_key(rec, &key);
if (strbuf_cmp(&w->last_key, &key) >= 0) {
err = REFTABLE_API_ERROR;
@@ -218,27 +230,42 @@ static int writer_add_record(struct reftable_writer *w,
strbuf_reset(&w->last_key);
strbuf_addbuf(&w->last_key, &key);
- if (!w->block_writer) {
+ if (!w->block_writer)
writer_reinit_block_writer(w, reftable_record_type(rec));
- }
- assert(block_writer_type(w->block_writer) == reftable_record_type(rec));
+ if (block_writer_type(w->block_writer) != reftable_record_type(rec))
+ BUG("record of type %d added to writer of type %d",
+ reftable_record_type(rec), block_writer_type(w->block_writer));
- if (block_writer_add(w->block_writer, rec) == 0) {
+ /*
+ * Try to add the record to the writer. If this succeeds then we're
+ * done. Otherwise the block writer may have hit the block size limit
+ * and needs to be flushed.
+ */
+ if (!block_writer_add(w->block_writer, rec)) {
err = 0;
goto done;
}
+ /*
+ * The current block is full, so we need to flush and reinitialize the
+ * writer to start writing the next block.
+ */
err = writer_flush_block(w);
- if (err < 0) {
+ if (err < 0)
goto done;
- }
-
writer_reinit_block_writer(w, reftable_record_type(rec));
+
+ /*
+ * Try to add the record to the writer again. If this still fails then
+ * the record does not fit into the block size.
+ *
+ * TODO: it would be great to have `block_writer_add()` return proper
+ * error codes so that we don't have to second-guess the failure
+ * mode here.
+ */
err = block_writer_add(w->block_writer, rec);
- if (err == -1) {
- /* we are writing into memory, so an error can only mean it
- * doesn't fit. */
+ if (err) {
err = REFTABLE_ENTRY_TOO_BIG_ERROR;
goto done;
}
@@ -452,7 +479,7 @@ static int writer_finish_section(struct reftable_writer *w)
bstats->max_index_level = max_level;
/* Reinit lastKey, as the next section can start with any key. */
- w->last_key.len = 0;
+ strbuf_reset(&w->last_key);
return 0;
}
@@ -627,74 +654,87 @@ int reftable_writer_close(struct reftable_writer *w)
}
done:
- /* free up memory. */
- block_writer_release(&w->block_writer_data);
- writer_clear_index(w);
- strbuf_release(&w->last_key);
+ writer_release(w);
return err;
}
static void writer_clear_index(struct reftable_writer *w)
{
- for (size_t i = 0; i < w->index_len; i++)
+ for (size_t i = 0; w->index && i < w->index_len; i++)
strbuf_release(&w->index[i].last_key);
FREE_AND_NULL(w->index);
w->index_len = 0;
w->index_cap = 0;
}
-static const int debug = 0;
-
static int writer_flush_nonempty_block(struct reftable_writer *w)
{
+ struct reftable_index_record index_record = {
+ .last_key = STRBUF_INIT,
+ };
uint8_t typ = block_writer_type(w->block_writer);
- struct reftable_block_stats *bstats =
- writer_reftable_block_stats(w, typ);
- uint64_t block_typ_off = (bstats->blocks == 0) ? w->next : 0;
- int raw_bytes = block_writer_finish(w->block_writer);
- int padding = 0;
- int err = 0;
- struct reftable_index_record ir = { .last_key = STRBUF_INIT };
+ struct reftable_block_stats *bstats;
+ int raw_bytes, padding = 0, err;
+ uint64_t block_typ_off;
+
+ /*
+ * Finish the current block. This will cause the block writer to emit
+ * restart points and potentially compress records in case we are
+ * writing a log block.
+ *
+ * Note that this is still happening in memory.
+ */
+ raw_bytes = block_writer_finish(w->block_writer);
if (raw_bytes < 0)
return raw_bytes;
- if (!w->opts.unpadded && typ != BLOCK_TYPE_LOG) {
+ /*
+ * By default, all records except for log records are padded to the
+ * block size.
+ */
+ if (!w->opts.unpadded && typ != BLOCK_TYPE_LOG)
padding = w->opts.block_size - raw_bytes;
- }
- if (block_typ_off > 0) {
+ bstats = writer_reftable_block_stats(w, typ);
+ block_typ_off = (bstats->blocks == 0) ? w->next : 0;
+ if (block_typ_off > 0)
bstats->offset = block_typ_off;
- }
-
bstats->entries += w->block_writer->entries;
bstats->restarts += w->block_writer->restart_len;
bstats->blocks++;
w->stats.blocks++;
- if (debug) {
- fprintf(stderr, "block %c off %" PRIu64 " sz %d (%d)\n", typ,
- w->next, raw_bytes,
- get_be24(w->block + w->block_writer->header_off + 1));
- }
-
- if (w->next == 0) {
+ /*
+ * If this is the first block we're writing to the table then we need
+ * to also write the reftable header.
+ */
+ if (!w->next)
writer_write_header(w, w->block);
- }
err = padded_write(w, w->block, raw_bytes, padding);
if (err < 0)
return err;
+ /*
+ * Add an index record for every block that we're writing. If we end up
+ * having more than a threshold of index records we will end up writing
+ * an index section in `writer_finish_section()`. Each index record
+ * contains the last record key of the block it is indexing as well as
+ * the offset of that block.
+ *
+ * Note that this also applies when flushing index blocks, in which
+ * case we will end up with a multi-level index.
+ */
REFTABLE_ALLOC_GROW(w->index, w->index_len + 1, w->index_cap);
-
- ir.offset = w->next;
- strbuf_reset(&ir.last_key);
- strbuf_addbuf(&ir.last_key, &w->block_writer->last_key);
- w->index[w->index_len] = ir;
-
+ index_record.offset = w->next;
+ strbuf_reset(&index_record.last_key);
+ strbuf_addbuf(&index_record.last_key, &w->block_writer->last_key);
+ w->index[w->index_len] = index_record;
w->index_len++;
+
w->next += padding + raw_bytes;
w->block_writer = NULL;
+
return 0;
}
diff --git a/remote-curl.c b/remote-curl.c
index 0b6d7815fd..4adcf25ed6 100644
--- a/remote-curl.c
+++ b/remote-curl.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "git-curl-compat.h"
#include "config.h"
@@ -58,9 +60,9 @@ struct options {
static struct options options;
static struct string_list cas_options = STRING_LIST_INIT_DUP;
-static int set_option(const char *name, const char *value)
+static int set_option(const char *name, size_t namelen, const char *value)
{
- if (!strcmp(name, "verbosity")) {
+ if (!strncmp(name, "verbosity", namelen)) {
char *end;
int v = strtol(value, &end, 10);
if (value == end || *end)
@@ -68,7 +70,7 @@ static int set_option(const char *name, const char *value)
options.verbosity = v;
return 0;
}
- else if (!strcmp(name, "progress")) {
+ else if (!strncmp(name, "progress", namelen)) {
if (!strcmp(value, "true"))
options.progress = 1;
else if (!strcmp(value, "false"))
@@ -77,7 +79,7 @@ static int set_option(const char *name, const char *value)
return -1;
return 0;
}
- else if (!strcmp(name, "depth")) {
+ else if (!strncmp(name, "depth", namelen)) {
char *end;
unsigned long v = strtoul(value, &end, 10);
if (value == end || *end)
@@ -85,15 +87,15 @@ static int set_option(const char *name, const char *value)
options.depth = v;
return 0;
}
- else if (!strcmp(name, "deepen-since")) {
+ else if (!strncmp(name, "deepen-since", namelen)) {
options.deepen_since = xstrdup(value);
return 0;
}
- else if (!strcmp(name, "deepen-not")) {
+ else if (!strncmp(name, "deepen-not", namelen)) {
string_list_append(&options.deepen_not, value);
return 0;
}
- else if (!strcmp(name, "deepen-relative")) {
+ else if (!strncmp(name, "deepen-relative", namelen)) {
if (!strcmp(value, "true"))
options.deepen_relative = 1;
else if (!strcmp(value, "false"))
@@ -102,7 +104,7 @@ static int set_option(const char *name, const char *value)
return -1;
return 0;
}
- else if (!strcmp(name, "followtags")) {
+ else if (!strncmp(name, "followtags", namelen)) {
if (!strcmp(value, "true"))
options.followtags = 1;
else if (!strcmp(value, "false"))
@@ -111,7 +113,7 @@ static int set_option(const char *name, const char *value)
return -1;
return 0;
}
- else if (!strcmp(name, "dry-run")) {
+ else if (!strncmp(name, "dry-run", namelen)) {
if (!strcmp(value, "true"))
options.dry_run = 1;
else if (!strcmp(value, "false"))
@@ -120,7 +122,7 @@ static int set_option(const char *name, const char *value)
return -1;
return 0;
}
- else if (!strcmp(name, "check-connectivity")) {
+ else if (!strncmp(name, "check-connectivity", namelen)) {
if (!strcmp(value, "true"))
options.check_self_contained_and_connected = 1;
else if (!strcmp(value, "false"))
@@ -129,7 +131,7 @@ static int set_option(const char *name, const char *value)
return -1;
return 0;
}
- else if (!strcmp(name, "cas")) {
+ else if (!strncmp(name, "cas", namelen)) {
struct strbuf val = STRBUF_INIT;
strbuf_addstr(&val, "--force-with-lease=");
if (*value != '"')
@@ -139,7 +141,7 @@ static int set_option(const char *name, const char *value)
string_list_append(&cas_options, val.buf);
strbuf_release(&val);
return 0;
- } else if (!strcmp(name, TRANS_OPT_FORCE_IF_INCLUDES)) {
+ } else if (!strncmp(name, TRANS_OPT_FORCE_IF_INCLUDES, namelen)) {
if (!strcmp(value, "true"))
options.force_if_includes = 1;
else if (!strcmp(value, "false"))
@@ -147,7 +149,7 @@ static int set_option(const char *name, const char *value)
else
return -1;
return 0;
- } else if (!strcmp(name, "cloning")) {
+ } else if (!strncmp(name, "cloning", namelen)) {
if (!strcmp(value, "true"))
options.cloning = 1;
else if (!strcmp(value, "false"))
@@ -155,7 +157,7 @@ static int set_option(const char *name, const char *value)
else
return -1;
return 0;
- } else if (!strcmp(name, "update-shallow")) {
+ } else if (!strncmp(name, "update-shallow", namelen)) {
if (!strcmp(value, "true"))
options.update_shallow = 1;
else if (!strcmp(value, "false"))
@@ -163,7 +165,7 @@ static int set_option(const char *name, const char *value)
else
return -1;
return 0;
- } else if (!strcmp(name, "pushcert")) {
+ } else if (!strncmp(name, "pushcert", namelen)) {
if (!strcmp(value, "true"))
options.push_cert = SEND_PACK_PUSH_CERT_ALWAYS;
else if (!strcmp(value, "false"))
@@ -173,7 +175,7 @@ static int set_option(const char *name, const char *value)
else
return -1;
return 0;
- } else if (!strcmp(name, "atomic")) {
+ } else if (!strncmp(name, "atomic", namelen)) {
if (!strcmp(value, "true"))
options.atomic = 1;
else if (!strcmp(value, "false"))
@@ -181,7 +183,7 @@ static int set_option(const char *name, const char *value)
else
return -1;
return 0;
- } else if (!strcmp(name, "push-option")) {
+ } else if (!strncmp(name, "push-option", namelen)) {
if (*value != '"')
string_list_append(&options.push_options, value);
else {
@@ -192,7 +194,7 @@ static int set_option(const char *name, const char *value)
strbuf_detach(&unquoted, NULL));
}
return 0;
- } else if (!strcmp(name, "family")) {
+ } else if (!strncmp(name, "family", namelen)) {
if (!strcmp(value, "ipv4"))
git_curl_ipresolve = CURL_IPRESOLVE_V4;
else if (!strcmp(value, "ipv6"))
@@ -202,16 +204,16 @@ static int set_option(const char *name, const char *value)
else
return -1;
return 0;
- } else if (!strcmp(name, "from-promisor")) {
+ } else if (!strncmp(name, "from-promisor", namelen)) {
options.from_promisor = 1;
return 0;
- } else if (!strcmp(name, "refetch")) {
+ } else if (!strncmp(name, "refetch", namelen)) {
options.refetch = 1;
return 0;
- } else if (!strcmp(name, "filter")) {
+ } else if (!strncmp(name, "filter", namelen)) {
options.filter = xstrdup(value);
return 0;
- } else if (!strcmp(name, "object-format")) {
+ } else if (!strncmp(name, "object-format", namelen)) {
options.object_format = 1;
if (strcmp(value, "true"))
die(_("unknown value for object-format: %s"), value);
@@ -266,12 +268,23 @@ static struct ref *parse_git_refs(struct discovery *heads, int for_push)
return list;
}
+/*
+ * Try to detect the hash algorithm used by the remote repository when using
+ * the dumb HTTP transport. As dumb transports cannot tell us the object hash
+ * directly have to derive it from the advertised ref lengths.
+ */
static const struct git_hash_algo *detect_hash_algo(struct discovery *heads)
{
const char *p = memchr(heads->buf, '\t', heads->len);
int algo;
+
+ /*
+ * In case the remote has no refs we have no way to reliably determine
+ * the object hash used by that repository. In that case we simply fall
+ * back to SHA1, which may or may not be correct.
+ */
if (!p)
- return the_hash_algo;
+ return &hash_algos[GIT_HASH_SHA1];
algo = hash_algo_by_length((p - heads->buf) / 2);
if (algo == GIT_HASH_UNKNOWN)
@@ -295,6 +308,12 @@ static struct ref *parse_info_refs(struct discovery *heads)
"is this a git repository?",
transport_anonymize_url(url.buf));
+ /*
+ * Set the repository's hash algo to whatever we have just detected.
+ * This ensures that we can correctly parse the remote references.
+ */
+ repo_set_hash_algo(the_repository, hash_algo_by_ptr(options.hash_algo));
+
data = heads->buf;
start = NULL;
mid = data;
@@ -889,7 +908,7 @@ static curl_off_t xcurl_off_t(size_t len)
static int post_rpc(struct rpc_state *rpc, int stateless_connect, int flush_received)
{
struct active_request_slot *slot;
- struct curl_slist *headers = http_copy_default_headers();
+ struct curl_slist *headers = NULL;
int use_gzip = rpc->gzip_request;
char *gzip_body = NULL;
size_t gzip_size = 0;
@@ -922,20 +941,24 @@ static int post_rpc(struct rpc_state *rpc, int stateless_connect, int flush_rece
do {
err = probe_rpc(rpc, &results);
if (err == HTTP_REAUTH)
- credential_fill(&http_auth);
+ credential_fill(&http_auth, 0);
} while (err == HTTP_REAUTH);
if (err != HTTP_OK)
return -1;
- if (results.auth_avail & CURLAUTH_GSSNEGOTIATE)
+ if (results.auth_avail & CURLAUTH_GSSNEGOTIATE || http_auth.authtype)
needs_100_continue = 1;
}
+retry:
+ headers = http_copy_default_headers();
headers = curl_slist_append(headers, rpc->hdr_content_type);
headers = curl_slist_append(headers, rpc->hdr_accept);
headers = curl_slist_append(headers, needs_100_continue ?
"Expect: 100-continue" : "Expect:");
+ headers = http_append_auth_header(&http_auth, headers);
+
/* Add Accept-Language header */
if (rpc->hdr_accept_language)
headers = curl_slist_append(headers, rpc->hdr_accept_language);
@@ -944,7 +967,6 @@ static int post_rpc(struct rpc_state *rpc, int stateless_connect, int flush_rece
if (rpc->protocol_header)
headers = curl_slist_append(headers, rpc->protocol_header);
-retry:
slot = get_active_slot();
curl_easy_setopt(slot->curl, CURLOPT_NOBODY, 0);
@@ -1041,7 +1063,8 @@ retry:
rpc->any_written = 0;
err = run_slot(slot, NULL);
if (err == HTTP_REAUTH && !large_request) {
- credential_fill(&http_auth);
+ credential_fill(&http_auth, 0);
+ curl_slist_free_all(headers);
goto retry;
}
if (err != HTTP_OK)
@@ -1553,7 +1576,7 @@ int cmd_main(int argc, const char **argv)
if (argc > 2) {
end_url_with_slash(&url, argv[2]);
} else {
- end_url_with_slash(&url, remote->url[0]);
+ end_url_with_slash(&url, remote->url.v[0]);
}
http_init(remote, url.buf, 0);
@@ -1584,15 +1607,16 @@ int cmd_main(int argc, const char **argv)
parse_push(&buf);
} else if (skip_prefix(buf.buf, "option ", &arg)) {
- char *value = strchr(arg, ' ');
+ const char *value = strchrnul(arg, ' ');
+ size_t arglen = value - arg;
int result;
- if (value)
- *value++ = '\0';
+ if (*value)
+ value++; /* skip over SP */
else
value = "true";
- result = set_option(arg, value);
+ result = set_option(arg, arglen, value);
if (!result)
printf("ok\n");
else if (result < 0)
diff --git a/remote.c b/remote.c
index 2b650b813b..f43cf5e7a4 100644
--- a/remote.c
+++ b/remote.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "abspath.h"
#include "config.h"
@@ -32,10 +34,10 @@ struct counted_string {
static int valid_remote(const struct remote *remote)
{
- return (!!remote->url) || (!!remote->foreign_vcs);
+ return !!remote->url.nr;
}
-static const char *alias_url(const char *url, struct rewrites *r)
+static char *alias_url(const char *url, struct rewrites *r)
{
int i, j;
struct counted_string *longest;
@@ -56,36 +58,43 @@ static const char *alias_url(const char *url, struct rewrites *r)
}
}
if (!longest)
- return url;
+ return NULL;
return xstrfmt("%s%s", r->rewrite[longest_i]->base, url + longest->len);
}
static void add_url(struct remote *remote, const char *url)
{
- ALLOC_GROW(remote->url, remote->url_nr + 1, remote->url_alloc);
- remote->url[remote->url_nr++] = url;
+ if (*url)
+ strvec_push(&remote->url, url);
+ else
+ strvec_clear(&remote->url);
}
static void add_pushurl(struct remote *remote, const char *pushurl)
{
- ALLOC_GROW(remote->pushurl, remote->pushurl_nr + 1, remote->pushurl_alloc);
- remote->pushurl[remote->pushurl_nr++] = pushurl;
+ if (*pushurl)
+ strvec_push(&remote->pushurl, pushurl);
+ else
+ strvec_clear(&remote->pushurl);
}
static void add_pushurl_alias(struct remote_state *remote_state,
struct remote *remote, const char *url)
{
- const char *pushurl = alias_url(url, &remote_state->rewrites_push);
- if (pushurl != url)
- add_pushurl(remote, pushurl);
+ char *alias = alias_url(url, &remote_state->rewrites_push);
+ if (alias)
+ add_pushurl(remote, alias);
+ free(alias);
}
static void add_url_alias(struct remote_state *remote_state,
struct remote *remote, const char *url)
{
- add_url(remote, alias_url(url, &remote_state->rewrites));
+ char *alias = alias_url(url, &remote_state->rewrites);
+ add_url(remote, alias ? alias : url);
add_pushurl_alias(remote_state, remote, url);
+ free(alias);
}
struct remotes_hash_key {
@@ -147,18 +156,12 @@ static struct remote *make_remote(struct remote_state *remote_state,
static void remote_clear(struct remote *remote)
{
- int i;
-
free((char *)remote->name);
free((char *)remote->foreign_vcs);
- for (i = 0; i < remote->url_nr; i++)
- free((char *)remote->url[i]);
- FREE_AND_NULL(remote->url);
+ strvec_clear(&remote->url);
+ strvec_clear(&remote->pushurl);
- for (i = 0; i < remote->pushurl_nr; i++)
- free((char *)remote->pushurl[i]);
- FREE_AND_NULL(remote->pushurl);
free((char *)remote->receivepack);
free((char *)remote->uploadpack);
FREE_AND_NULL(remote->http_proxy);
@@ -292,7 +295,7 @@ static void read_remotes_file(struct remote_state *remote_state,
if (skip_prefix(buf.buf, "URL:", &v))
add_url_alias(remote_state, remote,
- xstrdup(skip_spaces(v)));
+ skip_spaces(v));
else if (skip_prefix(buf.buf, "Push:", &v))
refspec_append(&remote->push, skip_spaces(v));
else if (skip_prefix(buf.buf, "Pull:", &v))
@@ -305,7 +308,7 @@ static void read_remotes_file(struct remote_state *remote_state,
static void read_branches_file(struct remote_state *remote_state,
struct remote *remote)
{
- char *frag;
+ char *frag, *to_free = NULL;
struct strbuf buf = STRBUF_INIT;
FILE *f = fopen_or_warn(git_path("branches/%s", remote->name), "r");
@@ -333,9 +336,9 @@ static void read_branches_file(struct remote_state *remote_state,
if (frag)
*(frag++) = '\0';
else
- frag = (char *)git_default_branch_name(0);
+ frag = to_free = repo_default_branch_name(the_repository, 0);
- add_url_alias(remote_state, remote, strbuf_detach(&buf, NULL));
+ add_url_alias(remote_state, remote, buf.buf);
refspec_appendf(&remote->fetch, "refs/heads/%s:refs/heads/%s",
frag, remote->name);
@@ -345,6 +348,9 @@ static void read_branches_file(struct remote_state *remote_state,
*/
refspec_appendf(&remote->push, "HEAD:refs/heads/%s", frag);
remote->fetch_tags = 1; /* always auto-follow */
+
+ strbuf_release(&buf);
+ free(to_free);
}
static int handle_config(const char *key, const char *value,
@@ -428,29 +434,27 @@ static int handle_config(const char *key, const char *value,
else if (!strcmp(subkey, "prunetags"))
remote->prune_tags = git_config_bool(key, value);
else if (!strcmp(subkey, "url")) {
- const char *v;
- if (git_config_string(&v, key, value))
- return -1;
- add_url(remote, v);
+ if (!value)
+ return config_error_nonbool(key);
+ add_url(remote, value);
} else if (!strcmp(subkey, "pushurl")) {
- const char *v;
- if (git_config_string(&v, key, value))
- return -1;
- add_pushurl(remote, v);
+ if (!value)
+ return config_error_nonbool(key);
+ add_pushurl(remote, value);
} else if (!strcmp(subkey, "push")) {
- const char *v;
+ char *v;
if (git_config_string(&v, key, value))
return -1;
refspec_append(&remote->push, v);
- free((char *)v);
+ free(v);
} else if (!strcmp(subkey, "fetch")) {
- const char *v;
+ char *v;
if (git_config_string(&v, key, value))
return -1;
refspec_append(&remote->fetch, v);
- free((char *)v);
+ free(v);
} else if (!strcmp(subkey, "receivepack")) {
- const char *v;
+ char *v;
if (git_config_string(&v, key, value))
return -1;
if (!remote->receivepack)
@@ -458,7 +462,7 @@ static int handle_config(const char *key, const char *value,
else
error(_("more than one receivepack given, using the first"));
} else if (!strcmp(subkey, "uploadpack")) {
- const char *v;
+ char *v;
if (git_config_string(&v, key, value))
return -1;
if (!remote->uploadpack)
@@ -471,10 +475,10 @@ static int handle_config(const char *key, const char *value,
else if (!strcmp(value, "--tags"))
remote->fetch_tags = 2;
} else if (!strcmp(subkey, "proxy")) {
- return git_config_string((const char **)&remote->http_proxy,
+ return git_config_string(&remote->http_proxy,
key, value);
} else if (!strcmp(subkey, "proxyauthmethod")) {
- return git_config_string((const char **)&remote->http_proxy_authmethod,
+ return git_config_string(&remote->http_proxy_authmethod,
key, value);
} else if (!strcmp(subkey, "vcs")) {
return git_config_string(&remote->foreign_vcs, key, value);
@@ -489,20 +493,25 @@ static void alias_all_urls(struct remote_state *remote_state)
int add_pushurl_aliases;
if (!remote_state->remotes[i])
continue;
- for (j = 0; j < remote_state->remotes[i]->pushurl_nr; j++) {
- remote_state->remotes[i]->pushurl[j] =
- alias_url(remote_state->remotes[i]->pushurl[j],
- &remote_state->rewrites);
+ for (j = 0; j < remote_state->remotes[i]->pushurl.nr; j++) {
+ char *alias = alias_url(remote_state->remotes[i]->pushurl.v[j],
+ &remote_state->rewrites);
+ if (alias)
+ strvec_replace(&remote_state->remotes[i]->pushurl,
+ j, alias);
}
- add_pushurl_aliases = remote_state->remotes[i]->pushurl_nr == 0;
- for (j = 0; j < remote_state->remotes[i]->url_nr; j++) {
+ add_pushurl_aliases = remote_state->remotes[i]->pushurl.nr == 0;
+ for (j = 0; j < remote_state->remotes[i]->url.nr; j++) {
+ char *alias;
if (add_pushurl_aliases)
add_pushurl_alias(
remote_state, remote_state->remotes[i],
- remote_state->remotes[i]->url[j]);
- remote_state->remotes[i]->url[j] =
- alias_url(remote_state->remotes[i]->url[j],
+ remote_state->remotes[i]->url.v[j]);
+ alias = alias_url(remote_state->remotes[i]->url.v[j],
&remote_state->rewrites);
+ if (alias)
+ strvec_replace(&remote_state->remotes[i]->url,
+ j, alias);
}
}
}
@@ -642,10 +651,10 @@ static void validate_remote_url(struct remote *remote)
else
die(_("unrecognized value transfer.credentialsInUrl: '%s'"), value);
- for (i = 0; i < remote->url_nr; i++) {
+ for (i = 0; i < remote->url.nr; i++) {
struct url_info url_info = { 0 };
- if (!url_normalize(remote->url[i], &url_info) ||
+ if (!url_normalize(remote->url.v[i], &url_info) ||
!url_info.passwd_off)
goto loop_cleanup;
@@ -819,13 +828,18 @@ struct ref *ref_remove_duplicates(struct ref *ref_map)
int remote_has_url(struct remote *remote, const char *url)
{
int i;
- for (i = 0; i < remote->url_nr; i++) {
- if (!strcmp(remote->url[i], url))
+ for (i = 0; i < remote->url.nr; i++) {
+ if (!strcmp(remote->url.v[i], url))
return 1;
}
return 0;
}
+struct strvec *push_url_of_remote(struct remote *remote)
+{
+ return remote->pushurl.nr ? &remote->pushurl : &remote->url;
+}
+
static int match_name_with_pattern(const char *key, const char *name,
const char *value, char **result)
{
@@ -1162,7 +1176,7 @@ static void tail_link_ref(struct ref *ref, struct ref ***tail)
static struct ref *alloc_delete_ref(void)
{
struct ref *ref = alloc_ref("(delete)");
- oidclr(&ref->new_oid);
+ oidclr(&ref->new_oid, the_repository->hash_algo);
return ref;
}
@@ -1198,8 +1212,10 @@ static char *guess_ref(const char *name, struct ref *peer)
{
struct strbuf buf = STRBUF_INIT;
- const char *r = resolve_ref_unsafe(peer->name, RESOLVE_REF_READING,
- NULL, NULL);
+ const char *r = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
+ peer->name,
+ RESOLVE_REF_READING,
+ NULL, NULL);
if (!r)
return NULL;
@@ -1316,9 +1332,10 @@ static int match_explicit(struct ref *src, struct ref *dst,
if (!dst_value) {
int flag;
- dst_value = resolve_ref_unsafe(matched_src->name,
- RESOLVE_REF_READING,
- NULL, &flag);
+ dst_value = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
+ matched_src->name,
+ RESOLVE_REF_READING,
+ NULL, &flag);
if (!dst_value ||
((flag & REF_ISSYMREF) &&
!starts_with(dst_value, "refs/heads/")))
@@ -1773,7 +1790,7 @@ void set_ref_status_for_push(struct ref *remote_refs, int send_mirror,
if (!reject_reason && !ref->deletion && !is_null_oid(&ref->old_oid)) {
if (starts_with(ref->name, "refs/tags/"))
reject_reason = REF_STATUS_REJECT_ALREADY_EXISTS;
- else if (!repo_has_object_file(the_repository, &ref->old_oid))
+ else if (!repo_has_object_file_with_flags(the_repository, &ref->old_oid, OBJECT_INFO_SKIP_FETCH_OBJECT))
reject_reason = REF_STATUS_REJECT_FETCH_FIRST;
else if (!lookup_commit_reference_gently(the_repository, &ref->old_oid, 1) ||
!lookup_commit_reference_gently(the_repository, &ref->new_oid, 1))
@@ -1882,7 +1899,7 @@ const char *branch_get_upstream(struct branch *branch, struct strbuf *err)
* or because it is not a real branch, and get_branch
* auto-vivified it?
*/
- if (!ref_exists(branch->refname))
+ if (!refs_ref_exists(get_main_ref_store(the_repository), branch->refname))
return error_buf(err, _("no such branch: '%s'"),
branch->name);
return error_buf(err,
@@ -2168,13 +2185,13 @@ static int stat_branch_pair(const char *branch_name, const char *base,
struct strvec argv = STRVEC_INIT;
/* Cannot stat if what we used to build on no longer exists */
- if (read_ref(base, &oid))
+ if (refs_read_ref(get_main_ref_store(the_repository), base, &oid))
return -1;
theirs = lookup_commit_reference(the_repository, &oid);
if (!theirs)
return -1;
- if (read_ref(branch_name, &oid))
+ if (refs_read_ref(get_main_ref_store(the_repository), branch_name, &oid))
return -1;
ours = lookup_commit_reference(the_repository, &oid);
if (!ours)
@@ -2278,7 +2295,8 @@ int format_tracking_info(struct branch *branch, struct strbuf *sb,
upstream_is_gone = 1;
}
- base = shorten_unambiguous_ref(full_base, 0);
+ base = refs_shorten_unambiguous_ref(get_main_ref_store(the_repository),
+ full_base, 0);
if (upstream_is_gone) {
strbuf_addf(sb,
_("Your branch is based on '%s', but the upstream is gone.\n"),
@@ -2358,7 +2376,8 @@ struct ref *get_local_heads(void)
{
struct ref *local_refs = NULL, **local_tail = &local_refs;
- for_each_ref(one_local_ref, &local_tail);
+ refs_for_each_ref(get_main_ref_store(the_repository), one_local_ref,
+ &local_tail);
return local_refs;
}
@@ -2383,11 +2402,13 @@ struct ref *guess_remote_head(const struct ref *head,
/* If a remote branch exists with the default branch name, let's use it. */
if (!all) {
- char *ref = xstrfmt("refs/heads/%s",
- git_default_branch_name(0));
+ char *default_branch = repo_default_branch_name(the_repository, 0);
+ char *ref = xstrfmt("refs/heads/%s", default_branch);
r = find_ref_by_name(refs, ref);
free(ref);
+ free(default_branch);
+
if (r && oideq(&r->old_oid, &head->old_oid))
return copy_ref(r);
@@ -2468,7 +2489,8 @@ struct ref *get_stale_heads(struct refspec *rs, struct ref *fetch_map)
for (ref = fetch_map; ref; ref = ref->next)
string_list_append(&ref_names, ref->name);
string_list_sort(&ref_names);
- for_each_ref(get_stale_heads_cb, &info);
+ refs_for_each_ref(get_main_ref_store(the_repository),
+ get_stale_heads_cb, &info);
string_list_clear(&ref_names, 0);
return stale_refs;
}
@@ -2521,7 +2543,7 @@ static int parse_push_cas_option(struct push_cas_option *cas, const char *arg, i
if (!*colon)
entry->use_tracking = 1;
else if (!colon[1])
- oidclr(&entry->expect);
+ oidclr(&entry->expect, the_repository->hash_algo);
else if (repo_get_oid(the_repository, colon + 1, &entry->expect))
return error(_("cannot parse expected object name '%s'"),
colon + 1);
@@ -2553,7 +2575,7 @@ static int remote_tracking(struct remote *remote, const char *refname,
dst = apply_refspecs(&remote->fetch, refname);
if (!dst)
return -1; /* no tracking ref for refname at remote */
- if (read_ref(dst, oid))
+ if (refs_read_ref(get_main_ref_store(the_repository), dst, oid))
return -1; /* we know what the tracking ref is but we cannot read it */
*dst_refname = dst;
@@ -2659,12 +2681,16 @@ static int is_reachable_in_reflog(const char *local, const struct ref *remote)
* Get the timestamp from the latest entry
* of the remote-tracking ref's reflog.
*/
- for_each_reflog_ent_reverse(remote->tracking_ref, peek_reflog, &date);
+ refs_for_each_reflog_ent_reverse(get_main_ref_store(the_repository),
+ remote->tracking_ref, peek_reflog,
+ &date);
cb.remote_commit = commit;
cb.local_commits = &arr;
cb.remote_reflog_timestamp = date;
- ret = for_each_reflog_ent_reverse(local, check_and_collect_until, &cb);
+ ret = refs_for_each_reflog_ent_reverse(get_main_ref_store(the_repository),
+ local, check_and_collect_until,
+ &cb);
/* We found an entry in the reflog. */
if (ret > 0)
@@ -2719,7 +2745,7 @@ static void apply_cas(struct push_cas_option *cas,
else if (remote_tracking(remote, ref->name,
&ref->old_oid_expect,
&ref->tracking_ref))
- oidclr(&ref->old_oid_expect);
+ oidclr(&ref->old_oid_expect, the_repository->hash_algo);
else
ref->check_reachable = cas->use_force_if_includes;
return;
@@ -2733,7 +2759,7 @@ static void apply_cas(struct push_cas_option *cas,
if (remote_tracking(remote, ref->name,
&ref->old_oid_expect,
&ref->tracking_ref))
- oidclr(&ref->old_oid_expect);
+ oidclr(&ref->old_oid_expect, the_repository->hash_algo);
else
ref->check_reachable = cas->use_force_if_includes;
}
diff --git a/remote.h b/remote.h
index dfd4837e60..b901b56746 100644
--- a/remote.h
+++ b/remote.h
@@ -1,9 +1,10 @@
#ifndef REMOTE_H
#define REMOTE_H
-#include "hash-ll.h"
+#include "hash.h"
#include "hashmap.h"
#include "refspec.h"
+#include "strvec.h"
struct option;
struct transport_ls_refs_options;
@@ -46,7 +47,7 @@ struct remote_state {
struct hashmap branches_hash;
struct branch *current_branch;
- const char *pushremote_name;
+ char *pushremote_name;
struct rewrites rewrites;
struct rewrites rewrites_push;
@@ -65,19 +66,12 @@ struct remote {
int origin, configured_in_repo;
- const char *foreign_vcs;
+ char *foreign_vcs;
/* An array of all of the url_nr URLs configured for the remote */
- const char **url;
-
- int url_nr;
- int url_alloc;
-
+ struct strvec url;
/* An array of all of the pushurl_nr push URLs configured for the remote */
- const char **pushurl;
-
- int pushurl_nr;
- int pushurl_alloc;
+ struct strvec pushurl;
struct refspec push;
@@ -129,6 +123,7 @@ typedef int each_remote_fn(struct remote *remote, void *priv);
int for_each_remote(each_remote_fn fn, void *priv);
int remote_has_url(struct remote *remote, const char *url);
+struct strvec *push_url_of_remote(struct remote *remote);
struct ref_push_report {
const char *ref_name;
@@ -200,7 +195,7 @@ struct ref {
};
#define REF_NORMAL (1u << 0)
-#define REF_HEADS (1u << 1)
+#define REF_BRANCHES (1u << 1)
#define REF_TAGS (1u << 2)
struct ref *find_ref_by_name(const struct ref *list, const char *name);
@@ -309,9 +304,9 @@ struct branch {
const char *refname;
/* The name of the remote listed in the configuration. */
- const char *remote_name;
+ char *remote_name;
- const char *pushremote_name;
+ char *pushremote_name;
/* An array of the "merge" lines in the configuration. */
const char **merge_name;
diff --git a/replace-object.c b/replace-object.c
index 523215589d..59252d565e 100644
--- a/replace-object.c
+++ b/replace-object.c
@@ -8,18 +8,19 @@
#include "repository.h"
#include "commit.h"
-static int register_replace_ref(struct repository *r,
- const char *refname,
+static int register_replace_ref(const char *refname,
const struct object_id *oid,
int flag UNUSED,
- void *cb_data UNUSED)
+ void *cb_data)
{
+ struct repository *r = cb_data;
+
/* Get sha1 from refname */
const char *slash = strrchr(refname, '/');
const char *hash = slash ? slash + 1 : refname;
struct replace_object *repl_obj = xmalloc(sizeof(*repl_obj));
- if (get_oid_hex(hash, &repl_obj->original.oid)) {
+ if (get_oid_hex_algop(hash, &repl_obj->original.oid, r->hash_algo)) {
free(repl_obj);
warning(_("bad replace ref name: %s"), refname);
return 0;
@@ -50,7 +51,8 @@ void prepare_replace_object(struct repository *r)
xmalloc(sizeof(*r->objects->replace_map));
oidmap_init(r->objects->replace_map, 0);
- for_each_replace_ref(r, register_replace_ref, NULL);
+ refs_for_each_replace_ref(get_main_ref_store(r),
+ register_replace_ref, r);
r->objects->replace_map_initialized = 1;
pthread_mutex_unlock(&r->objects->replace_mutex);
diff --git a/repo-settings.c b/repo-settings.c
index a0b590bc6c..2b4e68731b 100644
--- a/repo-settings.c
+++ b/repo-settings.c
@@ -23,6 +23,7 @@ void prepare_repo_settings(struct repository *r)
int value;
const char *strval;
int manyfiles;
+ int read_changed_paths;
if (!r->gitdir)
BUG("Cannot add settings for uninitialized repository");
@@ -54,7 +55,10 @@ void prepare_repo_settings(struct repository *r)
/* Commit graph config or default, does not cascade (simple) */
repo_cfg_bool(r, "core.commitgraph", &r->settings.core_commit_graph, 1);
repo_cfg_int(r, "commitgraph.generationversion", &r->settings.commit_graph_generation_version, 2);
- repo_cfg_bool(r, "commitgraph.readchangedpaths", &r->settings.commit_graph_read_changed_paths, 1);
+ repo_cfg_bool(r, "commitgraph.readchangedpaths", &read_changed_paths, 1);
+ repo_cfg_int(r, "commitgraph.changedpathsversion",
+ &r->settings.commit_graph_changed_paths_version,
+ read_changed_paths ? -1 : 0);
repo_cfg_bool(r, "gc.writecommitgraph", &r->settings.gc_write_commit_graph, 1);
repo_cfg_bool(r, "fetch.writecommitgraph", &r->settings.fetch_write_commit_graph, 0);
diff --git a/repository.c b/repository.c
index e15b416944..9825a30899 100644
--- a/repository.c
+++ b/repository.c
@@ -1,8 +1,3 @@
-/*
- * not really _using_ the compat macros, just make sure the_index
- * declaration matches the definition in this file.
- */
-#define USE_THE_INDEX_VARIABLE
#include "git-compat-util.h"
#include "abspath.h"
#include "repository.h"
@@ -19,24 +14,71 @@
#include "sparse-index.h"
#include "trace2.h"
#include "promisor-remote.h"
+#include "refs.h"
+
+/*
+ * We do not define `USE_THE_REPOSITORY_VARIABLE` in this file because we do
+ * not want to rely on functions that implicitly use `the_repository`. This
+ * means that the `extern` declaration of `the_repository` isn't visible here,
+ * which makes sparse unhappy. We thus declare it here.
+ */
+extern struct repository *the_repository;
/* The main repository */
static struct repository the_repo;
-struct repository *the_repository;
-struct index_state the_index;
+struct repository *the_repository = &the_repo;
-void initialize_the_repository(void)
+/*
+ * An escape hatch: if we hit a bug in the production code that fails
+ * to set an appropriate hash algorithm (most likely to happen when
+ * running outside a repository), we can tell the user who reported
+ * the crash to set the environment variable to "sha1" (all lowercase)
+ * to revert to the historical behaviour of defaulting to SHA-1.
+ */
+static void set_default_hash_algo(struct repository *repo)
{
- the_repository = &the_repo;
+ const char *hash_name;
+ int algo;
+
+ hash_name = getenv("GIT_TEST_DEFAULT_HASH_ALGO");
+ if (!hash_name)
+ return;
+ algo = hash_algo_by_name(hash_name);
+ if (algo == GIT_HASH_UNKNOWN)
+ return;
- the_repo.index = &the_index;
- the_repo.objects = raw_object_store_new();
- the_repo.remote_state = remote_state_new();
- the_repo.parsed_objects = parsed_object_pool_new();
+ repo_set_hash_algo(repo, algo);
+}
- index_state_init(&the_index, the_repository);
+void initialize_repository(struct repository *repo)
+{
+ repo->objects = raw_object_store_new();
+ repo->remote_state = remote_state_new();
+ repo->parsed_objects = parsed_object_pool_new();
+ ALLOC_ARRAY(repo->index, 1);
+ index_state_init(repo->index, repo);
- repo_set_hash_algo(&the_repo, GIT_HASH_SHA1);
+ /*
+ * When a command runs inside a repository, it learns what
+ * hash algorithm is in use from the repository, but some
+ * commands are designed to work outside a repository, yet
+ * they want to access the_hash_algo, if only for the length
+ * of the hashed value to see if their input looks like a
+ * plausible hash value.
+ *
+ * We are in the process of identifying such code paths and
+ * giving them an appropriate default individually; any
+ * unconverted code paths that try to access the_hash_algo
+ * will thus fail. The end-users however have an escape hatch
+ * to set GIT_TEST_DEFAULT_HASH_ALGO environment variable to
+ * "sha1" to get back the old behaviour of defaulting to SHA-1.
+ *
+ * This escape hatch is deliberately kept unadvertised, so
+ * that they see crashes and we can get a report before
+ * telling them about it.
+ */
+ if (repo == the_repository)
+ set_default_hash_algo(repo);
}
static void expand_base_dir(char **out, const char *in,
@@ -114,7 +156,8 @@ void repo_set_compat_hash_algo(struct repository *repo, int algo)
repo_read_loose_object_map(repo);
}
-void repo_set_ref_storage_format(struct repository *repo, unsigned int format)
+void repo_set_ref_storage_format(struct repository *repo,
+ enum ref_storage_format format)
{
repo->ref_storage_format = format;
}
@@ -188,9 +231,7 @@ int repo_init(struct repository *repo,
struct repository_format format = REPOSITORY_FORMAT_INIT;
memset(repo, 0, sizeof(*repo));
- repo->objects = raw_object_store_new();
- repo->parsed_objects = parsed_object_pool_new();
- repo->remote_state = remote_state_new();
+ initialize_repository(repo);
if (repo_init_gitdir(repo, gitdir))
goto error;
@@ -282,6 +323,9 @@ static void repo_clear_path_cache(struct repo_path_cache *cache)
void repo_clear(struct repository *repo)
{
+ struct hashmap_iter iter;
+ struct strmap_entry *e;
+
FREE_AND_NULL(repo->gitdir);
FREE_AND_NULL(repo->commondir);
FREE_AND_NULL(repo->graft_file);
@@ -295,6 +339,8 @@ void repo_clear(struct repository *repo)
parsed_object_pool_clear(repo->parsed_objects);
FREE_AND_NULL(repo->parsed_objects);
+ FREE_AND_NULL(repo->settings.fsmonitor);
+
if (repo->config) {
git_configset_clear(repo->config);
FREE_AND_NULL(repo->config);
@@ -307,8 +353,7 @@ void repo_clear(struct repository *repo)
if (repo->index) {
discard_index(repo->index);
- if (repo->index != &the_index)
- FREE_AND_NULL(repo->index);
+ FREE_AND_NULL(repo->index);
}
if (repo->promisor_remote_config) {
@@ -321,6 +366,14 @@ void repo_clear(struct repository *repo)
FREE_AND_NULL(repo->remote_state);
}
+ strmap_for_each_entry(&repo->submodule_ref_stores, &iter, e)
+ ref_store_release(e->value);
+ strmap_clear(&repo->submodule_ref_stores, 1);
+
+ strmap_for_each_entry(&repo->worktree_ref_stores, &iter, e)
+ ref_store_release(e->value);
+ strmap_clear(&repo->worktree_ref_stores, 1);
+
repo_clear_path_cache(&repo->cached_paths);
}
diff --git a/repository.h b/repository.h
index 268436779c..af6ea0a62c 100644
--- a/repository.h
+++ b/repository.h
@@ -1,6 +1,8 @@
#ifndef REPOSITORY_H
#define REPOSITORY_H
+#include "strmap.h"
+
struct config_set;
struct fsmonitor_settings;
struct git_hash_algo;
@@ -24,16 +26,18 @@ enum fetch_negotiation_setting {
FETCH_NEGOTIATION_NOOP,
};
-#define REF_STORAGE_FORMAT_UNKNOWN 0
-#define REF_STORAGE_FORMAT_FILES 1
-#define REF_STORAGE_FORMAT_REFTABLE 2
+enum ref_storage_format {
+ REF_STORAGE_FORMAT_UNKNOWN,
+ REF_STORAGE_FORMAT_FILES,
+ REF_STORAGE_FORMAT_REFTABLE,
+};
struct repo_settings {
int initialized;
int core_commit_graph;
int commit_graph_generation_version;
- int commit_graph_read_changed_paths;
+ int commit_graph_changed_paths_version;
int gc_write_commit_graph;
int fetch_write_commit_graph;
int command_requires_full_index;
@@ -109,6 +113,18 @@ struct repository {
struct ref_store *refs_private;
/*
+ * A strmap of ref_stores, stored by submodule name, accessible via
+ * `repo_get_submodule_ref_store()`.
+ */
+ struct strmap submodule_ref_stores;
+
+ /*
+ * A strmap of ref_stores, stored by worktree id, accessible via
+ * `get_worktree_ref_store()`.
+ */
+ struct strmap worktree_ref_stores;
+
+ /*
* Contains path to often used file names.
*/
struct repo_path_cache cached_paths;
@@ -167,7 +183,7 @@ struct repository {
const struct git_hash_algo *compat_hash_algo;
/* Repository's reference storage format, as serialized on disk. */
- unsigned int ref_storage_format;
+ enum ref_storage_format ref_storage_format;
/* A unique-id for tracing purposes. */
int trace2_repo_id;
@@ -186,9 +202,8 @@ struct repository {
unsigned different_commondir:1;
};
+#ifdef USE_THE_REPOSITORY_VARIABLE
extern struct repository *the_repository;
-#ifdef USE_THE_INDEX_VARIABLE
-extern struct index_state the_index;
#endif
/*
@@ -209,8 +224,9 @@ void repo_set_gitdir(struct repository *repo, const char *root,
void repo_set_worktree(struct repository *repo, const char *path);
void repo_set_hash_algo(struct repository *repo, int algo);
void repo_set_compat_hash_algo(struct repository *repo, int compat_algo);
-void repo_set_ref_storage_format(struct repository *repo, unsigned int format);
-void initialize_the_repository(void);
+void repo_set_ref_storage_format(struct repository *repo,
+ enum ref_storage_format format);
+void initialize_repository(struct repository *repo);
RESULT_MUST_BE_USED
int repo_init(struct repository *r, const char *gitdir, const char *worktree);
diff --git a/rerere.c b/rerere.c
index c7e1f8fd25..3a3888cce2 100644
--- a/rerere.c
+++ b/rerere.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "abspath.h"
#include "config.h"
@@ -849,6 +851,8 @@ static int do_plain_rerere(struct repository *r,
if (update.nr)
update_paths(r, &update);
+ string_list_clear(&conflict, 0);
+ string_list_clear(&update, 0);
return write_rr(rr, fd);
}
@@ -912,6 +916,7 @@ int repo_rerere(struct repository *r, int flags)
return 0;
status = do_plain_rerere(r, &merge_rr, fd);
free_rerere_dirs();
+ string_list_clear(&merge_rr, 1);
return status;
}
diff --git a/reset.c b/reset.c
index d619cb7115..9550dea03d 100644
--- a/reset.c
+++ b/reset.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "cache-tree.h"
#include "gettext.h"
@@ -47,11 +49,13 @@ static int update_refs(const struct reset_head_opts *opts,
strbuf_addstr(&msg, "updating ORIG_HEAD");
reflog_orig_head = msg.buf;
}
- update_ref(reflog_orig_head, "ORIG_HEAD",
- orig_head ? orig_head : head,
- old_orig, 0, UPDATE_REFS_MSG_ON_ERR);
+ refs_update_ref(get_main_ref_store(the_repository),
+ reflog_orig_head, "ORIG_HEAD",
+ orig_head ? orig_head : head,
+ old_orig, 0, UPDATE_REFS_MSG_ON_ERR);
} else if (old_orig)
- delete_ref(NULL, "ORIG_HEAD", old_orig, 0);
+ refs_delete_ref(get_main_ref_store(the_repository),
+ NULL, "ORIG_HEAD", old_orig, 0);
}
if (!reflog_head) {
@@ -60,16 +64,19 @@ static int update_refs(const struct reset_head_opts *opts,
reflog_head = msg.buf;
}
if (!switch_to_branch)
- ret = update_ref(reflog_head, "HEAD", oid, head,
- detach_head ? REF_NO_DEREF : 0,
- UPDATE_REFS_MSG_ON_ERR);
+ ret = refs_update_ref(get_main_ref_store(the_repository),
+ reflog_head, "HEAD", oid, head,
+ detach_head ? REF_NO_DEREF : 0,
+ UPDATE_REFS_MSG_ON_ERR);
else {
- ret = update_ref(reflog_branch ? reflog_branch : reflog_head,
- switch_to_branch, oid, NULL, 0,
- UPDATE_REFS_MSG_ON_ERR);
+ ret = refs_update_ref(get_main_ref_store(the_repository),
+ reflog_branch ? reflog_branch : reflog_head,
+ switch_to_branch, oid, NULL, 0,
+ UPDATE_REFS_MSG_ON_ERR);
if (!ret)
- ret = create_symref("HEAD", switch_to_branch,
- reflog_head);
+ ret = refs_update_symref(get_main_ref_store(the_repository),
+ "HEAD", switch_to_branch,
+ reflog_head);
}
if (!ret && run_hook)
run_hooks_l("post-checkout",
diff --git a/reset.h b/reset.h
index 10708d8ddc..a28f81829d 100644
--- a/reset.h
+++ b/reset.h
@@ -1,7 +1,7 @@
#ifndef RESET_H
#define RESET_H
-#include "hash-ll.h"
+#include "hash.h"
#include "repository.h"
#define GIT_REFLOG_ACTION_ENVIRONMENT "GIT_REFLOG_ACTION"
diff --git a/resolve-undo.c b/resolve-undo.c
index cd02dc9928..8c9911affb 100644
--- a/resolve-undo.c
+++ b/resolve-undo.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "dir.h"
#include "hash.h"
@@ -93,7 +95,8 @@ struct string_list *resolve_undo_read(const char *data, unsigned long size)
continue;
if (size < rawsz)
goto error;
- oidread(&ui->oid[i], (const unsigned char *)data);
+ oidread(&ui->oid[i], (const unsigned char *)data,
+ the_repository->hash_algo);
size -= rawsz;
data += rawsz;
}
diff --git a/resolve-undo.h b/resolve-undo.h
index f3f8462751..89a3227262 100644
--- a/resolve-undo.h
+++ b/resolve-undo.h
@@ -6,7 +6,7 @@ struct index_state;
struct pathspec;
struct string_list;
-#include "hash-ll.h"
+#include "hash.h"
struct resolve_undo_info {
unsigned int mode[3];
diff --git a/revision.c b/revision.c
index 7e45f765d9..1c0192f522 100644
--- a/revision.c
+++ b/revision.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "config.h"
#include "environment.h"
@@ -29,6 +31,7 @@
#include "bisect.h"
#include "packfile.h"
#include "worktree.h"
+#include "path.h"
#include "read-cache.h"
#include "setup.h"
#include "sparse-index.h"
@@ -844,17 +847,28 @@ static int rev_compare_tree(struct rev_info *revs,
return tree_difference;
}
-static int rev_same_tree_as_empty(struct rev_info *revs, struct commit *commit)
+static int rev_same_tree_as_empty(struct rev_info *revs, struct commit *commit,
+ int nth_parent)
{
struct tree *t1 = repo_get_commit_tree(the_repository, commit);
+ int bloom_ret = -1;
if (!t1)
return 0;
+ if (!nth_parent && revs->bloom_keys_nr) {
+ bloom_ret = check_maybe_different_in_bloom_filter(revs, commit);
+ if (!bloom_ret)
+ return 1;
+ }
+
tree_difference = REV_TREE_SAME;
revs->pruning.flags.has_changes = 0;
diff_tree_oid(NULL, &t1->object.oid, "", &revs->pruning);
+ if (bloom_ret == 1 && tree_difference == REV_TREE_SAME)
+ count_bloom_filter_false_positive++;
+
return tree_difference == REV_TREE_SAME;
}
@@ -892,7 +906,7 @@ static int compact_treesame(struct rev_info *revs, struct commit *commit, unsign
if (nth_parent != 0)
die("compact_treesame %u", nth_parent);
old_same = !!(commit->object.flags & TREESAME);
- if (rev_same_tree_as_empty(revs, commit))
+ if (rev_same_tree_as_empty(revs, commit, nth_parent))
commit->object.flags |= TREESAME;
else
commit->object.flags &= ~TREESAME;
@@ -988,7 +1002,14 @@ static void try_to_simplify_commit(struct rev_info *revs, struct commit *commit)
return;
if (!commit->parents) {
- if (rev_same_tree_as_empty(revs, commit))
+ /*
+ * Pretend as if we are comparing ourselves to the
+ * (non-existent) first parent of this commit object. Even
+ * though no such parent exists, its changed-path Bloom filter
+ * (if one exists) is relative to the empty tree, using Bloom
+ * filters is allowed here.
+ */
+ if (rev_same_tree_as_empty(revs, commit, 0))
commit->object.flags |= TREESAME;
return;
}
@@ -1069,7 +1090,7 @@ static void try_to_simplify_commit(struct rev_info *revs, struct commit *commit)
case REV_TREE_NEW:
if (revs->remove_empty_trees &&
- rev_same_tree_as_empty(revs, p)) {
+ rev_same_tree_as_empty(revs, p, nth_parent)) {
/* We are adding all the specified
* paths from this parent, so the
* history beyond this parent is not
@@ -1738,7 +1759,8 @@ void add_reflogs_to_pending(struct rev_info *revs, unsigned flags)
cb.all_revs = revs;
cb.all_flags = flags;
cb.wt = NULL;
- for_each_reflog(handle_one_reflog, &cb);
+ refs_for_each_reflog(get_main_ref_store(the_repository),
+ handle_one_reflog, &cb);
if (!revs->single_worktree)
add_other_reflogs_to_pending(&cb);
@@ -1979,9 +2001,9 @@ static const char *lookup_other_head(struct object_id *oid)
};
for (i = 0; i < ARRAY_SIZE(other_head); i++)
- if (!read_ref_full(other_head[i],
- RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,
- oid, NULL)) {
+ if (!refs_read_ref_full(get_main_ref_store(the_repository), other_head[i],
+ RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,
+ oid, NULL)) {
if (is_null_oid(oid))
die(_("%s exists but is a symbolic ref"), other_head[i]);
return other_head[i];
@@ -2129,30 +2151,26 @@ static int handle_dotdot(const char *arg,
struct rev_info *revs, int flags,
int cant_be_filename)
{
- struct object_context a_oc, b_oc;
+ struct object_context a_oc = {0}, b_oc = {0};
char *dotdot = strstr(arg, "..");
int ret;
if (!dotdot)
return -1;
- memset(&a_oc, 0, sizeof(a_oc));
- memset(&b_oc, 0, sizeof(b_oc));
-
*dotdot = '\0';
ret = handle_dotdot_1(arg, dotdot, revs, flags, cant_be_filename,
&a_oc, &b_oc);
*dotdot = '.';
- free(a_oc.path);
- free(b_oc.path);
-
+ object_context_release(&a_oc);
+ object_context_release(&b_oc);
return ret;
}
static int handle_revision_arg_1(const char *arg_, struct rev_info *revs, int flags, unsigned revarg_opt)
{
- struct object_context oc;
+ struct object_context oc = {0};
char *mark;
struct object *object;
struct object_id oid;
@@ -2160,6 +2178,7 @@ static int handle_revision_arg_1(const char *arg_, struct rev_info *revs, int fl
const char *arg = arg_;
int cant_be_filename = revarg_opt & REVARG_CANNOT_BE_FILENAME;
unsigned get_sha1_flags = GET_OID_RECORD_PATH;
+ int ret;
flags = flags & UNINTERESTING ? flags | BOTTOM : flags & ~BOTTOM;
@@ -2168,17 +2187,22 @@ static int handle_revision_arg_1(const char *arg_, struct rev_info *revs, int fl
* Just ".."? That is not a range but the
* pathspec for the parent directory.
*/
- return -1;
+ ret = -1;
+ goto out;
}
- if (!handle_dotdot(arg, revs, flags, revarg_opt))
- return 0;
+ if (!handle_dotdot(arg, revs, flags, revarg_opt)) {
+ ret = 0;
+ goto out;
+ }
mark = strstr(arg, "^@");
if (mark && !mark[2]) {
*mark = 0;
- if (add_parents_only(revs, arg, flags, 0))
- return 0;
+ if (add_parents_only(revs, arg, flags, 0)) {
+ ret = 0;
+ goto out;
+ }
*mark = '^';
}
mark = strstr(arg, "^!");
@@ -2193,8 +2217,10 @@ static int handle_revision_arg_1(const char *arg_, struct rev_info *revs, int fl
if (mark[2]) {
if (strtol_i(mark + 2, 10, &exclude_parent) ||
- exclude_parent < 1)
- return -1;
+ exclude_parent < 1) {
+ ret = -1;
+ goto out;
+ }
}
*mark = 0;
@@ -2216,17 +2242,25 @@ static int handle_revision_arg_1(const char *arg_, struct rev_info *revs, int fl
* should error out if we can't even get an oid, as
* `--missing=print` should be able to report missing oids.
*/
- if (get_oid_with_context(revs->repo, arg, get_sha1_flags, &oid, &oc))
- return revs->ignore_missing ? 0 : -1;
+ if (get_oid_with_context(revs->repo, arg, get_sha1_flags, &oid, &oc)) {
+ ret = revs->ignore_missing ? 0 : -1;
+ goto out;
+ }
if (!cant_be_filename)
verify_non_filename(revs->prefix, arg);
object = get_reference(revs, arg, &oid, flags ^ local_flags);
- if (!object)
- return (revs->ignore_missing || revs->do_not_die_on_missing_objects) ? 0 : -1;
+ if (!object) {
+ ret = (revs->ignore_missing || revs->do_not_die_on_missing_objects) ? 0 : -1;
+ goto out;
+ }
add_rev_cmdline(revs, object, arg_, REV_CMD_REV, flags ^ local_flags);
add_pending_object_with_path(revs, object, arg, oc.mode, oc.path);
- free(oc.path);
- return 0;
+
+ ret = 0;
+
+out:
+ object_context_release(&oc);
+ return ret;
}
int handle_revision_arg(const char *arg, struct rev_info *revs, int flags, unsigned revarg_opt)
@@ -2649,10 +2683,11 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
} else if (!strcmp(arg, "--invert-grep")) {
revs->grep_filter.no_body_match = 1;
} else if ((argcount = parse_long_opt("encoding", argv, &optarg))) {
+ free(git_log_output_encoding);
if (strcmp(optarg, "none"))
git_log_output_encoding = xstrdup(optarg);
else
- git_log_output_encoding = "";
+ git_log_output_encoding = xstrdup("");
return argcount;
} else if (!strcmp(arg, "--reverse")) {
revs->reverse ^= 1;
@@ -2789,7 +2824,8 @@ static int handle_revision_pseudo_opt(struct rev_info *revs,
} else if ((argcount = parse_long_opt("glob", argv, &optarg))) {
struct all_refs_cb cb;
init_all_refs_cb(&cb, revs, *flags);
- for_each_glob_ref(handle_one_ref, optarg, &cb);
+ refs_for_each_glob_ref(get_main_ref_store(the_repository),
+ handle_one_ref, optarg, &cb);
clear_ref_exclusions(&revs->ref_excludes);
return argcount;
} else if ((argcount = parse_long_opt("exclude", argv, &optarg))) {
@@ -2804,7 +2840,9 @@ static int handle_revision_pseudo_opt(struct rev_info *revs,
return error(_("options '%s' and '%s' cannot be used together"),
"--exclude-hidden", "--branches");
init_all_refs_cb(&cb, revs, *flags);
- for_each_glob_ref_in(handle_one_ref, optarg, "refs/heads/", &cb);
+ refs_for_each_glob_ref_in(get_main_ref_store(the_repository),
+ handle_one_ref, optarg,
+ "refs/heads/", &cb);
clear_ref_exclusions(&revs->ref_excludes);
} else if (skip_prefix(arg, "--tags=", &optarg)) {
struct all_refs_cb cb;
@@ -2812,7 +2850,9 @@ static int handle_revision_pseudo_opt(struct rev_info *revs,
return error(_("options '%s' and '%s' cannot be used together"),
"--exclude-hidden", "--tags");
init_all_refs_cb(&cb, revs, *flags);
- for_each_glob_ref_in(handle_one_ref, optarg, "refs/tags/", &cb);
+ refs_for_each_glob_ref_in(get_main_ref_store(the_repository),
+ handle_one_ref, optarg,
+ "refs/tags/", &cb);
clear_ref_exclusions(&revs->ref_excludes);
} else if (skip_prefix(arg, "--remotes=", &optarg)) {
struct all_refs_cb cb;
@@ -2820,7 +2860,9 @@ static int handle_revision_pseudo_opt(struct rev_info *revs,
return error(_("options '%s' and '%s' cannot be used together"),
"--exclude-hidden", "--remotes");
init_all_refs_cb(&cb, revs, *flags);
- for_each_glob_ref_in(handle_one_ref, optarg, "refs/remotes/", &cb);
+ refs_for_each_glob_ref_in(get_main_ref_store(the_repository),
+ handle_one_ref, optarg,
+ "refs/remotes/", &cb);
clear_ref_exclusions(&revs->ref_excludes);
} else if (!strcmp(arg, "--reflog")) {
add_reflogs_to_pending(revs, *flags);
@@ -2911,7 +2953,8 @@ static void NORETURN diagnose_missing_default(const char *def)
int flags;
const char *refname;
- refname = resolve_ref_unsafe(def, 0, NULL, &flags);
+ refname = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
+ def, 0, NULL, &flags);
if (!refname || !(flags & REF_ISSYMREF) || (flags & REF_ISBROKEN))
die(_("your current branch appears to be broken"));
@@ -3053,6 +3096,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
diagnose_missing_default(revs->def);
object = get_reference(revs, revs->def, &oid, 0);
add_pending_object_with_mode(revs, object, revs->def, oc.mode);
+ object_context_release(&oc);
}
/* Did the user ask for any diff output? Run the diff! */
@@ -3159,6 +3203,7 @@ void release_revisions(struct rev_info *revs)
{
free_commit_list(revs->commits);
free_commit_list(revs->ancestry_path_bottoms);
+ release_display_notes(&revs->notes_opt);
object_array_clear(&revs->pending);
object_array_clear(&revs->boundary_commits);
release_revisions_cmdline(&revs->cmdline);
@@ -3168,7 +3213,7 @@ void release_revisions(struct rev_info *revs)
release_revisions_mailmap(revs->mailmap);
free_grep_patterns(&revs->grep_filter);
graph_clear(revs->graph);
- /* TODO (need to handle "no_free"): diff_free(&revs->diffopt) */
+ diff_free(&revs->diffopt);
diff_free(&revs->pruning);
reflog_walk_info_release(revs->reflog_info);
release_revisions_topo_walk_info(revs->topo_walk_info);
@@ -4421,6 +4466,7 @@ struct commit *get_revision(struct rev_info *revs)
reversed = NULL;
while ((c = get_revision_internal(revs)))
commit_list_insert(c, &reversed);
+ free_commit_list(revs->commits);
revs->commits = reversed;
revs->reverse = 0;
revs->reverse_output_stage = 1;
diff --git a/run-command.c b/run-command.c
index 1b821042b4..45ba544932 100644
--- a/run-command.c
+++ b/run-command.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "run-command.h"
#include "environment.h"
@@ -272,17 +274,24 @@ int sane_execvp(const char *file, char * const argv[])
return -1;
}
+char *git_shell_path(void)
+{
+#ifndef GIT_WINDOWS_NATIVE
+ return xstrdup(SHELL_PATH);
+#else
+ char *p = locate_in_PATH("sh");
+ convert_slashes(p);
+ return p;
+#endif
+}
+
static const char **prepare_shell_cmd(struct strvec *out, const char **argv)
{
if (!argv[0])
BUG("shell command is empty");
if (strcspn(argv[0], "|&;<>()$`\\\"' \t\n*?[#~=%") != strlen(argv[0])) {
-#ifndef GIT_WINDOWS_NATIVE
- strvec_push(out, SHELL_PATH);
-#else
- strvec_push(out, "sh");
-#endif
+ strvec_push_nodup(out, git_shell_path());
strvec_push(out, "-c");
/*
@@ -663,7 +672,7 @@ int start_command(struct child_process *cmd)
int need_in, need_out, need_err;
int fdin[2], fdout[2], fderr[2];
int failed_errno;
- char *str;
+ const char *str;
/*
* In case of errors we must keep the promise to close FDs
@@ -746,6 +755,8 @@ fail_pipe:
goto end_of_spawn;
}
+ trace_argv_printf(&argv.v[1], "trace: start_command:");
+
if (pipe(notify_pipe))
notify_pipe[0] = notify_pipe[1] = -1;
@@ -913,6 +924,7 @@ end_of_spawn:
else if (cmd->use_shell)
cmd->args.v = prepare_shell_cmd(&nargv, sargv);
+ trace_argv_printf(cmd->args.v, "trace: start_command:");
cmd->pid = mingw_spawnvpe(cmd->args.v[0], cmd->args.v,
(char**) cmd->env.v,
cmd->dir, fhin, fhout, fherr);
@@ -1753,7 +1765,8 @@ void run_processes_parallel(const struct run_process_parallel_opts *opts)
if (do_trace2)
trace2_region_enter_printf(tr2_category, tr2_label, NULL,
- "max:%d", opts->processes);
+ "max:%"PRIuMAX,
+ (uintmax_t)opts->processes);
pp_init(&pp, opts, &pp_sig);
while (1) {
diff --git a/run-command.h b/run-command.h
index 55f6631a2a..03e7222d8b 100644
--- a/run-command.h
+++ b/run-command.h
@@ -196,6 +196,11 @@ int is_executable(const char *name);
int exists_in_PATH(const char *command);
/**
+ * Return the path that is used to execute Unix shell command-lines.
+ */
+char *git_shell_path(void);
+
+/**
* Start a sub-process. Takes a pointer to a `struct child_process`
* that specifies the details and returns pipe FDs (if requested).
* See below for details.
diff --git a/scalar.c b/scalar.c
index fb2940c2a0..1fe8a93e65 100644
--- a/scalar.c
+++ b/scalar.c
@@ -2,6 +2,8 @@
* The Scalar command-line interface.
*/
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "abspath.h"
#include "gettext.h"
@@ -70,6 +72,7 @@ static void setup_enlistment_directory(int argc, const char **argv,
strbuf_release(&path);
}
+LAST_ARG_MUST_BE_NULL
static int run_git(const char *arg, ...)
{
struct child_process cmd = CHILD_PROCESS_INIT;
@@ -288,6 +291,7 @@ static int unregister_dir(void)
}
/* printf-style interface, expects `<key>=<value>` argument */
+__attribute__((format (printf, 1, 2)))
static int set_config(const char *fmt, ...)
{
struct strbuf buf = STRBUF_INIT;
@@ -361,16 +365,13 @@ static char *remote_default_branch(const char *url)
static int delete_enlistment(struct strbuf *enlistment)
{
-#ifdef WIN32
struct strbuf parent = STRBUF_INIT;
size_t offset;
char *path_sep;
-#endif
if (unregister_dir())
return error(_("failed to unregister repository"));
-#ifdef WIN32
/*
* Change the current directory to one outside of the enlistment so
* that we may delete everything underneath it.
@@ -385,7 +386,6 @@ static int delete_enlistment(struct strbuf *enlistment)
return res;
}
strbuf_release(&parent);
-#endif
if (have_fsmonitor_support() && stop_fsmonitor_daemon())
return error(_("failed to stop the FSMonitor daemon"));
@@ -645,7 +645,6 @@ static int cmd_reconfigure(int argc, const char **argv)
};
struct string_list scalar_repos = STRING_LIST_INIT_DUP;
int i, res = 0;
- struct repository r = { NULL };
struct strbuf commondir = STRBUF_INIT, gitdir = STRBUF_INIT;
argc = parse_options(argc, argv, NULL, options,
@@ -665,6 +664,7 @@ static int cmd_reconfigure(int argc, const char **argv)
for (i = 0; i < scalar_repos.nr; i++) {
int succeeded = 0;
+ struct repository *old_repo, r = { NULL };
const char *dir = scalar_repos.items[i].string;
strbuf_reset(&commondir);
@@ -712,13 +712,17 @@ static int cmd_reconfigure(int argc, const char **argv)
git_config_clear();
+ if (repo_init(&r, gitdir.buf, commondir.buf))
+ goto loop_end;
+
+ old_repo = the_repository;
the_repository = &r;
- r.commondir = commondir.buf;
- r.gitdir = gitdir.buf;
if (set_recommended_config(1) >= 0)
succeeded = 1;
+ the_repository = old_repo;
+
loop_end:
if (!succeeded) {
res = -1;
diff --git a/send-pack.c b/send-pack.c
index 37f59d4f66..fa2f5eec17 100644
--- a/send-pack.c
+++ b/send-pack.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "config.h"
#include "commit.h"
@@ -259,7 +261,7 @@ static int receive_status(struct packet_reader *reader, struct ref *refs)
if (p)
hint->remote_status = xstrdup(p);
else
- hint->remote_status = "failed";
+ hint->remote_status = xstrdup("failed");
} else {
hint->status = REF_STATUS_OK;
hint->remote_status = xstrdup_or_null(p);
@@ -425,17 +427,26 @@ static void get_commons_through_negotiation(const char *url,
struct child_process child = CHILD_PROCESS_INIT;
const struct ref *ref;
int len = the_hash_algo->hexsz + 1; /* hash + NL */
+ int nr_negotiation_tip = 0;
child.git_cmd = 1;
child.no_stdin = 1;
child.out = -1;
strvec_pushl(&child.args, "fetch", "--negotiate-only", NULL);
for (ref = remote_refs; ref; ref = ref->next) {
- if (!is_null_oid(&ref->new_oid))
- strvec_pushf(&child.args, "--negotiation-tip=%s", oid_to_hex(&ref->new_oid));
+ if (!is_null_oid(&ref->new_oid)) {
+ strvec_pushf(&child.args, "--negotiation-tip=%s",
+ oid_to_hex(&ref->new_oid));
+ nr_negotiation_tip++;
+ }
}
strvec_push(&child.args, url);
+ if (!nr_negotiation_tip) {
+ child_process_clear(&child);
+ return;
+ }
+
if (start_command(&child))
die(_("send-pack: unable to fork off fetch subprocess"));
diff --git a/sequencer.c b/sequencer.c
index 2c19846385..a2284ac9e9 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "abspath.h"
#include "advice.h"
@@ -207,6 +209,46 @@ static GIT_PATH_FUNC(rebase_path_no_reschedule_failed_exec, "rebase-merge/no-res
static GIT_PATH_FUNC(rebase_path_drop_redundant_commits, "rebase-merge/drop_redundant_commits")
static GIT_PATH_FUNC(rebase_path_keep_redundant_commits, "rebase-merge/keep_redundant_commits")
+/*
+ * A 'struct replay_ctx' represents the private state of the sequencer.
+ */
+struct replay_ctx {
+ /*
+ * The commit message that will be used except at the end of a
+ * chain of fixup and squash commands.
+ */
+ struct strbuf message;
+ /*
+ * The list of completed fixup and squash commands in the
+ * current chain.
+ */
+ struct strbuf current_fixups;
+ /*
+ * Stores the reflog message that will be used when creating a
+ * commit. Points to a static buffer and should not be free()'d.
+ */
+ const char *reflog_message;
+ /*
+ * The number of completed fixup and squash commands in the
+ * current chain.
+ */
+ int current_fixup_count;
+ /*
+ * Whether message contains a commit message.
+ */
+ unsigned have_message :1;
+};
+
+struct replay_ctx* replay_ctx_new(void)
+{
+ struct replay_ctx *ctx = xcalloc(1, sizeof(*ctx));
+
+ strbuf_init(&ctx->current_fixups, 0);
+ strbuf_init(&ctx->message, 0);
+
+ return ctx;
+}
+
/**
* A 'struct update_refs_record' represents a value in the update-refs
* list. We use a string_list to map refs to these (before, after) pairs.
@@ -226,7 +268,7 @@ static struct update_ref_record *init_update_ref_record(const char *ref)
oidcpy(&rec->after, null_oid());
/* This may fail, but that's fine, we will keep the null OID. */
- read_ref(ref, &rec->before);
+ refs_read_ref(get_main_ref_store(the_repository), ref, &rec->before);
return rec;
}
@@ -266,7 +308,7 @@ static int git_sequencer_config(const char *k, const char *v,
}
if (!opts->default_strategy && !strcmp(k, "pull.twohead")) {
- int ret = git_config_string((const char**)&opts->default_strategy, k, v);
+ int ret = git_config_string(&opts->default_strategy, k, v);
if (ret == 0) {
/*
* pull.twohead is allowed to be multi-valued; we only
@@ -319,35 +361,32 @@ static const char *get_todo_path(const struct replay_opts *opts)
static int has_conforming_footer(struct strbuf *sb, struct strbuf *sob,
size_t ignore_footer)
{
- struct process_trailer_options opts = PROCESS_TRAILER_OPTIONS_INIT;
- struct trailer_info info;
- size_t i;
+ struct trailer_iterator iter;
+ size_t i = 0;
int found_sob = 0, found_sob_last = 0;
char saved_char;
- opts.no_divider = 1;
-
if (ignore_footer) {
saved_char = sb->buf[sb->len - ignore_footer];
sb->buf[sb->len - ignore_footer] = '\0';
}
- trailer_info_get(&opts, sb->buf, &info);
+ trailer_iterator_init(&iter, sb->buf);
if (ignore_footer)
sb->buf[sb->len - ignore_footer] = saved_char;
- if (info.trailer_block_start == info.trailer_block_end)
- return 0;
+ while (trailer_iterator_advance(&iter)) {
+ i++;
+ if (sob && !strncmp(iter.raw, sob->buf, sob->len))
+ found_sob = i;
+ }
+ trailer_iterator_release(&iter);
- for (i = 0; i < info.trailer_nr; i++)
- if (sob && !strncmp(info.trailers[i], sob->buf, sob->len)) {
- found_sob = 1;
- if (i == info.trailer_nr - 1)
- found_sob_last = 1;
- }
+ if (!i)
+ return 0;
- trailer_info_release(&info);
+ found_sob_last = (int)i == found_sob;
if (found_sob_last)
return 3;
@@ -366,17 +405,26 @@ static const char *gpg_sign_opt_quoted(struct replay_opts *opts)
return buf.buf;
}
+static void replay_ctx_release(struct replay_ctx *ctx)
+{
+ strbuf_release(&ctx->current_fixups);
+ strbuf_release(&ctx->message);
+}
+
void replay_opts_release(struct replay_opts *opts)
{
+ struct replay_ctx *ctx = opts->ctx;
+
free(opts->gpg_sign);
free(opts->reflog_action);
free(opts->default_strategy);
free(opts->strategy);
strvec_clear (&opts->xopts);
- strbuf_release(&opts->current_fixups);
if (opts->revs)
release_revisions(opts->revs);
free(opts->revs);
+ replay_ctx_release(ctx);
+ free(opts->ctx);
}
int sequencer_remove_state(struct replay_opts *opts)
@@ -391,7 +439,7 @@ int sequencer_remove_state(struct replay_opts *opts)
char *eol = strchr(p, '\n');
if (eol)
*eol = '\0';
- if (delete_ref("(rebase) cleanup", p, NULL, 0) < 0) {
+ if (refs_delete_ref(get_main_ref_store(the_repository), "(rebase) cleanup", p, NULL, 0) < 0) {
warning(_("could not delete '%s'"), p);
ret = -1;
}
@@ -612,11 +660,12 @@ static int fast_forward_to(struct repository *r,
strbuf_addf(&sb, "%s: fast-forward", action_name(opts));
- transaction = ref_transaction_begin(&err);
+ transaction = ref_store_transaction_begin(get_main_ref_store(the_repository),
+ &err);
if (!transaction ||
ref_transaction_update(transaction, "HEAD",
to, unborn && !is_rebase_i(opts) ?
- null_oid() : from,
+ null_oid() : from, NULL, NULL,
0, sb.buf, &err) ||
ref_transaction_commit(transaction, &err)) {
ref_transaction_free(transaction);
@@ -792,11 +841,12 @@ static int is_index_unchanged(struct repository *r)
struct index_state *istate = r->index;
const char *head_name;
- if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, &head_oid, NULL)) {
+ if (!refs_resolve_ref_unsafe(get_main_ref_store(the_repository), "HEAD", RESOLVE_REF_READING, &head_oid, NULL)) {
/* Check to see if this is an unborn branch */
- head_name = resolve_ref_unsafe("HEAD",
- RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,
- &head_oid, NULL);
+ head_name = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
+ "HEAD",
+ RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,
+ &head_oid, NULL);
if (!head_name ||
!starts_with(head_name, "refs/heads/") ||
!is_null_oid(&head_oid))
@@ -1084,6 +1134,7 @@ static int run_git_commit(const char *defmsg,
struct replay_opts *opts,
unsigned int flags)
{
+ struct replay_ctx *ctx = opts->ctx;
struct child_process cmd = CHILD_PROCESS_INIT;
if ((flags & CLEANUP_MSG) && (flags & VERBATIM_MSG))
@@ -1101,7 +1152,7 @@ static int run_git_commit(const char *defmsg,
gpg_opt, gpg_opt);
}
- strvec_pushf(&cmd.env, GIT_REFLOG_ACTION "=%s", opts->reflog_message);
+ strvec_pushf(&cmd.env, GIT_REFLOG_ACTION "=%s", ctx->reflog_message);
if (opts->committer_date_is_author_date)
strvec_pushf(&cmd.env, "GIT_COMMITTER_DATE=%s",
@@ -1244,11 +1295,12 @@ int update_head_with_reflog(const struct commit *old_head,
strbuf_addch(&sb, '\n');
}
- transaction = ref_transaction_begin(err);
+ transaction = ref_store_transaction_begin(get_main_ref_store(the_repository),
+ err);
if (!transaction ||
ref_transaction_update(transaction, "HEAD", new_head,
old_head ? &old_head->object.oid : null_oid(),
- 0, sb.buf, err) ||
+ NULL, NULL, 0, sb.buf, err) ||
ref_transaction_commit(transaction, err)) {
ret = -1;
}
@@ -1487,6 +1539,7 @@ static int try_to_commit(struct repository *r,
struct replay_opts *opts, unsigned int flags,
struct object_id *oid)
{
+ struct replay_ctx *ctx = opts->ctx;
struct object_id tree;
struct commit *current_head = NULL;
struct commit_list *parents = NULL;
@@ -1648,7 +1701,7 @@ static int try_to_commit(struct repository *r,
goto out;
}
- if (update_head_with_reflog(current_head, oid, opts->reflog_message,
+ if (update_head_with_reflog(current_head, oid, ctx->reflog_message,
msg, &err)) {
res = error("%s", err.buf);
goto out;
@@ -1660,6 +1713,7 @@ static int try_to_commit(struct repository *r,
out:
free_commit_extra_headers(extra);
+ free_commit_list(parents);
strbuf_release(&err);
strbuf_release(&commit_msg);
free(amend_author);
@@ -1669,8 +1723,8 @@ out:
static int write_rebase_head(struct object_id *oid)
{
- if (update_ref("rebase", "REBASE_HEAD", oid,
- NULL, REF_NO_DEREF, UPDATE_REFS_MSG_ON_ERR))
+ if (refs_update_ref(get_main_ref_store(the_repository), "rebase", "REBASE_HEAD", oid,
+ NULL, REF_NO_DEREF, UPDATE_REFS_MSG_ON_ERR))
return error(_("could not update %s"), "REBASE_HEAD");
return 0;
@@ -1878,10 +1932,10 @@ static void add_commented_lines(struct strbuf *buf, const void *str, size_t len)
}
/* Does the current fixup chain contain a squash command? */
-static int seen_squash(struct replay_opts *opts)
+static int seen_squash(struct replay_ctx *ctx)
{
- return starts_with(opts->current_fixups.buf, "squash") ||
- strstr(opts->current_fixups.buf, "\nsquash");
+ return starts_with(ctx->current_fixups.buf, "squash") ||
+ strstr(ctx->current_fixups.buf, "\nsquash");
}
static void update_comment_bufs(struct strbuf *buf1, struct strbuf *buf2, int n)
@@ -1957,6 +2011,7 @@ static int append_squash_message(struct strbuf *buf, const char *body,
enum todo_command command, struct replay_opts *opts,
unsigned flag)
{
+ struct replay_ctx *ctx = opts->ctx;
const char *fixup_msg;
size_t commented_len = 0, fixup_off;
/*
@@ -1965,13 +2020,13 @@ static int append_squash_message(struct strbuf *buf, const char *body,
* squashing commit messages.
*/
if (starts_with(body, "amend!") ||
- ((command == TODO_SQUASH || seen_squash(opts)) &&
+ ((command == TODO_SQUASH || seen_squash(ctx)) &&
(starts_with(body, "squash!") || starts_with(body, "fixup!"))))
commented_len = commit_subject_length(body);
strbuf_addf(buf, "\n%s ", comment_line_str);
strbuf_addf(buf, _(nth_commit_msg_fmt),
- ++opts->current_fixup_count + 1);
+ ++ctx->current_fixup_count + 1);
strbuf_addstr(buf, "\n\n");
strbuf_add_commented_lines(buf, body, commented_len, comment_line_str);
/* buf->buf may be reallocated so store an offset into the buffer */
@@ -1979,7 +2034,7 @@ static int append_squash_message(struct strbuf *buf, const char *body,
strbuf_addstr(buf, body + commented_len);
/* fixup -C after squash behaves like squash */
- if (is_fixup_flag(command, flag) && !seen_squash(opts)) {
+ if (is_fixup_flag(command, flag) && !seen_squash(ctx)) {
/*
* We're replacing the commit message so we need to
* append the Signed-off-by: trailer if the user
@@ -2013,12 +2068,13 @@ static int update_squash_messages(struct repository *r,
struct replay_opts *opts,
unsigned flag)
{
+ struct replay_ctx *ctx = opts->ctx;
struct strbuf buf = STRBUF_INIT;
int res = 0;
const char *message, *body;
const char *encoding = get_commit_output_encoding();
- if (opts->current_fixup_count > 0) {
+ if (ctx->current_fixup_count > 0) {
struct strbuf header = STRBUF_INIT;
char *eol;
@@ -2031,10 +2087,10 @@ static int update_squash_messages(struct repository *r,
strbuf_addf(&header, "%s ", comment_line_str);
strbuf_addf(&header, _(combined_commit_msg_fmt),
- opts->current_fixup_count + 2);
+ ctx->current_fixup_count + 2);
strbuf_splice(&buf, 0, eol - buf.buf, header.buf, header.len);
strbuf_release(&header);
- if (is_fixup_flag(command, flag) && !seen_squash(opts))
+ if (is_fixup_flag(command, flag) && !seen_squash(ctx))
update_squash_message_for_fixup(&buf);
} else {
struct object_id head;
@@ -2081,7 +2137,7 @@ static int update_squash_messages(struct repository *r,
} else if (command == TODO_FIXUP) {
strbuf_addf(&buf, "\n%s ", comment_line_str);
strbuf_addf(&buf, _(skip_nth_commit_msg_fmt),
- ++opts->current_fixup_count + 1);
+ ++ctx->current_fixup_count + 1);
strbuf_addstr(&buf, "\n\n");
strbuf_add_commented_lines(&buf, body, strlen(body),
comment_line_str);
@@ -2095,12 +2151,12 @@ static int update_squash_messages(struct repository *r,
strbuf_release(&buf);
if (!res) {
- strbuf_addf(&opts->current_fixups, "%s%s %s",
- opts->current_fixups.len ? "\n" : "",
+ strbuf_addf(&ctx->current_fixups, "%s%s %s",
+ ctx->current_fixups.len ? "\n" : "",
command_to_string(command),
oid_to_hex(&commit->object.oid));
- res = write_message(opts->current_fixups.buf,
- opts->current_fixups.len,
+ res = write_message(ctx->current_fixups.buf,
+ ctx->current_fixups.len,
rebase_path_current_fixups(), 0);
}
@@ -2178,6 +2234,7 @@ static int do_pick_commit(struct repository *r,
struct replay_opts *opts,
int final_fixup, int *check_todo)
{
+ struct replay_ctx *ctx = opts->ctx;
unsigned int flags = should_edit(opts) ? EDIT_MSG : 0;
const char *msg_file = should_edit(opts) ? NULL : git_path_merge_msg(r);
struct object_id head;
@@ -2185,7 +2242,6 @@ static int do_pick_commit(struct repository *r,
const char *base_label, *next_label;
char *author = NULL;
struct commit_message msg = { NULL, NULL, NULL, NULL };
- struct strbuf msgbuf = STRBUF_INIT;
int res, unborn = 0, reword = 0, allow, drop_commit;
enum todo_command command = item->command;
struct commit *commit = item->commit;
@@ -2210,7 +2266,7 @@ static int do_pick_commit(struct repository *r,
unborn = 1;
} else if (unborn)
oidcpy(&head, the_hash_algo->empty_tree);
- if (index_differs_from(r, unborn ? empty_tree_oid_hex() : "HEAD",
+ if (index_differs_from(r, unborn ? empty_tree_oid_hex(the_repository->hash_algo) : "HEAD",
NULL, 0))
return error_dirty_index(r, opts);
}
@@ -2284,7 +2340,7 @@ static int do_pick_commit(struct repository *r,
next = parent;
next_label = msg.parent_label;
if (opts->commit_use_reference) {
- strbuf_addstr(&msgbuf,
+ strbuf_addstr(&ctx->message,
"# *** SAY WHY WE ARE REVERTING ON THE TITLE LINE ***");
} else if (skip_prefix(msg.subject, "Revert \"", &orig_subject) &&
/*
@@ -2293,21 +2349,21 @@ static int do_pick_commit(struct repository *r,
* thus requiring excessive complexity to deal with.
*/
!starts_with(orig_subject, "Revert \"")) {
- strbuf_addstr(&msgbuf, "Reapply \"");
- strbuf_addstr(&msgbuf, orig_subject);
+ strbuf_addstr(&ctx->message, "Reapply \"");
+ strbuf_addstr(&ctx->message, orig_subject);
} else {
- strbuf_addstr(&msgbuf, "Revert \"");
- strbuf_addstr(&msgbuf, msg.subject);
- strbuf_addstr(&msgbuf, "\"");
+ strbuf_addstr(&ctx->message, "Revert \"");
+ strbuf_addstr(&ctx->message, msg.subject);
+ strbuf_addstr(&ctx->message, "\"");
}
- strbuf_addstr(&msgbuf, "\n\nThis reverts commit ");
- refer_to_commit(opts, &msgbuf, commit);
+ strbuf_addstr(&ctx->message, "\n\nThis reverts commit ");
+ refer_to_commit(opts, &ctx->message, commit);
if (commit->parents && commit->parents->next) {
- strbuf_addstr(&msgbuf, ", reversing\nchanges made to ");
- refer_to_commit(opts, &msgbuf, parent);
+ strbuf_addstr(&ctx->message, ", reversing\nchanges made to ");
+ refer_to_commit(opts, &ctx->message, parent);
}
- strbuf_addstr(&msgbuf, ".\n");
+ strbuf_addstr(&ctx->message, ".\n");
} else {
const char *p;
@@ -2316,21 +2372,22 @@ static int do_pick_commit(struct repository *r,
next = commit;
next_label = msg.label;
- /* Append the commit log message to msgbuf. */
+ /* Append the commit log message to ctx->message. */
if (find_commit_subject(msg.message, &p))
- strbuf_addstr(&msgbuf, p);
+ strbuf_addstr(&ctx->message, p);
if (opts->record_origin) {
- strbuf_complete_line(&msgbuf);
- if (!has_conforming_footer(&msgbuf, NULL, 0))
- strbuf_addch(&msgbuf, '\n');
- strbuf_addstr(&msgbuf, cherry_picked_prefix);
- strbuf_addstr(&msgbuf, oid_to_hex(&commit->object.oid));
- strbuf_addstr(&msgbuf, ")\n");
+ strbuf_complete_line(&ctx->message);
+ if (!has_conforming_footer(&ctx->message, NULL, 0))
+ strbuf_addch(&ctx->message, '\n');
+ strbuf_addstr(&ctx->message, cherry_picked_prefix);
+ strbuf_addstr(&ctx->message, oid_to_hex(&commit->object.oid));
+ strbuf_addstr(&ctx->message, ")\n");
}
if (!is_fixup(command))
author = get_author(msg.message);
}
+ ctx->have_message = 1;
if (command == TODO_REWORD)
reword = 1;
@@ -2361,7 +2418,7 @@ static int do_pick_commit(struct repository *r,
}
if (opts->signoff && !is_fixup(command))
- append_signoff(&msgbuf, 0, 0);
+ append_signoff(&ctx->message, 0, 0);
if (is_rebase_i(opts) && write_author_script(msg.message) < 0)
res = -1;
@@ -2370,17 +2427,17 @@ static int do_pick_commit(struct repository *r,
!strcmp(opts->strategy, "ort") ||
command == TODO_REVERT) {
res = do_recursive_merge(r, base, next, base_label, next_label,
- &head, &msgbuf, opts);
+ &head, &ctx->message, opts);
if (res < 0)
goto leave;
- res |= write_message(msgbuf.buf, msgbuf.len,
+ res |= write_message(ctx->message.buf, ctx->message.len,
git_path_merge_msg(r), 0);
} else {
struct commit_list *common = NULL;
struct commit_list *remotes = NULL;
- res = write_message(msgbuf.buf, msgbuf.len,
+ res = write_message(ctx->message.buf, ctx->message.len,
git_path_merge_msg(r), 0);
commit_list_insert(base, &common);
@@ -2401,12 +2458,12 @@ static int do_pick_commit(struct repository *r,
if ((command == TODO_PICK || command == TODO_REWORD ||
command == TODO_EDIT) && !opts->no_commit &&
(res == 0 || res == 1) &&
- update_ref(NULL, "CHERRY_PICK_HEAD", &commit->object.oid, NULL,
- REF_NO_DEREF, UPDATE_REFS_MSG_ON_ERR))
+ refs_update_ref(get_main_ref_store(the_repository), NULL, "CHERRY_PICK_HEAD", &commit->object.oid, NULL,
+ REF_NO_DEREF, UPDATE_REFS_MSG_ON_ERR))
res = -1;
if (command == TODO_REVERT && ((opts->no_commit && res == 0) || res == 1) &&
- update_ref(NULL, "REVERT_HEAD", &commit->object.oid, NULL,
- REF_NO_DEREF, UPDATE_REFS_MSG_ON_ERR))
+ refs_update_ref(get_main_ref_store(the_repository), NULL, "REVERT_HEAD", &commit->object.oid, NULL,
+ REF_NO_DEREF, UPDATE_REFS_MSG_ON_ERR))
res = -1;
if (res) {
@@ -2458,14 +2515,13 @@ fast_forward_edit:
unlink(rebase_path_fixup_msg());
unlink(rebase_path_squash_msg());
unlink(rebase_path_current_fixups());
- strbuf_reset(&opts->current_fixups);
- opts->current_fixup_count = 0;
+ strbuf_reset(&ctx->current_fixups);
+ ctx->current_fixup_count = 0;
}
leave:
free_message(commit, &msg);
free(author);
- strbuf_release(&msgbuf);
update_abort_safety_file();
return res;
@@ -2573,8 +2629,63 @@ static int check_label_or_ref_arg(enum todo_command command, const char *arg)
return 0;
}
-static int parse_insn_line(struct repository *r, struct todo_item *item,
- const char *buf, const char *bol, char *eol)
+static int check_merge_commit_insn(enum todo_command command)
+{
+ switch(command) {
+ case TODO_PICK:
+ error(_("'%s' does not accept merge commits"),
+ todo_command_info[command].str);
+ advise_if_enabled(ADVICE_REBASE_TODO_ERROR, _(
+ /*
+ * TRANSLATORS: 'pick' and 'merge -C' should not be
+ * translated.
+ */
+ "'pick' does not take a merge commit. If you wanted to\n"
+ "replay the merge, use 'merge -C' on the commit."));
+ return -1;
+
+ case TODO_REWORD:
+ error(_("'%s' does not accept merge commits"),
+ todo_command_info[command].str);
+ advise_if_enabled(ADVICE_REBASE_TODO_ERROR, _(
+ /*
+ * TRANSLATORS: 'reword' and 'merge -c' should not be
+ * translated.
+ */
+ "'reword' does not take a merge commit. If you wanted to\n"
+ "replay the merge and reword the commit message, use\n"
+ "'merge -c' on the commit"));
+ return -1;
+
+ case TODO_EDIT:
+ error(_("'%s' does not accept merge commits"),
+ todo_command_info[command].str);
+ advise_if_enabled(ADVICE_REBASE_TODO_ERROR, _(
+ /*
+ * TRANSLATORS: 'edit', 'merge -C' and 'break' should
+ * not be translated.
+ */
+ "'edit' does not take a merge commit. If you wanted to\n"
+ "replay the merge, use 'merge -C' on the commit, and then\n"
+ "'break' to give the control back to you so that you can\n"
+ "do 'git commit --amend && git rebase --continue'."));
+ return -1;
+
+ case TODO_FIXUP:
+ case TODO_SQUASH:
+ return error(_("cannot squash merge commit into another commit"));
+
+ case TODO_MERGE:
+ return 0;
+
+ default:
+ BUG("unexpected todo_command");
+ }
+}
+
+static int parse_insn_line(struct repository *r, struct replay_opts *opts,
+ struct todo_item *item, const char *buf,
+ const char *bol, char *eol)
{
struct object_id commit_oid;
char *end_of_object_name;
@@ -2678,7 +2789,12 @@ static int parse_insn_line(struct repository *r, struct todo_item *item,
return status;
item->commit = lookup_commit_reference(r, &commit_oid);
- return item->commit ? 0 : -1;
+ if (!item->commit)
+ return -1;
+ if (is_rebase_i(opts) &&
+ item->commit->parents && item->commit->parents->next)
+ return check_merge_commit_insn(item->command);
+ return 0;
}
int sequencer_get_last_command(struct repository *r UNUSED, enum replay_action *action)
@@ -2708,8 +2824,8 @@ int sequencer_get_last_command(struct repository *r UNUSED, enum replay_action *
return ret;
}
-int todo_list_parse_insn_buffer(struct repository *r, char *buf,
- struct todo_list *todo_list)
+int todo_list_parse_insn_buffer(struct repository *r, struct replay_opts *opts,
+ char *buf, struct todo_list *todo_list)
{
struct todo_item *item;
char *p = buf, *next_p;
@@ -2727,7 +2843,7 @@ int todo_list_parse_insn_buffer(struct repository *r, char *buf,
item = append_new_todo(todo_list);
item->offset_in_buf = p - todo_list->buf.buf;
- if (parse_insn_line(r, item, buf, p, eol)) {
+ if (parse_insn_line(r, opts, item, buf, p, eol)) {
res = error(_("invalid line %d: %.*s"),
i, (int)(eol - p), p);
item->command = TODO_COMMENT + 1;
@@ -2846,12 +2962,14 @@ void sequencer_post_commit_cleanup(struct repository *r, int verbose)
NULL, REF_NO_DEREF);
if (!need_cleanup)
- return;
+ goto out;
if (!have_finished_the_last_pick())
- return;
+ goto out;
sequencer_remove_state(&opts);
+out:
+ replay_opts_release(&opts);
}
static void todo_list_write_total_nr(struct todo_list *todo_list)
@@ -2875,7 +2993,7 @@ static int read_populate_todo(struct repository *r,
if (strbuf_read_file_or_whine(&todo_list->buf, todo_file) < 0)
return -1;
- res = todo_list_parse_insn_buffer(r, todo_list->buf.buf, todo_list);
+ res = todo_list_parse_insn_buffer(r, opts, todo_list->buf.buf, todo_list);
if (res) {
if (is_rebase_i(opts))
return error(_("please fix this using "
@@ -2905,7 +3023,7 @@ static int read_populate_todo(struct repository *r,
struct todo_list done = TODO_LIST_INIT;
if (strbuf_read_file(&done.buf, rebase_path_done(), 0) > 0 &&
- !todo_list_parse_insn_buffer(r, done.buf.buf, &done))
+ !todo_list_parse_insn_buffer(r, opts, done.buf.buf, &done))
todo_list->done_nr = count_commands(&done);
else
todo_list->done_nr = 0;
@@ -3022,6 +3140,8 @@ static void read_strategy_opts(struct replay_opts *opts, struct strbuf *buf)
static int read_populate_opts(struct replay_opts *opts)
{
+ struct replay_ctx *ctx = opts->ctx;
+
if (is_rebase_i(opts)) {
struct strbuf buf = STRBUF_INIT;
int ret = 0;
@@ -3081,13 +3201,13 @@ static int read_populate_opts(struct replay_opts *opts)
read_strategy_opts(opts, &buf);
strbuf_reset(&buf);
- if (read_oneliner(&opts->current_fixups,
+ if (read_oneliner(&ctx->current_fixups,
rebase_path_current_fixups(),
READ_ONELINER_SKIP_IF_EMPTY)) {
- const char *p = opts->current_fixups.buf;
- opts->current_fixup_count = 1;
+ const char *p = ctx->current_fixups.buf;
+ ctx->current_fixup_count = 1;
while ((p = strchr(p, '\n'))) {
- opts->current_fixup_count++;
+ ctx->current_fixup_count++;
p++;
}
}
@@ -3277,12 +3397,12 @@ static int rollback_is_safe(void)
strbuf_release(&sb);
}
else if (errno == ENOENT)
- oidclr(&expected_head);
+ oidclr(&expected_head, the_repository->hash_algo);
else
die_errno(_("could not read '%s'"), git_path_abort_safety_file());
if (repo_get_oid(the_repository, "HEAD", &actual_head))
- oidclr(&actual_head);
+ oidclr(&actual_head, the_repository->hash_algo);
return oideq(&actual_head, &expected_head);
}
@@ -3307,7 +3427,7 @@ static int rollback_single_pick(struct repository *r)
if (!refs_ref_exists(get_main_ref_store(r), "CHERRY_PICK_HEAD") &&
!refs_ref_exists(get_main_ref_store(r), "REVERT_HEAD"))
return error(_("no cherry-pick or revert in progress"));
- if (read_ref_full("HEAD", 0, &head_oid, NULL))
+ if (refs_read_ref_full(get_main_ref_store(the_repository), "HEAD", 0, &head_oid, NULL))
return error(_("cannot resolve HEAD"));
if (is_null_oid(&head_oid))
return error(_("cannot abort from a branch yet to be born"));
@@ -3318,7 +3438,7 @@ static int skip_single_pick(void)
{
struct object_id head;
- if (read_ref_full("HEAD", 0, &head, NULL))
+ if (refs_read_ref_full(get_main_ref_store(the_repository), "HEAD", 0, &head, NULL))
return error(_("cannot resolve HEAD"));
return reset_merge(&head);
}
@@ -3608,13 +3728,24 @@ static int error_with_patch(struct repository *r,
struct replay_opts *opts,
int exit_code, int to_amend)
{
- if (commit) {
- if (make_patch(r, commit, opts))
+ struct replay_ctx *ctx = opts->ctx;
+
+ /*
+ * Write the commit message to be used by "git rebase
+ * --continue". If a "fixup" or "squash" command has conflicts
+ * then we will have already written rebase_path_message() in
+ * error_failed_squash(). If an "edit" command was
+ * fast-forwarded then we don't have a message in ctx->message
+ * and rely on make_patch() to write rebase_path_message()
+ * instead.
+ */
+ if (ctx->have_message && !file_exists(rebase_path_message()) &&
+ write_message(ctx->message.buf, ctx->message.len,
+ rebase_path_message(), 0))
+ return error(_("could not write commit message file"));
+
+ if (commit && make_patch(r, commit, opts))
return -1;
- } else if (copy_file(rebase_path_message(),
- git_path_merge_msg(r), 0666))
- return error(_("unable to copy '%s' to '%s'"),
- git_path_merge_msg(r), rebase_path_message());
if (to_amend) {
if (intend_to_amend())
@@ -3764,8 +3895,9 @@ static int do_label(struct repository *r, const char *name, int len)
} else if (repo_get_oid(r, "HEAD", &head_oid)) {
error(_("could not read HEAD"));
ret = -1;
- } else if (ref_transaction_update(transaction, ref_name.buf, &head_oid,
- NULL, 0, msg.buf, &err) < 0 ||
+ } else if (ref_transaction_update(transaction, ref_name.buf,
+ &head_oid, NULL, NULL, NULL,
+ 0, msg.buf, &err) < 0 ||
ref_transaction_commit(transaction, &err)) {
error("%s", err.buf);
ret = -1;
@@ -3823,7 +3955,7 @@ static struct commit *lookup_label(struct repository *r, const char *label,
strbuf_reset(buf);
strbuf_addf(buf, "refs/rewritten/%.*s", len, label);
- if (!read_ref(buf->buf, &oid)) {
+ if (!refs_read_ref(get_main_ref_store(the_repository), buf->buf, &oid)) {
commit = lookup_commit_object(r, &oid);
} else {
/* fall back to non-rewritten ref or commit */
@@ -3919,9 +4051,10 @@ static int do_reset(struct repository *r,
ret = error(_("could not write index"));
if (!ret)
- ret = update_ref(reflog_message(opts, "reset", "'%.*s'",
- len, name), "HEAD", &oid,
- NULL, 0, UPDATE_REFS_MSG_ON_ERR);
+ ret = refs_update_ref(get_main_ref_store(the_repository), reflog_message(opts, "reset", "'%.*s'",
+ len, name),
+ "HEAD", &oid,
+ NULL, 0, UPDATE_REFS_MSG_ON_ERR);
cleanup:
free((void *)desc.buffer);
if (ret < 0)
@@ -3936,6 +4069,7 @@ static int do_merge(struct repository *r,
const char *arg, int arg_len,
int flags, int *check_todo, struct replay_opts *opts)
{
+ struct replay_ctx *ctx = opts->ctx;
int run_commit_flags = 0;
struct strbuf ref_name = STRBUF_INIT;
struct commit *head_commit, *merge_commit, *i;
@@ -4064,40 +4198,31 @@ static int do_merge(struct repository *r,
write_author_script(message);
find_commit_subject(message, &body);
len = strlen(body);
- ret = write_message(body, len, git_path_merge_msg(r), 0);
+ strbuf_add(&ctx->message, body, len);
repo_unuse_commit_buffer(r, commit, message);
- if (ret) {
- error_errno(_("could not write '%s'"),
- git_path_merge_msg(r));
- goto leave_merge;
- }
} else {
struct strbuf buf = STRBUF_INIT;
- int len;
strbuf_addf(&buf, "author %s", git_author_info(0));
write_author_script(buf.buf);
- strbuf_reset(&buf);
+ strbuf_release(&buf);
if (oneline_offset < arg_len) {
- p = arg + oneline_offset;
- len = arg_len - oneline_offset;
+ strbuf_add(&ctx->message, arg + oneline_offset,
+ arg_len - oneline_offset);
} else {
- strbuf_addf(&buf, "Merge %s '%.*s'",
+ strbuf_addf(&ctx->message, "Merge %s '%.*s'",
to_merge->next ? "branches" : "branch",
merge_arg_len, arg);
- p = buf.buf;
- len = buf.len;
- }
-
- ret = write_message(p, len, git_path_merge_msg(r), 0);
- strbuf_release(&buf);
- if (ret) {
- error_errno(_("could not write '%s'"),
- git_path_merge_msg(r));
- goto leave_merge;
}
}
+ ctx->have_message = 1;
+ if (write_message(ctx->message.buf, ctx->message.len,
+ git_path_merge_msg(r), 0)) {
+ ret = error_errno(_("could not write '%s'"),
+ git_path_merge_msg(r));
+ goto leave_merge;
+ }
if (strategy || to_merge->next) {
/* Octopus merge */
@@ -4252,6 +4377,7 @@ leave_merge:
strbuf_release(&ref_name);
rollback_lock_file(&lock);
free_commit_list(to_merge);
+ free_commit_list(bases);
return ret;
}
@@ -4411,7 +4537,7 @@ static int do_update_ref(struct repository *r, const char *refname)
for_each_string_list_item(item, &list) {
if (!strcmp(item->string, refname)) {
struct update_ref_record *rec = item->util;
- if (read_ref("HEAD", &rec->after))
+ if (refs_read_ref(get_main_ref_store(the_repository), "HEAD", &rec->after))
return -1;
break;
}
@@ -4758,11 +4884,12 @@ static int pick_one_commit(struct repository *r,
struct replay_opts *opts,
int *check_todo, int* reschedule)
{
+ struct replay_ctx *ctx = opts->ctx;
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(
+ ctx->reflog_message = reflog_message(
opts, command_to_string(item->command), NULL);
res = do_pick_commit(r, item, opts, is_final_fixup(todo_list),
@@ -4819,9 +4946,10 @@ static int pick_commits(struct repository *r,
struct todo_list *todo_list,
struct replay_opts *opts)
{
+ struct replay_ctx *ctx = opts->ctx;
int res = 0, reschedule = 0;
- opts->reflog_message = sequencer_reflog_action(opts);
+ ctx->reflog_message = sequencer_reflog_action(opts);
if (opts->allow_ff)
assert(!(opts->signoff || opts->no_commit ||
opts->record_origin || should_edit(opts) ||
@@ -4871,6 +4999,8 @@ static int pick_commits(struct repository *r,
return stopped_at_head(r);
}
}
+ strbuf_reset(&ctx->message);
+ ctx->have_message = 0;
if (item->command <= TODO_SQUASH) {
res = pick_one_commit(r, todo_list, opts, &check_todo,
&reschedule);
@@ -4967,15 +5097,15 @@ cleanup_head_ref:
}
msg = reflog_message(opts, "finish", "%s onto %s",
head_ref.buf, buf.buf);
- if (update_ref(msg, head_ref.buf, &head, &orig,
- REF_NO_DEREF, UPDATE_REFS_MSG_ON_ERR)) {
+ if (refs_update_ref(get_main_ref_store(the_repository), msg, head_ref.buf, &head, &orig,
+ REF_NO_DEREF, UPDATE_REFS_MSG_ON_ERR)) {
res = error(_("could not update %s"),
head_ref.buf);
goto cleanup_head_ref;
}
msg = reflog_message(opts, "finish", "returning to %s",
head_ref.buf);
- if (create_symref("HEAD", head_ref.buf, msg)) {
+ if (refs_update_symref(get_main_ref_store(the_repository), "HEAD", head_ref.buf, msg)) {
res = error(_("could not update HEAD to %s"),
head_ref.buf);
goto cleanup_head_ref;
@@ -5076,35 +5206,50 @@ static int commit_staged_changes(struct repository *r,
struct replay_opts *opts,
struct todo_list *todo_list)
{
+ struct replay_ctx *ctx = opts->ctx;
unsigned int flags = ALLOW_EMPTY | EDIT_MSG;
unsigned int final_fixup = 0, is_clean;
+ struct strbuf rev = STRBUF_INIT;
+ int ret;
- if (has_unstaged_changes(r, 1))
- return error(_("cannot rebase: You have unstaged changes."));
+ if (has_unstaged_changes(r, 1)) {
+ ret = error(_("cannot rebase: You have unstaged changes."));
+ goto out;
+ }
is_clean = !has_uncommitted_changes(r, 0);
if (!is_clean && !file_exists(rebase_path_message())) {
const char *gpg_opt = gpg_sign_opt_quoted(opts);
-
- return error(_(staged_changes_advice), gpg_opt, gpg_opt);
+ ret = error(_(staged_changes_advice), gpg_opt, gpg_opt);
+ goto out;
}
+
if (file_exists(rebase_path_amend())) {
- struct strbuf rev = STRBUF_INIT;
struct object_id head, to_amend;
- if (repo_get_oid(r, "HEAD", &head))
- return error(_("cannot amend non-existing commit"));
- if (!read_oneliner(&rev, rebase_path_amend(), 0))
- return error(_("invalid file: '%s'"), rebase_path_amend());
- if (get_oid_hex(rev.buf, &to_amend))
- return error(_("invalid contents: '%s'"),
- rebase_path_amend());
- if (!is_clean && !oideq(&head, &to_amend))
- return error(_("\nYou have uncommitted changes in your "
- "working tree. Please, commit them\n"
- "first and then run 'git rebase "
- "--continue' again."));
+ if (repo_get_oid(r, "HEAD", &head)) {
+ ret = error(_("cannot amend non-existing commit"));
+ goto out;
+ }
+
+ if (!read_oneliner(&rev, rebase_path_amend(), 0)) {
+ ret = error(_("invalid file: '%s'"), rebase_path_amend());
+ goto out;
+ }
+
+ if (get_oid_hex(rev.buf, &to_amend)) {
+ ret = error(_("invalid contents: '%s'"),
+ rebase_path_amend());
+ goto out;
+ }
+ if (!is_clean && !oideq(&head, &to_amend)) {
+ ret = error(_("\nYou have uncommitted changes in your "
+ "working tree. Please, commit them\n"
+ "first and then run 'git rebase "
+ "--continue' again."));
+ goto out;
+ }
/*
* When skipping a failed fixup/squash, we need to edit the
* commit message, the current fixup list and count, and if it
@@ -5112,7 +5257,7 @@ static int commit_staged_changes(struct repository *r,
* the commit message and if there was a squash, let the user
* edit it.
*/
- if (!is_clean || !opts->current_fixup_count)
+ if (!is_clean || !ctx->current_fixup_count)
; /* this is not the final fixup */
else if (!oideq(&head, &to_amend) ||
!file_exists(rebase_path_stopped_sha())) {
@@ -5121,24 +5266,26 @@ static int commit_staged_changes(struct repository *r,
unlink(rebase_path_fixup_msg());
unlink(rebase_path_squash_msg());
unlink(rebase_path_current_fixups());
- strbuf_reset(&opts->current_fixups);
- opts->current_fixup_count = 0;
+ strbuf_reset(&ctx->current_fixups);
+ ctx->current_fixup_count = 0;
}
} else {
/* we are in a fixup/squash chain */
- const char *p = opts->current_fixups.buf;
- int len = opts->current_fixups.len;
+ const char *p = ctx->current_fixups.buf;
+ int len = ctx->current_fixups.len;
- opts->current_fixup_count--;
+ ctx->current_fixup_count--;
if (!len)
BUG("Incorrect current_fixups:\n%s", p);
while (len && p[len - 1] != '\n')
len--;
- strbuf_setlen(&opts->current_fixups, len);
+ strbuf_setlen(&ctx->current_fixups, len);
if (write_message(p, len, rebase_path_current_fixups(),
- 0) < 0)
- return error(_("could not write file: '%s'"),
- rebase_path_current_fixups());
+ 0) < 0) {
+ ret = error(_("could not write file: '%s'"),
+ rebase_path_current_fixups());
+ goto out;
+ }
/*
* If a fixup/squash in a fixup/squash chain failed, the
@@ -5151,7 +5298,7 @@ static int commit_staged_changes(struct repository *r,
* actually need to re-commit with a cleaned up commit
* message.
*/
- if (opts->current_fixup_count > 0 &&
+ if (ctx->current_fixup_count > 0 &&
!is_fixup(peek_command(todo_list, 0))) {
final_fixup = 1;
/*
@@ -5168,35 +5315,38 @@ static int commit_staged_changes(struct repository *r,
* We need to update the squash message to skip
* the latest commit message.
*/
- int res = 0;
struct commit *commit;
const char *msg;
const char *path = rebase_path_squash_msg();
const char *encoding = get_commit_output_encoding();
- if (parse_head(r, &commit))
- return error(_("could not parse HEAD"));
+ if (parse_head(r, &commit)) {
+ ret = error(_("could not parse HEAD"));
+ goto out;
+ }
p = repo_logmsg_reencode(r, commit, NULL, encoding);
if (!p) {
- res = error(_("could not parse commit %s"),
+ ret = error(_("could not parse commit %s"),
oid_to_hex(&commit->object.oid));
goto unuse_commit_buffer;
}
find_commit_subject(p, &msg);
if (write_message(msg, strlen(msg), path, 0)) {
- res = error(_("could not write file: "
+ ret = error(_("could not write file: "
"'%s'"), path);
goto unuse_commit_buffer;
}
+
+ ret = 0;
+
unuse_commit_buffer:
repo_unuse_commit_buffer(r, commit, p);
- if (res)
- return res;
+ if (ret)
+ goto out;
}
}
- strbuf_release(&rev);
flags |= AMEND_MSG;
}
@@ -5204,18 +5354,29 @@ static int commit_staged_changes(struct repository *r,
if (refs_ref_exists(get_main_ref_store(r),
"CHERRY_PICK_HEAD") &&
refs_delete_ref(get_main_ref_store(r), "",
- "CHERRY_PICK_HEAD", NULL, REF_NO_DEREF))
- return error(_("could not remove CHERRY_PICK_HEAD"));
- if (unlink(git_path_merge_msg(r)) && errno != ENOENT)
- return error_errno(_("could not remove '%s'"),
- git_path_merge_msg(r));
- if (!final_fixup)
- return 0;
+ "CHERRY_PICK_HEAD", NULL, REF_NO_DEREF)) {
+ ret = error(_("could not remove CHERRY_PICK_HEAD"));
+ goto out;
+ }
+
+ if (unlink(git_path_merge_msg(r)) && errno != ENOENT) {
+ ret = error_errno(_("could not remove '%s'"),
+ git_path_merge_msg(r));
+ goto out;
+ }
+
+ if (!final_fixup) {
+ ret = 0;
+ goto out;
+ }
}
if (run_git_commit(final_fixup ? NULL : rebase_path_message(),
- opts, flags))
- return error(_("could not commit staged changes."));
+ opts, flags)) {
+ ret = error(_("could not commit staged changes."));
+ goto out;
+ }
+
unlink(rebase_path_amend());
unlink(git_path_merge_head(r));
refs_delete_ref(get_main_ref_store(r), "", "AUTO_MERGE",
@@ -5224,20 +5385,26 @@ static int commit_staged_changes(struct repository *r,
unlink(rebase_path_fixup_msg());
unlink(rebase_path_squash_msg());
}
- if (opts->current_fixup_count > 0) {
+ if (ctx->current_fixup_count > 0) {
/*
* Whether final fixup or not, we just cleaned up the commit
* message...
*/
unlink(rebase_path_current_fixups());
- strbuf_reset(&opts->current_fixups);
- opts->current_fixup_count = 0;
+ strbuf_reset(&ctx->current_fixups);
+ ctx->current_fixup_count = 0;
}
- return 0;
+
+ ret = 0;
+
+out:
+ strbuf_release(&rev);
+ return ret;
}
int sequencer_continue(struct repository *r, struct replay_opts *opts)
{
+ struct replay_ctx *ctx = opts->ctx;
struct todo_list todo_list = TODO_LIST_INIT;
int res;
@@ -5251,13 +5418,14 @@ int sequencer_continue(struct repository *r, struct replay_opts *opts)
goto release_todo_list;
if (file_exists(rebase_path_dropped())) {
- if ((res = todo_list_check_against_backup(r, &todo_list)))
+ if ((res = todo_list_check_against_backup(r, opts,
+ &todo_list)))
goto release_todo_list;
unlink(rebase_path_dropped());
}
- opts->reflog_message = reflog_message(opts, "continue", NULL);
+ ctx->reflog_message = reflog_message(opts, "continue", NULL);
if (commit_staged_changes(r, opts, &todo_list)) {
res = -1;
goto release_todo_list;
@@ -5309,7 +5477,7 @@ static int single_pick(struct repository *r,
TODO_PICK : TODO_REVERT;
item.commit = cmit;
- opts->reflog_message = sequencer_reflog_action(opts);
+ opts->ctx->reflog_message = sequencer_reflog_action(opts);
return do_pick_commit(r, &item, opts, 0, &check_todo);
}
@@ -5847,6 +6015,9 @@ static int make_script_with_merges(struct pretty_print_context *pp,
strbuf_release(&oneline);
strbuf_release(&buf);
+ oidset_clear(&interesting);
+ oidset_clear(&child_seen);
+ oidset_clear(&shown);
oidmap_free(&commit2todo, 1);
oidmap_free(&state.commit2label, 1);
hashmap_clear_and_free(&state.labels, struct labels_entry, entry);
@@ -6143,10 +6314,11 @@ static int add_decorations_to_list(const struct commit *commit,
struct todo_add_branch_context *ctx)
{
const struct name_decoration *decoration = get_name_decoration(&commit->object);
- const char *head_ref = resolve_ref_unsafe("HEAD",
- RESOLVE_REF_READING,
- NULL,
- NULL);
+ const char *head_ref = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
+ "HEAD",
+ RESOLVE_REF_READING,
+ NULL,
+ NULL);
while (decoration) {
struct todo_item *item;
@@ -6294,7 +6466,7 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla
return error(_("nothing to do"));
}
- res = edit_todo_list(r, todo_list, &new_todo, shortrevisions,
+ res = edit_todo_list(r, opts, todo_list, &new_todo, shortrevisions,
shortonto, flags);
if (res == -1)
return -1;
@@ -6322,7 +6494,7 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla
strbuf_release(&buf2);
/* 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)
+ if (todo_list_parse_insn_buffer(r, opts, new_todo.buf.buf, &new_todo) < 0)
BUG("invalid todo list after expanding IDs:\n%s",
new_todo.buf.buf);
diff --git a/sequencer.h b/sequencer.h
index 437eabd38a..304ba4b4d3 100644
--- a/sequencer.h
+++ b/sequencer.h
@@ -31,6 +31,9 @@ enum commit_msg_cleanup_mode {
COMMIT_MSG_CLEANUP_ALL
};
+struct replay_ctx;
+struct replay_ctx* replay_ctx_new(void);
+
struct replay_opts {
enum replay_action action;
@@ -68,10 +71,6 @@ struct replay_opts {
/* Reflog */
char *reflog_action;
- /* Used by fixup/squash */
- struct strbuf current_fixups;
- int current_fixup_count;
-
/* placeholder commit for -i --root */
struct object_id squash_onto;
int have_squash_onto;
@@ -80,13 +79,13 @@ struct replay_opts {
struct rev_info *revs;
/* Private use */
- const char *reflog_message;
+ struct replay_ctx *ctx;
};
#define REPLAY_OPTS_INIT { \
.edit = -1, \
.action = -1, \
- .current_fixups = STRBUF_INIT, \
.xopts = STRVEC_INIT, \
+ .ctx = replay_ctx_new(), \
}
/*
@@ -137,8 +136,8 @@ struct todo_list {
.buf = STRBUF_INIT, \
}
-int todo_list_parse_insn_buffer(struct repository *r, char *buf,
- struct todo_list *todo_list);
+int todo_list_parse_insn_buffer(struct repository *r, struct replay_opts *opts,
+ char *buf, struct todo_list *todo_list);
int todo_list_write_to_file(struct repository *r, struct todo_list *todo_list,
const char *file, const char *shortrevisions,
const char *shortonto, int num, unsigned flags);
diff --git a/serve.c b/serve.c
index aa651b73e9..884cd84ca8 100644
--- a/serve.c
+++ b/serve.c
@@ -1,7 +1,9 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "repository.h"
#include "config.h"
-#include "hash-ll.h"
+#include "hash.h"
#include "pkt-line.h"
#include "version.h"
#include "ls-refs.h"
diff --git a/server-info.c b/server-info.c
index e2fe0f9143..f61296a60d 100644
--- a/server-info.c
+++ b/server-info.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "dir.h"
#include "environment.h"
@@ -13,6 +15,7 @@
#include "object-store-ll.h"
#include "server-info.h"
#include "strbuf.h"
+#include "tempfile.h"
struct update_info_ctx {
FILE *cur_fp;
@@ -75,9 +78,8 @@ static int update_info_file(char *path,
int force)
{
char *tmp = mkpathdup("%s_XXXXXX", path);
+ struct tempfile *f = NULL;
int ret = -1;
- int fd = -1;
- FILE *to_close;
struct update_info_ctx uic = {
.cur_fp = NULL,
.old_fp = NULL,
@@ -86,13 +88,12 @@ static int update_info_file(char *path,
};
safe_create_leading_directories(path);
- fd = git_mkstemp_mode(tmp, 0666);
- if (fd < 0)
+ f = mks_tempfile_m(tmp, 0666);
+ if (!f)
goto out;
- to_close = uic.cur_fp = fdopen(fd, "w");
+ uic.cur_fp = fdopen_tempfile(f, "w");
if (!uic.cur_fp)
goto out;
- fd = -1;
/* no problem on ENOENT and old_fp == NULL, it's stale, now */
if (!force)
@@ -121,27 +122,22 @@ static int update_info_file(char *path,
}
uic.cur_fp = NULL;
- if (fclose(to_close))
- goto out;
if (uic_is_stale(&uic)) {
- if (adjust_shared_perm(tmp) < 0)
+ if (adjust_shared_perm(get_tempfile_path(f)) < 0)
goto out;
- if (rename(tmp, path) < 0)
+ if (rename_tempfile(&f, path) < 0)
goto out;
} else {
- unlink(tmp);
+ delete_tempfile(&f);
}
ret = 0;
out:
if (ret) {
error_errno("unable to update %s", path);
- if (uic.cur_fp)
- fclose(uic.cur_fp);
- else if (fd >= 0)
- close(fd);
- unlink(tmp);
+ if (f)
+ delete_tempfile(&f);
}
free(tmp);
if (uic.old_fp)
@@ -175,7 +171,8 @@ static int add_info_ref(const char *path, const struct object_id *oid,
static int generate_info_refs(struct update_info_ctx *uic)
{
- return for_each_ref(add_info_ref, uic);
+ return refs_for_each_ref(get_main_ref_store(the_repository),
+ add_info_ref, uic);
}
static int update_info_refs(int force)
diff --git a/setup.c b/setup.c
index f4b32f76e3..d458edcc02 100644
--- a/setup.c
+++ b/setup.c
@@ -1,9 +1,12 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "abspath.h"
#include "copy.h"
#include "environment.h"
#include "exec-cmd.h"
#include "gettext.h"
+#include "hex.h"
#include "object-name.h"
#include "refs.h"
#include "repository.h"
@@ -16,6 +19,7 @@
#include "quote.h"
#include "trace2.h"
#include "worktree.h"
+#include "exec-cmd.h"
static int inside_git_dir = -1;
static int inside_work_tree = -1;
@@ -47,7 +51,7 @@ static int abspath_part_inside_repo(char *path)
size_t wtlen;
char *path0;
int off;
- const char *work_tree = get_git_work_tree();
+ const char *work_tree = precompose_string_if_needed(get_git_work_tree());
struct strbuf realpath = STRBUF_INIT;
if (!work_tree)
@@ -341,6 +345,58 @@ int get_common_dir_noenv(struct strbuf *sb, const char *gitdir)
return ret;
}
+static int validate_headref(const char *path)
+{
+ struct stat st;
+ char buffer[256];
+ const char *refname;
+ struct object_id oid;
+ int fd;
+ ssize_t len;
+
+ if (lstat(path, &st) < 0)
+ return -1;
+
+ /* Make sure it is a "refs/.." symlink */
+ if (S_ISLNK(st.st_mode)) {
+ len = readlink(path, buffer, sizeof(buffer)-1);
+ if (len >= 5 && !memcmp("refs/", buffer, 5))
+ return 0;
+ return -1;
+ }
+
+ /*
+ * Anything else, just open it and try to see if it is a symbolic ref.
+ */
+ fd = open(path, O_RDONLY);
+ if (fd < 0)
+ return -1;
+ len = read_in_full(fd, buffer, sizeof(buffer)-1);
+ close(fd);
+
+ if (len < 0)
+ return -1;
+ buffer[len] = '\0';
+
+ /*
+ * Is it a symbolic ref?
+ */
+ if (skip_prefix(buffer, "ref:", &refname)) {
+ while (isspace(*refname))
+ refname++;
+ if (starts_with(refname, "refs/"))
+ return 0;
+ }
+
+ /*
+ * Is this a detached HEAD?
+ */
+ if (get_oid_hex_any(buffer, &oid) != GIT_HASH_UNKNOWN)
+ return 0;
+
+ return -1;
+}
+
/*
* Test if it looks like we're at a git directory.
* We want to see:
@@ -1176,13 +1232,20 @@ static int safe_directory_cb(const char *key, const char *value,
} else if (!strcmp(value, "*")) {
data->is_safe = 1;
} else {
- const char *interpolated = NULL;
-
- if (!git_config_pathname(&interpolated, key, value) &&
- !fspathcmp(data->path, interpolated ? interpolated : value))
- data->is_safe = 1;
-
- free((char *)interpolated);
+ char *allowed = NULL;
+
+ if (!git_config_pathname(&allowed, key, value)) {
+ const char *check = allowed ? allowed : value;
+ if (ends_with(check, "/*")) {
+ size_t len = strlen(check);
+ if (!fspathncmp(check, data->path, len - 1))
+ data->is_safe = 1;
+ } else if (!fspathcmp(data->path, check)) {
+ data->is_safe = 1;
+ }
+ }
+ if (allowed != value)
+ free(allowed);
}
return 0;
@@ -1220,6 +1283,27 @@ static int ensure_valid_ownership(const char *gitfile,
return data.is_safe;
}
+void die_upon_dubious_ownership(const char *gitfile, const char *worktree,
+ const char *gitdir)
+{
+ struct strbuf report = STRBUF_INIT, quoted = STRBUF_INIT;
+ const char *path;
+
+ if (ensure_valid_ownership(gitfile, worktree, gitdir, &report))
+ return;
+
+ strbuf_complete(&report, '\n');
+ path = gitfile ? gitfile : gitdir;
+ sq_quote_buf_pretty(&quoted, path);
+
+ die(_("detected dubious ownership in repository at '%s'\n"
+ "%s"
+ "To add an exception for this directory, call:\n"
+ "\n"
+ "\tgit config --global --add safe.directory %s"),
+ path, report.buf, quoted.buf);
+}
+
static int allowed_bare_repo_cb(const char *key, const char *value,
const struct config_context *ctx UNUSED,
void *d)
@@ -1781,6 +1865,57 @@ int daemonize(void)
#endif
}
+struct template_dir_cb_data {
+ char *path;
+ int initialized;
+};
+
+static int template_dir_cb(const char *key, const char *value,
+ const struct config_context *ctx, void *d)
+{
+ struct template_dir_cb_data *data = d;
+
+ if (strcmp(key, "init.templatedir"))
+ return 0;
+
+ if (!value) {
+ data->path = NULL;
+ } else {
+ char *path = NULL;
+
+ FREE_AND_NULL(data->path);
+ if (!git_config_pathname(&path, key, value))
+ data->path = path ? path : xstrdup(value);
+ }
+
+ return 0;
+}
+
+const char *get_template_dir(const char *option_template)
+{
+ const char *template_dir = option_template;
+
+ if (!template_dir)
+ template_dir = getenv(TEMPLATE_DIR_ENVIRONMENT);
+ if (!template_dir) {
+ static struct template_dir_cb_data data;
+
+ if (!data.initialized) {
+ git_protected_config(template_dir_cb, &data);
+ data.initialized = 1;
+ }
+ template_dir = data.path;
+ }
+ if (!template_dir) {
+ static char *dir;
+
+ if (!dir)
+ dir = system_path(DEFAULT_GIT_TEMPLATE_DIR);
+ template_dir = dir;
+ }
+ return template_dir;
+}
+
#ifdef NO_TRUSTABLE_FILEMODE
#define TEST_FILEMODE 0
#else
@@ -1856,8 +1991,9 @@ static void copy_templates_1(struct strbuf *path, struct strbuf *template_path,
}
}
-static void copy_templates(const char *template_dir, const char *init_template_dir)
+static void copy_templates(const char *option_template)
{
+ const char *template_dir = get_template_dir(option_template);
struct strbuf path = STRBUF_INIT;
struct strbuf template_path = STRBUF_INIT;
size_t template_len;
@@ -1866,16 +2002,8 @@ static void copy_templates(const char *template_dir, const char *init_template_d
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);
+ if (!template_dir || !*template_dir)
return;
- }
strbuf_addstr(&template_path, template_dir);
strbuf_complete(&template_path, '/');
@@ -1931,7 +2059,7 @@ static int needs_work_tree_config(const char *git_dir, const char *work_tree)
}
void initialize_repository_version(int hash_algo,
- unsigned int ref_storage_format,
+ enum ref_storage_format ref_storage_format,
int reinit)
{
char repo_version_string[10];
@@ -1962,6 +2090,8 @@ void initialize_repository_version(int hash_algo,
if (ref_storage_format != REF_STORAGE_FORMAT_FILES)
git_config_set("extensions.refstorage",
ref_storage_format_to_name(ref_storage_format));
+ else if (reinit)
+ git_config_set_gently("extensions.refstorage", NULL);
}
static int is_reinit(void)
@@ -1976,14 +2106,15 @@ static int is_reinit(void)
return ret;
}
-void create_reference_database(unsigned int ref_storage_format,
+void create_reference_database(enum ref_storage_format ref_storage_format,
const char *initial_branch, int quiet)
{
struct strbuf err = STRBUF_INIT;
+ char *to_free = NULL;
int reinit = is_reinit();
repo_set_ref_storage_format(the_repository, ref_storage_format);
- if (refs_init_db(get_main_ref_store(the_repository), 0, &err))
+ if (ref_store_create_on_disk(get_main_ref_store(the_repository), 0, &err))
die("failed to set up refs db: %s", err.buf);
/*
@@ -1994,14 +2125,15 @@ void create_reference_database(unsigned int ref_storage_format,
char *ref;
if (!initial_branch)
- initial_branch = git_default_branch_name(quiet);
+ initial_branch = to_free =
+ repo_default_branch_name(the_repository, 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)
+ if (refs_update_symref(get_main_ref_store(the_repository), "HEAD", ref, NULL) < 0)
exit(1);
free(ref);
}
@@ -2011,6 +2143,7 @@ void create_reference_database(unsigned int ref_storage_format,
initial_branch);
strbuf_release(&err);
+ free(to_free);
}
static int create_default_files(const char *template_path,
@@ -2023,7 +2156,6 @@ static int create_default_files(const char *template_path,
char *path;
int reinit;
int filemode;
- const char *init_template_dir = NULL;
const char *work_tree = get_git_work_tree();
/*
@@ -2035,9 +2167,7 @@ static int create_default_files(const char *template_path,
* 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);
+ copy_templates(template_path);
git_config_clear();
reset_shared_repository();
git_config(git_default_config, NULL);
@@ -2175,7 +2305,7 @@ static void validate_hash_algorithm(struct repository_format *repo_fmt, int hash
}
static void validate_ref_storage_format(struct repository_format *repo_fmt,
- unsigned int format)
+ enum ref_storage_format format)
{
const char *name = getenv("GIT_DEFAULT_REF_FORMAT");
@@ -2195,7 +2325,7 @@ static void validate_ref_storage_format(struct repository_format *repo_fmt,
int init_db(const char *git_dir, const char *real_git_dir,
const char *template_dir, int hash,
- unsigned int ref_storage_format,
+ enum ref_storage_format ref_storage_format,
const char *initial_branch,
int init_shared_repository, unsigned int flags)
{
@@ -2223,12 +2353,6 @@ int init_db(const char *git_dir, const char *real_git_dir,
}
startup_info->have_repository = 1;
- /* Ensure `core.hidedotfiles` is processed */
- git_config(platform_core_config, NULL);
-
- safe_create_dir(git_dir, 0);
-
-
/* 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
@@ -2239,9 +2363,6 @@ int init_db(const char *git_dir, const char *real_git_dir,
validate_hash_algorithm(&repo_fmt, hash);
validate_ref_storage_format(&repo_fmt, ref_storage_format);
- reinit = create_default_files(template_dir, original_git_dir,
- &repo_fmt, init_shared_repository);
-
/*
* Now that we have set up both the hash algorithm and the ref storage
* format we can update the repository's settings accordingly.
@@ -2249,6 +2370,18 @@ int init_db(const char *git_dir, const char *real_git_dir,
repo_set_hash_algo(the_repository, repo_fmt.hash_algo);
repo_set_ref_storage_format(the_repository, repo_fmt.ref_storage_format);
+ /*
+ * Ensure `core.hidedotfiles` is processed. This must happen after we
+ * have set up the repository format such that we can evaluate
+ * includeIf conditions correctly in the case of re-initialization.
+ */
+ git_config(platform_core_config, NULL);
+
+ safe_create_dir(git_dir, 0);
+
+ reinit = create_default_files(template_dir, original_git_dir,
+ &repo_fmt, init_shared_repository);
+
if (!(flags & INIT_DB_SKIP_REFDB))
create_reference_database(repo_fmt.ref_storage_format,
initial_branch, flags & INIT_DB_QUIET);
diff --git a/setup.h b/setup.h
index d88bb37aaf..cd8dbc2497 100644
--- a/setup.h
+++ b/setup.h
@@ -1,6 +1,7 @@
#ifndef SETUP_H
#define SETUP_H
+#include "refs.h"
#include "string-list.h"
int is_inside_git_dir(void);
@@ -41,6 +42,18 @@ const char *read_gitfile_gently(const char *path, int *return_error_code);
const char *resolve_gitdir_gently(const char *suspect, int *return_error_code);
#define resolve_gitdir(path) resolve_gitdir_gently((path), NULL)
+/*
+ * Check if a repository is safe and die if it is not, by verifying the
+ * ownership of the worktree (if any), the git directory, and the gitfile (if
+ * any).
+ *
+ * Exemptions for known-safe repositories can be added via `safe.directory`
+ * config settings; for non-bare repositories, their worktree needs to be
+ * added, for bare ones their git directory.
+ */
+void die_upon_dubious_ownership(const char *gitfile, const char *worktree,
+ const char *gitdir);
+
void setup_work_tree(void);
/*
@@ -116,7 +129,7 @@ struct repository_format {
int is_bare;
int hash_algo;
int compat_hash_algo;
- unsigned int ref_storage_format;
+ enum ref_storage_format ref_storage_format;
int sparse_index;
char *work_tree;
struct string_list unknown_extensions;
@@ -172,19 +185,21 @@ int verify_repository_format(const struct repository_format *format,
*/
void check_repository_format(struct repository_format *fmt);
+const char *get_template_dir(const char *option_template);
+
#define INIT_DB_QUIET (1 << 0)
#define INIT_DB_EXIST_OK (1 << 1)
#define INIT_DB_SKIP_REFDB (1 << 2)
int init_db(const char *git_dir, const char *real_git_dir,
const char *template_dir, int hash_algo,
- unsigned int ref_storage_format,
+ enum ref_storage_format ref_storage_format,
const char *initial_branch, int init_shared_repository,
unsigned int flags);
void initialize_repository_version(int hash_algo,
- unsigned int ref_storage_format,
+ enum ref_storage_format ref_storage_format,
int reinit);
-void create_reference_database(unsigned int ref_storage_format,
+void create_reference_database(enum ref_storage_format ref_storage_format,
const char *initial_branch, int quiet);
/*
diff --git a/shallow.c b/shallow.c
index 7ff50dd0da..31a6ca40fe 100644
--- a/shallow.c
+++ b/shallow.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "hex.h"
#include "repository.h"
@@ -678,8 +680,10 @@ void assign_shallow_commits_to_refs(struct shallow_info *info,
* connect to old refs. If not (e.g. force ref updates) it'll
* have to go down to the current shallow commits.
*/
- head_ref(mark_uninteresting, NULL);
- for_each_ref(mark_uninteresting, NULL);
+ refs_head_ref(get_main_ref_store(the_repository), mark_uninteresting,
+ NULL);
+ refs_for_each_ref(get_main_ref_store(the_repository),
+ mark_uninteresting, NULL);
/* Mark potential bottoms so we won't go out of bound */
for (i = 0; i < nr_shallow; i++) {
@@ -782,8 +786,8 @@ static void post_assign_shallow(struct shallow_info *info,
info->nr_theirs = dst;
memset(&ca, 0, sizeof(ca));
- head_ref(add_ref, &ca);
- for_each_ref(add_ref, &ca);
+ refs_head_ref(get_main_ref_store(the_repository), add_ref, &ca);
+ refs_for_each_ref(get_main_ref_store(the_repository), add_ref, &ca);
/* Remove unreachable shallow commits from "ours" */
for (i = dst = 0; i < info->nr_ours; i++) {
@@ -822,8 +826,10 @@ int delayed_reachability_test(struct shallow_info *si, int c)
struct commit_array ca;
memset(&ca, 0, sizeof(ca));
- head_ref(add_ref, &ca);
- for_each_ref(add_ref, &ca);
+ refs_head_ref(get_main_ref_store(the_repository),
+ add_ref, &ca);
+ refs_for_each_ref(get_main_ref_store(the_repository),
+ add_ref, &ca);
si->commits = ca.commits;
si->nr_commits = ca.nr;
}
diff --git a/sparse-index.c b/sparse-index.c
index e48e40cae7..9958656ded 100644
--- a/sparse-index.c
+++ b/sparse-index.c
@@ -12,6 +12,22 @@
#include "config.h"
#include "dir.h"
#include "fsmonitor-ll.h"
+#include "advice.h"
+
+/**
+ * This global is used by expand_index() to determine if we should give the
+ * advice for advice.sparseIndexExpanded when expanding a sparse index to a full
+ * one. However, this is sometimes done on purpose, such as in the sparse-checkout
+ * builtin, even when index.sparse=false. This may be disabled in
+ * convert_to_sparse().
+ */
+static int give_advice_on_expansion = 1;
+#define ADVICE_MSG \
+ "The sparse index is expanding to a full index, a slow operation.\n" \
+ "Your working directory likely has contents that are outside of\n" \
+ "your sparse-checkout patterns. Use 'git sparse-checkout list' to\n" \
+ "see your sparse-checkout definition and compare it to your working\n" \
+ "directory contents. Running 'git clean' may assist in this cleanup."
struct modify_index_context {
struct index_state *write;
@@ -184,6 +200,12 @@ int convert_to_sparse(struct index_state *istate, int flags)
return 0;
/*
+ * If we are purposefully collapsing a full index, then don't give
+ * advice when it is expanded later.
+ */
+ give_advice_on_expansion = 0;
+
+ /*
* NEEDSWORK: If we have unmerged entries, then stay full.
* Unmerged entries prevent the cache-tree extension from working.
*/
@@ -328,6 +350,12 @@ void expand_index(struct index_state *istate, struct pattern_list *pl)
pl = NULL;
}
+ if (!pl && give_advice_on_expansion) {
+ give_advice_on_expansion = 0;
+ advise_if_enabled(ADVICE_SPARSE_INDEX_EXPANDED,
+ _(ADVICE_MSG));
+ }
+
/*
* A NULL pattern set indicates we are expanding a full index, so
* we use a special region name that indicates the full expansion.
@@ -439,96 +467,208 @@ void ensure_correct_sparsity(struct index_state *istate)
ensure_full_index(istate);
}
-static int path_found(const char *path, const char **dirname, size_t *dir_len,
- int *dir_found)
+struct path_found_data {
+ /**
+ * The path stored in 'dir', if non-empty, corresponds to the most-
+ * recent path that we checked where:
+ *
+ * 1. The path should be a directory, according to the index.
+ * 2. The path does not exist.
+ * 3. The parent path _does_ exist. (This may be the root of the
+ * working directory.)
+ */
+ struct strbuf dir;
+ size_t lstat_count;
+};
+
+#define PATH_FOUND_DATA_INIT { \
+ .dir = STRBUF_INIT \
+}
+
+static void clear_path_found_data(struct path_found_data *data)
+{
+ strbuf_release(&data->dir);
+}
+
+/**
+ * Return the length of the longest common substring that ends in a
+ * slash ('/') to indicate the longest common parent directory. Returns
+ * zero if no common directory exists.
+ */
+static size_t max_common_dir_prefix(const char *path1, const char *path2)
+{
+ size_t common_prefix = 0;
+ for (size_t i = 0; path1[i] && path2[i]; i++) {
+ if (path1[i] != path2[i])
+ break;
+
+ /*
+ * If they agree at a directory separator, then add one
+ * to make sure it is included in the common prefix string.
+ */
+ if (path1[i] == '/')
+ common_prefix = i + 1;
+ }
+
+ return common_prefix;
+}
+
+static int path_found(const char *path, struct path_found_data *data)
{
struct stat st;
- char *newdir;
- char *tmp;
+ size_t common_prefix;
/*
- * If dirname corresponds to a directory that doesn't exist, and this
- * path starts with dirname, then path can't exist.
+ * If data->dir is non-empty, then it contains a path that doesn't
+ * exist, including an ending slash ('/'). If it is a prefix of 'path',
+ * then we can return 0.
*/
- if (!*dir_found && !memcmp(path, *dirname, *dir_len))
+ if (data->dir.len && !memcmp(path, data->dir.buf, data->dir.len))
return 0;
/*
- * If path itself exists, return 1.
+ * Otherwise, we must check if the current path exists. If it does, then
+ * return 1. The cached directory will be skipped until we come across
+ * a missing path again.
*/
+ data->lstat_count++;
if (!lstat(path, &st))
return 1;
/*
- * Otherwise, path does not exist so we'll return 0...but we'll first
- * determine some info about its parent directory so we can avoid
- * lstat calls for future cache entries.
+ * At this point, we know that 'path' doesn't exist, and we know that
+ * the parent directory of 'data->dir' does exist. Let's set 'data->dir'
+ * to be the top-most non-existing directory of 'path'. If the first
+ * parent of 'path' exists, then we will act as though 'path'
+ * corresponds to a directory (by adding a slash).
*/
- newdir = strrchr(path, '/');
- if (!newdir)
- return 0; /* Didn't find a parent dir; just return 0 now. */
+ common_prefix = max_common_dir_prefix(path, data->dir.buf);
/*
- * If path starts with directory (which we already lstat'ed and found),
- * then no need to lstat parent directory again.
+ * At this point, 'path' and 'data->dir' have a common existing parent
+ * directory given by path[0..common_prefix] (which could have length 0).
+ * We "grow" the data->dir buffer by checking for existing directories
+ * along 'path'.
*/
- if (*dir_found && *dirname && memcmp(path, *dirname, *dir_len))
- return 0;
- /* Free previous dirname, and cache path's dirname */
- *dirname = path;
- *dir_len = newdir - path + 1;
+ strbuf_setlen(&data->dir, common_prefix);
+ while (1) {
+ /* Find the next directory in 'path'. */
+ const char *rest = path + data->dir.len;
+ const char *next_slash = strchr(rest, '/');
+
+ /*
+ * If there are no more slashes, then 'path' doesn't contain a
+ * non-existent _parent_ directory. Set 'data->dir' to be equal
+ * to 'path' plus an additional slash, so it can be used for
+ * caching in the future. The filename of 'path' is considered
+ * a non-existent directory.
+ *
+ * Note: if "{path}/" exists as a directory, then it will never
+ * appear as a prefix of other callers to this method, assuming
+ * the context from the clear_skip_worktree... methods. If this
+ * method is reused, then this must be reconsidered.
+ */
+ if (!next_slash) {
+ strbuf_addstr(&data->dir, rest);
+ strbuf_addch(&data->dir, '/');
+ break;
+ }
- tmp = xstrndup(path, *dir_len);
- *dir_found = !lstat(tmp, &st);
- free(tmp);
+ /*
+ * Now that we have a slash, let's grow 'data->dir' to include
+ * this slash, then test if we should stop.
+ */
+ strbuf_add(&data->dir, rest, next_slash - rest + 1);
+ /* If the parent dir doesn't exist, then stop here. */
+ data->lstat_count++;
+ if (lstat(data->dir.buf, &st))
+ return 0;
+ }
+
+ /*
+ * At this point, 'data->dir' is equal to 'path' plus a slash character,
+ * and the parent directory of 'path' definitely exists. Moreover, we
+ * know that 'path' doesn't exist, or we would have returned 1 earlier.
+ */
return 0;
}
-void clear_skip_worktree_from_present_files(struct index_state *istate)
+static int clear_skip_worktree_from_present_files_sparse(struct index_state *istate)
{
- const char *last_dirname = NULL;
- size_t dir_len = 0;
- int dir_found = 1;
+ struct path_found_data data = PATH_FOUND_DATA_INIT;
- int i;
- int path_count[2] = {0, 0};
- int restarted = 0;
-
- if (!core_apply_sparse_checkout ||
- sparse_expect_files_outside_of_patterns)
- return;
+ int path_count = 0;
+ int to_restart = 0;
- trace2_region_enter("index", "clear_skip_worktree_from_present_files",
+ trace2_region_enter("index", "clear_skip_worktree_from_present_files_sparse",
istate->repo);
-restart:
- for (i = 0; i < istate->cache_nr; i++) {
+ for (int i = 0; i < istate->cache_nr; i++) {
struct cache_entry *ce = istate->cache[i];
if (ce_skip_worktree(ce)) {
- path_count[restarted]++;
- if (path_found(ce->name, &last_dirname, &dir_len, &dir_found)) {
+ path_count++;
+ if (path_found(ce->name, &data)) {
if (S_ISSPARSEDIR(ce->ce_mode)) {
- if (restarted)
- BUG("ensure-full-index did not fully flatten?");
- ensure_full_index(istate);
- restarted = 1;
- goto restart;
+ to_restart = 1;
+ break;
}
ce->ce_flags &= ~CE_SKIP_WORKTREE;
}
}
}
- if (path_count[0])
- trace2_data_intmax("index", istate->repo,
- "sparse_path_count", path_count[0]);
- if (restarted)
- trace2_data_intmax("index", istate->repo,
- "sparse_path_count_full", path_count[1]);
- trace2_region_leave("index", "clear_skip_worktree_from_present_files",
+ trace2_data_intmax("index", istate->repo,
+ "sparse_path_count", path_count);
+ trace2_data_intmax("index", istate->repo,
+ "sparse_lstat_count", data.lstat_count);
+ trace2_region_leave("index", "clear_skip_worktree_from_present_files_sparse",
istate->repo);
+ clear_path_found_data(&data);
+ return to_restart;
+}
+
+static void clear_skip_worktree_from_present_files_full(struct index_state *istate)
+{
+ struct path_found_data data = PATH_FOUND_DATA_INIT;
+
+ int path_count = 0;
+
+ trace2_region_enter("index", "clear_skip_worktree_from_present_files_full",
+ istate->repo);
+ for (int i = 0; i < istate->cache_nr; i++) {
+ struct cache_entry *ce = istate->cache[i];
+
+ if (S_ISSPARSEDIR(ce->ce_mode))
+ BUG("ensure-full-index did not fully flatten?");
+
+ if (ce_skip_worktree(ce)) {
+ path_count++;
+ if (path_found(ce->name, &data))
+ ce->ce_flags &= ~CE_SKIP_WORKTREE;
+ }
+ }
+
+ trace2_data_intmax("index", istate->repo,
+ "full_path_count", path_count);
+ trace2_data_intmax("index", istate->repo,
+ "full_lstat_count", data.lstat_count);
+ trace2_region_leave("index", "clear_skip_worktree_from_present_files_full",
+ istate->repo);
+ clear_path_found_data(&data);
+}
+
+void clear_skip_worktree_from_present_files(struct index_state *istate)
+{
+ if (!core_apply_sparse_checkout ||
+ sparse_expect_files_outside_of_patterns)
+ return;
+
+ if (clear_skip_worktree_from_present_files_sparse(istate)) {
+ ensure_full_index(istate);
+ clear_skip_worktree_from_present_files_full(istate);
+ }
}
/*
diff --git a/split-index.c b/split-index.c
index 8c38687c04..120c8190b1 100644
--- a/split-index.c
+++ b/split-index.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "gettext.h"
#include "hash.h"
@@ -29,7 +31,7 @@ int read_link_extension(struct index_state *istate,
if (sz < the_hash_algo->rawsz)
return error("corrupt link extension (too short)");
si = init_split_index(istate);
- oidread(&si->base_oid, data);
+ oidread(&si->base_oid, data, the_repository->hash_algo);
data += the_hash_algo->rawsz;
sz -= the_hash_algo->rawsz;
if (!sz)
diff --git a/split-index.h b/split-index.h
index 15a29cd08c..1a153f47ba 100644
--- a/split-index.h
+++ b/split-index.h
@@ -1,7 +1,7 @@
#ifndef SPLIT_INDEX_H
#define SPLIT_INDEX_H
-#include "hash-ll.h"
+#include "hash.h"
struct index_state;
struct strbuf;
diff --git a/strbuf.c b/strbuf.c
index 0d929e4e19..3d2189a7f6 100644
--- a/strbuf.c
+++ b/strbuf.c
@@ -313,6 +313,15 @@ void strbuf_add(struct strbuf *sb, const void *data, size_t len)
strbuf_setlen(sb, sb->len + len);
}
+void strbuf_addstrings(struct strbuf *sb, const char *s, size_t n)
+{
+ size_t len = strlen(s);
+
+ strbuf_grow(sb, st_mult(len, n));
+ for (size_t i = 0; i < n; i++)
+ strbuf_add(sb, s, len);
+}
+
void strbuf_addbuf(struct strbuf *sb, const struct strbuf *sb2)
{
strbuf_grow(sb, sb2->len);
@@ -691,8 +700,10 @@ int strbuf_getwholeline(struct strbuf *sb, FILE *fp, int term)
int strbuf_appendwholeline(struct strbuf *sb, FILE *fp, int term)
{
struct strbuf line = STRBUF_INIT;
- if (strbuf_getwholeline(&line, fp, term))
+ if (strbuf_getwholeline(&line, fp, term)) {
+ strbuf_release(&line);
return EOF;
+ }
strbuf_addbuf(sb, &line);
strbuf_release(&line);
return 0;
diff --git a/strbuf.h b/strbuf.h
index 97fa4a3d01..003f880ff7 100644
--- a/strbuf.h
+++ b/strbuf.h
@@ -311,6 +311,11 @@ static inline void strbuf_addstr(struct strbuf *sb, const char *s)
}
/**
+ * Add a NUL-terminated string the specified number of times to the buffer.
+ */
+void strbuf_addstrings(struct strbuf *sb, const char *s, size_t n);
+
+/**
* Copy the contents of another buffer at the end of the current one.
*/
void strbuf_addbuf(struct strbuf *sb, const struct strbuf *sb2);
diff --git a/streaming.c b/streaming.c
index 10adf625b2..38839511af 100644
--- a/streaming.c
+++ b/streaming.c
@@ -1,6 +1,9 @@
/*
* Copyright (c) 2011, Google Inc.
*/
+
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "convert.h"
#include "environment.h"
diff --git a/strvec.c b/strvec.c
index 178f4f3748..f712070f57 100644
--- a/strvec.c
+++ b/strvec.c
@@ -10,7 +10,7 @@ void strvec_init(struct strvec *array)
memcpy(array, &blank, sizeof(*array));
}
-static void strvec_push_nodup(struct strvec *array, const char *value)
+void strvec_push_nodup(struct strvec *array, char *value)
{
if (array->v == empty_strvec)
array->v = NULL;
@@ -56,6 +56,26 @@ void strvec_pushv(struct strvec *array, const char **items)
strvec_push(array, *items);
}
+const char *strvec_replace(struct strvec *array, size_t idx, const char *replacement)
+{
+ char *to_free;
+ if (idx >= array->nr)
+ BUG("index outside of array boundary");
+ to_free = (char *) array->v[idx];
+ array->v[idx] = xstrdup(replacement);
+ free(to_free);
+ return array->v[idx];
+}
+
+void strvec_remove(struct strvec *array, size_t idx)
+{
+ if (idx >= array->nr)
+ BUG("index outside of array boundary");
+ free((char *)array->v[idx]);
+ memmove(array->v + idx, array->v + idx + 1, (array->nr - idx) * sizeof(char *));
+ array->nr--;
+}
+
void strvec_pop(struct strvec *array)
{
if (!array->nr)
diff --git a/strvec.h b/strvec.h
index 4715d3e51f..4b73c1f092 100644
--- a/strvec.h
+++ b/strvec.h
@@ -46,6 +46,9 @@ void strvec_init(struct strvec *);
/* Push a copy of a string onto the end of the array. */
const char *strvec_push(struct strvec *, const char *);
+/* Push an allocated string onto the end of the array, taking ownership. */
+void strvec_push_nodup(struct strvec *array, char *value);
+
/**
* Format a string and push it onto the end of the array. This is a
* convenience wrapper combining `strbuf_addf` and `strvec_push`.
@@ -65,6 +68,19 @@ void strvec_pushl(struct strvec *, ...);
void strvec_pushv(struct strvec *, const char **);
/**
+ * Replace the value at the given index with a new value. The index must be
+ * valid. Returns a pointer to the inserted value.
+ */
+const char *strvec_replace(struct strvec *array, size_t idx, const char *replacement);
+
+/*
+ * Remove the value at the given index. The remainder of the array will be
+ * moved to fill the resulting gap. The provided index must point into the
+ * array.
+ */
+void strvec_remove(struct strvec *array, size_t idx);
+
+/**
* Remove the final element from the array. If there are no
* elements in the array, do nothing.
*/
diff --git a/submodule-config.c b/submodule-config.c
index 11428b4ada..9b0bb0b9f4 100644
--- a/submodule-config.c
+++ b/submodule-config.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "dir.h"
#include "environment.h"
@@ -91,6 +93,8 @@ static void free_one_config(struct submodule_entry *entry)
free((void *) entry->config->path);
free((void *) entry->config->name);
free((void *) entry->config->branch);
+ free((void *) entry->config->url);
+ free((void *) entry->config->ignore);
free((void *) entry->config->update_strategy.command);
free(entry->config);
}
@@ -680,7 +684,7 @@ static int gitmodule_oid_from_commit(const struct object_id *treeish_name,
int ret = 0;
if (is_null_oid(treeish_name)) {
- oidclr(gitmodules_oid);
+ oidclr(gitmodules_oid, the_repository->hash_algo);
return 1;
}
diff --git a/submodule.c b/submodule.c
index ce2d032521..ab99a30253 100644
--- a/submodule.c
+++ b/submodule.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "abspath.h"
#include "repository.h"
@@ -99,7 +101,8 @@ int is_staging_gitmodules_ok(struct index_state *istate)
static int for_each_remote_ref_submodule(const char *submodule,
each_ref_fn fn, void *cb_data)
{
- return refs_for_each_remote_ref(get_submodule_ref_store(submodule),
+ return refs_for_each_remote_ref(repo_get_submodule_ref_store(the_repository,
+ submodule),
fn, cb_data);
}
@@ -1015,6 +1018,9 @@ static int submodule_has_commits(struct repository *r,
.super_oid = super_oid
};
+ if (validate_submodule_path(path) < 0)
+ exit(128);
+
oid_array_for_each_unique(commits, check_has_commit, &has_commit);
if (has_commit.result) {
@@ -1137,6 +1143,9 @@ static int push_submodule(const char *path,
const struct string_list *push_options,
int dry_run)
{
+ if (validate_submodule_path(path) < 0)
+ exit(128);
+
if (for_each_remote_ref_submodule(path, has_remote, NULL) > 0) {
struct child_process cp = CHILD_PROCESS_INIT;
strvec_push(&cp.args, "push");
@@ -1186,6 +1195,9 @@ static void submodule_push_check(const char *path, const char *head,
struct child_process cp = CHILD_PROCESS_INIT;
int i;
+ if (validate_submodule_path(path) < 0)
+ exit(128);
+
strvec_push(&cp.args, "submodule--helper");
strvec_push(&cp.args, "push-check");
strvec_push(&cp.args, head);
@@ -1233,7 +1245,8 @@ int push_unpushed_submodules(struct repository *r,
char *head;
struct object_id head_oid;
- head = resolve_refdup("HEAD", 0, &head_oid, NULL);
+ head = refs_resolve_refdup(get_main_ref_store(the_repository),
+ "HEAD", 0, &head_oid, NULL);
if (!head)
die(_("Failed to resolve HEAD as a valid ref."));
@@ -1271,7 +1284,8 @@ static int append_oid_to_array(const char *ref UNUSED,
void check_for_new_submodule_commits(struct object_id *oid)
{
if (!initialized_fetch_ref_tips) {
- for_each_ref(append_oid_to_array, &ref_tips_before_fetch);
+ refs_for_each_ref(get_main_ref_store(the_repository),
+ append_oid_to_array, &ref_tips_before_fetch);
initialized_fetch_ref_tips = 1;
}
@@ -1517,6 +1531,9 @@ static struct fetch_task *fetch_task_create(struct submodule_parallel_fetch *spf
struct fetch_task *task = xmalloc(sizeof(*task));
memset(task, 0, sizeof(*task));
+ if (validate_submodule_path(path) < 0)
+ exit(128);
+
task->sub = submodule_from_path(spf->r, treeish_name, path);
if (!task->sub) {
@@ -1878,6 +1895,9 @@ unsigned is_submodule_modified(const char *path, int ignore_untracked)
const char *git_dir;
int ignore_cp_exit_code = 0;
+ if (validate_submodule_path(path) < 0)
+ exit(128);
+
strbuf_addf(&buf, "%s/.git", path);
git_dir = read_gitfile(buf.buf);
if (!git_dir)
@@ -1954,6 +1974,9 @@ int submodule_uses_gitfile(const char *path)
struct strbuf buf = STRBUF_INIT;
const char *git_dir;
+ if (validate_submodule_path(path) < 0)
+ exit(128);
+
strbuf_addf(&buf, "%s/.git", path);
git_dir = read_gitfile(buf.buf);
if (!git_dir) {
@@ -1993,6 +2016,9 @@ int bad_to_remove_submodule(const char *path, unsigned flags)
struct strbuf buf = STRBUF_INIT;
int ret = 0;
+ if (validate_submodule_path(path) < 0)
+ exit(128);
+
if (!file_exists(path) || is_empty_dir(path))
return 0;
@@ -2043,6 +2069,9 @@ void submodule_unset_core_worktree(const struct submodule *sub)
{
struct strbuf config_path = STRBUF_INIT;
+ if (validate_submodule_path(sub->path) < 0)
+ exit(128);
+
submodule_name_to_gitdir(&config_path, the_repository, sub->name);
strbuf_addstr(&config_path, "/config");
@@ -2057,6 +2086,9 @@ static int submodule_has_dirty_index(const struct submodule *sub)
{
struct child_process cp = CHILD_PROCESS_INIT;
+ if (validate_submodule_path(sub->path) < 0)
+ exit(128);
+
prepare_submodule_repo_env(&cp.env);
cp.git_cmd = 1;
@@ -2074,6 +2106,10 @@ static int submodule_has_dirty_index(const struct submodule *sub)
static void submodule_reset_index(const char *path, const char *super_prefix)
{
struct child_process cp = CHILD_PROCESS_INIT;
+
+ if (validate_submodule_path(path) < 0)
+ exit(128);
+
prepare_submodule_repo_env(&cp.env);
cp.git_cmd = 1;
@@ -2085,7 +2121,7 @@ static void submodule_reset_index(const char *path, const char *super_prefix)
strvec_pushf(&cp.args, "--super-prefix=%s%s/",
(super_prefix ? super_prefix : ""), path);
- strvec_push(&cp.args, empty_tree_oid_hex());
+ strvec_push(&cp.args, empty_tree_oid_hex(the_repository->hash_algo));
if (run_command(&cp))
die(_("could not reset submodule index"));
@@ -2137,10 +2173,27 @@ int submodule_move_head(const char *path, const char *super_prefix,
if (!submodule_uses_gitfile(path))
absorb_git_dir_into_superproject(path,
super_prefix);
+ else {
+ char *dotgit = xstrfmt("%s/.git", path);
+ char *git_dir = xstrdup(read_gitfile(dotgit));
+
+ free(dotgit);
+ if (validate_submodule_git_dir(git_dir,
+ sub->name) < 0)
+ die(_("refusing to create/use '%s' in "
+ "another submodule's git dir"),
+ git_dir);
+ free(git_dir);
+ }
} else {
struct strbuf gitdir = STRBUF_INIT;
submodule_name_to_gitdir(&gitdir, the_repository,
sub->name);
+ if (validate_submodule_git_dir(gitdir.buf,
+ sub->name) < 0)
+ die(_("refusing to create/use '%s' in another "
+ "submodule's git dir"),
+ gitdir.buf);
connect_work_tree_and_git_dir(path, gitdir.buf, 0);
strbuf_release(&gitdir);
@@ -2178,9 +2231,9 @@ int submodule_move_head(const char *path, const char *super_prefix,
strvec_push(&cp.args, "-m");
if (!(flags & SUBMODULE_MOVE_HEAD_FORCE))
- strvec_push(&cp.args, old_head ? old_head : empty_tree_oid_hex());
+ strvec_push(&cp.args, old_head ? old_head : empty_tree_oid_hex(the_repository->hash_algo));
- strvec_push(&cp.args, new_head ? new_head : empty_tree_oid_hex());
+ strvec_push(&cp.args, new_head ? new_head : empty_tree_oid_hex(the_repository->hash_algo));
if (run_command(&cp)) {
ret = error(_("Submodule '%s' could not be updated."), path);
@@ -2261,6 +2314,34 @@ int validate_submodule_git_dir(char *git_dir, const char *submodule_name)
return 0;
}
+int validate_submodule_path(const char *path)
+{
+ char *p = xstrdup(path);
+ struct stat st;
+ int i, ret = 0;
+ char sep;
+
+ for (i = 0; !ret && p[i]; i++) {
+ if (!is_dir_sep(p[i]))
+ continue;
+
+ sep = p[i];
+ p[i] = '\0';
+ /* allow missing components, but no symlinks */
+ ret = lstat(p, &st) || !S_ISLNK(st.st_mode) ? 0 : -1;
+ p[i] = sep;
+ if (ret)
+ error(_("expected '%.*s' in submodule path '%s' not to "
+ "be a symbolic link"), i, p, p);
+ }
+ if (!lstat(p, &st) && S_ISLNK(st.st_mode))
+ ret = error(_("expected submodule path '%s' not to be a "
+ "symbolic link"), p);
+ free(p);
+ return ret;
+}
+
+
/*
* Embeds a single submodules git directory into the superprojects git dir,
* non recursively.
@@ -2272,6 +2353,9 @@ static void relocate_single_git_dir_into_superproject(const char *path,
struct strbuf new_gitdir = STRBUF_INIT;
const struct submodule *sub;
+ if (validate_submodule_path(path) < 0)
+ exit(128);
+
if (submodule_uses_worktrees(path))
die(_("relocate_gitdir for submodule '%s' with "
"more than one worktree not supported"), path);
@@ -2313,6 +2397,9 @@ static void absorb_git_dir_into_superproject_recurse(const char *path,
struct child_process cp = CHILD_PROCESS_INIT;
+ if (validate_submodule_path(path) < 0)
+ exit(128);
+
cp.dir = path;
cp.git_cmd = 1;
cp.no_stdin = 1;
@@ -2337,6 +2424,10 @@ void absorb_git_dir_into_superproject(const char *path,
int err_code;
const char *sub_git_dir;
struct strbuf gitdir = STRBUF_INIT;
+
+ if (validate_submodule_path(path) < 0)
+ exit(128);
+
strbuf_addf(&gitdir, "%s/.git", path);
sub_git_dir = resolve_gitdir_gently(gitdir.buf, &err_code);
@@ -2479,6 +2570,9 @@ int submodule_to_gitdir(struct strbuf *buf, const char *submodule)
const char *git_dir;
int ret = 0;
+ if (validate_submodule_path(submodule) < 0)
+ exit(128);
+
strbuf_reset(buf);
strbuf_addstr(buf, submodule);
strbuf_complete(buf, '/');
diff --git a/submodule.h b/submodule.h
index c55a25ca37..b50d29eba4 100644
--- a/submodule.h
+++ b/submodule.h
@@ -148,6 +148,11 @@ void submodule_name_to_gitdir(struct strbuf *buf, struct repository *r,
*/
int validate_submodule_git_dir(char *git_dir, const char *submodule_name);
+/*
+ * Make sure that the given submodule path does not follow symlinks.
+ */
+int validate_submodule_path(const char *path);
+
#define SUBMODULE_MOVE_HEAD_DRY_RUN (1<<0)
#define SUBMODULE_MOVE_HEAD_FORCE (1<<1)
int submodule_move_head(const char *path, const char *super_prefix,
diff --git a/t/.gitattributes b/t/.gitattributes
index b9cea1795d..7664c6e027 100644
--- a/t/.gitattributes
+++ b/t/.gitattributes
@@ -1,5 +1,5 @@
t[0-9][0-9][0-9][0-9]/* -whitespace
-/chainlint/*.expect eol=lf
+/chainlint/*.expect eol=lf -whitespace
/t0110/url-* binary
/t3206/* eol=lf
/t3900/*.txt eol=lf
diff --git a/t/Makefile b/t/Makefile
index 2d95046f26..4c30e7c06f 100644
--- a/t/Makefile
+++ b/t/Makefile
@@ -48,7 +48,8 @@ CHAINLINTTESTS = $(sort $(patsubst chainlint/%.test,%,$(wildcard chainlint/*.tes
CHAINLINT = '$(PERL_PATH_SQ)' chainlint.pl
UNIT_TEST_SOURCES = $(wildcard unit-tests/t-*.c)
UNIT_TEST_PROGRAMS = $(patsubst unit-tests/%.c,unit-tests/bin/%$(X),$(UNIT_TEST_SOURCES))
-UNIT_TESTS = $(sort $(filter-out unit-tests/bin/t-basic%,$(UNIT_TEST_PROGRAMS)))
+UNIT_TESTS = $(sort $(UNIT_TEST_PROGRAMS))
+UNIT_TESTS_NO_DIR = $(notdir $(UNIT_TESTS))
# `test-chainlint` (which is a dependency of `test-lint`, `test` and `prove`)
# checks all tests in all scripts via a single invocation, so tell individual
@@ -67,7 +68,7 @@ failed:
test -z "$$failed" || $(MAKE) $$failed
prove: pre-clean check-chainlint $(TEST_LINT)
- @echo "*** prove ***"; $(CHAINLINTSUPPRESS) $(PROVE) --exec '$(TEST_SHELL_PATH_SQ)' $(GIT_PROVE_OPTS) $(T) :: $(GIT_TEST_OPTS)
+ @echo "*** prove (shell & unit tests) ***"; $(CHAINLINTSUPPRESS) TEST_SHELL_PATH='$(TEST_SHELL_PATH_SQ)' $(PROVE) --exec ./run-test.sh $(GIT_PROVE_OPTS) $(T) $(UNIT_TESTS) :: $(GIT_TEST_OPTS)
$(MAKE) clean-except-prove-cache
$(T):
@@ -76,7 +77,7 @@ $(T):
$(UNIT_TESTS):
@echo "*** $@ ***"; $@
-.PHONY: unit-tests unit-tests-raw unit-tests-prove
+.PHONY: unit-tests unit-tests-raw unit-tests-prove unit-tests-test-tool
unit-tests: $(DEFAULT_UNIT_TEST_TARGET)
unit-tests-raw: $(UNIT_TESTS)
@@ -84,6 +85,13 @@ unit-tests-raw: $(UNIT_TESTS)
unit-tests-prove:
@echo "*** prove - unit tests ***"; $(PROVE) $(GIT_PROVE_OPTS) $(UNIT_TESTS)
+unit-tests-test-tool:
+ @echo "*** test-tool - unit tests **"
+ ( \
+ cd unit-tests/bin && \
+ ../../helper/test-tool$X run-command testsuite $(UNIT_TESTS_NO_DIR)\
+ )
+
pre-clean:
$(RM) -r '$(TEST_RESULTS_DIRECTORY_SQ)'
@@ -100,20 +108,8 @@ clean-chainlint:
check-chainlint:
@mkdir -p '$(CHAINLINTTMP_SQ)' && \
- for i in $(CHAINLINTTESTS); do \
- echo "test_expect_success '$$i' '" && \
- sed -e '/^# LINT: /d' chainlint/$$i.test && \
- echo "'"; \
- done >'$(CHAINLINTTMP_SQ)'/tests && \
- { \
- echo "# chainlint: $(CHAINLINTTMP_SQ)/tests" && \
- for i in $(CHAINLINTTESTS); do \
- echo "# chainlint: $$i" && \
- cat chainlint/$$i.expect; \
- done \
- } >'$(CHAINLINTTMP_SQ)'/expect && \
- $(CHAINLINT) --emit-all '$(CHAINLINTTMP_SQ)'/tests | \
- sed -e 's/^[1-9][0-9]* //' >'$(CHAINLINTTMP_SQ)'/actual && \
+ '$(PERL_PATH_SQ)' chainlint-cat.pl '$(CHAINLINTTMP_SQ)' $(CHAINLINTTESTS) && \
+ { $(CHAINLINT) --emit-all '$(CHAINLINTTMP_SQ)'/tests >'$(CHAINLINTTMP_SQ)'/actual || true; } && \
diff -u '$(CHAINLINTTMP_SQ)'/expect '$(CHAINLINTTMP_SQ)'/actual
test-lint: test-lint-duplicates test-lint-executable test-lint-shell-syntax \
diff --git a/t/README b/t/README
index d9e0e07506..724ee58195 100644
--- a/t/README
+++ b/t/README
@@ -382,33 +382,9 @@ mapping between "TEST_PASSES_SANITIZE_LEAK=true" and those tests that
pass under "SANITIZE=leak". This is especially useful when testing a
series that fixes various memory leaks with "git rebase -x".
-GIT_TEST_SANITIZE_LEAK_LOG=true will log memory leaks to
-"test-results/$TEST_NAME.leak/trace.*" files. The logs include a
-"dedup_token" (see +"ASAN_OPTIONS=help=1 ./git") and other options to
-make logs +machine-readable.
-
-With GIT_TEST_SANITIZE_LEAK_LOG=true we'll look at the leak logs
-before exiting and exit on failure if the logs showed that we had a
-memory leak, even if the test itself would have otherwise passed. This
-allows us to catch e.g. missing &&-chaining. This is especially useful
-when combined with "GIT_TEST_PASSING_SANITIZE_LEAK", see below.
-
GIT_TEST_PASSING_SANITIZE_LEAK=check when combined with "--immediate"
will run to completion faster, and result in the same failing
-tests. The only practical reason to run
-GIT_TEST_PASSING_SANITIZE_LEAK=check without "--immediate" is to
-combine it with "GIT_TEST_SANITIZE_LEAK_LOG=true". If we stop at the
-first failing test case our leak logs won't show subsequent leaks we
-might have run into.
-
-GIT_TEST_PASSING_SANITIZE_LEAK=(true|check) will not catch all memory
-leaks unless combined with GIT_TEST_SANITIZE_LEAK_LOG=true. Some tests
-run "git" (or "test-tool" etc.) without properly checking the exit
-code, or git will invoke itself and fail to ferry the abort() exit
-code to the original caller. When the two modes are combined we'll
-look at the "test-results/$TEST_NAME.leak/trace.*" files at the end of
-the test run to see if had memory leaks which the test itself didn't
-catch.
+tests.
GIT_TEST_PROTOCOL_VERSION=<n>, when set, makes 'protocol.version'
default to n.
@@ -906,6 +882,14 @@ see test-lib-functions.sh for the full list and their options.
'git-write-tree should be able to write an empty tree.' \
'tree=$(git-write-tree)'
+ If <script> is `-` (a single dash), then the script to run is read
+ from stdin. This lets you more easily use single quotes within the
+ script by using a here-doc. For example:
+
+ test_expect_success 'output contains expected string' - <<\EOT
+ grep "this string has 'quotes' in it" output
+ EOT
+
If you supply three parameters the first will be taken to be a
prerequisite; see the test_set_prereq and test_have_prereq
documentation below:
diff --git a/t/chainlint-cat.pl b/t/chainlint-cat.pl
new file mode 100644
index 0000000000..388f6e1e41
--- /dev/null
+++ b/t/chainlint-cat.pl
@@ -0,0 +1,29 @@
+#!/usr/bin/env perl
+
+my $outdir = shift;
+open(my $tests, '>', "$outdir/tests")
+ or die "unable to open $outdir/tests: $!";
+open(my $expect, '>', "$outdir/expect")
+ or die "unable to open $outdir/expect: $!";
+
+print $expect "# chainlint: $outdir/tests\n";
+
+my $offset = 0;
+for my $script (@ARGV) {
+ print $expect "# chainlint: $script\n";
+
+ open(my $expect_in, '<', "chainlint/$script.expect")
+ or die "unable to open chainlint/$script.expect: $!";
+ while (<$expect_in>) {
+ s/^\d+/$& + $offset/e;
+ print $expect $_;
+ }
+
+ open(my $test_in, '<', "chainlint/$script.test")
+ or die "unable to open chainlint/$script.test: $!";
+ while (<$test_in>) {
+ /^# LINT: / and next;
+ print $tests $_;
+ $offset++;
+ }
+}
diff --git a/t/chainlint.pl b/t/chainlint.pl
index 556ee91a15..5361f23b1d 100755
--- a/t/chainlint.pl
+++ b/t/chainlint.pl
@@ -174,6 +174,10 @@ sub swallow_heredocs {
$$b =~ /(?:\G|\n)$indent\Q$$tag[0]\E(?:\n|\z)/gc;
if (pos($$b) > $start) {
my $body = substr($$b, $start, pos($$b) - $start);
+ $self->{parser}->{heredocs}->{$$tag[0]} = {
+ content => substr($body, 0, length($body) - length($&)),
+ start_line => $self->{lineno},
+ };
$self->{lineno} += () = $body =~ /\n/sg;
next;
}
@@ -232,7 +236,8 @@ sub new {
my $self = bless {
buff => [],
stop => [],
- output => []
+ output => [],
+ heredocs => {},
} => $class;
$self->{lexer} = Lexer->new($self, $s);
return $self;
@@ -616,14 +621,21 @@ sub unwrap {
sub check_test {
my $self = shift @_;
- my ($title, $body) = map(unwrap, @_);
+ my $title = unwrap(shift @_);
+ my $body = shift @_;
+ my $lineno = $body->[3];
+ $body = unwrap($body);
+ if ($body eq '-') {
+ my $herebody = shift @_;
+ $body = $herebody->{content};
+ $lineno = $herebody->{start_line};
+ }
$self->{ntests}++;
my $parser = TestParser->new(\$body);
my @tokens = $parser->parse();
my $problems = $parser->{problems};
return unless $emit_all || @$problems;
my $c = main::fd_colors(1);
- my $lineno = $_[1]->[3];
my $start = 0;
my $checked = '';
for (sort {$a->[1]->[2] <=> $b->[1]->[2]} @$problems) {
@@ -649,8 +661,13 @@ sub parse_cmd {
return @tokens unless @tokens && $tokens[0]->[0] =~ /^test_expect_(?:success|failure)$/;
my $n = $#tokens;
$n-- while $n >= 0 && $tokens[$n]->[0] =~ /^(?:[;&\n|]|&&|\|\|)$/;
- $self->check_test($tokens[1], $tokens[2]) if $n == 2; # title body
- $self->check_test($tokens[2], $tokens[3]) if $n > 2; # prereq title body
+ my $herebody;
+ if ($n >= 2 && $tokens[$n-1]->[0] eq '-' && $tokens[$n]->[0] =~ /^<<-?(.+)$/) {
+ $herebody = $self->{heredocs}->{$1};
+ $n--;
+ }
+ $self->check_test($tokens[1], $tokens[2], $herebody) if $n == 2; # title body
+ $self->check_test($tokens[2], $tokens[3], $herebody) if $n > 2; # prereq title body
return @tokens;
}
@@ -716,11 +733,25 @@ sub fd_colors {
sub ncores {
# Windows
- return $ENV{NUMBER_OF_PROCESSORS} if exists($ENV{NUMBER_OF_PROCESSORS});
+ if (exists($ENV{NUMBER_OF_PROCESSORS})) {
+ my $ncpu = $ENV{NUMBER_OF_PROCESSORS};
+ return $ncpu > 0 ? $ncpu : 1;
+ }
# Linux / MSYS2 / Cygwin / WSL
- do { local @ARGV='/proc/cpuinfo'; return scalar(grep(/^processor[\s\d]*:/, <>)); } if -r '/proc/cpuinfo';
+ if (open my $fh, '<', '/proc/cpuinfo') {
+ my $cpuinfo = do { local $/; <$fh> };
+ close($fh);
+ if ($cpuinfo =~ /^n?cpus active\s*:\s*(\d+)/m) {
+ return $1 if $1 > 0;
+ }
+ my @matches = ($cpuinfo =~ /^(processor|CPU)[\s\d]*:/mg);
+ return @matches ? scalar(@matches) : 1;
+ }
# macOS & BSD
- return qx/sysctl -n hw.ncpu/ if $^O =~ /(?:^darwin$|bsd)/;
+ if ($^O =~ /(?:^darwin$|bsd)/) {
+ my $ncpu = qx/sysctl -n hw.ncpu/;
+ return $ncpu > 0 ? $ncpu : 1;
+ }
return 1;
}
@@ -748,7 +779,7 @@ sub check_script {
while (my $path = $next_script->()) {
$nscripts++;
my $fh;
- unless (open($fh, "<", $path)) {
+ unless (open($fh, "<:unix:crlf", $path)) {
$emit->("?!ERR?! $path: $!\n");
next;
}
@@ -792,8 +823,10 @@ unless (@scripts) {
show_stats($start_time, \@stats) if $show_stats;
exit;
}
+$jobs = @scripts if @scripts < $jobs;
-unless ($Config{useithreads} && eval {
+unless ($jobs > 1 &&
+ $Config{useithreads} && eval {
require threads; threads->import();
require Thread::Queue; Thread::Queue->import();
1;
diff --git a/t/chainlint/arithmetic-expansion.expect b/t/chainlint/arithmetic-expansion.expect
index 46ee1046af..338ecd5861 100644
--- a/t/chainlint/arithmetic-expansion.expect
+++ b/t/chainlint/arithmetic-expansion.expect
@@ -1,9 +1,9 @@
-(
- foo &&
- bar=$((42 + 1)) &&
- baz
-) &&
-(
- bar=$((42 + 1)) ?!AMP?!
- baz
-)
+2 (
+3 foo &&
+4 bar=$((42 + 1)) &&
+5 baz
+6 ) &&
+7 (
+8 bar=$((42 + 1)) ?!AMP?!
+9 baz
+10 )
diff --git a/t/chainlint/arithmetic-expansion.test b/t/chainlint/arithmetic-expansion.test
index 16206960d8..7b4c5c9a41 100644
--- a/t/chainlint/arithmetic-expansion.test
+++ b/t/chainlint/arithmetic-expansion.test
@@ -1,3 +1,4 @@
+test_expect_success 'arithmetic-expansion' '
(
foo &&
# LINT: closing ")" of $((...)) not misinterpreted as subshell-closing ")"
@@ -9,3 +10,4 @@
bar=$((42 + 1))
baz
)
+'
diff --git a/t/chainlint/bash-array.expect b/t/chainlint/bash-array.expect
index 4c34eaee45..435dc8bdc8 100644
--- a/t/chainlint/bash-array.expect
+++ b/t/chainlint/bash-array.expect
@@ -1,10 +1,10 @@
-(
- foo &&
- bar=(gumbo stumbo wumbo) &&
- baz
-) &&
-(
- foo &&
- bar=${#bar[@]} &&
- baz
-)
+2 (
+3 foo &&
+4 bar=(gumbo stumbo wumbo) &&
+5 baz
+6 ) &&
+7 (
+8 foo &&
+9 bar=${#bar[@]} &&
+10 baz
+11 )
diff --git a/t/chainlint/bash-array.test b/t/chainlint/bash-array.test
index 92bbb777b8..4ca977d299 100644
--- a/t/chainlint/bash-array.test
+++ b/t/chainlint/bash-array.test
@@ -1,3 +1,4 @@
+test_expect_success 'bash-array' '
(
foo &&
# LINT: ")" in Bash array assignment not misinterpreted as subshell-closing ")"
@@ -10,3 +11,4 @@
bar=${#bar[@]} &&
baz
)
+'
diff --git a/t/chainlint/blank-line-before-esac.expect b/t/chainlint/blank-line-before-esac.expect
index 056e03003d..b88ba919eb 100644
--- a/t/chainlint/blank-line-before-esac.expect
+++ b/t/chainlint/blank-line-before-esac.expect
@@ -1,18 +1,18 @@
-test_done () {
- case "$test_failure" in
- 0)
- test_at_end_hook_
-
- exit 0 ;;
-
- *)
- if test $test_external_has_tap -eq 0
- then
- say_color error "# failed $test_failure among $msg"
- say "1..$test_count"
- fi
-
- exit 1 ;;
-
- esac
-}
+2 test_done () {
+3 case "$test_failure" in
+4 0)
+5 test_at_end_hook_
+6
+7 exit 0 ;;
+8
+9 *)
+10 if test $test_external_has_tap -eq 0
+11 then
+12 say_color error "# failed $test_failure among $msg"
+13 say "1..$test_count"
+14 fi
+15
+16 exit 1 ;;
+17
+18 esac
+19 }
diff --git a/t/chainlint/blank-line-before-esac.test b/t/chainlint/blank-line-before-esac.test
index cecccad19f..51f02ea0c5 100644
--- a/t/chainlint/blank-line-before-esac.test
+++ b/t/chainlint/blank-line-before-esac.test
@@ -1,3 +1,4 @@
+test_expect_success 'blank-line-before-esac' '
# LINT: blank line before "esac"
test_done () {
case "$test_failure" in
@@ -17,3 +18,4 @@ test_done () {
esac
}
+'
diff --git a/t/chainlint/blank-line.expect b/t/chainlint/blank-line.expect
index b47827d749..6ae39dd174 100644
--- a/t/chainlint/blank-line.expect
+++ b/t/chainlint/blank-line.expect
@@ -1,8 +1,8 @@
-(
-
- nothing &&
-
- something
-
-
-)
+2 (
+3
+4 nothing &&
+5
+6 something
+7
+8
+9 )
diff --git a/t/chainlint/blank-line.test b/t/chainlint/blank-line.test
index 0fdf15b3e1..6f29a491de 100644
--- a/t/chainlint/blank-line.test
+++ b/t/chainlint/blank-line.test
@@ -1,3 +1,4 @@
+test_expect_success 'blank-line' '
(
nothing &&
@@ -8,3 +9,4 @@
)
+'
diff --git a/t/chainlint/block-comment.expect b/t/chainlint/block-comment.expect
index df2beea888..7926936c18 100644
--- a/t/chainlint/block-comment.expect
+++ b/t/chainlint/block-comment.expect
@@ -1,8 +1,8 @@
-(
- {
- # show a
- echo a &&
- # show b
- echo b
- }
-)
+2 (
+3 {
+4 # show a
+5 echo a &&
+6 # show b
+7 echo b
+8 }
+9 )
diff --git a/t/chainlint/block-comment.test b/t/chainlint/block-comment.test
index df2beea888..934ef4113a 100644
--- a/t/chainlint/block-comment.test
+++ b/t/chainlint/block-comment.test
@@ -1,3 +1,4 @@
+test_expect_success 'block-comment' '
(
{
# show a
@@ -6,3 +7,4 @@
echo b
}
)
+'
diff --git a/t/chainlint/block.expect b/t/chainlint/block.expect
index 1c87326364..b62e3d58c3 100644
--- a/t/chainlint/block.expect
+++ b/t/chainlint/block.expect
@@ -1,23 +1,23 @@
-(
- foo &&
- {
- echo a ?!AMP?!
- echo b
- } &&
- bar &&
- {
- echo c
- } ?!AMP?!
- baz
-) &&
-
-{
- echo a; ?!AMP?! echo b
-} &&
-{ echo a; ?!AMP?! echo b; } &&
-
-{
- echo "${var}9" &&
- echo "done"
-} &&
-finis
+2 (
+3 foo &&
+4 {
+5 echo a ?!AMP?!
+6 echo b
+7 } &&
+8 bar &&
+9 {
+10 echo c
+11 } ?!AMP?!
+12 baz
+13 ) &&
+14
+15 {
+16 echo a; ?!AMP?! echo b
+17 } &&
+18 { echo a; ?!AMP?! echo b; } &&
+19
+20 {
+21 echo "${var}9" &&
+22 echo "done"
+23 } &&
+24 finis
diff --git a/t/chainlint/block.test b/t/chainlint/block.test
index 4ab69a4afc..a1b6b4dd32 100644
--- a/t/chainlint/block.test
+++ b/t/chainlint/block.test
@@ -1,3 +1,4 @@
+test_expect_success 'block' '
(
# LINT: missing "&&" after first "echo"
foo &&
@@ -25,3 +26,4 @@
echo "done"
} &&
finis
+'
diff --git a/t/chainlint/broken-chain.expect b/t/chainlint/broken-chain.expect
index cfb58fb6b9..9a1838736f 100644
--- a/t/chainlint/broken-chain.expect
+++ b/t/chainlint/broken-chain.expect
@@ -1,6 +1,6 @@
-(
- foo &&
- bar ?!AMP?!
- baz &&
- wop
-)
+2 (
+3 foo &&
+4 bar ?!AMP?!
+5 baz &&
+6 wop
+7 )
diff --git a/t/chainlint/broken-chain.test b/t/chainlint/broken-chain.test
index 2a44aa73b7..1966499ef9 100644
--- a/t/chainlint/broken-chain.test
+++ b/t/chainlint/broken-chain.test
@@ -1,3 +1,4 @@
+test_expect_success 'broken-chain' '
(
foo &&
# LINT: missing "&&" from "bar"
@@ -6,3 +7,4 @@
# LINT: final statement before closing ")" legitimately lacks "&&"
wop
)
+'
diff --git a/t/chainlint/case-comment.expect b/t/chainlint/case-comment.expect
index 641c157b98..2442dd5f25 100644
--- a/t/chainlint/case-comment.expect
+++ b/t/chainlint/case-comment.expect
@@ -1,11 +1,11 @@
-(
- case "$x" in
- # found foo
- x) foo ;;
- # found other
- *)
- # treat it as bar
- bar
- ;;
- esac
-)
+2 (
+3 case "$x" in
+4 # found foo
+5 x) foo ;;
+6 # found other
+7 *)
+8 # treat it as bar
+9 bar
+10 ;;
+11 esac
+12 )
diff --git a/t/chainlint/case-comment.test b/t/chainlint/case-comment.test
index 641c157b98..3f31ae9010 100644
--- a/t/chainlint/case-comment.test
+++ b/t/chainlint/case-comment.test
@@ -1,3 +1,4 @@
+test_expect_success 'case-comment' '
(
case "$x" in
# found foo
@@ -9,3 +10,4 @@
;;
esac
)
+'
diff --git a/t/chainlint/case.expect b/t/chainlint/case.expect
index 31f280d8ce..c04c61ff36 100644
--- a/t/chainlint/case.expect
+++ b/t/chainlint/case.expect
@@ -1,19 +1,19 @@
-(
- case "$x" in
- x) foo ;;
- *) bar ;;
- esac &&
- foobar
-) &&
-(
- case "$x" in
- x) foo ;;
- *) bar ;;
- esac ?!AMP?!
- foobar
-) &&
-(
- case "$x" in 1) true;; esac &&
- case "$y" in 2) false;; esac ?!AMP?!
- foobar
-)
+2 (
+3 case "$x" in
+4 x) foo ;;
+5 *) bar ;;
+6 esac &&
+7 foobar
+8 ) &&
+9 (
+10 case "$x" in
+11 x) foo ;;
+12 *) bar ;;
+13 esac ?!AMP?!
+14 foobar
+15 ) &&
+16 (
+17 case "$x" in 1) true;; esac &&
+18 case "$y" in 2) false;; esac ?!AMP?!
+19 foobar
+20 )
diff --git a/t/chainlint/case.test b/t/chainlint/case.test
index 4cb086bf87..bea21fee4f 100644
--- a/t/chainlint/case.test
+++ b/t/chainlint/case.test
@@ -1,3 +1,4 @@
+test_expect_success 'case' '
(
# LINT: "...)" arms in "case" not misinterpreted as subshell-closing ")"
case "$x" in
@@ -21,3 +22,4 @@
case "$y" in 2) false;; esac
foobar
)
+'
diff --git a/t/chainlint/chain-break-background.expect b/t/chainlint/chain-break-background.expect
index 20d0bb5333..d06deadae7 100644
--- a/t/chainlint/chain-break-background.expect
+++ b/t/chainlint/chain-break-background.expect
@@ -1,9 +1,9 @@
-JGIT_DAEMON_PID= &&
-git init --bare empty.git &&
->empty.git/git-daemon-export-ok &&
-mkfifo jgit_daemon_output &&
-{
- jgit daemon --port="$JGIT_DAEMON_PORT" . >jgit_daemon_output &
- JGIT_DAEMON_PID=$!
-} &&
-test_expect_code 2 git ls-remote --exit-code git://localhost:$JGIT_DAEMON_PORT/empty.git
+2 JGIT_DAEMON_PID= &&
+3 git init --bare empty.git &&
+4 >empty.git/git-daemon-export-ok &&
+5 mkfifo jgit_daemon_output &&
+6 {
+7 jgit daemon --port="$JGIT_DAEMON_PORT" . >jgit_daemon_output &
+8 JGIT_DAEMON_PID=$!
+9 } &&
+10 test_expect_code 2 git ls-remote --exit-code git://localhost:$JGIT_DAEMON_PORT/empty.git
diff --git a/t/chainlint/chain-break-background.test b/t/chainlint/chain-break-background.test
index e10f656b05..c68e1b04d5 100644
--- a/t/chainlint/chain-break-background.test
+++ b/t/chainlint/chain-break-background.test
@@ -1,3 +1,4 @@
+test_expect_success 'chain-break-background' '
JGIT_DAEMON_PID= &&
git init --bare empty.git &&
>empty.git/git-daemon-export-ok &&
@@ -8,3 +9,4 @@ mkfifo jgit_daemon_output &&
JGIT_DAEMON_PID=$!
} &&
test_expect_code 2 git ls-remote --exit-code git://localhost:$JGIT_DAEMON_PORT/empty.git
+'
diff --git a/t/chainlint/chain-break-continue.expect b/t/chainlint/chain-break-continue.expect
index 47a3457710..4bb60aae25 100644
--- a/t/chainlint/chain-break-continue.expect
+++ b/t/chainlint/chain-break-continue.expect
@@ -1,12 +1,12 @@
-git ls-tree --name-only -r refs/notes/many_notes |
-while read path
-do
- test "$path" = "foobar/non-note.txt" && continue
- test "$path" = "deadbeef" && continue
- test "$path" = "de/adbeef" && continue
-
- if test $(expr length "$path") -ne $hexsz
- then
- return 1
- fi
-done
+2 git ls-tree --name-only -r refs/notes/many_notes |
+3 while read path
+4 do
+5 test "$path" = "foobar/non-note.txt" && continue
+6 test "$path" = "deadbeef" && continue
+7 test "$path" = "de/adbeef" && continue
+8
+9 if test $(expr length "$path") -ne $hexsz
+10 then
+11 return 1
+12 fi
+13 done
diff --git a/t/chainlint/chain-break-continue.test b/t/chainlint/chain-break-continue.test
index f0af71d8bd..de8119b204 100644
--- a/t/chainlint/chain-break-continue.test
+++ b/t/chainlint/chain-break-continue.test
@@ -1,3 +1,4 @@
+test_expect_success 'chain-break-continue' '
git ls-tree --name-only -r refs/notes/many_notes |
while read path
do
@@ -11,3 +12,4 @@ do
return 1
fi
done
+'
diff --git a/t/chainlint/chain-break-false.expect b/t/chainlint/chain-break-false.expect
index 989766fb85..4f815f8e14 100644
--- a/t/chainlint/chain-break-false.expect
+++ b/t/chainlint/chain-break-false.expect
@@ -1,9 +1,9 @@
-if condition not satisified
-then
- echo it did not work...
- echo failed!
- false
-else
- echo it went okay ?!AMP?!
- congratulate user
-fi
+2 if condition not satisified
+3 then
+4 echo it did not work...
+5 echo failed!
+6 false
+7 else
+8 echo it went okay ?!AMP?!
+9 congratulate user
+10 fi
diff --git a/t/chainlint/chain-break-false.test b/t/chainlint/chain-break-false.test
index a5aaff8c8a..f78ad911fc 100644
--- a/t/chainlint/chain-break-false.test
+++ b/t/chainlint/chain-break-false.test
@@ -1,3 +1,4 @@
+test_expect_success 'chain-break-false' '
# LINT: broken &&-chain okay if explicit "false" signals failure
if condition not satisified
then
@@ -8,3 +9,4 @@ else
echo it went okay
congratulate user
fi
+'
diff --git a/t/chainlint/chain-break-return-exit.expect b/t/chainlint/chain-break-return-exit.expect
index 4cd18e2edf..ba0ec51aa0 100644
--- a/t/chainlint/chain-break-return-exit.expect
+++ b/t/chainlint/chain-break-return-exit.expect
@@ -1,19 +1,19 @@
-case "$(git ls-files)" in
-one) echo pass one ;;
-*) echo bad one; return 1 ;;
-esac &&
-(
- case "$(git ls-files)" in
- two) echo pass two ;;
- *) echo bad two; exit 1 ;;
- esac
-) &&
-case "$(git ls-files)" in
-dir/two"$LF"one) echo pass both ;;
-*) echo bad; return 1 ;;
-esac &&
-
-for i in 1 2 3 4 ; do
- git checkout main -b $i || return $?
- test_commit $i $i $i tag$i || return $?
-done
+2 case "$(git ls-files)" in
+3 one) echo pass one ;;
+4 *) echo bad one; return 1 ;;
+5 esac &&
+6 (
+7 case "$(git ls-files)" in
+8 two) echo pass two ;;
+9 *) echo bad two; exit 1 ;;
+10 esac
+11 ) &&
+12 case "$(git ls-files)" in
+13 dir/two"$LF"one) echo pass both ;;
+14 *) echo bad; return 1 ;;
+15 esac &&
+16
+17 for i in 1 2 3 4 ; do
+18 git checkout main -b $i || return $?
+19 test_commit $i $i $i tag$i || return $?
+20 done
diff --git a/t/chainlint/chain-break-return-exit.test b/t/chainlint/chain-break-return-exit.test
index 46542edf88..b6f519bb4d 100644
--- a/t/chainlint/chain-break-return-exit.test
+++ b/t/chainlint/chain-break-return-exit.test
@@ -1,3 +1,4 @@
+test_expect_success 'chain-break-return-exit' '
case "$(git ls-files)" in
one) echo pass one ;;
# LINT: broken &&-chain okay if explicit "return 1" signals failuire
@@ -21,3 +22,4 @@ for i in 1 2 3 4 ; do
git checkout main -b $i || return $?
test_commit $i $i $i tag$i || return $?
done
+'
diff --git a/t/chainlint/chain-break-status.expect b/t/chainlint/chain-break-status.expect
index e6b3b2193e..23c0caa7d8 100644
--- a/t/chainlint/chain-break-status.expect
+++ b/t/chainlint/chain-break-status.expect
@@ -1,9 +1,9 @@
-OUT=$( ((large_git; echo $? 1>&3) | :) 3>&1 ) &&
-test_match_signal 13 "$OUT" &&
-
-{ test-tool sigchain >actual; ret=$?; } &&
-{
- test_match_signal 15 "$ret" ||
- test "$ret" = 3
-} &&
-test_cmp expect actual
+2 OUT=$( ((large_git; echo $? 1>&3) | :) 3>&1 ) &&
+3 test_match_signal 13 "$OUT" &&
+4
+5 { test-tool sigchain >actual; ret=$?; } &&
+6 {
+7 test_match_signal 15 "$ret" ||
+8 test "$ret" = 3
+9 } &&
+10 test_cmp expect actual
diff --git a/t/chainlint/chain-break-status.test b/t/chainlint/chain-break-status.test
index a6602a7b99..d9fee190d9 100644
--- a/t/chainlint/chain-break-status.test
+++ b/t/chainlint/chain-break-status.test
@@ -1,3 +1,4 @@
+test_expect_success 'chain-break-status' '
# LINT: broken &&-chain okay if next command handles "$?" explicitly
OUT=$( ((large_git; echo $? 1>&3) | :) 3>&1 ) &&
test_match_signal 13 "$OUT" &&
@@ -9,3 +10,4 @@ test_match_signal 13 "$OUT" &&
test "$ret" = 3
} &&
test_cmp expect actual
+'
diff --git a/t/chainlint/chained-block.expect b/t/chainlint/chained-block.expect
index 574cdceb07..a546b714a6 100644
--- a/t/chainlint/chained-block.expect
+++ b/t/chainlint/chained-block.expect
@@ -1,9 +1,9 @@
-echo nobody home && {
- test the doohicky ?!AMP?!
- right now
-} &&
-
-GIT_EXTERNAL_DIFF=echo git diff | {
- read path oldfile oldhex oldmode newfile newhex newmode &&
- test "z$oh" = "z$oldhex"
-}
+2 echo nobody home && {
+3 test the doohicky ?!AMP?!
+4 right now
+5 } &&
+6
+7 GIT_EXTERNAL_DIFF=echo git diff | {
+8 read path oldfile oldhex oldmode newfile newhex newmode &&
+9 test "z$oh" = "z$oldhex"
+10 }
diff --git a/t/chainlint/chained-block.test b/t/chainlint/chained-block.test
index 86f81ece63..71ef1d0b7f 100644
--- a/t/chainlint/chained-block.test
+++ b/t/chainlint/chained-block.test
@@ -1,3 +1,4 @@
+test_expect_success 'chained-block' '
# LINT: start of block chained to preceding command
echo nobody home && {
test the doohicky
@@ -9,3 +10,4 @@ GIT_EXTERNAL_DIFF=echo git diff | {
read path oldfile oldhex oldmode newfile newhex newmode &&
test "z$oh" = "z$oldhex"
}
+'
diff --git a/t/chainlint/chained-subshell.expect b/t/chainlint/chained-subshell.expect
index 83810ea7ec..f78b268291 100644
--- a/t/chainlint/chained-subshell.expect
+++ b/t/chainlint/chained-subshell.expect
@@ -1,10 +1,10 @@
-mkdir sub && (
- cd sub &&
- foo the bar ?!AMP?!
- nuff said
-) &&
-
-cut "-d " -f actual | (read s1 s2 s3 &&
-test -f $s1 ?!AMP?!
-test $(cat $s2) = tree2path1 &&
-test $(cat $s3) = tree3path1)
+2 mkdir sub && (
+3 cd sub &&
+4 foo the bar ?!AMP?!
+5 nuff said
+6 ) &&
+7
+8 cut "-d " -f actual | (read s1 s2 s3 &&
+9 test -f $s1 ?!AMP?!
+10 test $(cat $s2) = tree2path1 &&
+11 test $(cat $s3) = tree3path1)
diff --git a/t/chainlint/chained-subshell.test b/t/chainlint/chained-subshell.test
index 4ff6ddd8cb..1f11f65398 100644
--- a/t/chainlint/chained-subshell.test
+++ b/t/chainlint/chained-subshell.test
@@ -1,3 +1,4 @@
+test_expect_success 'chained-subshell' '
# LINT: start of subshell chained to preceding command
mkdir sub && (
cd sub &&
@@ -11,3 +12,4 @@ test -f $s1
test $(cat $s2) = tree2path1 &&
# LINT: closing subshell ")" correctly detected on same line as "$(...)"
test $(cat $s3) = tree3path1)
+'
diff --git a/t/chainlint/close-nested-and-parent-together.expect b/t/chainlint/close-nested-and-parent-together.expect
index 72d482f76d..4167e54a59 100644
--- a/t/chainlint/close-nested-and-parent-together.expect
+++ b/t/chainlint/close-nested-and-parent-together.expect
@@ -1,3 +1,3 @@
-(cd foo &&
- (bar &&
- baz))
+2 (cd foo &&
+3 (bar &&
+4 baz))
diff --git a/t/chainlint/close-nested-and-parent-together.test b/t/chainlint/close-nested-and-parent-together.test
index 72d482f76d..56b28b186b 100644
--- a/t/chainlint/close-nested-and-parent-together.test
+++ b/t/chainlint/close-nested-and-parent-together.test
@@ -1,3 +1,5 @@
+test_expect_success 'close-nested-and-parent-together' '
(cd foo &&
(bar &&
baz))
+'
diff --git a/t/chainlint/close-subshell.expect b/t/chainlint/close-subshell.expect
index 2192a2870a..a272cfe72e 100644
--- a/t/chainlint/close-subshell.expect
+++ b/t/chainlint/close-subshell.expect
@@ -1,26 +1,26 @@
-(
- foo
-) &&
-(
- bar
-) >out &&
-(
- baz
-) 2>err &&
-(
- boo
-) <input &&
-(
- bip
-) | wuzzle &&
-(
- bop
-) | fazz \
- fozz &&
-(
- bup
-) |
-fuzzle &&
-(
- yop
-)
+2 (
+3 foo
+4 ) &&
+5 (
+6 bar
+7 ) >out &&
+8 (
+9 baz
+10 ) 2>err &&
+11 (
+12 boo
+13 ) <input &&
+14 (
+15 bip
+16 ) | wuzzle &&
+17 (
+18 bop
+19 ) | fazz \
+20 fozz &&
+21 (
+22 bup
+23 ) |
+24 fuzzle &&
+25 (
+26 yop
+27 )
diff --git a/t/chainlint/close-subshell.test b/t/chainlint/close-subshell.test
index 508ca447fd..b99f80569d 100644
--- a/t/chainlint/close-subshell.test
+++ b/t/chainlint/close-subshell.test
@@ -1,3 +1,4 @@
+test_expect_success 'close-subshell' '
# LINT: closing ")" with various decorations ("&&", ">", "|", etc.)
(
foo
@@ -25,3 +26,4 @@ fuzzle &&
(
yop
)
+'
diff --git a/t/chainlint/command-substitution-subsubshell.expect b/t/chainlint/command-substitution-subsubshell.expect
index ec42f2c30c..f2a9312dc8 100644
--- a/t/chainlint/command-substitution-subsubshell.expect
+++ b/t/chainlint/command-substitution-subsubshell.expect
@@ -1,2 +1,2 @@
-OUT=$( ((large_git 1>&3) | :) 3>&1 ) &&
-test_match_signal 13 "$OUT"
+2 OUT=$( ((large_git 1>&3) | :) 3>&1 ) &&
+3 test_match_signal 13 "$OUT"
diff --git a/t/chainlint/command-substitution-subsubshell.test b/t/chainlint/command-substitution-subsubshell.test
index 321de2951c..4ea772d60a 100644
--- a/t/chainlint/command-substitution-subsubshell.test
+++ b/t/chainlint/command-substitution-subsubshell.test
@@ -1,3 +1,5 @@
+test_expect_success 'command-substitution-subsubshell' '
# LINT: subshell nested in subshell nested in command substitution
OUT=$( ((large_git 1>&3) | :) 3>&1 ) &&
test_match_signal 13 "$OUT"
+'
diff --git a/t/chainlint/command-substitution.expect b/t/chainlint/command-substitution.expect
index c72e4df9e7..5e31b36db6 100644
--- a/t/chainlint/command-substitution.expect
+++ b/t/chainlint/command-substitution.expect
@@ -1,9 +1,9 @@
-(
- foo &&
- bar=$(gobble) &&
- baz
-) &&
-(
- bar=$(gobble blocks) ?!AMP?!
- baz
-)
+2 (
+3 foo &&
+4 bar=$(gobble) &&
+5 baz
+6 ) &&
+7 (
+8 bar=$(gobble blocks) ?!AMP?!
+9 baz
+10 )
diff --git a/t/chainlint/command-substitution.test b/t/chainlint/command-substitution.test
index 3bbb002a4c..494d671e80 100644
--- a/t/chainlint/command-substitution.test
+++ b/t/chainlint/command-substitution.test
@@ -1,3 +1,4 @@
+test_expect_success 'command-substitution' '
(
foo &&
# LINT: closing ")" of $(...) not misinterpreted as subshell-closing ")"
@@ -9,3 +10,4 @@
bar=$(gobble blocks)
baz
)
+'
diff --git a/t/chainlint/comment.expect b/t/chainlint/comment.expect
index a68f1f9d7c..584098d6ba 100644
--- a/t/chainlint/comment.expect
+++ b/t/chainlint/comment.expect
@@ -1,8 +1,8 @@
-(
- # comment 1
- nothing &&
- # comment 2
- something
- # comment 3
- # comment 4
-)
+2 (
+3 # comment 1
+4 nothing &&
+5 # comment 2
+6 something
+7 # comment 3
+8 # comment 4
+9 )
diff --git a/t/chainlint/comment.test b/t/chainlint/comment.test
index 113c0c466f..c488beac0d 100644
--- a/t/chainlint/comment.test
+++ b/t/chainlint/comment.test
@@ -1,3 +1,4 @@
+test_expect_success 'comment' '
(
# LINT: swallow comment lines
# comment 1
@@ -9,3 +10,4 @@
# comment 3
# comment 4
)
+'
diff --git a/t/chainlint/complex-if-in-cuddled-loop.expect b/t/chainlint/complex-if-in-cuddled-loop.expect
index dac2d0fd1d..3a740103db 100644
--- a/t/chainlint/complex-if-in-cuddled-loop.expect
+++ b/t/chainlint/complex-if-in-cuddled-loop.expect
@@ -1,9 +1,9 @@
-(for i in a b c; do
- if test "$(echo $(waffle bat))" = "eleventeen" &&
- test "$x" = "$y"; then
- :
- else
- echo >file
- fi ?!LOOP?!
- done) &&
-test ! -f file
+2 (for i in a b c; do
+3 if test "$(echo $(waffle bat))" = "eleventeen" &&
+4 test "$x" = "$y"; then
+5 :
+6 else
+7 echo >file
+8 fi ?!LOOP?!
+9 done) &&
+10 test ! -f file
diff --git a/t/chainlint/complex-if-in-cuddled-loop.test b/t/chainlint/complex-if-in-cuddled-loop.test
index 5efeda58b2..f98ae4c42d 100644
--- a/t/chainlint/complex-if-in-cuddled-loop.test
+++ b/t/chainlint/complex-if-in-cuddled-loop.test
@@ -1,3 +1,4 @@
+test_expect_success 'complex-if-in-cuddled-loop' '
# LINT: "for" loop cuddled with "(" and ")" and nested "if" with complex
# LINT: multi-line condition; indented with spaces, not tabs
(for i in a b c; do
@@ -9,3 +10,4 @@
fi
done) &&
test ! -f file
+'
diff --git a/t/chainlint/cuddled-if-then-else.expect b/t/chainlint/cuddled-if-then-else.expect
index 1d8ed58c49..72da8794cb 100644
--- a/t/chainlint/cuddled-if-then-else.expect
+++ b/t/chainlint/cuddled-if-then-else.expect
@@ -1,6 +1,6 @@
-(if test -z ""; then
- echo empty
- else
- echo bizzy
- fi) &&
-echo foobar
+2 (if test -z ""; then
+3 echo empty
+4 else
+5 echo bizzy
+6 fi) &&
+7 echo foobar
diff --git a/t/chainlint/cuddled-if-then-else.test b/t/chainlint/cuddled-if-then-else.test
index 7c53f4efe3..b1b42e1aac 100644
--- a/t/chainlint/cuddled-if-then-else.test
+++ b/t/chainlint/cuddled-if-then-else.test
@@ -1,3 +1,4 @@
+test_expect_success 'cuddled-if-then-else' '
# LINT: "if" cuddled with "(" and ")"; indented with spaces, not tabs
(if test -z ""; then
echo empty
@@ -5,3 +6,4 @@
echo bizzy
fi) &&
echo foobar
+'
diff --git a/t/chainlint/cuddled-loop.expect b/t/chainlint/cuddled-loop.expect
index 9cf260708e..c38585c756 100644
--- a/t/chainlint/cuddled-loop.expect
+++ b/t/chainlint/cuddled-loop.expect
@@ -1,4 +1,4 @@
-( while read x
- do foobar bop || exit 1
- done <file ) &&
-outside subshell
+2 ( while read x
+3 do foobar bop || exit 1
+4 done <file ) &&
+5 outside subshell
diff --git a/t/chainlint/cuddled-loop.test b/t/chainlint/cuddled-loop.test
index 3c2a62f751..6fccb6ac22 100644
--- a/t/chainlint/cuddled-loop.test
+++ b/t/chainlint/cuddled-loop.test
@@ -1,3 +1,4 @@
+test_expect_success 'cuddled-loop' '
# LINT: "while" loop cuddled with "(" and ")", with embedded (allowed)
# LINT: "|| exit {n}" to exit loop early, and using redirection "<" to feed
# LINT: loop; indented with spaces, not tabs
@@ -5,3 +6,4 @@
do foobar bop || exit 1
done <file ) &&
outside subshell
+'
diff --git a/t/chainlint/cuddled.expect b/t/chainlint/cuddled.expect
index c3e0be4047..b06d638311 100644
--- a/t/chainlint/cuddled.expect
+++ b/t/chainlint/cuddled.expect
@@ -1,17 +1,17 @@
-(cd foo &&
- bar
-) &&
-
-(cd foo ?!AMP?!
- bar
-) &&
-
-(
- cd foo &&
- bar) &&
-
-(cd foo &&
- bar) &&
-
-(cd foo ?!AMP?!
- bar)
+2 (cd foo &&
+3 bar
+4 ) &&
+5
+6 (cd foo ?!AMP?!
+7 bar
+8 ) &&
+9
+10 (
+11 cd foo &&
+12 bar) &&
+13
+14 (cd foo &&
+15 bar) &&
+16
+17 (cd foo ?!AMP?!
+18 bar)
diff --git a/t/chainlint/cuddled.test b/t/chainlint/cuddled.test
index 257b5b5eed..5a6ef7a4a6 100644
--- a/t/chainlint/cuddled.test
+++ b/t/chainlint/cuddled.test
@@ -1,3 +1,4 @@
+test_expect_success 'cuddled' '
# LINT: first subshell statement cuddled with opening "("
(cd foo &&
bar
@@ -20,3 +21,4 @@
# LINT: same with missing "&&"
(cd foo
bar)
+'
diff --git a/t/chainlint/double-here-doc.expect b/t/chainlint/double-here-doc.expect
index cd584a4357..48c04ecd58 100644
--- a/t/chainlint/double-here-doc.expect
+++ b/t/chainlint/double-here-doc.expect
@@ -1,12 +1,12 @@
-run_sub_test_lib_test_err run-inv-range-start \
- "--run invalid range start" \
- --run="a-5" <<-\EOF &&
-test_expect_success "passing test #1" "true"
-test_done
-EOF
-check_sub_test_lib_test_err run-inv-range-start \
- <<-\EOF_OUT 3<<-EOF_ERR
-> FATAL: Unexpected exit with code 1
-EOF_OUT
-> error: --run: invalid non-numeric in range start: ${SQ}a-5${SQ}
-EOF_ERR
+2 run_sub_test_lib_test_err run-inv-range-start \
+3 "--run invalid range start" \
+4 --run="a-5" <<-\EOF &&
+5 test_expect_success "passing test #1" "true"
+6 test_done
+7 EOF
+8 check_sub_test_lib_test_err run-inv-range-start \
+9 <<-\EOF_OUT 3<<-EOF_ERR
+10 > FATAL: Unexpected exit with code 1
+11 EOF_OUT
+12 > error: --run: invalid non-numeric in range start: ${SQ}a-5${SQ}
+13 EOF_ERR
diff --git a/t/chainlint/double-here-doc.test b/t/chainlint/double-here-doc.test
index cd584a4357..1b69b7a651 100644
--- a/t/chainlint/double-here-doc.test
+++ b/t/chainlint/double-here-doc.test
@@ -1,3 +1,4 @@
+test_expect_success 'double-here-doc' '
run_sub_test_lib_test_err run-inv-range-start \
"--run invalid range start" \
--run="a-5" <<-\EOF &&
@@ -10,3 +11,4 @@ check_sub_test_lib_test_err run-inv-range-start \
EOF_OUT
> error: --run: invalid non-numeric in range start: ${SQ}a-5${SQ}
EOF_ERR
+'
diff --git a/t/chainlint/dqstring-line-splice.expect b/t/chainlint/dqstring-line-splice.expect
index 37eab80738..2ca1c92cd6 100644
--- a/t/chainlint/dqstring-line-splice.expect
+++ b/t/chainlint/dqstring-line-splice.expect
@@ -1,5 +1,5 @@
-
-echo 'fatal: reword option of --fixup is mutually exclusive with' '--patch/--interactive/--all/--include/--only' >expect &&
-test_must_fail git commit --fixup=reword:HEAD~ $1 2>actual &&
-test_cmp expect actual
-
+2
+3 echo 'fatal: reword option of --fixup is mutually exclusive with' '--patch/--interactive/--all/--include/--only' >expect &&
+4 test_must_fail git commit --fixup=reword:HEAD~ $1 2>actual &&
+5 test_cmp expect actual
+6
diff --git a/t/chainlint/dqstring-line-splice.test b/t/chainlint/dqstring-line-splice.test
index b40714439f..f6aa637be8 100644
--- a/t/chainlint/dqstring-line-splice.test
+++ b/t/chainlint/dqstring-line-splice.test
@@ -1,3 +1,4 @@
+test_expect_success 'dqstring-line-splice' '
# LINT: line-splice within DQ-string
'"
echo 'fatal: reword option of --fixup is mutually exclusive with'\
@@ -5,3 +6,4 @@ echo 'fatal: reword option of --fixup is mutually exclusive with'\
test_must_fail git commit --fixup=reword:HEAD~ $1 2>actual &&
test_cmp expect actual
"'
+'
diff --git a/t/chainlint/dqstring-no-interpolate.expect b/t/chainlint/dqstring-no-interpolate.expect
index 087eda15e4..c9f75849c5 100644
--- a/t/chainlint/dqstring-no-interpolate.expect
+++ b/t/chainlint/dqstring-no-interpolate.expect
@@ -1,12 +1,12 @@
-grep "^ ! [rejected][ ]*$BRANCH -> $BRANCH (non-fast-forward)$" out &&
-
-grep "^\.git$" output.txt &&
-
-
-(
- cd client$version &&
- GIT_TEST_PROTOCOL_VERSION=$version git fetch-pack --no-progress .. $(cat ../input)
-) >output &&
- cut -d ' ' -f 2 <output | sort >actual &&
- test_cmp expect actual
-
+2 grep "^ ! [rejected][ ]*$BRANCH -> $BRANCH (non-fast-forward)$" out &&
+3
+4 grep "^\.git$" output.txt &&
+5
+6
+7 (
+8 cd client$version &&
+9 GIT_TEST_PROTOCOL_VERSION=$version git fetch-pack --no-progress .. $(cat ../input)
+10 ) >output &&
+11 cut -d ' ' -f 2 <output | sort >actual &&
+12 test_cmp expect actual
+13
diff --git a/t/chainlint/dqstring-no-interpolate.test b/t/chainlint/dqstring-no-interpolate.test
index d2f4219cbb..7ae079b558 100644
--- a/t/chainlint/dqstring-no-interpolate.test
+++ b/t/chainlint/dqstring-no-interpolate.test
@@ -1,3 +1,4 @@
+test_expect_success 'dqstring-no-interpolate' '
# LINT: regex dollar-sign eol anchor in double-quoted string not special
grep "^ ! \[rejected\][ ]*$BRANCH -> $BRANCH (non-fast-forward)$" out &&
@@ -13,3 +14,4 @@ grep "^\\.git\$" output.txt &&
cut -d ' ' -f 2 <output | sort >actual &&
test_cmp expect actual
"'
+'
diff --git a/t/chainlint/empty-here-doc.expect b/t/chainlint/empty-here-doc.expect
index 8507721192..54b33f823a 100644
--- a/t/chainlint/empty-here-doc.expect
+++ b/t/chainlint/empty-here-doc.expect
@@ -1,4 +1,4 @@
-git ls-tree $tree path >current &&
-cat >expected <<\EOF &&
-EOF
-test_output
+2 git ls-tree $tree path >current &&
+3 cat >expected <<\EOF &&
+4 EOF
+5 test_output
diff --git a/t/chainlint/empty-here-doc.test b/t/chainlint/empty-here-doc.test
index 24fc165de3..8b7ab6eb5f 100644
--- a/t/chainlint/empty-here-doc.test
+++ b/t/chainlint/empty-here-doc.test
@@ -1,5 +1,7 @@
+test_expect_success 'empty-here-doc' '
git ls-tree $tree path >current &&
# LINT: empty here-doc
cat >expected <<\EOF &&
EOF
test_output
+'
diff --git a/t/chainlint/exclamation.expect b/t/chainlint/exclamation.expect
index 765a35bb4c..078744b61b 100644
--- a/t/chainlint/exclamation.expect
+++ b/t/chainlint/exclamation.expect
@@ -1,4 +1,4 @@
-if ! condition; then echo nope; else yep; fi &&
-test_prerequisite !MINGW &&
-mail uucp!address &&
-echo !whatever!
+2 if ! condition; then echo nope; else yep; fi &&
+3 test_prerequisite !MINGW &&
+4 mail uucp!address &&
+5 echo !whatever!
diff --git a/t/chainlint/exclamation.test b/t/chainlint/exclamation.test
index 323595b5bd..796de21b7c 100644
--- a/t/chainlint/exclamation.test
+++ b/t/chainlint/exclamation.test
@@ -1,3 +1,4 @@
+test_expect_success 'exclamation' '
# LINT: "! word" is two tokens
if ! condition; then echo nope; else yep; fi &&
# LINT: "!word" is single token, not two tokens "!" and "word"
@@ -6,3 +7,4 @@ test_prerequisite !MINGW &&
mail uucp!address &&
# LINT: "!word!" is single token, not three tokens "!", "word", and "!"
echo !whatever!
+'
diff --git a/t/chainlint/exit-loop.expect b/t/chainlint/exit-loop.expect
index f76aa60466..407278094c 100644
--- a/t/chainlint/exit-loop.expect
+++ b/t/chainlint/exit-loop.expect
@@ -1,24 +1,24 @@
-(
- for i in a b c
- do
- foo || exit 1
- bar &&
- baz
- done
-) &&
-(
- while true
- do
- foo || exit 1
- bar &&
- baz
- done
-) &&
-(
- i=0 &&
- while test $i -lt 10
- do
- echo $i || exit
- i=$(($i + 1))
- done
-)
+2 (
+3 for i in a b c
+4 do
+5 foo || exit 1
+6 bar &&
+7 baz
+8 done
+9 ) &&
+10 (
+11 while true
+12 do
+13 foo || exit 1
+14 bar &&
+15 baz
+16 done
+17 ) &&
+18 (
+19 i=0 &&
+20 while test $i -lt 10
+21 do
+22 echo $i || exit
+23 i=$(($i + 1))
+24 done
+25 )
diff --git a/t/chainlint/exit-loop.test b/t/chainlint/exit-loop.test
index 2f038207e1..7e8b68b465 100644
--- a/t/chainlint/exit-loop.test
+++ b/t/chainlint/exit-loop.test
@@ -1,3 +1,4 @@
+test_expect_success 'exit-loop' '
(
for i in a b c
do
@@ -25,3 +26,4 @@
i=$(($i + 1))
done
)
+'
diff --git a/t/chainlint/exit-subshell.expect b/t/chainlint/exit-subshell.expect
index da80339f78..793db12453 100644
--- a/t/chainlint/exit-subshell.expect
+++ b/t/chainlint/exit-subshell.expect
@@ -1,5 +1,5 @@
-(
- foo || exit 1
- bar &&
- baz
-)
+2 (
+3 foo || exit 1
+4 bar &&
+5 baz
+6 )
diff --git a/t/chainlint/exit-subshell.test b/t/chainlint/exit-subshell.test
index 4e6ab69b88..05dff55cd7 100644
--- a/t/chainlint/exit-subshell.test
+++ b/t/chainlint/exit-subshell.test
@@ -1,6 +1,8 @@
+test_expect_success 'exit-subshell' '
(
# LINT: "|| exit {n}" valid subshell escape without hurting &&-chain
foo || exit 1
bar &&
baz
)
+'
diff --git a/t/chainlint/for-loop-abbreviated.expect b/t/chainlint/for-loop-abbreviated.expect
index 02c0d15cca..5574831976 100644
--- a/t/chainlint/for-loop-abbreviated.expect
+++ b/t/chainlint/for-loop-abbreviated.expect
@@ -1,5 +1,5 @@
-for it
-do
- path=$(expr "$it" : ([^:]*)) &&
- git update-index --add "$path" || exit
-done
+2 for it
+3 do
+4 path=$(expr "$it" : ([^:]*)) &&
+5 git update-index --add "$path" || exit
+6 done
diff --git a/t/chainlint/for-loop-abbreviated.test b/t/chainlint/for-loop-abbreviated.test
index 1084eccb89..1dd14f2a44 100644
--- a/t/chainlint/for-loop-abbreviated.test
+++ b/t/chainlint/for-loop-abbreviated.test
@@ -1,6 +1,8 @@
+test_expect_success 'for-loop-abbreviated' '
# LINT: for-loop lacking optional "in [word...]" before "do"
for it
do
path=$(expr "$it" : '\([^:]*\)') &&
git update-index --add "$path" || exit
done
+'
diff --git a/t/chainlint/for-loop.expect b/t/chainlint/for-loop.expect
index d2237f1e38..908aeedf96 100644
--- a/t/chainlint/for-loop.expect
+++ b/t/chainlint/for-loop.expect
@@ -1,14 +1,14 @@
-(
- for i in a b c
- do
- echo $i ?!AMP?!
- cat <<-\EOF ?!LOOP?!
- bar
- EOF
- done ?!AMP?!
-
- for i in a b c; do
- echo $i &&
- cat $i ?!LOOP?!
- done
-)
+2 (
+3 for i in a b c
+4 do
+5 echo $i ?!AMP?!
+6 cat <<-\EOF ?!LOOP?!
+7 bar
+8 EOF
+9 done ?!AMP?!
+10
+11 for i in a b c; do
+12 echo $i &&
+13 cat $i ?!LOOP?!
+14 done
+15 )
diff --git a/t/chainlint/for-loop.test b/t/chainlint/for-loop.test
index 6cb3428158..6f2489eb19 100644
--- a/t/chainlint/for-loop.test
+++ b/t/chainlint/for-loop.test
@@ -1,3 +1,4 @@
+test_expect_success 'for-loop' '
(
# LINT: "for", "do", "done" do not need "&&"
for i in a b c
@@ -17,3 +18,4 @@
cat $i
done
)
+'
diff --git a/t/chainlint/function.expect b/t/chainlint/function.expect
index dd7c997a3c..c226246b25 100644
--- a/t/chainlint/function.expect
+++ b/t/chainlint/function.expect
@@ -1,11 +1,11 @@
-sha1_file() {
- echo "$*" | sed "s#..#.git/objects/&/#"
-} &&
-
-remove_object() {
- file=$(sha1_file "$*") &&
- test -e "$file" ?!AMP?!
- rm -f "$file"
-} ?!AMP?!
-
-sha1_file arg && remove_object arg
+2 sha1_file() {
+3 echo "$*" | sed "s#..#.git/objects/&/#"
+4 } &&
+5
+6 remove_object() {
+7 file=$(sha1_file "$*") &&
+8 test -e "$file" ?!AMP?!
+9 rm -f "$file"
+10 } ?!AMP?!
+11
+12 sha1_file arg && remove_object arg
diff --git a/t/chainlint/function.test b/t/chainlint/function.test
index 5ee59562c9..763fcf3f87 100644
--- a/t/chainlint/function.test
+++ b/t/chainlint/function.test
@@ -1,3 +1,4 @@
+test_expect_success 'function' '
# LINT: "()" in function definition not mistaken for subshell
sha1_file() {
echo "$*" | sed "s#..#.git/objects/&/#"
@@ -11,3 +12,4 @@ remove_object() {
}
sha1_file arg && remove_object arg
+'
diff --git a/t/chainlint/here-doc-body-indent.expect b/t/chainlint/here-doc-body-indent.expect
new file mode 100644
index 0000000000..4323acc93d
--- /dev/null
+++ b/t/chainlint/here-doc-body-indent.expect
@@ -0,0 +1,2 @@
+2 echo "we should find this" ?!AMP?!
+3 echo "even though our heredoc has its indent stripped"
diff --git a/t/chainlint/here-doc-body-indent.test b/t/chainlint/here-doc-body-indent.test
new file mode 100644
index 0000000000..39ff970ef3
--- /dev/null
+++ b/t/chainlint/here-doc-body-indent.test
@@ -0,0 +1,4 @@
+test_expect_success 'here-doc-body-indent' - <<-\EOT
+ echo "we should find this"
+ echo "even though our heredoc has its indent stripped"
+EOT
diff --git a/t/chainlint/here-doc-body-pathological.expect b/t/chainlint/here-doc-body-pathological.expect
new file mode 100644
index 0000000000..a93a1fa3aa
--- /dev/null
+++ b/t/chainlint/here-doc-body-pathological.expect
@@ -0,0 +1,7 @@
+2 echo "outer here-doc does not allow indented end-tag" ?!AMP?!
+3 cat >file <<-\EOF &&
+4 but this inner here-doc
+5 does allow indented EOF
+6 EOF
+7 echo "missing chain after" ?!AMP?!
+8 echo "but this line is OK because it's the end"
diff --git a/t/chainlint/here-doc-body-pathological.test b/t/chainlint/here-doc-body-pathological.test
new file mode 100644
index 0000000000..7d2daa44f9
--- /dev/null
+++ b/t/chainlint/here-doc-body-pathological.test
@@ -0,0 +1,9 @@
+test_expect_success 'here-doc-body-pathological' - <<\EOF
+ echo "outer here-doc does not allow indented end-tag"
+ cat >file <<-\EOF &&
+ but this inner here-doc
+ does allow indented EOF
+ EOF
+ echo "missing chain after"
+ echo "but this line is OK because it's the end"
+EOF
diff --git a/t/chainlint/here-doc-body.expect b/t/chainlint/here-doc-body.expect
new file mode 100644
index 0000000000..ddf1c412af
--- /dev/null
+++ b/t/chainlint/here-doc-body.expect
@@ -0,0 +1,7 @@
+2 echo "missing chain before" ?!AMP?!
+3 cat >file <<-\EOF &&
+4 inside inner here-doc
+5 these are not shell commands
+6 EOF
+7 echo "missing chain after" ?!AMP?!
+8 echo "but this line is OK because it's the end"
diff --git a/t/chainlint/here-doc-body.test b/t/chainlint/here-doc-body.test
new file mode 100644
index 0000000000..989ac2f4e1
--- /dev/null
+++ b/t/chainlint/here-doc-body.test
@@ -0,0 +1,9 @@
+test_expect_success 'here-doc-body' - <<\EOT
+ echo "missing chain before"
+ cat >file <<-\EOF &&
+ inside inner here-doc
+ these are not shell commands
+ EOF
+ echo "missing chain after"
+ echo "but this line is OK because it's the end"
+EOT
diff --git a/t/chainlint/here-doc-close-subshell.expect b/t/chainlint/here-doc-close-subshell.expect
index 7d9c2b5607..965813f463 100644
--- a/t/chainlint/here-doc-close-subshell.expect
+++ b/t/chainlint/here-doc-close-subshell.expect
@@ -1,4 +1,4 @@
-(
- cat <<-\INPUT)
- fizz
- INPUT
+2 (
+3 cat <<-\INPUT)
+4 fizz
+5 INPUT
diff --git a/t/chainlint/here-doc-close-subshell.test b/t/chainlint/here-doc-close-subshell.test
index b857ff5467..2458f3323b 100644
--- a/t/chainlint/here-doc-close-subshell.test
+++ b/t/chainlint/here-doc-close-subshell.test
@@ -1,5 +1,7 @@
+test_expect_success 'here-doc-close-subshell' '
(
# LINT: line contains here-doc and closes nested subshell
cat <<-\INPUT)
fizz
INPUT
+'
diff --git a/t/chainlint/here-doc-double.expect b/t/chainlint/here-doc-double.expect
new file mode 100644
index 0000000000..20dba4b452
--- /dev/null
+++ b/t/chainlint/here-doc-double.expect
@@ -0,0 +1,2 @@
+8 echo "actual test commands" ?!AMP?!
+9 echo "that should be checked"
diff --git a/t/chainlint/here-doc-double.test b/t/chainlint/here-doc-double.test
new file mode 100644
index 0000000000..777389f0d9
--- /dev/null
+++ b/t/chainlint/here-doc-double.test
@@ -0,0 +1,10 @@
+# This is obviously a ridiculous thing to do, but we should be able
+# to handle two here-docs on the same line, and attribute them
+# correctly.
+test_expect_success "$(cat <<END_OF_PREREQS)" 'here-doc-double' - <<\EOT
+SOME
+PREREQS
+END_OF_PREREQS
+ echo "actual test commands"
+ echo "that should be checked"
+EOT
diff --git a/t/chainlint/here-doc-indent-operator.expect b/t/chainlint/here-doc-indent-operator.expect
index f92a7ce999..277a11202d 100644
--- a/t/chainlint/here-doc-indent-operator.expect
+++ b/t/chainlint/here-doc-indent-operator.expect
@@ -1,11 +1,11 @@
-cat >expect <<- EOF &&
-header: 43475048 1 $(test_oid oid_version) $NUM_CHUNKS 0
-num_commits: $1
-chunks: oid_fanout oid_lookup commit_metadata generation_data bloom_indexes bloom_data
-EOF
-
-cat >expect << -EOF ?!AMP?!
-this is not indented
--EOF
-
-cleanup
+2 cat >expect <<- EOF &&
+3 header: 43475048 1 $(test_oid oid_version) $NUM_CHUNKS 0
+4 num_commits: $1
+5 chunks: oid_fanout oid_lookup commit_metadata generation_data bloom_indexes bloom_data
+6 EOF
+7
+8 cat >expect << -EOF ?!AMP?!
+9 this is not indented
+10 -EOF
+11
+12 cleanup
diff --git a/t/chainlint/here-doc-indent-operator.test b/t/chainlint/here-doc-indent-operator.test
index c8a6f18eb4..a2656f47c1 100644
--- a/t/chainlint/here-doc-indent-operator.test
+++ b/t/chainlint/here-doc-indent-operator.test
@@ -1,3 +1,4 @@
+test_expect_success 'here-doc-indent-operator' '
# LINT: whitespace between operator "<<-" and tag legal
cat >expect <<- EOF &&
header: 43475048 1 $(test_oid oid_version) $NUM_CHUNKS 0
@@ -11,3 +12,4 @@ this is not indented
-EOF
cleanup
+'
diff --git a/t/chainlint/here-doc-multi-line-command-subst.expect b/t/chainlint/here-doc-multi-line-command-subst.expect
index b7364c82c8..41b55f6437 100644
--- a/t/chainlint/here-doc-multi-line-command-subst.expect
+++ b/t/chainlint/here-doc-multi-line-command-subst.expect
@@ -1,8 +1,8 @@
-(
- x=$(bobble <<-\END &&
- fossil
- vegetable
- END
- wiffle) ?!AMP?!
- echo $x
-)
+2 (
+3 x=$(bobble <<-\END &&
+4 fossil
+5 vegetable
+6 END
+7 wiffle) ?!AMP?!
+8 echo $x
+9 )
diff --git a/t/chainlint/here-doc-multi-line-command-subst.test b/t/chainlint/here-doc-multi-line-command-subst.test
index 899bc5de8b..8710a8c483 100644
--- a/t/chainlint/here-doc-multi-line-command-subst.test
+++ b/t/chainlint/here-doc-multi-line-command-subst.test
@@ -1,3 +1,4 @@
+test_expect_success 'here-doc-multi-line-command-subst' '
(
# LINT: line contains here-doc and opens multi-line $(...)
x=$(bobble <<-\END &&
@@ -7,3 +8,4 @@
wiffle)
echo $x
)
+'
diff --git a/t/chainlint/here-doc-multi-line-string.expect b/t/chainlint/here-doc-multi-line-string.expect
index 6c13bdcbfb..c71828589e 100644
--- a/t/chainlint/here-doc-multi-line-string.expect
+++ b/t/chainlint/here-doc-multi-line-string.expect
@@ -1,7 +1,7 @@
-(
- cat <<-\TXT && echo "multi-line
- string" ?!AMP?!
- fizzle
- TXT
- bap
-)
+2 (
+3 cat <<-\TXT && echo "multi-line
+4 string" ?!AMP?!
+5 fizzle
+6 TXT
+7 bap
+8 )
diff --git a/t/chainlint/here-doc-multi-line-string.test b/t/chainlint/here-doc-multi-line-string.test
index a53edbcc8d..2f496002fd 100644
--- a/t/chainlint/here-doc-multi-line-string.test
+++ b/t/chainlint/here-doc-multi-line-string.test
@@ -1,3 +1,4 @@
+test_expect_success 'here-doc-multi-line-string' '
(
# LINT: line contains here-doc and opens multi-line string
cat <<-\TXT && echo "multi-line
@@ -6,3 +7,4 @@
TXT
bap
)
+'
diff --git a/t/chainlint/here-doc.expect b/t/chainlint/here-doc.expect
index 91b961242a..2c382dd8eb 100644
--- a/t/chainlint/here-doc.expect
+++ b/t/chainlint/here-doc.expect
@@ -1,25 +1,25 @@
-boodle wobba \
- gorgo snoot \
- wafta snurb <<EOF &&
-quoth the raven,
-nevermore...
-EOF
-
-cat <<-Arbitrary_Tag_42 >foo &&
-snoz
-boz
-woz
-Arbitrary_Tag_42
-
-cat <<"zump" >boo &&
-snoz
-boz
-woz
-zump
-
-horticulture <<\EOF
-gomez
-morticia
-wednesday
-pugsly
-EOF
+2 boodle wobba \
+3 gorgo snoot \
+4 wafta snurb <<EOF &&
+5 quoth the raven,
+6 nevermore...
+7 EOF
+8
+9 cat <<-Arbitrary_Tag_42 >foo &&
+10 snoz
+11 boz
+12 woz
+13 Arbitrary_Tag_42
+14
+15 cat <<"zump" >boo &&
+16 snoz
+17 boz
+18 woz
+19 zump
+20
+21 horticulture <<\EOF
+22 gomez
+23 morticia
+24 wednesday
+25 pugsly
+26 EOF
diff --git a/t/chainlint/here-doc.test b/t/chainlint/here-doc.test
index 3f5f92cad3..c91b695319 100644
--- a/t/chainlint/here-doc.test
+++ b/t/chainlint/here-doc.test
@@ -1,3 +1,4 @@
+test_expect_success 'here-doc' '
# LINT: stitch together incomplete \-ending lines
# LINT: swallow here-doc to avoid false positives in content
boodle wobba \
@@ -28,3 +29,4 @@ morticia
wednesday
pugsly
EOF
+'
diff --git a/t/chainlint/if-condition-split.expect b/t/chainlint/if-condition-split.expect
index ee745ef8d7..9daf3d294a 100644
--- a/t/chainlint/if-condition-split.expect
+++ b/t/chainlint/if-condition-split.expect
@@ -1,7 +1,7 @@
-if bob &&
- marcia ||
- kevin
-then
- echo "nomads" ?!AMP?!
- echo "for sure"
-fi
+2 if bob &&
+3 marcia ||
+4 kevin
+5 then
+6 echo "nomads" ?!AMP?!
+7 echo "for sure"
+8 fi
diff --git a/t/chainlint/if-condition-split.test b/t/chainlint/if-condition-split.test
index 240daa9fd5..9a3b3ed04a 100644
--- a/t/chainlint/if-condition-split.test
+++ b/t/chainlint/if-condition-split.test
@@ -1,3 +1,4 @@
+test_expect_success 'if-condition-split' '
# LINT: "if" condition split across multiple lines at "&&" or "||"
if bob &&
marcia ||
@@ -6,3 +7,4 @@ then
echo "nomads"
echo "for sure"
fi
+'
diff --git a/t/chainlint/if-in-loop.expect b/t/chainlint/if-in-loop.expect
index d6514ae749..ff8c60dbdb 100644
--- a/t/chainlint/if-in-loop.expect
+++ b/t/chainlint/if-in-loop.expect
@@ -1,12 +1,12 @@
-(
- for i in a b c
- do
- if false
- then
- echo "err"
- exit 1
- fi ?!AMP?!
- foo
- done ?!AMP?!
- bar
-)
+2 (
+3 for i in a b c
+4 do
+5 if false
+6 then
+7 echo "err"
+8 exit 1
+9 fi ?!AMP?!
+10 foo
+11 done ?!AMP?!
+12 bar
+13 )
diff --git a/t/chainlint/if-in-loop.test b/t/chainlint/if-in-loop.test
index 90c23976fe..5be9d1cfa5 100644
--- a/t/chainlint/if-in-loop.test
+++ b/t/chainlint/if-in-loop.test
@@ -1,3 +1,4 @@
+test_expect_success 'if-in-loop' '
(
for i in a b c
do
@@ -13,3 +14,4 @@
done
bar
)
+'
diff --git a/t/chainlint/if-then-else.expect b/t/chainlint/if-then-else.expect
index cbaaf857d4..965d7e41a2 100644
--- a/t/chainlint/if-then-else.expect
+++ b/t/chainlint/if-then-else.expect
@@ -1,22 +1,22 @@
-(
- if test -n ""
- then
- echo very ?!AMP?!
- echo empty
- elif test -z ""
- then
- echo foo
- else
- echo foo &&
- cat <<-\EOF
- bar
- EOF
- fi ?!AMP?!
- echo poodle
-) &&
-(
- if test -n ""; then
- echo very &&
- echo empty
- fi
-)
+2 (
+3 if test -n ""
+4 then
+5 echo very ?!AMP?!
+6 echo empty
+7 elif test -z ""
+8 then
+9 echo foo
+10 else
+11 echo foo &&
+12 cat <<-\EOF
+13 bar
+14 EOF
+15 fi ?!AMP?!
+16 echo poodle
+17 ) &&
+18 (
+19 if test -n ""; then
+20 echo very &&
+21 echo empty
+22 fi
+23 )
diff --git a/t/chainlint/if-then-else.test b/t/chainlint/if-then-else.test
index 2055336c2b..6582a7f440 100644
--- a/t/chainlint/if-then-else.test
+++ b/t/chainlint/if-then-else.test
@@ -1,3 +1,4 @@
+test_expect_success 'if-then-else' '
(
# LINT: "if", "then", "elif", "else", "fi" do not need "&&"
if test -n ""
@@ -27,3 +28,4 @@
echo empty
fi
)
+'
diff --git a/t/chainlint/incomplete-line.expect b/t/chainlint/incomplete-line.expect
index 134d3a14f5..b15e00b901 100644
--- a/t/chainlint/incomplete-line.expect
+++ b/t/chainlint/incomplete-line.expect
@@ -1,10 +1,10 @@
-line 1 \
-line 2 \
-line 3 \
-line 4 &&
-(
- line 5 \
- line 6 \
- line 7 \
- line 8
-)
+2 line 1 \
+3 line 2 \
+4 line 3 \
+5 line 4 &&
+6 (
+7 line 5 \
+8 line 6 \
+9 line 7 \
+10 line 8
+11 )
diff --git a/t/chainlint/incomplete-line.test b/t/chainlint/incomplete-line.test
index d856658083..74a93021eb 100644
--- a/t/chainlint/incomplete-line.test
+++ b/t/chainlint/incomplete-line.test
@@ -1,3 +1,4 @@
+test_expect_success 'incomplete-line' '
# LINT: stitch together all incomplete \-ending lines
line 1 \
line 2 \
@@ -10,3 +11,4 @@ line 4 &&
line 7 \
line 8
)
+'
diff --git a/t/chainlint/inline-comment.expect b/t/chainlint/inline-comment.expect
index 6bad218530..0285c0b22c 100644
--- a/t/chainlint/inline-comment.expect
+++ b/t/chainlint/inline-comment.expect
@@ -1,8 +1,8 @@
-(
- foobar && # comment 1
- barfoo ?!AMP?! # wrong position for &&
- flibble "not a # comment"
-) &&
-
-(cd foo &&
- flibble "not a # comment")
+2 (
+3 foobar && # comment 1
+4 barfoo ?!AMP?! # wrong position for &&
+5 flibble "not a # comment"
+6 ) &&
+7
+8 (cd foo &&
+9 flibble "not a # comment")
diff --git a/t/chainlint/inline-comment.test b/t/chainlint/inline-comment.test
index 8f26856e77..4fbbf1058a 100644
--- a/t/chainlint/inline-comment.test
+++ b/t/chainlint/inline-comment.test
@@ -1,3 +1,4 @@
+test_expect_success 'inline-comment' '
(
# LINT: swallow inline comment (leaving command intact)
foobar && # comment 1
@@ -10,3 +11,4 @@
# LINT: "#" in string in cuddled subshell not misinterpreted as comment
(cd foo &&
flibble "not a # comment")
+'
diff --git a/t/chainlint/loop-detect-failure.expect b/t/chainlint/loop-detect-failure.expect
index a66025c39d..40c06f0d53 100644
--- a/t/chainlint/loop-detect-failure.expect
+++ b/t/chainlint/loop-detect-failure.expect
@@ -1,15 +1,15 @@
-git init r1 &&
-for n in 1 2 3 4 5
-do
- echo "This is file: $n" > r1/file.$n &&
- git -C r1 add file.$n &&
- git -C r1 commit -m "$n" || return 1
-done &&
-
-git init r2 &&
-for n in 1000 10000
-do
- printf "%"$n"s" X > r2/large.$n &&
- git -C r2 add large.$n &&
- git -C r2 commit -m "$n" ?!LOOP?!
-done
+2 git init r1 &&
+3 for n in 1 2 3 4 5
+4 do
+5 echo "This is file: $n" > r1/file.$n &&
+6 git -C r1 add file.$n &&
+7 git -C r1 commit -m "$n" || return 1
+8 done &&
+9
+10 git init r2 &&
+11 for n in 1000 10000
+12 do
+13 printf "%"$n"s" X > r2/large.$n &&
+14 git -C r2 add large.$n &&
+15 git -C r2 commit -m "$n" ?!LOOP?!
+16 done
diff --git a/t/chainlint/loop-detect-failure.test b/t/chainlint/loop-detect-failure.test
index b9791cc802..44673aa394 100644
--- a/t/chainlint/loop-detect-failure.test
+++ b/t/chainlint/loop-detect-failure.test
@@ -1,3 +1,4 @@
+test_expect_success 'loop-detect-failure' '
git init r1 &&
# LINT: loop handles failure explicitly with "|| return 1"
for n in 1 2 3 4 5
@@ -15,3 +16,4 @@ do
git -C r2 add large.$n &&
git -C r2 commit -m "$n"
done
+'
diff --git a/t/chainlint/loop-detect-status.expect b/t/chainlint/loop-detect-status.expect
index 7ce3a34806..0f180b08de 100644
--- a/t/chainlint/loop-detect-status.expect
+++ b/t/chainlint/loop-detect-status.expect
@@ -1,18 +1,18 @@
-(while test $i -le $blobcount
- do
- printf "Generating blob $i/$blobcount\r" >&2 &&
- printf "blob\nmark :$i\ndata $blobsize\n" &&
- #test-tool genrandom $i $blobsize &&
- printf "%-${blobsize}s" $i &&
- echo "M 100644 :$i $i" >> commit &&
- i=$(($i+1)) ||
- echo $? > exit-status
- done &&
- echo "commit refs/heads/main" &&
- echo "author A U Thor <author@email.com> 123456789 +0000" &&
- echo "committer C O Mitter <committer@email.com> 123456789 +0000" &&
- echo "data 5" &&
- echo ">2gb" &&
- cat commit) |
-git fast-import --big-file-threshold=2 &&
-test ! -f exit-status
+2 (while test $i -le $blobcount
+3 do
+4 printf "Generating blob $i/$blobcount\r" >&2 &&
+5 printf "blob\nmark :$i\ndata $blobsize\n" &&
+6 #test-tool genrandom $i $blobsize &&
+7 printf "%-${blobsize}s" $i &&
+8 echo "M 100644 :$i $i" >> commit &&
+9 i=$(($i+1)) ||
+10 echo $? > exit-status
+11 done &&
+12 echo "commit refs/heads/main" &&
+13 echo "author A U Thor <author@email.com> 123456789 +0000" &&
+14 echo "committer C O Mitter <committer@email.com> 123456789 +0000" &&
+15 echo "data 5" &&
+16 echo ">2gb" &&
+17 cat commit) |
+18 git fast-import --big-file-threshold=2 &&
+19 test ! -f exit-status
diff --git a/t/chainlint/loop-detect-status.test b/t/chainlint/loop-detect-status.test
index 1c6c23cfc9..8b639be073 100644
--- a/t/chainlint/loop-detect-status.test
+++ b/t/chainlint/loop-detect-status.test
@@ -1,3 +1,4 @@
+test_expect_success 'loop-detect-status' '
# LINT: "$?" handled explicitly within loop body
(while test $i -le $blobcount
do
@@ -17,3 +18,4 @@
cat commit) |
git fast-import --big-file-threshold=2 &&
test ! -f exit-status
+'
diff --git a/t/chainlint/loop-in-if.expect b/t/chainlint/loop-in-if.expect
index 6c5d6e5b24..4e8c67c914 100644
--- a/t/chainlint/loop-in-if.expect
+++ b/t/chainlint/loop-in-if.expect
@@ -1,12 +1,12 @@
-(
- if true
- then
- while true
- do
- echo "pop" ?!AMP?!
- echo "glup" ?!LOOP?!
- done ?!AMP?!
- foo
- fi ?!AMP?!
- bar
-)
+2 (
+3 if true
+4 then
+5 while true
+6 do
+7 echo "pop" ?!AMP?!
+8 echo "glup" ?!LOOP?!
+9 done ?!AMP?!
+10 foo
+11 fi ?!AMP?!
+12 bar
+13 )
diff --git a/t/chainlint/loop-in-if.test b/t/chainlint/loop-in-if.test
index dfcc3f98fb..b0d0d393cf 100644
--- a/t/chainlint/loop-in-if.test
+++ b/t/chainlint/loop-in-if.test
@@ -1,3 +1,4 @@
+test_expect_success 'loop-in-if' '
(
if true
then
@@ -13,3 +14,4 @@
fi
bar
)
+'
diff --git a/t/chainlint/loop-upstream-pipe.expect b/t/chainlint/loop-upstream-pipe.expect
index 0b82ecc4b9..bef82479ca 100644
--- a/t/chainlint/loop-upstream-pipe.expect
+++ b/t/chainlint/loop-upstream-pipe.expect
@@ -1,10 +1,10 @@
-(
- git rev-list --objects --no-object-names base..loose |
- while read oid
- do
- path="$objdir/$(test_oid_to_path "$oid")" &&
- printf "%s %d\n" "$oid" "$(test-tool chmtime --get "$path")" ||
- echo "object list generation failed for $oid"
- done |
- sort -k1
-) >expect &&
+2 (
+3 git rev-list --objects --no-object-names base..loose |
+4 while read oid
+5 do
+6 path="$objdir/$(test_oid_to_path "$oid")" &&
+7 printf "%s %d\n" "$oid" "$(test-tool chmtime --get "$path")" ||
+8 echo "object list generation failed for $oid"
+9 done |
+10 sort -k1
+11 ) >expect &&
diff --git a/t/chainlint/loop-upstream-pipe.test b/t/chainlint/loop-upstream-pipe.test
index efb77da897..8415a4db27 100644
--- a/t/chainlint/loop-upstream-pipe.test
+++ b/t/chainlint/loop-upstream-pipe.test
@@ -1,3 +1,4 @@
+test_expect_success 'loop-upstream-pipe' '
(
git rev-list --objects --no-object-names base..loose |
while read oid
@@ -9,3 +10,4 @@
done |
sort -k1
) >expect &&
+'
diff --git a/t/chainlint/multi-line-nested-command-substitution.expect b/t/chainlint/multi-line-nested-command-substitution.expect
index 300058341b..ad27e43e05 100644
--- a/t/chainlint/multi-line-nested-command-substitution.expect
+++ b/t/chainlint/multi-line-nested-command-substitution.expect
@@ -1,18 +1,18 @@
-(
- foo &&
- x=$(
- echo bar |
- cat
- ) &&
- echo ok
-) |
-sort &&
-(
- bar &&
- x=$(echo bar |
- cat
- ) &&
- y=$(echo baz |
- fip) &&
- echo fail
-)
+2 (
+3 foo &&
+4 x=$(
+5 echo bar |
+6 cat
+7 ) &&
+8 echo ok
+9 ) |
+10 sort &&
+11 (
+12 bar &&
+13 x=$(echo bar |
+14 cat
+15 ) &&
+16 y=$(echo baz |
+17 fip) &&
+18 echo fail
+19 )
diff --git a/t/chainlint/multi-line-nested-command-substitution.test b/t/chainlint/multi-line-nested-command-substitution.test
index 300058341b..e811c63f2b 100644
--- a/t/chainlint/multi-line-nested-command-substitution.test
+++ b/t/chainlint/multi-line-nested-command-substitution.test
@@ -1,3 +1,4 @@
+test_expect_success 'multi-line-nested-command-substitution' '
(
foo &&
x=$(
@@ -16,3 +17,4 @@ sort &&
fip) &&
echo fail
)
+'
diff --git a/t/chainlint/multi-line-string.expect b/t/chainlint/multi-line-string.expect
index 27ff95218e..62c54e3a5e 100644
--- a/t/chainlint/multi-line-string.expect
+++ b/t/chainlint/multi-line-string.expect
@@ -1,14 +1,14 @@
-(
- x="line 1
- line 2
- line 3" &&
- y="line 1
- line2" ?!AMP?!
- foobar
-) &&
-(
- echo "xyz" "abc
- def
- ghi" &&
- barfoo
-)
+2 (
+3 x="line 1
+4 line 2
+5 line 3" &&
+6 y="line 1
+7 line2" ?!AMP?!
+8 foobar
+9 ) &&
+10 (
+11 echo "xyz" "abc
+12 def
+13 ghi" &&
+14 barfoo
+15 )
diff --git a/t/chainlint/multi-line-string.test b/t/chainlint/multi-line-string.test
index 4a0af2107d..7b5048d2ea 100644
--- a/t/chainlint/multi-line-string.test
+++ b/t/chainlint/multi-line-string.test
@@ -1,3 +1,4 @@
+test_expect_success 'multi-line-string' '
(
x="line 1
line 2
@@ -13,3 +14,4 @@
ghi" &&
barfoo
)
+'
diff --git a/t/chainlint/negated-one-liner.expect b/t/chainlint/negated-one-liner.expect
index ad4c2d949e..a6ce52a1da 100644
--- a/t/chainlint/negated-one-liner.expect
+++ b/t/chainlint/negated-one-liner.expect
@@ -1,5 +1,5 @@
-! (foo && bar) &&
-! (foo && bar) >baz &&
-
-! (foo; ?!AMP?! bar) &&
-! (foo; ?!AMP?! bar) >baz
+2 ! (foo && bar) &&
+3 ! (foo && bar) >baz &&
+4
+5 ! (foo; ?!AMP?! bar) &&
+6 ! (foo; ?!AMP?! bar) >baz
diff --git a/t/chainlint/negated-one-liner.test b/t/chainlint/negated-one-liner.test
index c9598e9153..30f4cc5a9b 100644
--- a/t/chainlint/negated-one-liner.test
+++ b/t/chainlint/negated-one-liner.test
@@ -1,3 +1,4 @@
+test_expect_success 'negated-one-liner' '
# LINT: top-level one-liner subshell
! (foo && bar) &&
! (foo && bar) >baz &&
@@ -5,3 +6,4 @@
# LINT: top-level one-liner subshell missing internal "&&"
! (foo; bar) &&
! (foo; bar) >baz
+'
diff --git a/t/chainlint/nested-cuddled-subshell.expect b/t/chainlint/nested-cuddled-subshell.expect
index 3836049cc4..0191c9c294 100644
--- a/t/chainlint/nested-cuddled-subshell.expect
+++ b/t/chainlint/nested-cuddled-subshell.expect
@@ -1,25 +1,25 @@
-(
- (cd foo &&
- bar
- ) &&
-
- (cd foo &&
- bar
- ) ?!AMP?!
-
- (
- cd foo &&
- bar) &&
-
- (
- cd foo &&
- bar) ?!AMP?!
-
- (cd foo &&
- bar) &&
-
- (cd foo &&
- bar) ?!AMP?!
-
- foobar
-)
+2 (
+3 (cd foo &&
+4 bar
+5 ) &&
+6
+7 (cd foo &&
+8 bar
+9 ) ?!AMP?!
+10
+11 (
+12 cd foo &&
+13 bar) &&
+14
+15 (
+16 cd foo &&
+17 bar) ?!AMP?!
+18
+19 (cd foo &&
+20 bar) &&
+21
+22 (cd foo &&
+23 bar) ?!AMP?!
+24
+25 foobar
+26 )
diff --git a/t/chainlint/nested-cuddled-subshell.test b/t/chainlint/nested-cuddled-subshell.test
index 8fd656c7b5..31e92d3be4 100644
--- a/t/chainlint/nested-cuddled-subshell.test
+++ b/t/chainlint/nested-cuddled-subshell.test
@@ -1,3 +1,4 @@
+test_expect_success 'nested-cuddled-subshell' '
(
# LINT: opening "(" cuddled with first nested subshell statement
(cd foo &&
@@ -29,3 +30,4 @@
foobar
)
+'
diff --git a/t/chainlint/nested-here-doc.expect b/t/chainlint/nested-here-doc.expect
index 29b3832a98..70d9b68dc9 100644
--- a/t/chainlint/nested-here-doc.expect
+++ b/t/chainlint/nested-here-doc.expect
@@ -1,30 +1,30 @@
-cat <<ARBITRARY >foop &&
-naddle
-fub <<EOF
- nozzle
- noodle
-EOF
-formp
-ARBITRARY
-
-(
- cat <<-\INPUT_END &&
- fish are mice
- but geese go slow
- data <<EOF
- perl is lerp
- and nothing else
- EOF
- toink
- INPUT_END
-
- cat <<-\EOT ?!AMP?!
- text goes here
- data <<EOF
- data goes here
- EOF
- more test here
- EOT
-
- foobar
-)
+2 cat <<ARBITRARY >foop &&
+3 naddle
+4 fub <<EOF
+5 nozzle
+6 noodle
+7 EOF
+8 formp
+9 ARBITRARY
+10
+11 (
+12 cat <<-\INPUT_END &&
+13 fish are mice
+14 but geese go slow
+15 data <<EOF
+16 perl is lerp
+17 and nothing else
+18 EOF
+19 toink
+20 INPUT_END
+21
+22 cat <<-\EOT ?!AMP?!
+23 text goes here
+24 data <<EOF
+25 data goes here
+26 EOF
+27 more test here
+28 EOT
+29
+30 foobar
+31 )
diff --git a/t/chainlint/nested-here-doc.test b/t/chainlint/nested-here-doc.test
index f35404bf0f..9505c47a34 100644
--- a/t/chainlint/nested-here-doc.test
+++ b/t/chainlint/nested-here-doc.test
@@ -1,3 +1,4 @@
+test_expect_success 'nested-here-doc' '
# LINT: inner "EOF" not misintrepreted as closing ARBITRARY here-doc
cat <<ARBITRARY >foop &&
naddle
@@ -31,3 +32,4 @@ ARBITRARY
foobar
)
+'
diff --git a/t/chainlint/nested-loop-detect-failure.expect b/t/chainlint/nested-loop-detect-failure.expect
index 3461df40e5..c13c4d2f90 100644
--- a/t/chainlint/nested-loop-detect-failure.expect
+++ b/t/chainlint/nested-loop-detect-failure.expect
@@ -1,31 +1,31 @@
-for i in 0 1 2 3 4 5 6 7 8 9;
-do
- for j in 0 1 2 3 4 5 6 7 8 9;
- do
- echo "$i$j" >"path$i$j" ?!LOOP?!
- done ?!LOOP?!
-done &&
-
-for i in 0 1 2 3 4 5 6 7 8 9;
-do
- for j in 0 1 2 3 4 5 6 7 8 9;
- do
- echo "$i$j" >"path$i$j" || return 1
- done
-done &&
-
-for i in 0 1 2 3 4 5 6 7 8 9;
-do
- for j in 0 1 2 3 4 5 6 7 8 9;
- do
- echo "$i$j" >"path$i$j" ?!LOOP?!
- done || return 1
-done &&
-
-for i in 0 1 2 3 4 5 6 7 8 9;
-do
- for j in 0 1 2 3 4 5 6 7 8 9;
- do
- echo "$i$j" >"path$i$j" || return 1
- done || return 1
-done
+2 for i in 0 1 2 3 4 5 6 7 8 9;
+3 do
+4 for j in 0 1 2 3 4 5 6 7 8 9;
+5 do
+6 echo "$i$j" >"path$i$j" ?!LOOP?!
+7 done ?!LOOP?!
+8 done &&
+9
+10 for i in 0 1 2 3 4 5 6 7 8 9;
+11 do
+12 for j in 0 1 2 3 4 5 6 7 8 9;
+13 do
+14 echo "$i$j" >"path$i$j" || return 1
+15 done
+16 done &&
+17
+18 for i in 0 1 2 3 4 5 6 7 8 9;
+19 do
+20 for j in 0 1 2 3 4 5 6 7 8 9;
+21 do
+22 echo "$i$j" >"path$i$j" ?!LOOP?!
+23 done || return 1
+24 done &&
+25
+26 for i in 0 1 2 3 4 5 6 7 8 9;
+27 do
+28 for j in 0 1 2 3 4 5 6 7 8 9;
+29 do
+30 echo "$i$j" >"path$i$j" || return 1
+31 done || return 1
+32 done
diff --git a/t/chainlint/nested-loop-detect-failure.test b/t/chainlint/nested-loop-detect-failure.test
index e6f0c1acfb..3d4b657412 100644
--- a/t/chainlint/nested-loop-detect-failure.test
+++ b/t/chainlint/nested-loop-detect-failure.test
@@ -1,3 +1,4 @@
+test_expect_success 'nested-loop-detect-failure' '
# LINT: neither loop handles failure explicitly with "|| return 1"
for i in 0 1 2 3 4 5 6 7 8 9;
do
@@ -33,3 +34,4 @@ do
echo "$i$j" >"path$i$j" || return 1
done || return 1
done
+'
diff --git a/t/chainlint/nested-subshell-comment.expect b/t/chainlint/nested-subshell-comment.expect
index 9138cf386d..f89a8d03a8 100644
--- a/t/chainlint/nested-subshell-comment.expect
+++ b/t/chainlint/nested-subshell-comment.expect
@@ -1,11 +1,11 @@
-(
- foo &&
- (
- bar &&
- # bottles wobble while fiddles gobble
- # minor numbers of cows (or do they?)
- baz &&
- snaff
- ) ?!AMP?!
- fuzzy
-)
+2 (
+3 foo &&
+4 (
+5 bar &&
+6 # bottles wobble while fiddles gobble
+7 # minor numbers of cows (or do they?)
+8 baz &&
+9 snaff
+10 ) ?!AMP?!
+11 fuzzy
+12 )
diff --git a/t/chainlint/nested-subshell-comment.test b/t/chainlint/nested-subshell-comment.test
index 0215cdb192..b430580ce0 100644
--- a/t/chainlint/nested-subshell-comment.test
+++ b/t/chainlint/nested-subshell-comment.test
@@ -1,3 +1,4 @@
+test_expect_success 'nested-subshell-comment' '
(
foo &&
(
@@ -11,3 +12,4 @@
)
fuzzy
)
+'
diff --git a/t/chainlint/nested-subshell.expect b/t/chainlint/nested-subshell.expect
index 73ff28546a..811e8a7912 100644
--- a/t/chainlint/nested-subshell.expect
+++ b/t/chainlint/nested-subshell.expect
@@ -1,13 +1,13 @@
-(
- cd foo &&
- (
- echo a &&
- echo b
- ) >file &&
-
- cd foo &&
- (
- echo a ?!AMP?!
- echo b
- ) >file
-)
+2 (
+3 cd foo &&
+4 (
+5 echo a &&
+6 echo b
+7 ) >file &&
+8
+9 cd foo &&
+10 (
+11 echo a ?!AMP?!
+12 echo b
+13 ) >file
+14 )
diff --git a/t/chainlint/nested-subshell.test b/t/chainlint/nested-subshell.test
index 440ee9992d..c31da34b73 100644
--- a/t/chainlint/nested-subshell.test
+++ b/t/chainlint/nested-subshell.test
@@ -1,3 +1,4 @@
+test_expect_success 'nested-subshell' '
(
cd foo &&
(
@@ -11,3 +12,4 @@
echo b
) >file
)
+'
diff --git a/t/chainlint/not-heredoc.expect b/t/chainlint/not-heredoc.expect
index 2e9bb135fe..611b7b75cb 100644
--- a/t/chainlint/not-heredoc.expect
+++ b/t/chainlint/not-heredoc.expect
@@ -1,14 +1,14 @@
-echo "<<<<<<< ours" &&
-echo ourside &&
-echo "=======" &&
-echo theirside &&
-echo ">>>>>>> theirs" &&
-
-(
- echo "<<<<<<< ours" &&
- echo ourside &&
- echo "=======" &&
- echo theirside &&
- echo ">>>>>>> theirs" ?!AMP?!
- poodle
-) >merged
+2 echo "<<<<<<< ours" &&
+3 echo ourside &&
+4 echo "=======" &&
+5 echo theirside &&
+6 echo ">>>>>>> theirs" &&
+7
+8 (
+9 echo "<<<<<<< ours" &&
+10 echo ourside &&
+11 echo "=======" &&
+12 echo theirside &&
+13 echo ">>>>>>> theirs" ?!AMP?!
+14 poodle
+15 ) >merged
diff --git a/t/chainlint/not-heredoc.test b/t/chainlint/not-heredoc.test
index 9aa57346cd..09711e45e0 100644
--- a/t/chainlint/not-heredoc.test
+++ b/t/chainlint/not-heredoc.test
@@ -1,3 +1,4 @@
+test_expect_success 'not-heredoc' '
# LINT: "<< ours" inside string is not here-doc
echo "<<<<<<< ours" &&
echo ourside &&
@@ -14,3 +15,4 @@ echo ">>>>>>> theirs" &&
echo ">>>>>>> theirs"
poodle
) >merged
+'
diff --git a/t/chainlint/one-liner-for-loop.expect b/t/chainlint/one-liner-for-loop.expect
index 51a3dc7c54..49dcf065ef 100644
--- a/t/chainlint/one-liner-for-loop.expect
+++ b/t/chainlint/one-liner-for-loop.expect
@@ -1,9 +1,9 @@
-git init dir-rename-and-content &&
-(
- cd dir-rename-and-content &&
- test_write_lines 1 2 3 4 5 >foo &&
- mkdir olddir &&
- for i in a b c; do echo $i >olddir/$i; ?!LOOP?! done ?!AMP?!
- git add foo olddir &&
- git commit -m "original" &&
-)
+2 git init dir-rename-and-content &&
+3 (
+4 cd dir-rename-and-content &&
+5 test_write_lines 1 2 3 4 5 >foo &&
+6 mkdir olddir &&
+7 for i in a b c; do echo $i >olddir/$i; ?!LOOP?! done ?!AMP?!
+8 git add foo olddir &&
+9 git commit -m "original" &&
+10 )
diff --git a/t/chainlint/one-liner-for-loop.test b/t/chainlint/one-liner-for-loop.test
index 4bd8c066c7..00afd7ef76 100644
--- a/t/chainlint/one-liner-for-loop.test
+++ b/t/chainlint/one-liner-for-loop.test
@@ -1,3 +1,4 @@
+test_expect_success 'one-liner-for-loop' '
git init dir-rename-and-content &&
(
cd dir-rename-and-content &&
@@ -8,3 +9,4 @@ git init dir-rename-and-content &&
git add foo olddir &&
git commit -m "original" &&
)
+'
diff --git a/t/chainlint/one-liner.expect b/t/chainlint/one-liner.expect
index 57a7a444c1..9861811283 100644
--- a/t/chainlint/one-liner.expect
+++ b/t/chainlint/one-liner.expect
@@ -1,9 +1,9 @@
-(foo && bar) &&
-(foo && bar) |
-(foo && bar) >baz &&
-
-(foo; ?!AMP?! bar) &&
-(foo; ?!AMP?! bar) |
-(foo; ?!AMP?! bar) >baz &&
-
-(foo "bar; baz")
+2 (foo && bar) &&
+3 (foo && bar) |
+4 (foo && bar) >baz &&
+5
+6 (foo; ?!AMP?! bar) &&
+7 (foo; ?!AMP?! bar) |
+8 (foo; ?!AMP?! bar) >baz &&
+9
+10 (foo "bar; baz")
diff --git a/t/chainlint/one-liner.test b/t/chainlint/one-liner.test
index be9858fa29..6e42ee1b5e 100644
--- a/t/chainlint/one-liner.test
+++ b/t/chainlint/one-liner.test
@@ -1,3 +1,4 @@
+test_expect_success 'one-liner' '
# LINT: top-level one-liner subshell
(foo && bar) &&
(foo && bar) |
@@ -10,3 +11,4 @@
# LINT: ";" in string not misinterpreted as broken &&-chain
(foo "bar; baz")
+'
diff --git a/t/chainlint/p4-filespec.expect b/t/chainlint/p4-filespec.expect
index 1290fd1ff2..cff3e4e3d1 100644
--- a/t/chainlint/p4-filespec.expect
+++ b/t/chainlint/p4-filespec.expect
@@ -1,4 +1,4 @@
-(
- p4 print -1 //depot/fiddle#42 >file &&
- foobar
-)
+2 (
+3 p4 print -1 //depot/fiddle#42 >file &&
+4 foobar
+5 )
diff --git a/t/chainlint/p4-filespec.test b/t/chainlint/p4-filespec.test
index 4fd2d6e2b8..8ba6b911dc 100644
--- a/t/chainlint/p4-filespec.test
+++ b/t/chainlint/p4-filespec.test
@@ -1,5 +1,7 @@
+test_expect_success 'p4-filespec' '
(
# LINT: Perforce revspec in filespec not misinterpreted as in-line comment
p4 print -1 //depot/fiddle#42 >file &&
foobar
)
+'
diff --git a/t/chainlint/pipe.expect b/t/chainlint/pipe.expect
index 811971b1a3..1bbe5a2ce1 100644
--- a/t/chainlint/pipe.expect
+++ b/t/chainlint/pipe.expect
@@ -1,10 +1,10 @@
-(
- foo |
- bar |
- baz &&
-
- fish |
- cow ?!AMP?!
-
- sunder
-)
+2 (
+3 foo |
+4 bar |
+5 baz &&
+6
+7 fish |
+8 cow ?!AMP?!
+9
+10 sunder
+11 )
diff --git a/t/chainlint/pipe.test b/t/chainlint/pipe.test
index dd82534c66..1af81c243b 100644
--- a/t/chainlint/pipe.test
+++ b/t/chainlint/pipe.test
@@ -1,3 +1,4 @@
+test_expect_success 'pipe' '
(
# LINT: no "&&" needed on line ending with "|"
foo |
@@ -10,3 +11,4 @@
sunder
)
+'
diff --git a/t/chainlint/return-loop.expect b/t/chainlint/return-loop.expect
index cfc0549bef..da8f9abea3 100644
--- a/t/chainlint/return-loop.expect
+++ b/t/chainlint/return-loop.expect
@@ -1,5 +1,5 @@
-while test $i -lt $((num - 5))
-do
- git notes add -m "notes for commit$i" HEAD~$i || return 1
- i=$((i + 1))
-done
+2 while test $i -lt $((num - 5))
+3 do
+4 git notes add -m "notes for commit$i" HEAD~$i || return 1
+5 i=$((i + 1))
+6 done
diff --git a/t/chainlint/return-loop.test b/t/chainlint/return-loop.test
index f90b171300..ea76c3593a 100644
--- a/t/chainlint/return-loop.test
+++ b/t/chainlint/return-loop.test
@@ -1,6 +1,8 @@
+test_expect_success 'return-loop' '
while test $i -lt $((num - 5))
do
# LINT: "|| return {n}" valid loop escape outside subshell; no "&&" needed
git notes add -m "notes for commit$i" HEAD~$i || return 1
i=$((i + 1))
done
+'
diff --git a/t/chainlint/semicolon.expect b/t/chainlint/semicolon.expect
index 3aa2259f36..866438310c 100644
--- a/t/chainlint/semicolon.expect
+++ b/t/chainlint/semicolon.expect
@@ -1,19 +1,19 @@
-(
- cat foo ; ?!AMP?! echo bar ?!AMP?!
- cat foo ; ?!AMP?! echo bar
-) &&
-(
- cat foo ; ?!AMP?! echo bar &&
- cat foo ; ?!AMP?! echo bar
-) &&
-(
- echo "foo; bar" &&
- cat foo; ?!AMP?! echo bar
-) &&
-(
- foo;
-) &&
-(cd foo &&
- for i in a b c; do
- echo; ?!LOOP?!
- done)
+2 (
+3 cat foo ; ?!AMP?! echo bar ?!AMP?!
+4 cat foo ; ?!AMP?! echo bar
+5 ) &&
+6 (
+7 cat foo ; ?!AMP?! echo bar &&
+8 cat foo ; ?!AMP?! echo bar
+9 ) &&
+10 (
+11 echo "foo; bar" &&
+12 cat foo; ?!AMP?! echo bar
+13 ) &&
+14 (
+15 foo;
+16 ) &&
+17 (cd foo &&
+18 for i in a b c; do
+19 echo; ?!LOOP?!
+20 done)
diff --git a/t/chainlint/semicolon.test b/t/chainlint/semicolon.test
index 67e1192c50..fc0ba1b539 100644
--- a/t/chainlint/semicolon.test
+++ b/t/chainlint/semicolon.test
@@ -1,3 +1,4 @@
+test_expect_success 'semicolon' '
(
# LINT: missing internal "&&" and ending "&&"
cat foo ; echo bar
@@ -23,3 +24,4 @@
# LINT: semicolon unnecessary but legitimate
echo;
done)
+'
diff --git a/t/chainlint/sqstring-in-sqstring.expect b/t/chainlint/sqstring-in-sqstring.expect
index cf0b591cf7..ba5d3c3a6d 100644
--- a/t/chainlint/sqstring-in-sqstring.expect
+++ b/t/chainlint/sqstring-in-sqstring.expect
@@ -1,4 +1,4 @@
-perl -e '
- defined($_ = -s $_) or die for @ARGV;
- exit 1 if $ARGV[0] <= $ARGV[1];
-' test-2-$packname_2.pack test-3-$packname_3.pack
+2 perl -e '
+3 defined($_ = -s $_) or die for @ARGV;
+4 exit 1 if $ARGV[0] <= $ARGV[1];
+5 ' test-2-$packname_2.pack test-3-$packname_3.pack
diff --git a/t/chainlint/sqstring-in-sqstring.test b/t/chainlint/sqstring-in-sqstring.test
index 77a425e0c7..24169724a5 100644
--- a/t/chainlint/sqstring-in-sqstring.test
+++ b/t/chainlint/sqstring-in-sqstring.test
@@ -1,5 +1,7 @@
+test_expect_success 'sqstring-in-sqstring' '
# LINT: SQ-string Perl code fragment within SQ-string
perl -e '\''
defined($_ = -s $_) or die for @ARGV;
exit 1 if $ARGV[0] <= $ARGV[1];
'\'' test-2-$packname_2.pack test-3-$packname_3.pack
+'
diff --git a/t/chainlint/subshell-here-doc.expect b/t/chainlint/subshell-here-doc.expect
index 75d6f607e2..5647500c82 100644
--- a/t/chainlint/subshell-here-doc.expect
+++ b/t/chainlint/subshell-here-doc.expect
@@ -1,30 +1,30 @@
-(
- echo wobba \
- gorgo snoot \
- wafta snurb <<-EOF &&
- quoth the raven,
- nevermore...
- EOF
-
- cat <<EOF >bip ?!AMP?!
- fish fly high
-EOF
-
- echo <<-\EOF >bop
- gomez
- morticia
- wednesday
- pugsly
- EOF
-) &&
-(
- cat <<-\ARBITRARY >bup &&
- glink
- FIZZ
- ARBITRARY
- cat <<-"ARBITRARY3" >bup3 &&
- glink
- FIZZ
- ARBITRARY3
- meep
-)
+2 (
+3 echo wobba \
+4 gorgo snoot \
+5 wafta snurb <<-EOF &&
+6 quoth the raven,
+7 nevermore...
+8 EOF
+9
+10 cat <<EOF >bip ?!AMP?!
+11 fish fly high
+12 EOF
+13
+14 echo <<-\EOF >bop
+15 gomez
+16 morticia
+17 wednesday
+18 pugsly
+19 EOF
+20 ) &&
+21 (
+22 cat <<-\ARBITRARY >bup &&
+23 glink
+24 FIZZ
+25 ARBITRARY
+26 cat <<-"ARBITRARY3" >bup3 &&
+27 glink
+28 FIZZ
+29 ARBITRARY3
+30 meep
+31 )
diff --git a/t/chainlint/subshell-here-doc.test b/t/chainlint/subshell-here-doc.test
index d40eb65583..4a38f47f01 100644
--- a/t/chainlint/subshell-here-doc.test
+++ b/t/chainlint/subshell-here-doc.test
@@ -1,3 +1,4 @@
+test_expect_success 'subshell-here-doc' '
(
# LINT: stitch together incomplete \-ending lines
# LINT: swallow here-doc to avoid false positives in content
@@ -33,3 +34,4 @@ EOF
ARBITRARY3
meep
)
+'
diff --git a/t/chainlint/subshell-one-liner.expect b/t/chainlint/subshell-one-liner.expect
index 8f694990e8..214316c6a0 100644
--- a/t/chainlint/subshell-one-liner.expect
+++ b/t/chainlint/subshell-one-liner.expect
@@ -1,19 +1,19 @@
-(
- (foo && bar) &&
- (foo && bar) |
- (foo && bar) >baz &&
-
- (foo; ?!AMP?! bar) &&
- (foo; ?!AMP?! bar) |
- (foo; ?!AMP?! bar) >baz &&
-
- (foo || exit 1) &&
- (foo || exit 1) |
- (foo || exit 1) >baz &&
-
- (foo && bar) ?!AMP?!
-
- (foo && bar; ?!AMP?! baz) ?!AMP?!
-
- foobar
-)
+2 (
+3 (foo && bar) &&
+4 (foo && bar) |
+5 (foo && bar) >baz &&
+6
+7 (foo; ?!AMP?! bar) &&
+8 (foo; ?!AMP?! bar) |
+9 (foo; ?!AMP?! bar) >baz &&
+10
+11 (foo || exit 1) &&
+12 (foo || exit 1) |
+13 (foo || exit 1) >baz &&
+14
+15 (foo && bar) ?!AMP?!
+16
+17 (foo && bar; ?!AMP?! baz) ?!AMP?!
+18
+19 foobar
+20 )
diff --git a/t/chainlint/subshell-one-liner.test b/t/chainlint/subshell-one-liner.test
index 37fa643c20..dac536afcc 100644
--- a/t/chainlint/subshell-one-liner.test
+++ b/t/chainlint/subshell-one-liner.test
@@ -1,3 +1,4 @@
+test_expect_success 'subshell-one-liner' '
(
# LINT: nested one-liner subshell
(foo && bar) &&
@@ -22,3 +23,4 @@
foobar
)
+'
diff --git a/t/chainlint/t7900-subtree.expect b/t/chainlint/t7900-subtree.expect
index 02f3129232..9e60338bcf 100644
--- a/t/chainlint/t7900-subtree.expect
+++ b/t/chainlint/t7900-subtree.expect
@@ -1,22 +1,22 @@
-(
- chks="sub1
-sub2
-sub3
-sub4" &&
- chks_sub=$(cat <<TXT | sed "s,^,sub dir/,"
-$chks
-TXT
-) &&
- chkms="main-sub1
-main-sub2
-main-sub3
-main-sub4" &&
- chkms_sub=$(cat <<TXT | sed "s,^,sub dir/,"
-$chkms
-TXT
-) &&
-
- subfiles=$(git ls-files) &&
- check_equal "$subfiles" "$chkms
-$chks"
-)
+2 (
+3 chks="sub1
+4 sub2
+5 sub3
+6 sub4" &&
+7 chks_sub=$(cat <<TXT | sed "s,^,sub dir/,"
+8 $chks
+9 TXT
+10 ) &&
+11 chkms="main-sub1
+12 main-sub2
+13 main-sub3
+14 main-sub4" &&
+15 chkms_sub=$(cat <<TXT | sed "s,^,sub dir/,"
+16 $chkms
+17 TXT
+18 ) &&
+19
+20 subfiles=$(git ls-files) &&
+21 check_equal "$subfiles" "$chkms
+22 $chks"
+23 )
diff --git a/t/chainlint/t7900-subtree.test b/t/chainlint/t7900-subtree.test
index 02f3129232..1f4f03300f 100644
--- a/t/chainlint/t7900-subtree.test
+++ b/t/chainlint/t7900-subtree.test
@@ -1,3 +1,4 @@
+test_expect_success 't7900-subtree' '
(
chks="sub1
sub2
@@ -20,3 +21,4 @@ TXT
check_equal "$subfiles" "$chkms
$chks"
)
+'
diff --git a/t/chainlint/token-pasting.expect b/t/chainlint/token-pasting.expect
index 6a387917a7..64f3235d26 100644
--- a/t/chainlint/token-pasting.expect
+++ b/t/chainlint/token-pasting.expect
@@ -1,27 +1,27 @@
-git config filter.rot13.smudge ./rot13.sh &&
-git config filter.rot13.clean ./rot13.sh &&
-
-{
- echo "*.t filter=rot13" ?!AMP?!
- echo "*.i ident"
-} >.gitattributes &&
-
-{
- echo a b c d e f g h i j k l m ?!AMP?!
- echo n o p q r s t u v w x y z ?!AMP?!
- echo '$Id$'
-} >test &&
-cat test >test.t &&
-cat test >test.o &&
-cat test >test.i &&
-git add test test.t test.i &&
-rm -f test test.t test.i &&
-git checkout -- test test.t test.i &&
-
-echo "content-test2" >test2.o &&
-echo "content-test3 - filename with special characters" >"test3 'sq',$x=.o" ?!AMP?!
-
-downstream_url_for_sed=$(
- printf "%sn" "$downstream_url" |
- sed -e 's/\/\\/g' -e 's/[[/.*^$]/\&/g'
-)
+2 git config filter.rot13.smudge ./rot13.sh &&
+3 git config filter.rot13.clean ./rot13.sh &&
+4
+5 {
+6 echo "*.t filter=rot13" ?!AMP?!
+7 echo "*.i ident"
+8 } >.gitattributes &&
+9
+10 {
+11 echo a b c d e f g h i j k l m ?!AMP?!
+12 echo n o p q r s t u v w x y z ?!AMP?!
+13 echo '$Id$'
+14 } >test &&
+15 cat test >test.t &&
+16 cat test >test.o &&
+17 cat test >test.i &&
+18 git add test test.t test.i &&
+19 rm -f test test.t test.i &&
+20 git checkout -- test test.t test.i &&
+21
+22 echo "content-test2" >test2.o &&
+23 echo "content-test3 - filename with special characters" >"test3 'sq',$x=.o" ?!AMP?!
+24
+25 downstream_url_for_sed=$(
+26 printf "%sn" "$downstream_url" |
+27 sed -e 's/\/\\/g' -e 's/[[/.*^$]/\&/g'
+28 )
diff --git a/t/chainlint/token-pasting.test b/t/chainlint/token-pasting.test
index b4610ce815..590914b733 100644
--- a/t/chainlint/token-pasting.test
+++ b/t/chainlint/token-pasting.test
@@ -1,3 +1,4 @@
+test_expect_success 'token-pasting' '
# LINT: single token; composite of multiple strings
git config filter.rot13.smudge ./rot13.sh &&
git config filter.rot13.clean ./rot13.sh &&
@@ -30,3 +31,4 @@ downstream_url_for_sed=$(
# LINT: exit/enter string context; "&" inside string not command terminator
sed -e '\''s/\\/\\\\/g'\'' -e '\''s/[[/.*^$]/\\&/g'\''
)
+'
diff --git a/t/chainlint/unclosed-here-doc-indent.expect b/t/chainlint/unclosed-here-doc-indent.expect
index 7c30a1a024..f78e23cb63 100644
--- a/t/chainlint/unclosed-here-doc-indent.expect
+++ b/t/chainlint/unclosed-here-doc-indent.expect
@@ -1,4 +1,4 @@
-command_which_is_run &&
-cat >expect <<-\EOF ?!UNCLOSED-HEREDOC?! &&
-we forget to end the here-doc
-command_which_is_gobbled
+2 command_which_is_run &&
+3 cat >expect <<-\EOF ?!UNCLOSED-HEREDOC?! &&
+4 we forget to end the here-doc
+5 command_which_is_gobbled
diff --git a/t/chainlint/unclosed-here-doc-indent.test b/t/chainlint/unclosed-here-doc-indent.test
index 5c841a9dfd..7ac9d0f7d7 100644
--- a/t/chainlint/unclosed-here-doc-indent.test
+++ b/t/chainlint/unclosed-here-doc-indent.test
@@ -1,4 +1,6 @@
+test_expect_success 'unclosed-here-doc-indent' '
command_which_is_run &&
cat >expect <<-\EOF &&
we forget to end the here-doc
command_which_is_gobbled
+'
diff --git a/t/chainlint/unclosed-here-doc.expect b/t/chainlint/unclosed-here-doc.expect
index d65e50f78d..51304672cf 100644
--- a/t/chainlint/unclosed-here-doc.expect
+++ b/t/chainlint/unclosed-here-doc.expect
@@ -1,7 +1,7 @@
-command_which_is_run &&
-cat >expect <<\EOF ?!UNCLOSED-HEREDOC?! &&
- we try to end the here-doc below,
- but the indentation throws us off
- since the operator is not "<<-".
- EOF
-command_which_is_gobbled
+2 command_which_is_run &&
+3 cat >expect <<\EOF ?!UNCLOSED-HEREDOC?! &&
+4 we try to end the here-doc below,
+5 but the indentation throws us off
+6 since the operator is not "<<-".
+7 EOF
+8 command_which_is_gobbled
diff --git a/t/chainlint/unclosed-here-doc.test b/t/chainlint/unclosed-here-doc.test
index 69d3786c34..68e78f06f3 100644
--- a/t/chainlint/unclosed-here-doc.test
+++ b/t/chainlint/unclosed-here-doc.test
@@ -1,3 +1,4 @@
+test_expect_success 'unclosed-here-doc' '
command_which_is_run &&
cat >expect <<\EOF &&
we try to end the here-doc below,
@@ -5,3 +6,4 @@ cat >expect <<\EOF &&
since the operator is not "<<-".
EOF
command_which_is_gobbled
+'
diff --git a/t/chainlint/while-loop.expect b/t/chainlint/while-loop.expect
index 06c1567f48..5ffabd5a93 100644
--- a/t/chainlint/while-loop.expect
+++ b/t/chainlint/while-loop.expect
@@ -1,14 +1,14 @@
-(
- while true
- do
- echo foo ?!AMP?!
- cat <<-\EOF ?!LOOP?!
- bar
- EOF
- done ?!AMP?!
-
- while true; do
- echo foo &&
- cat bar ?!LOOP?!
- done
-)
+2 (
+3 while true
+4 do
+5 echo foo ?!AMP?!
+6 cat <<-\EOF ?!LOOP?!
+7 bar
+8 EOF
+9 done ?!AMP?!
+10
+11 while true; do
+12 echo foo &&
+13 cat bar ?!LOOP?!
+14 done
+15 )
diff --git a/t/chainlint/while-loop.test b/t/chainlint/while-loop.test
index d09fb016e4..33a201906a 100644
--- a/t/chainlint/while-loop.test
+++ b/t/chainlint/while-loop.test
@@ -1,3 +1,4 @@
+test_expect_success 'while-loop' '
(
# LINT: "while", "do", "done" do not need "&&"
while true
@@ -17,3 +18,4 @@
cat bar
done
)
+'
diff --git a/t/helper/test-bitmap.c b/t/helper/test-bitmap.c
index af43ee1cb5..3f23f21072 100644
--- a/t/helper/test-bitmap.c
+++ b/t/helper/test-bitmap.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "test-tool.h"
#include "git-compat-util.h"
#include "pack-bitmap.h"
@@ -13,21 +15,41 @@ static int bitmap_dump_hashes(void)
return test_bitmap_hashes(the_repository);
}
+static int bitmap_dump_pseudo_merges(void)
+{
+ return test_bitmap_pseudo_merges(the_repository);
+}
+
+static int bitmap_dump_pseudo_merge_commits(uint32_t n)
+{
+ return test_bitmap_pseudo_merge_commits(the_repository, n);
+}
+
+static int bitmap_dump_pseudo_merge_objects(uint32_t n)
+{
+ return test_bitmap_pseudo_merge_objects(the_repository, n);
+}
+
int cmd__bitmap(int argc, const char **argv)
{
setup_git_directory();
- if (argc != 2)
- goto usage;
-
- if (!strcmp(argv[1], "list-commits"))
+ if (argc == 2 && !strcmp(argv[1], "list-commits"))
return bitmap_list_commits();
- if (!strcmp(argv[1], "dump-hashes"))
+ if (argc == 2 && !strcmp(argv[1], "dump-hashes"))
return bitmap_dump_hashes();
+ if (argc == 2 && !strcmp(argv[1], "dump-pseudo-merges"))
+ return bitmap_dump_pseudo_merges();
+ if (argc == 3 && !strcmp(argv[1], "dump-pseudo-merge-commits"))
+ return bitmap_dump_pseudo_merge_commits(atoi(argv[2]));
+ if (argc == 3 && !strcmp(argv[1], "dump-pseudo-merge-objects"))
+ return bitmap_dump_pseudo_merge_objects(atoi(argv[2]));
-usage:
usage("\ttest-tool bitmap list-commits\n"
- "\ttest-tool bitmap dump-hashes");
+ "\ttest-tool bitmap dump-hashes\n"
+ "\ttest-tool bitmap dump-pseudo-merges\n"
+ "\ttest-tool bitmap dump-pseudo-merge-commits <n>\n"
+ "\ttest-tool bitmap dump-pseudo-merge-objects <n>");
return -1;
}
diff --git a/t/helper/test-bloom.c b/t/helper/test-bloom.c
index 1281e66876..97541daf71 100644
--- a/t/helper/test-bloom.c
+++ b/t/helper/test-bloom.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "test-tool.h"
#include "bloom.h"
#include "hex.h"
@@ -49,6 +51,7 @@ static void get_bloom_filter_for_commit(const struct object_id *commit_oid)
static const char *bloom_usage = "\n"
" test-tool bloom get_murmur3 <string>\n"
+" test-tool bloom get_murmur3_seven_highbit\n"
" test-tool bloom generate_filter <string> [<string>...]\n"
" test-tool bloom get_filter_for_commit <commit-hex>\n";
@@ -63,7 +66,13 @@ int cmd__bloom(int argc, const char **argv)
uint32_t hashed;
if (argc < 3)
usage(bloom_usage);
- hashed = murmur3_seeded(0, argv[2], strlen(argv[2]));
+ hashed = murmur3_seeded_v2(0, argv[2], strlen(argv[2]));
+ printf("Murmur3 Hash with seed=0:0x%08x\n", hashed);
+ }
+
+ if (!strcmp(argv[1], "get_murmur3_seven_highbit")) {
+ uint32_t hashed;
+ hashed = murmur3_seeded_v2(0, "\x99\xaa\xbb\xcc\xdd\xee\xff", 7);
printf("Murmur3 Hash with seed=0:0x%08x\n", hashed);
}
diff --git a/t/helper/test-bundle-uri.c b/t/helper/test-bundle-uri.c
index 09dc78733c..0c5fa723d8 100644
--- a/t/helper/test-bundle-uri.c
+++ b/t/helper/test-bundle-uri.c
@@ -88,8 +88,6 @@ static int cmd_ls_remote(int argc, const char **argv)
die(_("bad repository '%s'"), dest);
die(_("no remote configured to get bundle URIs from"));
}
- if (!remote->url_nr)
- die(_("remote '%s' has no configured URL"), dest);
transport = transport_get(remote, NULL);
if (transport_get_remote_bundle_uri(transport) < 0) {
diff --git a/t/helper/test-cache-tree.c b/t/helper/test-cache-tree.c
index e7236392c8..5cdef3ebef 100644
--- a/t/helper/test-cache-tree.c
+++ b/t/helper/test-cache-tree.c
@@ -1,4 +1,5 @@
-#define USE_THE_INDEX_VARIABLE
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "test-tool.h"
#include "gettext.h"
#include "hex.h"
@@ -38,29 +39,29 @@ int cmd__cache_tree(int argc, const char **argv)
if (repo_read_index(the_repository) < 0)
die(_("unable to read index file"));
- oidcpy(&oid, &the_index.cache_tree->oid);
+ oidcpy(&oid, &the_repository->index->cache_tree->oid);
tree = parse_tree_indirect(&oid);
if (!tree)
die(_("not a tree object: %s"), oid_to_hex(&oid));
if (empty) {
/* clear the cache tree & allocate a new one */
- cache_tree_free(&the_index.cache_tree);
- the_index.cache_tree = cache_tree();
+ cache_tree_free(&the_repository->index->cache_tree);
+ the_repository->index->cache_tree = cache_tree();
} else if (invalidate_qty) {
/* invalidate the specified number of unique paths */
- float f_interval = (float)the_index.cache_nr / invalidate_qty;
+ float f_interval = (float)the_repository->index->cache_nr / invalidate_qty;
int interval = f_interval < 1.0 ? 1 : (int)f_interval;
- for (i = 0; i < invalidate_qty && i * interval < the_index.cache_nr; i++)
- cache_tree_invalidate_path(&the_index, the_index.cache[i * interval]->name);
+ for (i = 0; i < invalidate_qty && i * interval < the_repository->index->cache_nr; i++)
+ cache_tree_invalidate_path(the_repository->index, the_repository->index->cache[i * interval]->name);
}
if (argc != 1)
usage_with_options(test_cache_tree_usage, options);
else if (!strcmp(argv[0], "prime"))
- prime_cache_tree(the_repository, &the_index, tree);
+ prime_cache_tree(the_repository, the_repository->index, tree);
else if (!strcmp(argv[0], "update"))
- cache_tree_update(&the_index, WRITE_TREE_SILENT | WRITE_TREE_REPAIR);
+ cache_tree_update(the_repository->index, WRITE_TREE_SILENT | WRITE_TREE_REPAIR);
/* use "control" subcommand to specify no-op */
else if (!!strcmp(argv[0], "control"))
die(_("Unhandled subcommand '%s'"), argv[0]);
diff --git a/t/helper/test-dump-cache-tree.c b/t/helper/test-dump-cache-tree.c
index c38f546e4f..3f0c7d0ed0 100644
--- a/t/helper/test-dump-cache-tree.c
+++ b/t/helper/test-dump-cache-tree.c
@@ -1,4 +1,5 @@
-#define USE_THE_INDEX_VARIABLE
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "test-tool.h"
#include "hash.h"
#include "hex.h"
@@ -68,10 +69,10 @@ int cmd__dump_cache_tree(int ac UNUSED, const char **av UNUSED)
setup_git_directory();
if (repo_read_index(the_repository) < 0)
die("unable to read index file");
- istate = the_index;
+ istate = *the_repository->index;
istate.cache_tree = another;
cache_tree_update(&istate, WRITE_TREE_DRY_RUN);
- ret = dump_cache_tree(the_index.cache_tree, another, "");
+ ret = dump_cache_tree(the_repository->index->cache_tree, another, "");
cache_tree_free(&another);
return ret;
diff --git a/t/helper/test-dump-fsmonitor.c b/t/helper/test-dump-fsmonitor.c
index 4f215fea02..1b7f37a84f 100644
--- a/t/helper/test-dump-fsmonitor.c
+++ b/t/helper/test-dump-fsmonitor.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "test-tool.h"
#include "read-cache-ll.h"
#include "repository.h"
diff --git a/t/helper/test-dump-split-index.c b/t/helper/test-dump-split-index.c
index f29d18ef94..a6720faf9c 100644
--- a/t/helper/test-dump-split-index.c
+++ b/t/helper/test-dump-split-index.c
@@ -1,4 +1,5 @@
-#define USE_THE_INDEX_VARIABLE
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "test-tool.h"
#include "hex.h"
#include "read-cache-ll.h"
@@ -19,16 +20,16 @@ int cmd__dump_split_index(int ac UNUSED, const char **av)
setup_git_directory();
- do_read_index(&the_index, av[1], 1);
- printf("own %s\n", oid_to_hex(&the_index.oid));
- si = the_index.split_index;
+ do_read_index(the_repository->index, av[1], 1);
+ printf("own %s\n", oid_to_hex(&the_repository->index->oid));
+ si = the_repository->index->split_index;
if (!si) {
printf("not a split index\n");
return 0;
}
printf("base %s\n", oid_to_hex(&si->base_oid));
- for (i = 0; i < the_index.cache_nr; i++) {
- struct cache_entry *ce = the_index.cache[i];
+ for (i = 0; i < the_repository->index->cache_nr; i++) {
+ struct cache_entry *ce = the_repository->index->cache[i];
printf("%06o %s %d\t%s\n", ce->ce_mode,
oid_to_hex(&ce->oid), ce_stage(ce), ce->name);
}
diff --git a/t/helper/test-dump-untracked-cache.c b/t/helper/test-dump-untracked-cache.c
index b4af9712fe..4f010d5324 100644
--- a/t/helper/test-dump-untracked-cache.c
+++ b/t/helper/test-dump-untracked-cache.c
@@ -1,4 +1,5 @@
-#define USE_THE_INDEX_VARIABLE
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "test-tool.h"
#include "dir.h"
#include "hex.h"
@@ -56,7 +57,7 @@ int cmd__dump_untracked_cache(int ac UNUSED, const char **av UNUSED)
setup_git_directory();
if (repo_read_index(the_repository) < 0)
die("unable to read index file");
- uc = the_index.untracked;
+ uc = the_repository->index->untracked;
if (!uc) {
printf("no untracked cache\n");
return 0;
diff --git a/t/helper/test-example-decorate.c b/t/helper/test-example-decorate.c
deleted file mode 100644
index 8f59f6be4c..0000000000
--- a/t/helper/test-example-decorate.c
+++ /dev/null
@@ -1,78 +0,0 @@
-#include "test-tool.h"
-#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)
-{
- struct decoration n;
- struct object_id one_oid = { {1} };
- struct object_id two_oid = { {2} };
- struct object_id three_oid = { {3} };
- struct object *one, *two, *three;
-
- int decoration_a, decoration_b;
-
- void *ret;
-
- int i, objects_noticed = 0;
-
- /*
- * The struct must be zero-initialized.
- */
- memset(&n, 0, sizeof(n));
-
- /*
- * Add 2 objects, one with a non-NULL decoration and one with a NULL
- * decoration.
- */
- one = lookup_unknown_object(the_repository, &one_oid);
- two = lookup_unknown_object(the_repository, &two_oid);
- ret = add_decoration(&n, one, &decoration_a);
- if (ret)
- BUG("when adding a brand-new object, NULL should be returned");
- ret = add_decoration(&n, two, NULL);
- if (ret)
- BUG("when adding a brand-new object, NULL should be returned");
-
- /*
- * When re-adding an already existing object, the old decoration is
- * returned.
- */
- ret = add_decoration(&n, one, NULL);
- if (ret != &decoration_a)
- BUG("when readding an already existing object, existing decoration should be returned");
- ret = add_decoration(&n, two, &decoration_b);
- if (ret)
- BUG("when readding an already existing object, existing decoration should be returned");
-
- /*
- * Lookup returns the added declarations, or NULL if the object was
- * never added.
- */
- ret = lookup_decoration(&n, one);
- if (ret)
- BUG("lookup should return added declaration");
- ret = lookup_decoration(&n, two);
- if (ret != &decoration_b)
- BUG("lookup should return added declaration");
- three = lookup_unknown_object(the_repository, &three_oid);
- ret = lookup_decoration(&n, three);
- if (ret)
- BUG("lookup for unknown object should return NULL");
-
- /*
- * The user can also loop through all entries.
- */
- for (i = 0; i < n.size; i++) {
- if (n.entries[i].base)
- objects_noticed++;
- }
- if (objects_noticed != 2)
- BUG("should have 2 objects");
-
- clear_decoration(&n, NULL);
-
- return 0;
-}
diff --git a/t/unit-tests/t-basic.c b/t/helper/test-example-tap.c
index fda1ae59a6..d072ad559f 100644
--- a/t/unit-tests/t-basic.c
+++ b/t/helper/test-example-tap.c
@@ -1,4 +1,5 @@
-#include "test-lib.h"
+#include "test-tool.h"
+#include "t/unit-tests/test-lib.h"
/*
* The purpose of this "unit test" is to verify a few invariants of the unit
@@ -69,7 +70,7 @@ static void t_empty(void)
; /* empty */
}
-int cmd_main(int argc, const char **argv)
+int cmd__example_tap(int argc, const char **argv)
{
test_res = TEST(check_res = check_int(1, ==, 1), "passing test");
TEST(t_res(1), "passing test and assertion return 1");
diff --git a/t/helper/test-find-pack.c b/t/helper/test-find-pack.c
index e8bd793e58..14b2b0c12c 100644
--- a/t/helper/test-find-pack.c
+++ b/t/helper/test-find-pack.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "test-tool.h"
#include "object-name.h"
#include "object-store.h"
diff --git a/t/helper/test-fsmonitor-client.c b/t/helper/test-fsmonitor-client.c
index 8280984d08..02bfe92e8d 100644
--- a/t/helper/test-fsmonitor-client.c
+++ b/t/helper/test-fsmonitor-client.c
@@ -3,6 +3,8 @@
* a `git fsmonitor--daemon` daemon.
*/
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "test-tool.h"
#include "parse-options.h"
#include "fsmonitor-ipc.h"
diff --git a/t/helper/test-hash-speed.c b/t/helper/test-hash-speed.c
index b235da594f..7de822af51 100644
--- a/t/helper/test-hash-speed.c
+++ b/t/helper/test-hash-speed.c
@@ -1,5 +1,5 @@
#include "test-tool.h"
-#include "hash-ll.h"
+#include "hash.h"
#define NUM_SECONDS 3
diff --git a/t/helper/test-hashmap.c b/t/helper/test-hashmap.c
index 0eb0b3d49c..2912899558 100644
--- a/t/helper/test-hashmap.c
+++ b/t/helper/test-hashmap.c
@@ -36,7 +36,8 @@ static int test_entry_cmp(const void *cmp_data,
}
static struct test_entry *alloc_test_entry(unsigned int hash,
- char *key, char *value)
+ const char *key,
+ const char *value)
{
size_t klen = strlen(key);
size_t vlen = strlen(value);
diff --git a/t/helper/test-json-writer.c b/t/helper/test-json-writer.c
index afe393f597..ed52eb76bf 100644
--- a/t/helper/test-json-writer.c
+++ b/t/helper/test-json-writer.c
@@ -174,7 +174,7 @@ static void make_arr4(int pretty)
jw_end(&arr4);
}
-static char *expect_nest1 =
+static const char *expect_nest1 =
"{\"obj1\":{\"a\":\"abc\",\"b\":42,\"c\":true},\"arr1\":[\"abc\",42,true]}";
static struct json_writer nest1 = JSON_WRITER_INIT;
@@ -195,10 +195,10 @@ static void make_nest1(int pretty)
jw_release(&arr1);
}
-static char *expect_inline1 =
+static const char *expect_inline1 =
"{\"obj1\":{\"a\":\"abc\",\"b\":42,\"c\":true},\"arr1\":[\"abc\",42,true]}";
-static char *pretty_inline1 =
+static const char *pretty_inline1 =
("{\n"
" \"obj1\": {\n"
" \"a\": \"abc\",\n"
@@ -236,10 +236,10 @@ static void make_inline1(int pretty)
jw_end(&inline1);
}
-static char *expect_inline2 =
+static const char *expect_inline2 =
"[[1,2],[3,4],{\"a\":\"abc\"}]";
-static char *pretty_inline2 =
+static const char *pretty_inline2 =
("[\n"
" [\n"
" 1,\n"
diff --git a/t/helper/test-lazy-init-name-hash.c b/t/helper/test-lazy-init-name-hash.c
index 187a115d57..40f5df4412 100644
--- a/t/helper/test-lazy-init-name-hash.c
+++ b/t/helper/test-lazy-init-name-hash.c
@@ -1,4 +1,5 @@
-#define USE_THE_INDEX_VARIABLE
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "test-tool.h"
#include "environment.h"
#include "name-hash.h"
@@ -40,22 +41,22 @@ static void dump_run(void)
repo_read_index(the_repository);
if (single) {
- test_lazy_init_name_hash(&the_index, 0);
+ test_lazy_init_name_hash(the_repository->index, 0);
} else {
- int nr_threads_used = test_lazy_init_name_hash(&the_index, 1);
+ int nr_threads_used = test_lazy_init_name_hash(the_repository->index, 1);
if (!nr_threads_used)
die("non-threaded code path used");
}
- hashmap_for_each_entry(&the_index.dir_hash, &iter_dir, dir,
+ hashmap_for_each_entry(&the_repository->index->dir_hash, &iter_dir, dir,
ent /* member name */)
printf("dir %08x %7d %s\n", dir->ent.hash, dir->nr, dir->name);
- hashmap_for_each_entry(&the_index.name_hash, &iter_cache, ce,
+ hashmap_for_each_entry(&the_repository->index->name_hash, &iter_cache, ce,
ent /* member name */)
printf("name %08x %s\n", ce->ent.hash, ce->name);
- discard_index(&the_index);
+ discard_index(the_repository->index);
}
/*
@@ -74,7 +75,7 @@ static uint64_t time_runs(int try_threaded)
t0 = getnanotime();
repo_read_index(the_repository);
t1 = getnanotime();
- nr_threads_used = test_lazy_init_name_hash(&the_index, try_threaded);
+ nr_threads_used = test_lazy_init_name_hash(the_repository->index, try_threaded);
t2 = getnanotime();
sum += (t2 - t1);
@@ -86,16 +87,16 @@ static uint64_t time_runs(int try_threaded)
printf("%f %f %d multi %d\n",
((double)(t1 - t0))/1000000000,
((double)(t2 - t1))/1000000000,
- the_index.cache_nr,
+ the_repository->index->cache_nr,
nr_threads_used);
else
printf("%f %f %d single\n",
((double)(t1 - t0))/1000000000,
((double)(t2 - t1))/1000000000,
- the_index.cache_nr);
+ the_repository->index->cache_nr);
fflush(stdout);
- discard_index(&the_index);
+ discard_index(the_repository->index);
}
avg = sum / count;
@@ -120,8 +121,8 @@ static void analyze_run(void)
int nr;
repo_read_index(the_repository);
- cache_nr_limit = the_index.cache_nr;
- discard_index(&the_index);
+ cache_nr_limit = the_repository->index->cache_nr;
+ discard_index(the_repository->index);
nr = analyze;
while (1) {
@@ -135,22 +136,22 @@ static void analyze_run(void)
for (i = 0; i < count; i++) {
repo_read_index(the_repository);
- the_index.cache_nr = nr; /* cheap truncate of index */
+ the_repository->index->cache_nr = nr; /* cheap truncate of index */
t1s = getnanotime();
- test_lazy_init_name_hash(&the_index, 0);
+ test_lazy_init_name_hash(the_repository->index, 0);
t2s = getnanotime();
sum_single += (t2s - t1s);
- the_index.cache_nr = cache_nr_limit;
- discard_index(&the_index);
+ the_repository->index->cache_nr = cache_nr_limit;
+ discard_index(the_repository->index);
repo_read_index(the_repository);
- the_index.cache_nr = nr; /* cheap truncate of index */
+ the_repository->index->cache_nr = nr; /* cheap truncate of index */
t1m = getnanotime();
- nr_threads_used = test_lazy_init_name_hash(&the_index, 1);
+ nr_threads_used = test_lazy_init_name_hash(the_repository->index, 1);
t2m = getnanotime();
sum_multi += (t2m - t1m);
- the_index.cache_nr = cache_nr_limit;
- discard_index(&the_index);
+ the_repository->index->cache_nr = cache_nr_limit;
+ discard_index(the_repository->index);
if (!nr_threads_used)
printf(" [size %8d] [single %f] non-threaded code path used\n",
diff --git a/t/helper/test-match-trees.c b/t/helper/test-match-trees.c
index d0db5ff26f..e0e2048320 100644
--- a/t/helper/test-match-trees.c
+++ b/t/helper/test-match-trees.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "test-tool.h"
#include "hex.h"
#include "match-trees.h"
diff --git a/t/helper/test-oid-array.c b/t/helper/test-oid-array.c
index aafe398ef0..076b849cbf 100644
--- a/t/helper/test-oid-array.c
+++ b/t/helper/test-oid-array.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "test-tool.h"
#include "hex.h"
#include "oid-array.h"
@@ -17,6 +19,8 @@ int cmd__oid_array(int argc UNUSED, const char **argv UNUSED)
int nongit_ok;
setup_git_directory_gently(&nongit_ok);
+ if (nongit_ok)
+ repo_set_hash_algo(the_repository, GIT_HASH_SHA1);
while (strbuf_getline(&line, stdin) != EOF) {
const char *arg;
diff --git a/t/helper/test-oidmap.c b/t/helper/test-oidmap.c
deleted file mode 100644
index bd30244a54..0000000000
--- a/t/helper/test-oidmap.c
+++ /dev/null
@@ -1,123 +0,0 @@
-#include "test-tool.h"
-#include "hex.h"
-#include "object-name.h"
-#include "oidmap.h"
-#include "repository.h"
-#include "setup.h"
-#include "strbuf.h"
-#include "string-list.h"
-
-/* key is an oid and value is a name (could be a refname for example) */
-struct test_entry {
- struct oidmap_entry entry;
- char name[FLEX_ARRAY];
-};
-
-#define DELIM " \t\r\n"
-
-/*
- * Read stdin line by line and print result of commands to stdout:
- *
- * hash oidkey -> sha1hash(oidkey)
- * put oidkey namevalue -> NULL / old namevalue
- * get oidkey -> NULL / namevalue
- * remove oidkey -> NULL / old namevalue
- * iterate -> oidkey1 namevalue1\noidkey2 namevalue2\n...
- *
- */
-int cmd__oidmap(int argc UNUSED, const char **argv UNUSED)
-{
- struct string_list parts = STRING_LIST_INIT_NODUP;
- struct strbuf line = STRBUF_INIT;
- struct oidmap map = OIDMAP_INIT;
-
- setup_git_directory();
-
- /* init oidmap */
- oidmap_init(&map, 0);
-
- /* process commands from stdin */
- while (strbuf_getline(&line, stdin) != EOF) {
- char *cmd, *p1, *p2;
- struct test_entry *entry;
- struct object_id oid;
-
- /* break line into command and up to two parameters */
- string_list_setlen(&parts, 0);
- string_list_split_in_place(&parts, line.buf, DELIM, 2);
- string_list_remove_empty_items(&parts, 0);
-
- /* ignore empty lines */
- if (!parts.nr)
- continue;
- if (!*parts.items[0].string || *parts.items[0].string == '#')
- continue;
-
- cmd = parts.items[0].string;
- p1 = parts.nr >= 1 ? parts.items[1].string : NULL;
- p2 = parts.nr >= 2 ? parts.items[2].string : NULL;
-
- if (!strcmp("put", cmd) && p1 && p2) {
-
- if (repo_get_oid(the_repository, p1, &oid)) {
- printf("Unknown oid: %s\n", p1);
- continue;
- }
-
- /* create entry with oid_key = p1, name_value = p2 */
- FLEX_ALLOC_STR(entry, name, p2);
- oidcpy(&entry->entry.oid, &oid);
-
- /* add / replace entry */
- entry = oidmap_put(&map, entry);
-
- /* print and free replaced entry, if any */
- puts(entry ? entry->name : "NULL");
- free(entry);
-
- } else if (!strcmp("get", cmd) && p1) {
-
- if (repo_get_oid(the_repository, p1, &oid)) {
- printf("Unknown oid: %s\n", p1);
- continue;
- }
-
- /* lookup entry in oidmap */
- entry = oidmap_get(&map, &oid);
-
- /* print result */
- puts(entry ? entry->name : "NULL");
-
- } else if (!strcmp("remove", cmd) && p1) {
-
- if (repo_get_oid(the_repository, p1, &oid)) {
- printf("Unknown oid: %s\n", p1);
- continue;
- }
-
- /* remove entry from oidmap */
- entry = oidmap_remove(&map, &oid);
-
- /* print result and free entry*/
- puts(entry ? entry->name : "NULL");
- free(entry);
-
- } else if (!strcmp("iterate", cmd)) {
-
- struct oidmap_iter iter;
- oidmap_iter_init(&map, &iter);
- while ((entry = oidmap_iter_next(&iter)))
- printf("%s %s\n", oid_to_hex(&entry->entry.oid), entry->name);
-
- } else {
-
- printf("Unknown command %s\n", cmd);
-
- }
- }
-
- string_list_clear(&parts, 0);
- strbuf_release(&line);
- oidmap_free(&map, 1);
- return 0;
-}
diff --git a/t/helper/test-oidtree.c b/t/helper/test-oidtree.c
deleted file mode 100644
index c7a1d4c642..0000000000
--- a/t/helper/test-oidtree.c
+++ /dev/null
@@ -1,54 +0,0 @@
-#include "test-tool.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)
-{
- puts(oid_to_hex(oid));
- return CB_CONTINUE;
-}
-
-int cmd__oidtree(int argc UNUSED, const char **argv UNUSED)
-{
- struct oidtree ot;
- struct strbuf line = STRBUF_INIT;
- int nongit_ok;
- int algo = GIT_HASH_UNKNOWN;
-
- oidtree_init(&ot);
- setup_git_directory_gently(&nongit_ok);
-
- while (strbuf_getline(&line, stdin) != EOF) {
- const char *arg;
- struct object_id oid;
-
- if (skip_prefix(line.buf, "insert ", &arg)) {
- if (get_oid_hex_any(arg, &oid) == GIT_HASH_UNKNOWN)
- die("insert not a hexadecimal oid: %s", arg);
- algo = oid.algo;
- oidtree_insert(&ot, &oid);
- } else if (skip_prefix(line.buf, "contains ", &arg)) {
- if (get_oid_hex(arg, &oid))
- die("contains not a hexadecimal oid: %s", arg);
- printf("%d\n", oidtree_contains(&ot, &oid));
- } else if (skip_prefix(line.buf, "each ", &arg)) {
- char buf[GIT_MAX_HEXSZ + 1] = { '0' };
- memset(&oid, 0, sizeof(oid));
- memcpy(buf, arg, strlen(arg));
- buf[hash_algos[algo].hexsz] = '\0';
- get_oid_hex_any(buf, &oid);
- oid.algo = algo;
- oidtree_each(&ot, &oid, strlen(arg), print_oid, NULL);
- } else if (!strcmp(line.buf, "clear")) {
- oidtree_clear(&ot);
- } else {
- die("unknown command: %s", line.buf);
- }
- }
-
- strbuf_release(&line);
-
- return 0;
-}
diff --git a/t/helper/test-pack-mtimes.c b/t/helper/test-pack-mtimes.c
index 67a964ef90..f8f9afbb5b 100644
--- a/t/helper/test-pack-mtimes.c
+++ b/t/helper/test-pack-mtimes.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "test-tool.h"
#include "hex.h"
#include "strbuf.h"
diff --git a/t/helper/test-parse-options.c b/t/helper/test-parse-options.c
index ded8116cc5..5250913d99 100644
--- a/t/helper/test-parse-options.c
+++ b/t/helper/test-parse-options.c
@@ -207,6 +207,7 @@ int cmd__parse_options(int argc, const char **argv)
expect.strdup_strings = 1;
string_list_clear(&expect, 0);
string_list_clear(&list, 0);
+ free(file);
return ret;
}
diff --git a/t/helper/test-partial-clone.c b/t/helper/test-partial-clone.c
index 910a128614..0ead529167 100644
--- a/t/helper/test-partial-clone.c
+++ b/t/helper/test-partial-clone.c
@@ -21,7 +21,7 @@ static void object_info(const char *gitdir, const char *oid_hex)
if (repo_init(&r, gitdir, NULL))
die("could not init repo");
- if (parse_oid_hex(oid_hex, &oid, &p))
+ if (parse_oid_hex_algop(oid_hex, &oid, &p, r.hash_algo))
die("could not parse oid");
if (oid_object_info_extended(&r, &oid, &oi, 0))
die("could not obtain object info");
diff --git a/t/helper/test-path-utils.c b/t/helper/test-path-utils.c
index 70396fa384..bf0e23ed50 100644
--- a/t/helper/test-path-utils.c
+++ b/t/helper/test-path-utils.c
@@ -7,6 +7,7 @@
#include "string-list.h"
#include "trace.h"
#include "utf8.h"
+#include "copy.h"
/*
* A "string_list_each_func_t" function that normalizes an entry from
diff --git a/t/helper/test-proc-receive.c b/t/helper/test-proc-receive.c
index f30022d222..29361c7aab 100644
--- a/t/helper/test-proc-receive.c
+++ b/t/helper/test-proc-receive.c
@@ -3,8 +3,8 @@
#include "hex.h"
#include "parse-options.h"
#include "pkt-line.h"
-#include "setup.h"
#include "sigchain.h"
+#include "string-list.h"
static const char *proc_receive_usage[] = {
"test-tool proc-receive [<options>]",
@@ -92,9 +92,9 @@ static void proc_receive_read_commands(struct packet_reader *reader,
if (die_read_commands)
die("die with the --die-read-commands option");
- if (parse_oid_hex(reader->line, &old_oid, &p) ||
+ if (parse_oid_hex_any(reader->line, &old_oid, &p) == GIT_HASH_UNKNOWN ||
*p++ != ' ' ||
- parse_oid_hex(p, &new_oid, &p) ||
+ parse_oid_hex_any(p, &new_oid, &p) == GIT_HASH_UNKNOWN ||
*p++ != ' ')
die("protocol error: expected 'old new ref', got '%s'",
reader->line);
@@ -128,7 +128,6 @@ static void proc_receive_read_push_options(struct packet_reader *reader,
int cmd__proc_receive(int argc, const char **argv)
{
- int nongit_ok = 0;
struct packet_reader reader;
struct command *commands = NULL;
struct string_list push_options = STRING_LIST_INIT_DUP;
@@ -154,8 +153,6 @@ int cmd__proc_receive(int argc, const char **argv)
OPT_END()
};
- setup_git_directory_gently(&nongit_ok);
-
argc = parse_options(argc, argv, "test-tools", options, proc_receive_usage, 0);
if (argc > 0)
usage_msg_opt("Too many arguments.", proc_receive_usage, options);
diff --git a/t/helper/test-reach.c b/t/helper/test-reach.c
index 1e3b431e3e..5dd374379c 100644
--- a/t/helper/test-reach.c
+++ b/t/helper/test-reach.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "test-tool.h"
#include "commit.h"
#include "commit-reach.h"
@@ -62,7 +64,7 @@ int cmd__reach(int ac, const char **av)
die("failed to resolve %s", buf.buf + 2);
orig = parse_object(r, &oid);
- peeled = deref_tag_noverify(orig);
+ peeled = deref_tag_noverify(the_repository, orig);
if (!peeled)
die("failed to load commit for input %s resulting in oid %s\n",
diff --git a/t/helper/test-read-cache.c b/t/helper/test-read-cache.c
index 1acd362346..d285c656bd 100644
--- a/t/helper/test-read-cache.c
+++ b/t/helper/test-read-cache.c
@@ -1,4 +1,5 @@
-#define USE_THE_INDEX_VARIABLE
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "test-tool.h"
#include "config.h"
#include "read-cache-ll.h"
@@ -10,7 +11,7 @@ int cmd__read_cache(int argc, const char **argv)
int i, cnt = 1;
const char *name = NULL;
- initialize_the_repository();
+ initialize_repository(the_repository);
if (argc > 1 && skip_prefix(argv[1], "--print-and-refresh=", &name)) {
argc--;
@@ -27,16 +28,16 @@ int cmd__read_cache(int argc, const char **argv)
if (name) {
int pos;
- refresh_index(&the_index, REFRESH_QUIET,
+ refresh_index(the_repository->index, REFRESH_QUIET,
NULL, NULL, NULL);
- pos = index_name_pos(&the_index, name, strlen(name));
+ pos = index_name_pos(the_repository->index, name, strlen(name));
if (pos < 0)
die("%s not in index", name);
printf("%s is%s up to date\n", name,
- ce_uptodate(the_index.cache[pos]) ? "" : " not");
+ ce_uptodate(the_repository->index->cache[pos]) ? "" : " not");
write_file(name, "%d\n", i);
}
- discard_index(&the_index);
+ discard_index(the_repository->index);
}
return 0;
}
diff --git a/t/helper/test-read-graph.c b/t/helper/test-read-graph.c
index 8c7a83f578..9018c9f541 100644
--- a/t/helper/test-read-graph.c
+++ b/t/helper/test-read-graph.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "test-tool.h"
#include "commit-graph.h"
#include "repository.h"
@@ -5,20 +7,8 @@
#include "bloom.h"
#include "setup.h"
-int cmd__read_graph(int argc UNUSED, const char **argv UNUSED)
+static void dump_graph_info(struct commit_graph *graph)
{
- struct commit_graph *graph = NULL;
- struct object_directory *odb;
-
- setup_git_directory();
- odb = the_repository->objects->odb;
-
- prepare_repo_settings(the_repository);
-
- graph = read_commit_graph_one(the_repository, odb);
- if (!graph)
- return 1;
-
printf("header: %08x %d %d %d %d\n",
ntohl(*(uint32_t*)graph->data),
*(unsigned char*)(graph->data + 4),
@@ -57,8 +47,57 @@ int cmd__read_graph(int argc UNUSED, const char **argv UNUSED)
if (graph->topo_levels)
printf(" topo_levels");
printf("\n");
+}
+
+static void dump_graph_bloom_filters(struct commit_graph *graph)
+{
+ uint32_t i;
+
+ for (i = 0; i < graph->num_commits + graph->num_commits_in_base; i++) {
+ struct bloom_filter filter = { 0 };
+ size_t j;
+
+ if (load_bloom_filter_from_graph(graph, &filter, i) < 0) {
+ fprintf(stderr, "missing Bloom filter for graph "
+ "position %"PRIu32"\n", i);
+ continue;
+ }
+
+ for (j = 0; j < filter.len; j++)
+ printf("%02x", filter.data[j]);
+ if (filter.len)
+ printf("\n");
+ }
+}
+
+int cmd__read_graph(int argc, const char **argv)
+{
+ struct commit_graph *graph = NULL;
+ struct object_directory *odb;
+ int ret = 0;
+
+ setup_git_directory();
+ odb = the_repository->objects->odb;
+
+ prepare_repo_settings(the_repository);
+
+ graph = read_commit_graph_one(the_repository, odb);
+ if (!graph) {
+ ret = 1;
+ goto done;
+ }
+
+ if (argc <= 1)
+ dump_graph_info(graph);
+ else if (!strcmp(argv[1], "bloom-filters"))
+ dump_graph_bloom_filters(graph);
+ else {
+ fprintf(stderr, "unknown sub-command: '%s'\n", argv[1]);
+ ret = 1;
+ }
+done:
UNLEAK(graph);
- return 0;
+ return ret;
}
diff --git a/t/helper/test-read-midx.c b/t/helper/test-read-midx.c
index 4acae41bb9..83effc2b5f 100644
--- a/t/helper/test-read-midx.c
+++ b/t/helper/test-read-midx.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "test-tool.h"
#include "hex.h"
#include "midx.h"
diff --git a/t/helper/test-ref-store.c b/t/helper/test-ref-store.c
index 82bbf6e2e6..637b8b294e 100644
--- a/t/helper/test-ref-store.c
+++ b/t/helper/test-ref-store.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "test-tool.h"
#include "hex.h"
#include "refs.h"
@@ -82,7 +84,7 @@ static const char **get_store(const char **argv, struct ref_store **refs)
add_to_alternates_memory(sb.buf);
strbuf_release(&sb);
- *refs = get_submodule_ref_store(gitdir);
+ *refs = repo_get_submodule_ref_store(the_repository, gitdir);
} else if (skip_prefix(argv[0], "worktree:", &gitdir)) {
struct worktree **p, **worktrees = get_worktrees();
@@ -118,7 +120,7 @@ static int cmd_create_symref(struct ref_store *refs, const char **argv)
const char *target = notnull(*argv++, "target");
const char *logmsg = *argv++;
- return refs_create_symref(refs, refname, target, logmsg);
+ return refs_update_symref(refs, refname, target, logmsg);
}
static struct flag_definition transaction_flags[] = {
@@ -126,6 +128,7 @@ static struct flag_definition transaction_flags[] = {
FLAG_DEF(REF_FORCE_CREATE_REFLOG),
FLAG_DEF(REF_SKIP_OID_VERIFICATION),
FLAG_DEF(REF_SKIP_REFNAME_VERIFICATION),
+ FLAG_DEF(REF_SKIP_CREATE_REFLOG),
{ NULL, 0 }
};
diff --git a/t/helper/test-reftable.c b/t/helper/test-reftable.c
index 00237ef0d9..aa6538a8da 100644
--- a/t/helper/test-reftable.c
+++ b/t/helper/test-reftable.c
@@ -5,15 +5,12 @@
int cmd__reftable(int argc, const char **argv)
{
/* test from simple to complex. */
- basics_test_main(argc, argv);
- record_test_main(argc, argv);
block_test_main(argc, argv);
tree_test_main(argc, argv);
pq_test_main(argc, argv);
readwrite_test_main(argc, argv);
merged_test_main(argc, argv);
stack_test_main(argc, argv);
- refname_test_main(argc, argv);
return 0;
}
diff --git a/t/helper/test-regex.c b/t/helper/test-regex.c
index 80042eafc2..366bd70976 100644
--- a/t/helper/test-regex.c
+++ b/t/helper/test-regex.c
@@ -20,8 +20,8 @@ static struct reg_flag reg_flags[] = {
static int test_regex_bug(void)
{
- char *pat = "[^={} \t]+";
- char *str = "={}\nfred";
+ const char *pat = "[^={} \t]+";
+ const char *str = "={}\nfred";
regex_t r;
regmatch_t m[1];
diff --git a/t/helper/test-repository.c b/t/helper/test-repository.c
index 0c7c5aa4dd..c6a074df3d 100644
--- a/t/helper/test-repository.c
+++ b/t/helper/test-repository.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "test-tool.h"
#include "commit-graph.h"
#include "commit.h"
diff --git a/t/helper/test-revision-walking.c b/t/helper/test-revision-walking.c
index f346951bc2..071f5bd1e2 100644
--- a/t/helper/test-revision-walking.c
+++ b/t/helper/test-revision-walking.c
@@ -8,6 +8,8 @@
* published by the Free Software Foundation.
*/
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "test-tool.h"
#include "commit.h"
#include "diff.h"
diff --git a/t/helper/test-rot13-filter.c b/t/helper/test-rot13-filter.c
index f8d564c622..7e1d9e0ee4 100644
--- a/t/helper/test-rot13-filter.c
+++ b/t/helper/test-rot13-filter.c
@@ -136,7 +136,7 @@ static void free_delay_entries(void)
strmap_clear(&delay, 0);
}
-static void add_delay_entry(char *pathname, int count, int requested)
+static void add_delay_entry(const char *pathname, int count, int requested)
{
struct delay_entry *entry = xcalloc(1, sizeof(*entry));
entry->count = count;
@@ -189,7 +189,8 @@ static void reply_list_available_blobs_cmd(void)
static void command_loop(void)
{
for (;;) {
- char *buf, *output;
+ char *buf;
+ const char *output;
char *pathname;
struct delay_entry *entry;
struct strbuf input = STRBUF_INIT;
diff --git a/t/helper/test-run-command.c b/t/helper/test-run-command.c
index c0ed8722c8..61eb1175fe 100644
--- a/t/helper/test-run-command.c
+++ b/t/helper/test-run-command.c
@@ -65,6 +65,7 @@ struct testsuite {
struct string_list tests, failed;
int next;
int quiet, immediate, verbose, verbose_log, trace, write_junit_xml;
+ const char *shell_path;
};
#define TESTSUITE_INIT { \
.tests = STRING_LIST_INIT_DUP, \
@@ -80,7 +81,9 @@ static int next_test(struct child_process *cp, struct strbuf *err, void *cb,
return 0;
test = suite->tests.items[suite->next++].string;
- strvec_pushl(&cp->args, "sh", test, NULL);
+ if (suite->shell_path)
+ strvec_push(&cp->args, suite->shell_path);
+ strvec_push(&cp->args, test);
if (suite->quiet)
strvec_push(&cp->args, "--quiet");
if (suite->immediate)
@@ -155,6 +158,8 @@ static int testsuite(int argc, const char **argv)
.task_finished = test_finished,
.data = &suite,
};
+ struct strbuf progpath = STRBUF_INIT;
+ size_t path_prefix_len;
argc = parse_options(argc, argv, NULL, options,
testsuite_usage, PARSE_OPT_STOP_AT_NON_OPTION);
@@ -162,26 +167,36 @@ static int testsuite(int argc, const char **argv)
if (max_jobs <= 0)
max_jobs = online_cpus();
+ /*
+ * If we run without a shell, execute the programs directly from CWD.
+ */
+ suite.shell_path = getenv("TEST_SHELL_PATH");
+ if (!suite.shell_path)
+ strbuf_addstr(&progpath, "./");
+ path_prefix_len = progpath.len;
+
dir = opendir(".");
if (!dir)
die("Could not open the current directory");
while ((d = readdir(dir))) {
const char *p = d->d_name;
- if (*p != 't' || !isdigit(p[1]) || !isdigit(p[2]) ||
- !isdigit(p[3]) || !isdigit(p[4]) || p[5] != '-' ||
- !ends_with(p, ".sh"))
+ if (!strcmp(p, ".") || !strcmp(p, ".."))
continue;
/* No pattern: match all */
if (!argc) {
- string_list_append(&suite.tests, p);
+ strbuf_setlen(&progpath, path_prefix_len);
+ strbuf_addstr(&progpath, p);
+ string_list_append(&suite.tests, progpath.buf);
continue;
}
for (i = 0; i < argc; i++)
if (!wildmatch(argv[i], p, 0)) {
- string_list_append(&suite.tests, p);
+ strbuf_setlen(&progpath, path_prefix_len);
+ strbuf_addstr(&progpath, p);
+ string_list_append(&suite.tests, progpath.buf);
break;
}
}
@@ -208,6 +223,7 @@ static int testsuite(int argc, const char **argv)
string_list_clear(&suite.tests, 0);
string_list_clear(&suite.failed, 0);
+ strbuf_release(&progpath);
return ret;
}
diff --git a/t/helper/test-scrap-cache-tree.c b/t/helper/test-scrap-cache-tree.c
index 0a816a96e2..64fff6e9e3 100644
--- a/t/helper/test-scrap-cache-tree.c
+++ b/t/helper/test-scrap-cache-tree.c
@@ -1,4 +1,5 @@
-#define USE_THE_INDEX_VARIABLE
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "test-tool.h"
#include "lockfile.h"
#include "read-cache-ll.h"
@@ -15,9 +16,9 @@ int cmd__scrap_cache_tree(int ac UNUSED, const char **av UNUSED)
repo_hold_locked_index(the_repository, &index_lock, LOCK_DIE_ON_ERROR);
if (repo_read_index(the_repository) < 0)
die("unable to read index file");
- cache_tree_free(&the_index.cache_tree);
- the_index.cache_tree = NULL;
- if (write_locked_index(&the_index, &index_lock, COMMIT_LOCK))
+ cache_tree_free(&the_repository->index->cache_tree);
+ the_repository->index->cache_tree = NULL;
+ if (write_locked_index(the_repository->index, &index_lock, COMMIT_LOCK))
die("unable to write index file");
return 0;
}
diff --git a/t/helper/test-sha1.c b/t/helper/test-sha1.c
index dcb7f6c003..e60d000c03 100644
--- a/t/helper/test-sha1.c
+++ b/t/helper/test-sha1.c
@@ -1,5 +1,5 @@
#include "test-tool.h"
-#include "hash-ll.h"
+#include "hash.h"
int cmd__sha1(int ac, const char **av)
{
diff --git a/t/helper/test-sha256.c b/t/helper/test-sha256.c
index 08cf149001..2fb20438f3 100644
--- a/t/helper/test-sha256.c
+++ b/t/helper/test-sha256.c
@@ -1,5 +1,5 @@
#include "test-tool.h"
-#include "hash-ll.h"
+#include "hash.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
deleted file mode 100644
index d8473cf2fc..0000000000
--- a/t/helper/test-strcmp-offset.c
+++ /dev/null
@@ -1,23 +0,0 @@
-#include "test-tool.h"
-#include "read-cache-ll.h"
-
-int cmd__strcmp_offset(int argc UNUSED, const char **argv)
-{
- int result;
- size_t offset;
-
- if (!argv[1] || !argv[2])
- die("usage: %s <string1> <string2>", argv[0]);
-
- result = strcmp_offset(argv[1], argv[2], &offset);
-
- /*
- * Because different CRTs behave differently, only rely on signs
- * of the result values.
- */
- result = (result < 0 ? -1 :
- result > 0 ? 1 :
- 0);
- printf("%d %"PRIuMAX"\n", result, (uintmax_t)offset);
- return 0;
-}
diff --git a/t/helper/test-submodule-config.c b/t/helper/test-submodule-config.c
index 9df2f03ac8..cbe93f2f9e 100644
--- a/t/helper/test-submodule-config.c
+++ b/t/helper/test-submodule-config.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "test-tool.h"
#include "config.h"
#include "hash.h"
@@ -44,7 +46,7 @@ int cmd__submodule_config(int argc, const char **argv)
path_or_name = arg[1];
if (commit[0] == '\0')
- oidclr(&commit_oid);
+ oidclr(&commit_oid, the_repository->hash_algo);
else if (repo_get_oid(the_repository, commit, &commit_oid) < 0)
die_usage(argc, argv, "Commit not found.");
diff --git a/t/helper/test-submodule-nested-repo-config.c b/t/helper/test-submodule-nested-repo-config.c
index ecd40ded99..6ca069ce63 100644
--- a/t/helper/test-submodule-nested-repo-config.c
+++ b/t/helper/test-submodule-nested-repo-config.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "test-tool.h"
#include "repository.h"
#include "setup.h"
diff --git a/t/helper/test-submodule.c b/t/helper/test-submodule.c
index 7197969a08..22e518d229 100644
--- a/t/helper/test-submodule.c
+++ b/t/helper/test-submodule.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "test-tool.h"
#include "test-tool-utils.h"
#include "parse-options.h"
diff --git a/t/helper/test-tool.c b/t/helper/test-tool.c
index 80a946b847..da3e69128a 100644
--- a/t/helper/test-tool.c
+++ b/t/helper/test-tool.c
@@ -29,7 +29,7 @@ static struct test_cmd cmds[] = {
{ "dump-split-index", cmd__dump_split_index },
{ "dump-untracked-cache", cmd__dump_untracked_cache },
{ "env-helper", cmd__env_helper },
- { "example-decorate", cmd__example_decorate },
+ { "example-tap", cmd__example_tap },
{ "find-pack", cmd__find_pack },
{ "fsmonitor-client", cmd__fsmonitor_client },
{ "genrandom", cmd__genrandom },
@@ -44,8 +44,6 @@ static struct test_cmd cmds[] = {
{ "mergesort", cmd__mergesort },
{ "mktemp", cmd__mktemp },
{ "oid-array", cmd__oid_array },
- { "oidmap", cmd__oidmap },
- { "oidtree", cmd__oidtree },
{ "online-cpus", cmd__online_cpus },
{ "pack-mtimes", cmd__pack_mtimes },
{ "parse-options", cmd__parse_options },
@@ -77,7 +75,6 @@ static struct test_cmd cmds[] = {
{ "sha256", cmd__sha256 },
{ "sigchain", cmd__sigchain },
{ "simple-ipc", cmd__simple_ipc },
- { "strcmp-offset", cmd__strcmp_offset },
{ "string-list", cmd__string_list },
{ "submodule", cmd__submodule },
{ "submodule-config", cmd__submodule_config },
diff --git a/t/helper/test-tool.h b/t/helper/test-tool.h
index 2808b92419..642a34578c 100644
--- a/t/helper/test-tool.h
+++ b/t/helper/test-tool.h
@@ -23,7 +23,7 @@ int cmd__dump_split_index(int argc, const char **argv);
int cmd__dump_untracked_cache(int argc, const char **argv);
int cmd__dump_reftable(int argc, const char **argv);
int cmd__env_helper(int argc, const char **argv);
-int cmd__example_decorate(int argc, const char **argv);
+int cmd__example_tap(int argc, const char **argv);
int cmd__find_pack(int argc, const char **argv);
int cmd__fsmonitor_client(int argc, const char **argv);
int cmd__genrandom(int argc, const char **argv);
@@ -37,8 +37,6 @@ int cmd__lazy_init_name_hash(int argc, const char **argv);
int cmd__match_trees(int argc, const char **argv);
int cmd__mergesort(int argc, const char **argv);
int cmd__mktemp(int argc, const char **argv);
-int cmd__oidmap(int argc, const char **argv);
-int cmd__oidtree(int argc, const char **argv);
int cmd__online_cpus(int argc, const char **argv);
int cmd__pack_mtimes(int argc, const char **argv);
int cmd__parse_options(int argc, const char **argv);
@@ -70,7 +68,6 @@ int cmd__oid_array(int argc, const char **argv);
int cmd__sha256(int argc, const char **argv);
int cmd__sigchain(int argc, const char **argv);
int cmd__simple_ipc(int argc, const char **argv);
-int cmd__strcmp_offset(int argc, const char **argv);
int cmd__string_list(int argc, const char **argv);
int cmd__submodule(int argc, const char **argv);
int cmd__submodule_config(int argc, const char **argv);
diff --git a/t/helper/test-trace2.c b/t/helper/test-trace2.c
index 1adac29a57..cd955ec63e 100644
--- a/t/helper/test-trace2.c
+++ b/t/helper/test-trace2.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "test-tool.h"
#include "strvec.h"
#include "run-command.h"
diff --git a/t/helper/test-write-cache.c b/t/helper/test-write-cache.c
index f084034d38..b37dd2c5d6 100644
--- a/t/helper/test-write-cache.c
+++ b/t/helper/test-write-cache.c
@@ -1,4 +1,5 @@
-#define USE_THE_INDEX_VARIABLE
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "test-tool.h"
#include "lockfile.h"
#include "read-cache-ll.h"
@@ -16,7 +17,7 @@ int cmd__write_cache(int argc, const char **argv)
for (i = 0; i < cnt; i++) {
repo_hold_locked_index(the_repository, &index_lock,
LOCK_DIE_ON_ERROR);
- if (write_locked_index(&the_index, &index_lock, COMMIT_LOCK))
+ if (write_locked_index(the_repository->index, &index_lock, COMMIT_LOCK))
die("unable to write index file");
}
diff --git a/t/lib-bundle-uri-protocol.sh b/t/lib-bundle-uri-protocol.sh
index a4a1af8d02..de09b6b02e 100644
--- a/t/lib-bundle-uri-protocol.sh
+++ b/t/lib-bundle-uri-protocol.sh
@@ -18,7 +18,7 @@ git)
start_git_daemon --export-all --enable=receive-pack
BUNDLE_URI_PARENT="$GIT_DAEMON_DOCUMENT_ROOT_PATH/parent"
BUNDLE_URI_REPO_URI="$GIT_DAEMON_URL/parent"
- BUNDLE_URI_BUNDLE_URI="https://example.com/fake.bdl"
+ BUNDLE_URI_BUNDLE_URI="$BUNDLE_URI_REPO_URI/fake.bdl"
test_set_prereq BUNDLE_URI_GIT
;;
http)
@@ -26,7 +26,7 @@ http)
start_httpd
BUNDLE_URI_PARENT="$HTTPD_DOCUMENT_ROOT_PATH/http_parent"
BUNDLE_URI_REPO_URI="$HTTPD_URL/smart/http_parent"
- BUNDLE_URI_BUNDLE_URI="https://example.com/fake.bdl"
+ BUNDLE_URI_BUNDLE_URI="$BUNDLE_URI_REPO_URL/fake.bdl"
test_set_prereq BUNDLE_URI_HTTP
;;
*)
diff --git a/t/lib-chunk.sh b/t/lib-chunk.sh
index a7cd9c3c6d..9f01df190b 100644
--- a/t/lib-chunk.sh
+++ b/t/lib-chunk.sh
@@ -13,5 +13,6 @@ corrupt_chunk_file () {
fn=$1; shift
perl "$TEST_DIRECTORY"/lib-chunk/corrupt-chunk-file.pl \
"$@" <"$fn" >"$fn.tmp" &&
- mv "$fn.tmp" "$fn"
+ # some vintages of macOS 'mv' fails to overwrite a read-only file.
+ mv -f "$fn.tmp" "$fn"
}
diff --git a/t/lib-credential.sh b/t/lib-credential.sh
index 44799c0d38..58b9c74060 100644
--- a/t/lib-credential.sh
+++ b/t/lib-credential.sh
@@ -538,6 +538,129 @@ helper_test_oauth_refresh_token() {
'
}
+helper_test_authtype() {
+ HELPER=$1
+
+ test_expect_success "helper ($HELPER) stores authtype and credential" '
+ check approve $HELPER <<-\EOF
+ capability[]=authtype
+ authtype=Bearer
+ credential=random-token
+ protocol=https
+ host=git.example.com
+ EOF
+ '
+
+ test_expect_success "helper ($HELPER) gets authtype and credential" '
+ check fill $HELPER <<-\EOF
+ capability[]=authtype
+ protocol=https
+ host=git.example.com
+ --
+ capability[]=authtype
+ authtype=Bearer
+ credential=random-token
+ protocol=https
+ host=git.example.com
+ --
+ EOF
+ '
+
+ test_expect_success "helper ($HELPER) stores authtype and credential with username" '
+ check approve $HELPER <<-\EOF
+ capability[]=authtype
+ authtype=Bearer
+ credential=other-token
+ protocol=https
+ host=git.example.com
+ username=foobar
+ EOF
+ '
+
+ test_expect_success "helper ($HELPER) gets authtype and credential with username" '
+ check fill $HELPER <<-\EOF
+ capability[]=authtype
+ protocol=https
+ host=git.example.com
+ username=foobar
+ --
+ capability[]=authtype
+ authtype=Bearer
+ credential=other-token
+ protocol=https
+ host=git.example.com
+ username=foobar
+ --
+ EOF
+ '
+
+ test_expect_success "helper ($HELPER) does not get authtype and credential with different username" '
+ check fill $HELPER <<-\EOF
+ capability[]=authtype
+ protocol=https
+ host=git.example.com
+ username=barbaz
+ --
+ protocol=https
+ host=git.example.com
+ username=barbaz
+ password=askpass-password
+ --
+ askpass: Password for '\''https://barbaz@git.example.com'\'':
+ EOF
+ '
+
+ test_expect_success "helper ($HELPER) does not store ephemeral authtype and credential" '
+ check approve $HELPER <<-\EOF &&
+ capability[]=authtype
+ authtype=Bearer
+ credential=git2-token
+ protocol=https
+ host=git2.example.com
+ ephemeral=1
+ EOF
+
+ check fill $HELPER <<-\EOF
+ capability[]=authtype
+ protocol=https
+ host=git2.example.com
+ --
+ protocol=https
+ host=git2.example.com
+ username=askpass-username
+ password=askpass-password
+ --
+ askpass: Username for '\''https://git2.example.com'\'':
+ askpass: Password for '\''https://askpass-username@git2.example.com'\'':
+ EOF
+ '
+
+ test_expect_success "helper ($HELPER) does not store ephemeral username and password" '
+ check approve $HELPER <<-\EOF &&
+ capability[]=authtype
+ protocol=https
+ host=git2.example.com
+ user=barbaz
+ password=secret
+ ephemeral=1
+ EOF
+
+ check fill $HELPER <<-\EOF
+ capability[]=authtype
+ protocol=https
+ host=git2.example.com
+ --
+ protocol=https
+ host=git2.example.com
+ username=askpass-username
+ password=askpass-password
+ --
+ askpass: Username for '\''https://git2.example.com'\'':
+ askpass: Password for '\''https://askpass-username@git2.example.com'\'':
+ 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-httpd/nph-custom-auth.sh b/t/lib-httpd/nph-custom-auth.sh
index f5345e775e..d408d2caad 100644
--- a/t/lib-httpd/nph-custom-auth.sh
+++ b/t/lib-httpd/nph-custom-auth.sh
@@ -19,21 +19,30 @@ CHALLENGE_FILE=custom-auth.challenge
#
if test -n "$HTTP_AUTHORIZATION" && \
- grep -Fqsx "${HTTP_AUTHORIZATION}" "$VALID_CREDS_FILE"
+ grep -Fqs "creds=${HTTP_AUTHORIZATION}" "$VALID_CREDS_FILE"
then
+ idno=$(grep -F "creds=${HTTP_AUTHORIZATION}" "$VALID_CREDS_FILE" | sed -e 's/^id=\([a-z0-9-][a-z0-9-]*\) .*$/\1/')
+ status=$(sed -ne "s/^id=$idno.*status=\\([0-9][0-9][0-9]\\).*\$/\\1/p" "$CHALLENGE_FILE" | head -n1)
# Note that although git-http-backend returns a status line, it
# does so using a CGI 'Status' header. Because this script is an
# No Parsed Headers (NPH) script, we must return a real HTTP
# status line.
# This is only a test script, so we don't bother to check for
# the actual status from git-http-backend and always return 200.
- echo 'HTTP/1.1 200 OK'
- exec "$GIT_EXEC_PATH"/git-http-backend
+ echo "HTTP/1.1 $status Nonspecific Reason Phrase"
+ if test "$status" -eq 200
+ then
+ exec "$GIT_EXEC_PATH"/git-http-backend
+ else
+ sed -ne "s/^id=$idno.*response=//p" "$CHALLENGE_FILE"
+ echo
+ exit
+ fi
fi
echo 'HTTP/1.1 401 Authorization Required'
if test -f "$CHALLENGE_FILE"
then
- cat "$CHALLENGE_FILE"
+ sed -ne 's/^id=default.*response=//p' "$CHALLENGE_FILE"
fi
echo
diff --git a/t/perf/p5333-pseudo-merge-bitmaps.sh b/t/perf/p5333-pseudo-merge-bitmaps.sh
new file mode 100755
index 0000000000..2e8b1d2635
--- /dev/null
+++ b/t/perf/p5333-pseudo-merge-bitmaps.sh
@@ -0,0 +1,32 @@
+#!/bin/sh
+
+test_description='pseudo-merge bitmaps'
+. ./perf-lib.sh
+
+test_perf_large_repo
+
+test_expect_success 'setup' '
+ git \
+ -c bitmapPseudoMerge.all.pattern="refs/" \
+ -c bitmapPseudoMerge.all.threshold=now \
+ -c bitmapPseudoMerge.all.stableThreshold=never \
+ -c bitmapPseudoMerge.all.maxMerges=64 \
+ -c pack.writeBitmapLookupTable=true \
+ repack -adb
+'
+
+test_perf 'git rev-list --count --all --objects (no bitmaps)' '
+ git rev-list --objects --all
+'
+
+test_perf 'git rev-list --count --all --objects (no pseudo-merges)' '
+ GIT_TEST_USE_PSEUDO_MERGES=0 \
+ git rev-list --objects --all --use-bitmap-index
+'
+
+test_perf 'git rev-list --count --all --objects (with pseudo-merges)' '
+ GIT_TEST_USE_PSEUDO_MERGES=1 \
+ git rev-list --objects --all --use-bitmap-index
+'
+
+test_done
diff --git a/t/run-test.sh b/t/run-test.sh
new file mode 100755
index 0000000000..13c353b91b
--- /dev/null
+++ b/t/run-test.sh
@@ -0,0 +1,18 @@
+#!/bin/sh
+
+# A simple wrapper to run shell tests via TEST_SHELL_PATH,
+# or exec unit tests directly.
+
+case "$1" in
+*.sh)
+ if test -z "${TEST_SHELL_PATH}"
+ then
+ echo >&2 "ERROR: TEST_SHELL_PATH is empty or not set"
+ exit 1
+ fi
+ exec "${TEST_SHELL_PATH}" "$@"
+ ;;
+*)
+ exec "$@"
+ ;;
+esac
diff --git a/t/t0000-basic.sh b/t/t0000-basic.sh
index 6e300be2ac..98b81e4d63 100755
--- a/t/t0000-basic.sh
+++ b/t/t0000-basic.sh
@@ -1201,6 +1201,34 @@ test_expect_success 'very long name in the index handled sanely' '
test $len = 4098
'
+# D/F conflict checking uses an optimization when adding to the end.
+# make sure it does not get confused by `a-` sorting _between_
+# `a` and `a/`.
+test_expect_success 'more update-index D/F conflicts' '
+ # empty the index to make sure our entry is last
+ git read-tree --empty &&
+ cacheinfo=100644,$(test_oid empty_blob) &&
+ git update-index --add --cacheinfo $cacheinfo,path5/a &&
+
+ test_must_fail git update-index --add --cacheinfo $cacheinfo,path5/a/file &&
+ test_must_fail git update-index --add --cacheinfo $cacheinfo,path5/a/b/file &&
+ test_must_fail git update-index --add --cacheinfo $cacheinfo,path5/a/b/c/file &&
+
+ # "a-" sorts between "a" and "a/"
+ git update-index --add --cacheinfo $cacheinfo,path5/a- &&
+
+ test_must_fail git update-index --add --cacheinfo $cacheinfo,path5/a/file &&
+ test_must_fail git update-index --add --cacheinfo $cacheinfo,path5/a/b/file &&
+ test_must_fail git update-index --add --cacheinfo $cacheinfo,path5/a/b/c/file &&
+
+ cat >expected <<-\EOF &&
+ path5/a
+ path5/a-
+ EOF
+ git ls-files >actual &&
+ test_cmp expected actual
+'
+
test_expect_success 'test_must_fail on a failing git command' '
test_must_fail git notacommand
'
diff --git a/t/t0001-init.sh b/t/t0001-init.sh
index b131d665db..49e9bf77c6 100755
--- a/t/t0001-init.sh
+++ b/t/t0001-init.sh
@@ -584,14 +584,39 @@ test_expect_success 'init with --ref-format=files' '
test_cmp expect actual
'
-test_expect_success 're-init with same format' '
- test_when_finished "rm -rf refformat" &&
- git init --ref-format=files refformat &&
- git init --ref-format=files refformat &&
- echo files >expect &&
- git -C refformat rev-parse --show-ref-format >actual &&
- test_cmp expect actual
-'
+backends="files reftable"
+for from_format in $backends
+do
+ test_expect_success "re-init with same format ($from_format)" '
+ test_when_finished "rm -rf refformat" &&
+ git init --ref-format=$from_format refformat &&
+ git init --ref-format=$from_format refformat &&
+ echo $from_format >expect &&
+ git -C refformat rev-parse --show-ref-format >actual &&
+ test_cmp expect actual
+ '
+
+ for to_format in $backends
+ do
+ if test "$from_format" = "$to_format"
+ then
+ continue
+ fi
+
+ test_expect_success "re-init with different format fails ($from_format -> $to_format)" '
+ test_when_finished "rm -rf refformat" &&
+ git init --ref-format=$from_format refformat &&
+ cat >expect <<-EOF &&
+ fatal: attempt to reinitialize repository with different reference storage format
+ EOF
+ test_must_fail git init --ref-format=$to_format refformat 2>err &&
+ test_cmp expect err &&
+ echo $from_format >expect &&
+ git -C refformat rev-parse --show-ref-format >actual &&
+ test_cmp expect actual
+ '
+ done
+done
test_expect_success 'init with --ref-format=garbage' '
test_when_finished "rm -rf refformat" &&
@@ -678,4 +703,64 @@ test_expect_success 'branch -m with the initial branch' '
test_cmp expect actual
'
+test_expect_success 'init with includeIf.onbranch condition' '
+ test_when_finished "rm -rf repo" &&
+ git -c includeIf.onbranch:main.path=nonexistent init repo &&
+ echo $GIT_DEFAULT_REF_FORMAT >expect &&
+ git -C repo rev-parse --show-ref-format >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'init with includeIf.onbranch condition with existing directory' '
+ test_when_finished "rm -rf repo" &&
+ mkdir repo &&
+ git -c includeIf.onbranch:nonexistent.path=/does/not/exist init repo &&
+ echo $GIT_DEFAULT_REF_FORMAT >expect &&
+ git -C repo rev-parse --show-ref-format >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 're-init with includeIf.onbranch condition' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ git -c includeIf.onbranch:nonexistent.path=/does/not/exist init repo &&
+ echo $GIT_DEFAULT_REF_FORMAT >expect &&
+ git -C repo rev-parse --show-ref-format >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 're-init with includeIf.onbranch condition' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ git -c includeIf.onbranch:nonexistent.path=/does/not/exist init repo &&
+ echo $GIT_DEFAULT_REF_FORMAT >expect &&
+ git -C repo rev-parse --show-ref-format >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 're-init skips non-matching includeIf.onbranch' '
+ test_when_finished "rm -rf repo config" &&
+ cat >config <<-EOF &&
+ [
+ garbage
+ EOF
+ git init repo &&
+ git -c includeIf.onbranch:nonexistent.path="$(test-tool path-utils absolute_path config)" init repo
+'
+
+test_expect_success 're-init reads matching includeIf.onbranch' '
+ test_when_finished "rm -rf repo config" &&
+ cat >config <<-EOF &&
+ [
+ garbage
+ EOF
+ path="$(test-tool path-utils absolute_path config)" &&
+ git init --initial-branch=branch repo &&
+ cat >expect <<-EOF &&
+ fatal: bad config line 1 in file $path
+ EOF
+ test_must_fail git -c includeIf.onbranch:branch.path="$path" init repo 2>err &&
+ test_cmp expect err
+'
+
test_done
diff --git a/t/t0003-attributes.sh b/t/t0003-attributes.sh
index 774b52c298..66ccb5889d 100755
--- a/t/t0003-attributes.sh
+++ b/t/t0003-attributes.sh
@@ -398,13 +398,19 @@ test_expect_success 'bad attr source defaults to reading .gitattributes file' '
)
'
-test_expect_success 'bare repo defaults to reading .gitattributes from HEAD' '
+test_expect_success 'bare repo no longer defaults to reading .gitattributes from HEAD' '
test_when_finished rm -rf test bare_with_gitattribute &&
git init test &&
test_commit -C test gitattributes .gitattributes "f/path test=val" &&
git clone --bare test bare_with_gitattribute &&
- echo "f/path: test: val" >expect &&
+
+ echo "f/path: test: unspecified" >expect &&
git -C bare_with_gitattribute check-attr test -- f/path >actual &&
+ test_cmp expect actual &&
+
+ echo "f/path: test: val" >expect &&
+ git -C bare_with_gitattribute -c attr.tree=HEAD \
+ check-attr test -- f/path >actual &&
test_cmp expect actual
'
@@ -428,6 +434,21 @@ test_expect_success 'precedence of --attr-source, GIT_ATTR_SOURCE, then attr.tre
)
'
+test_expect_success 'diff without repository with attr source' '
+ mkdir -p "$TRASH_DIRECTORY/outside/nongit" &&
+ (
+ cd "$TRASH_DIRECTORY/outside/nongit" &&
+ GIT_CEILING_DIRECTORIES="$TRASH_DIRECTORY/outside" &&
+ export GIT_CEILING_DIRECTORIES &&
+ touch file &&
+ cat >expect <<-EOF &&
+ fatal: cannot use --attr-source or GIT_ATTR_SOURCE without repo
+ EOF
+ test_must_fail env GIT_ATTR_SOURCE=HEAD git grep --no-index foo file 2>err &&
+ test_cmp expect err
+ )
+'
+
test_expect_success 'bare repository: with --source' '
(
cd bare.git &&
@@ -572,6 +593,16 @@ test_expect_success EXPENSIVE 'large attributes file ignored in index' '
test_cmp expect err
'
+test_expect_success EXPENSIVE 'large attributes blob ignored' '
+ test_when_finished "git update-index --remove .gitattributes" &&
+ blob=$(dd if=/dev/zero bs=1048576 count=101 2>/dev/null | git hash-object -w --stdin) &&
+ git update-index --add --cacheinfo 100644,$blob,.gitattributes &&
+ tree="$(git write-tree)" &&
+ git check-attr --cached --all --source="$tree" path >/dev/null 2>err &&
+ echo "warning: ignoring overly large gitattributes blob ${SQ}.gitattributes${SQ}" >expect &&
+ test_cmp expect err
+'
+
test_expect_success 'builtin object mode attributes work (dir and regular paths)' '
>normal &&
attr_check_object_mode normal 100644 &&
diff --git a/t/t0006-date.sh b/t/t0006-date.sh
index 3031256d14..fd373e1b39 100755
--- a/t/t0006-date.sh
+++ b/t/t0006-date.sh
@@ -8,6 +8,11 @@ TEST_PASSES_SANITIZE_LEAK=true
# arbitrary reference time: 2009-08-30 19:20:00
GIT_TEST_DATE_NOW=1251660000; export GIT_TEST_DATE_NOW
+if test_have_prereq TIME_IS_64BIT,TIME_T_IS_64BIT
+then
+ test_set_prereq HAVE_64BIT_TIME
+fi
+
check_relative() {
t=$(($GIT_TEST_DATE_NOW - $1))
echo "$t -> $2" >expect
@@ -80,14 +85,15 @@ check_show raw "$TIME" '1466000000 -0200'
# arbitrary time absurdly far in the future
FUTURE="5758122296 -0400"
-check_show iso "$FUTURE" "2152-06-19 18:24:56 -0400" TIME_IS_64BIT,TIME_T_IS_64BIT
-check_show iso-local "$FUTURE" "2152-06-19 22:24:56 +0000" TIME_IS_64BIT,TIME_T_IS_64BIT
+check_show iso "$FUTURE" "2152-06-19 18:24:56 -0400" HAVE_64BIT_TIME
+check_show iso-local "$FUTURE" "2152-06-19 22:24:56 +0000" HAVE_64BIT_TIME
-check_parse() {
+REQUIRE_64BIT_TIME=
+check_parse () {
echo "$1 -> $2" >expect
- test_expect_${4:-success} "parse date ($1${3:+ TZ=$3})" "
- TZ=${3:-$TZ} test-tool date parse '$1' >actual &&
- test_cmp expect actual
+ test_expect_success $REQUIRE_64BIT_TIME "parse date ($1${3:+ TZ=$3}) -> $2" "
+ TZ=${3:-$TZ} test-tool date parse '$1' >actual &&
+ test_cmp expect actual
"
}
@@ -117,6 +123,39 @@ check_parse '2008-02-14 20:30:45 -05:00' '2008-02-14 20:30:45 -0500'
check_parse '2008-02-14 20:30:45' '2008-02-14 20:30:45 -0500' EST5
check_parse 'Thu, 7 Apr 2005 15:14:13 -0700' '2005-04-07 15:14:13 -0700'
+check_parse '1970-01-01 00:00:00' '1970-01-01 00:00:00 +0000'
+check_parse '1970-01-01 00:00:00 +00' '1970-01-01 00:00:00 +0000'
+check_parse '1970-01-01 00:00:00 Z' '1970-01-01 00:00:00 +0000'
+check_parse '1970-01-01 00:00:00 -01' '1970-01-01 00:00:00 -0100'
+check_parse '1970-01-01 00:00:00 +01' bad
+check_parse '1970-01-01 00:00:00 +11' bad
+check_parse '1970-01-01 00:59:59 +01' bad
+check_parse '1970-01-01 01:00:00 +01' '1970-01-01 01:00:00 +0100'
+check_parse '1970-01-01 01:00:00 +11' bad
+check_parse '1970-01-02 00:00:00 +11' '1970-01-02 00:00:00 +1100'
+check_parse '1969-12-31 23:59:59' bad
+check_parse '1969-12-31 23:59:59 +00' bad
+check_parse '1969-12-31 23:59:59 Z' bad
+check_parse '1969-12-31 23:59:59 +11' bad
+check_parse '1969-12-31 23:59:59 -11' bad
+
+REQUIRE_64BIT_TIME=HAVE_64BIT_TIME
+check_parse '2099-12-31 23:59:59' '2099-12-31 23:59:59 +0000'
+check_parse '2099-12-31 23:59:59 +00' '2099-12-31 23:59:59 +0000'
+check_parse '2099-12-31 23:59:59 Z' '2099-12-31 23:59:59 +0000'
+check_parse '2099-12-31 23:59:59 +01' '2099-12-31 23:59:59 +0100'
+check_parse '2099-12-31 23:59:59 -01' bad
+check_parse '2099-12-31 23:59:59 -11' bad
+check_parse '2099-12-31 23:00:00 -01' bad
+check_parse '2099-12-31 22:59:59 -01' '2099-12-31 22:59:59 -0100'
+check_parse '2100-00-00 00:00:00' bad
+check_parse '2099-12-30 00:00:00 -11' '2099-12-30 00:00:00 -1100'
+check_parse '2100-00-00 00:00:00 +00' bad
+check_parse '2100-00-00 00:00:00 Z' bad
+check_parse '2100-00-00 00:00:00 -11' bad
+check_parse '2100-00-00 00:00:00 +11' bad
+REQUIRE_64BIT_TIME=
+
check_approxidate() {
echo "$1 -> $2 +0000" >expect
test_expect_${3:-success} "parse approxidate ($1)" "
diff --git a/t/t0007-git-var.sh b/t/t0007-git-var.sh
index ff4fd9348c..9fc5882387 100755
--- a/t/t0007-git-var.sh
+++ b/t/t0007-git-var.sh
@@ -157,7 +157,7 @@ test_expect_success POSIXPERM 'GIT_SHELL_PATH points to a valid executable' '
test_expect_success MINGW 'GIT_SHELL_PATH points to a suitable shell' '
shellpath=$(git var GIT_SHELL_PATH) &&
case "$shellpath" in
- *sh) ;;
+ [A-Z]:/*/sh.exe) test -f "$shellpath";;
*) return 1;;
esac
'
diff --git a/t/t0008-ignores.sh b/t/t0008-ignores.sh
index 361446b2f4..02a18d4fdb 100755
--- a/t/t0008-ignores.sh
+++ b/t/t0008-ignores.sh
@@ -945,4 +945,12 @@ test_expect_success SYMLINKS 'symlinks not respected in-tree' '
test_grep "unable to access.*gitignore" err
'
+test_expect_success EXPENSIVE 'large exclude file ignored in tree' '
+ test_when_finished "rm .gitignore" &&
+ dd if=/dev/zero of=.gitignore bs=101M count=1 &&
+ git ls-files -o --exclude-standard 2>err &&
+ echo "warning: ignoring excessively large pattern file: .gitignore" >expect &&
+ test_cmp expect err
+'
+
test_done
diff --git a/t/t0014-alias.sh b/t/t0014-alias.sh
index 95568342be..854d59ec58 100755
--- a/t/t0014-alias.sh
+++ b/t/t0014-alias.sh
@@ -44,4 +44,15 @@ test_expect_success 'run-command formats empty args properly' '
test_cmp expect actual
'
+test_expect_success 'tracing a shell alias with arguments shows trace of prepared command' '
+ cat >expect <<-EOF &&
+ trace: start_command: SHELL -c ${SQ}echo \$* "\$@"${SQ} ${SQ}echo \$*${SQ} arg
+ EOF
+ git config alias.echo "!echo \$*" &&
+ env GIT_TRACE=1 git echo arg 2>output &&
+ # redact platform differences
+ sed -n -e "s/^\(trace: start_command:\) .* -c /\1 SHELL -c /p" output >actual &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t0015-hash.sh b/t/t0015-hash.sh
deleted file mode 100755
index 0a087a1983..0000000000
--- a/t/t0015-hash.sh
+++ /dev/null
@@ -1,56 +0,0 @@
-#!/bin/sh
-
-test_description='test basic hash implementation'
-
-TEST_PASSES_SANITIZE_LEAK=true
-. ./test-lib.sh
-
-test_expect_success 'test basic SHA-1 hash values' '
- test-tool sha1 </dev/null >actual &&
- grep da39a3ee5e6b4b0d3255bfef95601890afd80709 actual &&
- printf "a" | test-tool sha1 >actual &&
- grep 86f7e437faa5a7fce15d1ddcb9eaeaea377667b8 actual &&
- printf "abc" | test-tool sha1 >actual &&
- grep a9993e364706816aba3e25717850c26c9cd0d89d actual &&
- printf "message digest" | test-tool sha1 >actual &&
- grep c12252ceda8be8994d5fa0290a47231c1d16aae3 actual &&
- printf "abcdefghijklmnopqrstuvwxyz" | test-tool sha1 >actual &&
- grep 32d10c7b8cf96570ca04ce37f2a19d84240d3a89 actual &&
- perl -e "$| = 1; print q{aaaaaaaaaa} for 1..100000;" |
- test-tool sha1 >actual &&
- grep 34aa973cd4c4daa4f61eeb2bdbad27316534016f actual &&
- printf "blob 0\0" | test-tool sha1 >actual &&
- grep e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 actual &&
- printf "blob 3\0abc" | test-tool sha1 >actual &&
- grep f2ba8f84ab5c1bce84a7b441cb1959cfc7093b7f actual &&
- printf "tree 0\0" | test-tool sha1 >actual &&
- grep 4b825dc642cb6eb9a060e54bf8d69288fbee4904 actual
-'
-
-test_expect_success 'test basic SHA-256 hash values' '
- test-tool sha256 </dev/null >actual &&
- grep e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 actual &&
- printf "a" | test-tool sha256 >actual &&
- grep ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb actual &&
- printf "abc" | test-tool sha256 >actual &&
- grep ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad actual &&
- printf "message digest" | test-tool sha256 >actual &&
- grep f7846f55cf23e14eebeab5b4e1550cad5b509e3348fbc4efa3a1413d393cb650 actual &&
- printf "abcdefghijklmnopqrstuvwxyz" | test-tool sha256 >actual &&
- grep 71c480df93d6ae2f1efad1447c66c9525e316218cf51fc8d9ed832f2daf18b73 actual &&
- # Try to exercise the chunking code by turning autoflush on.
- perl -e "$| = 1; print q{aaaaaaaaaa} for 1..100000;" |
- test-tool sha256 >actual &&
- grep cdc76e5c9914fb9281a1c7e284d73e67f1809a48a497200e046d39ccc7112cd0 actual &&
- perl -e "$| = 1; print q{abcdefghijklmnopqrstuvwxyz} for 1..100000;" |
- test-tool sha256 >actual &&
- grep e406ba321ca712ad35a698bf0af8d61fc4dc40eca6bdcea4697962724ccbde35 actual &&
- printf "blob 0\0" | test-tool sha256 >actual &&
- grep 473a0f4c3be8a93681a267e3b1e9a7dcda1185436fe141f7749120a303721813 actual &&
- printf "blob 3\0abc" | test-tool sha256 >actual &&
- grep c1cf6e465077930e88dc5136641d402f72a229ddd996f627d60e9639eaba35a6 actual &&
- printf "tree 0\0" | test-tool sha256 >actual &&
- grep 6ef19b41225c5369f1c104d45d8d85efa9b057b53b14b4b9b939dd74decc5321 actual
-'
-
-test_done
diff --git a/t/t0016-oidmap.sh b/t/t0016-oidmap.sh
deleted file mode 100755
index 0faef1f4f1..0000000000
--- a/t/t0016-oidmap.sh
+++ /dev/null
@@ -1,112 +0,0 @@
-#!/bin/sh
-
-test_description='test oidmap'
-
-TEST_PASSES_SANITIZE_LEAK=true
-. ./test-lib.sh
-
-# This purposefully is very similar to t0011-hashmap.sh
-
-test_oidmap () {
- echo "$1" | test-tool oidmap $3 >actual &&
- echo "$2" >expect &&
- test_cmp expect actual
-}
-
-
-test_expect_success 'setup' '
-
- test_commit one &&
- test_commit two &&
- test_commit three &&
- test_commit four
-
-'
-
-test_expect_success 'put' '
-
-test_oidmap "put one 1
-put two 2
-put invalidOid 4
-put three 3" "NULL
-NULL
-Unknown oid: invalidOid
-NULL"
-
-'
-
-test_expect_success 'replace' '
-
-test_oidmap "put one 1
-put two 2
-put three 3
-put invalidOid 4
-put two deux
-put one un" "NULL
-NULL
-NULL
-Unknown oid: invalidOid
-2
-1"
-
-'
-
-test_expect_success 'get' '
-
-test_oidmap "put one 1
-put two 2
-put three 3
-get two
-get four
-get invalidOid
-get one" "NULL
-NULL
-NULL
-2
-NULL
-Unknown oid: invalidOid
-1"
-
-'
-
-test_expect_success 'remove' '
-
-test_oidmap "put one 1
-put two 2
-put three 3
-remove one
-remove two
-remove invalidOid
-remove four" "NULL
-NULL
-NULL
-1
-2
-Unknown oid: invalidOid
-NULL"
-
-'
-
-test_expect_success 'iterate' '
- test-tool oidmap >actual.raw <<-\EOF &&
- put one 1
- put two 2
- put three 3
- iterate
- EOF
-
- # sort "expect" too so we do not rely on the order of particular oids
- sort >expect <<-EOF &&
- NULL
- NULL
- NULL
- $(git rev-parse one) 1
- $(git rev-parse two) 2
- $(git rev-parse three) 3
- EOF
-
- sort <actual.raw >actual &&
- test_cmp expect actual
-'
-
-test_done
diff --git a/t/t0017-env-helper.sh b/t/t0017-env-helper.sh
index fc14ba091c..f3a16859cc 100755
--- a/t/t0017-env-helper.sh
+++ b/t/t0017-env-helper.sh
@@ -91,9 +91,16 @@ test_expect_success 'test-tool env-helper reads config thanks to trace2' '
git config -l 2>err &&
grep "exceeded maximum include depth" err &&
+ # This validates that the assumption that we attempt to
+ # read the configuration and fail very early in the start-up
+ # sequence (due to trace2 subsystem), even before we notice
+ # that the directory named with "test-tool -C" does not exist
+ # and die. It is a dubious thing to test, though.
test_must_fail \
env HOME="$(pwd)/home" GIT_TEST_ENV_HELPER=true \
- test-tool -C cycle env-helper --type=bool --default=0 --exit-code GIT_TEST_ENV_HELPER 2>err &&
+ test-tool -C no-such-directory \
+ env-helper --type=bool --default=0 \
+ --exit-code GIT_TEST_ENV_HELPER 2>err &&
grep "exceeded maximum include depth" err
'
diff --git a/t/t0018-advice.sh b/t/t0018-advice.sh
index 0dcfb760a2..29306b367c 100755
--- a/t/t0018-advice.sh
+++ b/t/t0018-advice.sh
@@ -2,6 +2,9 @@
test_description='Test advise_if_enabled functionality'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=trunk
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
@@ -29,4 +32,72 @@ test_expect_success 'advice should not be printed when config variable is set to
test_must_be_empty actual
'
+test_expect_success 'advice should not be printed when --no-advice is used' '
+ q_to_tab >expect <<-\EOF &&
+ On branch trunk
+
+ No commits yet
+
+ Untracked files:
+ QREADME
+
+ nothing added to commit but untracked files present
+ EOF
+
+ test_when_finished "rm -fr advice-test" &&
+ git init advice-test &&
+ (
+ cd advice-test &&
+ >README &&
+ git --no-advice status
+ ) >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'advice should not be printed when GIT_ADVICE is set to false' '
+ q_to_tab >expect <<-\EOF &&
+ On branch trunk
+
+ No commits yet
+
+ Untracked files:
+ QREADME
+
+ nothing added to commit but untracked files present
+ EOF
+
+ test_when_finished "rm -fr advice-test" &&
+ git init advice-test &&
+ (
+ cd advice-test &&
+ >README &&
+ GIT_ADVICE=false git status
+ ) >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'advice should be printed when GIT_ADVICE is set to true' '
+ q_to_tab >expect <<-\EOF &&
+ On branch trunk
+
+ No commits yet
+
+ Untracked files:
+ (use "git add <file>..." to include in what will be committed)
+ QREADME
+
+ nothing added to commit but untracked files present (use "git add" to track)
+ EOF
+
+ test_when_finished "rm -fr advice-test" &&
+ git init advice-test &&
+ (
+ cd advice-test &&
+ >README &&
+ GIT_ADVICE=true git status
+ ) >actual &&
+ cat actual > /tmp/actual &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t0033-safe-directory.sh b/t/t0033-safe-directory.sh
index dc3496897a..5fe61f1291 100755
--- a/t/t0033-safe-directory.sh
+++ b/t/t0033-safe-directory.sh
@@ -71,7 +71,22 @@ test_expect_success 'safe.directory=*, but is reset' '
expect_rejected_dir
'
+test_expect_success 'safe.directory with matching glob' '
+ git config --global --unset-all safe.directory &&
+ p=$(pwd) &&
+ git config --global safe.directory "${p%/*}/*" &&
+ git status
+'
+
+test_expect_success 'safe.directory with unmatching glob' '
+ git config --global --unset-all safe.directory &&
+ p=$(pwd) &&
+ git config --global safe.directory "${p%/*}no/*" &&
+ expect_rejected_dir
+'
+
test_expect_success 'safe.directory in included file' '
+ git config --global --unset-all safe.directory &&
cat >gitconfig-include <<-EOF &&
[safe]
directory = "$(pwd)"
@@ -80,4 +95,28 @@ test_expect_success 'safe.directory in included file' '
git status
'
+test_expect_success 'local clone of unowned repo refused in unsafe directory' '
+ test_when_finished "rm -rf source" &&
+ git init source &&
+ (
+ sane_unset GIT_TEST_ASSUME_DIFFERENT_OWNER &&
+ test_commit -C source initial
+ ) &&
+ test_must_fail git clone --local source target &&
+ test_path_is_missing target
+'
+
+test_expect_success 'local clone of unowned repo accepted in safe directory' '
+ test_when_finished "rm -rf source" &&
+ git init source &&
+ (
+ sane_unset GIT_TEST_ASSUME_DIFFERENT_OWNER &&
+ test_commit -C source initial
+ ) &&
+ test_must_fail git clone --local source target &&
+ git config --global --add safe.directory "$(pwd)/source/.git" &&
+ git clone --local source target &&
+ test_path_is_dir target
+'
+
test_done
diff --git a/t/t0040-parse-options.sh b/t/t0040-parse-options.sh
index 8bb2a8b453..45a773642f 100755
--- a/t/t0040-parse-options.sh
+++ b/t/t0040-parse-options.sh
@@ -176,6 +176,23 @@ test_expect_success 'long options' '
test_cmp expect output
'
+test_expect_success 'abbreviate to something longer than SHA1 length' '
+ cat >expect <<-EOF &&
+ boolean: 0
+ integer: 0
+ magnitude: 0
+ timestamp: 0
+ string: (not set)
+ abbrev: 100
+ verbose: -1
+ quiet: 0
+ dry run: no
+ file: (not set)
+ EOF
+ test-tool parse-options --abbrev=100 >output &&
+ test_cmp expect output
+'
+
test_expect_success 'missing required value' '
cat >expect <<-\EOF &&
error: switch `s'\'' requires a value
diff --git a/t/t0064-oid-array.sh b/t/t0064-oid-array.sh
index 88c89e8f48..de74b692d0 100755
--- a/t/t0064-oid-array.sh
+++ b/t/t0064-oid-array.sh
@@ -15,6 +15,24 @@ echoid () {
done
}
+test_expect_success 'without repository' '
+ cat >expect <<-EOF &&
+ 4444444444444444444444444444444444444444
+ 5555555555555555555555555555555555555555
+ 8888888888888888888888888888888888888888
+ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+ EOF
+ cat >input <<-EOF &&
+ append 4444444444444444444444444444444444444444
+ append 5555555555555555555555555555555555555555
+ append 8888888888888888888888888888888888888888
+ append aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+ for_each_unique
+ EOF
+ nongit test-tool oid-array <input >actual &&
+ test_cmp expect actual
+'
+
test_expect_success 'ordered enumeration' '
echoid "" 44 55 88 aa >expect &&
{
diff --git a/t/t0065-strcmp-offset.sh b/t/t0065-strcmp-offset.sh
deleted file mode 100755
index 94e34c83ed..0000000000
--- a/t/t0065-strcmp-offset.sh
+++ /dev/null
@@ -1,22 +0,0 @@
-#!/bin/sh
-
-test_description='Test strcmp_offset functionality'
-
-TEST_PASSES_SANITIZE_LEAK=true
-. ./test-lib.sh
-
-while read s1 s2 expect
-do
- test_expect_success "strcmp_offset($s1, $s2)" '
- echo "$expect" >expect &&
- test-tool strcmp-offset "$s1" "$s2" >actual &&
- test_cmp expect actual
- '
-done <<-EOF
-abc abc 0 3
-abc def -1 0
-abc abz -1 2
-abc abcdef -1 3
-EOF
-
-test_done
diff --git a/t/t0068-for-each-repo.sh b/t/t0068-for-each-repo.sh
index 4b90b74d5d..95019e01ed 100755
--- a/t/t0068-for-each-repo.sh
+++ b/t/t0068-for-each-repo.sh
@@ -59,4 +59,20 @@ test_expect_success 'error on NULL value for config keys' '
test_cmp expect actual
'
+test_expect_success '--keep-going' '
+ git config keep.going non-existing &&
+ git config --add keep.going . &&
+
+ test_must_fail git for-each-repo --config=keep.going \
+ -- branch >out 2>err &&
+ test_grep "cannot change to .*non-existing" err &&
+ test_must_be_empty out &&
+
+ test_must_fail git for-each-repo --config=keep.going --keep-going \
+ -- branch >out 2>err &&
+ test_grep "cannot change to .*non-existing" err &&
+ git branch >expect &&
+ test_cmp expect out
+'
+
test_done
diff --git a/t/t0069-oidtree.sh b/t/t0069-oidtree.sh
deleted file mode 100755
index 889db50818..0000000000
--- a/t/t0069-oidtree.sh
+++ /dev/null
@@ -1,50 +0,0 @@
-#!/bin/sh
-
-test_description='basic tests for the oidtree implementation'
-TEST_PASSES_SANITIZE_LEAK=true
-. ./test-lib.sh
-
-maxhexsz=$(test_oid hexsz)
-echoid () {
- prefix="${1:+$1 }"
- shift
- while test $# -gt 0
- do
- shortoid="$1"
- shift
- difference=$(($maxhexsz - ${#shortoid}))
- printf "%s%s%0${difference}d\\n" "$prefix" "$shortoid" "0"
- done
-}
-
-test_expect_success 'oidtree insert and contains' '
- cat >expect <<-\EOF &&
- 0
- 0
- 0
- 1
- 1
- 0
- EOF
- {
- echoid insert 444 1 2 3 4 5 a b c d e &&
- echoid contains 44 441 440 444 4440 4444 &&
- echo clear
- } | test-tool oidtree >actual &&
- test_cmp expect actual
-'
-
-test_expect_success 'oidtree each' '
- echoid "" 123 321 321 >expect &&
- {
- echoid insert f 9 8 123 321 a b c d e &&
- echo each 12300 &&
- echo each 3211 &&
- echo each 3210 &&
- echo each 32100 &&
- echo clear
- } | test-tool oidtree >actual &&
- test_cmp expect actual
-'
-
-test_done
diff --git a/t/t0080-unit-test-output.sh b/t/t0080-unit-test-output.sh
index 6657c114a3..7bbb065d58 100755
--- a/t/t0080-unit-test-output.sh
+++ b/t/t0080-unit-test-output.sh
@@ -9,50 +9,50 @@ test_expect_success 'TAP output from unit tests' '
cat >expect <<-EOF &&
ok 1 - passing test
ok 2 - passing test and assertion return 1
- # check "1 == 2" failed at t/unit-tests/t-basic.c:76
+ # check "1 == 2" failed at t/helper/test-example-tap.c:77
# left: 1
# right: 2
not ok 3 - failing test
ok 4 - failing test and assertion return 0
not ok 5 - passing TEST_TODO() # TODO
ok 6 - passing TEST_TODO() returns 1
- # todo check ${SQ}check(x)${SQ} succeeded at t/unit-tests/t-basic.c:25
+ # todo check ${SQ}check(x)${SQ} succeeded at t/helper/test-example-tap.c:26
not ok 7 - failing TEST_TODO()
ok 8 - failing TEST_TODO() returns 0
- # check "0" failed at t/unit-tests/t-basic.c:30
+ # check "0" failed at t/helper/test-example-tap.c:31
# skipping test - missing prerequisite
- # skipping check ${SQ}1${SQ} at t/unit-tests/t-basic.c:32
+ # skipping check ${SQ}1${SQ} at t/helper/test-example-tap.c:33
ok 9 - test_skip() # SKIP
ok 10 - skipped test returns 1
# skipping test - missing prerequisite
ok 11 - test_skip() inside TEST_TODO() # SKIP
ok 12 - test_skip() inside TEST_TODO() returns 1
- # check "0" failed at t/unit-tests/t-basic.c:48
+ # check "0" failed at t/helper/test-example-tap.c:49
not ok 13 - TEST_TODO() after failing check
ok 14 - TEST_TODO() after failing check returns 0
- # check "0" failed at t/unit-tests/t-basic.c:56
+ # check "0" failed at t/helper/test-example-tap.c:57
not ok 15 - failing check after TEST_TODO()
ok 16 - failing check after TEST_TODO() returns 0
- # check "!strcmp("\thello\\\\", "there\"\n")" failed at t/unit-tests/t-basic.c:61
+ # check "!strcmp("\thello\\\\", "there\"\n")" failed at t/helper/test-example-tap.c:62
# left: "\011hello\\\\"
# right: "there\"\012"
- # check "!strcmp("NULL", NULL)" failed at t/unit-tests/t-basic.c:62
+ # check "!strcmp("NULL", NULL)" failed at t/helper/test-example-tap.c:63
# left: "NULL"
# right: NULL
- # check "${SQ}a${SQ} == ${SQ}\n${SQ}" failed at t/unit-tests/t-basic.c:63
+ # check "${SQ}a${SQ} == ${SQ}\n${SQ}" failed at t/helper/test-example-tap.c:64
# left: ${SQ}a${SQ}
# right: ${SQ}\012${SQ}
- # check "${SQ}\\\\${SQ} == ${SQ}\\${SQ}${SQ}" failed at t/unit-tests/t-basic.c:64
+ # check "${SQ}\\\\${SQ} == ${SQ}\\${SQ}${SQ}" failed at t/helper/test-example-tap.c:65
# left: ${SQ}\\\\${SQ}
# right: ${SQ}\\${SQ}${SQ}
not ok 17 - messages from failing string and char comparison
- # BUG: test has no checks at t/unit-tests/t-basic.c:91
+ # BUG: test has no checks at t/helper/test-example-tap.c:92
not ok 18 - test with no checks
ok 19 - test with no checks returns 0
1..19
EOF
- ! "$GIT_BUILD_DIR"/t/unit-tests/bin/t-basic >actual &&
+ ! test-tool example-tap >actual &&
test_cmp expect actual
'
diff --git a/t/t0095-bloom.sh b/t/t0095-bloom.sh
index b567383eb8..c8d84ab606 100755
--- a/t/t0095-bloom.sh
+++ b/t/t0095-bloom.sh
@@ -29,6 +29,14 @@ test_expect_success 'compute unseeded murmur3 hash for test string 2' '
test_cmp expect actual
'
+test_expect_success 'compute unseeded murmur3 hash for test string 3' '
+ cat >expect <<-\EOF &&
+ Murmur3 Hash with seed=0:0xa183ccfd
+ EOF
+ test-tool bloom get_murmur3_seven_highbit >actual &&
+ test_cmp expect actual
+'
+
test_expect_success 'compute bloom key for empty string' '
cat >expect <<-\EOF &&
Hashes:0x5615800c|0x5b966560|0x61174ab4|0x66983008|0x6c19155c|0x7199fab0|0x771ae004|
diff --git a/t/t0211-trace2-perf.sh b/t/t0211-trace2-perf.sh
index 13ef69b92f..070fe7a5da 100755
--- a/t/t0211-trace2-perf.sh
+++ b/t/t0211-trace2-perf.sh
@@ -233,7 +233,7 @@ have_counter_event () {
pattern="d0|${thread}|${event}||||${category}|name:${name} value:${value}" &&
- grep "${patern}" ${file}
+ grep "${pattern}" ${file}
}
test_expect_success 'global counter test/test1' '
diff --git a/t/t0300-credentials.sh b/t/t0300-credentials.sh
index 400f6bdbca..6a76b7fdbd 100755
--- a/t/t0300-credentials.sh
+++ b/t/t0300-credentials.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='basic credential helper tests'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-credential.sh
@@ -12,7 +14,13 @@ test_expect_success 'setup helper scripts' '
IFS==
while read key value; do
echo >&2 "$whoami: $key=$value"
- eval "$key=$value"
+ if test -z "${key%%*\[\]}"
+ then
+ key=${key%%\[\]}
+ eval "$key=\"\$$key $value\""
+ else
+ eval "$key=$value"
+ fi
done
IFS=$OIFS
EOF
@@ -35,6 +43,30 @@ test_expect_success 'setup helper scripts' '
test -z "$pass" || echo password=$pass
EOF
+ write_script git-credential-verbatim-cred <<-\EOF &&
+ authtype=$1; shift
+ credential=$1; shift
+ . ./dump
+ echo capability[]=authtype
+ echo capability[]=state
+ test -z "${capability##*authtype*}" || exit 0
+ test -z "$authtype" || echo authtype=$authtype
+ test -z "$credential" || echo credential=$credential
+ test -z "${capability##*state*}" || exit 0
+ echo state[]=verbatim-cred:foo
+ EOF
+
+ write_script git-credential-verbatim-ephemeral <<-\EOF &&
+ authtype=$1; shift
+ credential=$1; shift
+ . ./dump
+ echo capability[]=authtype
+ test -z "${capability##*authtype*}" || exit 0
+ test -z "$authtype" || echo authtype=$authtype
+ test -z "$credential" || echo credential=$credential
+ echo "ephemeral=1"
+ EOF
+
write_script git-credential-verbatim-with-expiry <<-\EOF &&
user=$1; shift
pass=$1; shift
@@ -64,6 +96,67 @@ test_expect_success 'credential_fill invokes helper' '
EOF
'
+test_expect_success 'credential_fill invokes helper with credential' '
+ check fill "verbatim-cred Bearer token" <<-\EOF
+ capability[]=authtype
+ protocol=http
+ host=example.com
+ --
+ capability[]=authtype
+ authtype=Bearer
+ credential=token
+ protocol=http
+ host=example.com
+ --
+ verbatim-cred: get
+ verbatim-cred: capability[]=authtype
+ verbatim-cred: protocol=http
+ verbatim-cred: host=example.com
+ EOF
+'
+
+test_expect_success 'credential_fill invokes helper with ephemeral credential' '
+ check fill "verbatim-ephemeral Bearer token" <<-\EOF
+ capability[]=authtype
+ protocol=http
+ host=example.com
+ --
+ capability[]=authtype
+ authtype=Bearer
+ credential=token
+ ephemeral=1
+ protocol=http
+ host=example.com
+ --
+ verbatim-ephemeral: get
+ verbatim-ephemeral: capability[]=authtype
+ verbatim-ephemeral: protocol=http
+ verbatim-ephemeral: host=example.com
+ EOF
+'
+test_expect_success 'credential_fill invokes helper with credential and state' '
+ check fill "verbatim-cred Bearer token" <<-\EOF
+ capability[]=authtype
+ capability[]=state
+ protocol=http
+ host=example.com
+ --
+ capability[]=authtype
+ capability[]=state
+ authtype=Bearer
+ credential=token
+ protocol=http
+ host=example.com
+ state[]=verbatim-cred:foo
+ --
+ verbatim-cred: get
+ verbatim-cred: capability[]=authtype
+ verbatim-cred: capability[]=state
+ verbatim-cred: protocol=http
+ verbatim-cred: host=example.com
+ EOF
+'
+
test_expect_success 'credential_fill invokes multiple helpers' '
check fill useless "verbatim foo bar" <<-\EOF
protocol=http
@@ -83,6 +176,45 @@ test_expect_success 'credential_fill invokes multiple helpers' '
EOF
'
+test_expect_success 'credential_fill response does not get capabilities when helpers are incapable' '
+ check fill useless "verbatim foo bar" <<-\EOF
+ capability[]=authtype
+ capability[]=state
+ protocol=http
+ host=example.com
+ --
+ protocol=http
+ host=example.com
+ username=foo
+ password=bar
+ --
+ useless: get
+ useless: capability[]=authtype
+ useless: capability[]=state
+ useless: protocol=http
+ useless: host=example.com
+ verbatim: get
+ verbatim: capability[]=authtype
+ verbatim: capability[]=state
+ verbatim: protocol=http
+ verbatim: host=example.com
+ EOF
+'
+
+test_expect_success 'credential_fill response does not get capabilities when caller is incapable' '
+ check fill "verbatim-cred Bearer token" <<-\EOF
+ protocol=http
+ host=example.com
+ --
+ protocol=http
+ host=example.com
+ --
+ verbatim-cred: get
+ verbatim-cred: protocol=http
+ verbatim-cred: host=example.com
+ EOF
+'
+
test_expect_success 'credential_fill stops when we get a full response' '
check fill "verbatim one two" "verbatim three four" <<-\EOF
protocol=http
@@ -99,6 +231,25 @@ test_expect_success 'credential_fill stops when we get a full response' '
EOF
'
+test_expect_success 'credential_fill thinks a credential is a full response' '
+ check fill "verbatim-cred Bearer token" "verbatim three four" <<-\EOF
+ capability[]=authtype
+ protocol=http
+ host=example.com
+ --
+ capability[]=authtype
+ authtype=Bearer
+ credential=token
+ protocol=http
+ host=example.com
+ --
+ verbatim-cred: get
+ verbatim-cred: capability[]=authtype
+ verbatim-cred: protocol=http
+ verbatim-cred: host=example.com
+ EOF
+'
+
test_expect_success 'credential_fill continues through partial response' '
check fill "verbatim one \"\"" "verbatim two three" <<-\EOF
protocol=http
@@ -175,6 +326,20 @@ test_expect_success 'credential_fill passes along metadata' '
EOF
'
+test_expect_success 'credential_fill produces no credential without capability' '
+ check fill "verbatim-cred Bearer token" <<-\EOF
+ protocol=http
+ host=example.com
+ --
+ protocol=http
+ host=example.com
+ --
+ verbatim-cred: get
+ verbatim-cred: protocol=http
+ verbatim-cred: host=example.com
+ EOF
+'
+
test_expect_success 'credential_approve calls all helpers' '
check approve useless "verbatim one two" <<-\EOF
protocol=http
diff --git a/t/t0301-credential-cache.sh b/t/t0301-credential-cache.sh
index f2c146fa2a..c10e35905e 100755
--- a/t/t0301-credential-cache.sh
+++ b/t/t0301-credential-cache.sh
@@ -39,6 +39,7 @@ test_atexit 'git credential-cache exit'
helper_test cache
helper_test_password_expiry_utc cache
helper_test_oauth_refresh_token cache
+helper_test_authtype cache
test_expect_success 'socket defaults to ~/.cache/git/credential/socket' '
test_when_finished "
diff --git a/t/t0410-partial-clone.sh b/t/t0410-partial-clone.sh
index 88a66f0904..2c30c86e7b 100755
--- a/t/t0410-partial-clone.sh
+++ b/t/t0410-partial-clone.sh
@@ -3,6 +3,7 @@
test_description='partial clone'
. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-terminal.sh
# missing promisor objects cause repacks which write bitmaps to fail
GIT_TEST_MULTI_PACK_INDEX_WRITE_BITMAP=0
@@ -689,6 +690,67 @@ test_expect_success 'lazy-fetch when accessing object not in the_repository' '
! grep "[?]$FILE_HASH" out
'
+test_expect_success 'push should not fetch new commit objects' '
+ rm -rf server client &&
+ test_create_repo server &&
+ test_config -C server uploadpack.allowfilter 1 &&
+ test_config -C server uploadpack.allowanysha1inwant 1 &&
+ test_commit -C server server1 &&
+
+ git clone --filter=blob:none "file://$(pwd)/server" client &&
+ test_commit -C client client1 &&
+
+ test_commit -C server server2 &&
+ COMMIT=$(git -C server rev-parse server2) &&
+
+ test_must_fail git -C client push 2>err &&
+ grep "fetch first" err &&
+ git -C client rev-list --objects --missing=print "$COMMIT" >objects &&
+ grep "^[?]$COMMIT" objects
+'
+
+test_expect_success 'setup for promisor.quiet tests' '
+ rm -rf server &&
+ test_create_repo server &&
+ test_commit -C server foo &&
+ git -C server rm foo.t &&
+ git -C server commit -m remove &&
+ git -C server config uploadpack.allowanysha1inwant 1 &&
+ git -C server config uploadpack.allowfilter 1
+'
+
+test_expect_success TTY 'promisor.quiet=false shows progress messages' '
+ rm -rf repo &&
+ git clone --filter=blob:none "file://$(pwd)/server" repo &&
+ git -C repo config promisor.quiet "false" &&
+
+ test_terminal git -C repo cat-file -p foo:foo.t 2>err &&
+
+ # Ensure that progress messages are written
+ grep "Receiving objects" err
+'
+
+test_expect_success TTY 'promisor.quiet=true does not show progress messages' '
+ rm -rf repo &&
+ git clone --filter=blob:none "file://$(pwd)/server" repo &&
+ git -C repo config promisor.quiet "true" &&
+
+ test_terminal git -C repo cat-file -p foo:foo.t 2>err &&
+
+ # Ensure that no progress messages are written
+ ! grep "Receiving objects" err
+'
+
+test_expect_success TTY 'promisor.quiet=unconfigured shows progress messages' '
+ rm -rf repo &&
+ git clone --filter=blob:none "file://$(pwd)/server" repo &&
+
+ test_terminal git -C repo cat-file -p foo:foo.t 2>err &&
+
+ # Ensure that progress messages are written
+ grep "Receiving objects" err
+'
+
. "$TEST_DIRECTORY"/lib-httpd.sh
start_httpd
diff --git a/t/t0411-clone-from-partial.sh b/t/t0411-clone-from-partial.sh
new file mode 100755
index 0000000000..932bf2067d
--- /dev/null
+++ b/t/t0411-clone-from-partial.sh
@@ -0,0 +1,79 @@
+#!/bin/sh
+
+test_description='check that local clone does not fetch from promisor remotes'
+
+TEST_PASSES_SANITIZE_LEAK=true
+. ./test-lib.sh
+
+test_expect_success 'create evil repo' '
+ git init tmp &&
+ test_commit -C tmp a &&
+ git -C tmp config uploadpack.allowfilter 1 &&
+ git clone --filter=blob:none --no-local --no-checkout tmp evil &&
+ rm -rf tmp &&
+
+ git -C evil config remote.origin.uploadpack \"\$TRASH_DIRECTORY/fake-upload-pack\" &&
+ write_script fake-upload-pack <<-\EOF &&
+ echo >&2 "fake-upload-pack running"
+ >"$TRASH_DIRECTORY/script-executed"
+ exit 1
+ EOF
+ export TRASH_DIRECTORY &&
+
+ # empty shallow file disables local clone optimization
+ >evil/.git/shallow
+'
+
+test_expect_success 'local clone must not fetch from promisor remote and execute script' '
+ rm -f script-executed &&
+ test_must_fail git clone \
+ --upload-pack="GIT_TEST_ASSUME_DIFFERENT_OWNER=true git-upload-pack" \
+ evil clone1 2>err &&
+ test_grep "detected dubious ownership" err &&
+ test_grep ! "fake-upload-pack running" err &&
+ test_path_is_missing script-executed
+'
+
+test_expect_success 'clone from file://... must not fetch from promisor remote and execute script' '
+ rm -f script-executed &&
+ test_must_fail git clone \
+ --upload-pack="GIT_TEST_ASSUME_DIFFERENT_OWNER=true git-upload-pack" \
+ "file://$(pwd)/evil" clone2 2>err &&
+ test_grep "detected dubious ownership" err &&
+ test_grep ! "fake-upload-pack running" err &&
+ test_path_is_missing script-executed
+'
+
+test_expect_success 'fetch from file://... must not fetch from promisor remote and execute script' '
+ rm -f script-executed &&
+ test_must_fail git fetch \
+ --upload-pack="GIT_TEST_ASSUME_DIFFERENT_OWNER=true git-upload-pack" \
+ "file://$(pwd)/evil" 2>err &&
+ test_grep "detected dubious ownership" err &&
+ test_grep ! "fake-upload-pack running" err &&
+ test_path_is_missing script-executed
+'
+
+test_expect_success 'pack-objects should fetch from promisor remote and execute script' '
+ rm -f script-executed &&
+ echo "HEAD" | test_must_fail git -C evil pack-objects --revs --stdout >/dev/null 2>err &&
+ test_grep "fake-upload-pack running" err &&
+ test_path_is_file script-executed
+'
+
+test_expect_success 'clone from promisor remote does not lazy-fetch by default' '
+ rm -f script-executed &&
+ test_must_fail git clone evil no-lazy 2>err &&
+ test_grep "lazy fetching disabled" err &&
+ test_path_is_missing script-executed
+'
+
+test_expect_success 'promisor lazy-fetching can be re-enabled' '
+ rm -f script-executed &&
+ test_must_fail env GIT_NO_LAZY_FETCH=0 \
+ git clone evil lazy-ok 2>err &&
+ test_grep "fake-upload-pack running" err &&
+ test_path_is_file script-executed
+'
+
+test_done
diff --git a/t/t0450/txt-help-mismatches b/t/t0450/txt-help-mismatches
index a0777acd66..28003f18c9 100644
--- a/t/t0450/txt-help-mismatches
+++ b/t/t0450/txt-help-mismatches
@@ -10,7 +10,6 @@ checkout
checkout-index
clone
column
-config
credential
credential-cache
credential-store
diff --git a/t/t0600-reffiles-backend.sh b/t/t0600-reffiles-backend.sh
index 64214340e7..20df336cc3 100755
--- a/t/t0600-reffiles-backend.sh
+++ b/t/t0600-reffiles-backend.sh
@@ -4,16 +4,12 @@ test_description='Test reffiles backend'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+GIT_TEST_DEFAULT_REF_FORMAT=files
+export GIT_TEST_DEFAULT_REF_FORMAT
TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
-if ! test_have_prereq REFFILES
-then
- skip_all='skipping reffiles specific tests'
- test_done
-fi
-
test_expect_success 'setup' '
git commit --allow-empty -m Initial &&
C=$(git rev-parse HEAD) &&
@@ -95,82 +91,82 @@ test_expect_success 'empty directory should not fool 1-arg delete' '
git update-ref --stdin
'
-test_expect_success 'non-empty directory blocks create' '
+test_expect_success 'non-empty directory blocks create' - <<\EOT
prefix=refs/ne-create &&
mkdir -p .git/$prefix/foo/bar &&
: >.git/$prefix/foo/bar/baz.lock &&
test_when_finished "rm -f .git/$prefix/foo/bar/baz.lock" &&
cat >expected <<-EOF &&
- fatal: cannot lock ref $SQ$prefix/foo$SQ: there is a non-empty directory $SQ.git/$prefix/foo$SQ blocking reference $SQ$prefix/foo$SQ
+ fatal: cannot lock ref '$prefix/foo': there is a non-empty directory '.git/$prefix/foo' blocking reference '$prefix/foo'
EOF
printf "%s\n" "update $prefix/foo $C" |
test_must_fail git update-ref --stdin 2>output.err &&
test_cmp expected output.err &&
cat >expected <<-EOF &&
- fatal: cannot lock ref $SQ$prefix/foo$SQ: unable to resolve reference $SQ$prefix/foo$SQ
+ fatal: cannot lock ref '$prefix/foo': unable to resolve reference '$prefix/foo'
EOF
printf "%s\n" "update $prefix/foo $D $C" |
test_must_fail git update-ref --stdin 2>output.err &&
test_cmp expected output.err
-'
+EOT
-test_expect_success 'broken reference blocks create' '
+test_expect_success 'broken reference blocks create' - <<\EOT
prefix=refs/broken-create &&
mkdir -p .git/$prefix &&
echo "gobbledigook" >.git/$prefix/foo &&
test_when_finished "rm -f .git/$prefix/foo" &&
cat >expected <<-EOF &&
- fatal: cannot lock ref $SQ$prefix/foo$SQ: unable to resolve reference $SQ$prefix/foo$SQ: reference broken
+ fatal: cannot lock ref '$prefix/foo': unable to resolve reference '$prefix/foo': reference broken
EOF
printf "%s\n" "update $prefix/foo $C" |
test_must_fail git update-ref --stdin 2>output.err &&
test_cmp expected output.err &&
cat >expected <<-EOF &&
- fatal: cannot lock ref $SQ$prefix/foo$SQ: unable to resolve reference $SQ$prefix/foo$SQ: reference broken
+ fatal: cannot lock ref '$prefix/foo': unable to resolve reference '$prefix/foo': reference broken
EOF
printf "%s\n" "update $prefix/foo $D $C" |
test_must_fail git update-ref --stdin 2>output.err &&
test_cmp expected output.err
-'
+EOT
-test_expect_success 'non-empty directory blocks indirect create' '
+test_expect_success 'non-empty directory blocks indirect create' - <<\EOT
prefix=refs/ne-indirect-create &&
git symbolic-ref $prefix/symref $prefix/foo &&
mkdir -p .git/$prefix/foo/bar &&
: >.git/$prefix/foo/bar/baz.lock &&
test_when_finished "rm -f .git/$prefix/foo/bar/baz.lock" &&
cat >expected <<-EOF &&
- fatal: cannot lock ref $SQ$prefix/symref$SQ: there is a non-empty directory $SQ.git/$prefix/foo$SQ blocking reference $SQ$prefix/foo$SQ
+ fatal: cannot lock ref '$prefix/symref': there is a non-empty directory '.git/$prefix/foo' blocking reference '$prefix/foo'
EOF
printf "%s\n" "update $prefix/symref $C" |
test_must_fail git update-ref --stdin 2>output.err &&
test_cmp expected output.err &&
cat >expected <<-EOF &&
- fatal: cannot lock ref $SQ$prefix/symref$SQ: unable to resolve reference $SQ$prefix/foo$SQ
+ fatal: cannot lock ref '$prefix/symref': unable to resolve reference '$prefix/foo'
EOF
printf "%s\n" "update $prefix/symref $D $C" |
test_must_fail git update-ref --stdin 2>output.err &&
test_cmp expected output.err
-'
+EOT
-test_expect_success 'broken reference blocks indirect create' '
+test_expect_success 'broken reference blocks indirect create' - <<\EOT
prefix=refs/broken-indirect-create &&
git symbolic-ref $prefix/symref $prefix/foo &&
echo "gobbledigook" >.git/$prefix/foo &&
test_when_finished "rm -f .git/$prefix/foo" &&
cat >expected <<-EOF &&
- fatal: cannot lock ref $SQ$prefix/symref$SQ: unable to resolve reference $SQ$prefix/foo$SQ: reference broken
+ fatal: cannot lock ref '$prefix/symref': unable to resolve reference '$prefix/foo': reference broken
EOF
printf "%s\n" "update $prefix/symref $C" |
test_must_fail git update-ref --stdin 2>output.err &&
test_cmp expected output.err &&
cat >expected <<-EOF &&
- fatal: cannot lock ref $SQ$prefix/symref$SQ: unable to resolve reference $SQ$prefix/foo$SQ: reference broken
+ fatal: cannot lock ref '$prefix/symref': unable to resolve reference '$prefix/foo': reference broken
EOF
printf "%s\n" "update $prefix/symref $D $C" |
test_must_fail git update-ref --stdin 2>output.err &&
test_cmp expected output.err
-'
+EOT
test_expect_success 'no bogus intermediate values during delete' '
prefix=refs/slow-transaction &&
@@ -228,7 +224,7 @@ test_expect_success 'no bogus intermediate values during delete' '
test_must_fail git rev-parse --verify --quiet $prefix/foo
'
-test_expect_success 'delete fails cleanly if packed-refs file is locked' '
+test_expect_success 'delete fails cleanly if packed-refs file is locked' - <<\EOT
prefix=refs/locked-packed-refs &&
# Set up a reference with differing loose and packed versions:
git update-ref $prefix/foo $C &&
@@ -240,9 +236,9 @@ test_expect_success 'delete fails cleanly if packed-refs file is locked' '
test_when_finished "rm -f .git/packed-refs.lock" &&
test_must_fail git update-ref -d $prefix/foo >out 2>err &&
git for-each-ref $prefix >actual &&
- test_grep "Unable to create $SQ.*packed-refs.lock$SQ: " err &&
+ test_grep "Unable to create '.*packed-refs.lock': " err &&
test_cmp unchanged actual
-'
+EOT
test_expect_success 'delete fails cleanly if packed-refs.new write fails' '
# Setup and expectations are similar to the test above.
@@ -428,7 +424,7 @@ test_expect_success SYMLINKS 'git branch -m with symlinked .git/refs' '
test_when_finished "rm -rf subdir" &&
git init --bare subdir &&
- rm -rfv subdir/refs subdir/objects subdir/packed-refs &&
+ rm -rf subdir/refs subdir/objects subdir/packed-refs &&
ln -s ../.git/refs subdir/refs &&
ln -s ../.git/objects subdir/objects &&
ln -s ../.git/packed-refs subdir/packed-refs &&
@@ -472,4 +468,36 @@ test_expect_success POSIXPERM 'git reflog expire honors core.sharedRepository' '
esac
'
+test_expect_success SYMLINKS 'symref transaction supports symlinks' '
+ test_when_finished "git symbolic-ref -d TEST_SYMREF_HEAD" &&
+ git update-ref refs/heads/new @ &&
+ test_config core.prefersymlinkrefs true &&
+ cat >stdin <<-EOF &&
+ start
+ symref-create TEST_SYMREF_HEAD refs/heads/new
+ prepare
+ commit
+ EOF
+ git update-ref --no-deref --stdin <stdin &&
+ test_path_is_symlink .git/TEST_SYMREF_HEAD &&
+ test "$(test_readlink .git/TEST_SYMREF_HEAD)" = refs/heads/new
+'
+
+test_expect_success 'symref transaction supports false symlink config' '
+ test_when_finished "git symbolic-ref -d TEST_SYMREF_HEAD" &&
+ git update-ref refs/heads/new @ &&
+ test_config core.prefersymlinkrefs false &&
+ cat >stdin <<-EOF &&
+ start
+ symref-create TEST_SYMREF_HEAD refs/heads/new
+ prepare
+ commit
+ EOF
+ git update-ref --no-deref --stdin <stdin &&
+ test_path_is_file .git/TEST_SYMREF_HEAD &&
+ git symbolic-ref TEST_SYMREF_HEAD >actual &&
+ echo refs/heads/new >expect &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t0601-reffiles-pack-refs.sh b/t/t0601-reffiles-pack-refs.sh
index 7d4ab0b91a..60a544b8ee 100755
--- a/t/t0601-reffiles-pack-refs.sh
+++ b/t/t0601-reffiles-pack-refs.sh
@@ -9,18 +9,15 @@ test_description='git pack-refs should not change the branch semantic
This test runs git pack-refs and git show-ref and checks that the branch
semantic is still the same.
'
+
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+GIT_TEST_DEFAULT_REF_FORMAT=files
+export GIT_TEST_DEFAULT_REF_FORMAT
TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
-if ! test_have_prereq REFFILES
-then
- skip_all='skipping reffiles specific tests'
- test_done
-fi
-
test_expect_success 'enable reflogs' '
git config core.logallrefupdates true
'
diff --git a/t/t0610-reftable-basics.sh b/t/t0610-reftable-basics.sh
index 178791e086..b06c46999d 100755
--- a/t/t0610-reftable-basics.sh
+++ b/t/t0610-reftable-basics.sh
@@ -4,17 +4,15 @@
#
test_description='reftable basics'
+
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+GIT_TEST_DEFAULT_REF_FORMAT=reftable
+export GIT_TEST_DEFAULT_REF_FORMAT
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
-if ! test_have_prereq REFTABLE
-then
- skip_all='skipping reftable tests; set GIT_TEST_DEFAULT_REF_FORMAT=reftable'
- test_done
-fi
-
INVALID_OID=$(test_oid 001)
test_expect_success 'init: creates basic reftable structures' '
@@ -81,8 +79,8 @@ test_expect_success 'init: reinitializing reftable with files backend fails' '
'
test_expect_perms () {
- local perms="$1"
- local file="$2"
+ local perms="$1" &&
+ local file="$2" &&
local actual="$(ls -l "$file")" &&
case "$actual" in
@@ -286,7 +284,7 @@ test_expect_success 'ref transaction: creating symbolic ref fails with F/D confl
git init repo &&
test_commit -C repo A &&
cat >expect <<-EOF &&
- error: unable to write symref for refs/heads: file/directory conflict
+ error: ${SQ}refs/heads/main${SQ} exists; cannot create ${SQ}refs/heads${SQ}
EOF
test_must_fail git -C repo symbolic-ref refs/heads refs/heads/foo 2>err &&
test_cmp expect err
@@ -854,6 +852,39 @@ test_expect_success 'reflog: updates via HEAD update HEAD reflog' '
)
'
+test_expect_success 'branch: copying branch with D/F conflict' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit A &&
+ git branch branch &&
+ cat >expect <<-EOF &&
+ error: ${SQ}refs/heads/branch${SQ} exists; cannot create ${SQ}refs/heads/branch/moved${SQ}
+ fatal: branch copy failed
+ EOF
+ test_must_fail git branch -c branch branch/moved 2>err &&
+ test_cmp expect err
+ )
+'
+
+test_expect_success 'branch: moving branch with D/F conflict' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit A &&
+ git branch branch &&
+ git branch conflict &&
+ cat >expect <<-EOF &&
+ error: ${SQ}refs/heads/conflict${SQ} exists; cannot create ${SQ}refs/heads/conflict/moved${SQ}
+ fatal: branch rename failed
+ EOF
+ test_must_fail git branch -m branch conflict/moved 2>err &&
+ test_cmp expect err
+ )
+'
+
test_expect_success 'worktree: adding worktree creates separate stack' '
test_when_finished "rm -rf repo worktree" &&
git init repo &&
diff --git a/t/t0611-reftable-httpd.sh b/t/t0611-reftable-httpd.sh
index 5e05b9c1f2..2805995cc8 100755
--- a/t/t0611-reftable-httpd.sh
+++ b/t/t0611-reftable-httpd.sh
@@ -2,6 +2,7 @@
test_description='reftable HTTPD tests'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-httpd.sh
diff --git a/t/t0612-reftable-jgit-compatibility.sh b/t/t0612-reftable-jgit-compatibility.sh
new file mode 100755
index 0000000000..84922153ab
--- /dev/null
+++ b/t/t0612-reftable-jgit-compatibility.sh
@@ -0,0 +1,133 @@
+#!/bin/sh
+
+test_description='reftables are compatible with JGit'
+
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+GIT_TEST_DEFAULT_REF_FORMAT=reftable
+export GIT_TEST_DEFAULT_REF_FORMAT
+
+# JGit does not support the 'link' DIRC extension.
+GIT_TEST_SPLIT_INDEX=0
+export GIT_TEST_SPLIT_INDEX
+
+TEST_PASSES_SANITIZE_LEAK=true
+. ./test-lib.sh
+
+if ! test_have_prereq JGIT
+then
+ skip_all='skipping reftable JGit tests; JGit is not present in PATH'
+ test_done
+fi
+
+if ! test_have_prereq SHA1
+then
+ skip_all='skipping reftable JGit tests; JGit does not support SHA256 reftables'
+ test_done
+fi
+
+test_commit_jgit () {
+ touch "$1" &&
+ jgit add "$1" &&
+ jgit commit -m "$1"
+}
+
+test_same_refs () {
+ git show-ref --head >cgit.actual &&
+ jgit show-ref >jgit-tabs.actual &&
+ tr "\t" " " <jgit-tabs.actual >jgit.actual &&
+ test_cmp cgit.actual jgit.actual
+}
+
+test_same_ref () {
+ git rev-parse "$1" >cgit.actual &&
+ jgit rev-parse "$1" >jgit.actual &&
+ test_cmp cgit.actual jgit.actual
+}
+
+test_same_reflog () {
+ git reflog "$*" >cgit.actual &&
+ jgit reflog "$*" >jgit-newline.actual &&
+ sed '/^$/d' <jgit-newline.actual >jgit.actual &&
+ test_cmp cgit.actual jgit.actual
+}
+
+test_expect_success 'CGit repository can be read by JGit' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit A &&
+ test_same_refs &&
+ test_same_ref HEAD &&
+ test_same_reflog HEAD
+ )
+'
+
+test_expect_success 'JGit repository can be read by CGit' '
+ test_when_finished "rm -rf repo" &&
+ jgit init repo &&
+ (
+ cd repo &&
+
+ touch file &&
+ jgit add file &&
+ jgit commit -m "initial commit" &&
+
+ # Note that we must convert the ref storage after we have
+ # written the default branch. Otherwise JGit will end up with
+ # no HEAD at all.
+ jgit convert-ref-storage --format=reftable &&
+
+ test_same_refs &&
+ test_same_ref HEAD &&
+ # Interestingly, JGit cannot read its own reflog here. CGit can
+ # though.
+ printf "%s HEAD@{0}: commit (initial): initial commit" "$(git rev-parse --short HEAD)" >expect &&
+ git reflog HEAD >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'mixed writes from JGit and CGit' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+
+ test_commit A &&
+ test_commit_jgit B &&
+ test_commit C &&
+ test_commit_jgit D &&
+
+ test_same_refs &&
+ test_same_ref HEAD &&
+ test_same_reflog HEAD
+ )
+'
+
+test_expect_success 'JGit can read multi-level index' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+
+ test_commit A &&
+ awk "
+ BEGIN {
+ print \"start\";
+ for (i = 0; i < 10000; i++)
+ printf \"create refs/heads/branch-%d HEAD\n\", i;
+ print \"commit\";
+ }
+ " >input &&
+ git update-ref --stdin <input &&
+
+ test_same_refs &&
+ test_same_ref refs/heads/branch-1 &&
+ test_same_ref refs/heads/branch-5738 &&
+ test_same_ref refs/heads/branch-9999
+ )
+'
+
+test_done
diff --git a/t/t0613-reftable-write-options.sh b/t/t0613-reftable-write-options.sh
new file mode 100755
index 0000000000..b1c6c97524
--- /dev/null
+++ b/t/t0613-reftable-write-options.sh
@@ -0,0 +1,287 @@
+#!/bin/sh
+
+test_description='reftable write options'
+
+GIT_TEST_DEFAULT_REF_FORMAT=reftable
+export GIT_TEST_DEFAULT_REF_FORMAT
+# Disable auto-compaction for all tests as we explicitly control repacking of
+# refs.
+GIT_TEST_REFTABLE_AUTOCOMPACTION=false
+export GIT_TEST_REFTABLE_AUTOCOMPACTION
+# Block sizes depend on the hash function, so we force SHA1 here.
+GIT_TEST_DEFAULT_HASH=sha1
+export GIT_TEST_DEFAULT_HASH
+# Block sizes also depend on the actual refs we write, so we force "master" to
+# be the default initial branch name.
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=master
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
+TEST_PASSES_SANITIZE_LEAK=true
+. ./test-lib.sh
+
+test_expect_success 'default write options' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit initial &&
+ git pack-refs &&
+ cat >expect <<-EOF &&
+ header:
+ block_size: 4096
+ ref:
+ - length: 129
+ restarts: 2
+ log:
+ - length: 262
+ restarts: 2
+ EOF
+ test-tool dump-reftable -b .git/reftable/*.ref >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'disabled reflog writes no log blocks' '
+ test_config_global core.logAllRefUpdates false &&
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit initial &&
+ git pack-refs &&
+ cat >expect <<-EOF &&
+ header:
+ block_size: 4096
+ ref:
+ - length: 129
+ restarts: 2
+ EOF
+ test-tool dump-reftable -b .git/reftable/*.ref >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'many refs results in multiple blocks' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit initial &&
+ for i in $(test_seq 200)
+ do
+ printf "update refs/heads/branch-%d HEAD\n" "$i" ||
+ return 1
+ done >input &&
+ git update-ref --stdin <input &&
+ git pack-refs &&
+
+ cat >expect <<-EOF &&
+ header:
+ block_size: 4096
+ ref:
+ - length: 4049
+ restarts: 11
+ - length: 1136
+ restarts: 3
+ log:
+ - length: 4041
+ restarts: 4
+ - length: 4015
+ restarts: 3
+ - length: 4014
+ restarts: 3
+ - length: 4012
+ restarts: 3
+ - length: 3289
+ restarts: 3
+ EOF
+ test-tool dump-reftable -b .git/reftable/*.ref >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'tiny block size leads to error' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit initial &&
+ cat >expect <<-EOF &&
+ error: unable to compact stack: entry too large
+ EOF
+ test_must_fail git -c reftable.blockSize=50 pack-refs 2>err &&
+ test_cmp expect err
+ )
+'
+
+test_expect_success 'small block size leads to multiple ref blocks' '
+ test_config_global core.logAllRefUpdates false &&
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit A &&
+ test_commit B &&
+ git -c reftable.blockSize=100 pack-refs &&
+
+ cat >expect <<-EOF &&
+ header:
+ block_size: 100
+ ref:
+ - length: 53
+ restarts: 1
+ - length: 74
+ restarts: 1
+ - length: 38
+ restarts: 1
+ EOF
+ test-tool dump-reftable -b .git/reftable/*.ref >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'small block size fails with large reflog message' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit A &&
+ perl -e "print \"a\" x 500" >logmsg &&
+ cat >expect <<-EOF &&
+ fatal: update_ref failed for ref ${SQ}refs/heads/logme${SQ}: reftable: transaction failure: entry too large
+ EOF
+ test_must_fail git -c reftable.blockSize=100 \
+ update-ref -m "$(cat logmsg)" refs/heads/logme HEAD 2>err &&
+ test_cmp expect err
+ )
+'
+
+test_expect_success 'block size exceeding maximum supported size' '
+ test_config_global core.logAllRefUpdates false &&
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit A &&
+ test_commit B &&
+ cat >expect <<-EOF &&
+ fatal: reftable block size cannot exceed 16MB
+ EOF
+ test_must_fail git -c reftable.blockSize=16777216 pack-refs 2>err &&
+ test_cmp expect err
+ )
+'
+
+test_expect_success 'restart interval at every single record' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit initial &&
+ for i in $(test_seq 10)
+ do
+ printf "update refs/heads/branch-%d HEAD\n" "$i" ||
+ return 1
+ done >input &&
+ git update-ref --stdin <input &&
+ git -c reftable.restartInterval=1 pack-refs &&
+
+ cat >expect <<-EOF &&
+ header:
+ block_size: 4096
+ ref:
+ - length: 566
+ restarts: 13
+ log:
+ - length: 1393
+ restarts: 12
+ EOF
+ test-tool dump-reftable -b .git/reftable/*.ref >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'restart interval exceeding maximum supported interval' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit initial &&
+ cat >expect <<-EOF &&
+ fatal: reftable block size cannot exceed 65535
+ EOF
+ test_must_fail git -c reftable.restartInterval=65536 pack-refs 2>err &&
+ test_cmp expect err
+ )
+'
+
+test_expect_success 'object index gets written by default with ref index' '
+ test_config_global core.logAllRefUpdates false &&
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit initial &&
+ for i in $(test_seq 5)
+ do
+ printf "update refs/heads/branch-%d HEAD\n" "$i" ||
+ return 1
+ done >input &&
+ git update-ref --stdin <input &&
+ git -c reftable.blockSize=100 pack-refs &&
+
+ cat >expect <<-EOF &&
+ header:
+ block_size: 100
+ ref:
+ - length: 53
+ restarts: 1
+ - length: 95
+ restarts: 1
+ - length: 71
+ restarts: 1
+ - length: 80
+ restarts: 1
+ obj:
+ - length: 11
+ restarts: 1
+ EOF
+ test-tool dump-reftable -b .git/reftable/*.ref >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'object index can be disabled' '
+ test_config_global core.logAllRefUpdates false &&
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit initial &&
+ for i in $(test_seq 5)
+ do
+ printf "update refs/heads/branch-%d HEAD\n" "$i" ||
+ return 1
+ done >input &&
+ git update-ref --stdin <input &&
+ git -c reftable.blockSize=100 -c reftable.indexObjects=false pack-refs &&
+
+ cat >expect <<-EOF &&
+ header:
+ block_size: 100
+ ref:
+ - length: 53
+ restarts: 1
+ - length: 95
+ restarts: 1
+ - length: 71
+ restarts: 1
+ - length: 80
+ restarts: 1
+ EOF
+ test-tool dump-reftable -b .git/reftable/*.ref >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_done
diff --git a/t/t1004-read-tree-m-u-wf.sh b/t/t1004-read-tree-m-u-wf.sh
index 11bf10424f..2b9720b0fe 100755
--- a/t/t1004-read-tree-m-u-wf.sh
+++ b/t/t1004-read-tree-m-u-wf.sh
@@ -5,6 +5,7 @@ test_description='read-tree -m -u checks working tree files'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-read-tree.sh
diff --git a/t/t1006-cat-file.sh b/t/t1006-cat-file.sh
index e12b221972..ff9bf213aa 100755
--- a/t/t1006-cat-file.sh
+++ b/t/t1006-cat-file.sh
@@ -1294,4 +1294,34 @@ test_expect_success 'batch-command flush without --buffer' '
grep "^fatal:.*flush is only for --buffer mode.*" err
'
+script='
+use warnings;
+use strict;
+use IPC::Open2;
+my ($opt, $oid, $expect, @pfx) = @ARGV;
+my @cmd = (qw(git cat-file), $opt);
+my $pid = open2(my $out, my $in, @cmd) or die "open2: @cmd";
+print $in @pfx, $oid, "\n" or die "print $!";
+my $rvec = "";
+vec($rvec, fileno($out), 1) = 1;
+select($rvec, undef, undef, 30) or die "no response to `@pfx $oid` from @cmd";
+my $info = <$out>;
+chop($info) eq "\n" or die "no LF";
+$info eq $expect or die "`$info` != `$expect`";
+close $in or die "close in $!";
+close $out or die "close out $!";
+waitpid $pid, 0;
+$? == 0 or die "\$?=$?";
+'
+
+expect="$hello_oid blob $hello_size"
+
+test_expect_success PERL '--batch-check is unbuffered by default' '
+ perl -e "$script" -- --batch-check $hello_oid "$expect"
+'
+
+test_expect_success PERL '--batch-command info is unbuffered by default' '
+ perl -e "$script" -- --batch-command $hello_oid "$expect" "info "
+'
+
test_done
diff --git a/t/t1007-hash-object.sh b/t/t1007-hash-object.sh
index 64aea38486..d73a5cc237 100755
--- a/t/t1007-hash-object.sh
+++ b/t/t1007-hash-object.sh
@@ -260,4 +260,10 @@ test_expect_success '--literally with extra-long type' '
echo example | git hash-object -t $t --literally --stdin
'
+test_expect_success '--stdin outside of repository (uses SHA-1)' '
+ nongit git hash-object --stdin <hello >actual &&
+ echo "$(test_oid --hash=sha1 hello)" >expect &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t1013-read-tree-submodule.sh b/t/t1013-read-tree-submodule.sh
index bfc90d4cf2..cf8b94ebed 100755
--- a/t/t1013-read-tree-submodule.sh
+++ b/t/t1013-read-tree-submodule.sh
@@ -2,6 +2,7 @@
test_description='read-tree can handle submodules'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-submodule-update.sh
diff --git a/t/t1015-read-index-unmerged.sh b/t/t1015-read-index-unmerged.sh
index 55d22da32c..da737a32a2 100755
--- a/t/t1015-read-index-unmerged.sh
+++ b/t/t1015-read-index-unmerged.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='Test various callers of read_index_unmerged'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup modify/delete + directory/file conflict' '
diff --git a/t/t1021-rerere-in-workdir.sh b/t/t1021-rerere-in-workdir.sh
index 0b892894eb..69bf9476cb 100755
--- a/t/t1021-rerere-in-workdir.sh
+++ b/t/t1021-rerere-in-workdir.sh
@@ -4,6 +4,7 @@ test_description='rerere run in a workdir'
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 SYMLINKS setup '
diff --git a/t/t1090-sparse-checkout-scope.sh b/t/t1090-sparse-checkout-scope.sh
index 3a14218b24..da0e7714d5 100755
--- a/t/t1090-sparse-checkout-scope.sh
+++ b/t/t1090-sparse-checkout-scope.sh
@@ -6,6 +6,7 @@ GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
TEST_CREATE_REPO_NO_TEMPLATE=1
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
diff --git a/t/t1091-sparse-checkout-builtin.sh b/t/t1091-sparse-checkout-builtin.sh
index ab3a105fff..8c5cd651b4 100755
--- a/t/t1091-sparse-checkout-builtin.sh
+++ b/t/t1091-sparse-checkout-builtin.sh
@@ -8,6 +8,7 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
GIT_TEST_SPLIT_INDEX=false
export GIT_TEST_SPLIT_INDEX
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
list_files() {
diff --git a/t/t1092-sparse-checkout-compatibility.sh b/t/t1092-sparse-checkout-compatibility.sh
index 2f1ae5fd3b..a2c0e1b4dc 100755
--- a/t/t1092-sparse-checkout-compatibility.sh
+++ b/t/t1092-sparse-checkout-compatibility.sh
@@ -159,7 +159,10 @@ init_repos () {
git -C sparse-checkout sparse-checkout set deep &&
git -C sparse-index sparse-checkout init --cone --sparse-index &&
test_cmp_config -C sparse-index true index.sparse &&
- git -C sparse-index sparse-checkout set deep
+ git -C sparse-index sparse-checkout set deep &&
+
+ # Disable this message to keep stderr the same.
+ git -C sparse-index config advice.sparseIndexExpanded false
}
init_repos_as_submodules () {
@@ -2331,4 +2334,15 @@ test_expect_success 'sparse-index is not expanded: check-attr' '
ensure_not_expanded check-attr -a --cached -- folder1/a
'
+test_expect_success 'advice.sparseIndexExpanded' '
+ init_repos &&
+
+ git -C sparse-index config --unset advice.sparseIndexExpanded &&
+ git -C sparse-index sparse-checkout set deep/deeper1 &&
+ mkdir -p sparse-index/deep/deeper2/deepest &&
+ touch sparse-index/deep/deeper2/deepest/bogus &&
+ git -C sparse-index status 2>err &&
+ grep "The sparse index is expanding to a full index" err
+'
+
test_done
diff --git a/t/t1300-config.sh b/t/t1300-config.sh
index 9b65d9eaf5..9de2d95f06 100755
--- a/t/t1300-config.sh
+++ b/t/t1300-config.sh
@@ -11,6 +11,34 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
+for mode in legacy subcommands
+do
+
+case "$mode" in
+legacy)
+ mode_prefix="--"
+ mode_get=""
+ mode_get_all="--get-all"
+ mode_get_regexp="--get-regexp"
+ mode_set=""
+ mode_replace_all="--replace-all"
+ mode_unset="--unset"
+ mode_unset_all="--unset-all"
+ ;;
+subcommands)
+ mode_prefix=""
+ mode_get="get"
+ mode_get_all="get --all"
+ mode_get_regexp="get --regexp --all --show-names"
+ mode_set="set"
+ mode_replace_all="set --all"
+ mode_unset="unset"
+ mode_unset_all="unset --all"
+ ;;
+*)
+ BUG "unknown mode $mode";;
+esac
+
test_expect_success 'setup whitespace config' '
sed -e "s/^|//" \
-e "s/[$]$//" \
@@ -112,7 +140,7 @@ cat > expect << EOF
penguin = little blue
EOF
test_expect_success 'initial' '
- git config section.penguin "little blue" &&
+ git config ${mode_set} section.penguin "little blue" &&
test_cmp expect .git/config
'
@@ -122,7 +150,7 @@ cat > expect << EOF
Movie = BadPhysics
EOF
test_expect_success 'mixed case' '
- git config Section.Movie BadPhysics &&
+ git config ${mode_set} Section.Movie BadPhysics &&
test_cmp expect .git/config
'
@@ -134,7 +162,7 @@ cat > expect << EOF
WhatEver = Second
EOF
test_expect_success 'similar section' '
- git config Sections.WhatEver Second &&
+ git config ${mode_set} Sections.WhatEver Second &&
test_cmp expect .git/config
'
@@ -147,7 +175,7 @@ cat > expect << EOF
WhatEver = Second
EOF
test_expect_success 'uppercase section' '
- git config SECTION.UPPERCASE true &&
+ git config ${mode_set} SECTION.UPPERCASE true &&
test_cmp expect .git/config
'
@@ -174,8 +202,8 @@ EOF
test_expect_success 'append comments' '
git config --replace-all --comment="Pygoscelis papua" section.penguin gentoo &&
- git config --comment="find fish" section.disposition peckish &&
- git config --comment="#abc" section.foo bar &&
+ git config ${mode_set} --comment="find fish" section.disposition peckish &&
+ git config ${mode_set} --comment="#abc" section.foo bar &&
git config --comment="and comment" section.spsp value &&
git config --comment=" # and comment" section.htsp value &&
@@ -184,7 +212,7 @@ test_expect_success 'append comments' '
'
test_expect_success 'Prohibited LF in comment' '
- test_must_fail git config --comment="a${LF}b" section.k v
+ test_must_fail git config ${mode_set} --comment="a${LF}b" section.k v
'
test_expect_success 'non-match result' 'test_cmp expect .git/config'
@@ -235,7 +263,7 @@ foo = bar
EOF
test_expect_success 'unset with cont. lines' '
- git config --unset beta.baz
+ git config ${mode_unset} beta.baz
'
cat > expect <<\EOF
@@ -262,7 +290,7 @@ EOF
cp .git/config .git/config2
test_expect_success 'multiple unset' '
- git config --unset-all beta.haha
+ git config ${mode_unset_all} beta.haha
'
cat > expect << EOF
@@ -281,14 +309,14 @@ test_expect_success 'multiple unset is correct' '
cp .git/config2 .git/config
test_expect_success '--replace-all missing value' '
- test_must_fail git config --replace-all beta.haha &&
+ test_must_fail git config ${mode_replace_all} beta.haha &&
test_cmp .git/config2 .git/config
'
rm .git/config2
test_expect_success '--replace-all' '
- git config --replace-all beta.haha gamma
+ git config ${mode_replace_all} beta.haha gamma
'
cat > expect << EOF
@@ -315,7 +343,7 @@ noIndent= sillyValue ; 'nother silly comment
[nextSection] noNewline = ouch
EOF
test_expect_success 'really mean test' '
- git config beta.haha alpha &&
+ git config ${mode_set} beta.haha alpha &&
test_cmp expect .git/config
'
@@ -330,7 +358,7 @@ noIndent= sillyValue ; 'nother silly comment
nonewline = wow
EOF
test_expect_success 'really really mean test' '
- git config nextsection.nonewline wow &&
+ git config ${mode_set} nextsection.nonewline wow &&
test_cmp expect .git/config
'
@@ -348,7 +376,7 @@ noIndent= sillyValue ; 'nother silly comment
nonewline = wow
EOF
test_expect_success 'unset' '
- git config --unset beta.haha &&
+ git config ${mode_unset} beta.haha &&
test_cmp expect .git/config
'
@@ -384,7 +412,7 @@ test_expect_success 'multi-valued get-all returns all' '
wow
wow2 for me
EOF
- git config --get-all nextsection.nonewline >actual &&
+ git config ${mode_get_all} nextsection.nonewline >actual &&
test_cmp expect actual
'
@@ -404,11 +432,11 @@ test_expect_success 'multivar replace' '
'
test_expect_success 'ambiguous unset' '
- test_must_fail git config --unset nextsection.nonewline
+ test_must_fail git config ${mode_unset} nextsection.nonewline
'
test_expect_success 'invalid unset' '
- test_must_fail git config --unset somesection.nonewline
+ test_must_fail git config ${mode_unset} somesection.nonewline
'
cat > expect << EOF
@@ -422,7 +450,12 @@ noIndent= sillyValue ; 'nother silly comment
EOF
test_expect_success 'multivar unset' '
- git config --unset nextsection.nonewline "wow3$" &&
+ case "$mode" in
+ legacy)
+ git config --unset nextsection.nonewline "wow3$";;
+ subcommands)
+ git config unset --value="wow3$" nextsection.nonewline;;
+ esac &&
test_cmp expect .git/config
'
@@ -460,11 +493,11 @@ version.1.2.3eX.alpha=beta
EOF
test_expect_success 'working --list' '
- git config --list > output &&
+ git config ${mode_prefix}list > output &&
test_cmp expect output
'
test_expect_success '--list without repo produces empty output' '
- git --git-dir=nonexistent config --list >output &&
+ git --git-dir=nonexistent config ${mode_prefix}list >output &&
test_must_be_empty output
'
@@ -476,7 +509,7 @@ version.1.2.3eX.alpha
EOF
test_expect_success '--name-only --list' '
- git config --name-only --list >output &&
+ git config ${mode_prefix}list --name-only >output &&
test_cmp expect output
'
@@ -486,7 +519,7 @@ nextsection.nonewline wow2 for me
EOF
test_expect_success '--get-regexp' '
- git config --get-regexp in >output &&
+ git config ${mode_get_regexp} in >output &&
test_cmp expect output
'
@@ -496,7 +529,7 @@ nextsection.nonewline
EOF
test_expect_success '--name-only --get-regexp' '
- git config --name-only --get-regexp in >output &&
+ git config ${mode_get_regexp} --name-only in >output &&
test_cmp expect output
'
@@ -507,7 +540,7 @@ EOF
test_expect_success '--add' '
git config --add nextsection.nonewline "wow4 for you" &&
- git config --get-all nextsection.nonewline > output &&
+ git config ${mode_get_all} nextsection.nonewline > output &&
test_cmp expect output
'
@@ -529,21 +562,21 @@ test_expect_success 'get variable with empty value' '
echo novalue.variable > expect
test_expect_success 'get-regexp variable with no value' '
- git config --get-regexp novalue > output &&
+ git config ${mode_get_regexp} novalue > output &&
test_cmp expect output
'
echo 'novalue.variable true' > expect
test_expect_success 'get-regexp --bool variable with no value' '
- git config --bool --get-regexp novalue > output &&
+ git config ${mode_get_regexp} --bool novalue > output &&
test_cmp expect output
'
echo 'emptyvalue.variable ' > expect
test_expect_success 'get-regexp variable with empty value' '
- git config --get-regexp emptyvalue > output &&
+ git config ${mode_get_regexp} emptyvalue > output &&
test_cmp expect output
'
@@ -563,7 +596,8 @@ test_expect_success 'get bool variable with empty value' '
test_expect_success 'no arguments, but no crash' '
test_must_fail git config >output 2>&1 &&
- test_grep usage output
+ echo "error: no action specified" >expect &&
+ test_cmp expect output
'
cat > .git/config << EOF
@@ -614,17 +648,17 @@ ein.bahn=strasse
EOF
test_expect_success 'alternative GIT_CONFIG' '
- GIT_CONFIG=other-config git config --list >output &&
+ GIT_CONFIG=other-config git config ${mode_prefix}list >output &&
test_cmp expect output
'
test_expect_success 'alternative GIT_CONFIG (--file)' '
- git config --file other-config --list >output &&
+ git config ${mode_prefix}list --file other-config >output &&
test_cmp expect output
'
test_expect_success 'alternative GIT_CONFIG (--file=-)' '
- git config --file - --list <other-config >output &&
+ git config ${mode_prefix}list --file - <other-config >output &&
test_cmp expect output
'
@@ -633,10 +667,11 @@ test_expect_success 'setting a value in stdin is an error' '
'
test_expect_success 'editing stdin is an error' '
- test_must_fail git config --file - --edit
+ test_must_fail git config ${mode_prefix}edit --file -
'
test_expect_success 'refer config from subdirectory' '
+ test_when_finished "rm -r x" &&
mkdir x &&
test_cmp_config -C x strasse --file=../other-config --get ein.bahn
'
@@ -665,7 +700,7 @@ weird
EOF
test_expect_success 'rename section' '
- git config --rename-section branch.eins branch.zwei
+ git config ${mode_prefix}rename-section branch.eins branch.zwei
'
cat > expect << EOF
@@ -684,7 +719,7 @@ test_expect_success 'rename succeeded' '
'
test_expect_success 'rename non-existing section' '
- test_must_fail git config --rename-section \
+ test_must_fail git config ${mode_prefix}rename-section \
branch."world domination" branch.drei
'
@@ -693,7 +728,7 @@ test_expect_success 'rename succeeded' '
'
test_expect_success 'rename another section' '
- git config --rename-section branch."1 234 blabl/a" branch.drei
+ git config ${mode_prefix}rename-section branch."1 234 blabl/a" branch.drei
'
cat > expect << EOF
@@ -716,7 +751,7 @@ cat >> .git/config << EOF
EOF
test_expect_success 'rename a section with a var on the same line' '
- git config --rename-section branch.vier branch.zwei
+ git config ${mode_prefix}rename-section branch.vier branch.zwei
'
cat > expect << EOF
@@ -737,11 +772,11 @@ test_expect_success 'rename succeeded' '
'
test_expect_success 'renaming empty section name is rejected' '
- test_must_fail git config --rename-section branch.zwei ""
+ test_must_fail git config ${mode_prefix}rename-section branch.zwei ""
'
test_expect_success 'renaming to bogus section is rejected' '
- test_must_fail git config --rename-section branch.zwei "bogus name"
+ test_must_fail git config ${mode_prefix}rename-section branch.zwei "bogus name"
'
test_expect_success 'renaming a section with a long line' '
@@ -750,7 +785,7 @@ test_expect_success 'renaming a section with a long line' '
printf " c = d %1024s [a] e = f\\n" " " &&
printf "[a] g = h\\n"
} >y &&
- git config -f y --rename-section a xyz &&
+ git config ${mode_prefix}rename-section -f y a xyz &&
test_must_fail git config -f y b.e
'
@@ -760,7 +795,7 @@ test_expect_success 'renaming an embedded section with a long line' '
printf " c = d %1024s [a] [foo] e = f\\n" " " &&
printf "[a] g = h\\n"
} >y &&
- git config -f y --rename-section a xyz &&
+ git config ${mode_prefix}rename-section -f y a xyz &&
test_must_fail git config -f y foo.e
'
@@ -770,7 +805,7 @@ test_expect_success 'renaming a section with an overly-long line' '
printf " c = d %525000s e" " " &&
printf "[a] g = h\\n"
} >y &&
- test_must_fail git config -f y --rename-section a xyz 2>err &&
+ test_must_fail git config ${mode_prefix}rename-section -f y a xyz 2>err &&
grep "refusing to work with overly long line in .y. on line 2" err
'
@@ -779,7 +814,7 @@ cat >> .git/config << EOF
EOF
test_expect_success 'remove section' '
- git config --remove-section branch.zwei
+ git config ${mode_prefix}remove-section branch.zwei
'
cat > expect << EOF
@@ -803,16 +838,16 @@ EOF
test_expect_success 'section ending' '
rm -f .git/config &&
- git config gitcvs.enabled true &&
- git config gitcvs.ext.dbname %Ggitcvs1.%a.%m.sqlite &&
- git config gitcvs.dbname %Ggitcvs2.%a.%m.sqlite &&
+ git config ${mode_set} gitcvs.enabled true &&
+ git config ${mode_set} gitcvs.ext.dbname %Ggitcvs1.%a.%m.sqlite &&
+ git config ${mode_set} gitcvs.dbname %Ggitcvs2.%a.%m.sqlite &&
test_cmp expect .git/config
'
test_expect_success numbers '
- git config kilo.gram 1k &&
- git config mega.ton 1m &&
+ git config ${mode_set} kilo.gram 1k &&
+ git config ${mode_set} mega.ton 1m &&
echo 1024 >expect &&
echo 1048576 >>expect &&
git config --int --get kilo.gram >actual &&
@@ -821,20 +856,20 @@ test_expect_success numbers '
'
test_expect_success '--int is at least 64 bits' '
- git config giga.watts 121g &&
+ git config ${mode_set} giga.watts 121g &&
echo >expect &&
test_cmp_config 129922760704 --int --get giga.watts
'
test_expect_success 'invalid unit' '
- git config aninvalid.unit "1auto" &&
+ git config ${mode_set} aninvalid.unit "1auto" &&
test_cmp_config 1auto aninvalid.unit &&
test_must_fail git config --int --get aninvalid.unit 2>actual &&
test_grep "bad numeric config value .1auto. for .aninvalid.unit. in file .git/config: invalid unit" actual
'
test_expect_success 'invalid unit boolean' '
- git config commit.gpgsign "1true" &&
+ git config ${mode_set} commit.gpgsign "1true" &&
test_cmp_config 1true commit.gpgsign &&
test_must_fail git config --bool --get commit.gpgsign 2>actual &&
test_grep "bad boolean config value .1true. for .commit.gpgsign." actual
@@ -847,7 +882,7 @@ test_expect_success 'line number is reported correctly' '
'
test_expect_success 'invalid stdin config' '
- echo "[broken" | test_must_fail git config --list --file - >output 2>&1 &&
+ echo "[broken" | test_must_fail git config ${mode_prefix}list --file - >output 2>&1 &&
test_grep "bad config line 1 in standard input" output
'
@@ -864,14 +899,14 @@ EOF
test_expect_success bool '
- git config bool.true1 01 &&
- git config bool.true2 -1 &&
- git config bool.true3 YeS &&
- git config bool.true4 true &&
- git config bool.false1 000 &&
- git config bool.false2 "" &&
- git config bool.false3 nO &&
- git config bool.false4 FALSE &&
+ git config ${mode_set} bool.true1 01 &&
+ git config ${mode_set} bool.true2 -1 &&
+ git config ${mode_set} bool.true3 YeS &&
+ git config ${mode_set} bool.true4 true &&
+ git config ${mode_set} bool.false1 000 &&
+ git config ${mode_set} bool.false2 "" &&
+ git config ${mode_set} bool.false3 nO &&
+ git config ${mode_set} bool.false4 FALSE &&
rm -f result &&
for i in 1 2 3 4
do
@@ -882,7 +917,7 @@ test_expect_success bool '
test_expect_success 'invalid bool (--get)' '
- git config bool.nobool foobar &&
+ git config ${mode_set} bool.nobool foobar &&
test_must_fail git config --bool --get bool.nobool'
test_expect_success 'invalid bool (set)' '
@@ -1071,7 +1106,7 @@ test_expect_success 'get --expiry-date' '
test_expect_success 'get --type=color' '
rm .git/config &&
- git config foo.color "red" &&
+ git config ${mode_set} foo.color "red" &&
git config --get --type=color foo.color >actual.raw &&
test_decode_color <actual.raw >actual &&
echo "<RED>" >expect &&
@@ -1108,18 +1143,18 @@ cat > expect << EOF
EOF
test_expect_success 'quoting' '
rm -f .git/config &&
- git config quote.leading " test" &&
- git config quote.ending "test " &&
- git config quote.semicolon "test;test" &&
- git config quote.hash "test#test" &&
+ git config ${mode_set} quote.leading " test" &&
+ git config ${mode_set} quote.ending "test " &&
+ git config ${mode_set} quote.semicolon "test;test" &&
+ git config ${mode_set} quote.hash "test#test" &&
test_cmp expect .git/config
'
test_expect_success 'key with newline' '
- test_must_fail git config "key.with
+ test_must_fail git config ${mode_get} "key.with
newline" 123'
-test_expect_success 'value with newline' 'git config key.sub value.with\\\
+test_expect_success 'value with newline' 'git config ${mode_set} key.sub value.with\\\
newline'
cat > .git/config <<\EOF
@@ -1139,7 +1174,7 @@ section.quotecont=cont;inued
EOF
test_expect_success 'value continued on next line' '
- git config --list > result &&
+ git config ${mode_prefix}list > result &&
test_cmp expect result
'
@@ -1163,14 +1198,14 @@ Qsection.sub=section.val4
Qsection.sub=section.val5Q
EOF
test_expect_success '--null --list' '
- git config --null --list >result.raw &&
+ git config ${mode_prefix}list --null >result.raw &&
nul_to_q <result.raw >result &&
echo >>result &&
test_cmp expect result
'
test_expect_success '--null --get-regexp' '
- git config --null --get-regexp "val[0-9]" >result.raw &&
+ git config ${mode_get_regexp} --null "val[0-9]" >result.raw &&
nul_to_q <result.raw >result &&
echo >>result &&
test_cmp expect result
@@ -1178,26 +1213,27 @@ test_expect_success '--null --get-regexp' '
test_expect_success 'inner whitespace kept verbatim, spaces only' '
echo "foo bar" >expect &&
- git config section.val "foo bar" &&
- git config --get section.val >actual &&
+ git config ${mode_set} section.val "foo bar" &&
+ git config ${mode_get} section.val >actual &&
test_cmp expect actual
'
test_expect_success 'inner whitespace kept verbatim, horizontal tabs only' '
echo "fooQQbar" | q_to_tab >expect &&
- git config section.val "$(cat expect)" &&
- git config --get section.val >actual &&
+ git config ${mode_set} section.val "$(cat expect)" &&
+ git config ${mode_get} section.val >actual &&
test_cmp expect actual
'
test_expect_success 'inner whitespace kept verbatim, horizontal tabs and spaces' '
echo "foo Q bar" | q_to_tab >expect &&
- git config section.val "$(cat expect)" &&
- git config --get section.val >actual &&
+ git config ${mode_set} section.val "$(cat expect)" &&
+ git config ${mode_get} section.val >actual &&
test_cmp expect actual
'
test_expect_success SYMLINKS 'symlinked configuration' '
+ test_when_finished "rm myconfig" &&
ln -s notyet myconfig &&
git config --file=myconfig test.frotz nitfol &&
test -h myconfig &&
@@ -1218,10 +1254,11 @@ test_expect_success SYMLINKS 'symlinked configuration' '
'
test_expect_success SYMLINKS 'symlink to nonexistent configuration' '
+ test_when_finished "rm linktonada linktolinktonada" &&
ln -s doesnotexist linktonada &&
ln -s linktonada linktolinktonada &&
- test_must_fail git config --file=linktonada --list &&
- test_must_fail git config --file=linktolinktonada --list
+ test_must_fail git config ${mode_prefix}list --file=linktonada &&
+ test_must_fail git config ${mode_prefix}list --file=linktolinktonada
'
test_expect_success 'check split_cmdline return' '
@@ -1229,12 +1266,12 @@ test_expect_success 'check split_cmdline return' '
git init repo &&
(
cd repo &&
- git config alias.split-cmdline-fix "echo \"" &&
+ git config ${mode_set} alias.split-cmdline-fix "echo \"" &&
test_must_fail git split-cmdline-fix &&
echo foo >foo &&
git add foo &&
git commit -m "initial commit" &&
- git config branch.main.mergeoptions "echo \"" &&
+ git config ${mode_set} branch.main.mergeoptions "echo \"" &&
test_must_fail git merge main
)
'
@@ -1266,18 +1303,18 @@ test_expect_success 'git -c can represent empty string' '
'
test_expect_success 'key sanity-checking' '
- test_must_fail git config foo=bar &&
- test_must_fail git config foo=.bar &&
- test_must_fail git config foo.ba=r &&
- test_must_fail git config foo.1bar &&
- test_must_fail git config foo."ba
+ test_must_fail git config ${mode_get} foo=bar &&
+ test_must_fail git config ${mode_get} foo=.bar &&
+ test_must_fail git config ${mode_get} foo.ba=r &&
+ test_must_fail git config ${mode_get} foo.1bar &&
+ test_must_fail git config ${mode_get} foo."ba
z".bar &&
- test_must_fail git config . false &&
- test_must_fail git config .foo false &&
- test_must_fail git config foo. false &&
- test_must_fail git config .foo. false &&
- git config foo.bar true &&
- git config foo."ba =z".bar false
+ test_must_fail git config ${mode_set} . false &&
+ test_must_fail git config ${mode_set} .foo false &&
+ test_must_fail git config ${mode_set} foo. false &&
+ test_must_fail git config ${mode_set} .foo. false &&
+ git config ${mode_set} foo.bar true &&
+ git config ${mode_set} foo."ba =z".bar false
'
test_expect_success 'git -c works with aliases of builtins' '
@@ -1319,7 +1356,7 @@ test_expect_success 'git -c complains about empty key and value' '
'
test_expect_success 'multiple git -c appends config' '
- test_config alias.x "!git -c x.two=2 config --get-regexp ^x\.*" &&
+ test_config alias.x "!git -c x.two=2 config ${mode_get_regexp} ^x\.*" &&
cat >expect <<-\EOF &&
x.one 1
x.two 2
@@ -1478,14 +1515,14 @@ do
done
test_expect_success 'git -c is not confused by empty environment' '
- GIT_CONFIG_PARAMETERS="" git -c x.one=1 config --list
+ GIT_CONFIG_PARAMETERS="" git -c x.one=1 config ${mode_prefix}list
'
test_expect_success 'GIT_CONFIG_PARAMETERS handles old-style entries' '
v="${SQ}key.one=foo${SQ}" &&
v="$v ${SQ}key.two=bar${SQ}" &&
v="$v ${SQ}key.ambiguous=section.whatever=value${SQ}" &&
- GIT_CONFIG_PARAMETERS=$v git config --get-regexp "key.*" >actual &&
+ GIT_CONFIG_PARAMETERS=$v git config ${mode_get_regexp} "key.*" >actual &&
cat >expect <<-EOF &&
key.one foo
key.two bar
@@ -1498,7 +1535,7 @@ test_expect_success 'GIT_CONFIG_PARAMETERS handles new-style entries' '
v="${SQ}key.one${SQ}=${SQ}foo${SQ}" &&
v="$v ${SQ}key.two${SQ}=${SQ}bar${SQ}" &&
v="$v ${SQ}key.ambiguous=section.whatever${SQ}=${SQ}value${SQ}" &&
- GIT_CONFIG_PARAMETERS=$v git config --get-regexp "key.*" >actual &&
+ GIT_CONFIG_PARAMETERS=$v git config ${mode_get_regexp} "key.*" >actual &&
cat >expect <<-EOF &&
key.one foo
key.two bar
@@ -1512,7 +1549,7 @@ test_expect_success 'old and new-style entries can mix' '
v="$v ${SQ}key.newone${SQ}=${SQ}newfoo${SQ}" &&
v="$v ${SQ}key.oldtwo=oldbar${SQ}" &&
v="$v ${SQ}key.newtwo${SQ}=${SQ}newbar${SQ}" &&
- GIT_CONFIG_PARAMETERS=$v git config --get-regexp "key.*" >actual &&
+ GIT_CONFIG_PARAMETERS=$v git config ${mode_get_regexp} "key.*" >actual &&
cat >expect <<-EOF &&
key.oldone oldfoo
key.newone newfoo
@@ -1525,7 +1562,7 @@ test_expect_success 'old and new-style entries can mix' '
test_expect_success 'old and new bools with ambiguous subsection' '
v="${SQ}key.with=equals.oldbool${SQ}" &&
v="$v ${SQ}key.with=equals.newbool${SQ}=" &&
- GIT_CONFIG_PARAMETERS=$v git config --get-regexp "key.*" >actual &&
+ GIT_CONFIG_PARAMETERS=$v git config ${mode_get_regexp} "key.*" >actual &&
cat >expect <<-EOF &&
key.with equals.oldbool
key.with=equals.newbool
@@ -1539,7 +1576,7 @@ test_expect_success 'detect bogus GIT_CONFIG_PARAMETERS' '
env.two two
EOF
GIT_CONFIG_PARAMETERS="${SQ}env.one=one${SQ} ${SQ}env.two=two${SQ}" \
- git config --get-regexp "env.*" >actual &&
+ git config ${mode_get_regexp} "env.*" >actual &&
test_cmp expect actual &&
cat >expect <<-EOF &&
@@ -1547,12 +1584,12 @@ test_expect_success 'detect bogus GIT_CONFIG_PARAMETERS' '
env.two two
EOF
GIT_CONFIG_PARAMETERS="${SQ}env.one=one${SQ}\\$SQ$SQ$SQ ${SQ}env.two=two${SQ}" \
- git config --get-regexp "env.*" >actual &&
+ git config ${mode_get_regexp} "env.*" >actual &&
test_cmp expect actual &&
test_must_fail env \
GIT_CONFIG_PARAMETERS="${SQ}env.one=one${SQ}\\$SQ ${SQ}env.two=two${SQ}" \
- git config --get-regexp "env.*"
+ git config ${mode_get_regexp} "env.*"
'
test_expect_success 'git --config-env=key=envvar support' '
@@ -1600,7 +1637,7 @@ test_expect_success 'git -c and --config-env work together' '
ENVVAR=env-value git \
-c bar.cmd=cmd-value \
--config-env=bar.env=ENVVAR \
- config --get-regexp "^bar.*" >actual &&
+ config ${mode_get_regexp} "^bar.*" >actual &&
test_cmp expect actual
'
@@ -1628,7 +1665,7 @@ test_expect_success 'git config handles environment config pairs' '
GIT_CONFIG_COUNT=2 \
GIT_CONFIG_KEY_0="pair.one" GIT_CONFIG_VALUE_0="foo" \
GIT_CONFIG_KEY_1="pair.two" GIT_CONFIG_VALUE_1="bar" \
- git config --get-regexp "pair.*" >actual &&
+ git config ${mode_get_regexp} "pair.*" >actual &&
cat >expect <<-EOF &&
pair.one foo
pair.two bar
@@ -1638,7 +1675,7 @@ test_expect_success 'git config handles environment config pairs' '
test_expect_success 'git config ignores pairs without count' '
test_must_fail env GIT_CONFIG_KEY_0="pair.one" GIT_CONFIG_VALUE_0="value" \
- git config pair.one 2>error &&
+ git config ${mode_get} pair.one 2>error &&
test_must_be_empty error
'
@@ -1646,7 +1683,7 @@ 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 2>error &&
+ git config ${mode_get_regexp} "pair.*" >actual 2>error &&
cat >expect <<-EOF &&
pair.one value
EOF
@@ -1657,43 +1694,43 @@ test_expect_success 'git config ignores pairs exceeding count' '
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 2>error &&
+ git config ${mode_get} 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 2>error &&
+ git config ${mode_get} pair.one 2>error &&
test_must_be_empty error
'
test_expect_success 'git config fails with invalid count' '
- test_must_fail env GIT_CONFIG_COUNT=10a git config --list 2>error &&
+ test_must_fail env GIT_CONFIG_COUNT=10a git config ${mode_prefix}list 2>error &&
test_grep "bogus count" error &&
- test_must_fail env GIT_CONFIG_COUNT=9999999999999999 git config --list 2>error &&
+ test_must_fail env GIT_CONFIG_COUNT=9999999999999999 git config ${mode_prefix}list 2>error &&
test_grep "too many entries" error
'
test_expect_success 'git config fails with missing config key' '
test_must_fail env GIT_CONFIG_COUNT=1 GIT_CONFIG_VALUE_0="value" \
- git config --list 2>error &&
+ git config ${mode_prefix}list 2>error &&
test_grep "missing config key" error
'
test_expect_success 'git config fails with missing config value' '
test_must_fail env GIT_CONFIG_COUNT=1 GIT_CONFIG_KEY_0="pair.one" \
- git config --list 2>error &&
+ git config ${mode_prefix}list 2>error &&
test_grep "missing config value" error
'
test_expect_success 'git config fails with invalid config pair key' '
test_must_fail env GIT_CONFIG_COUNT=1 \
GIT_CONFIG_KEY_0= GIT_CONFIG_VALUE_0=value \
- git config --list &&
+ git config ${mode_prefix}list &&
test_must_fail env GIT_CONFIG_COUNT=1 \
GIT_CONFIG_KEY_0=missing-section GIT_CONFIG_VALUE_0=value \
- git config --list
+ git config ${mode_prefix}list
'
test_expect_success 'environment overrides config file' '
@@ -1703,7 +1740,7 @@ test_expect_success 'environment overrides config file' '
one = value
EOF
GIT_CONFIG_COUNT=1 GIT_CONFIG_KEY_0=pair.one GIT_CONFIG_VALUE_0=override \
- git config pair.one >actual &&
+ git config ${mode_get} pair.one >actual &&
cat >expect <<-EOF &&
override
EOF
@@ -1713,7 +1750,7 @@ test_expect_success 'environment overrides config file' '
test_expect_success 'GIT_CONFIG_PARAMETERS overrides environment config' '
GIT_CONFIG_COUNT=1 GIT_CONFIG_KEY_0=pair.one GIT_CONFIG_VALUE_0=value \
GIT_CONFIG_PARAMETERS="${SQ}pair.one=override${SQ}" \
- git config pair.one >actual &&
+ git config ${mode_get} pair.one >actual &&
cat >expect <<-EOF &&
override
EOF
@@ -1732,8 +1769,8 @@ test_expect_success 'command line overrides environment config' '
test_expect_success 'git config --edit works' '
git config -f tmp test.value no &&
echo test.value=yes >expect &&
- GIT_EDITOR="echo [test]value=yes >" git config -f tmp --edit &&
- git config -f tmp --list >actual &&
+ GIT_EDITOR="echo [test]value=yes >" git config ${mode_prefix}edit -f tmp &&
+ git config ${mode_prefix}list -f tmp >actual &&
test_cmp expect actual
'
@@ -1741,8 +1778,8 @@ test_expect_success 'git config --edit respects core.editor' '
git config -f tmp test.value no &&
echo test.value=yes >expect &&
test_config core.editor "echo [test]value=yes >" &&
- git config -f tmp --edit &&
- git config -f tmp --list >actual &&
+ git config ${mode_prefix}edit -f tmp &&
+ git config ${mode_prefix}list -f tmp >actual &&
test_cmp expect actual
'
@@ -1788,20 +1825,28 @@ test_expect_success 'urlmatch' '
test_expect_code 1 git config --bool --get-urlmatch doesnt.exist https://good.example.com >actual &&
test_must_be_empty actual &&
+ test_expect_code 1 git config get --url=https://good.example.com --bool doesnt.exist >actual &&
+ test_must_be_empty actual &&
echo true >expect &&
git config --bool --get-urlmatch http.SSLverify https://good.example.com >actual &&
test_cmp expect actual &&
+ git config get --bool --url=https://good.example.com http.SSLverify >actual &&
+ test_cmp expect actual &&
echo false >expect &&
git config --bool --get-urlmatch http.sslverify https://weak.example.com >actual &&
test_cmp expect actual &&
+ git config get --bool --url=https://weak.example.com http.sslverify >actual &&
+ test_cmp expect actual &&
{
echo http.cookiefile /tmp/cookie.txt &&
echo http.sslverify false
} >expect &&
git config --get-urlmatch HTTP https://weak.example.com >actual &&
+ test_cmp expect actual &&
+ git config get --url=https://weak.example.com HTTP >actual &&
test_cmp expect actual
'
@@ -1817,6 +1862,8 @@ test_expect_success 'urlmatch with --show-scope' '
local http.sslverify false
EOF
git config --get-urlmatch --show-scope HTTP https://weak.example.com >actual &&
+ test_cmp expect actual &&
+ git config get --url=https://weak.example.com --show-scope HTTP >actual &&
test_cmp expect actual
'
@@ -1849,45 +1896,67 @@ test_expect_success 'urlmatch favors more specific URLs' '
echo http.cookiefile /tmp/root.txt >expect &&
git config --get-urlmatch HTTP https://example.com >actual &&
test_cmp expect actual &&
+ git config get --url=https://example.com HTTP >actual &&
+ test_cmp expect actual &&
echo http.cookiefile /tmp/subdirectory.txt >expect &&
git config --get-urlmatch HTTP https://example.com/subdirectory >actual &&
test_cmp expect actual &&
+ git config get --url=https://example.com/subdirectory HTTP >actual &&
+ test_cmp expect actual &&
echo http.cookiefile /tmp/subdirectory.txt >expect &&
git config --get-urlmatch HTTP https://example.com/subdirectory/nested >actual &&
test_cmp expect actual &&
+ git config get --url=https://example.com/subdirectory/nested HTTP >actual &&
+ test_cmp expect actual &&
echo http.cookiefile /tmp/user.txt >expect &&
git config --get-urlmatch HTTP https://user@example.com/ >actual &&
test_cmp expect actual &&
+ git config get --url=https://user@example.com/ HTTP >actual &&
+ test_cmp expect actual &&
echo http.cookiefile /tmp/subdirectory.txt >expect &&
git config --get-urlmatch HTTP https://averylonguser@example.com/subdirectory >actual &&
test_cmp expect actual &&
+ git config get --url=https://averylonguser@example.com/subdirectory HTTP >actual &&
+ test_cmp expect actual &&
echo http.cookiefile /tmp/preceding.txt >expect &&
git config --get-urlmatch HTTP https://preceding.example.com >actual &&
test_cmp expect actual &&
+ git config get --url=https://preceding.example.com HTTP >actual &&
+ test_cmp expect actual &&
echo http.cookiefile /tmp/wildcard.txt >expect &&
git config --get-urlmatch HTTP https://wildcard.example.com >actual &&
test_cmp expect actual &&
+ git config get --url=https://wildcard.example.com HTTP >actual &&
+ test_cmp expect actual &&
echo http.cookiefile /tmp/sub.txt >expect &&
git config --get-urlmatch HTTP https://sub.example.com/wildcardwithsubdomain >actual &&
test_cmp expect actual &&
+ git config get --url=https://sub.example.com/wildcardwithsubdomain HTTP >actual &&
+ test_cmp expect actual &&
echo http.cookiefile /tmp/trailing.txt >expect &&
git config --get-urlmatch HTTP https://trailing.example.com >actual &&
test_cmp expect actual &&
+ git config get --url=https://trailing.example.com HTTP >actual &&
+ test_cmp expect actual &&
echo http.cookiefile /tmp/sub.txt >expect &&
git config --get-urlmatch HTTP https://user@sub.example.com >actual &&
test_cmp expect actual &&
+ git config get --url=https://user@sub.example.com HTTP >actual &&
+ test_cmp expect actual &&
echo http.cookiefile /tmp/multiwildcard.txt >expect &&
git config --get-urlmatch HTTP https://wildcard.example.org >actual &&
+ test_cmp expect actual &&
+ git config get --url=https://wildcard.example.org HTTP >actual &&
test_cmp expect actual
'
@@ -1954,7 +2023,7 @@ test_expect_success '--unset last key removes section (except if commented)' '
# please be careful when you update the above variable
EOF
- git config --unset section.key &&
+ git config ${mode_unset} section.key &&
test_cmp expect .git/config &&
cat >.git/config <<-\EOF &&
@@ -1967,7 +2036,7 @@ test_expect_success '--unset last key removes section (except if commented)' '
[next-section]
EOF
- git config --unset section.key &&
+ git config ${mode_unset} section.key &&
test_cmp expect .git/config &&
q_to_tab >.git/config <<-\EOF &&
@@ -1977,7 +2046,7 @@ test_expect_success '--unset last key removes section (except if commented)' '
[two]
key = true
EOF
- git config --unset two.key &&
+ git config ${mode_unset} two.key &&
! grep two .git/config &&
q_to_tab >.git/config <<-\EOF &&
@@ -1987,7 +2056,7 @@ test_expect_success '--unset last key removes section (except if commented)' '
[one]
key = true
EOF
- git config --unset-all one.key &&
+ git config ${mode_unset_all} one.key &&
test_line_count = 0 .git/config &&
q_to_tab >.git/config <<-\EOF &&
@@ -1997,7 +2066,7 @@ test_expect_success '--unset last key removes section (except if commented)' '
[two]
Qkey = true
EOF
- git config --unset two.key &&
+ git config ${mode_unset} two.key &&
grep two .git/config &&
q_to_tab >.git/config <<-\EOF &&
@@ -2009,8 +2078,8 @@ test_expect_success '--unset last key removes section (except if commented)' '
[TWO "subsection"]
[one]
EOF
- git config --unset two.subsection.key &&
- test "not [two subsection]" = "$(git config one.key)" &&
+ git config ${mode_unset} two.subsection.key &&
+ test "not [two subsection]" = "$(git config ${mode_get} one.key)" &&
test_line_count = 3 .git/config
'
@@ -2021,7 +2090,7 @@ test_expect_success '--unset-all removes section if empty & uncommented' '
key = value2
EOF
- git config --unset-all section.key &&
+ git config ${mode_unset_all} section.key &&
test_line_count = 0 .git/config
'
@@ -2044,7 +2113,7 @@ test_expect_success POSIXPERM,PERL 'preserves existing permissions' '
git config imap.pass Hunter2 &&
perl -e \
"die q(badset) if ((stat(q(.git/config)))[2] & 07777) != 0600" &&
- git config --rename-section imap pop &&
+ git config ${mode_prefix}rename-section imap pop &&
perl -e \
"die q(badrename) if ((stat(q(.git/config)))[2] & 07777) != 0600"
'
@@ -2093,7 +2162,7 @@ test_expect_success '--show-origin with --list' '
command line: user.cmdline=true
EOF
GIT_CONFIG_COUNT=1 GIT_CONFIG_KEY_0=user.environ GIT_CONFIG_VALUE_0=true\
- git -c user.cmdline=true config --list --show-origin >output &&
+ git -c user.cmdline=true config ${mode_prefix}list --show-origin >output &&
test_cmp expect output
'
@@ -2110,7 +2179,7 @@ test_expect_success '--show-origin with --list --null' '
includeQcommand line:Quser.cmdline
trueQ
EOF
- git -c user.cmdline=true config --null --list --show-origin >output.raw &&
+ git -c user.cmdline=true config ${mode_prefix}list --null --show-origin >output.raw &&
nul_to_q <output.raw >output &&
# The here-doc above adds a newline that the --null output would not
# include. Add it here to make the two comparable.
@@ -2124,7 +2193,7 @@ test_expect_success '--show-origin with single file' '
file:.git/config user.override=local
file:.git/config include.path=../include/relative.include
EOF
- git config --local --list --show-origin >output &&
+ git config ${mode_prefix}list --local --show-origin >output &&
test_cmp expect output
'
@@ -2133,7 +2202,7 @@ test_expect_success '--show-origin with --get-regexp' '
file:$HOME/.gitconfig user.global true
file:.git/config user.local true
EOF
- git config --show-origin --get-regexp "user\.[g|l].*" >output &&
+ git config ${mode_get_regexp} --show-origin "user\.[g|l].*" >output &&
test_cmp expect output
'
@@ -2141,7 +2210,7 @@ test_expect_success '--show-origin getting a single key' '
cat >expect <<-\EOF &&
file:.git/config local
EOF
- git config --show-origin user.override >output &&
+ git config ${mode_get} --show-origin user.override >output &&
test_cmp expect output
'
@@ -2162,7 +2231,7 @@ test_expect_success !MINGW '--show-origin escape special file name characters' '
cat >expect <<-\EOF &&
file:"file\" (dq) and spaces.conf" user.custom=true
EOF
- git config --file "$WEIRDLY_NAMED_FILE" --show-origin --list >output &&
+ git config ${mode_prefix}list --file "$WEIRDLY_NAMED_FILE" --show-origin >output &&
test_cmp expect output
'
@@ -2170,7 +2239,7 @@ test_expect_success '--show-origin stdin' '
cat >expect <<-\EOF &&
standard input: user.custom=true
EOF
- git config --file - --show-origin --list <"$CUSTOM_CONFIG_FILE" >output &&
+ git config ${mode_prefix}list --file - --show-origin <"$CUSTOM_CONFIG_FILE" >output &&
test_cmp expect output
'
@@ -2197,7 +2266,7 @@ test_expect_success '--show-origin blob' '
cat >expect <<-EOF &&
blob:$blob user.custom=true
EOF
- git config --blob=$blob --show-origin --list >output &&
+ git config ${mode_prefix}list --blob=$blob --show-origin >output &&
test_cmp expect output
)
'
@@ -2213,7 +2282,7 @@ test_expect_success '--show-origin blob ref' '
cp "$CUSTOM_CONFIG_FILE" custom.conf &&
git add custom.conf &&
git commit -m "new config file" &&
- git config --blob=main:custom.conf --show-origin --list >output &&
+ git config ${mode_prefix}list --blob=main:custom.conf --show-origin >output &&
test_cmp expect output
)
'
@@ -2239,13 +2308,14 @@ test_expect_success '--show-scope with --list' '
worktree user.worktree=true
command user.cmdline=true
EOF
+ test_when_finished "git worktree remove wt1" &&
git worktree add wt1 &&
# We need these to test for worktree scope, but outside of this
# test, this is just noise
test_config core.repositoryformatversion 1 &&
test_config extensions.worktreeConfig true &&
git config --worktree user.worktree true &&
- git -c user.cmdline=true config --list --show-scope >output &&
+ git -c user.cmdline=true config ${mode_prefix}list --show-scope >output &&
test_cmp expect output
'
@@ -2254,7 +2324,7 @@ test_expect_success !MINGW '--show-scope with --blob' '
cat >expect <<-EOF &&
command user.custom=true
EOF
- git config --blob=$blob --show-scope --list >output &&
+ git config ${mode_prefix}list --blob=$blob --show-scope >output &&
test_cmp expect output
'
@@ -2264,7 +2334,7 @@ test_expect_success '--show-scope with --local' '
local user.override=local
local include.path=../include/relative.include
EOF
- git config --local --list --show-scope >output &&
+ git config ${mode_prefix}list --local --show-scope >output &&
test_cmp expect output
'
@@ -2272,7 +2342,7 @@ test_expect_success '--show-scope getting a single value' '
cat >expect <<-\EOF &&
local true
EOF
- git config --show-scope --get user.local >output &&
+ git config ${mode_get} --show-scope user.local >output &&
test_cmp expect output
'
@@ -2288,7 +2358,7 @@ test_expect_success '--show-scope with --show-origin' '
local file:.git/../include/relative.include user.relative=include
command command line: user.cmdline=true
EOF
- git -c user.cmdline=true config --list --show-origin --show-scope >output &&
+ git -c user.cmdline=true config ${mode_prefix}list --show-origin --show-scope >output &&
test_cmp expect output
'
@@ -2329,7 +2399,7 @@ test_expect_success 'override global and system config' '
global home.config=true
local local.config=true
EOF
- git config --show-scope --list >output &&
+ git config ${mode_prefix}list --show-scope >output &&
test_cmp expect output &&
cat >expect <<-EOF &&
@@ -2338,20 +2408,20 @@ test_expect_success 'override global and system config' '
local local.config=true
EOF
GIT_CONFIG_NOSYSTEM=false GIT_CONFIG_SYSTEM=custom-system-config GIT_CONFIG_GLOBAL=custom-global-config \
- git config --show-scope --list >output &&
+ git config ${mode_prefix}list --show-scope >output &&
test_cmp expect output &&
cat >expect <<-EOF &&
local local.config=true
EOF
GIT_CONFIG_NOSYSTEM=false GIT_CONFIG_SYSTEM=/dev/null GIT_CONFIG_GLOBAL=/dev/null \
- git config --show-scope --list >output &&
+ git config ${mode_prefix}list --show-scope >output &&
test_cmp expect output
'
test_expect_success 'override global and system config with missing file' '
- test_must_fail env GIT_CONFIG_GLOBAL=does-not-exist GIT_CONFIG_SYSTEM=/dev/null git config --global --list &&
- test_must_fail env GIT_CONFIG_GLOBAL=/dev/null GIT_CONFIG_SYSTEM=does-not-exist git config --system --list &&
+ test_must_fail env GIT_CONFIG_GLOBAL=does-not-exist GIT_CONFIG_SYSTEM=/dev/null git config ${mode_prefix}list --global &&
+ test_must_fail env GIT_CONFIG_GLOBAL=/dev/null GIT_CONFIG_SYSTEM=does-not-exist git config ${mode_prefix}list --system &&
GIT_CONFIG_GLOBAL=does-not-exist GIT_CONFIG_SYSTEM=does-not-exist git version
'
@@ -2467,7 +2537,7 @@ test_expect_success '--replace-all does not invent newlines' '
[abc]
Qkey = b
EOF
- git config --replace-all abc.key b &&
+ git config ${mode_replace_all} abc.key b &&
test_cmp expect .git/config
'
@@ -2478,7 +2548,7 @@ test_expect_success 'set all config with value-pattern' '
# no match => add new entry
cp initial config &&
git config --file=config abc.key two a+ &&
- git config --file=config --list >actual &&
+ git config ${mode_prefix}list --file=config >actual &&
cat >expect <<-\EOF &&
abc.key=one
abc.key=two
@@ -2491,7 +2561,7 @@ test_expect_success 'set all config with value-pattern' '
# multiple values, no match => add
git config --file=config abc.key three a+ &&
- git config --file=config --list >actual &&
+ git config ${mode_prefix}list --file=config >actual &&
cat >expect <<-\EOF &&
abc.key=one
abc.key=two
@@ -2501,7 +2571,7 @@ test_expect_success 'set all config with value-pattern' '
# single match => replace
git config --file=config abc.key four h+ &&
- git config --file=config --list >actual &&
+ git config ${mode_prefix}list --file=config >actual &&
cat >expect <<-\EOF &&
abc.key=one
abc.key=two
@@ -2516,7 +2586,7 @@ test_expect_success '--replace-all and value-pattern' '
git config --file=config --add abc.key two &&
git config --file=config --add abc.key three &&
git config --file=config --replace-all abc.key four "o+" &&
- git config --file=config --list >actual &&
+ git config ${mode_prefix}list --file=config >actual &&
cat >expect <<-\EOF &&
abc.key=four
abc.key=three
@@ -2532,20 +2602,20 @@ test_expect_success 'refuse --fixed-value for incompatible actions' '
test_must_fail git config --file=config --fixed-value --add dev.null bogus &&
test_must_fail git config --file=config --fixed-value --get-urlmatch dev.null bogus &&
test_must_fail git config --file=config --fixed-value --get-urlmatch dev.null bogus &&
- test_must_fail git config --file=config --fixed-value --rename-section dev null &&
- test_must_fail git config --file=config --fixed-value --remove-section dev &&
- test_must_fail git config --file=config --fixed-value --list &&
+ test_must_fail git config ${mode_prefix}rename-section --file=config --fixed-value dev null &&
+ test_must_fail git config ${mode_prefix}remove-section --file=config --fixed-value dev &&
+ test_must_fail git config ${mode_prefix}list --file=config --fixed-value &&
test_must_fail git config --file=config --fixed-value --get-color dev.null &&
test_must_fail git config --file=config --fixed-value --get-colorbool dev.null &&
# These modes complain when --fixed-value has no value-pattern
- test_must_fail git config --file=config --fixed-value dev.null bogus &&
- test_must_fail git config --file=config --fixed-value --replace-all dev.null bogus &&
- test_must_fail git config --file=config --fixed-value --get dev.null &&
- test_must_fail git config --file=config --fixed-value --get-all dev.null &&
- test_must_fail git config --file=config --fixed-value --get-regexp "dev.*" &&
- test_must_fail git config --file=config --fixed-value --unset dev.null &&
- test_must_fail git config --file=config --fixed-value --unset-all dev.null
+ test_must_fail git config ${mode_set} --file=config --fixed-value dev.null bogus &&
+ test_must_fail git config ${mode_replace_all} --file=config --fixed-value dev.null bogus &&
+ test_must_fail git config ${mode_prefix}get --file=config --fixed-value dev.null &&
+ test_must_fail git config ${mode_get_all} --file=config --fixed-value dev.null &&
+ test_must_fail git config ${mode_get_regexp} --file=config --fixed-value "dev.*" &&
+ test_must_fail git config ${mode_unset} --file=config --fixed-value dev.null &&
+ test_must_fail git config ${mode_unset_all} --file=config --fixed-value dev.null
'
test_expect_success '--fixed-value uses exact string matching' '
@@ -2555,7 +2625,7 @@ test_expect_success '--fixed-value uses exact string matching' '
cp initial config &&
git config --file=config fixed.test bogus "$META" &&
- git config --file=config --list >actual &&
+ git config ${mode_prefix}list --file=config >actual &&
cat >expect <<-EOF &&
fixed.test=$META
fixed.test=bogus
@@ -2564,7 +2634,7 @@ test_expect_success '--fixed-value uses exact string matching' '
cp initial config &&
git config --file=config --fixed-value fixed.test bogus "$META" &&
- git config --file=config --list >actual &&
+ git config ${mode_prefix}list --file=config >actual &&
cat >expect <<-\EOF &&
fixed.test=bogus
EOF
@@ -2573,16 +2643,21 @@ test_expect_success '--fixed-value uses exact string matching' '
cp initial config &&
test_must_fail git config --file=config --unset fixed.test "$META" &&
git config --file=config --fixed-value --unset fixed.test "$META" &&
- test_must_fail git config --file=config fixed.test &&
+ test_must_fail git config ${mode_get} --file=config fixed.test &&
+
+ cp initial config &&
+ test_must_fail git config unset --file=config --value="$META" fixed.test &&
+ git config unset --file=config --fixed-value --value="$META" fixed.test &&
+ test_must_fail git config ${mode_get} --file=config fixed.test &&
cp initial config &&
test_must_fail git config --file=config --unset-all fixed.test "$META" &&
git config --file=config --fixed-value --unset-all fixed.test "$META" &&
- test_must_fail git config --file=config fixed.test &&
+ test_must_fail git config ${mode_get} --file=config fixed.test &&
cp initial config &&
- git config --file=config --replace-all fixed.test bogus "$META" &&
- git config --file=config --list >actual &&
+ git config --file=config fixed.test bogus "$META" &&
+ git config ${mode_prefix}list --file=config >actual &&
cat >expect <<-EOF &&
fixed.test=$META
fixed.test=bogus
@@ -2590,7 +2665,7 @@ test_expect_success '--fixed-value uses exact string matching' '
test_cmp expect actual &&
git config --file=config --fixed-value --replace-all fixed.test bogus "$META" &&
- git config --file=config --list >actual &&
+ git config ${mode_prefix}list --file=config >actual &&
cat >expect <<-EOF &&
fixed.test=bogus
fixed.test=bogus
@@ -2605,18 +2680,27 @@ test_expect_success '--get and --get-all with --fixed-value' '
git config --file=config --add fixed.test "$META" &&
git config --file=config --get fixed.test bogus &&
+ git config get --file=config --value=bogus fixed.test &&
test_must_fail git config --file=config --get fixed.test "$META" &&
+ test_must_fail git config get --file=config --value="$META" fixed.test &&
git config --file=config --get --fixed-value fixed.test "$META" &&
+ git config get --file=config --fixed-value --value="$META" fixed.test &&
test_must_fail git config --file=config --get --fixed-value fixed.test non-existent &&
git config --file=config --get-all fixed.test bogus &&
+ git config get --all --file=config --value=bogus fixed.test &&
test_must_fail git config --file=config --get-all fixed.test "$META" &&
+ test_must_fail git config get --all --file=config --value="$META" fixed.test &&
git config --file=config --get-all --fixed-value fixed.test "$META" &&
+ git config get --all --file=config --value="$META" --fixed-value fixed.test &&
test_must_fail git config --file=config --get-all --fixed-value fixed.test non-existent &&
git config --file=config --get-regexp fixed+ bogus &&
+ git config get --regexp --file=config --value=bogus fixed+ &&
test_must_fail git config --file=config --get-regexp fixed+ "$META" &&
+ test_must_fail git config get --regexp --file=config --value="$META" fixed+ &&
git config --file=config --get-regexp --fixed-value fixed+ "$META" &&
+ git config get --regexp --file=config --fixed-value --value="$META" fixed+ &&
test_must_fail git config --file=config --get-regexp --fixed-value fixed+ non-existent
'
@@ -2738,4 +2822,25 @@ test_expect_success 'includeIf.hasconfig:remote.*.url forbids remote url in such
grep "fatal: remote URLs cannot be configured in file directly or indirectly included by includeIf.hasconfig:remote.*.url" err
'
+test_expect_success 'negated mode causes failure' '
+ test_must_fail git config --no-get 2>err &&
+ grep "unknown option \`no-get${SQ}" err
+'
+
+test_expect_success 'specifying multiple modes causes failure' '
+ cat >expect <<-EOF &&
+ error: options ${SQ}--get-all${SQ} and ${SQ}--get${SQ} cannot be used together
+ EOF
+ test_must_fail git config --get --get-all 2>err &&
+ test_cmp expect err
+'
+
+test_expect_success 'writing to stdin is rejected' '
+ echo "fatal: writing to stdin is not supported" >expect &&
+ test_must_fail git config ${mode_set} --file - foo.bar baz 2>err &&
+ test_cmp expect err
+'
+
+done
+
test_done
diff --git a/t/t1306-xdg-files.sh b/t/t1306-xdg-files.sh
index 40d3c42618..53e5b290b9 100755
--- a/t/t1306-xdg-files.sh
+++ b/t/t1306-xdg-files.sh
@@ -7,6 +7,7 @@
test_description='Compatibility with $XDG_CONFIG_HOME/git/ files'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'read config: xdg file exists and ~/.gitconfig doesn'\''t' '
diff --git a/t/t1350-config-hooks-path.sh b/t/t1350-config-hooks-path.sh
index f6dc83e2aa..ceeb7ac3a4 100755
--- a/t/t1350-config-hooks-path.sh
+++ b/t/t1350-config-hooks-path.sh
@@ -2,6 +2,7 @@
test_description='Test the core.hooksPath configuration variable'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'set up a pre-commit hook in core.hooksPath' '
@@ -41,4 +42,11 @@ test_expect_success 'git rev-parse --git-path hooks' '
test .git/custom-hooks/abc = "$(cat actual)"
'
+test_expect_success 'core.hooksPath=/dev/null' '
+ git clone -c core.hooksPath=/dev/null . no-templates &&
+ value="$(git -C no-templates config --local core.hooksPath)" &&
+ # The Bash used by Git for Windows rewrites `/dev/null` to `nul`
+ { test /dev/null = "$value" || test nul = "$value"; }
+'
+
test_done
diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh
index ec3443cc87..eb1691860d 100755
--- a/t/t1400-update-ref.sh
+++ b/t/t1400-update-ref.sh
@@ -4,6 +4,8 @@
#
test_description='Test git update-ref and basic ref logging'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
Z=$ZERO_OID
@@ -890,17 +892,23 @@ test_expect_success 'stdin update/create/verify combination works' '
'
test_expect_success 'stdin verify succeeds for correct value' '
+ test-tool ref-store main for-each-reflog-ent $m >before &&
git rev-parse $m >expect &&
echo "verify $m $m" >stdin &&
git update-ref --stdin <stdin &&
git rev-parse $m >actual &&
- test_cmp expect actual
+ test_cmp expect actual &&
+ test-tool ref-store main for-each-reflog-ent $m >after &&
+ test_cmp before after
'
test_expect_success 'stdin verify succeeds for missing reference' '
+ test-tool ref-store main for-each-reflog-ent $m >before &&
echo "verify refs/heads/missing $Z" >stdin &&
git update-ref --stdin <stdin &&
- test_must_fail git rev-parse --verify -q refs/heads/missing
+ test_must_fail git rev-parse --verify -q refs/heads/missing &&
+ test-tool ref-store main for-each-reflog-ent $m >after &&
+ test_cmp before after
'
test_expect_success 'stdin verify treats no value as missing' '
@@ -1354,6 +1362,7 @@ test_expect_success 'fails with duplicate HEAD update' '
'
test_expect_success 'fails with duplicate ref update via symref' '
+ test_when_finished "git symbolic-ref -d refs/heads/symref2" &&
git branch target2 $A &&
git symbolic-ref refs/heads/symref2 refs/heads/target2 &&
cat >stdin <<-EOF &&
@@ -1641,4 +1650,423 @@ test_expect_success PIPE 'transaction flushes status updates' '
test_cmp expected actual
'
+format_command () {
+ if test "$1" = "-z"
+ then
+ shift
+ printf "$F" "$@"
+ else
+ echo "$@"
+ fi
+}
+
+for type in "" "-z"
+do
+
+ test_expect_success "stdin $type symref-verify fails without --no-deref" '
+ git symbolic-ref refs/heads/symref $a &&
+ format_command $type "symref-verify refs/heads/symref" "$a" >stdin &&
+ test_must_fail git update-ref --stdin $type <stdin 2>err &&
+ grep "fatal: symref-verify: cannot operate with deref mode" err
+ '
+
+ test_expect_success "stdin $type symref-verify fails with too many arguments" '
+ format_command $type "symref-verify refs/heads/symref" "$a" "$a" >stdin &&
+ test_must_fail git update-ref --stdin $type --no-deref <stdin 2>err &&
+ if test "$type" = "-z"
+ then
+ grep "fatal: unknown command: $a" err
+ else
+ grep "fatal: symref-verify refs/heads/symref: extra input: $a" err
+ fi
+ '
+
+ test_expect_success "stdin $type symref-verify succeeds for correct value" '
+ git symbolic-ref refs/heads/symref >expect &&
+ test-tool ref-store main for-each-reflog-ent refs/heads/symref >before &&
+ format_command $type "symref-verify refs/heads/symref" "$a" >stdin &&
+ git update-ref --stdin $type --no-deref <stdin &&
+ git symbolic-ref refs/heads/symref >actual &&
+ test_cmp expect actual &&
+ test-tool ref-store main for-each-reflog-ent refs/heads/symref >after &&
+ test_cmp before after
+ '
+
+ test_expect_success "stdin $type symref-verify fails with no value" '
+ git symbolic-ref refs/heads/symref >expect &&
+ format_command $type "symref-verify refs/heads/symref" "" >stdin &&
+ test_must_fail git update-ref --stdin $type --no-deref <stdin
+ '
+
+ test_expect_success "stdin $type symref-verify succeeds for dangling reference" '
+ test_when_finished "git symbolic-ref -d refs/heads/symref2" &&
+ test_must_fail git symbolic-ref refs/heads/nonexistent &&
+ git symbolic-ref refs/heads/symref2 refs/heads/nonexistent &&
+ format_command $type "symref-verify refs/heads/symref2" "refs/heads/nonexistent" >stdin &&
+ git update-ref --stdin $type --no-deref <stdin
+ '
+
+ test_expect_success "stdin $type symref-verify fails for missing reference" '
+ test-tool ref-store main for-each-reflog-ent refs/heads/symref >before &&
+ format_command $type "symref-verify refs/heads/missing" "refs/heads/unknown" >stdin &&
+ test_must_fail git update-ref --stdin $type --no-deref <stdin 2>err &&
+ grep "fatal: cannot lock ref ${SQ}refs/heads/missing${SQ}: unable to resolve reference ${SQ}refs/heads/missing${SQ}" err &&
+ test_must_fail git rev-parse --verify -q refs/heads/missing &&
+ test-tool ref-store main for-each-reflog-ent refs/heads/symref >after &&
+ test_cmp before after
+ '
+
+ test_expect_success "stdin $type symref-verify fails for wrong value" '
+ git symbolic-ref refs/heads/symref >expect &&
+ format_command $type "symref-verify refs/heads/symref" "$b" >stdin &&
+ test_must_fail git update-ref --stdin $type --no-deref <stdin &&
+ git symbolic-ref refs/heads/symref >actual &&
+ test_cmp expect actual
+ '
+
+ test_expect_success "stdin $type symref-verify fails for mistaken null value" '
+ git symbolic-ref refs/heads/symref >expect &&
+ format_command $type "symref-verify refs/heads/symref" "$Z" >stdin &&
+ test_must_fail git update-ref --stdin $type --no-deref <stdin &&
+ git symbolic-ref refs/heads/symref >actual &&
+ test_cmp expect actual
+ '
+
+ test_expect_success "stdin $type symref-delete fails without --no-deref" '
+ git symbolic-ref refs/heads/symref $a &&
+ format_command $type "symref-delete refs/heads/symref" "$a" >stdin &&
+ test_must_fail git update-ref --stdin $type <stdin 2>err &&
+ grep "fatal: symref-delete: cannot operate with deref mode" err
+ '
+
+ test_expect_success "stdin $type symref-delete fails with no ref" '
+ format_command $type "symref-delete " >stdin &&
+ test_must_fail git update-ref --stdin $type --no-deref <stdin 2>err &&
+ grep "fatal: symref-delete: missing <ref>" err
+ '
+
+ test_expect_success "stdin $type symref-delete fails deleting regular ref" '
+ test_when_finished "git update-ref -d refs/heads/regularref" &&
+ git update-ref refs/heads/regularref $a &&
+ format_command $type "symref-delete refs/heads/regularref" "$a" >stdin &&
+ test_must_fail git update-ref --stdin $type --no-deref <stdin 2>err &&
+ grep "fatal: cannot lock ref ${SQ}refs/heads/regularref${SQ}: expected symref with target ${SQ}$a${SQ}: but is a regular ref" err
+ '
+
+ test_expect_success "stdin $type symref-delete fails with too many arguments" '
+ format_command $type "symref-delete refs/heads/symref" "$a" "$a" >stdin &&
+ test_must_fail git update-ref --stdin $type --no-deref <stdin 2>err &&
+ if test "$type" = "-z"
+ then
+ grep "fatal: unknown command: $a" err
+ else
+ grep "fatal: symref-delete refs/heads/symref: extra input: $a" err
+ fi
+ '
+
+ test_expect_success "stdin $type symref-delete fails with wrong old value" '
+ format_command $type "symref-delete refs/heads/symref" "$m" >stdin &&
+ test_must_fail git update-ref --stdin $type --no-deref <stdin 2>err &&
+ grep "fatal: verifying symref target: ${SQ}refs/heads/symref${SQ}: is at $a but expected refs/heads/main" err &&
+ git symbolic-ref refs/heads/symref >expect &&
+ echo $a >actual &&
+ test_cmp expect actual
+ '
+
+ test_expect_success "stdin $type symref-delete works with right old value" '
+ format_command $type "symref-delete refs/heads/symref" "$a" >stdin &&
+ git update-ref --stdin $type --no-deref <stdin &&
+ test_must_fail git rev-parse --verify -q refs/heads/symref
+ '
+
+ test_expect_success "stdin $type symref-delete works with empty old value" '
+ git symbolic-ref refs/heads/symref $a >stdin &&
+ format_command $type "symref-delete refs/heads/symref" "" >stdin &&
+ git update-ref --stdin $type --no-deref <stdin &&
+ test_must_fail git rev-parse --verify -q $b
+ '
+
+ test_expect_success "stdin $type symref-delete succeeds for dangling reference" '
+ test_must_fail git symbolic-ref refs/heads/nonexistent &&
+ git symbolic-ref refs/heads/symref2 refs/heads/nonexistent &&
+ format_command $type "symref-delete refs/heads/symref2" "refs/heads/nonexistent" >stdin &&
+ git update-ref --stdin $type --no-deref <stdin &&
+ test_must_fail git symbolic-ref -d refs/heads/symref2
+ '
+
+ test_expect_success "stdin $type symref-delete deletes regular ref without target" '
+ git update-ref refs/heads/regularref $a &&
+ format_command $type "symref-delete refs/heads/regularref" >stdin &&
+ git update-ref --stdin $type --no-deref <stdin
+ '
+
+ test_expect_success "stdin $type symref-create fails with too many arguments" '
+ format_command $type "symref-create refs/heads/symref" "$a" "$a" >stdin &&
+ test_must_fail git update-ref --stdin $type --no-deref <stdin 2>err &&
+ if test "$type" = "-z"
+ then
+ grep "fatal: unknown command: $a" err
+ else
+ grep "fatal: symref-create refs/heads/symref: extra input: $a" err
+ fi
+ '
+
+ test_expect_success "stdin $type symref-create fails with no target" '
+ format_command $type "symref-create refs/heads/symref" >stdin &&
+ test_must_fail git update-ref --stdin $type --no-deref <stdin
+ '
+
+ test_expect_success "stdin $type symref-create fails with empty target" '
+ format_command $type "symref-create refs/heads/symref" "" >stdin &&
+ test_must_fail git update-ref --stdin $type --no-deref <stdin
+ '
+
+ test_expect_success "stdin $type symref-create works" '
+ test_when_finished "git symbolic-ref -d refs/heads/symref" &&
+ format_command $type "symref-create refs/heads/symref" "$a" >stdin &&
+ git update-ref --stdin $type --no-deref <stdin &&
+ git symbolic-ref refs/heads/symref >expect &&
+ echo $a >actual &&
+ test_cmp expect actual
+ '
+
+ test_expect_success "stdin $type symref-create works with --no-deref" '
+ test_when_finished "git symbolic-ref -d refs/heads/symref" &&
+ format_command $type "symref-create refs/heads/symref" "$a" &&
+ git update-ref --stdin $type <stdin 2>err
+ '
+
+ test_expect_success "stdin $type create dangling symref ref works" '
+ test_when_finished "git symbolic-ref -d refs/heads/symref" &&
+ format_command $type "symref-create refs/heads/symref" "refs/heads/unkown" >stdin &&
+ git update-ref --stdin $type --no-deref <stdin &&
+ git symbolic-ref refs/heads/symref >expect &&
+ echo refs/heads/unkown >actual &&
+ test_cmp expect actual
+ '
+
+ test_expect_success "stdin $type symref-create does not create reflogs by default" '
+ test_when_finished "git symbolic-ref -d refs/symref" &&
+ format_command $type "symref-create refs/symref" "$a" >stdin &&
+ git update-ref --stdin $type --no-deref <stdin &&
+ git symbolic-ref refs/symref >expect &&
+ echo $a >actual &&
+ test_cmp expect actual &&
+ test_must_fail git reflog exists refs/symref
+ '
+
+ test_expect_success "stdin $type symref-create reflogs with --create-reflog" '
+ test_when_finished "git symbolic-ref -d refs/heads/symref" &&
+ format_command $type "symref-create refs/heads/symref" "$a" >stdin &&
+ git update-ref --create-reflog --stdin $type --no-deref <stdin &&
+ git symbolic-ref refs/heads/symref >expect &&
+ echo $a >actual &&
+ test_cmp expect actual &&
+ git reflog exists refs/heads/symref
+ '
+
+ test_expect_success "stdin $type symref-update fails with too many arguments" '
+ format_command $type "symref-update refs/heads/symref" "$a" "ref" "$a" "$a" >stdin &&
+ test_must_fail git update-ref --stdin $type --no-deref <stdin 2>err &&
+ if test "$type" = "-z"
+ then
+ grep "fatal: unknown command: $a" err
+ else
+ grep "fatal: symref-update refs/heads/symref: extra input: $a" err
+ fi
+ '
+
+ test_expect_success "stdin $type symref-update fails with wrong old value argument" '
+ format_command $type "symref-update refs/heads/symref" "$a" "foo" "$a" "$a" >stdin &&
+ test_must_fail git update-ref --stdin $type --no-deref <stdin 2>err &&
+ grep "fatal: symref-update refs/heads/symref: invalid arg ${SQ}foo${SQ} for old value" err
+ '
+
+ test_expect_success "stdin $type symref-update creates with zero old value" '
+ test_when_finished "git symbolic-ref -d refs/heads/symref" &&
+ format_command $type "symref-update refs/heads/symref" "$a" "oid" "$Z" >stdin &&
+ git update-ref --stdin $type --no-deref <stdin &&
+ echo $a >expect &&
+ git symbolic-ref refs/heads/symref >actual &&
+ test_cmp expect actual
+ '
+
+ test_expect_success "stdin $type symref-update creates with no old value" '
+ test_when_finished "git symbolic-ref -d refs/heads/symref" &&
+ format_command $type "symref-update refs/heads/symref" "$a" >stdin &&
+ git update-ref --stdin $type --no-deref <stdin &&
+ echo $a >expect &&
+ git symbolic-ref refs/heads/symref >actual &&
+ test_cmp expect actual
+ '
+
+ test_expect_success "stdin $type symref-update creates dangling" '
+ test_when_finished "git symbolic-ref -d refs/heads/symref" &&
+ test_must_fail git rev-parse refs/heads/nonexistent &&
+ format_command $type "symref-update refs/heads/symref" "refs/heads/nonexistent" >stdin &&
+ git update-ref --stdin $type --no-deref <stdin &&
+ echo refs/heads/nonexistent >expect &&
+ git symbolic-ref refs/heads/symref >actual &&
+ test_cmp expect actual
+ '
+
+ test_expect_success "stdin $type symref-update fails with wrong old value" '
+ test_when_finished "git symbolic-ref -d refs/heads/symref" &&
+ git symbolic-ref refs/heads/symref $a &&
+ format_command $type "symref-update refs/heads/symref" "$m" "ref" "$b" >stdin &&
+ test_must_fail git update-ref --stdin $type --no-deref <stdin 2>err &&
+ grep "fatal: verifying symref target: ${SQ}refs/heads/symref${SQ}: is at $a but expected $b" err &&
+ test_must_fail git rev-parse --verify -q $c
+ '
+
+ test_expect_success "stdin $type symref-update updates dangling ref" '
+ test_when_finished "git symbolic-ref -d refs/heads/symref" &&
+ test_must_fail git rev-parse refs/heads/nonexistent &&
+ git symbolic-ref refs/heads/symref refs/heads/nonexistent &&
+ format_command $type "symref-update refs/heads/symref" "$a" >stdin &&
+ git update-ref --stdin $type --no-deref <stdin &&
+ echo $a >expect &&
+ git symbolic-ref refs/heads/symref >actual &&
+ test_cmp expect actual
+ '
+
+ test_expect_success "stdin $type symref-update updates dangling ref with old value" '
+ test_when_finished "git symbolic-ref -d refs/heads/symref" &&
+ test_must_fail git rev-parse refs/heads/nonexistent &&
+ git symbolic-ref refs/heads/symref refs/heads/nonexistent &&
+ format_command $type "symref-update refs/heads/symref" "$a" "ref" "refs/heads/nonexistent" >stdin &&
+ git update-ref --stdin $type --no-deref <stdin &&
+ echo $a >expect &&
+ git symbolic-ref refs/heads/symref >actual &&
+ test_cmp expect actual
+ '
+
+ test_expect_success "stdin $type symref-update fails update dangling ref with wrong old value" '
+ test_when_finished "git symbolic-ref -d refs/heads/symref" &&
+ test_must_fail git rev-parse refs/heads/nonexistent &&
+ git symbolic-ref refs/heads/symref refs/heads/nonexistent &&
+ format_command $type "symref-update refs/heads/symref" "$a" "ref" "refs/heads/wrongref" >stdin &&
+ test_must_fail git update-ref --stdin $type --no-deref <stdin &&
+ echo refs/heads/nonexistent >expect &&
+ git symbolic-ref refs/heads/symref >actual &&
+ test_cmp expect actual
+ '
+
+ test_expect_success "stdin $type symref-update works with right old value" '
+ test_when_finished "git symbolic-ref -d refs/heads/symref" &&
+ git symbolic-ref refs/heads/symref $a &&
+ format_command $type "symref-update refs/heads/symref" "$m" "ref" "$a" >stdin &&
+ git update-ref --stdin $type --no-deref <stdin &&
+ echo $m >expect &&
+ git symbolic-ref refs/heads/symref >actual &&
+ test_cmp expect actual
+ '
+
+ test_expect_success "stdin $type symref-update works with no old value" '
+ test_when_finished "git symbolic-ref -d refs/heads/symref" &&
+ git symbolic-ref refs/heads/symref $a &&
+ format_command $type "symref-update refs/heads/symref" "$m" >stdin &&
+ git update-ref --stdin $type --no-deref <stdin &&
+ echo $m >expect &&
+ git symbolic-ref refs/heads/symref >actual &&
+ test_cmp expect actual
+ '
+
+ test_expect_success "stdin $type symref-update fails with empty old ref-target" '
+ test_when_finished "git symbolic-ref -d refs/heads/symref" &&
+ git symbolic-ref refs/heads/symref $a &&
+ format_command $type "symref-update refs/heads/symref" "$m" "ref" "" >stdin &&
+ test_must_fail git update-ref --stdin $type --no-deref <stdin &&
+ echo $a >expect &&
+ git symbolic-ref refs/heads/symref >actual &&
+ test_cmp expect actual
+ '
+
+ test_expect_success "stdin $type symref-update creates (with deref)" '
+ test_when_finished "git symbolic-ref -d refs/heads/symref" &&
+ format_command $type "symref-update refs/heads/symref" "$a" >stdin &&
+ git update-ref --stdin $type <stdin &&
+ echo $a >expect &&
+ git symbolic-ref --no-recurse refs/heads/symref >actual &&
+ test_cmp expect actual &&
+ test-tool ref-store main for-each-reflog-ent refs/heads/symref >actual &&
+ grep "$Z $(git rev-parse $a)" actual
+ '
+
+ test_expect_success "stdin $type symref-update regular ref to symref with correct old-oid" '
+ test_when_finished "git symbolic-ref -d --no-recurse refs/heads/regularref" &&
+ git update-ref --no-deref refs/heads/regularref $a &&
+ format_command $type "symref-update refs/heads/regularref" "$a" "oid" "$(git rev-parse $a)" >stdin &&
+ git update-ref --stdin $type <stdin &&
+ echo $a >expect &&
+ git symbolic-ref --no-recurse refs/heads/regularref >actual &&
+ test_cmp expect actual &&
+ test-tool ref-store main for-each-reflog-ent refs/heads/regularref >actual &&
+ grep "$(git rev-parse $a) $(git rev-parse $a)" actual
+ '
+
+ test_expect_success "stdin $type symref-update regular ref to symref fails with wrong old-oid" '
+ test_when_finished "git update-ref -d refs/heads/regularref" &&
+ git update-ref --no-deref refs/heads/regularref $a &&
+ format_command $type "symref-update refs/heads/regularref" "$a" "oid" "$(git rev-parse refs/heads/target2)" >stdin &&
+ test_must_fail git update-ref --stdin $type <stdin 2>err &&
+ grep "fatal: cannot lock ref ${SQ}refs/heads/regularref${SQ}: is at $(git rev-parse $a) but expected $(git rev-parse refs/heads/target2)" err &&
+ echo $(git rev-parse $a) >expect &&
+ git rev-parse refs/heads/regularref >actual &&
+ test_cmp expect actual
+ '
+
+ test_expect_success "stdin $type symref-update regular ref to symref fails with invalid old-oid" '
+ test_when_finished "git update-ref -d refs/heads/regularref" &&
+ git update-ref --no-deref refs/heads/regularref $a &&
+ format_command $type "symref-update refs/heads/regularref" "$a" "oid" "not-a-ref-oid" >stdin &&
+ test_must_fail git update-ref --stdin $type <stdin 2>err &&
+ grep "fatal: symref-update refs/heads/regularref: invalid oid: not-a-ref-oid" err &&
+ echo $(git rev-parse $a) >expect &&
+ git rev-parse refs/heads/regularref >actual &&
+ test_cmp expect actual
+ '
+
+ test_expect_success "stdin $type symref-update existing symref with zero old-oid" '
+ test_when_finished "git symbolic-ref -d --no-recurse refs/heads/symref" &&
+ git symbolic-ref refs/heads/symref refs/heads/target2 &&
+ format_command $type "symref-update refs/heads/symref" "$a" "oid" "$Z" >stdin &&
+ test_must_fail git update-ref --stdin $type <stdin 2>err &&
+ grep "fatal: cannot lock ref ${SQ}refs/heads/symref${SQ}: reference already exists" err &&
+ echo refs/heads/target2 >expect &&
+ git symbolic-ref refs/heads/symref >actual &&
+ test_cmp expect actual
+ '
+
+ test_expect_success "stdin $type symref-update regular ref to symref (with deref)" '
+ test_when_finished "git symbolic-ref -d refs/heads/symref" &&
+ test_when_finished "git update-ref -d --no-deref refs/heads/symref2" &&
+ git update-ref refs/heads/symref2 $a &&
+ git symbolic-ref --no-recurse refs/heads/symref refs/heads/symref2 &&
+ format_command $type "symref-update refs/heads/symref" "$a" >stdin &&
+ git update-ref $type --stdin <stdin &&
+ echo $a >expect &&
+ git symbolic-ref --no-recurse refs/heads/symref2 >actual &&
+ test_cmp expect actual &&
+ echo refs/heads/symref2 >expect &&
+ git symbolic-ref --no-recurse refs/heads/symref >actual &&
+ test_cmp expect actual &&
+ test-tool ref-store main for-each-reflog-ent refs/heads/symref >actual &&
+ grep "$(git rev-parse $a) $(git rev-parse $a)" actual
+ '
+
+ test_expect_success "stdin $type symref-update regular ref to symref" '
+ test_when_finished "git symbolic-ref -d --no-recurse refs/heads/regularref" &&
+ git update-ref --no-deref refs/heads/regularref $a &&
+ format_command $type "symref-update refs/heads/regularref" "$a" >stdin &&
+ git update-ref $type --stdin <stdin &&
+ echo $a >expect &&
+ git symbolic-ref --no-recurse refs/heads/regularref >actual &&
+ test_cmp expect actual &&
+ test-tool ref-store main for-each-reflog-ent refs/heads/regularref >actual &&
+ grep "$(git rev-parse $a) $(git rev-parse $a)" actual
+ '
+
+done
+
test_done
diff --git a/t/t1403-show-ref.sh b/t/t1403-show-ref.sh
index 33fb7a38ff..403f6b8f7d 100755
--- a/t/t1403-show-ref.sh
+++ b/t/t1403-show-ref.sh
@@ -121,13 +121,13 @@ test_expect_success 'show-ref -d' '
'
-test_expect_success 'show-ref --heads, --tags, --head, pattern' '
+test_expect_success 'show-ref --branches, --tags, --head, pattern' '
for branch in B main side
do
echo $(git rev-parse refs/heads/$branch) refs/heads/$branch || return 1
- done >expect.heads &&
- git show-ref --heads >actual &&
- test_cmp expect.heads actual &&
+ done >expect.branches &&
+ git show-ref --branches >actual &&
+ test_cmp expect.branches actual &&
for tag in A B C
do
@@ -136,15 +136,15 @@ test_expect_success 'show-ref --heads, --tags, --head, pattern' '
git show-ref --tags >actual &&
test_cmp expect.tags actual &&
- cat expect.heads expect.tags >expect &&
- git show-ref --heads --tags >actual &&
+ cat expect.branches expect.tags >expect &&
+ git show-ref --branches --tags >actual &&
test_cmp expect actual &&
{
echo $(git rev-parse HEAD) HEAD &&
- cat expect.heads expect.tags
+ cat expect.branches expect.tags
} >expect &&
- git show-ref --heads --tags --head >actual &&
+ git show-ref --branches --tags --head >actual &&
test_cmp expect actual &&
{
@@ -165,6 +165,14 @@ test_expect_success 'show-ref --heads, --tags, --head, pattern' '
test_cmp expect actual
'
+test_expect_success 'show-ref --heads is deprecated and hidden' '
+ test_expect_code 129 git show-ref -h >short-help &&
+ test_grep ! -e --heads short-help &&
+ git show-ref --heads >actual 2>warning &&
+ test_grep ! deprecated warning &&
+ test_cmp expect.branches actual
+'
+
test_expect_success 'show-ref --verify HEAD' '
echo $(git rev-parse HEAD) HEAD >expect &&
git show-ref --verify HEAD >actual &&
diff --git a/t/t1404-update-ref-errors.sh b/t/t1404-update-ref-errors.sh
index 98e9158bd2..df90112618 100755
--- a/t/t1404-update-ref-errors.sh
+++ b/t/t1404-update-ref-errors.sh
@@ -100,13 +100,13 @@ df_test() {
printf "%s\n" "delete $delname" "create $addname $D"
fi >commands &&
test_must_fail git update-ref --stdin <commands 2>output.err &&
- grep "fatal:\( cannot lock ref $SQ$addname$SQ:\)\? $SQ$delref$SQ exists; cannot create $SQ$addref$SQ" output.err &&
+ grep -E "fatal:( cannot lock ref '$addname':)? '$delref' exists; cannot create '$addref'" output.err &&
printf "%s\n" "$C $delref" >expected-refs &&
git for-each-ref --format="%(objectname) %(refname)" $prefix/r >actual-refs &&
test_cmp expected-refs actual-refs
}
-test_expect_success 'setup' '
+test_expect_success 'setup' - <<\EOT
git commit --allow-empty -m Initial &&
C=$(git rev-parse HEAD) &&
@@ -114,283 +114,283 @@ test_expect_success 'setup' '
D=$(git rev-parse HEAD) &&
git commit --allow-empty -m Third &&
E=$(git rev-parse HEAD)
-'
+EOT
-test_expect_success 'existing loose ref is a simple prefix of new' '
+test_expect_success 'existing loose ref is a simple prefix of new' - <<\EOT
prefix=refs/1l &&
test_update_rejected "a c e" false "b c/x d" \
- "$SQ$prefix/c$SQ exists; cannot create $SQ$prefix/c/x$SQ"
+ "'$prefix/c' exists; cannot create '$prefix/c/x'"
-'
+EOT
-test_expect_success 'existing packed ref is a simple prefix of new' '
+test_expect_success 'existing packed ref is a simple prefix of new' - <<\EOT
prefix=refs/1p &&
test_update_rejected "a c e" true "b c/x d" \
- "$SQ$prefix/c$SQ exists; cannot create $SQ$prefix/c/x$SQ"
+ "'$prefix/c' exists; cannot create '$prefix/c/x'"
-'
+EOT
-test_expect_success 'existing loose ref is a deeper prefix of new' '
+test_expect_success 'existing loose ref is a deeper prefix of new' - <<\EOT
prefix=refs/2l &&
test_update_rejected "a c e" false "b c/x/y d" \
- "$SQ$prefix/c$SQ exists; cannot create $SQ$prefix/c/x/y$SQ"
+ "'$prefix/c' exists; cannot create '$prefix/c/x/y'"
-'
+EOT
-test_expect_success 'existing packed ref is a deeper prefix of new' '
+test_expect_success 'existing packed ref is a deeper prefix of new' - <<\EOT
prefix=refs/2p &&
test_update_rejected "a c e" true "b c/x/y d" \
- "$SQ$prefix/c$SQ exists; cannot create $SQ$prefix/c/x/y$SQ"
+ "'$prefix/c' exists; cannot create '$prefix/c/x/y'"
-'
+EOT
-test_expect_success 'new ref is a simple prefix of existing loose' '
+test_expect_success 'new ref is a simple prefix of existing loose' - <<\EOT
prefix=refs/3l &&
test_update_rejected "a c/x e" false "b c d" \
- "$SQ$prefix/c/x$SQ exists; cannot create $SQ$prefix/c$SQ"
+ "'$prefix/c/x' exists; cannot create '$prefix/c'"
-'
+EOT
-test_expect_success 'new ref is a simple prefix of existing packed' '
+test_expect_success 'new ref is a simple prefix of existing packed' - <<\EOT
prefix=refs/3p &&
test_update_rejected "a c/x e" true "b c d" \
- "$SQ$prefix/c/x$SQ exists; cannot create $SQ$prefix/c$SQ"
+ "'$prefix/c/x' exists; cannot create '$prefix/c'"
-'
+EOT
-test_expect_success 'new ref is a deeper prefix of existing loose' '
+test_expect_success 'new ref is a deeper prefix of existing loose' - <<\EOT
prefix=refs/4l &&
test_update_rejected "a c/x/y e" false "b c d" \
- "$SQ$prefix/c/x/y$SQ exists; cannot create $SQ$prefix/c$SQ"
+ "'$prefix/c/x/y' exists; cannot create '$prefix/c'"
-'
+EOT
-test_expect_success 'new ref is a deeper prefix of existing packed' '
+test_expect_success 'new ref is a deeper prefix of existing packed' - <<\EOT
prefix=refs/4p &&
test_update_rejected "a c/x/y e" true "b c d" \
- "$SQ$prefix/c/x/y$SQ exists; cannot create $SQ$prefix/c$SQ"
+ "'$prefix/c/x/y' exists; cannot create '$prefix/c'"
-'
+EOT
-test_expect_success 'one new ref is a simple prefix of another' '
+test_expect_success 'one new ref is a simple prefix of another' - <<\EOT
prefix=refs/5 &&
test_update_rejected "a e" false "b c c/x d" \
- "cannot process $SQ$prefix/c$SQ and $SQ$prefix/c/x$SQ at the same time"
+ "cannot process '$prefix/c' and '$prefix/c/x' at the same time"
-'
+EOT
-test_expect_success 'D/F conflict prevents add long + delete short' '
+test_expect_success 'D/F conflict prevents add long + delete short' - <<\EOT
df_test refs/df-al-ds --add-del foo/bar foo
-'
+EOT
-test_expect_success 'D/F conflict prevents add short + delete long' '
+test_expect_success 'D/F conflict prevents add short + delete long' - <<\EOT
df_test refs/df-as-dl --add-del foo foo/bar
-'
+EOT
-test_expect_success 'D/F conflict prevents delete long + add short' '
+test_expect_success 'D/F conflict prevents delete long + add short' - <<\EOT
df_test refs/df-dl-as --del-add foo/bar foo
-'
+EOT
-test_expect_success 'D/F conflict prevents delete short + add long' '
+test_expect_success 'D/F conflict prevents delete short + add long' - <<\EOT
df_test refs/df-ds-al --del-add foo foo/bar
-'
+EOT
-test_expect_success 'D/F conflict prevents add long + delete short packed' '
+test_expect_success 'D/F conflict prevents add long + delete short packed' - <<\EOT
df_test refs/df-al-dsp --pack --add-del foo/bar foo
-'
+EOT
-test_expect_success 'D/F conflict prevents add short + delete long packed' '
+test_expect_success 'D/F conflict prevents add short + delete long packed' - <<\EOT
df_test refs/df-as-dlp --pack --add-del foo foo/bar
-'
+EOT
-test_expect_success 'D/F conflict prevents delete long packed + add short' '
+test_expect_success 'D/F conflict prevents delete long packed + add short' - <<\EOT
df_test refs/df-dlp-as --pack --del-add foo/bar foo
-'
+EOT
-test_expect_success 'D/F conflict prevents delete short packed + add long' '
+test_expect_success 'D/F conflict prevents delete short packed + add long' - <<\EOT
df_test refs/df-dsp-al --pack --del-add foo foo/bar
-'
+EOT
# Try some combinations involving symbolic refs...
-test_expect_success 'D/F conflict prevents indirect add long + delete short' '
+test_expect_success 'D/F conflict prevents indirect add long + delete short' - <<\EOT
df_test refs/df-ial-ds --sym-add --add-del foo/bar foo
-'
+EOT
-test_expect_success 'D/F conflict prevents indirect add long + indirect delete short' '
+test_expect_success 'D/F conflict prevents indirect add long + indirect delete short' - <<\EOT
df_test refs/df-ial-ids --sym-add --sym-del --add-del foo/bar foo
-'
+EOT
-test_expect_success 'D/F conflict prevents indirect add short + indirect delete long' '
+test_expect_success 'D/F conflict prevents indirect add short + indirect delete long' - <<\EOT
df_test refs/df-ias-idl --sym-add --sym-del --add-del foo foo/bar
-'
+EOT
-test_expect_success 'D/F conflict prevents indirect delete long + indirect add short' '
+test_expect_success 'D/F conflict prevents indirect delete long + indirect add short' - <<\EOT
df_test refs/df-idl-ias --sym-add --sym-del --del-add foo/bar foo
-'
+EOT
-test_expect_success 'D/F conflict prevents indirect add long + delete short packed' '
+test_expect_success 'D/F conflict prevents indirect add long + delete short packed' - <<\EOT
df_test refs/df-ial-dsp --sym-add --pack --add-del foo/bar foo
-'
+EOT
-test_expect_success 'D/F conflict prevents indirect add long + indirect delete short packed' '
+test_expect_success 'D/F conflict prevents indirect add long + indirect delete short packed' - <<\EOT
df_test refs/df-ial-idsp --sym-add --sym-del --pack --add-del foo/bar foo
-'
+EOT
-test_expect_success 'D/F conflict prevents add long + indirect delete short packed' '
+test_expect_success 'D/F conflict prevents add long + indirect delete short packed' - <<\EOT
df_test refs/df-al-idsp --sym-del --pack --add-del foo/bar foo
-'
+EOT
-test_expect_success 'D/F conflict prevents indirect delete long packed + indirect add short' '
+test_expect_success 'D/F conflict prevents indirect delete long packed + indirect add short' - <<\EOT
df_test refs/df-idlp-ias --sym-add --sym-del --pack --del-add foo/bar foo
-'
+EOT
# Test various errors when reading the old values of references...
-test_expect_success 'missing old value blocks update' '
+test_expect_success 'missing old value blocks update' - <<\EOT
prefix=refs/missing-update &&
cat >expected <<-EOF &&
- fatal: cannot lock ref $SQ$prefix/foo$SQ: unable to resolve reference $SQ$prefix/foo$SQ
+ fatal: cannot lock ref '$prefix/foo': unable to resolve reference '$prefix/foo'
EOF
printf "%s\n" "update $prefix/foo $E $D" |
test_must_fail git update-ref --stdin 2>output.err &&
test_cmp expected output.err
-'
+EOT
-test_expect_success 'incorrect old value blocks update' '
+test_expect_success 'incorrect old value blocks update' - <<\EOT
prefix=refs/incorrect-update &&
git update-ref $prefix/foo $C &&
cat >expected <<-EOF &&
- fatal: cannot lock ref $SQ$prefix/foo$SQ: is at $C but expected $D
+ fatal: cannot lock ref '$prefix/foo': is at $C but expected $D
EOF
printf "%s\n" "update $prefix/foo $E $D" |
test_must_fail git update-ref --stdin 2>output.err &&
test_cmp expected output.err
-'
+EOT
-test_expect_success 'existing old value blocks create' '
+test_expect_success 'existing old value blocks create' - <<\EOT
prefix=refs/existing-create &&
git update-ref $prefix/foo $C &&
cat >expected <<-EOF &&
- fatal: cannot lock ref $SQ$prefix/foo$SQ: reference already exists
+ fatal: cannot lock ref '$prefix/foo': reference already exists
EOF
printf "%s\n" "create $prefix/foo $E" |
test_must_fail git update-ref --stdin 2>output.err &&
test_cmp expected output.err
-'
+EOT
-test_expect_success 'incorrect old value blocks delete' '
+test_expect_success 'incorrect old value blocks delete' - <<\EOT
prefix=refs/incorrect-delete &&
git update-ref $prefix/foo $C &&
cat >expected <<-EOF &&
- fatal: cannot lock ref $SQ$prefix/foo$SQ: is at $C but expected $D
+ fatal: cannot lock ref '$prefix/foo': is at $C but expected $D
EOF
printf "%s\n" "delete $prefix/foo $D" |
test_must_fail git update-ref --stdin 2>output.err &&
test_cmp expected output.err
-'
+EOT
-test_expect_success 'missing old value blocks indirect update' '
+test_expect_success 'missing old value blocks indirect update' - <<\EOT
prefix=refs/missing-indirect-update &&
git symbolic-ref $prefix/symref $prefix/foo &&
cat >expected <<-EOF &&
- fatal: cannot lock ref $SQ$prefix/symref$SQ: unable to resolve reference $SQ$prefix/foo$SQ
+ fatal: cannot lock ref '$prefix/symref': unable to resolve reference '$prefix/foo'
EOF
printf "%s\n" "update $prefix/symref $E $D" |
test_must_fail git update-ref --stdin 2>output.err &&
test_cmp expected output.err
-'
+EOT
-test_expect_success 'incorrect old value blocks indirect update' '
+test_expect_success 'incorrect old value blocks indirect update' - <<\EOT
prefix=refs/incorrect-indirect-update &&
git symbolic-ref $prefix/symref $prefix/foo &&
git update-ref $prefix/foo $C &&
cat >expected <<-EOF &&
- fatal: cannot lock ref $SQ$prefix/symref$SQ: is at $C but expected $D
+ fatal: cannot lock ref '$prefix/symref': is at $C but expected $D
EOF
printf "%s\n" "update $prefix/symref $E $D" |
test_must_fail git update-ref --stdin 2>output.err &&
test_cmp expected output.err
-'
+EOT
-test_expect_success 'existing old value blocks indirect create' '
+test_expect_success 'existing old value blocks indirect create' - <<\EOT
prefix=refs/existing-indirect-create &&
git symbolic-ref $prefix/symref $prefix/foo &&
git update-ref $prefix/foo $C &&
cat >expected <<-EOF &&
- fatal: cannot lock ref $SQ$prefix/symref$SQ: reference already exists
+ fatal: cannot lock ref '$prefix/symref': reference already exists
EOF
printf "%s\n" "create $prefix/symref $E" |
test_must_fail git update-ref --stdin 2>output.err &&
test_cmp expected output.err
-'
+EOT
-test_expect_success 'incorrect old value blocks indirect delete' '
+test_expect_success 'incorrect old value blocks indirect delete' - <<\EOT
prefix=refs/incorrect-indirect-delete &&
git symbolic-ref $prefix/symref $prefix/foo &&
git update-ref $prefix/foo $C &&
cat >expected <<-EOF &&
- fatal: cannot lock ref $SQ$prefix/symref$SQ: is at $C but expected $D
+ fatal: cannot lock ref '$prefix/symref': is at $C but expected $D
EOF
printf "%s\n" "delete $prefix/symref $D" |
test_must_fail git update-ref --stdin 2>output.err &&
test_cmp expected output.err
-'
+EOT
-test_expect_success 'missing old value blocks indirect no-deref update' '
+test_expect_success 'missing old value blocks indirect no-deref update' - <<\EOT
prefix=refs/missing-noderef-update &&
git symbolic-ref $prefix/symref $prefix/foo &&
cat >expected <<-EOF &&
- fatal: cannot lock ref $SQ$prefix/symref$SQ: reference is missing but expected $D
+ fatal: cannot lock ref '$prefix/symref': reference is missing but expected $D
EOF
printf "%s\n" "option no-deref" "update $prefix/symref $E $D" |
test_must_fail git update-ref --stdin 2>output.err &&
test_cmp expected output.err
-'
+EOT
-test_expect_success 'incorrect old value blocks indirect no-deref update' '
+test_expect_success 'incorrect old value blocks indirect no-deref update' - <<\EOT
prefix=refs/incorrect-noderef-update &&
git symbolic-ref $prefix/symref $prefix/foo &&
git update-ref $prefix/foo $C &&
cat >expected <<-EOF &&
- fatal: cannot lock ref $SQ$prefix/symref$SQ: is at $C but expected $D
+ fatal: cannot lock ref '$prefix/symref': is at $C but expected $D
EOF
printf "%s\n" "option no-deref" "update $prefix/symref $E $D" |
test_must_fail git update-ref --stdin 2>output.err &&
test_cmp expected output.err
-'
+EOT
-test_expect_success 'existing old value blocks indirect no-deref create' '
+test_expect_success 'existing old value blocks indirect no-deref create' - <<\EOT
prefix=refs/existing-noderef-create &&
git symbolic-ref $prefix/symref $prefix/foo &&
git update-ref $prefix/foo $C &&
cat >expected <<-EOF &&
- fatal: cannot lock ref $SQ$prefix/symref$SQ: reference already exists
+ fatal: cannot lock ref '$prefix/symref': reference already exists
EOF
printf "%s\n" "option no-deref" "create $prefix/symref $E" |
test_must_fail git update-ref --stdin 2>output.err &&
test_cmp expected output.err
-'
+EOT
-test_expect_success 'incorrect old value blocks indirect no-deref delete' '
+test_expect_success 'incorrect old value blocks indirect no-deref delete' - <<\EOT
prefix=refs/incorrect-noderef-delete &&
git symbolic-ref $prefix/symref $prefix/foo &&
git update-ref $prefix/foo $C &&
cat >expected <<-EOF &&
- fatal: cannot lock ref $SQ$prefix/symref$SQ: is at $C but expected $D
+ fatal: cannot lock ref '$prefix/symref': is at $C but expected $D
EOF
printf "%s\n" "option no-deref" "delete $prefix/symref $D" |
test_must_fail git update-ref --stdin 2>output.err &&
test_cmp expected output.err
-'
+EOT
test_done
diff --git a/t/t1416-ref-transaction-hooks.sh b/t/t1416-ref-transaction-hooks.sh
index 2092488090..5a812ca3c0 100755
--- a/t/t1416-ref-transaction-hooks.sh
+++ b/t/t1416-ref-transaction-hooks.sh
@@ -134,4 +134,81 @@ test_expect_success 'interleaving hook calls succeed' '
test_cmp expect target-repo.git/actual
'
+test_expect_success 'hook captures git-symbolic-ref updates' '
+ test_when_finished "rm actual" &&
+
+ test_hook reference-transaction <<-\EOF &&
+ echo "$*" >>actual
+ while read -r line
+ do
+ printf "%s\n" "$line"
+ done >>actual
+ EOF
+
+ git symbolic-ref refs/heads/symref refs/heads/main &&
+
+ cat >expect <<-EOF &&
+ prepared
+ $ZERO_OID ref:refs/heads/main refs/heads/symref
+ committed
+ $ZERO_OID ref:refs/heads/main refs/heads/symref
+ EOF
+
+ test_cmp expect actual
+'
+
+test_expect_success 'hook gets all queued symref updates' '
+ test_when_finished "rm actual" &&
+
+ git update-ref refs/heads/branch $POST_OID &&
+ git symbolic-ref refs/heads/symref refs/heads/main &&
+ git symbolic-ref refs/heads/symrefd refs/heads/main &&
+ git symbolic-ref refs/heads/symrefu refs/heads/main &&
+
+ test_hook reference-transaction <<-\EOF &&
+ echo "$*" >>actual
+ while read -r line
+ do
+ printf "%s\n" "$line"
+ done >>actual
+ EOF
+
+ # In the files backend, "delete" also triggers an additional transaction
+ # update on the packed-refs backend, which constitutes additional reflog
+ # entries.
+ if test_have_prereq REFFILES
+ then
+ cat >expect <<-EOF
+ aborted
+ $ZERO_OID $ZERO_OID refs/heads/symrefd
+ EOF
+ else
+ >expect
+ fi &&
+
+ cat >>expect <<-EOF &&
+ prepared
+ ref:refs/heads/main $ZERO_OID refs/heads/symref
+ ref:refs/heads/main $ZERO_OID refs/heads/symrefd
+ $ZERO_OID ref:refs/heads/main refs/heads/symrefc
+ ref:refs/heads/main ref:refs/heads/branch refs/heads/symrefu
+ committed
+ ref:refs/heads/main $ZERO_OID refs/heads/symref
+ ref:refs/heads/main $ZERO_OID refs/heads/symrefd
+ $ZERO_OID ref:refs/heads/main refs/heads/symrefc
+ ref:refs/heads/main ref:refs/heads/branch refs/heads/symrefu
+ EOF
+
+ git update-ref --no-deref --stdin <<-EOF &&
+ start
+ symref-verify refs/heads/symref refs/heads/main
+ symref-delete refs/heads/symrefd refs/heads/main
+ symref-create refs/heads/symrefc refs/heads/main
+ symref-update refs/heads/symrefu refs/heads/branch ref refs/heads/main
+ prepare
+ commit
+ EOF
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t1460-refs-migrate.sh b/t/t1460-refs-migrate.sh
new file mode 100755
index 0000000000..f7c0783d30
--- /dev/null
+++ b/t/t1460-refs-migrate.sh
@@ -0,0 +1,243 @@
+#!/bin/sh
+
+test_description='migration of ref storage backends'
+
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
+TEST_PASSES_SANITIZE_LEAK=true
+. ./test-lib.sh
+
+test_migration () {
+ git -C "$1" for-each-ref --include-root-refs \
+ --format='%(refname) %(objectname) %(symref)' >expect &&
+ git -C "$1" refs migrate --ref-format="$2" &&
+ git -C "$1" for-each-ref --include-root-refs \
+ --format='%(refname) %(objectname) %(symref)' >actual &&
+ test_cmp expect actual &&
+
+ git -C "$1" rev-parse --show-ref-format >actual &&
+ echo "$2" >expect &&
+ test_cmp expect actual
+}
+
+test_expect_success 'setup' '
+ rm -rf .git &&
+ # The migration does not yet support reflogs.
+ git config --global core.logAllRefUpdates false
+'
+
+test_expect_success "superfluous arguments" '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ test_must_fail git -C repo refs migrate foo 2>err &&
+ cat >expect <<-EOF &&
+ usage: too many arguments
+ EOF
+ test_cmp expect err
+'
+
+test_expect_success "missing ref storage format" '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ test_must_fail git -C repo refs migrate 2>err &&
+ cat >expect <<-EOF &&
+ usage: missing --ref-format=<format>
+ EOF
+ test_cmp expect err
+'
+
+test_expect_success "unknown ref storage format" '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ test_must_fail git -C repo refs migrate \
+ --ref-format=unknown 2>err &&
+ cat >expect <<-EOF &&
+ error: unknown ref storage format ${SQ}unknown${SQ}
+ EOF
+ test_cmp expect err
+'
+
+ref_formats="files reftable"
+for from_format in $ref_formats
+do
+ for to_format in $ref_formats
+ do
+ if test "$from_format" = "$to_format"
+ then
+ continue
+ fi
+
+ test_expect_success "$from_format: migration to same format fails" '
+ test_when_finished "rm -rf repo" &&
+ git init --ref-format=$from_format repo &&
+ test_must_fail git -C repo refs migrate \
+ --ref-format=$from_format 2>err &&
+ cat >expect <<-EOF &&
+ error: repository already uses ${SQ}$from_format${SQ} format
+ EOF
+ test_cmp expect err
+ '
+
+ test_expect_success "$from_format -> $to_format: migration with reflog fails" '
+ test_when_finished "rm -rf repo" &&
+ git init --ref-format=$from_format repo &&
+ test_config -C repo core.logAllRefUpdates true &&
+ test_commit -C repo logged &&
+ test_must_fail git -C repo refs migrate \
+ --ref-format=$to_format 2>err &&
+ cat >expect <<-EOF &&
+ error: migrating reflogs is not supported yet
+ EOF
+ test_cmp expect err
+ '
+
+ test_expect_success "$from_format -> $to_format: migration with worktree fails" '
+ test_when_finished "rm -rf repo" &&
+ git init --ref-format=$from_format repo &&
+ git -C repo worktree add wt &&
+ test_must_fail git -C repo refs migrate \
+ --ref-format=$to_format 2>err &&
+ cat >expect <<-EOF &&
+ error: migrating repositories with worktrees is not supported yet
+ EOF
+ test_cmp expect err
+ '
+
+ test_expect_success "$from_format -> $to_format: unborn HEAD" '
+ test_when_finished "rm -rf repo" &&
+ git init --ref-format=$from_format repo &&
+ test_migration repo "$to_format"
+ '
+
+ test_expect_success "$from_format -> $to_format: single ref" '
+ test_when_finished "rm -rf repo" &&
+ git init --ref-format=$from_format repo &&
+ test_commit -C repo initial &&
+ test_migration repo "$to_format"
+ '
+
+ test_expect_success "$from_format -> $to_format: bare repository" '
+ test_when_finished "rm -rf repo repo.git" &&
+ git init --ref-format=$from_format repo &&
+ test_commit -C repo initial &&
+ git clone --ref-format=$from_format --mirror repo repo.git &&
+ test_migration repo.git "$to_format"
+ '
+
+ test_expect_success "$from_format -> $to_format: dangling symref" '
+ test_when_finished "rm -rf repo" &&
+ git init --ref-format=$from_format repo &&
+ test_commit -C repo initial &&
+ git -C repo symbolic-ref BROKEN_HEAD refs/heads/nonexistent &&
+ test_migration repo "$to_format" &&
+ echo refs/heads/nonexistent >expect &&
+ git -C repo symbolic-ref BROKEN_HEAD >actual &&
+ test_cmp expect actual
+ '
+
+ test_expect_success "$from_format -> $to_format: broken ref" '
+ test_when_finished "rm -rf repo" &&
+ git init --ref-format=$from_format repo &&
+ test_commit -C repo initial &&
+ test-tool -C repo ref-store main update-ref "" refs/heads/broken \
+ "$(test_oid 001)" "$ZERO_OID" REF_SKIP_CREATE_REFLOG,REF_SKIP_OID_VERIFICATION &&
+ test_migration repo "$to_format" &&
+ test_oid 001 >expect &&
+ git -C repo rev-parse refs/heads/broken >actual &&
+ test_cmp expect actual
+ '
+
+ test_expect_success "$from_format -> $to_format: pseudo-refs" '
+ test_when_finished "rm -rf repo" &&
+ git init --ref-format=$from_format repo &&
+ test_commit -C repo initial &&
+ git -C repo update-ref FOO_HEAD HEAD &&
+ test_migration repo "$to_format"
+ '
+
+ test_expect_success "$from_format -> $to_format: special refs are left alone" '
+ test_when_finished "rm -rf repo" &&
+ git init --ref-format=$from_format repo &&
+ test_commit -C repo initial &&
+ git -C repo rev-parse HEAD >repo/.git/MERGE_HEAD &&
+ git -C repo rev-parse MERGE_HEAD &&
+ test_migration repo "$to_format" &&
+ test_path_is_file repo/.git/MERGE_HEAD
+ '
+
+ test_expect_success "$from_format -> $to_format: a bunch of refs" '
+ test_when_finished "rm -rf repo" &&
+ git init --ref-format=$from_format repo &&
+
+ test_commit -C repo initial &&
+ cat >input <<-EOF &&
+ create FOO_HEAD HEAD
+ create refs/heads/branch-1 HEAD
+ create refs/heads/branch-2 HEAD
+ create refs/heads/branch-3 HEAD
+ create refs/heads/branch-4 HEAD
+ create refs/tags/tag-1 HEAD
+ create refs/tags/tag-2 HEAD
+ EOF
+ git -C repo update-ref --stdin <input &&
+ test_migration repo "$to_format"
+ '
+
+ test_expect_success "$from_format -> $to_format: dry-run migration does not modify repository" '
+ test_when_finished "rm -rf repo" &&
+ git init --ref-format=$from_format repo &&
+ test_commit -C repo initial &&
+ git -C repo refs migrate --dry-run \
+ --ref-format=$to_format >output &&
+ grep "Finished dry-run migration of refs" output &&
+ test_path_is_dir repo/.git/ref_migration.* &&
+ echo $from_format >expect &&
+ git -C repo rev-parse --show-ref-format >actual &&
+ test_cmp expect actual
+ '
+ done
+done
+
+test_expect_success 'migrating from files format deletes backend files' '
+ test_when_finished "rm -rf repo" &&
+ git init --ref-format=files repo &&
+ test_commit -C repo first &&
+ git -C repo pack-refs --all &&
+ test_commit -C repo second &&
+ git -C repo update-ref ORIG_HEAD HEAD &&
+ git -C repo rev-parse HEAD >repo/.git/FETCH_HEAD &&
+
+ test_path_is_file repo/.git/HEAD &&
+ test_path_is_file repo/.git/ORIG_HEAD &&
+ test_path_is_file repo/.git/refs/heads/main &&
+ test_path_is_file repo/.git/packed-refs &&
+
+ test_migration repo reftable &&
+
+ echo "ref: refs/heads/.invalid" >expect &&
+ test_cmp expect repo/.git/HEAD &&
+ echo "this repository uses the reftable format" >expect &&
+ test_cmp expect repo/.git/refs/heads &&
+ test_path_is_file repo/.git/FETCH_HEAD &&
+ test_path_is_missing repo/.git/ORIG_HEAD &&
+ test_path_is_missing repo/.git/refs/heads/main &&
+ test_path_is_missing repo/.git/logs &&
+ test_path_is_missing repo/.git/packed-refs
+'
+
+test_expect_success 'migrating from reftable format deletes backend files' '
+ test_when_finished "rm -rf repo" &&
+ git init --ref-format=reftable repo &&
+ test_commit -C repo first &&
+
+ test_path_is_dir repo/.git/reftable &&
+ test_migration repo files &&
+
+ test_path_is_missing repo/.git/reftable &&
+ echo "ref: refs/heads/main" >expect &&
+ test_cmp expect repo/.git/HEAD &&
+ test_path_is_file repo/.git/refs/heads/main
+'
+
+test_done
diff --git a/t/t1500-rev-parse.sh b/t/t1500-rev-parse.sh
index a669e592f1..30c31918fd 100755
--- a/t/t1500-rev-parse.sh
+++ b/t/t1500-rev-parse.sh
@@ -304,4 +304,10 @@ test_expect_success 'rev-parse --bisect includes bad, excludes good' '
test_cmp expect actual
'
+test_expect_success '--short= truncates to the actual hash length' '
+ git rev-parse HEAD >expect &&
+ git rev-parse --short=100 HEAD >actual &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t1512-rev-parse-disambiguation.sh b/t/t1512-rev-parse-disambiguation.sh
index 70f1e0a998..f9d68ce74e 100755
--- a/t/t1512-rev-parse-disambiguation.sh
+++ b/t/t1512-rev-parse-disambiguation.sh
@@ -23,6 +23,7 @@ one tagged as v1.0.0. They all have one regular file each.
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_cmp_failed_rev_parse () {
diff --git a/t/t1517-outside-repo.sh b/t/t1517-outside-repo.sh
new file mode 100755
index 0000000000..990a036582
--- /dev/null
+++ b/t/t1517-outside-repo.sh
@@ -0,0 +1,111 @@
+#!/bin/sh
+
+test_description='check random commands outside repo'
+
+TEST_PASSES_SANITIZE_LEAK=true
+. ./test-lib.sh
+
+test_expect_success 'set up a non-repo directory and test file' '
+ GIT_CEILING_DIRECTORIES=$(pwd) &&
+ export GIT_CEILING_DIRECTORIES &&
+ mkdir non-repo &&
+ (
+ cd non-repo &&
+ # confirm that git does not find a repo
+ test_must_fail git rev-parse --git-dir
+ ) &&
+ test_write_lines one two three four >nums &&
+ git add nums &&
+ cp nums nums.old &&
+ test_write_lines five >>nums &&
+ git diff >sample.patch
+'
+
+test_expect_success 'compute a patch-id outside repository (uses SHA-1)' '
+ nongit env GIT_DEFAULT_HASH=sha1 \
+ git patch-id <sample.patch >patch-id.expect &&
+ nongit \
+ git patch-id <sample.patch >patch-id.actual &&
+ test_cmp patch-id.expect patch-id.actual
+'
+
+test_expect_success 'hash-object outside repository (uses SHA-1)' '
+ nongit env GIT_DEFAULT_HASH=sha1 \
+ git hash-object --stdin <sample.patch >hash.expect &&
+ nongit \
+ git hash-object --stdin <sample.patch >hash.actual &&
+ test_cmp hash.expect hash.actual
+'
+
+test_expect_success 'apply a patch outside repository' '
+ (
+ cd non-repo &&
+ cp ../nums.old nums &&
+ git apply ../sample.patch
+ ) &&
+ test_cmp nums non-repo/nums
+'
+
+test_expect_success 'grep outside repository' '
+ git grep --cached two >expect &&
+ (
+ cd non-repo &&
+ cp ../nums.old nums &&
+ git grep --no-index two >../actual
+ ) &&
+ test_cmp expect actual
+'
+
+test_expect_success 'imap-send outside repository' '
+ test_config_global imap.host imaps://localhost &&
+ test_config_global imap.folder Drafts &&
+
+ echo nothing to send >expect &&
+ test_must_fail git imap-send -v </dev/null 2>actual &&
+ test_cmp expect actual &&
+
+ (
+ cd non-repo &&
+ test_must_fail git imap-send -v </dev/null 2>../actual
+ ) &&
+ test_cmp expect actual
+'
+
+test_expect_success 'check-ref-format outside repository' '
+ git check-ref-format --branch refs/heads/xyzzy >expect &&
+ nongit git check-ref-format --branch refs/heads/xyzzy >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'diff outside repository' '
+ echo one >one &&
+ echo two >two &&
+ test_must_fail git diff --no-index one two >expect.raw &&
+ (
+ cd non-repo &&
+ cp ../one . &&
+ cp ../two . &&
+ test_must_fail git diff one two >../actual.raw
+ ) &&
+ # outside repository diff falls back to SHA-1 but
+ # GIT_DEFAULT_HASH may be set to sha256 on the in-repo side.
+ sed -e "/^index /d" expect.raw >expect &&
+ sed -e "/^index /d" actual.raw >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'stripspace outside repository' '
+ nongit git stripspace -s </dev/null
+'
+
+test_expect_success 'remote-http outside repository' '
+ test_must_fail git remote-http 2>actual &&
+ test_grep "^error: remote-curl" actual &&
+ (
+ cd non-repo &&
+ test_must_fail git remote-http 2>../actual
+ ) &&
+ test_grep "^error: remote-curl" actual
+'
+
+test_done
diff --git a/t/t1700-split-index.sh b/t/t1700-split-index.sh
index a7b7263b35..ac4a5b2734 100755
--- a/t/t1700-split-index.sh
+++ b/t/t1700-split-index.sh
@@ -527,7 +527,7 @@ test_expect_success 'reading split index at alternate location' '
# ... and, for backwards compatibility, in the current GIT_DIR
# as well.
- mv -v ./reading-alternate-location/.git/sharedindex.* .git &&
+ mv ./reading-alternate-location/.git/sharedindex.* .git &&
GIT_INDEX_FILE=./reading-alternate-location/.git/index \
git ls-files --cached >actual &&
test_cmp expect actual
diff --git a/t/t2013-checkout-submodule.sh b/t/t2013-checkout-submodule.sh
index b2bdd1fcb4..3c1d663d94 100755
--- a/t/t2013-checkout-submodule.sh
+++ b/t/t2013-checkout-submodule.sh
@@ -2,6 +2,7 @@
test_description='checkout can handle submodules'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-submodule-update.sh
diff --git a/t/t2024-checkout-dwim.sh b/t/t2024-checkout-dwim.sh
index a3b1449ef1..2caada3d83 100755
--- a/t/t2024-checkout-dwim.sh
+++ b/t/t2024-checkout-dwim.sh
@@ -4,6 +4,7 @@ test_description='checkout <branch>
Ensures that checkout on an unborn branch does what the user expects'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# Is the current branch "refs/heads/$1"?
diff --git a/t/t2060-switch.sh b/t/t2060-switch.sh
index c91c4db936..77b2346291 100755
--- a/t/t2060-switch.sh
+++ b/t/t2060-switch.sh
@@ -5,6 +5,7 @@ test_description='switch basic functionality'
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/t2405-worktree-submodule.sh b/t/t2405-worktree-submodule.sh
index 11018f37c7..1d7f605633 100755
--- a/t/t2405-worktree-submodule.sh
+++ b/t/t2405-worktree-submodule.sh
@@ -5,6 +5,7 @@ test_description='Combination of submodules and multiple worktrees'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
base_path=$(pwd -P)
diff --git a/t/t2500-untracked-overwriting.sh b/t/t2500-untracked-overwriting.sh
index 5c0bf4d21f..714feb83be 100755
--- a/t/t2500-untracked-overwriting.sh
+++ b/t/t2500-untracked-overwriting.sh
@@ -2,6 +2,7 @@
test_description='Test handling of overwriting untracked files'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_setup_reset () {
diff --git a/t/t3007-ls-files-recurse-submodules.sh b/t/t3007-ls-files-recurse-submodules.sh
index 61771eec83..f04bdc8c78 100755
--- a/t/t3007-ls-files-recurse-submodules.sh
+++ b/t/t3007-ls-files-recurse-submodules.sh
@@ -6,6 +6,7 @@ This test verifies the recurse-submodules feature correctly lists files from
submodules.
'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup directory structure and submodules' '
diff --git a/t/t3203-branch-output.sh b/t/t3203-branch-output.sh
index 758963b189..e627f08a17 100755
--- a/t/t3203-branch-output.sh
+++ b/t/t3203-branch-output.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='git branch display tests'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-terminal.sh
diff --git a/t/t3206-range-diff.sh b/t/t3206-range-diff.sh
index 7b05bf3961..a767c3520e 100755
--- a/t/t3206-range-diff.sh
+++ b/t/t3206-range-diff.sh
@@ -545,6 +545,20 @@ do
'
done
+test_expect_success "--range-diff implies --cover-letter for multi-patch series" '
+ test_when_finished "rm -f v2-000?-*" &&
+ git format-patch -v2 --range-diff=topic main..unmodified &&
+ test_grep "^Range-diff against v1:$" v2-0000-cover-letter.patch
+'
+
+test_expect_success "explicit --no-cover-letter defeats implied --cover-letter" '
+ test_when_finished "rm -f v2-000?-*" &&
+ test_must_fail git format-patch --no-cover-letter \
+ -v2 --range-diff=topic main..unmodified &&
+ test_must_fail git -c format.coverLetter=no format-patch \
+ -v2 --range-diff=topic main..unmodified
+'
+
test_expect_success 'format-patch --range-diff as commentary' '
git format-patch --range-diff=HEAD~1 HEAD~1 >actual &&
test_when_finished "rm 0001-*" &&
diff --git a/t/t3301-notes.sh b/t/t3301-notes.sh
index cf23c06c09..536bd11ff4 100755
--- a/t/t3301-notes.sh
+++ b/t/t3301-notes.sh
@@ -5,6 +5,7 @@
test_description='Test commit notes'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
write_script fake_editor <<\EOF
diff --git a/t/t3306-notes-prune.sh b/t/t3306-notes-prune.sh
index 8f4102ff9e..b6e9f643e3 100755
--- a/t/t3306-notes-prune.sh
+++ b/t/t3306-notes-prune.sh
@@ -2,6 +2,7 @@
test_description='Test git notes prune'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup: create a few commits with notes' '
diff --git a/t/t3308-notes-merge.sh b/t/t3308-notes-merge.sh
index 202702be1a..e1d05ff6bc 100755
--- a/t/t3308-notes-merge.sh
+++ b/t/t3308-notes-merge.sh
@@ -5,6 +5,7 @@
test_description='Test merging of notes trees'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
diff --git a/t/t3309-notes-merge-auto-resolve.sh b/t/t3309-notes-merge-auto-resolve.sh
index 9bd5dbf341..f55277f499 100755
--- a/t/t3309-notes-merge-auto-resolve.sh
+++ b/t/t3309-notes-merge-auto-resolve.sh
@@ -5,6 +5,7 @@
test_description='Test notes merging with auto-resolving strategies'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# Set up a notes merge scenario with all kinds of potential conflicts
diff --git a/t/t3400-rebase.sh b/t/t3400-rebase.sh
index e1c8c5f701..ae34bfad60 100755
--- a/t/t3400-rebase.sh
+++ b/t/t3400-rebase.sh
@@ -11,6 +11,7 @@ among other things.
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
GIT_AUTHOR_NAME=author@name
diff --git a/t/t3401-rebase-and-am-rename.sh b/t/t3401-rebase-and-am-rename.sh
index f18bae9450..328c1d3a3f 100755
--- a/t/t3401-rebase-and-am-rename.sh
+++ b/t/t3401-rebase-and-am-rename.sh
@@ -2,6 +2,7 @@
test_description='git rebase + directory rename tests'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-rebase.sh
diff --git a/t/t3403-rebase-skip.sh b/t/t3403-rebase-skip.sh
index a1911c4a9d..4f1d6e8ea6 100755
--- a/t/t3403-rebase-skip.sh
+++ b/t/t3403-rebase-skip.sh
@@ -8,6 +8,7 @@ test_description='git rebase --merge --skip tests'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-rebase.sh
diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh
index d1bead61fa..f92baad138 100755
--- a/t/t3404-rebase-interactive.sh
+++ b/t/t3404-rebase-interactive.sh
@@ -2215,6 +2215,51 @@ test_expect_success 'bad labels and refs rejected when parsing todo list' '
test_path_is_missing execed
'
+test_expect_success 'non-merge commands reject merge commits' '
+ test_when_finished "test_might_fail git rebase --abort" &&
+ git checkout E &&
+ git merge I &&
+ oid=$(git rev-parse HEAD) &&
+ cat >todo <<-EOF &&
+ pick $oid
+ reword $oid
+ edit $oid
+ fixup $oid
+ squash $oid
+ EOF
+ (
+ set_replace_editor todo &&
+ test_must_fail git rebase -i HEAD 2>actual
+ ) &&
+ cat >expect <<-EOF &&
+ error: ${SQ}pick${SQ} does not accept merge commits
+ hint: ${SQ}pick${SQ} does not take a merge commit. If you wanted to
+ hint: replay the merge, use ${SQ}merge -C${SQ} on the commit.
+ hint: Disable this message with "git config advice.rebaseTodoError false"
+ error: invalid line 1: pick $oid
+ error: ${SQ}reword${SQ} does not accept merge commits
+ hint: ${SQ}reword${SQ} does not take a merge commit. If you wanted to
+ hint: replay the merge and reword the commit message, use
+ hint: ${SQ}merge -c${SQ} on the commit
+ hint: Disable this message with "git config advice.rebaseTodoError false"
+ error: invalid line 2: reword $oid
+ error: ${SQ}edit${SQ} does not accept merge commits
+ hint: ${SQ}edit${SQ} does not take a merge commit. If you wanted to
+ hint: replay the merge, use ${SQ}merge -C${SQ} on the commit, and then
+ hint: ${SQ}break${SQ} to give the control back to you so that you can
+ hint: do ${SQ}git commit --amend && git rebase --continue${SQ}.
+ hint: Disable this message with "git config advice.rebaseTodoError false"
+ error: invalid line 3: edit $oid
+ error: cannot squash merge commit into another commit
+ error: invalid line 4: fixup $oid
+ error: cannot squash merge commit into another commit
+ error: invalid line 5: squash $oid
+ You can fix this with ${SQ}git rebase --edit-todo${SQ} and then run ${SQ}git rebase --continue${SQ}.
+ Or you can abort the rebase with ${SQ}git rebase --abort${SQ}.
+ EOF
+ test_cmp expect actual
+'
+
# This must be the last test in this file
test_expect_success '$EDITOR and friends are unchanged' '
test_editor_unchanged
diff --git a/t/t3406-rebase-message.sh b/t/t3406-rebase-message.sh
index a1d7fa7f7c..82108b67e6 100755
--- a/t/t3406-rebase-message.sh
+++ b/t/t3406-rebase-message.sh
@@ -5,6 +5,7 @@ test_description='messages from rebase operation'
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/t3407-rebase-abort.sh b/t/t3407-rebase-abort.sh
index 9f49c4228b..2c3f38d45a 100755
--- a/t/t3407-rebase-abort.sh
+++ b/t/t3407-rebase-abort.sh
@@ -5,6 +5,7 @@ test_description='git rebase --abort tests'
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/t3415-rebase-autosquash.sh b/t/t3415-rebase-autosquash.sh
index fcc40d6fe1..22452ff84c 100755
--- a/t/t3415-rebase-autosquash.sh
+++ b/t/t3415-rebase-autosquash.sh
@@ -5,6 +5,7 @@ test_description='auto squash'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-rebase.sh
diff --git a/t/t3417-rebase-whitespace-fix.sh b/t/t3417-rebase-whitespace-fix.sh
index 96f2cf22fa..22ee3a2045 100755
--- a/t/t3417-rebase-whitespace-fix.sh
+++ b/t/t3417-rebase-whitespace-fix.sh
@@ -5,6 +5,7 @@ test_description='git rebase --whitespace=fix
This test runs git rebase --whitespace=fix and make sure that it works.
'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# prepare initial revision of "file" with a blank line at the end
diff --git a/t/t3418-rebase-continue.sh b/t/t3418-rebase-continue.sh
index 127216f722..c0d29c2154 100755
--- a/t/t3418-rebase-continue.sh
+++ b/t/t3418-rebase-continue.sh
@@ -5,6 +5,7 @@ test_description='git rebase --continue tests'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-rebase.sh
diff --git a/t/t3420-rebase-autostash.sh b/t/t3420-rebase-autostash.sh
index 1a820f1481..63e400b89f 100755
--- a/t/t3420-rebase-autostash.sh
+++ b/t/t3420-rebase-autostash.sh
@@ -7,6 +7,7 @@ test_description='git rebase --autostash tests'
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/t3421-rebase-topology-linear.sh b/t/t3421-rebase-topology-linear.sh
index 62d86d557d..737af80bb3 100755
--- a/t/t3421-rebase-topology-linear.sh
+++ b/t/t3421-rebase-topology-linear.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='basic rebase topology tests'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-rebase.sh
diff --git a/t/t3424-rebase-empty.sh b/t/t3424-rebase-empty.sh
index 1ee6b00fd5..515c949ae3 100755
--- a/t/t3424-rebase-empty.sh
+++ b/t/t3424-rebase-empty.sh
@@ -2,6 +2,7 @@
test_description='git rebase of commits that start or become empty'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup test repository' '
diff --git a/t/t3426-rebase-submodule.sh b/t/t3426-rebase-submodule.sh
index ba069dccbd..94ea88e384 100755
--- a/t/t3426-rebase-submodule.sh
+++ b/t/t3426-rebase-submodule.sh
@@ -2,6 +2,7 @@
test_description='rebase can handle submodules'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-submodule-update.sh
. "$TEST_DIRECTORY"/lib-rebase.sh
diff --git a/t/t3428-rebase-signoff.sh b/t/t3428-rebase-signoff.sh
index 1bebd1ce74..365436ebfc 100755
--- a/t/t3428-rebase-signoff.sh
+++ b/t/t3428-rebase-signoff.sh
@@ -7,10 +7,16 @@ This test runs git rebase --signoff and make sure that it works.
TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-rebase.sh
test_expect_success 'setup' '
git commit --allow-empty -m "Initial empty commit" &&
test_commit first file a &&
+ test_commit second file &&
+ git checkout -b conflict-branch first &&
+ test_commit file-2 file-2 &&
+ test_commit conflict file &&
+ test_commit third file &&
ident="$GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>" &&
@@ -28,6 +34,22 @@ test_expect_success 'setup' '
Signed-off-by: $ident
EOF
+ # Expected commit message after conflict resolution for rebase --signoff
+ cat >expected-signed-conflict <<-EOF &&
+ third
+
+ Signed-off-by: $ident
+
+ conflict
+
+ Signed-off-by: $ident
+
+ file-2
+
+ Signed-off-by: $ident
+
+ EOF
+
# Expected commit message after rebase without --signoff (or with --no-signoff)
cat >expected-unsigned <<-EOF &&
first
@@ -39,8 +61,12 @@ test_expect_success 'setup' '
# We configure an alias to do the rebase --signoff so that
# on the next subtest we can show that --no-signoff overrides the alias
test_expect_success 'rebase --apply --signoff adds a sign-off line' '
- git rbs --apply HEAD^ &&
- test_commit_message HEAD expected-signed
+ test_must_fail git rbs --apply second third &&
+ git checkout --theirs file &&
+ git add file &&
+ git rebase --continue &&
+ git log --format=%B -n3 >actual &&
+ test_cmp expected-signed-conflict actual
'
test_expect_success 'rebase --no-signoff does not add a sign-off line' '
@@ -51,28 +77,65 @@ test_expect_success 'rebase --no-signoff does not add a sign-off line' '
test_expect_success 'rebase --exec --signoff adds a sign-off line' '
test_when_finished "rm exec" &&
- git commit --amend -m "first" &&
- git rebase --exec "touch exec" --signoff HEAD^ &&
+ git rebase --exec "touch exec" --signoff first^ first &&
test_path_is_file exec &&
test_commit_message HEAD expected-signed
'
test_expect_success 'rebase --root --signoff adds a sign-off line' '
- git commit --amend -m "first" &&
+ git checkout first &&
git rebase --root --keep-empty --signoff &&
test_commit_message HEAD^ expected-initial-signed &&
test_commit_message HEAD expected-signed
'
-test_expect_success 'rebase -i --signoff fails' '
- git commit --amend -m "first" &&
- git rebase -i --signoff HEAD^ &&
- test_commit_message HEAD expected-signed
+test_expect_success 'rebase -m --signoff adds a sign-off line' '
+ test_must_fail git rebase -m --signoff second third &&
+ git checkout --theirs file &&
+ git add file &&
+ GIT_EDITOR="sed -n /Conflicts:/,/^\\\$/p >actual" \
+ git rebase --continue &&
+ cat >expect <<-\EOF &&
+ # Conflicts:
+ # file
+
+ EOF
+ test_cmp expect actual &&
+ git log --format=%B -n3 >actual &&
+ test_cmp expected-signed-conflict actual
'
-test_expect_success 'rebase -m --signoff fails' '
- git commit --amend -m "first" &&
- git rebase -m --signoff HEAD^ &&
- test_commit_message HEAD expected-signed
+test_expect_success 'rebase -i --signoff adds a sign-off line when editing commit' '
+ (
+ set_fake_editor &&
+ FAKE_LINES="edit 1 edit 3 edit 2" \
+ git rebase -i --signoff first third
+ ) &&
+ echo a >a &&
+ git add a &&
+ test_must_fail git rebase --continue &&
+ git checkout --ours file &&
+ echo b >a &&
+ git add a file &&
+ git rebase --continue &&
+ echo c >a &&
+ git add a &&
+ git log --format=%B -n3 >actual &&
+ cat >expect <<-EOF &&
+ conflict
+
+ Signed-off-by: $ident
+
+ third
+
+ Signed-off-by: $ident
+
+ file-2
+
+ Signed-off-by: $ident
+
+ EOF
+ test_cmp expect actual
'
+
test_done
diff --git a/t/t3430-rebase-merges.sh b/t/t3430-rebase-merges.sh
index 59b5d6b6f2..36ca126bcd 100755
--- a/t/t3430-rebase-merges.sh
+++ b/t/t3430-rebase-merges.sh
@@ -21,6 +21,7 @@ Initial setup:
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-rebase.sh
. "$TEST_DIRECTORY"/lib-log-graph.sh
diff --git a/t/t3434-rebase-i18n.sh b/t/t3434-rebase-i18n.sh
index e6fef696bb..26a48d6b10 100755
--- a/t/t3434-rebase-i18n.sh
+++ b/t/t3434-rebase-i18n.sh
@@ -17,6 +17,7 @@ Initial setup:
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
compare_msg () {
@@ -71,7 +72,7 @@ test_rebase_continue_update_encode () {
git config i18n.commitencoding $new &&
test_must_fail git rebase -m main &&
test -f .git/rebase-merge/message &&
- git stripspace <.git/rebase-merge/message >two.t &&
+ git stripspace -s <.git/rebase-merge/message >two.t &&
git add two.t &&
git rebase --continue &&
compare_msg $msgfile $old $new &&
diff --git a/t/t3500-cherry.sh b/t/t3500-cherry.sh
index 78c3eac54b..61ca87512d 100755
--- a/t/t3500-cherry.sh
+++ b/t/t3500-cherry.sh
@@ -11,6 +11,7 @@ checks that git cherry only returns the second patch in the local branch
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
GIT_AUTHOR_EMAIL=bogus_email_address
diff --git a/t/t3504-cherry-pick-rerere.sh b/t/t3504-cherry-pick-rerere.sh
index 4581ae98b8..597c98e9c5 100755
--- a/t/t3504-cherry-pick-rerere.sh
+++ b/t/t3504-cherry-pick-rerere.sh
@@ -5,6 +5,7 @@ test_description='cherry-pick should rerere for conflicts'
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/t3505-cherry-pick-empty.sh b/t/t3505-cherry-pick-empty.sh
index 9748443530..ead3fb4680 100755
--- a/t/t3505-cherry-pick-empty.sh
+++ b/t/t3505-cherry-pick-empty.sh
@@ -5,6 +5,7 @@ test_description='test cherry-picking an empty commit'
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/t3508-cherry-pick-many-commits.sh b/t/t3508-cherry-pick-many-commits.sh
index 2d53ce754c..afa7727a4a 100755
--- a/t/t3508-cherry-pick-many-commits.sh
+++ b/t/t3508-cherry-pick-many-commits.sh
@@ -5,6 +5,7 @@ test_description='test cherry-picking many commits'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
check_head_differs_from() {
diff --git a/t/t3509-cherry-pick-merge-df.sh b/t/t3509-cherry-pick-merge-df.sh
index f4159246e1..171cc6d76b 100755
--- a/t/t3509-cherry-pick-merge-df.sh
+++ b/t/t3509-cherry-pick-merge-df.sh
@@ -4,6 +4,7 @@ test_description='Test cherry-pick with directory/file conflicts'
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 'Initialize repository' '
diff --git a/t/t3512-cherry-pick-submodule.sh b/t/t3512-cherry-pick-submodule.sh
index f22d1ddead..9387a22a9e 100755
--- a/t/t3512-cherry-pick-submodule.sh
+++ b/t/t3512-cherry-pick-submodule.sh
@@ -5,6 +5,7 @@ test_description='cherry-pick can handle submodules'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-submodule-update.sh
diff --git a/t/t3513-revert-submodule.sh b/t/t3513-revert-submodule.sh
index 8bfe3ed246..e178968b40 100755
--- a/t/t3513-revert-submodule.sh
+++ b/t/t3513-revert-submodule.sh
@@ -2,6 +2,7 @@
test_description='revert can handle submodules'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-submodule-update.sh
diff --git a/t/t3600-rm.sh b/t/t3600-rm.sh
index 98259e2ada..31ac31d4bc 100755
--- a/t/t3600-rm.sh
+++ b/t/t3600-rm.sh
@@ -8,6 +8,7 @@ test_description='Test of the various options to git rm.'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# Setup some files to be removed, some with funny characters
diff --git a/t/t3602-rm-sparse-checkout.sh b/t/t3602-rm-sparse-checkout.sh
index 08580fd3dc..fcdefba48c 100755
--- a/t/t3602-rm-sparse-checkout.sh
+++ b/t/t3602-rm-sparse-checkout.sh
@@ -2,6 +2,7 @@
test_description='git rm in sparse checked out working trees'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' "
diff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh
index 04d8333373..5d78868ac1 100755
--- a/t/t3701-add-interactive.sh
+++ b/t/t3701-add-interactive.sh
@@ -8,6 +8,8 @@ TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-terminal.sh
+SP=" "
+
diff_cmp () {
for x
do
@@ -41,19 +43,17 @@ force_color () {
)
}
-test_expect_success 'warn about add.interactive.useBuiltin' '
- cat >expect <<-\EOF &&
- warning: the add.interactive.useBuiltin setting has been removed!
- See its entry in '\''git help config'\'' for details.
- No changes.
+test_expect_success 'unknown command' '
+ test_when_finished "git reset --hard; rm -f command" &&
+ echo W >command &&
+ git add -N command &&
+ git diff command >expect &&
+ cat >>expect <<-EOF &&
+ (1/1) Stage addition [y,n,q,a,d,e,p,?]? Unknown command ${SQ}W${SQ} (use ${SQ}?${SQ} for help)
+ (1/1) Stage addition [y,n,q,a,d,e,p,?]?$SP
EOF
-
- for v in = =true =false
- do
- git -c "add.interactive.useBuiltin$v" add -p >out 2>actual &&
- test_must_be_empty out &&
- test_cmp expect actual || return 1
- done
+ git add -p -- command <command >actual 2>&1 &&
+ test_cmp expect actual
'
test_expect_success 'setup (initial)' '
@@ -145,6 +145,14 @@ test_expect_success 'revert works (commit)' '
grep "unchanged *+3/-0 file" output
'
+test_expect_success 'reject multi-key input' '
+ saved=$(git hash-object -w file) &&
+ test_when_finished "git cat-file blob $saved >file" &&
+ echo an extra line >>file &&
+ test_write_lines aa | git add -p >actual &&
+ test_grep "is expected, got ${SQ}aa${SQ}" actual
+'
+
test_expect_success 'setup expected' '
cat >expected <<-\EOF
EOF
@@ -232,7 +240,6 @@ test_expect_success 'setup file' '
'
test_expect_success 'setup patch' '
- SP=" " &&
NULL="" &&
cat >patch <<-EOF
@@ -1,4 +1,4 @@
@@ -335,13 +342,13 @@ test_expect_success 'different prompts for mode change/deleted' '
test_expect_success 'correct message when there is nothing to do' '
git reset --hard &&
- git add -p 2>err &&
- test_grep "No changes" err &&
+ git add -p >out &&
+ test_grep "No changes" out &&
printf "\\0123" >binary &&
git add binary &&
printf "\\0abc" >binary &&
- git add -p 2>err &&
- test_grep "Only binary files changed" err
+ git add -p >out &&
+ test_grep "Only binary files changed" out
'
test_expect_success 'setup again' '
@@ -512,7 +519,7 @@ test_expect_success 'split hunk setup' '
test_write_lines 10 15 20 21 22 23 24 30 40 50 60 >test
'
-test_expect_success 'goto hunk' '
+test_expect_success 'goto hunk 1 with "g 1"' '
test_when_finished "git reset" &&
tr _ " " >expect <<-EOF &&
(2/2) Stage this hunk [y,n,q,a,d,K,g,/,e,p,?]? + 1: -1,2 +1,3 +15
@@ -528,7 +535,20 @@ test_expect_success 'goto hunk' '
test_cmp expect actual.trimmed
'
-test_expect_success 'navigate to hunk via regex' '
+test_expect_success 'goto hunk 1 with "g1"' '
+ test_when_finished "git reset" &&
+ tr _ " " >expect <<-EOF &&
+ _10
+ +15
+ _20
+ (1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]?_
+ EOF
+ test_write_lines s y g1 | git add -p >actual &&
+ tail -n 4 <actual >actual.trimmed &&
+ test_cmp expect actual.trimmed
+'
+
+test_expect_success 'navigate to hunk via regex /pattern' '
test_when_finished "git reset" &&
tr _ " " >expect <<-EOF &&
(2/2) Stage this hunk [y,n,q,a,d,K,g,/,e,p,?]? @@ -1,2 +1,3 @@
@@ -542,6 +562,19 @@ test_expect_success 'navigate to hunk via regex' '
test_cmp expect actual.trimmed
'
+test_expect_success 'navigate to hunk via regex / pattern' '
+ test_when_finished "git reset" &&
+ tr _ " " >expect <<-EOF &&
+ _10
+ +15
+ _20
+ (1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]?_
+ EOF
+ test_write_lines s y / 1,2 | git add -p >actual &&
+ tail -n 4 <actual >actual.trimmed &&
+ test_cmp expect actual.trimmed
+'
+
test_expect_success 'split hunk "add -p (edit)"' '
# Split, say Edit and do nothing. Then:
#
diff --git a/t/t3900-i18n-commit.sh b/t/t3900-i18n-commit.sh
index f27d09cfd9..db7b403bc1 100755
--- a/t/t3900-i18n-commit.sh
+++ b/t/t3900-i18n-commit.sh
@@ -5,6 +5,7 @@
test_description='commit and log output encodings'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
compare_with () {
diff --git a/t/t3901-i18n-patch.sh b/t/t3901-i18n-patch.sh
index 4b37f78829..5f0b9afc3f 100755
--- a/t/t3901-i18n-patch.sh
+++ b/t/t3901-i18n-patch.sh
@@ -8,6 +8,7 @@ test_description='i18n settings and format-patch | am pipe'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
check_encoding () {
diff --git a/t/t3903-stash.sh b/t/t3903-stash.sh
index 00db82fb24..a7f71f8126 100755
--- a/t/t3903-stash.sh
+++ b/t/t3903-stash.sh
@@ -393,6 +393,15 @@ test_expect_success 'stash --staged' '
test bar,bar4 = $(cat file),$(cat file2)
'
+test_expect_success 'stash --staged with binary file' '
+ printf "\0" >file &&
+ git add file &&
+ git stash --staged &&
+ git stash pop &&
+ printf "\0" >expect &&
+ test_cmp expect file
+'
+
test_expect_success 'dont assume push with non-option args' '
test_must_fail git stash -q drop 2>err &&
test_grep -e "subcommand wasn'\''t specified; '\''push'\'' can'\''t be assumed due to unexpected token '\''drop'\''" err
diff --git a/t/t3906-stash-submodule.sh b/t/t3906-stash-submodule.sh
index 0f7348ec21..0f61f01ef4 100755
--- a/t/t3906-stash-submodule.sh
+++ b/t/t3906-stash-submodule.sh
@@ -2,6 +2,7 @@
test_description='stash can handle submodules'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-submodule-update.sh
diff --git a/t/t3907-stash-show-config.sh b/t/t3907-stash-show-config.sh
index 10914bba7b..7a2eb98b86 100755
--- a/t/t3907-stash-show-config.sh
+++ b/t/t3907-stash-show-config.sh
@@ -2,6 +2,7 @@
test_description='Test git stash show configuration.'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
diff --git a/t/t3910-mac-os-precompose.sh b/t/t3910-mac-os-precompose.sh
index 898267a6bd..6d5918c8fe 100755
--- a/t/t3910-mac-os-precompose.sh
+++ b/t/t3910-mac-os-precompose.sh
@@ -37,6 +37,27 @@ Alongc=$Alongc$Alongc$Alongc$Alongc$Alongc #50 Byte
Alongc=$Alongc$Alongc$Alongc$Alongc$Alongc #250 Byte
Alongc=$Alongc$AEligatu$AEligatu #254 Byte
+
+ls_files_nfc_nfd () {
+ test_when_finished "git config --global --unset core.precomposeunicode" &&
+ prglbl=$1
+ prlocl=$2
+ aumlcreat=$3
+ aumllist=$4
+ git config --global core.precomposeunicode $prglbl &&
+ (
+ rm -rf .git &&
+ mkdir -p "somewhere/$prglbl/$prlocl/$aumlcreat" &&
+ mypwd=$PWD &&
+ cd "somewhere/$prglbl/$prlocl/$aumlcreat" &&
+ git init &&
+ git config core.precomposeunicode $prlocl &&
+ git --literal-pathspecs ls-files "$mypwd/somewhere/$prglbl/$prlocl/$aumllist" 2>err &&
+ >expected &&
+ test_cmp expected err
+ )
+}
+
test_expect_success "detect if nfd needed" '
precomposeunicode=$(git config core.precomposeunicode) &&
test "$precomposeunicode" = true &&
@@ -211,8 +232,8 @@ test_expect_success "unicode decomposed: git restore -p . " '
'
# Test if the global core.precomposeunicode stops autosensing
-# Must be the last test case
test_expect_success "respect git config --global core.precomposeunicode" '
+ test_when_finished "git config --global --unset core.precomposeunicode" &&
git config --global core.precomposeunicode true &&
rm -rf .git &&
git init &&
@@ -220,4 +241,20 @@ test_expect_success "respect git config --global core.precomposeunicode" '
test "$precomposeunicode" = "true"
'
+test_expect_success "ls-files false false nfd nfd" '
+ ls_files_nfc_nfd false false $Adiarnfd $Adiarnfd
+'
+
+test_expect_success "ls-files false true nfd nfd" '
+ ls_files_nfc_nfd false true $Adiarnfd $Adiarnfd
+'
+
+test_expect_success "ls-files true false nfd nfd" '
+ ls_files_nfc_nfd true false $Adiarnfd $Adiarnfd
+'
+
+test_expect_success "ls-files true true nfd nfd" '
+ ls_files_nfc_nfd true true $Adiarnfd $Adiarnfd
+'
+
test_done
diff --git a/t/t4001-diff-rename.sh b/t/t4001-diff-rename.sh
index 49c042a38a..cd1931dd55 100755
--- a/t/t4001-diff-rename.sh
+++ b/t/t4001-diff-rename.sh
@@ -3,9 +3,9 @@
# Copyright (c) 2005 Junio C Hamano
#
-test_description='Test rename detection in diff engine.
+test_description='Test rename detection in diff engine.'
-'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-diff.sh
diff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh
index e37a1411ee..884f83fb8a 100755
--- a/t/t4014-format-patch.sh
+++ b/t/t4014-format-patch.sh
@@ -820,8 +820,8 @@ test_expect_success 'format-patch --notes --signoff' '
'
test_expect_success 'format-patch notes output control' '
+ test_when_finished "git notes remove HEAD || :" &&
git notes add -m "notes config message" HEAD &&
- test_when_finished git notes remove HEAD &&
git format-patch -1 --stdout >out &&
! grep "notes config message" out &&
@@ -848,10 +848,10 @@ test_expect_success 'format-patch notes output control' '
'
test_expect_success 'format-patch with multiple notes refs' '
+ test_when_finished "git notes --ref note1 remove HEAD;
+ git notes --ref note2 remove HEAD || :" &&
git notes --ref note1 add -m "this is note 1" HEAD &&
- test_when_finished git notes --ref note1 remove HEAD &&
git notes --ref note2 add -m "this is note 2" HEAD &&
- test_when_finished git notes --ref note2 remove HEAD &&
git format-patch -1 --stdout >out &&
! grep "this is note 1" out &&
@@ -892,10 +892,10 @@ test_expect_success 'format-patch with multiple notes refs' '
test_expect_success 'format-patch with multiple notes refs in config' '
test_when_finished "test_unconfig format.notes" &&
+ test_when_finished "git notes --ref note1 remove HEAD;
+ git notes --ref note2 remove HEAD || :" &&
git notes --ref note1 add -m "this is note 1" HEAD &&
- test_when_finished git notes --ref note1 remove HEAD &&
git notes --ref note2 add -m "this is note 2" HEAD &&
- test_when_finished git notes --ref note2 remove HEAD &&
git config format.notes note1 &&
git format-patch -1 --stdout >out &&
@@ -1368,12 +1368,38 @@ test_expect_success 'empty subject prefix does not have extra space' '
test_cmp expect actual
'
-test_expect_success '--rfc' '
+test_expect_success '--rfc and --no-rfc' '
cat >expect <<-\EOF &&
Subject: [RFC PATCH 1/1] header with . in it
EOF
git format-patch -n -1 --stdout --rfc >patch &&
grep "^Subject:" patch >actual &&
+ test_cmp expect actual &&
+ git format-patch -n -1 --stdout --rfc --no-rfc >patch &&
+ sed -e "s/RFC //" expect >expect-raw &&
+ grep "^Subject:" patch >actual &&
+ test_cmp expect-raw actual
+'
+
+test_expect_success '--rfc=WIP and --rfc=' '
+ cat >expect <<-\EOF &&
+ Subject: [WIP PATCH 1/1] header with . in it
+ EOF
+ git format-patch -n -1 --stdout --rfc=WIP >patch &&
+ grep "^Subject:" patch >actual &&
+ test_cmp expect actual &&
+ git format-patch -n -1 --stdout --rfc --rfc= >patch &&
+ sed -e "s/WIP //" expect >expect-raw &&
+ grep "^Subject:" patch >actual &&
+ test_cmp expect-raw actual
+'
+
+test_expect_success '--rfc=-(WIP) appends' '
+ cat >expect <<-\EOF &&
+ Subject: [PATCH (WIP) 1/1] header with . in it
+ EOF
+ git format-patch -n -1 --stdout --rfc="-(WIP)" >patch &&
+ grep "^Subject:" patch >actual &&
test_cmp expect actual
'
@@ -1397,6 +1423,27 @@ test_expect_success '--rfc is argument order independent' '
test_cmp expect actual
'
+test_expect_success '--subject-prefix="<non-empty>" and -k cannot be used together' '
+ echo "fatal: options '\''--subject-prefix/--rfc'\'' and '\''-k'\'' cannot be used together" >expect.err &&
+ test_must_fail git format-patch -1 --stdout --subject-prefix="MYPREFIX" -k >actual.out 2>actual.err &&
+ test_must_be_empty actual.out &&
+ test_cmp expect.err actual.err
+'
+
+test_expect_success '--subject-prefix="" and -k cannot be used together' '
+ echo "fatal: options '\''--subject-prefix/--rfc'\'' and '\''-k'\'' cannot be used together" >expect.err &&
+ test_must_fail git format-patch -1 --stdout --subject-prefix="" -k >actual.out 2>actual.err &&
+ test_must_be_empty actual.out &&
+ test_cmp expect.err actual.err
+'
+
+test_expect_success '--rfc and -k cannot be used together' '
+ echo "fatal: options '\''--subject-prefix/--rfc'\'' and '\''-k'\'' cannot be used together" >expect.err &&
+ test_must_fail git format-patch -1 --stdout --rfc -k >actual.out 2>actual.err &&
+ test_must_be_empty actual.out &&
+ test_cmp expect.err actual.err
+'
+
test_expect_success '--from=ident notices bogus ident' '
test_must_fail git format-patch -1 --stdout --from=foo >patch
'
@@ -2435,16 +2482,55 @@ test_expect_success 'interdiff: reroll-count with a integer' '
'
test_expect_success 'interdiff: solo-patch' '
- cat >expect <<-\EOF &&
- +fleep
-
- EOF
git format-patch --interdiff=boop~2 -1 boop &&
- test_grep "^Interdiff:$" 0001-fleep.patch &&
- sed "1,/^ @@ /d; /^$/q" 0001-fleep.patch >actual &&
+
+ # remove up to the last "patch" output line,
+ # and remove everything below the signature mark.
+ sed -e "1,/^+fleep\$/d" -e "/^-- /,\$d" 0001-fleep.patch >actual &&
+
+ # fabricate Interdiff output.
+ git diff boop~2 boop >inter &&
+ {
+ echo &&
+ echo "Interdiff:" &&
+ sed -e "s/^/ /" inter
+ } >expect &&
test_cmp expect actual
'
+test_expect_success 'range-diff: solo-patch' '
+ git format-patch --creation-factor=999 \
+ --range-diff=boop~2..boop~1 -1 boop &&
+
+ # remove up to the last "patch" output line,
+ # and remove everything below the signature mark.
+ sed -e "1,/^+fleep\$/d" -e "/^-- /,\$d" 0001-fleep.patch >actual &&
+
+ # fabricate range-diff output.
+ {
+ echo &&
+ echo "Range-diff:" &&
+ git range-diff --creation-factor=999 \
+ boop~2..boop~1 boop~1..boop
+ } >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'interdiff: multi-patch, implicit --cover-letter' '
+ test_when_finished "rm -f v23-0*.patch" &&
+ git format-patch --interdiff=boop~2 -2 -v23 &&
+ test_grep "^Interdiff against v22:$" v23-0000-cover-letter.patch &&
+ test_cmp expect actual
+'
+
+test_expect_success 'interdiff: explicit --no-cover-letter defeats implied --cover-letter' '
+ test_when_finished "rm -f v23-0*.patch" &&
+ test_must_fail git format-patch --no-cover-letter \
+ --interdiff=boop~2 -2 -v23 &&
+ test_must_fail git -c format.coverLetter=no format-patch \
+ --interdiff=boop~2 -2 -v23
+'
+
test_expect_success 'format-patch does not respect diff.noprefix' '
git -c diff.noprefix format-patch -1 --stdout >actual &&
grep "^--- a/blorp" actual
diff --git a/t/t4015-diff-whitespace.sh b/t/t4015-diff-whitespace.sh
index b443626afd..851cfe4f32 100755
--- a/t/t4015-diff-whitespace.sh
+++ b/t/t4015-diff-whitespace.sh
@@ -1184,6 +1184,15 @@ test_expect_success 'detect moved code, complete file' '
test_cmp expected actual
'
+test_expect_success '--color-moved with --no-ext-diff' '
+ test_config color.diff.oldMoved "yellow" &&
+ test_config color.diff.newMoved "blue" &&
+ args="--color --color-moved=zebra --no-renames HEAD" &&
+ git diff $args >expect &&
+ git -c diff.external=echo diff --no-ext-diff $args >actual &&
+ test_cmp expect actual
+'
+
test_expect_success 'detect malicious moved code, inside file' '
test_config color.diff.oldMoved "normal red" &&
test_config color.diff.newMoved "normal green" &&
diff --git a/t/t4020-diff-external.sh b/t/t4020-diff-external.sh
index fdd865f7c3..3baa52a9bf 100755
--- a/t/t4020-diff-external.sh
+++ b/t/t4020-diff-external.sh
@@ -172,6 +172,72 @@ test_expect_success 'no diff with -diff' '
grep Binary out
'
+check_external_diff () {
+ expect_code=$1
+ expect_out=$2
+ expect_err=$3
+ command_code=$4
+ trust_exit_code=$5
+ shift 5
+ options="$@"
+
+ command="echo output; exit $command_code;"
+ desc="external diff '$command' with trustExitCode=$trust_exit_code"
+ with_options="${options:+ with }$options"
+
+ test_expect_success "$desc via attribute$with_options" "
+ test_config diff.foo.command \"$command\" &&
+ test_config diff.foo.trustExitCode $trust_exit_code &&
+ echo \"file diff=foo\" >.gitattributes &&
+ test_expect_code $expect_code git diff $options >out 2>err &&
+ test_cmp $expect_out out &&
+ test_cmp $expect_err err
+ "
+
+ test_expect_success "$desc via diff.external$with_options" "
+ test_config diff.external \"$command\" &&
+ test_config diff.trustExitCode $trust_exit_code &&
+ >.gitattributes &&
+ test_expect_code $expect_code git diff $options >out 2>err &&
+ test_cmp $expect_out out &&
+ test_cmp $expect_err err
+ "
+
+ test_expect_success "$desc via GIT_EXTERNAL_DIFF$with_options" "
+ >.gitattributes &&
+ test_expect_code $expect_code env \
+ GIT_EXTERNAL_DIFF=\"$command\" \
+ GIT_EXTERNAL_DIFF_TRUST_EXIT_CODE=$trust_exit_code \
+ git diff $options >out 2>err &&
+ test_cmp $expect_out out &&
+ test_cmp $expect_err err
+ "
+}
+
+test_expect_success 'setup output files' '
+ : >empty &&
+ echo output >output &&
+ echo "fatal: external diff died, stopping at file" >error
+'
+
+check_external_diff 0 output empty 0 off
+check_external_diff 128 output error 1 off
+check_external_diff 0 output empty 0 on
+check_external_diff 0 output empty 1 on
+check_external_diff 128 output error 2 on
+
+check_external_diff 1 output empty 0 off --exit-code
+check_external_diff 128 output error 1 off --exit-code
+check_external_diff 0 output empty 0 on --exit-code
+check_external_diff 1 output empty 1 on --exit-code
+check_external_diff 128 output error 2 on --exit-code
+
+check_external_diff 1 empty empty 0 off --quiet
+check_external_diff 1 empty empty 1 off --quiet # we don't even call the program
+check_external_diff 0 empty empty 0 on --quiet
+check_external_diff 1 empty empty 1 on --quiet
+check_external_diff 128 empty error 2 on --quiet
+
echo NULZbetweenZwords | perl -pe 'y/Z/\000/' > file
test_expect_success 'force diff with "diff"' '
diff --git a/t/t4026-color.sh b/t/t4026-color.sh
index cc3f60d468..b05f2a9b60 100755
--- a/t/t4026-color.sh
+++ b/t/t4026-color.sh
@@ -96,8 +96,8 @@ test_expect_success '256 colors' '
color "254 bold 255" "[1;38;5;254;48;5;255m"
'
-test_expect_success '24-bit colors' '
- color "#ff00ff black" "[38;2;255;0;255;40m"
+test_expect_success 'RGB colors' '
+ color "#ff00ff #0f0" "[38;2;255;0;255;48;2;0;255;0m"
'
test_expect_success '"default" foreground' '
@@ -112,7 +112,7 @@ test_expect_success '"default" can be combined with attributes' '
color "default default no-reverse bold" "[1;27;39;49m"
'
-test_expect_success '"normal" yields no color at all"' '
+test_expect_success '"normal" yields no color at all' '
color "normal black" "[40m"
'
@@ -140,6 +140,26 @@ test_expect_success 'extra character after attribute' '
invalid_color "dimX"
'
+test_expect_success 'non-hex character in RGB color' '
+ invalid_color "#x23456" &&
+ invalid_color "#1x3456" &&
+ invalid_color "#12x456" &&
+ invalid_color "#123x56" &&
+ invalid_color "#1234x6" &&
+ invalid_color "#12345x" &&
+ invalid_color "#x23" &&
+ invalid_color "#1x3" &&
+ invalid_color "#12x"
+'
+
+test_expect_success 'wrong number of letters in RGB color' '
+ invalid_color "#1" &&
+ invalid_color "#23" &&
+ invalid_color "#789a" &&
+ invalid_color "#bcdef" &&
+ invalid_color "#1234567"
+'
+
test_expect_success 'unknown color slots are ignored (diff)' '
git config color.diff.nosuchslotwilleverbedefined white &&
git diff --color
diff --git a/t/t4041-diff-submodule-option.sh b/t/t4041-diff-submodule-option.sh
index 0c1502d4b0..8fc40e75eb 100755
--- a/t/t4041-diff-submodule-option.sh
+++ b/t/t4041-diff-submodule-option.sh
@@ -12,6 +12,7 @@ This test tries to verify the sanity of the --submodule option of git diff.
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# Tested non-UTF-8 encoding
diff --git a/t/t4043-diff-rename-binary.sh b/t/t4043-diff-rename-binary.sh
index 2a2cf91352..e486493908 100755
--- a/t/t4043-diff-rename-binary.sh
+++ b/t/t4043-diff-rename-binary.sh
@@ -5,6 +5,7 @@
test_description='Move a binary file'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
diff --git a/t/t4046-diff-unmerged.sh b/t/t4046-diff-unmerged.sh
index ffaf69335f..afda629c98 100755
--- a/t/t4046-diff-unmerged.sh
+++ b/t/t4046-diff-unmerged.sh
@@ -20,13 +20,15 @@ test_expect_success setup '
for t in o x
do
path="$b$o$t" &&
- case "$path" in ooo) continue ;; esac &&
- paths="$paths$path " &&
- p=" $path" &&
- case "$b" in x) echo "$m1$p" ;; esac &&
- case "$o" in x) echo "$m2$p" ;; esac &&
- case "$t" in x) echo "$m3$p" ;; esac ||
- return 1
+ if test "$path" != ooo
+ then
+ paths="$paths$path " &&
+ p=" $path" &&
+ case "$b" in x) echo "$m1$p" ;; esac &&
+ case "$o" in x) echo "$m2$p" ;; esac &&
+ case "$t" in x) echo "$m3$p" ;; esac ||
+ return 1
+ fi
done
done
done >ls-files-s.expect &&
@@ -96,4 +98,12 @@ test_expect_success 'diff --stat' '
test_cmp diff-stat.expect diff-stat.actual
'
+test_expect_success 'diff --quiet' '
+ test_expect_code 1 git diff --cached --quiet
+'
+
+test_expect_success 'diff --quiet --ignore-all-space' '
+ test_expect_code 1 git diff --cached --quiet --ignore-all-space
+'
+
test_done
diff --git a/t/t4059-diff-submodule-not-initialized.sh b/t/t4059-diff-submodule-not-initialized.sh
index d489230df8..668f526303 100755
--- a/t/t4059-diff-submodule-not-initialized.sh
+++ b/t/t4059-diff-submodule-not-initialized.sh
@@ -9,6 +9,7 @@ This test tries to verify that add_submodule_odb works when the submodule was
initialized previously but the checkout has since been removed.
'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# Tested non-UTF-8 encoding
diff --git a/t/t4060-diff-submodule-option-diff-format.sh b/t/t4060-diff-submodule-option-diff-format.sh
index 97c6424cd5..8ce67442d9 100755
--- a/t/t4060-diff-submodule-option-diff-format.sh
+++ b/t/t4060-diff-submodule-option-diff-format.sh
@@ -10,6 +10,7 @@ test_description='Support for diff format verbose submodule difference in git di
This test tries to verify the sanity of --submodule=diff option of git diff.
'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# Tested non-UTF-8 encoding
diff --git a/t/t4061-diff-indent.sh b/t/t4061-diff-indent.sh
index 7750b87ca1..2942e5d9b9 100755
--- a/t/t4061-diff-indent.sh
+++ b/t/t4061-diff-indent.sh
@@ -6,6 +6,7 @@ test_description='Test diff indent heuristic.
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-diff.sh
diff --git a/t/t4120-apply-popt.sh b/t/t4120-apply-popt.sh
index 697e86c0ff..f788428540 100755
--- a/t/t4120-apply-popt.sh
+++ b/t/t4120-apply-popt.sh
@@ -5,6 +5,7 @@
test_description='git apply -p handling.'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
diff --git a/t/t4131-apply-fake-ancestor.sh b/t/t4131-apply-fake-ancestor.sh
index b1361ce546..40c92115a6 100755
--- a/t/t4131-apply-fake-ancestor.sh
+++ b/t/t4131-apply-fake-ancestor.sh
@@ -5,6 +5,7 @@
test_description='git apply --build-fake-ancestor handling.'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
diff --git a/t/t4137-apply-submodule.sh b/t/t4137-apply-submodule.sh
index 07d5262537..ebd0d4ad17 100755
--- a/t/t4137-apply-submodule.sh
+++ b/t/t4137-apply-submodule.sh
@@ -2,6 +2,7 @@
test_description='git apply handling submodules'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-submodule-update.sh
diff --git a/t/t4151-am-abort.sh b/t/t4151-am-abort.sh
index edb38da701..1825a89d6a 100755
--- a/t/t4151-am-abort.sh
+++ b/t/t4151-am-abort.sh
@@ -2,6 +2,7 @@
test_description='am --abort'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
diff --git a/t/t4153-am-resume-override-opts.sh b/t/t4153-am-resume-override-opts.sh
index 4add7c7757..a4d0c03ca6 100755
--- a/t/t4153-am-resume-override-opts.sh
+++ b/t/t4153-am-resume-override-opts.sh
@@ -2,8 +2,8 @@
test_description='git-am command-line options override saved options'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
-. "$TEST_DIRECTORY"/lib-terminal.sh
format_patch () {
git format-patch --stdout -1 "$1" >"$1".eml
@@ -27,7 +27,12 @@ test_expect_success 'setup' '
format_patch side2
'
-test_expect_success TTY '--3way overrides --no-3way' '
+test_expect_success '--retry fails without in-progress operation' '
+ test_must_fail git am --retry 2>err &&
+ test_grep "operation not in progress" err
+'
+
+test_expect_success '--3way overrides --no-3way' '
rm -fr .git/rebase-apply &&
git reset --hard &&
git checkout renamed-file &&
@@ -40,7 +45,7 @@ test_expect_success TTY '--3way overrides --no-3way' '
# Applying side1 with am --3way will succeed due to the threeway-merge.
# Applying side2 will fail as --3way does not apply to it.
- test_must_fail test_terminal git am --3way </dev/zero &&
+ test_must_fail git am --retry --3way &&
test_path_is_dir .git/rebase-apply &&
test side1 = "$(cat file2)"
'
@@ -84,7 +89,7 @@ test_expect_success '--signoff overrides --no-signoff' '
test $(git cat-file commit HEAD | grep -c "Signed-off-by:") -eq 0
'
-test_expect_success TTY '--reject overrides --no-reject' '
+test_expect_success '--reject overrides --no-reject' '
rm -fr .git/rebase-apply &&
git reset --hard &&
git checkout first &&
@@ -94,7 +99,7 @@ test_expect_success TTY '--reject overrides --no-reject' '
test_path_is_dir .git/rebase-apply &&
test_path_is_missing file.rej &&
- test_must_fail test_terminal git am --reject </dev/zero &&
+ test_must_fail git am --retry --reject </dev/zero &&
test_path_is_dir .git/rebase-apply &&
test_path_is_file file.rej
'
diff --git a/t/t4202-log.sh b/t/t4202-log.sh
index 60fe60d761..51f7beb59f 100755
--- a/t/t4202-log.sh
+++ b/t/t4202-log.sh
@@ -1237,6 +1237,30 @@ test_expect_success 'log.abbrevCommit configuration' '
test_cmp expect.whatchanged.full actual
'
+test_expect_success '--abbrev-commit with core.abbrev=false' '
+ git log --no-abbrev >expect &&
+ git -c core.abbrev=false log --abbrev-commit >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '--abbrev-commit with --no-abbrev' '
+ git log --no-abbrev >expect &&
+ git log --abbrev-commit --no-abbrev >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '--abbrev-commit with core.abbrev=9000' '
+ git log --no-abbrev >expect &&
+ git -c core.abbrev=9000 log --abbrev-commit >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '--abbrev-commit with --abbrev=9000' '
+ git log --no-abbrev >expect &&
+ git log --abbrev-commit --abbrev=9000 >actual &&
+ test_cmp expect actual
+'
+
test_expect_success 'show added path under "--follow -M"' '
# This tests for a regression introduced in v1.7.2-rc0~103^2~2
test_create_repo regression &&
@@ -2022,7 +2046,7 @@ test_expect_success GPGSM 'log --graph --show-signature x509' '
test_expect_success GPGSSH 'log --graph --show-signature ssh' '
test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
git log --graph --show-signature -n1 signed-ssh >actual &&
- grep "${GOOD_SIGNATURE_TRUSTED}" actual
+ grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual
'
test_expect_success GPGSSH,GPGSSH_VERIFYTIME 'log shows failure on expired signature key' '
diff --git a/t/t4204-patch-id.sh b/t/t4204-patch-id.sh
index a7fa94ce0a..605faea0c7 100755
--- a/t/t4204-patch-id.sh
+++ b/t/t4204-patch-id.sh
@@ -310,4 +310,38 @@ test_expect_success 'patch-id handles diffs with one line of before/after' '
test_config patchid.stable true &&
calc_patch_id diffu1stable <diffu1
'
+
+test_expect_failure 'patch-id computes same ID with different object hashes' '
+ test_when_finished "rm -rf repo-sha1 repo-sha256" &&
+
+ cat >diff <<-\EOF &&
+ diff --git a/bar b/bar
+ index bdaf90f..31051f6 100644
+ --- a/bar
+ +++ b/bar
+ @@ -2 +2,2 @@
+ b
+ +c
+ EOF
+
+ git init --object-format=sha1 repo-sha1 &&
+ git -C repo-sha1 patch-id <diff >patch-id-sha1 &&
+ git init --object-format=sha256 repo-sha256 &&
+ git -C repo-sha256 patch-id <diff >patch-id-sha256 &&
+ test_cmp patch-id-sha1 patch-id-sha256
+'
+
+test_expect_success 'patch-id without repository' '
+ cat >diff <<-\EOF &&
+ diff --git a/bar b/bar
+ index bdaf90f..31051f6 100644
+ --- a/bar
+ +++ b/bar
+ @@ -2 +2,2 @@
+ b
+ +c
+ EOF
+ nongit git patch-id <diff
+'
+
test_done
diff --git a/t/t4208-log-magic-pathspec.sh b/t/t4208-log-magic-pathspec.sh
index 806b2809d4..2a46eb6bed 100755
--- a/t/t4208-log-magic-pathspec.sh
+++ b/t/t4208-log-magic-pathspec.sh
@@ -5,6 +5,7 @@ test_description='magic pathspec tests using git-log'
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/t4210-log-i18n.sh b/t/t4210-log-i18n.sh
index 75216f19ce..7120030b5c 100755
--- a/t/t4210-log-i18n.sh
+++ b/t/t4210-log-i18n.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='test log with i18n features'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./lib-gettext.sh
# two forms of é
diff --git a/t/t4216-log-bloom.sh b/t/t4216-log-bloom.sh
index 2ba0324a69..3f163dc396 100755
--- a/t/t4216-log-bloom.sh
+++ b/t/t4216-log-bloom.sh
@@ -82,7 +82,23 @@ test_bloom_filters_used () {
test_bloom_filters_not_used () {
log_args=$1
setup "$log_args" &&
- ! grep -q "statistics:{\"filter_not_present\":" "$TRASH_DIRECTORY/trace.perf" &&
+
+ if grep -q "statistics:{\"filter_not_present\":" "$TRASH_DIRECTORY/trace.perf"
+ then
+ # if the Bloom filter system is initialized, ensure that no
+ # filters were used
+ data="statistics:{"
+ # unusable filters (e.g., those computed with a
+ # different value of commitGraph.changedPathsVersion)
+ # are counted in the filter_not_present bucket, so any
+ # value is OK there.
+ data="$data\"filter_not_present\":[0-9][0-9]*,"
+ data="$data\"maybe\":0,"
+ data="$data\"definitely_not\":0,"
+ data="$data\"false_positive\":0}"
+
+ grep -q "$data" "$TRASH_DIRECTORY/trace.perf"
+ fi &&
test_cmp log_wo_bloom log_w_bloom
}
@@ -163,7 +179,7 @@ test_expect_success 'setup - add commit-graph to the chain with Bloom filters' '
test_bloom_filters_used_when_some_filters_are_missing () {
log_args=$1
- bloom_trace_prefix="statistics:{\"filter_not_present\":3,\"maybe\":6,\"definitely_not\":9"
+ bloom_trace_prefix="statistics:{\"filter_not_present\":3,\"maybe\":6,\"definitely_not\":10"
setup "$log_args" &&
grep -q "$bloom_trace_prefix" "$TRASH_DIRECTORY/trace.perf" &&
test_cmp log_wo_bloom log_w_bloom
@@ -206,6 +222,10 @@ test_filter_trunc_large () {
grep "\"key\":\"filter-trunc-large\",\"value\":\"$1\"" $2
}
+test_filter_upgraded () {
+ grep "\"key\":\"filter-upgraded\",\"value\":\"$1\"" $2
+}
+
test_expect_success 'correctly report changes over limit' '
git init limits &&
(
@@ -405,8 +425,307 @@ test_expect_success 'Bloom generation backfills empty commits' '
)
'
+graph=.git/objects/info/commit-graph
+graphdir=.git/objects/info/commit-graphs
+chain=$graphdir/commit-graph-chain
+
+test_expect_success 'setup for mixed Bloom setting tests' '
+ repo=mixed-bloom-settings &&
+
+ git init $repo &&
+ for i in one two three
+ do
+ test_commit -C $repo $i file || return 1
+ done
+'
+
+test_expect_success 'ensure Bloom filters with incompatible settings are ignored' '
+ # Compute Bloom filters with "unusual" settings.
+ git -C $repo rev-parse one >in &&
+ GIT_TEST_BLOOM_SETTINGS_NUM_HASHES=3 git -C $repo commit-graph write \
+ --stdin-commits --changed-paths --split <in &&
+ layer=$(head -n 1 $repo/$chain) &&
+
+ # A commit-graph layer without Bloom filters "hides" the layers
+ # below ...
+ git -C $repo rev-parse two >in &&
+ git -C $repo commit-graph write --stdin-commits --no-changed-paths \
+ --split=no-merge <in &&
+
+ # Another commit-graph layer that has Bloom filters, but with
+ # standard settings, and is thus incompatible with the base
+ # layer written above.
+ git -C $repo rev-parse HEAD >in &&
+ git -C $repo commit-graph write --stdin-commits --changed-paths \
+ --split=no-merge <in &&
+
+ test_line_count = 3 $repo/$chain &&
+
+ # Ensure that incompatible Bloom filters are ignored.
+ git -C $repo -c core.commitGraph=false log --oneline --no-decorate -- file \
+ >expect 2>err &&
+ git -C $repo log --oneline --no-decorate -- file >actual 2>err &&
+ test_cmp expect actual &&
+ grep "disabling Bloom filters for commit-graph layer .$layer." err
+'
+
+test_expect_success 'merge graph layers with incompatible Bloom settings' '
+ # Ensure that incompatible Bloom filters are ignored when
+ # merging existing layers.
+ >trace2.txt &&
+ GIT_TRACE2_EVENT="$(pwd)/trace2.txt" \
+ git -C $repo commit-graph write --reachable --changed-paths 2>err &&
+ grep "disabling Bloom filters for commit-graph layer .$layer." err &&
+ grep "{\"hash_version\":1,\"num_hashes\":7,\"bits_per_entry\":10,\"max_changed_paths\":512" trace2.txt &&
+
+ test_path_is_file $repo/$graph &&
+ test_dir_is_empty $repo/$graphdir &&
+
+ git -C $repo -c core.commitGraph=false log --oneline --no-decorate -- \
+ file >expect &&
+ trace_out="$(pwd)/trace.perf" &&
+ GIT_TRACE2_PERF="$trace_out" \
+ git -C $repo log --oneline --no-decorate -- file >actual 2>err &&
+
+ test_cmp expect actual &&
+ grep "statistics:{\"filter_not_present\":0," trace.perf &&
+ test_must_be_empty err
+'
+
+# chosen to be the same under all Unicode normalization forms
+CENT=$(printf "\302\242")
+
+test_expect_success 'ensure Bloom filter with incompatible versions are ignored' '
+ rm "$repo/$graph" &&
+
+ git -C $repo log --oneline --no-decorate -- $CENT >expect &&
+
+ # Compute v1 Bloom filters for commits at the bottom.
+ git -C $repo rev-parse HEAD^ >in &&
+ git -C $repo commit-graph write --stdin-commits --changed-paths \
+ --split <in &&
+
+ # Compute v2 Bloomfilters for the rest of the commits at the top.
+ git -C $repo rev-parse HEAD >in &&
+ git -C $repo -c commitGraph.changedPathsVersion=2 commit-graph write \
+ --stdin-commits --changed-paths --split=no-merge <in &&
+
+ test_line_count = 2 $repo/$chain &&
+
+ git -C $repo log --oneline --no-decorate -- $CENT >actual 2>err &&
+ test_cmp expect actual &&
+
+ layer="$(head -n 1 $repo/$chain)" &&
+ cat >expect.err <<-EOF &&
+ warning: disabling Bloom filters for commit-graph layer $SQ$layer$SQ due to incompatible settings
+ EOF
+ test_cmp expect.err err &&
+
+ # Merge the two layers with incompatible bloom filter versions,
+ # ensuring that the v2 filters are used.
+ >trace2.txt &&
+ GIT_TRACE2_EVENT="$(pwd)/trace2.txt" \
+ git -C $repo -c commitGraph.changedPathsVersion=2 commit-graph write --reachable --changed-paths 2>err &&
+ grep "disabling Bloom filters for commit-graph layer .$layer." err &&
+ grep "{\"hash_version\":2,\"num_hashes\":7,\"bits_per_entry\":10,\"max_changed_paths\":512" trace2.txt
+'
+
+get_first_changed_path_filter () {
+ test-tool read-graph bloom-filters >filters.dat &&
+ head -n 1 filters.dat
+}
+
+test_expect_success 'set up repo with high bit path, version 1 changed-path' '
+ git init highbit1 &&
+ test_commit -C highbit1 c1 "$CENT" &&
+ git -C highbit1 commit-graph write --reachable --changed-paths
+'
+
+test_expect_success 'setup check value of version 1 changed-path' '
+ (
+ cd highbit1 &&
+ echo "52a9" >expect &&
+ get_first_changed_path_filter >actual
+ )
+'
+
+# expect will not match actual if char is unsigned by default. Write the test
+# in this way, so that a user running this test script can still see if the two
+# files match. (It will appear as an ordinary success if they match, and a skip
+# if not.)
+if test_cmp highbit1/expect highbit1/actual
+then
+ test_set_prereq SIGNED_CHAR_BY_DEFAULT
+fi
+test_expect_success SIGNED_CHAR_BY_DEFAULT 'check value of version 1 changed-path' '
+ # Only the prereq matters for this test.
+ true
+'
+
+test_expect_success 'setup make another commit' '
+ # "git log" does not use Bloom filters for root commits - see how, in
+ # revision.c, rev_compare_tree() (the only code path that eventually calls
+ # get_bloom_filter()) is only called by try_to_simplify_commit() when the commit
+ # has one parent. Therefore, make another commit so that we perform the tests on
+ # a non-root commit.
+ test_commit -C highbit1 anotherc1 "another$CENT"
+'
+
+test_expect_success 'version 1 changed-path used when version 1 requested' '
+ (
+ cd highbit1 &&
+ test_bloom_filters_used "-- another$CENT"
+ )
+'
+
+test_expect_success 'version 1 changed-path not used when version 2 requested' '
+ (
+ cd highbit1 &&
+ git config --add commitGraph.changedPathsVersion 2 &&
+ test_bloom_filters_not_used "-- another$CENT"
+ )
+'
+
+test_expect_success 'version 1 changed-path used when autodetect requested' '
+ (
+ cd highbit1 &&
+ git config --add commitGraph.changedPathsVersion -1 &&
+ test_bloom_filters_used "-- another$CENT"
+ )
+'
+
+test_expect_success 'when writing another commit graph, preserve existing version 1 of changed-path' '
+ test_commit -C highbit1 c1double "$CENT$CENT" &&
+ git -C highbit1 commit-graph write --reachable --changed-paths &&
+ (
+ cd highbit1 &&
+ git config --add commitGraph.changedPathsVersion -1 &&
+ echo "options: bloom(1,10,7) read_generation_data" >expect &&
+ test-tool read-graph >full &&
+ grep options full >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'set up repo with high bit path, version 2 changed-path' '
+ git init highbit2 &&
+ git -C highbit2 config --add commitGraph.changedPathsVersion 2 &&
+ test_commit -C highbit2 c2 "$CENT" &&
+ git -C highbit2 commit-graph write --reachable --changed-paths
+'
+
+test_expect_success 'check value of version 2 changed-path' '
+ (
+ cd highbit2 &&
+ echo "c01f" >expect &&
+ get_first_changed_path_filter >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'setup make another commit' '
+ # "git log" does not use Bloom filters for root commits - see how, in
+ # revision.c, rev_compare_tree() (the only code path that eventually calls
+ # get_bloom_filter()) is only called by try_to_simplify_commit() when the commit
+ # has one parent. Therefore, make another commit so that we perform the tests on
+ # a non-root commit.
+ test_commit -C highbit2 anotherc2 "another$CENT"
+'
+
+test_expect_success 'version 2 changed-path used when version 2 requested' '
+ (
+ cd highbit2 &&
+ test_bloom_filters_used "-- another$CENT"
+ )
+'
+
+test_expect_success 'version 2 changed-path not used when version 1 requested' '
+ (
+ cd highbit2 &&
+ git config --add commitGraph.changedPathsVersion 1 &&
+ test_bloom_filters_not_used "-- another$CENT"
+ )
+'
+
+test_expect_success 'version 2 changed-path used when autodetect requested' '
+ (
+ cd highbit2 &&
+ git config --add commitGraph.changedPathsVersion -1 &&
+ test_bloom_filters_used "-- another$CENT"
+ )
+'
+
+test_expect_success 'when writing another commit graph, preserve existing version 2 of changed-path' '
+ test_commit -C highbit2 c2double "$CENT$CENT" &&
+ git -C highbit2 commit-graph write --reachable --changed-paths &&
+ (
+ cd highbit2 &&
+ git config --add commitGraph.changedPathsVersion -1 &&
+ echo "options: bloom(2,10,7) read_generation_data" >expect &&
+ test-tool read-graph >full &&
+ grep options full >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'when writing commit graph, do not reuse changed-path of another version' '
+ git init doublewrite &&
+ test_commit -C doublewrite c "$CENT" &&
+
+ git -C doublewrite config --add commitGraph.changedPathsVersion 1 &&
+ >trace2.txt &&
+ GIT_TRACE2_EVENT="$(pwd)/trace2.txt" \
+ git -C doublewrite commit-graph write --reachable --changed-paths &&
+ test_filter_computed 1 trace2.txt &&
+ test_filter_upgraded 0 trace2.txt &&
+
+ git -C doublewrite commit-graph write --reachable --changed-paths &&
+ for v in -2 3
+ do
+ git -C doublewrite config --add commitGraph.changedPathsVersion $v &&
+ git -C doublewrite commit-graph write --reachable --changed-paths 2>err &&
+ cat >expect <<-EOF &&
+ warning: attempting to write a commit-graph, but ${SQ}commitGraph.changedPathsVersion${SQ} ($v) is not supported
+ EOF
+ test_cmp expect err || return 1
+ done &&
+
+ git -C doublewrite config --add commitGraph.changedPathsVersion 2 &&
+ >trace2.txt &&
+ GIT_TRACE2_EVENT="$(pwd)/trace2.txt" \
+ git -C doublewrite commit-graph write --reachable --changed-paths &&
+ test_filter_computed 1 trace2.txt &&
+ test_filter_upgraded 0 trace2.txt &&
+
+ (
+ cd doublewrite &&
+ echo "c01f" >expect &&
+ get_first_changed_path_filter >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'when writing commit graph, reuse changed-path of another version where possible' '
+ git init upgrade &&
+
+ test_commit -C upgrade base no-high-bits &&
+
+ git -C upgrade config --add commitGraph.changedPathsVersion 1 &&
+ >trace2.txt &&
+ GIT_TRACE2_EVENT="$(pwd)/trace2.txt" \
+ git -C upgrade commit-graph write --reachable --changed-paths &&
+ test_filter_computed 1 trace2.txt &&
+ test_filter_upgraded 0 trace2.txt &&
+
+ git -C upgrade config --add commitGraph.changedPathsVersion 2 &&
+ >trace2.txt &&
+ GIT_TRACE2_EVENT="$(pwd)/trace2.txt" \
+ git -C upgrade commit-graph write --reachable --changed-paths &&
+ test_filter_computed 0 trace2.txt &&
+ test_filter_upgraded 1 trace2.txt
+'
+
corrupt_graph () {
- graph=.git/objects/info/commit-graph &&
test_when_finished "rm -rf $graph" &&
git commit-graph write --reachable --changed-paths &&
corrupt_chunk_file $graph "$@"
diff --git a/t/t4253-am-keep-cr-dos.sh b/t/t4253-am-keep-cr-dos.sh
index 0ee69d2a0c..2bcdd9f34f 100755
--- a/t/t4253-am-keep-cr-dos.sh
+++ b/t/t4253-am-keep-cr-dos.sh
@@ -9,6 +9,7 @@ test_description='git-am mbox with dos line ending.
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# Three patches which will be added as files with dos line ending.
diff --git a/t/t4255-am-submodule.sh b/t/t4255-am-submodule.sh
index a7ba08f728..04f3ccfc41 100755
--- a/t/t4255-am-submodule.sh
+++ b/t/t4255-am-submodule.sh
@@ -2,6 +2,7 @@
test_description='git am handling submodules'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-submodule-update.sh
diff --git a/t/t5001-archive-attr.sh b/t/t5001-archive-attr.sh
index eaf959d8f6..7310774af5 100755
--- a/t/t5001-archive-attr.sh
+++ b/t/t5001-archive-attr.sh
@@ -133,7 +133,8 @@ test_expect_success 'git archive vs. bare' '
'
test_expect_success 'git archive with worktree attributes, bare' '
- (cd bare && git archive --worktree-attributes HEAD) >bare-worktree.tar &&
+ (cd bare &&
+ git -c attr.tree=HEAD archive --worktree-attributes HEAD) >bare-worktree.tar &&
(mkdir bare-worktree && cd bare-worktree && "$TAR" xf -) <bare-worktree.tar
'
diff --git a/t/t5150-request-pull.sh b/t/t5150-request-pull.sh
index cb67bac1c4..86bee33160 100755
--- a/t/t5150-request-pull.sh
+++ b/t/t5150-request-pull.sh
@@ -5,6 +5,7 @@ test_description='Test workflows involving pull request.'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
if ! test_have_prereq PERL
diff --git a/t/t5300-pack-object.sh b/t/t5300-pack-object.sh
index 61e2be2903..4ad023c846 100755
--- a/t/t5300-pack-object.sh
+++ b/t/t5300-pack-object.sh
@@ -3,9 +3,9 @@
# Copyright (c) 2005 Junio C Hamano
#
-test_description='git pack-object
+test_description='git pack-object'
-'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
diff --git a/t/t5305-include-tag.sh b/t/t5305-include-tag.sh
index 44bd9ef45f..dc8fe55c82 100755
--- a/t/t5305-include-tag.sh
+++ b/t/t5305-include-tag.sh
@@ -4,6 +4,7 @@ test_description='git pack-object --include-tag'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
TRASH=$(pwd)
diff --git a/t/t5319-multi-pack-index.sh b/t/t5319-multi-pack-index.sh
index dd09134db0..10d2a6bf92 100755
--- a/t/t5319-multi-pack-index.sh
+++ b/t/t5319-multi-pack-index.sh
@@ -350,6 +350,29 @@ test_expect_success 'preferred packs must be non-empty' '
)
'
+test_expect_success 'preferred pack from existing MIDX without bitmaps' '
+ git init preferred-without-bitmaps &&
+ (
+ cd preferred-without-bitmaps &&
+
+ test_commit one &&
+ pack="$(git pack-objects --all $objdir/pack/pack </dev/null)" &&
+
+ git multi-pack-index write &&
+
+ # make another pack so that the subsequent MIDX write
+ # has something to do
+ test_commit two &&
+ git repack -d &&
+
+ # write a new MIDX without bitmaps reusing the singular
+ # pack from the existing MIDX as the preferred pack in
+ # the new MIDX
+ git multi-pack-index write --preferred-pack=pack-$pack.pack
+ )
+
+'
+
test_expect_success 'verify multi-pack-index success' '
git multi-pack-index verify --object-dir=$objdir
'
diff --git a/t/t5324-split-commit-graph.sh b/t/t5324-split-commit-graph.sh
index 281266f788..77e91547ea 100755
--- a/t/t5324-split-commit-graph.sh
+++ b/t/t5324-split-commit-graph.sh
@@ -13,7 +13,8 @@ test_expect_success 'setup repo' '
git init &&
git config core.commitGraph true &&
git config gc.writeCommitGraph false &&
- infodir=".git/objects/info" &&
+ objdir=".git/objects" &&
+ infodir="$objdir/info" &&
graphdir="$infodir/commit-graphs" &&
test_oid_cache <<-EOM
shallow sha1:2132
@@ -718,4 +719,27 @@ test_expect_success 'write generation data chunk when commit-graph chain is repl
)
'
+test_expect_success 'temporary graph layer is discarded upon failure' '
+ git init layer-discard &&
+ (
+ cd layer-discard &&
+
+ test_commit A &&
+ test_commit B &&
+
+ # Intentionally remove commit "A" from the object store
+ # so that the commit-graph machinery fails to parse the
+ # parents of "B".
+ #
+ # This takes place after the commit-graph machinery has
+ # initialized a new temporary file to store the contents
+ # of the new graph layer, so will allow us to ensure
+ # that the temporary file is discarded upon failure.
+ rm $objdir/$(test_oid_to_path $(git rev-parse HEAD^)) &&
+
+ test_must_fail git commit-graph write --reachable --split &&
+ test_dir_is_empty $graphdir
+ )
+'
+
test_done
diff --git a/t/t5326-multi-pack-bitmaps.sh b/t/t5326-multi-pack-bitmaps.sh
index 5d7d321840..916da389b6 100755
--- a/t/t5326-multi-pack-bitmaps.sh
+++ b/t/t5326-multi-pack-bitmaps.sh
@@ -434,6 +434,27 @@ test_expect_success 'tagged commits are selected for bitmapping' '
)
'
+test_expect_success 'do not follow replace objects for MIDX bitmap' '
+ rm -fr repo &&
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+
+ test_commit A &&
+ test_commit B &&
+ git checkout --orphan=orphan A &&
+ test_commit orphan &&
+
+ git replace A HEAD &&
+ git repack -ad --write-midx --write-bitmap-index &&
+
+ # generating reachability bitmaps with replace refs
+ # enabled will result in broken clones
+ git clone --no-local --bare . clone.git
+ )
+'
+
corrupt_file () {
chmod a+w "$1" &&
printf "bogus" | dd of="$1" bs=1 seek="12" conv=notrunc
@@ -530,4 +551,34 @@ do
'
done
+test_expect_success 'remove one packfile between MIDX bitmap writes' '
+ git init remove-pack-between-writes &&
+ (
+ cd remove-pack-between-writes &&
+
+ test_commit A &&
+ test_commit B &&
+ test_commit C &&
+
+ # Create packs with the prefix "pack-A", "pack-B",
+ # "pack-C" to impose a lexicographic order on these
+ # packs so the pack being removed is always from the
+ # middle.
+ packdir=.git/objects/pack &&
+ A="$(echo A | git pack-objects $packdir/pack-A --revs)" &&
+ B="$(echo B | git pack-objects $packdir/pack-B --revs)" &&
+ C="$(echo C | git pack-objects $packdir/pack-C --revs)" &&
+
+ git multi-pack-index write --bitmap &&
+
+ cat >in <<-EOF &&
+ pack-A-$A.idx
+ pack-C-$C.idx
+ EOF
+ git multi-pack-index write --bitmap --stdin-packs <in &&
+
+ git rev-list --test-bitmap HEAD
+ )
+'
+
test_done
diff --git a/t/t5332-multi-pack-reuse.sh b/t/t5332-multi-pack-reuse.sh
index 3c20738bce..ed823f37bc 100755
--- a/t/t5332-multi-pack-reuse.sh
+++ b/t/t5332-multi-pack-reuse.sh
@@ -204,4 +204,30 @@ test_expect_success 'omit delta from uninteresting base (cross pack)' '
test_pack_objects_reused_all $(($objects_nr - 1)) $packs_nr
'
+test_expect_success 'non-omitted delta in MIDX preferred pack' '
+ test_config pack.allowPackReuse single &&
+
+ cat >p1.objects <<-EOF &&
+ $(git rev-parse $base)
+ ^$(git rev-parse $delta^)
+ EOF
+ cat >p2.objects <<-EOF &&
+ $(git rev-parse F)
+ EOF
+
+ p1="$(git pack-objects --revs $packdir/pack <p1.objects)" &&
+ p2="$(git pack-objects --revs $packdir/pack <p2.objects)" &&
+
+ cat >in <<-EOF &&
+ pack-$p1.idx
+ pack-$p2.idx
+ EOF
+ git multi-pack-index write --bitmap --stdin-packs \
+ --preferred-pack=pack-$p1.pack <in &&
+
+ git show-index <$packdir/pack-$p1.idx >expect &&
+
+ test_pack_objects_reused_all $(wc -l <expect) 1
+'
+
test_done
diff --git a/t/t5333-pseudo-merge-bitmaps.sh b/t/t5333-pseudo-merge-bitmaps.sh
new file mode 100755
index 0000000000..f052f395a7
--- /dev/null
+++ b/t/t5333-pseudo-merge-bitmaps.sh
@@ -0,0 +1,393 @@
+#!/bin/sh
+
+test_description='pseudo-merge bitmaps'
+
+GIT_TEST_MULTI_PACK_INDEX_WRITE_BITMAP=0
+
+. ./test-lib.sh
+
+test_pseudo_merges () {
+ test-tool bitmap dump-pseudo-merges
+}
+
+test_pseudo_merge_commits () {
+ test-tool bitmap dump-pseudo-merge-commits "$1"
+}
+
+test_pseudo_merges_satisfied () {
+ test_trace2_data bitmap pseudo_merges_satisfied "$1"
+}
+
+test_pseudo_merges_cascades () {
+ test_trace2_data bitmap pseudo_merges_cascades "$1"
+}
+
+test_pseudo_merges_reused () {
+ test_trace2_data pack-bitmap-write building_bitmaps_pseudo_merge_reused "$1"
+}
+
+tag_everything () {
+ git rev-list --all --no-object-names >in &&
+ perl -lne '
+ print "create refs/tags/" . $. . " " . $1 if /([0-9a-f]+)/
+ ' <in | git update-ref --stdin
+}
+
+test_expect_success 'setup' '
+ test_commit_bulk 512 &&
+ tag_everything
+'
+
+test_expect_success 'bitmap traversal without pseudo-merges' '
+ git repack -adb &&
+
+ git rev-list --count --all --objects >expect &&
+
+ : >trace2.txt &&
+ GIT_TRACE2_EVENT=$PWD/trace2.txt \
+ git rev-list --count --all --objects --use-bitmap-index >actual &&
+
+ test_pseudo_merges_satisfied 0 <trace2.txt &&
+ test_pseudo_merges_cascades 0 <trace2.txt &&
+ test_pseudo_merges >merges &&
+ test_must_be_empty merges &&
+ test_cmp expect actual
+'
+
+test_expect_success 'pseudo-merges accurately represent their objects' '
+ test_config bitmapPseudoMerge.test.pattern "refs/tags/" &&
+ test_config bitmapPseudoMerge.test.maxMerges 8 &&
+ test_config bitmapPseudoMerge.test.stableThreshold never &&
+
+ git repack -adb &&
+
+ test_pseudo_merges >merges &&
+ test_line_count = 8 merges &&
+
+ for i in $(test_seq 0 $(($(wc -l <merges)-1)))
+ do
+ test-tool bitmap dump-pseudo-merge-commits $i >commits &&
+
+ git rev-list --objects --no-object-names --stdin <commits >expect.raw &&
+ test-tool bitmap dump-pseudo-merge-objects $i >actual.raw &&
+
+ sort -u <expect.raw >expect &&
+ sort -u <actual.raw >actual &&
+
+ test_cmp expect actual || return 1
+ done
+'
+
+test_expect_success 'bitmap traversal with pseudo-merges' '
+ : >trace2.txt &&
+ GIT_TRACE2_EVENT=$PWD/trace2.txt \
+ git rev-list --count --all --objects --use-bitmap-index >actual &&
+ git rev-list --count --all --objects >expect &&
+
+ test_pseudo_merges_satisfied 8 <trace2.txt &&
+ test_pseudo_merges_cascades 1 <trace2.txt &&
+ test_cmp expect actual
+'
+
+test_expect_success 'stale bitmap traversal with pseudo-merges' '
+ test_commit other &&
+
+ : >trace2.txt &&
+ GIT_TRACE2_EVENT=$PWD/trace2.txt \
+ git rev-list --count --all --objects --use-bitmap-index >actual &&
+ git rev-list --count --all --objects >expect &&
+
+ test_pseudo_merges_satisfied 8 <trace2.txt &&
+ test_pseudo_merges_cascades 1 <trace2.txt &&
+ test_cmp expect actual
+'
+
+test_expect_success 'bitmapPseudoMerge.sampleRate adjusts commit selection rate' '
+ test_config bitmapPseudoMerge.test.pattern "refs/tags/" &&
+ test_config bitmapPseudoMerge.test.maxMerges 1 &&
+ test_config bitmapPseudoMerge.test.stableThreshold never &&
+
+ commits_nr=$(git rev-list --all --count) &&
+
+ for rate in 1.0 0.5 0.25
+ do
+ git -c bitmapPseudoMerge.test.sampleRate=$rate repack -adb &&
+
+ test_pseudo_merges >merges &&
+ test_line_count = 1 merges &&
+ test_pseudo_merge_commits 0 >commits &&
+
+ test-tool bitmap list-commits >bitmaps &&
+ bitmaps_nr="$(wc -l <bitmaps)" &&
+
+ perl -MPOSIX -e "print ceil(\$ARGV[0]*(\$ARGV[1]-\$ARGV[2]))" \
+ "$rate" "$commits_nr" "$bitmaps_nr" >expect &&
+
+ test $(cat expect) -eq $(wc -l <commits) || return 1
+ done
+'
+
+test_expect_success 'bitmapPseudoMerge.threshold excludes newer commits' '
+ git init pseudo-merge-threshold &&
+ (
+ cd pseudo-merge-threshold &&
+
+ new="1672549200" && # 2023-01-01
+ old="1641013200" && # 2022-01-01
+
+ GIT_COMMITTER_DATE="$new +0000" &&
+ export GIT_COMMITTER_DATE &&
+ test_commit_bulk --message="new" --notick 128 &&
+
+ GIT_COMMITTER_DATE="$old +0000" &&
+ export GIT_COMMITTER_DATE &&
+ test_commit_bulk --message="old" --notick 128 &&
+
+ tag_everything &&
+
+ git \
+ -c bitmapPseudoMerge.test.pattern="refs/tags/" \
+ -c bitmapPseudoMerge.test.maxMerges=1 \
+ -c bitmapPseudoMerge.test.threshold=$(($new - 1)) \
+ -c bitmapPseudoMerge.test.stableThreshold=never \
+ repack -adb &&
+
+ test_pseudo_merges >merges &&
+ test_line_count = 1 merges &&
+
+ test_pseudo_merge_commits 0 >oids &&
+ git cat-file --batch <oids >commits &&
+
+ test $(wc -l <oids) = $(grep -c "^committer.*$old +0000$" commits)
+ )
+'
+
+test_expect_success 'bitmapPseudoMerge.stableThreshold creates stable groups' '
+ (
+ cd pseudo-merge-threshold &&
+
+ new="1672549200" && # 2023-01-01
+ mid="1654059600" && # 2022-06-01
+ old="1641013200" && # 2022-01-01
+
+ GIT_COMMITTER_DATE="$mid +0000" &&
+ export GIT_COMMITTER_DATE &&
+ test_commit_bulk --message="mid" --notick 128 &&
+
+ git for-each-ref --format="delete %(refname)" refs/tags >in &&
+ git update-ref --stdin <in &&
+
+ tag_everything &&
+
+ git \
+ -c bitmapPseudoMerge.test.pattern="refs/tags/" \
+ -c bitmapPseudoMerge.test.maxMerges=1 \
+ -c bitmapPseudoMerge.test.threshold=$(($new - 1)) \
+ -c bitmapPseudoMerge.test.stableThreshold=$(($mid - 1)) \
+ -c bitmapPseudoMerge.test.stableSize=10 \
+ repack -adb &&
+
+ test_pseudo_merges >merges &&
+ merges_nr="$(wc -l <merges)" &&
+
+ for i in $(test_seq $(($merges_nr - 1)))
+ do
+ test_pseudo_merge_commits 0 >oids &&
+ git cat-file --batch <oids >commits &&
+
+ expect="$(grep -c "^committer.*$old +0000$" commits)" &&
+ actual="$(wc -l <oids)" &&
+
+ test $expect = $actual || return 1
+ done &&
+
+ test_pseudo_merge_commits $(($merges_nr - 1)) >oids &&
+ git cat-file --batch <oids >commits &&
+ test $(wc -l <oids) = $(grep -c "^committer.*$mid +0000$" commits)
+ )
+'
+
+test_expect_success 'out of order thresholds are rejected' '
+ test_must_fail git \
+ -c bitmapPseudoMerge.test.pattern="refs/*" \
+ -c bitmapPseudoMerge.test.threshold=1.month.ago \
+ -c bitmapPseudoMerge.test.stableThreshold=1.week.ago \
+ repack -adb 2>err &&
+
+ cat >expect <<-EOF &&
+ fatal: pseudo-merge group ${SQ}test${SQ} has unstable threshold before stable one
+ EOF
+
+ test_cmp expect err
+'
+
+test_expect_success 'pseudo-merge pattern with capture groups' '
+ git init pseudo-merge-captures &&
+ (
+ cd pseudo-merge-captures &&
+
+ test_commit_bulk 128 &&
+ tag_everything &&
+
+ for r in $(test_seq 8)
+ do
+ test_commit_bulk 16 &&
+
+ git rev-list HEAD~16.. >in &&
+
+ perl -lne "print \"create refs/remotes/$r/tags/\$. \$_\"" <in |
+ git update-ref --stdin || return 1
+ done &&
+
+ git \
+ -c bitmapPseudoMerge.tags.pattern="refs/remotes/([0-9]+)/tags/" \
+ -c bitmapPseudoMerge.tags.maxMerges=1 \
+ repack -adb &&
+
+ git for-each-ref --format="%(objectname) %(refname)" >refs &&
+
+ test_pseudo_merges >merges &&
+ for m in $(test_seq 0 $(($(wc -l <merges) - 1)))
+ do
+ test_pseudo_merge_commits $m >oids &&
+ grep -f oids refs |
+ perl -lne "print \$1 if /refs\/remotes\/([0-9]+)/" |
+ sort -u || return 1
+ done >remotes &&
+
+ test $(wc -l <remotes) -eq $(sort -u <remotes | wc -l)
+ )
+'
+
+test_expect_success 'pseudo-merge overlap setup' '
+ git init pseudo-merge-overlap &&
+ (
+ cd pseudo-merge-overlap &&
+
+ test_commit_bulk 256 &&
+ tag_everything &&
+
+ git \
+ -c bitmapPseudoMerge.all.pattern="refs/" \
+ -c bitmapPseudoMerge.all.maxMerges=1 \
+ -c bitmapPseudoMerge.all.stableThreshold=never \
+ -c bitmapPseudoMerge.tags.pattern="refs/tags/" \
+ -c bitmapPseudoMerge.tags.maxMerges=1 \
+ -c bitmapPseudoMerge.tags.stableThreshold=never \
+ repack -adb
+ )
+'
+
+test_expect_success 'pseudo-merge overlap generates overlapping groups' '
+ (
+ cd pseudo-merge-overlap &&
+
+ test_pseudo_merges >merges &&
+ test_line_count = 2 merges &&
+
+ test_pseudo_merge_commits 0 >commits-0.raw &&
+ test_pseudo_merge_commits 1 >commits-1.raw &&
+
+ sort commits-0.raw >commits-0 &&
+ sort commits-1.raw >commits-1 &&
+
+ comm -12 commits-0 commits-1 >overlap &&
+
+ test_line_count -gt 0 overlap
+ )
+'
+
+test_expect_success 'pseudo-merge overlap traversal' '
+ (
+ cd pseudo-merge-overlap &&
+
+ : >trace2.txt &&
+ GIT_TRACE2_EVENT=$PWD/trace2.txt \
+ git rev-list --count --all --objects --use-bitmap-index >actual &&
+ git rev-list --count --all --objects >expect &&
+
+ test_pseudo_merges_satisfied 2 <trace2.txt &&
+ test_pseudo_merges_cascades 1 <trace2.txt &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'pseudo-merge overlap stale traversal' '
+ (
+ cd pseudo-merge-overlap &&
+
+ test_commit other &&
+
+ : >trace2.txt &&
+ GIT_TRACE2_EVENT=$PWD/trace2.txt \
+ git rev-list --count --all --objects --use-bitmap-index >actual &&
+ git rev-list --count --all --objects >expect &&
+
+ test_pseudo_merges_satisfied 2 <trace2.txt &&
+ test_pseudo_merges_cascades 1 <trace2.txt &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'pseudo-merge reuse' '
+ git init pseudo-merge-reuse &&
+ (
+ cd pseudo-merge-reuse &&
+
+ stable="1641013200" && # 2022-01-01
+ unstable="1672549200" && # 2023-01-01
+
+ GIT_COMMITTER_DATE="$stable +0000" &&
+ export GIT_COMMITTER_DATE &&
+ test_commit_bulk --notick 128 &&
+ GIT_COMMITTER_DATE="$unstable +0000" &&
+ export GIT_COMMITTER_DATE &&
+ test_commit_bulk --notick 128 &&
+
+ tag_everything &&
+
+ git \
+ -c bitmapPseudoMerge.test.pattern="refs/tags/" \
+ -c bitmapPseudoMerge.test.maxMerges=1 \
+ -c bitmapPseudoMerge.test.threshold=now \
+ -c bitmapPseudoMerge.test.stableThreshold=$(($unstable - 1)) \
+ -c bitmapPseudoMerge.test.stableSize=512 \
+ repack -adb &&
+
+ test_pseudo_merges >merges &&
+ test_line_count = 2 merges &&
+
+ test_pseudo_merge_commits 0 >stable-oids.before &&
+ test_pseudo_merge_commits 1 >unstable-oids.before &&
+
+ : >trace2.txt &&
+ GIT_TRACE2_EVENT=$PWD/trace2.txt git \
+ -c bitmapPseudoMerge.test.pattern="refs/tags/" \
+ -c bitmapPseudoMerge.test.maxMerges=2 \
+ -c bitmapPseudoMerge.test.threshold=now \
+ -c bitmapPseudoMerge.test.stableThreshold=$(($unstable - 1)) \
+ -c bitmapPseudoMerge.test.stableSize=512 \
+ repack -adb &&
+
+ test_pseudo_merges_reused 1 <trace2.txt &&
+
+ test_pseudo_merges >merges &&
+ test_line_count = 3 merges &&
+
+ test_pseudo_merge_commits 0 >stable-oids.after &&
+ for i in 1 2
+ do
+ test_pseudo_merge_commits $i || return 1
+ done >unstable-oids.after &&
+
+ sort -u <stable-oids.before >expect &&
+ sort -u <stable-oids.after >actual &&
+ test_cmp expect actual &&
+
+ sort -u <unstable-oids.before >expect &&
+ sort -u <unstable-oids.after >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_done
diff --git a/t/t5407-post-rewrite-hook.sh b/t/t5407-post-rewrite-hook.sh
index ad7f8c6f00..e99e728236 100755
--- a/t/t5407-post-rewrite-hook.sh
+++ b/t/t5407-post-rewrite-hook.sh
@@ -7,6 +7,7 @@ test_description='Test the post-rewrite hook.'
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/t5500-fetch-pack.sh b/t/t5500-fetch-pack.sh
index 1bc15a3f08..585ea0ee16 100755
--- a/t/t5500-fetch-pack.sh
+++ b/t/t5500-fetch-pack.sh
@@ -993,6 +993,16 @@ test_expect_success 'ensure bogus fetch.negotiationAlgorithm yields error' '
fetch origin server_has both_have_2
'
+test_expect_success 'fetch-pack with fsckObjects and keep-file does not segfault' '
+ rm -rf server client &&
+ test_create_repo server &&
+ test_commit -C server one &&
+
+ test_create_repo client &&
+ git -c fetch.fsckObjects=true \
+ -C client fetch-pack -k -k ../server HEAD
+'
+
test_expect_success 'filtering by size' '
rm -rf server client &&
test_create_repo server &&
@@ -1046,7 +1056,7 @@ fetch_filter_blob_limit_zero () {
# Ensure that commit is fetched, but blob is not
commit=$(git -C "$SERVER" rev-parse two) &&
- blob=$(git hash-object server/two.t) &&
+ blob=$(git hash-object "$SERVER/two.t") &&
git -C client rev-list --objects --missing=allow-any "$commit" >oids &&
grep "$commit" oids &&
! grep "$blob" oids
diff --git a/t/t5505-remote.sh b/t/t5505-remote.sh
index 7789ff12c4..08424e878e 100755
--- a/t/t5505-remote.sh
+++ b/t/t5505-remote.sh
@@ -1492,4 +1492,40 @@ test_expect_success 'refs/remotes/* <src> refspec and unqualified <dst> DWIM and
)
'
+test_expect_success 'empty config clears remote.*.url list' '
+ test_when_finished "git config --remove-section remote.multi" &&
+ git config --add remote.multi.url wrong-one &&
+ git config --add remote.multi.url wrong-two &&
+ git -c remote.multi.url= \
+ -c remote.multi.url=right-one \
+ -c remote.multi.url=right-two \
+ remote show -n multi >actual.raw &&
+ grep URL actual.raw >actual &&
+ cat >expect <<-\EOF &&
+ Fetch URL: right-one
+ Push URL: right-one
+ Push URL: right-two
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'empty config clears remote.*.pushurl list' '
+ test_when_finished "git config --remove-section remote.multi" &&
+ git config --add remote.multi.url right &&
+ git config --add remote.multi.url will-be-ignored &&
+ git config --add remote.multi.pushurl wrong-push-one &&
+ git config --add remote.multi.pushurl wrong-push-two &&
+ git -c remote.multi.pushurl= \
+ -c remote.multi.pushurl=right-push-one \
+ -c remote.multi.pushurl=right-push-two \
+ remote show -n multi >actual.raw &&
+ grep URL actual.raw >actual &&
+ cat >expect <<-\EOF &&
+ Fetch URL: right
+ Push URL: right-push-one
+ Push URL: right-push-two
+ EOF
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh
index 4eb569f4df..3b3991ab86 100755
--- a/t/t5510-fetch.sh
+++ b/t/t5510-fetch.sh
@@ -1091,6 +1091,22 @@ test_expect_success 'branchname D/F conflict resolved by --prune' '
test_cmp expect actual
'
+test_expect_success 'branchname D/F conflict rejected with targeted error message' '
+ git clone . df-conflict-error &&
+ git branch dir_conflict &&
+ (
+ cd df-conflict-error &&
+ git update-ref refs/remotes/origin/dir_conflict/file HEAD &&
+ test_must_fail git fetch 2>err &&
+ test_grep "error: some local refs could not be updated; try running" err &&
+ test_grep " ${SQ}git remote prune origin${SQ} to remove any old, conflicting branches" err &&
+ git pack-refs --all &&
+ test_must_fail git fetch 2>err-packed &&
+ test_grep "error: some local refs could not be updated; try running" err-packed &&
+ test_grep " ${SQ}git remote prune origin${SQ} to remove any old, conflicting branches" err-packed
+ )
+'
+
test_expect_success 'fetching a one-level ref works' '
test_commit extra &&
git reset --hard HEAD^ &&
@@ -1252,6 +1268,30 @@ EOF
test_cmp fatal-expect fatal-actual
'
+test_expect_success SYMLINKS 'clone does not get confused by a D/F conflict' '
+ git init df-conflict &&
+ (
+ cd df-conflict &&
+ ln -s .git a &&
+ git add a &&
+ test_tick &&
+ git commit -m symlink &&
+ test_commit a- &&
+ rm a &&
+ mkdir -p a/hooks &&
+ write_script a/hooks/post-checkout <<-EOF &&
+ echo WHOOPSIE >&2
+ echo whoopsie >"$TRASH_DIRECTORY"/whoops
+ EOF
+ git add a/hooks/post-checkout &&
+ test_tick &&
+ git commit -m post-checkout
+ ) &&
+ git clone df-conflict clone 2>err &&
+ test_grep ! WHOOPS err &&
+ test_path_is_missing whoops
+'
+
. "$TEST_DIRECTORY"/lib-httpd.sh
start_httpd
diff --git a/t/t5512-ls-remote.sh b/t/t5512-ls-remote.sh
index 5dbe107ce8..42e77eb5a9 100755
--- a/t/t5512-ls-remote.sh
+++ b/t/t5512-ls-remote.sh
@@ -47,6 +47,7 @@ test_expect_success setup '
git show-ref -d >refs &&
sed -e "s/ / /" refs >>expected.all &&
+ grep refs/heads/ expected.all >expected.branches &&
git remote add self "$(pwd)/.git" &&
git remote add self2 "."
'
@@ -71,6 +72,27 @@ test_expect_success 'ls-remote self' '
test_cmp expected.all actual
'
+test_expect_success 'ls-remote --branches self' '
+ git ls-remote --branches self >actual &&
+ test_cmp expected.branches actual &&
+ git ls-remote -b self >actual &&
+ test_cmp expected.branches actual
+'
+
+test_expect_success 'ls-remote -h is deprecated w/o warning' '
+ git ls-remote -h self >actual 2>warning &&
+ test_cmp expected.branches actual &&
+ test_grep ! deprecated warning
+'
+
+test_expect_success 'ls-remote --heads is deprecated and hidden w/o warning' '
+ test_expect_code 129 git ls-remote -h >short-help &&
+ test_grep ! -e --head short-help &&
+ git ls-remote --heads self >actual 2>warning &&
+ test_cmp expected.branches actual &&
+ test_grep ! deprecated warning
+'
+
test_expect_success 'ls-remote --sort="version:refname" --tags self' '
generate_references \
refs/tags/mark \
@@ -275,7 +297,7 @@ test_expect_success 'ls-remote with filtered symref (refname)' '
test_cmp expect actual
'
-test_expect_success 'ls-remote with filtered symref (--heads)' '
+test_expect_success 'ls-remote with filtered symref (--branches)' '
git symbolic-ref refs/heads/foo refs/tags/mark &&
cat >expect.v2 <<-EOF &&
ref: refs/tags/mark refs/heads/foo
@@ -283,9 +305,9 @@ test_expect_success 'ls-remote with filtered symref (--heads)' '
$rev refs/heads/main
EOF
grep -v "^ref: refs/tags/" <expect.v2 >expect.v0 &&
- git -c protocol.version=0 ls-remote --symref --heads . >actual.v0 &&
+ git -c protocol.version=0 ls-remote --symref --branches . >actual.v0 &&
test_cmp expect.v0 actual.v0 &&
- git -c protocol.version=2 ls-remote --symref --heads . >actual.v2 &&
+ git -c protocol.version=2 ls-remote --symref --branches . >actual.v2 &&
test_cmp expect.v2 actual.v2
'
@@ -335,9 +357,9 @@ test_expect_success 'ls-remote patterns work with all protocol versions' '
test_expect_success 'ls-remote prefixes work with all protocol versions' '
git for-each-ref --format="%(objectname) %(refname)" \
refs/heads/ refs/tags/ >expect &&
- git -c protocol.version=0 ls-remote --heads --tags . >actual.v0 &&
+ git -c protocol.version=0 ls-remote --branches --tags . >actual.v0 &&
test_cmp expect actual.v0 &&
- git -c protocol.version=2 ls-remote --heads --tags . >actual.v2 &&
+ git -c protocol.version=2 ls-remote --branches --tags . >actual.v2 &&
test_cmp expect actual.v2
'
diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh
index 2e7c0e1648..9d693eb57f 100755
--- a/t/t5516-fetch-push.sh
+++ b/t/t5516-fetch-push.sh
@@ -230,6 +230,16 @@ test_expect_success 'push with negotiation proceeds anyway even if negotiation f
test_grep "push negotiation failed" err
'
+test_expect_success 'push deletion with negotiation' '
+ mk_empty testrepo &&
+ git push testrepo $the_first_commit:refs/heads/master &&
+ git -c push.negotiate=1 push testrepo \
+ :master $the_first_commit:refs/heads/next 2>errors-2 &&
+ test_grep ! "negotiate-only needs one or " errors-2 &&
+ git -c push.negotiate=1 push testrepo :next 2>errors-1 &&
+ test_grep ! "negotiate-only needs one or " errors-1
+'
+
test_expect_success 'push with negotiation does not attempt to fetch submodules' '
mk_empty submodule_upstream &&
test_commit -C submodule_upstream submodule_commit &&
diff --git a/t/t5529-push-errors.sh b/t/t5529-push-errors.sh
index 0247137cb3..17d7257892 100755
--- a/t/t5529-push-errors.sh
+++ b/t/t5529-push-errors.sh
@@ -2,6 +2,9 @@
test_description='detect some push errors early (before contacting remote)'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
@@ -38,6 +41,20 @@ test_expect_success 'detect missing sha1 expressions early' '
test_cmp expect rp-ran
'
+# We use an existing local_ref, since it follows a different flow in
+# 'builtin/push.c:set_refspecs()' and we want to test that regression.
+test_expect_success 'detect empty remote with existing local ref' '
+ test_must_fail git push "" main 2> stderr &&
+ grep "fatal: bad repository ${SQ}${SQ}" stderr
+'
+
+# While similar to the previous test, here we want to ensure that
+# even targeted refspecs are handled.
+test_expect_success 'detect empty remote with targeted refspec' '
+ test_must_fail git push "" HEAD:refs/heads/main 2> stderr &&
+ grep "fatal: bad repository ${SQ}${SQ}" stderr
+'
+
test_expect_success 'detect ambiguous refs early' '
git branch foo &&
git tag foo &&
diff --git a/t/t5550-http-fetch-dumb.sh b/t/t5550-http-fetch-dumb.sh
index 4c3b32785d..ea8e48f627 100755
--- a/t/t5550-http-fetch-dumb.sh
+++ b/t/t5550-http-fetch-dumb.sh
@@ -25,6 +25,12 @@ test_expect_success 'setup repository' '
git commit -m two
'
+test_expect_success 'packfile without repository does not crash' '
+ echo "fatal: not a git repository" >expect &&
+ test_must_fail nongit git http-fetch --packfile=abc 2>err &&
+ test_cmp expect err
+'
+
setup_post_update_server_info_hook () {
test_hook --setup -C "$1" post-update <<-\EOF &&
exec git update-server-info
@@ -55,6 +61,21 @@ test_expect_success 'list refs from outside any repository' '
test_cmp expect actual
'
+
+test_expect_success 'list detached HEAD from outside any repository' '
+ git clone --mirror "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" \
+ "$HTTPD_DOCUMENT_ROOT_PATH/repo-detached.git" &&
+ git -C "$HTTPD_DOCUMENT_ROOT_PATH/repo-detached.git" \
+ update-ref --no-deref HEAD refs/heads/main &&
+ git -C "$HTTPD_DOCUMENT_ROOT_PATH/repo-detached.git" update-server-info &&
+ cat >expect <<-EOF &&
+ $(git rev-parse main) HEAD
+ $(git rev-parse main) refs/heads/main
+ EOF
+ nongit git ls-remote "$HTTPD_URL/dumb/repo-detached.git" >actual &&
+ test_cmp expect actual
+'
+
test_expect_success 'create password-protected repository' '
mkdir -p "$HTTPD_DOCUMENT_ROOT_PATH/auth/dumb/" &&
cp -Rf "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" \
diff --git a/t/t5551-http-fetch-smart.sh b/t/t5551-http-fetch-smart.sh
index a623a1058c..7b5ab0eae1 100755
--- a/t/t5551-http-fetch-smart.sh
+++ b/t/t5551-http-fetch-smart.sh
@@ -643,7 +643,6 @@ test_expect_success 'clone empty SHA-256 repository with protocol v0' '
test_expect_success 'passing hostname resolution information works' '
BOGUS_HOST=gitbogusexamplehost.invalid &&
BOGUS_HTTPD_URL=$HTTPD_PROTO://$BOGUS_HOST:$LIB_HTTPD_PORT &&
- test_must_fail git ls-remote "$BOGUS_HTTPD_URL/smart/repo.git" >/dev/null &&
git -c "http.curloptResolve=$BOGUS_HOST:$LIB_HTTPD_PORT:127.0.0.1" ls-remote "$BOGUS_HTTPD_URL/smart/repo.git" >/dev/null
'
diff --git a/t/t5553-set-upstream.sh b/t/t5553-set-upstream.sh
index 48050162c2..70e3376d31 100755
--- a/t/t5553-set-upstream.sh
+++ b/t/t5553-set-upstream.sh
@@ -73,10 +73,10 @@ test_expect_success 'fetch --set-upstream main:other does not set the branch oth
check_config_missing other2
'
-test_expect_success 'fetch --set-upstream http://nosuchdomain.example.com fails with invalid url' '
+test_expect_success 'fetch --set-upstream ./does-not-exist fails with invalid url' '
# main explicitly not cleared, we check that it is not touched from previous value
clear_config other other2 &&
- test_must_fail git fetch --set-upstream http://nosuchdomain.example.com &&
+ test_must_fail git fetch --set-upstream ./does-not-exist &&
check_config main upstream refs/heads/other &&
check_config_missing other &&
check_config_missing other2
@@ -143,10 +143,10 @@ test_expect_success 'pull --set-upstream upstream tag does not set the tag' '
check_config_missing three
'
-test_expect_success 'pull --set-upstream http://nosuchdomain.example.com fails with invalid url' '
+test_expect_success 'pull --set-upstream ./does-not-exist fails with invalid url' '
# main explicitly not cleared, we check that it is not touched from previous value
clear_config other other2 three &&
- test_must_fail git pull --set-upstream http://nosuchdomain.example.com &&
+ test_must_fail git pull --set-upstream ./does-not-exist &&
check_config main upstream refs/heads/other &&
check_config_missing other &&
check_config_missing other2 &&
diff --git a/t/t5558-clone-bundle-uri.sh b/t/t5558-clone-bundle-uri.sh
index 1ca5f745e7..cd05321e17 100755
--- a/t/t5558-clone-bundle-uri.sh
+++ b/t/t5558-clone-bundle-uri.sh
@@ -3,6 +3,7 @@
test_description='test fetching bundles with --bundle-uri'
. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-bundle.sh
test_expect_success 'fail to clone from non-existent file' '
test_when_finished rm -rf test &&
@@ -19,10 +20,39 @@ test_expect_success 'fail to clone from non-bundle file' '
test_expect_success 'create bundle' '
git init clone-from &&
- git -C clone-from checkout -b topic &&
- test_commit -C clone-from A &&
- test_commit -C clone-from B &&
- git -C clone-from bundle create B.bundle topic
+ (
+ cd clone-from &&
+ git checkout -b topic &&
+
+ test_commit A &&
+ git bundle create A.bundle topic &&
+
+ test_commit B &&
+ git bundle create B.bundle topic &&
+
+ # Create a bundle with reference pointing to non-existent object.
+ commit_a=$(git rev-parse A) &&
+ commit_b=$(git rev-parse B) &&
+ sed -e "/^$/q" -e "s/$commit_a /$commit_b /" \
+ <A.bundle >bad-header.bundle &&
+ convert_bundle_to_pack \
+ <A.bundle >>bad-header.bundle &&
+
+ tree_b=$(git rev-parse B^{tree}) &&
+ cat >data <<-EOF &&
+ tree $tree_b
+ parent $commit_b
+ author A U Thor
+ committer A U Thor
+
+ commit: this is a commit with bad emails
+
+ EOF
+ bad_commit=$(git hash-object --literally -t commit -w --stdin <data) &&
+ git branch bad $bad_commit &&
+ git bundle create bad-object.bundle bad &&
+ git update-ref -d refs/heads/bad
+ )
'
test_expect_success 'clone with path bundle' '
@@ -33,6 +63,33 @@ test_expect_success 'clone with path bundle' '
test_cmp expect actual
'
+test_expect_success 'clone with bundle that has bad header' '
+ # Write bundle ref fails, but clone can still proceed.
+ git clone --bundle-uri="clone-from/bad-header.bundle" \
+ clone-from clone-bad-header 2>err &&
+ commit_b=$(git -C clone-from rev-parse B) &&
+ test_grep "trying to write ref '\''refs/bundles/topic'\'' with nonexistent object $commit_b" err &&
+ git -C clone-bad-header for-each-ref --format="%(refname)" >refs &&
+ test_grep ! "refs/bundles/" refs
+'
+
+test_expect_success 'clone with bundle that has bad object' '
+ # Unbundle succeeds if no fsckObjects configured.
+ git clone --bundle-uri="clone-from/bad-object.bundle" \
+ clone-from clone-bad-object-no-fsck &&
+ git -C clone-bad-object-no-fsck for-each-ref --format="%(refname)" >refs &&
+ grep "refs/bundles/" refs >actual &&
+ test_write_lines refs/bundles/bad >expect &&
+ test_cmp expect actual &&
+
+ # Unbundle fails with fsckObjects set true, but clone can still proceed.
+ git -c fetch.fsckObjects=true clone --bundle-uri="clone-from/bad-object.bundle" \
+ clone-from clone-bad-object-fsck 2>err &&
+ test_grep "missingEmail" err &&
+ git -C clone-bad-object-fsck for-each-ref --format="%(refname)" >refs &&
+ test_grep ! "refs/bundles/" refs
+'
+
test_expect_success 'clone with path bundle and non-default hash' '
test_when_finished "rm -rf clone-path-non-default-hash" &&
GIT_DEFAULT_HASH=sha256 git clone --bundle-uri="clone-from/B.bundle" \
@@ -259,6 +316,128 @@ test_expect_success 'clone bundle list (file, any mode, all failures)' '
! grep "refs/bundles/" refs
'
+test_expect_success 'negotiation: bundle with part of wanted commits' '
+ test_when_finished "rm -f trace*.txt" &&
+ GIT_TRACE_PACKET="$(pwd)/trace-packet.txt" \
+ git clone --no-local --bundle-uri="clone-from/A.bundle" \
+ clone-from nego-bundle-part &&
+ git -C nego-bundle-part for-each-ref --format="%(refname)" >refs &&
+ grep "refs/bundles/" refs >actual &&
+ test_write_lines refs/bundles/topic >expect &&
+ test_cmp expect actual &&
+ # Ensure that refs/bundles/topic are sent as "have".
+ tip=$(git -C clone-from rev-parse A) &&
+ test_grep "clone> have $tip" trace-packet.txt
+'
+
+test_expect_success 'negotiation: bundle with all wanted commits' '
+ test_when_finished "rm -f trace*.txt" &&
+ GIT_TRACE_PACKET="$(pwd)/trace-packet.txt" \
+ git clone --no-local --single-branch --branch=topic --no-tags \
+ --bundle-uri="clone-from/B.bundle" \
+ clone-from nego-bundle-all &&
+ git -C nego-bundle-all for-each-ref --format="%(refname)" >refs &&
+ grep "refs/bundles/" refs >actual &&
+ test_write_lines refs/bundles/topic >expect &&
+ test_cmp expect actual &&
+ # We already have all needed commits so no "want" needed.
+ test_grep ! "clone> want " trace-packet.txt
+'
+
+test_expect_success 'negotiation: bundle list (no heuristic)' '
+ test_when_finished "rm -f trace*.txt" &&
+ cat >bundle-list <<-EOF &&
+ [bundle]
+ version = 1
+ mode = all
+
+ [bundle "bundle-1"]
+ uri = file://$(pwd)/clone-from/bundle-1.bundle
+
+ [bundle "bundle-2"]
+ uri = file://$(pwd)/clone-from/bundle-2.bundle
+ EOF
+
+ GIT_TRACE_PACKET="$(pwd)/trace-packet.txt" \
+ git clone --no-local --bundle-uri="file://$(pwd)/bundle-list" \
+ clone-from nego-bundle-list-no-heuristic &&
+
+ git -C nego-bundle-list-no-heuristic for-each-ref --format="%(refname)" >refs &&
+ grep "refs/bundles/" refs >actual &&
+ cat >expect <<-\EOF &&
+ refs/bundles/base
+ refs/bundles/left
+ EOF
+ test_cmp expect actual &&
+ tip=$(git -C nego-bundle-list-no-heuristic rev-parse refs/bundles/left) &&
+ test_grep "clone> have $tip" trace-packet.txt
+'
+
+test_expect_success 'negotiation: bundle list (creationToken)' '
+ test_when_finished "rm -f trace*.txt" &&
+ cat >bundle-list <<-EOF &&
+ [bundle]
+ version = 1
+ mode = all
+ heuristic = creationToken
+
+ [bundle "bundle-1"]
+ uri = file://$(pwd)/clone-from/bundle-1.bundle
+ creationToken = 1
+
+ [bundle "bundle-2"]
+ uri = file://$(pwd)/clone-from/bundle-2.bundle
+ creationToken = 2
+ EOF
+
+ GIT_TRACE_PACKET="$(pwd)/trace-packet.txt" \
+ git clone --no-local --bundle-uri="file://$(pwd)/bundle-list" \
+ clone-from nego-bundle-list-heuristic &&
+
+ git -C nego-bundle-list-heuristic for-each-ref --format="%(refname)" >refs &&
+ grep "refs/bundles/" refs >actual &&
+ cat >expect <<-\EOF &&
+ refs/bundles/base
+ refs/bundles/left
+ EOF
+ test_cmp expect actual &&
+ tip=$(git -C nego-bundle-list-heuristic rev-parse refs/bundles/left) &&
+ test_grep "clone> have $tip" trace-packet.txt
+'
+
+test_expect_success 'negotiation: bundle list with all wanted commits' '
+ test_when_finished "rm -f trace*.txt" &&
+ cat >bundle-list <<-EOF &&
+ [bundle]
+ version = 1
+ mode = all
+ heuristic = creationToken
+
+ [bundle "bundle-1"]
+ uri = file://$(pwd)/clone-from/bundle-1.bundle
+ creationToken = 1
+
+ [bundle "bundle-2"]
+ uri = file://$(pwd)/clone-from/bundle-2.bundle
+ creationToken = 2
+ EOF
+
+ GIT_TRACE_PACKET="$(pwd)/trace-packet.txt" \
+ git clone --no-local --single-branch --branch=left --no-tags \
+ --bundle-uri="file://$(pwd)/bundle-list" \
+ clone-from nego-bundle-list-all &&
+
+ git -C nego-bundle-list-all for-each-ref --format="%(refname)" >refs &&
+ grep "refs/bundles/" refs >actual &&
+ cat >expect <<-\EOF &&
+ refs/bundles/base
+ refs/bundles/left
+ EOF
+ test_cmp expect actual &&
+ # We already have all needed commits so no "want" needed.
+ test_grep ! "clone> want " trace-packet.txt
+'
+
#########################################################################
# HTTP tests begin here
diff --git a/t/t5563-simple-http-auth.sh b/t/t5563-simple-http-auth.sh
index ab8a721ccc..ba03f6a09f 100755
--- a/t/t5563-simple-http-auth.sh
+++ b/t/t5563-simple-http-auth.sh
@@ -2,6 +2,7 @@
test_description='test http auth header and credential helper interop'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-httpd.sh
@@ -21,9 +22,17 @@ test_expect_success 'setup_credential_helper' '
CREDENTIAL_HELPER="$TRASH_DIRECTORY/bin/git-credential-test-helper" &&
write_script "$CREDENTIAL_HELPER" <<-\EOF
cmd=$1
- teefile=$cmd-query.cred
+ teefile=$cmd-query-temp.cred
catfile=$cmd-reply.cred
sed -n -e "/^$/q" -e "p" >>$teefile
+ state=$(sed -ne "s/^state\[\]=helper://p" "$teefile")
+ if test -z "$state"
+ then
+ mv "$teefile" "$cmd-query.cred"
+ else
+ mv "$teefile" "$cmd-query-$state.cred"
+ catfile="$cmd-reply-$state.cred"
+ fi
if test "$cmd" = "get"
then
cat $catfile
@@ -32,13 +41,15 @@ test_expect_success 'setup_credential_helper' '
'
set_credential_reply () {
- cat >"$TRASH_DIRECTORY/$1-reply.cred"
+ local suffix="$(test -n "$2" && echo "-$2")"
+ cat >"$TRASH_DIRECTORY/$1-reply$suffix.cred"
}
expect_credential_query () {
- cat >"$TRASH_DIRECTORY/$1-expect.cred" &&
- test_cmp "$TRASH_DIRECTORY/$1-expect.cred" \
- "$TRASH_DIRECTORY/$1-query.cred"
+ local suffix="$(test -n "$2" && echo "-$2")"
+ cat >"$TRASH_DIRECTORY/$1-expect$suffix.cred" &&
+ test_cmp "$TRASH_DIRECTORY/$1-expect$suffix.cred" \
+ "$TRASH_DIRECTORY/$1-query$suffix.cred"
}
per_test_cleanup () {
@@ -63,17 +74,20 @@ test_expect_success 'access using basic auth' '
# Basic base64(alice:secret-passwd)
cat >"$HTTPD_ROOT_PATH/custom-auth.valid" <<-EOF &&
- Basic YWxpY2U6c2VjcmV0LXBhc3N3ZA==
+ id=1 creds=Basic YWxpY2U6c2VjcmV0LXBhc3N3ZA==
EOF
cat >"$HTTPD_ROOT_PATH/custom-auth.challenge" <<-EOF &&
- WWW-Authenticate: Basic realm="example.com"
+ id=1 status=200
+ id=default response=WWW-Authenticate: Basic realm="example.com"
EOF
test_config_global credential.helper test-helper &&
git ls-remote "$HTTPD_URL/custom_auth/repo.git" &&
expect_credential_query get <<-EOF &&
+ capability[]=authtype
+ capability[]=state
protocol=http
host=$HTTPD_DEST
wwwauth[]=Basic realm="example.com"
@@ -87,6 +101,45 @@ test_expect_success 'access using basic auth' '
EOF
'
+test_expect_success 'access using basic auth via authtype' '
+ test_when_finished "per_test_cleanup" &&
+
+ set_credential_reply get <<-EOF &&
+ capability[]=authtype
+ authtype=Basic
+ credential=YWxpY2U6c2VjcmV0LXBhc3N3ZA==
+ EOF
+
+ # Basic base64(alice:secret-passwd)
+ cat >"$HTTPD_ROOT_PATH/custom-auth.valid" <<-EOF &&
+ id=1 creds=Basic YWxpY2U6c2VjcmV0LXBhc3N3ZA==
+ EOF
+
+ cat >"$HTTPD_ROOT_PATH/custom-auth.challenge" <<-EOF &&
+ id=1 status=200
+ id=default response=WWW-Authenticate: Basic realm="example.com"
+ EOF
+
+ test_config_global credential.helper test-helper &&
+ GIT_CURL_VERBOSE=1 git ls-remote "$HTTPD_URL/custom_auth/repo.git" &&
+
+ expect_credential_query get <<-EOF &&
+ capability[]=authtype
+ capability[]=state
+ protocol=http
+ host=$HTTPD_DEST
+ wwwauth[]=Basic realm="example.com"
+ EOF
+
+ expect_credential_query store <<-EOF
+ capability[]=authtype
+ authtype=Basic
+ credential=YWxpY2U6c2VjcmV0LXBhc3N3ZA==
+ protocol=http
+ host=$HTTPD_DEST
+ EOF
+'
+
test_expect_success 'access using basic auth invalid credentials' '
test_when_finished "per_test_cleanup" &&
@@ -97,17 +150,20 @@ test_expect_success 'access using basic auth invalid credentials' '
# Basic base64(alice:secret-passwd)
cat >"$HTTPD_ROOT_PATH/custom-auth.valid" <<-EOF &&
- Basic YWxpY2U6c2VjcmV0LXBhc3N3ZA==
+ id=1 creds=Basic YWxpY2U6c2VjcmV0LXBhc3N3ZA==
EOF
cat >"$HTTPD_ROOT_PATH/custom-auth.challenge" <<-EOF &&
- WWW-Authenticate: Basic realm="example.com"
+ id=1 status=200
+ id=default response=WWW-Authenticate: Basic realm="example.com"
EOF
test_config_global credential.helper test-helper &&
test_must_fail git ls-remote "$HTTPD_URL/custom_auth/repo.git" &&
expect_credential_query get <<-EOF &&
+ capability[]=authtype
+ capability[]=state
protocol=http
host=$HTTPD_DEST
wwwauth[]=Basic realm="example.com"
@@ -122,6 +178,122 @@ test_expect_success 'access using basic auth invalid credentials' '
EOF
'
+test_expect_success 'access using basic proactive auth' '
+ test_when_finished "per_test_cleanup" &&
+
+ set_credential_reply get <<-EOF &&
+ username=alice
+ password=secret-passwd
+ EOF
+
+ # Basic base64(alice:secret-passwd)
+ cat >"$HTTPD_ROOT_PATH/custom-auth.valid" <<-EOF &&
+ id=1 creds=Basic YWxpY2U6c2VjcmV0LXBhc3N3ZA==
+ EOF
+
+ cat >"$HTTPD_ROOT_PATH/custom-auth.challenge" <<-EOF &&
+ id=1 status=200
+ id=default status=403
+ EOF
+
+ test_config_global credential.helper test-helper &&
+ test_config_global http.proactiveAuth basic &&
+ git ls-remote "$HTTPD_URL/custom_auth/repo.git" &&
+
+ expect_credential_query get <<-EOF &&
+ capability[]=authtype
+ capability[]=state
+ protocol=http
+ host=$HTTPD_DEST
+ wwwauth[]=Basic
+ EOF
+
+ expect_credential_query store <<-EOF
+ protocol=http
+ host=$HTTPD_DEST
+ username=alice
+ password=secret-passwd
+ EOF
+'
+
+test_expect_success 'access using auto proactive auth with basic default' '
+ test_when_finished "per_test_cleanup" &&
+
+ set_credential_reply get <<-EOF &&
+ username=alice
+ password=secret-passwd
+ EOF
+
+ # Basic base64(alice:secret-passwd)
+ cat >"$HTTPD_ROOT_PATH/custom-auth.valid" <<-EOF &&
+ id=1 creds=Basic YWxpY2U6c2VjcmV0LXBhc3N3ZA==
+ EOF
+
+ cat >"$HTTPD_ROOT_PATH/custom-auth.challenge" <<-EOF &&
+ id=1 status=200
+ id=default status=403
+ EOF
+
+ test_config_global credential.helper test-helper &&
+ test_config_global http.proactiveAuth auto &&
+ git ls-remote "$HTTPD_URL/custom_auth/repo.git" &&
+
+ expect_credential_query get <<-EOF &&
+ capability[]=authtype
+ capability[]=state
+ protocol=http
+ host=$HTTPD_DEST
+ EOF
+
+ expect_credential_query store <<-EOF
+ protocol=http
+ host=$HTTPD_DEST
+ username=alice
+ password=secret-passwd
+ EOF
+'
+
+test_expect_success 'access using auto proactive auth with authtype from credential helper' '
+ test_when_finished "per_test_cleanup" &&
+
+ set_credential_reply get <<-EOF &&
+ capability[]=authtype
+ authtype=Bearer
+ credential=YS1naXQtdG9rZW4=
+ EOF
+
+ # Basic base64(a-git-token)
+ cat >"$HTTPD_ROOT_PATH/custom-auth.valid" <<-EOF &&
+ id=1 creds=Bearer YS1naXQtdG9rZW4=
+ EOF
+
+ CHALLENGE="$HTTPD_ROOT_PATH/custom-auth.challenge" &&
+
+ cat >"$HTTPD_ROOT_PATH/custom-auth.challenge" <<-EOF &&
+ id=1 status=200
+ id=default status=403
+ EOF
+
+ test_config_global credential.helper test-helper &&
+ test_config_global http.proactiveAuth auto &&
+ git ls-remote "$HTTPD_URL/custom_auth/repo.git" &&
+
+ expect_credential_query get <<-EOF &&
+ capability[]=authtype
+ capability[]=state
+ protocol=http
+ host=$HTTPD_DEST
+ EOF
+
+ expect_credential_query store <<-EOF
+ capability[]=authtype
+ authtype=Bearer
+ credential=YS1naXQtdG9rZW4=
+ protocol=http
+ host=$HTTPD_DEST
+ EOF
+'
+
test_expect_success 'access using basic auth with extra challenges' '
test_when_finished "per_test_cleanup" &&
@@ -132,19 +304,22 @@ test_expect_success 'access using basic auth with extra challenges' '
# Basic base64(alice:secret-passwd)
cat >"$HTTPD_ROOT_PATH/custom-auth.valid" <<-EOF &&
- Basic YWxpY2U6c2VjcmV0LXBhc3N3ZA==
+ id=1 creds=Basic YWxpY2U6c2VjcmV0LXBhc3N3ZA==
EOF
cat >"$HTTPD_ROOT_PATH/custom-auth.challenge" <<-EOF &&
- WWW-Authenticate: FooBar param1="value1" param2="value2"
- WWW-Authenticate: Bearer authorize_uri="id.example.com" p=1 q=0
- WWW-Authenticate: Basic realm="example.com"
+ id=1 status=200
+ id=default response=WWW-Authenticate: FooBar param1="value1" param2="value2"
+ id=default response=WWW-Authenticate: Bearer authorize_uri="id.example.com" p=1 q=0
+ id=default response=WWW-Authenticate: Basic realm="example.com"
EOF
test_config_global credential.helper test-helper &&
git ls-remote "$HTTPD_URL/custom_auth/repo.git" &&
expect_credential_query get <<-EOF &&
+ capability[]=authtype
+ capability[]=state
protocol=http
host=$HTTPD_DEST
wwwauth[]=FooBar param1="value1" param2="value2"
@@ -170,19 +345,22 @@ test_expect_success 'access using basic auth mixed-case wwwauth header name' '
# Basic base64(alice:secret-passwd)
cat >"$HTTPD_ROOT_PATH/custom-auth.valid" <<-EOF &&
- Basic YWxpY2U6c2VjcmV0LXBhc3N3ZA==
+ id=1 creds=Basic YWxpY2U6c2VjcmV0LXBhc3N3ZA==
EOF
cat >"$HTTPD_ROOT_PATH/custom-auth.challenge" <<-EOF &&
- www-authenticate: foobar param1="value1" param2="value2"
- WWW-AUTHENTICATE: BEARER authorize_uri="id.example.com" p=1 q=0
- WwW-aUtHeNtIcAtE: baSiC realm="example.com"
+ id=1 status=200
+ id=default response=www-authenticate: foobar param1="value1" param2="value2"
+ id=default response=WWW-AUTHENTICATE: BEARER authorize_uri="id.example.com" p=1 q=0
+ id=default response=WwW-aUtHeNtIcAtE: baSiC realm="example.com"
EOF
test_config_global credential.helper test-helper &&
git ls-remote "$HTTPD_URL/custom_auth/repo.git" &&
expect_credential_query get <<-EOF &&
+ capability[]=authtype
+ capability[]=state
protocol=http
host=$HTTPD_DEST
wwwauth[]=foobar param1="value1" param2="value2"
@@ -208,24 +386,27 @@ test_expect_success 'access using basic auth with wwwauth header continuations'
# Basic base64(alice:secret-passwd)
cat >"$HTTPD_ROOT_PATH/custom-auth.valid" <<-EOF &&
- Basic YWxpY2U6c2VjcmV0LXBhc3N3ZA==
+ id=1 creds=Basic YWxpY2U6c2VjcmV0LXBhc3N3ZA==
EOF
# Note that leading and trailing whitespace is important to correctly
# simulate a continuation/folded header.
cat >"$HTTPD_ROOT_PATH/custom-auth.challenge" <<-EOF &&
- WWW-Authenticate: FooBar param1="value1"
- param2="value2"
- WWW-Authenticate: Bearer authorize_uri="id.example.com"
- p=1
- q=0
- WWW-Authenticate: Basic realm="example.com"
+ id=1 status=200
+ id=default response=WWW-Authenticate: FooBar param1="value1"
+ id=default response= param2="value2"
+ id=default response=WWW-Authenticate: Bearer authorize_uri="id.example.com"
+ id=default response= p=1
+ id=default response= q=0
+ id=default response=WWW-Authenticate: Basic realm="example.com"
EOF
test_config_global credential.helper test-helper &&
git ls-remote "$HTTPD_URL/custom_auth/repo.git" &&
expect_credential_query get <<-EOF &&
+ capability[]=authtype
+ capability[]=state
protocol=http
host=$HTTPD_DEST
wwwauth[]=FooBar param1="value1" param2="value2"
@@ -251,26 +432,29 @@ test_expect_success 'access using basic auth with wwwauth header empty continuat
# Basic base64(alice:secret-passwd)
cat >"$HTTPD_ROOT_PATH/custom-auth.valid" <<-EOF &&
- Basic YWxpY2U6c2VjcmV0LXBhc3N3ZA==
+ id=1 creds=Basic YWxpY2U6c2VjcmV0LXBhc3N3ZA==
EOF
CHALLENGE="$HTTPD_ROOT_PATH/custom-auth.challenge" &&
# Note that leading and trailing whitespace is important to correctly
# simulate a continuation/folded header.
- printf "WWW-Authenticate: FooBar param1=\"value1\"\r\n" >"$CHALLENGE" &&
- printf " \r\n" >>"$CHALLENGE" &&
- printf " param2=\"value2\"\r\n" >>"$CHALLENGE" &&
- printf "WWW-Authenticate: Bearer authorize_uri=\"id.example.com\"\r\n" >>"$CHALLENGE" &&
- printf " p=1\r\n" >>"$CHALLENGE" &&
- printf " \r\n" >>"$CHALLENGE" &&
- printf " q=0\r\n" >>"$CHALLENGE" &&
- printf "WWW-Authenticate: Basic realm=\"example.com\"\r\n" >>"$CHALLENGE" &&
+ printf "id=1 status=200\n" >"$CHALLENGE" &&
+ printf "id=default response=WWW-Authenticate: FooBar param1=\"value1\"\r\n" >>"$CHALLENGE" &&
+ printf "id=default response= \r\n" >>"$CHALLENGE" &&
+ printf "id=default response= param2=\"value2\"\r\n" >>"$CHALLENGE" &&
+ printf "id=default response=WWW-Authenticate: Bearer authorize_uri=\"id.example.com\"\r\n" >>"$CHALLENGE" &&
+ printf "id=default response= p=1\r\n" >>"$CHALLENGE" &&
+ printf "id=default response= \r\n" >>"$CHALLENGE" &&
+ printf "id=default response= q=0\r\n" >>"$CHALLENGE" &&
+ printf "id=default response=WWW-Authenticate: Basic realm=\"example.com\"\r\n" >>"$CHALLENGE" &&
test_config_global credential.helper test-helper &&
git ls-remote "$HTTPD_URL/custom_auth/repo.git" &&
expect_credential_query get <<-EOF &&
+ capability[]=authtype
+ capability[]=state
protocol=http
host=$HTTPD_DEST
wwwauth[]=FooBar param1="value1" param2="value2"
@@ -296,22 +480,25 @@ test_expect_success 'access using basic auth with wwwauth header mixed line-endi
# Basic base64(alice:secret-passwd)
cat >"$HTTPD_ROOT_PATH/custom-auth.valid" <<-EOF &&
- Basic YWxpY2U6c2VjcmV0LXBhc3N3ZA==
+ id=1 creds=Basic YWxpY2U6c2VjcmV0LXBhc3N3ZA==
EOF
CHALLENGE="$HTTPD_ROOT_PATH/custom-auth.challenge" &&
# Note that leading and trailing whitespace is important to correctly
# simulate a continuation/folded header.
- printf "WWW-Authenticate: FooBar param1=\"value1\"\r\n" >"$CHALLENGE" &&
- printf " \r\n" >>"$CHALLENGE" &&
- printf "\tparam2=\"value2\"\r\n" >>"$CHALLENGE" &&
- printf "WWW-Authenticate: Basic realm=\"example.com\"" >>"$CHALLENGE" &&
+ printf "id=1 status=200\n" >"$CHALLENGE" &&
+ printf "id=default response=WWW-Authenticate: FooBar param1=\"value1\"\r\n" >>"$CHALLENGE" &&
+ printf "id=default response= \r\n" >>"$CHALLENGE" &&
+ printf "id=default response=\tparam2=\"value2\"\r\n" >>"$CHALLENGE" &&
+ printf "id=default response=WWW-Authenticate: Basic realm=\"example.com\"" >>"$CHALLENGE" &&
test_config_global credential.helper test-helper &&
git ls-remote "$HTTPD_URL/custom_auth/repo.git" &&
expect_credential_query get <<-EOF &&
+ capability[]=authtype
+ capability[]=state
protocol=http
host=$HTTPD_DEST
wwwauth[]=FooBar param1="value1" param2="value2"
@@ -326,4 +513,166 @@ test_expect_success 'access using basic auth with wwwauth header mixed line-endi
EOF
'
+test_expect_success 'access using bearer auth' '
+ test_when_finished "per_test_cleanup" &&
+
+ set_credential_reply get <<-EOF &&
+ capability[]=authtype
+ authtype=Bearer
+ credential=YS1naXQtdG9rZW4=
+ EOF
+
+ # Basic base64(a-git-token)
+ cat >"$HTTPD_ROOT_PATH/custom-auth.valid" <<-EOF &&
+ id=1 creds=Bearer YS1naXQtdG9rZW4=
+ EOF
+
+ CHALLENGE="$HTTPD_ROOT_PATH/custom-auth.challenge" &&
+
+ cat >"$HTTPD_ROOT_PATH/custom-auth.challenge" <<-EOF &&
+ id=1 status=200
+ id=default response=WWW-Authenticate: FooBar param1="value1" param2="value2"
+ id=default response=WWW-Authenticate: Bearer authorize_uri="id.example.com" p=1 q=0
+ id=default response=WWW-Authenticate: Basic realm="example.com"
+ EOF
+
+ test_config_global credential.helper test-helper &&
+ git ls-remote "$HTTPD_URL/custom_auth/repo.git" &&
+
+ expect_credential_query get <<-EOF &&
+ capability[]=authtype
+ capability[]=state
+ protocol=http
+ host=$HTTPD_DEST
+ wwwauth[]=FooBar param1="value1" param2="value2"
+ wwwauth[]=Bearer authorize_uri="id.example.com" p=1 q=0
+ wwwauth[]=Basic realm="example.com"
+ EOF
+
+ expect_credential_query store <<-EOF
+ capability[]=authtype
+ authtype=Bearer
+ credential=YS1naXQtdG9rZW4=
+ protocol=http
+ host=$HTTPD_DEST
+ EOF
+'
+
+test_expect_success 'access using bearer auth with invalid credentials' '
+ test_when_finished "per_test_cleanup" &&
+
+ set_credential_reply get <<-EOF &&
+ capability[]=authtype
+ authtype=Bearer
+ credential=incorrect-token
+ EOF
+
+ # Basic base64(a-git-token)
+ cat >"$HTTPD_ROOT_PATH/custom-auth.valid" <<-EOF &&
+ id=1 creds=Bearer YS1naXQtdG9rZW4=
+ EOF
+
+ CHALLENGE="$HTTPD_ROOT_PATH/custom-auth.challenge" &&
+
+ cat >"$HTTPD_ROOT_PATH/custom-auth.challenge" <<-EOF &&
+ id=1 status=200
+ id=default response=WWW-Authenticate: FooBar param1="value1" param2="value2"
+ id=default response=WWW-Authenticate: Bearer authorize_uri="id.example.com" p=1 q=0
+ id=default response=WWW-Authenticate: Basic realm="example.com"
+ EOF
+
+ test_config_global credential.helper test-helper &&
+ test_must_fail git ls-remote "$HTTPD_URL/custom_auth/repo.git" &&
+
+ expect_credential_query get <<-EOF &&
+ capability[]=authtype
+ capability[]=state
+ protocol=http
+ host=$HTTPD_DEST
+ wwwauth[]=FooBar param1="value1" param2="value2"
+ wwwauth[]=Bearer authorize_uri="id.example.com" p=1 q=0
+ wwwauth[]=Basic realm="example.com"
+ EOF
+
+ expect_credential_query erase <<-EOF
+ capability[]=authtype
+ authtype=Bearer
+ credential=incorrect-token
+ protocol=http
+ host=$HTTPD_DEST
+ wwwauth[]=FooBar param1="value1" param2="value2"
+ wwwauth[]=Bearer authorize_uri="id.example.com" p=1 q=0
+ wwwauth[]=Basic realm="example.com"
+ EOF
+'
+
+test_expect_success 'access using three-legged auth' '
+ test_when_finished "per_test_cleanup" &&
+
+ set_credential_reply get <<-EOF &&
+ capability[]=authtype
+ capability[]=state
+ authtype=Multistage
+ credential=YS1naXQtdG9rZW4=
+ state[]=helper:foobar
+ continue=1
+ EOF
+
+ set_credential_reply get foobar <<-EOF &&
+ capability[]=authtype
+ capability[]=state
+ authtype=Multistage
+ credential=YW5vdGhlci10b2tlbg==
+ state[]=helper:bazquux
+ EOF
+
+ cat >"$HTTPD_ROOT_PATH/custom-auth.valid" <<-EOF &&
+ id=1 creds=Multistage YS1naXQtdG9rZW4=
+ id=2 creds=Multistage YW5vdGhlci10b2tlbg==
+ EOF
+
+ CHALLENGE="$HTTPD_ROOT_PATH/custom-auth.challenge" &&
+
+ cat >"$HTTPD_ROOT_PATH/custom-auth.challenge" <<-EOF &&
+ id=1 status=401 response=WWW-Authenticate: Multistage challenge="456"
+ id=1 status=401 response=WWW-Authenticate: Bearer authorize_uri="id.example.com" p=1 q=0
+ id=2 status=200
+ id=default response=WWW-Authenticate: Multistage challenge="123"
+ id=default response=WWW-Authenticate: Bearer authorize_uri="id.example.com" p=1 q=0
+ EOF
+
+ test_config_global credential.helper test-helper &&
+ git ls-remote "$HTTPD_URL/custom_auth/repo.git" &&
+
+ expect_credential_query get <<-EOF &&
+ capability[]=authtype
+ capability[]=state
+ protocol=http
+ host=$HTTPD_DEST
+ wwwauth[]=Multistage challenge="123"
+ wwwauth[]=Bearer authorize_uri="id.example.com" p=1 q=0
+ EOF
+
+ expect_credential_query get foobar <<-EOF &&
+ capability[]=authtype
+ capability[]=state
+ authtype=Multistage
+ protocol=http
+ host=$HTTPD_DEST
+ wwwauth[]=Multistage challenge="456"
+ wwwauth[]=Bearer authorize_uri="id.example.com" p=1 q=0
+ state[]=helper:foobar
+ EOF
+
+ expect_credential_query store bazquux <<-EOF
+ capability[]=authtype
+ capability[]=state
+ authtype=Multistage
+ credential=YW5vdGhlci10b2tlbg==
+ protocol=http
+ host=$HTTPD_DEST
+ state[]=helper:bazquux
+ EOF
+'
+
test_done
diff --git a/t/t5564-http-proxy.sh b/t/t5564-http-proxy.sh
index 9da5134614..bb35b87071 100755
--- a/t/t5564-http-proxy.sh
+++ b/t/t5564-http-proxy.sh
@@ -2,6 +2,7 @@
test_description="test fetching through http proxy"
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-httpd.sh
diff --git a/t/t5581-http-curl-verbose.sh b/t/t5581-http-curl-verbose.sh
index cded79c16b..724f610054 100755
--- a/t/t5581-http-curl-verbose.sh
+++ b/t/t5581-http-curl-verbose.sh
@@ -4,6 +4,7 @@ test_description='test GIT_CURL_VERBOSE'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-httpd.sh
start_httpd
diff --git a/t/t5601-clone.sh b/t/t5601-clone.sh
index ca43185681..5d7ea147f1 100755
--- a/t/t5601-clone.sh
+++ b/t/t5601-clone.sh
@@ -46,6 +46,13 @@ test_expect_success 'output from clone' '
test $(grep Clon output | wc -l) = 1
'
+test_expect_success 'output from clone with core.abbrev does not crash' '
+ rm -fr dst &&
+ echo "Cloning into ${SQ}dst${SQ}..." >expect &&
+ git -c core.abbrev=12 clone -n "file://$(pwd)/src" dst >actual 2>&1 &&
+ test_cmp expect actual
+'
+
test_expect_success 'clone does not keep pack' '
rm -fr dst &&
@@ -650,6 +657,21 @@ test_expect_success CASE_INSENSITIVE_FS 'colliding file detection' '
test_grep "the following paths have collided" icasefs/warning
'
+test_expect_success CASE_INSENSITIVE_FS,SYMLINKS \
+ 'colliding symlink/directory keeps directory' '
+ git init icasefs-colliding-symlink &&
+ (
+ cd icasefs-colliding-symlink &&
+ a=$(printf a | git hash-object -w --stdin) &&
+ printf "100644 %s 0\tA/dir/b\n120000 %s 0\ta\n" $a $a >idx &&
+ git update-index --index-info <idx &&
+ test_tick &&
+ git commit -m initial
+ ) &&
+ git clone icasefs-colliding-symlink icasefs-colliding-symlink-clone &&
+ test_file_not_empty icasefs-colliding-symlink-clone/A/dir/b
+'
+
test_expect_success 'clone with GIT_DEFAULT_HASH' '
(
sane_unset GIT_DEFAULT_HASH &&
diff --git a/t/t5605-clone-local.sh b/t/t5605-clone-local.sh
index a3055869bc..d9a320abd2 100755
--- a/t/t5605-clone-local.sh
+++ b/t/t5605-clone-local.sh
@@ -4,6 +4,7 @@ test_description='test local clone'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
repo_is_hardlinked() {
@@ -163,7 +164,7 @@ test_expect_success REFFILES 'local clone from repo with corrupt refs fails grac
echo a >corrupt/.git/refs/heads/topic &&
test_must_fail git clone corrupt working 2>err &&
- grep "has a null OID" err
+ grep "has neither a valid OID nor a target" err
'
test_done
diff --git a/t/t5607-clone-bundle.sh b/t/t5607-clone-bundle.sh
index 0d1e92d996..7ceaa8194d 100755
--- a/t/t5607-clone-bundle.sh
+++ b/t/t5607-clone-bundle.sh
@@ -4,6 +4,7 @@ test_description='some bundle related tests'
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' '
@@ -138,6 +139,41 @@ test_expect_success 'fetch SHA-1 from bundle' '
git fetch --no-tags foo/tip.bundle "$(cat hash)"
'
+test_expect_success 'clone bundle with different fsckObjects configurations' '
+ test_create_repo bundle-fsck &&
+ (
+ cd bundle-fsck &&
+ test_commit A &&
+ commit_a=$(git rev-parse A) &&
+ tree_a=$(git rev-parse A^{tree}) &&
+ cat >data <<-EOF &&
+ tree $tree_a
+ parent $commit_a
+ author A U Thor
+ committer A U Thor
+
+ commit: this is a commit with bad emails
+
+ EOF
+ bad_commit=$(git hash-object --literally -t commit -w --stdin <data) &&
+ git branch bad $bad_commit &&
+ git bundle create bad.bundle bad
+ ) &&
+
+ git clone bundle-fsck/bad.bundle bundle-no-fsck &&
+
+ git -c fetch.fsckObjects=false -c transfer.fsckObjects=true \
+ clone bundle-fsck/bad.bundle bundle-fetch-no-fsck &&
+
+ test_must_fail git -c fetch.fsckObjects=true \
+ clone bundle-fsck/bad.bundle bundle-fetch-fsck 2>err &&
+ test_grep "missingEmail" err &&
+
+ test_must_fail git -c transfer.fsckObjects=true \
+ clone bundle-fsck/bad.bundle bundle-transfer-fsck 2>err &&
+ test_grep "missingEmail" err
+'
+
test_expect_success 'git bundle uses expected default format' '
git bundle create bundle HEAD^.. &&
cat >expect <<-EOF &&
diff --git a/t/t5612-clone-refspec.sh b/t/t5612-clone-refspec.sh
index 3126cfd7e9..72762de977 100755
--- a/t/t5612-clone-refspec.sh
+++ b/t/t5612-clone-refspec.sh
@@ -4,6 +4,7 @@ test_description='test refspec written by clone-command'
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/t5801-remote-helpers.sh b/t/t5801-remote-helpers.sh
index 4e0a77f985..20f43f7b7d 100755
--- a/t/t5801-remote-helpers.sh
+++ b/t/t5801-remote-helpers.sh
@@ -38,6 +38,29 @@ test_expect_success 'cloning from local repo' '
test_cmp server/file local/file
'
+test_expect_success 'clone with remote.*.vcs config' '
+ GIT_TRACE=$PWD/vcs-clone.trace \
+ git clone --no-local -c remote.origin.vcs=testgit "$PWD/server" vcs-clone &&
+ test_grep remote-testgit vcs-clone.trace
+'
+
+test_expect_success 'fetch with configured remote.*.vcs' '
+ git init vcs-fetch &&
+ git -C vcs-fetch config remote.origin.vcs testgit &&
+ git -C vcs-fetch config remote.origin.url "$PWD/server" &&
+ GIT_TRACE=$PWD/vcs-fetch.trace \
+ git -C vcs-fetch fetch origin &&
+ test_grep remote-testgit vcs-fetch.trace
+'
+
+test_expect_success 'vcs remote with no url' '
+ NOURL_UPSTREAM=$PWD/server &&
+ export NOURL_UPSTREAM &&
+ git init vcs-nourl &&
+ git -C vcs-nourl config remote.origin.vcs nourl &&
+ git -C vcs-nourl fetch origin
+'
+
test_expect_success 'create new commit on remote' '
(cd server &&
echo content >>file &&
diff --git a/t/t5801/git-remote-nourl b/t/t5801/git-remote-nourl
new file mode 100755
index 0000000000..09be6013c5
--- /dev/null
+++ b/t/t5801/git-remote-nourl
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+exec git-remote-testgit "$1" "$NOURL_UPSTREAM"
diff --git a/t/t5801/git-remote-testgit b/t/t5801/git-remote-testgit
index c5b10f5775..f8b476499f 100755
--- a/t/t5801/git-remote-testgit
+++ b/t/t5801/git-remote-testgit
@@ -26,7 +26,8 @@ then
t_refspec=""
fi
-GIT_DIR="$url/.git"
+unset $(git rev-parse --local-env-vars)
+GIT_DIR=$(git -C "$url" rev-parse --absolute-git-dir)
export GIT_DIR
force=
diff --git a/t/t6000-rev-list-misc.sh b/t/t6000-rev-list-misc.sh
index 6289a2e8b0..f6d17ee902 100755
--- a/t/t6000-rev-list-misc.sh
+++ b/t/t6000-rev-list-misc.sh
@@ -5,6 +5,7 @@ test_description='miscellaneous rev-list tests'
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/t6001-rev-list-graft.sh b/t/t6001-rev-list-graft.sh
index 73a2465aa0..3553bbbfe7 100755
--- a/t/t6001-rev-list-graft.sh
+++ b/t/t6001-rev-list-graft.sh
@@ -5,6 +5,7 @@ test_description='Revision traversal vs grafts and path limiter'
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/t6006-rev-list-format.sh b/t/t6006-rev-list-format.sh
index 573eb97a0f..f1623b1c06 100755
--- a/t/t6006-rev-list-format.sh
+++ b/t/t6006-rev-list-format.sh
@@ -8,6 +8,7 @@ test_description='git rev-list --pretty=format test'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-terminal.sh
diff --git a/t/t6013-rev-list-reverse-parents.sh b/t/t6013-rev-list-reverse-parents.sh
index 39793cbbd6..4128269c1d 100755
--- a/t/t6013-rev-list-reverse-parents.sh
+++ b/t/t6013-rev-list-reverse-parents.sh
@@ -5,6 +5,7 @@ test_description='--reverse combines with --parents'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
diff --git a/t/t6017-rev-list-stdin.sh b/t/t6017-rev-list-stdin.sh
index 4821b90e74..a0a40fe55c 100755
--- a/t/t6017-rev-list-stdin.sh
+++ b/t/t6017-rev-list-stdin.sh
@@ -8,6 +8,7 @@ test_description='log family learns --stdin'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
check () {
diff --git a/t/t6020-bundle-misc.sh b/t/t6020-bundle-misc.sh
index 3e6bcbf30c..fe75a06572 100755
--- a/t/t6020-bundle-misc.sh
+++ b/t/t6020-bundle-misc.sh
@@ -8,6 +8,7 @@ test_description='Test git-bundle'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-bundle.sh
. "$TEST_DIRECTORY"/lib-terminal.sh
diff --git a/t/t6041-bisect-submodule.sh b/t/t6041-bisect-submodule.sh
index 82013fc903..3946e18089 100755
--- a/t/t6041-bisect-submodule.sh
+++ b/t/t6041-bisect-submodule.sh
@@ -2,6 +2,7 @@
test_description='bisect can handle submodules'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-submodule-update.sh
diff --git a/t/t6112-rev-list-filters-objects.sh b/t/t6112-rev-list-filters-objects.sh
index 43e1afd44c..0387f35a32 100755
--- a/t/t6112-rev-list-filters-objects.sh
+++ b/t/t6112-rev-list-filters-objects.sh
@@ -701,4 +701,16 @@ test_expect_success 'expand blob limit in protocol' '
grep "blob:limit=1024" trace
'
+test_expect_success EXPENSIVE 'large sparse filter file ignored' '
+ blob=$(dd if=/dev/zero bs=101M count=1 |
+ git hash-object -w --stdin) &&
+ test_must_fail \
+ git rev-list --all --objects --filter=sparse:oid=$blob 2>err &&
+ cat >expect <<-EOF &&
+ warning: ignoring excessively large pattern blob: $blob
+ fatal: unable to parse sparse filter data in $blob
+ EOF
+ test_cmp expect err
+'
+
test_done
diff --git a/t/t6115-rev-list-du.sh b/t/t6115-rev-list-du.sh
index c0cfda62fa..21c4a211b1 100755
--- a/t/t6115-rev-list-du.sh
+++ b/t/t6115-rev-list-du.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='basic tests of rev-list --disk-usage'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# we want a mix of reachable and unreachable, as well as
diff --git a/t/t6120-describe.sh b/t/t6120-describe.sh
index e78315d23d..79e0f19deb 100755
--- a/t/t6120-describe.sh
+++ b/t/t6120-describe.sh
@@ -671,4 +671,40 @@ test_expect_success 'setup misleading taggerdates' '
check_describe newer-tag-older-commit~1 --contains unique-file~2
+test_expect_success 'describe --dirty with a file with changed stat' '
+ test_when_finished "rm -fr stat-dirty" &&
+ git init stat-dirty &&
+ (
+ cd stat-dirty &&
+
+ echo A >file &&
+ git add file &&
+ git commit -m A &&
+ git tag A -a -m A &&
+ echo "A" >expect &&
+
+ test-tool chmtime -10 file &&
+ git describe --dirty >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'describe --broken --dirty with a file with changed stat' '
+ test_when_finished "rm -fr stat-dirty" &&
+ git init stat-dirty &&
+ (
+ cd stat-dirty &&
+
+ echo A >file &&
+ git add file &&
+ git commit -m A &&
+ git tag A -a -m A &&
+ echo "A" >expect &&
+
+ test-tool chmtime -10 file &&
+ git describe --dirty --broken >actual &&
+ test_cmp expect actual
+ )
+'
+
test_done
diff --git a/t/t6130-pathspec-noglob.sh b/t/t6130-pathspec-noglob.sh
index ba7902c9cd..82de25d549 100755
--- a/t/t6130-pathspec-noglob.sh
+++ b/t/t6130-pathspec-noglob.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='test globbing (and noglob) of pathspec limiting'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'create commits with glob characters' '
diff --git a/t/t6400-merge-df.sh b/t/t6400-merge-df.sh
index 3de4ef6bd9..27d6efdc9a 100755
--- a/t/t6400-merge-df.sh
+++ b/t/t6400-merge-df.sh
@@ -7,6 +7,7 @@ test_description='Test merge with directory/file conflicts'
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 'prepare repository' '
diff --git a/t/t6402-merge-rename.sh b/t/t6402-merge-rename.sh
index 2738b50c2a..729aac9842 100755
--- a/t/t6402-merge-rename.sh
+++ b/t/t6402-merge-rename.sh
@@ -4,6 +4,7 @@ test_description='Merge-recursive merging renames'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
modify () {
diff --git a/t/t6406-merge-attr.sh b/t/t6406-merge-attr.sh
index 156a1efacf..9bf9524934 100755
--- a/t/t6406-merge-attr.sh
+++ b/t/t6406-merge-attr.sh
@@ -185,7 +185,7 @@ test_expect_success !WINDOWS 'custom merge driver that is killed with a signal'
>./please-abort &&
echo "* merge=custom" >.gitattributes &&
- test_must_fail git merge main 2>err &&
+ test_expect_code 2 git merge main 2>err &&
grep "^error: failed to execute internal merge" err &&
git ls-files -u >output &&
git diff --name-only HEAD >>output &&
@@ -261,4 +261,44 @@ test_expect_success 'binary files with union attribute' '
grep -i "warning.*cannot merge.*HEAD vs. bin-main" output
'
+test_expect_success !WINDOWS 'custom merge driver that is killed with a signal on recursive merge' '
+ test_when_finished "rm -f output please-abort" &&
+ test_when_finished "git checkout side" &&
+
+ git reset --hard anchor &&
+
+ git checkout -b base-a main^ &&
+ echo base-a >text &&
+ git commit -m base-a text &&
+
+ git checkout -b base-b main^ &&
+ echo base-b >text &&
+ git commit -m base-b text &&
+
+ git checkout -b recursive-a base-a &&
+ test_must_fail git merge base-b &&
+ echo recursive-a >text &&
+ git add text &&
+ git commit -m recursive-a &&
+
+ git checkout -b recursive-b base-b &&
+ test_must_fail git merge base-a &&
+ echo recursive-b >text &&
+ git add text &&
+ git commit -m recursive-b &&
+
+ git config --replace-all \
+ merge.custom.driver "./custom-merge %O %A %B 0 %P %S %X %Y" &&
+ git config --replace-all \
+ merge.custom.name "custom merge driver for testing" &&
+
+ >./please-abort &&
+ echo "* merge=custom" >.gitattributes &&
+ test_expect_code 2 git merge recursive-a 2>err &&
+ grep "error: failed to execute internal merge" err &&
+ git ls-files -u >output &&
+ git diff --name-only HEAD >>output &&
+ test_must_be_empty output
+'
+
test_done
diff --git a/t/t6412-merge-large-rename.sh b/t/t6412-merge-large-rename.sh
index ca018d11f5..d0863a8fb5 100755
--- a/t/t6412-merge-large-rename.sh
+++ b/t/t6412-merge-large-rename.sh
@@ -4,6 +4,7 @@ test_description='merging with large rename matrix'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
count() {
diff --git a/t/t6426-merge-skip-unneeded-updates.sh b/t/t6426-merge-skip-unneeded-updates.sh
index b059475ed0..62f0180325 100755
--- a/t/t6426-merge-skip-unneeded-updates.sh
+++ b/t/t6426-merge-skip-unneeded-updates.sh
@@ -22,6 +22,7 @@ test_description="merge cases"
# underscore notation is to differentiate different
# files that might be renamed into each other's paths.)
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-merge.sh
diff --git a/t/t6427-diff3-conflict-markers.sh b/t/t6427-diff3-conflict-markers.sh
index dd5fe6a402..a13271b349 100755
--- a/t/t6427-diff3-conflict-markers.sh
+++ b/t/t6427-diff3-conflict-markers.sh
@@ -5,6 +5,7 @@ test_description='recursive merge diff3 style conflict markers'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# Setup:
diff --git a/t/t6429-merge-sequence-rename-caching.sh b/t/t6429-merge-sequence-rename-caching.sh
index 0f39ed0d08..cb1c4ceef7 100755
--- a/t/t6429-merge-sequence-rename-caching.sh
+++ b/t/t6429-merge-sequence-rename-caching.sh
@@ -2,6 +2,7 @@
test_description="remember regular & dir renames in sequence of merges"
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
#
diff --git a/t/t6430-merge-recursive.sh b/t/t6430-merge-recursive.sh
index ca15e6dd6d..555f00f78a 100755
--- a/t/t6430-merge-recursive.sh
+++ b/t/t6430-merge-recursive.sh
@@ -5,6 +5,7 @@ test_description='merge-recursive backend test'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-merge.sh
diff --git a/t/t6432-merge-recursive-space-options.sh b/t/t6432-merge-recursive-space-options.sh
index db4b77e63d..c93538b0c3 100755
--- a/t/t6432-merge-recursive-space-options.sh
+++ b/t/t6432-merge-recursive-space-options.sh
@@ -14,6 +14,7 @@ test_description='merge-recursive space options
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_have_prereq SED_STRIPS_CR && SED_OPTIONS=-b
diff --git a/t/t6434-merge-recursive-rename-options.sh b/t/t6434-merge-recursive-rename-options.sh
index a11707835b..df1d0c156c 100755
--- a/t/t6434-merge-recursive-rename-options.sh
+++ b/t/t6434-merge-recursive-rename-options.sh
@@ -29,6 +29,7 @@ mentions this in a different context).
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
get_expected_stages () {
diff --git a/t/t6436-merge-overwrite.sh b/t/t6436-merge-overwrite.sh
index 4f4376421e..ccc620477d 100755
--- a/t/t6436-merge-overwrite.sh
+++ b/t/t6436-merge-overwrite.sh
@@ -7,6 +7,7 @@ Do not overwrite changes.'
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/t6438-submodule-directory-file-conflicts.sh b/t/t6438-submodule-directory-file-conflicts.sh
index 8df67a0ef9..3594190af8 100755
--- a/t/t6438-submodule-directory-file-conflicts.sh
+++ b/t/t6438-submodule-directory-file-conflicts.sh
@@ -2,6 +2,7 @@
test_description='merge can handle submodules'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-submodule-update.sh
diff --git a/t/t6500-gc.sh b/t/t6500-gc.sh
index 43d40175f8..1b5909d1b7 100755
--- a/t/t6500-gc.sh
+++ b/t/t6500-gc.sh
@@ -158,7 +158,7 @@ test_expect_success TTY 'with TTY: gc --no-quiet' '
git -c gc.writeCommitGraph=true gc --no-quiet >stdout 2>stderr &&
test_must_be_empty stdout &&
test_grep "Enumerating objects" stderr &&
- test_grep "Computing commit graph generation numbers" stderr
+ test_grep "Computing commit graph generation numbers: 100% (4/4), done." stderr
'
test_expect_success 'gc --quiet' '
diff --git a/t/t7001-mv.sh b/t/t7001-mv.sh
index 879a6dce60..86258f9f43 100755
--- a/t/t7001-mv.sh
+++ b/t/t7001-mv.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='git mv in subdirs'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-diff-data.sh
diff --git a/t/t7002-mv-sparse-checkout.sh b/t/t7002-mv-sparse-checkout.sh
index 26582ae4e5..57969ce805 100755
--- a/t/t7002-mv-sparse-checkout.sh
+++ b/t/t7002-mv-sparse-checkout.sh
@@ -2,6 +2,7 @@
test_description='git mv in sparse working trees'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
setup_sparse_checkout () {
diff --git a/t/t7004-tag.sh b/t/t7004-tag.sh
index 696866d779..fa6336edf9 100755
--- a/t/t7004-tag.sh
+++ b/t/t7004-tag.sh
@@ -668,6 +668,115 @@ test_expect_success \
test_cmp expect actual
'
+# trailers
+
+test_expect_success 'create tag with -m and --trailer' '
+ get_tag_header tag-with-inline-message-and-trailers $commit commit $time >expect &&
+ cat >>expect <<-\EOF &&
+ create tag with trailers
+
+ my-trailer: here
+ alt-trailer: there
+ EOF
+ git tag -m "create tag with trailers" \
+ --trailer my-trailer=here \
+ --trailer alt-trailer=there \
+ tag-with-inline-message-and-trailers &&
+ get_tag_msg tag-with-inline-message-and-trailers >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'list tag extracting trailers' '
+ cat >expect <<-\EOF &&
+ my-trailer: here
+ alt-trailer: there
+
+ EOF
+ git tag --list --format="%(trailers)" tag-with-inline-message-and-trailers >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'create tag with -F and --trailer' '
+ echo "create tag from message file using --trailer" >messagefilewithnotrailers &&
+ get_tag_header tag-with-file-message-and-trailers $commit commit $time >expect &&
+ cat >>expect <<-\EOF &&
+ create tag from message file using --trailer
+
+ my-trailer: here
+ alt-trailer: there
+ EOF
+ git tag -F messagefilewithnotrailers \
+ --trailer my-trailer=here \
+ --trailer alt-trailer=there \
+ tag-with-file-message-and-trailers &&
+ get_tag_msg tag-with-file-message-and-trailers >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'create tag with -m and --trailer and --edit' '
+ write_script fakeeditor <<-\EOF &&
+ sed -e "1s/^/EDITED: /g" <"$1" >"$1-"
+ mv "$1-" "$1"
+ EOF
+ get_tag_header tag-with-edited-inline-message-and-trailers $commit commit $time >expect &&
+ cat >>expect <<-\EOF &&
+ EDITED: create tag with trailers
+
+ my-trailer: here
+ alt-trailer: there
+ EOF
+ GIT_EDITOR=./fakeeditor git tag --edit \
+ -m "create tag with trailers" \
+ --trailer my-trailer=here \
+ --trailer alt-trailer=there \
+ tag-with-edited-inline-message-and-trailers &&
+ get_tag_msg tag-with-edited-inline-message-and-trailers >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'create tag with -F and --trailer and --edit' '
+ echo "create tag from message file using --trailer" >messagefilewithnotrailers &&
+ get_tag_header tag-with-edited-file-message-and-trailers $commit commit $time >expect &&
+ cat >>expect <<-\EOF &&
+ EDITED: create tag from message file using --trailer
+
+ my-trailer: here
+ alt-trailer: there
+ EOF
+ GIT_EDITOR=./fakeeditor git tag --edit \
+ -F messagefilewithnotrailers \
+ --trailer my-trailer=here \
+ --trailer alt-trailer=there \
+ tag-with-edited-file-message-and-trailers &&
+ get_tag_msg tag-with-edited-file-message-and-trailers >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'create annotated tag and force editor when only --trailer is given' '
+ write_script fakeeditor <<-\EOF &&
+ echo "add a line" >"$1-"
+ cat <"$1" >>"$1-"
+ mv "$1-" "$1"
+ EOF
+ get_tag_header tag-with-trailers-and-no-message $commit commit $time >expect &&
+ cat >>expect <<-\EOF &&
+ add a line
+
+ my-trailer: here
+ alt-trailer: there
+ EOF
+ GIT_EDITOR=./fakeeditor git tag \
+ --trailer my-trailer=here \
+ --trailer alt-trailer=there \
+ tag-with-trailers-and-no-message &&
+ get_tag_msg tag-with-trailers-and-no-message >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'bad editor causes panic when only --trailer is given' '
+ test_must_fail env GIT_EDITOR=false git tag --trailer my-trailer=here tag-will-not-exist
+'
+
# listing messages for annotated non-signed tags:
test_expect_success \
@@ -810,6 +919,11 @@ test_expect_success 'git tag --format with ahead-behind' '
refs/tags/tag-lines 0 1 !
refs/tags/tag-one-line 0 1 !
refs/tags/tag-right 0 0 !
+ refs/tags/tag-with-edited-file-message-and-trailers 0 1 !
+ refs/tags/tag-with-edited-inline-message-and-trailers 0 1 !
+ refs/tags/tag-with-file-message-and-trailers 0 1 !
+ refs/tags/tag-with-inline-message-and-trailers 0 1 !
+ refs/tags/tag-with-trailers-and-no-message 0 1 !
refs/tags/tag-zero-lines 0 1 !
EOF
git tag -l --format="%(refname) %(ahead-behind:HEAD) !" >actual 2>err &&
diff --git a/t/t7005-editor.sh b/t/t7005-editor.sh
index 5fcf281dfb..b9822294fe 100755
--- a/t/t7005-editor.sh
+++ b/t/t7005-editor.sh
@@ -2,6 +2,7 @@
test_description='GIT_EDITOR, core.editor, and stuff'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
unset EDITOR VISUAL GIT_EDITOR
diff --git a/t/t7006-pager.sh b/t/t7006-pager.sh
index e56ca5b0fa..a0296d6ca4 100755
--- a/t/t7006-pager.sh
+++ b/t/t7006-pager.sh
@@ -2,6 +2,7 @@
test_description='Test automatic use of a pager.'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-pager.sh
. "$TEST_DIRECTORY"/lib-terminal.sh
@@ -725,18 +726,11 @@ test_expect_success TTY 'git discards pager non-zero exit without SIGPIPE' '
test_path_is_file pager-used
'
-test_expect_success TTY 'git skips paging nonexisting command' '
- test_when_finished "rm trace.normal" &&
+test_expect_success TTY 'git errors when asked to execute nonexisting pager' '
+ test_when_finished "rm -f err" &&
test_config core.pager "does-not-exist" &&
- GIT_TRACE2="$(pwd)/trace.normal" &&
- export GIT_TRACE2 &&
- test_when_finished "unset GIT_TRACE2" &&
-
- test_terminal git log &&
-
- grep child_exit trace.normal >child-exits &&
- test_line_count = 1 child-exits &&
- grep " code:-1 " child-exits
+ test_must_fail test_terminal git log 2>err &&
+ test_grep "unable to execute pager" err
'
test_expect_success TTY 'git returns SIGPIPE on propagated signals from pager' '
@@ -762,7 +756,7 @@ test_expect_success TTY 'git returns SIGPIPE on propagated signals from pager' '
test_expect_success TTY 'non-existent pager doesnt cause crash' '
test_config pager.show invalid-pager &&
- test_terminal git show
+ test_must_fail test_terminal git show
'
test_done
diff --git a/t/t7010-setup.sh b/t/t7010-setup.sh
index 520f96d09f..d9add2162e 100755
--- a/t/t7010-setup.sh
+++ b/t/t7010-setup.sh
@@ -2,6 +2,7 @@
test_description='setup taking and sanitizing funny paths'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
diff --git a/t/t7012-skip-worktree-writing.sh b/t/t7012-skip-worktree-writing.sh
index cd5c20fe51..d984200c17 100755
--- a/t/t7012-skip-worktree-writing.sh
+++ b/t/t7012-skip-worktree-writing.sh
@@ -5,6 +5,7 @@
test_description='test worktree writing operations when skip-worktree is used'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
diff --git a/t/t7102-reset.sh b/t/t7102-reset.sh
index 62d9f846ce..2add26d768 100755
--- a/t/t7102-reset.sh
+++ b/t/t7102-reset.sh
@@ -10,6 +10,7 @@ Documented tests for git reset'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
commit_msg () {
diff --git a/t/t7112-reset-submodule.sh b/t/t7112-reset-submodule.sh
index a3e2413bc3..b0d3d93b0b 100755
--- a/t/t7112-reset-submodule.sh
+++ b/t/t7112-reset-submodule.sh
@@ -2,6 +2,7 @@
test_description='reset can handle submodules'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-submodule-update.sh
diff --git a/t/t7201-co.sh b/t/t7201-co.sh
index 42352dc0db..189d8e341b 100755
--- a/t/t7201-co.sh
+++ b/t/t7201-co.sh
@@ -23,6 +23,7 @@ Test switching across them.
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_tick
diff --git a/t/t7400-submodule-basic.sh b/t/t7400-submodule-basic.sh
index 5c4a89df5c..981488885f 100755
--- a/t/t7400-submodule-basic.sh
+++ b/t/t7400-submodule-basic.sh
@@ -1451,4 +1451,35 @@ test_expect_success 'recursive clone respects -q' '
test_must_be_empty actual
'
+test_expect_success '`submodule init` and `init.templateDir`' '
+ mkdir -p tmpl/hooks &&
+ write_script tmpl/hooks/post-checkout <<-EOF &&
+ echo HOOK-RUN >&2
+ echo I was here >hook.run
+ exit 1
+ EOF
+
+ test_config init.templateDir "$(pwd)/tmpl" &&
+ test_when_finished \
+ "git config --global --unset init.templateDir || true" &&
+ (
+ sane_unset GIT_TEMPLATE_DIR &&
+ NO_SET_GIT_TEMPLATE_DIR=t &&
+ export NO_SET_GIT_TEMPLATE_DIR &&
+
+ git config --global init.templateDir "$(pwd)/tmpl" &&
+ test_must_fail git submodule \
+ add "$submodurl" sub-global 2>err &&
+ git config --global --unset init.templateDir &&
+ test_grep HOOK-RUN err &&
+ test_path_is_file sub-global/hook.run &&
+
+ git config init.templateDir "$(pwd)/tmpl" &&
+ git submodule add "$submodurl" sub-local 2>err &&
+ git config --unset init.templateDir &&
+ test_grep ! HOOK-RUN err &&
+ test_path_is_missing sub-local/hook.run
+ )
+'
+
test_done
diff --git a/t/t7406-submodule-update.sh b/t/t7406-submodule-update.sh
index 8491b8c58b..297c6c3b5c 100755
--- a/t/t7406-submodule-update.sh
+++ b/t/t7406-submodule-update.sh
@@ -1202,4 +1202,52 @@ test_expect_success 'commit with staged submodule change with ignoreSubmodules a
add_submodule_commit_and_validate
'
+test_expect_success CASE_INSENSITIVE_FS,SYMLINKS \
+ 'submodule paths must not follow symlinks' '
+
+ # This is only needed because we want to run this in a self-contained
+ # test without having to spin up an HTTP server; However, it would not
+ # be needed in a real-world scenario where the submodule is simply
+ # hosted on a public site.
+ test_config_global protocol.file.allow always &&
+
+ # Make sure that Git tries to use symlinks on Windows
+ test_config_global core.symlinks true &&
+
+ tell_tale_path="$PWD/tell.tale" &&
+ git init hook &&
+ (
+ cd hook &&
+ mkdir -p y/hooks &&
+ write_script y/hooks/post-checkout <<-EOF &&
+ echo HOOK-RUN >&2
+ echo hook-run >"$tell_tale_path"
+ EOF
+ git add y/hooks/post-checkout &&
+ test_tick &&
+ git commit -m post-checkout
+ ) &&
+
+ hook_repo_path="$(pwd)/hook" &&
+ git init captain &&
+ (
+ cd captain &&
+ git submodule add --name x/y "$hook_repo_path" A/modules/x &&
+ test_tick &&
+ git commit -m add-submodule &&
+
+ printf .git >dotgit.txt &&
+ git hash-object -w --stdin <dotgit.txt >dot-git.hash &&
+ printf "120000 %s 0\ta\n" "$(cat dot-git.hash)" >index.info &&
+ git update-index --index-info <index.info &&
+ test_tick &&
+ git commit -m add-symlink
+ ) &&
+
+ test_path_is_missing "$tell_tale_path" &&
+ git clone --recursive captain hooked 2>err &&
+ test_grep ! HOOK-RUN err &&
+ test_path_is_missing "$tell_tale_path"
+'
+
test_done
diff --git a/t/t7417-submodule-path-url.sh b/t/t7417-submodule-path-url.sh
index 5e3051da8b..dbbb3853dc 100755
--- a/t/t7417-submodule-path-url.sh
+++ b/t/t7417-submodule-path-url.sh
@@ -4,6 +4,7 @@ test_description='check handling of .gitmodule path with dash'
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/t7421-submodule-summary-add.sh b/t/t7421-submodule-summary-add.sh
index ce64d8b137..479c8fdde1 100755
--- a/t/t7421-submodule-summary-add.sh
+++ b/t/t7421-submodule-summary-add.sh
@@ -10,6 +10,7 @@ while making sure to add submodules using `git submodule add` instead of
`git add` as done in t7401.
'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
diff --git a/t/t7423-submodule-symlinks.sh b/t/t7423-submodule-symlinks.sh
new file mode 100755
index 0000000000..f45d806201
--- /dev/null
+++ b/t/t7423-submodule-symlinks.sh
@@ -0,0 +1,68 @@
+#!/bin/sh
+
+test_description='check that submodule operations do not follow symlinks'
+
+TEST_PASSES_SANITIZE_LEAK=true
+. ./test-lib.sh
+
+test_expect_success 'prepare' '
+ git config --global protocol.file.allow always &&
+ test_commit initial &&
+ git init upstream &&
+ test_commit -C upstream upstream submodule_file &&
+ git submodule add ./upstream a/sm &&
+ test_tick &&
+ git commit -m submodule
+'
+
+test_expect_success SYMLINKS 'git submodule update must not create submodule behind symlink' '
+ rm -rf a b &&
+ mkdir b &&
+ ln -s b a &&
+ test_path_is_missing b/sm &&
+ test_must_fail git submodule update &&
+ test_path_is_missing b/sm
+'
+
+test_expect_success SYMLINKS,CASE_INSENSITIVE_FS 'git submodule update must not create submodule behind symlink on case insensitive fs' '
+ rm -rf a b &&
+ mkdir b &&
+ ln -s b A &&
+ test_must_fail git submodule update &&
+ test_path_is_missing b/sm
+'
+
+prepare_symlink_to_repo() {
+ rm -rf a &&
+ mkdir a &&
+ git init a/target &&
+ git -C a/target fetch ../../upstream &&
+ ln -s target a/sm
+}
+
+test_expect_success SYMLINKS 'git restore --recurse-submodules must not be confused by a symlink' '
+ prepare_symlink_to_repo &&
+ test_must_fail git restore --recurse-submodules a/sm &&
+ test_path_is_missing a/sm/submodule_file &&
+ test_path_is_dir a/target/.git &&
+ test_path_is_missing a/target/submodule_file
+'
+
+test_expect_success SYMLINKS 'git restore --recurse-submodules must not migrate git dir of symlinked repo' '
+ prepare_symlink_to_repo &&
+ rm -rf .git/modules &&
+ test_must_fail git restore --recurse-submodules a/sm &&
+ test_path_is_dir a/target/.git &&
+ test_path_is_missing .git/modules/a/sm &&
+ test_path_is_missing a/target/submodule_file
+'
+
+test_expect_success SYMLINKS 'git checkout -f --recurse-submodules must not migrate git dir of symlinked repo when removing submodule' '
+ prepare_symlink_to_repo &&
+ rm -rf .git/modules &&
+ test_must_fail git checkout -f --recurse-submodules initial &&
+ test_path_is_dir a/target/.git &&
+ test_path_is_missing .git/modules/a/sm
+'
+
+test_done
diff --git a/t/t7450-bad-git-dotfiles.sh b/t/t7450-bad-git-dotfiles.sh
index 46d4fb0354..4a9c22c9e2 100755
--- a/t/t7450-bad-git-dotfiles.sh
+++ b/t/t7450-bad-git-dotfiles.sh
@@ -320,7 +320,7 @@ test_expect_success WINDOWS 'prevent git~1 squatting on Windows' '
fi
'
-test_expect_success 'git dirs of sibling submodules must not be nested' '
+test_expect_success 'setup submodules with nested git dirs' '
git init nested &&
test_commit -C nested nested &&
(
@@ -338,9 +338,39 @@ test_expect_success 'git dirs of sibling submodules must not be nested' '
git add .gitmodules thing1 thing2 &&
test_tick &&
git commit -m nested
- ) &&
+ )
+'
+
+test_expect_success 'git dirs of sibling submodules must not be nested' '
test_must_fail git clone --recurse-submodules nested clone 2>err &&
test_grep "is inside git dir" err
'
+test_expect_success 'submodule git dir nesting detection must work with parallel cloning' '
+ test_must_fail git clone --recurse-submodules --jobs=2 nested clone_parallel 2>err &&
+ cat err &&
+ grep -E "(already exists|is inside git dir|not a git repository)" err &&
+ {
+ test_path_is_missing .git/modules/hippo/HEAD ||
+ test_path_is_missing .git/modules/hippo/hooks/HEAD
+ }
+'
+
+test_expect_success 'checkout -f --recurse-submodules must not use a nested gitdir' '
+ git clone nested nested_checkout &&
+ (
+ cd nested_checkout &&
+ git submodule init &&
+ git submodule update thing1 &&
+ mkdir -p .git/modules/hippo/hooks/refs &&
+ mkdir -p .git/modules/hippo/hooks/objects/info &&
+ echo "../../../../objects" >.git/modules/hippo/hooks/objects/info/alternates &&
+ echo "ref: refs/heads/master" >.git/modules/hippo/hooks/HEAD
+ ) &&
+ test_must_fail git -C nested_checkout checkout -f --recurse-submodules HEAD 2>err &&
+ cat err &&
+ grep "is inside git dir" err &&
+ test_path_is_missing nested_checkout/thing2/.git
+'
+
test_done
diff --git a/t/t7501-commit-basic-functionality.sh b/t/t7501-commit-basic-functionality.sh
index cc12f99f11..52f5e28154 100755
--- a/t/t7501-commit-basic-functionality.sh
+++ b/t/t7501-commit-basic-functionality.sh
@@ -9,6 +9,7 @@ test_description='git commit'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY/lib-diff.sh"
diff --git a/t/t7505-prepare-commit-msg-hook.sh b/t/t7505-prepare-commit-msg-hook.sh
index 2128142a61..b88383df9e 100755
--- a/t/t7505-prepare-commit-msg-hook.sh
+++ b/t/t7505-prepare-commit-msg-hook.sh
@@ -5,6 +5,7 @@ test_description='prepare-commit-msg hook'
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 'set up commits for rebasing' '
diff --git a/t/t7512-status-help.sh b/t/t7512-status-help.sh
index 802f8f704c..cdd5f2c697 100755
--- a/t/t7512-status-help.sh
+++ b/t/t7512-status-help.sh
@@ -10,6 +10,7 @@ test_description='git status advice'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-rebase.sh
diff --git a/t/t7600-merge.sh b/t/t7600-merge.sh
index e5ff073099..65fd3d8552 100755
--- a/t/t7600-merge.sh
+++ b/t/t7600-merge.sh
@@ -29,6 +29,7 @@ Testing basic merge operations/option parsing.
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-gpg.sh
@@ -236,6 +237,16 @@ test_expect_success 'merge c1 with c2' '
verify_parents $c1 $c2
'
+test_expect_success 'merge c1 with c2 when index.lock exists' '
+ test_when_finished rm .git/index.lock &&
+ git reset --hard c1 &&
+ >.git/index.lock &&
+ test_must_fail git merge c2 &&
+ test_path_is_missing .git/MERGE_HEAD &&
+ test_path_is_missing .git/MERGE_MODE &&
+ test_path_is_missing .git/MERGE_MSG
+'
+
test_expect_success 'merge --squash c3 with c7' '
git reset --hard c3 &&
test_must_fail git merge --squash c7 &&
diff --git a/t/t7606-merge-custom.sh b/t/t7606-merge-custom.sh
index 81fb7c474c..135cb23085 100755
--- a/t/t7606-merge-custom.sh
+++ b/t/t7606-merge-custom.sh
@@ -14,6 +14,7 @@ Testing a custom strategy.
* (tag: c0) c0
"
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'set up custom strategy' '
diff --git a/t/t7611-merge-abort.sh b/t/t7611-merge-abort.sh
index d6975ca48d..992a8f9874 100755
--- a/t/t7611-merge-abort.sh
+++ b/t/t7611-merge-abort.sh
@@ -25,6 +25,7 @@ Next, test git merge --abort with the following variables:
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/t7900-maintenance.sh b/t/t7900-maintenance.sh
index 0943dfa18a..8595489ceb 100755
--- a/t/t7900-maintenance.sh
+++ b/t/t7900-maintenance.sh
@@ -639,9 +639,9 @@ test_expect_success 'start from empty cron table' '
# start registers the repo
git config --get --global --fixed-value maintenance.repo "$(pwd)" &&
- grep "for-each-repo --config=maintenance.repo maintenance run --schedule=daily" cron.txt &&
- grep "for-each-repo --config=maintenance.repo maintenance run --schedule=hourly" cron.txt &&
- grep "for-each-repo --config=maintenance.repo maintenance run --schedule=weekly" cron.txt
+ grep "for-each-repo --keep-going --config=maintenance.repo maintenance run --schedule=daily" cron.txt &&
+ grep "for-each-repo --keep-going --config=maintenance.repo maintenance run --schedule=hourly" cron.txt &&
+ grep "for-each-repo --keep-going --config=maintenance.repo maintenance run --schedule=weekly" cron.txt
'
test_expect_success 'stop from existing schedule' '
diff --git a/t/t8002-blame.sh b/t/t8002-blame.sh
index 0147de304b..3596634039 100755
--- a/t/t8002-blame.sh
+++ b/t/t8002-blame.sh
@@ -5,6 +5,7 @@ GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
TEST_CREATE_REPO_NO_TEMPLATE=1
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
PROG='git blame -c'
diff --git a/t/t8003-blame-corner-cases.sh b/t/t8003-blame-corner-cases.sh
index 731265541a..6288352f57 100755
--- a/t/t8003-blame-corner-cases.sh
+++ b/t/t8003-blame-corner-cases.sh
@@ -4,6 +4,7 @@ test_description='git blame corner cases'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
pick_fc='s/^[0-9a-f^]* *\([^ ]*\) *(\([^ ]*\) .*/\1-\2/'
diff --git a/t/t8004-blame-with-conflicts.sh b/t/t8004-blame-with-conflicts.sh
index 35414a5336..2c2a0b33f9 100755
--- a/t/t8004-blame-with-conflicts.sh
+++ b/t/t8004-blame-with-conflicts.sh
@@ -6,6 +6,7 @@ test_description='git blame on conflicted files'
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 first case' '
diff --git a/t/t8006-blame-textconv.sh b/t/t8006-blame-textconv.sh
index 7683515155..42f8be25a3 100755
--- a/t/t8006-blame-textconv.sh
+++ b/t/t8006-blame-textconv.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='git blame textconv support'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
find_blame() {
diff --git a/t/t8008-blame-formats.sh b/t/t8008-blame-formats.sh
index ae4b579d24..fb5d225a67 100755
--- a/t/t8008-blame-formats.sh
+++ b/t/t8008-blame-formats.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='blame output in various formats on a simple case'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
diff --git a/t/t8009-blame-vs-topicbranches.sh b/t/t8009-blame-vs-topicbranches.sh
index 72596e38b2..30331713b9 100755
--- a/t/t8009-blame-vs-topicbranches.sh
+++ b/t/t8009-blame-vs-topicbranches.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='blaming trough history with topic branches'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# Creates the history shown below. '*'s mark the first parent in the merges.
diff --git a/t/t8011-blame-split-file.sh b/t/t8011-blame-split-file.sh
index bdda0c03fe..da1801f4d2 100755
--- a/t/t8011-blame-split-file.sh
+++ b/t/t8011-blame-split-file.sh
@@ -10,6 +10,8 @@ Note that we need to use "blame -C" to find the commit for all lines. We will
not bother testing that the non-C case fails to find it. That is how blame
behaves now, but it is not a property we want to make sure is retained.
'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# help avoid typing and reading long strings of similar lines
diff --git a/t/t8012-blame-colors.sh b/t/t8012-blame-colors.sh
index c3a5f6d01f..9a79c109f2 100755
--- a/t/t8012-blame-colors.sh
+++ b/t/t8012-blame-colors.sh
@@ -5,6 +5,7 @@ GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
TEST_CREATE_REPO_NO_TEMPLATE=1
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
PROG='git blame -c'
diff --git a/t/t8013-blame-ignore-revs.sh b/t/t8013-blame-ignore-revs.sh
index dbfbd86e83..d33788d867 100755
--- a/t/t8013-blame-ignore-revs.sh
+++ b/t/t8013-blame-ignore-revs.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='ignore revisions when blaming'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# Creates:
diff --git a/t/t8014-blame-ignore-fuzzy.sh b/t/t8014-blame-ignore-fuzzy.sh
index 0bd0341301..933222cea1 100755
--- a/t/t8014-blame-ignore-fuzzy.sh
+++ b/t/t8014-blame-ignore-fuzzy.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='git blame ignore fuzzy heuristic'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
pick_author='s/^[0-9a-f^]* *(\([^ ]*\) .*/\1/'
diff --git a/t/t9001-send-email.sh b/t/t9001-send-email.sh
index 5a771000c9..64a4ab3736 100755
--- a/t/t9001-send-email.sh
+++ b/t/t9001-send-email.sh
@@ -1299,6 +1299,49 @@ test_expect_success $PREREQ 'utf8 sender is not duplicated' '
test_line_count = 1 msgfrom
'
+test_expect_success $PREREQ 'setup expect for cc list' "
+cat >expected-cc <<\EOF
+!recipient@example.com!
+!author@example.com!
+!one@example.com!
+!os@example.com!
+!odd_?=mail@example.com!
+!doug@example.com!
+!codev@example.com!
+!thor.au@example.com!
+EOF
+"
+
+test_expect_success $PREREQ 'cc list is sanitized' '
+ clean_fake_sendmail &&
+ test_commit weird_cc_body &&
+ test_when_finished "git reset --hard HEAD^" &&
+ git commit --amend -F - <<-EOF &&
+ Test Cc: sanitization.
+
+ Cc: Person, One <one@example.com>
+ Cc: Ronnie O${SQ}Sullivan <os@example.com>
+ Reviewed-by: Füñný Nâmé <odd_?=mail@example.com>
+ Reported-by: bugger on Jira
+ Reported-by: Douglas Reporter <doug@example.com> [from Jira profile]
+ BugID: 12345
+ Co-developed-by: "C. O. Developer" <codev@example.com>
+ Signed-off-by: A. U. Thor <thor.au@example.com>
+ EOF
+ git send-email -1 --to=recipient@example.com \
+ --smtp-server="$(pwd)/fake.sendmail" >actual-show-all-headers &&
+ test_cmp expected-cc commandline1 &&
+ test_grep "^(body) Adding cc: \"Person, One\" <one@example.com>" actual-show-all-headers &&
+ test_grep "^(body) Adding cc: Ronnie O${SQ}Sullivan <os@example.com>" actual-show-all-headers &&
+ test_grep "^(body) Adding cc: =?UTF-8?q?F=C3=BC=C3=B1n=C3=BD=20N=C3=A2m=C3=A9?="\
+" <odd_?=mail@example.com>" actual-show-all-headers &&
+ test_grep "^(body) Ignoring Reported-by .* bugger on Jira" actual-show-all-headers &&
+ test_grep "^(body) Adding cc: Douglas Reporter <doug@example.com>" actual-show-all-headers &&
+ test_grep ! "12345" actual-show-all-headers &&
+ test_grep "^(body) Adding cc: \"C. O. Developer\" <codev@example.com>" actual-show-all-headers &&
+ test_grep "^(body) Adding cc: \"A. U. Thor\" <thor.au@example.com>" actual-show-all-headers
+'
+
test_expect_success $PREREQ 'sendemail.composeencoding works' '
clean_fake_sendmail &&
git config sendemail.composeencoding iso-8859-1 &&
@@ -2526,7 +2569,7 @@ test_expect_success $PREREQ 'test forbidSendmailVariables behavior override' '
test_expect_success $PREREQ '--compose handles lowercase headers' '
write_script fake-editor <<-\EOF &&
- sed "s/^From:.*/from: edited-from@example.com/i" "$1" >"$1.tmp" &&
+ sed "s/^[Ff][Rr][Oo][Mm]:.*/from: edited-from@example.com/" "$1" >"$1.tmp" &&
mv "$1.tmp" "$1"
EOF
clean_fake_sendmail &&
diff --git a/t/t9004-example.sh b/t/t9004-example.sh
deleted file mode 100755
index 590aab0304..0000000000
--- a/t/t9004-example.sh
+++ /dev/null
@@ -1,12 +0,0 @@
-#!/bin/sh
-
-test_description='check that example code compiles and runs'
-
-TEST_PASSES_SANITIZE_LEAK=true
-. ./test-lib.sh
-
-test_expect_success 'decorate' '
- test-tool example-decorate
-'
-
-test_done
diff --git a/t/t9118-git-svn-funky-branch-names.sh b/t/t9118-git-svn-funky-branch-names.sh
index d3261e35b8..a34fd46ecc 100755
--- a/t/t9118-git-svn-funky-branch-names.sh
+++ b/t/t9118-git-svn-funky-branch-names.sh
@@ -38,7 +38,7 @@ test_expect_success 'setup svnrepo' '
# SVN 1.7 will truncate "not-a%40{0]" to just "not-a".
# Look at what SVN wound up naming the branch and use that.
# Be sure to escape the @ if it shows up.
-non_reflog=$(svn_cmd ls "$svnrepo/pr ject/branches" | sed -ne '/not-a/ { s/\///; s/@/%40/; p }')
+non_reflog=$(svn_cmd ls "$svnrepo/pr ject/branches" | sed -ne '/not-a/ { s/\///; s/@/%40/; p; }')
test_expect_success 'test clone with funky branch names' '
git svn clone -s "$svnrepo/pr ject" project &&
diff --git a/t/t9129-git-svn-i18n-commitencoding.sh b/t/t9129-git-svn-i18n-commitencoding.sh
index 185248a4cd..01e1e8a8f7 100755
--- a/t/t9129-git-svn-i18n-commitencoding.sh
+++ b/t/t9129-git-svn-i18n-commitencoding.sh
@@ -4,7 +4,6 @@
test_description='git svn honors i18n.commitEncoding in config'
-TEST_FAILS_SANITIZE_LEAK=true
. ./lib-git-svn.sh
compare_git_head_with () {
diff --git a/t/t9139-git-svn-non-utf8-commitencoding.sh b/t/t9139-git-svn-non-utf8-commitencoding.sh
index b7f756b2b7..22d80b0be2 100755
--- a/t/t9139-git-svn-non-utf8-commitencoding.sh
+++ b/t/t9139-git-svn-non-utf8-commitencoding.sh
@@ -4,7 +4,6 @@
test_description='git svn refuses to dcommit non-UTF8 messages'
-TEST_FAILS_SANITIZE_LEAK=true
. ./lib-git-svn.sh
# ISO-2022-JP can pass for valid UTF-8, so skipping that in this test
diff --git a/t/t9200-git-cvsexportcommit.sh b/t/t9200-git-cvsexportcommit.sh
index a44eabf0d8..3d4842164c 100755
--- a/t/t9200-git-cvsexportcommit.sh
+++ b/t/t9200-git-cvsexportcommit.sh
@@ -4,6 +4,7 @@
#
test_description='Test export of commits to CVS'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
if ! test_have_prereq PERL; then
diff --git a/t/t9210-scalar.sh b/t/t9210-scalar.sh
index 428339e342..a41b4fcc08 100755
--- a/t/t9210-scalar.sh
+++ b/t/t9210-scalar.sh
@@ -180,6 +180,44 @@ test_expect_success 'scalar reconfigure' '
test true = "$(git -C one/src config core.preloadIndex)"
'
+test_expect_success 'scalar reconfigure --all with includeIf.onbranch' '
+ repos="two three four" &&
+ for num in $repos
+ do
+ git init $num/src &&
+ scalar register $num/src &&
+ git -C $num/src config includeif."onbranch:foo".path something &&
+ git -C $num/src config core.preloadIndex false || return 1
+ done &&
+
+ scalar reconfigure --all &&
+
+ for num in $repos
+ do
+ test true = "$(git -C $num/src config core.preloadIndex)" || return 1
+ done
+'
+
+ test_expect_success 'scalar reconfigure --all with detached HEADs' '
+ repos="two three four" &&
+ for num in $repos
+ do
+ rm -rf $num/src &&
+ git init $num/src &&
+ scalar register $num/src &&
+ git -C $num/src config core.preloadIndex false &&
+ test_commit -C $num/src initial &&
+ git -C $num/src switch --detach HEAD || return 1
+ done &&
+
+ scalar reconfigure --all &&
+
+ for num in $repos
+ do
+ test true = "$(git -C $num/src config core.preloadIndex)" || return 1
+ done
+'
+
test_expect_success '`reconfigure -a` removes stale config entries' '
git init stale/src &&
scalar register stale &&
diff --git a/t/t9401-git-cvsserver-crlf.sh b/t/t9401-git-cvsserver-crlf.sh
index a34805acdc..a67e6abd49 100755
--- a/t/t9401-git-cvsserver-crlf.sh
+++ b/t/t9401-git-cvsserver-crlf.sh
@@ -12,6 +12,7 @@ repository using cvs CLI client via git-cvsserver server'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
marked_as () {
diff --git a/t/t9500-gitweb-standalone-no-errors.sh b/t/t9500-gitweb-standalone-no-errors.sh
index 7679780fb8..ccfa415384 100755
--- a/t/t9500-gitweb-standalone-no-errors.sh
+++ b/t/t9500-gitweb-standalone-no-errors.sh
@@ -13,6 +13,7 @@ or warnings to log.'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./lib-gitweb.sh
# ----------------------------------------------------------------------
diff --git a/t/t9502-gitweb-standalone-parse-output.sh b/t/t9502-gitweb-standalone-parse-output.sh
index 81d5625557..b41ea19331 100755
--- a/t/t9502-gitweb-standalone-parse-output.sh
+++ b/t/t9502-gitweb-standalone-parse-output.sh
@@ -13,6 +13,7 @@ in the HTTP header or the actual script output.'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./lib-gitweb.sh
# ----------------------------------------------------------------------
diff --git a/t/t9600-cvsimport.sh b/t/t9600-cvsimport.sh
index 5680849218..41fcf3606b 100755
--- a/t/t9600-cvsimport.sh
+++ b/t/t9600-cvsimport.sh
@@ -4,6 +4,7 @@ test_description='git cvsimport basic tests'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./lib-cvs.sh
if ! test_have_prereq NOT_ROOT; then
diff --git a/t/t9601-cvsimport-vendor-branch.sh b/t/t9601-cvsimport-vendor-branch.sh
index 116cddba3a..e007669495 100755
--- a/t/t9601-cvsimport-vendor-branch.sh
+++ b/t/t9601-cvsimport-vendor-branch.sh
@@ -35,6 +35,7 @@ test_description='git cvsimport handling of vendor branches'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./lib-cvs.sh
setup_cvs_test_repository t9601
diff --git a/t/t9602-cvsimport-branches-tags.sh b/t/t9602-cvsimport-branches-tags.sh
index e5266c9a87..3768e3bd8c 100755
--- a/t/t9602-cvsimport-branches-tags.sh
+++ b/t/t9602-cvsimport-branches-tags.sh
@@ -7,6 +7,7 @@ test_description='git cvsimport handling of branches and tags'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./lib-cvs.sh
setup_cvs_test_repository t9602
diff --git a/t/t9603-cvsimport-patchsets.sh b/t/t9603-cvsimport-patchsets.sh
index 19f38f78f2..2a387fdbaa 100755
--- a/t/t9603-cvsimport-patchsets.sh
+++ b/t/t9603-cvsimport-patchsets.sh
@@ -12,6 +12,8 @@
# bug.
test_description='git cvsimport testing for correct patchset estimation'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./lib-cvs.sh
setup_cvs_test_repository t9603
diff --git a/t/t9604-cvsimport-timestamps.sh b/t/t9604-cvsimport-timestamps.sh
index 2d03259729..9cf0685d56 100755
--- a/t/t9604-cvsimport-timestamps.sh
+++ b/t/t9604-cvsimport-timestamps.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='git cvsimport timestamps'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./lib-cvs.sh
test_lazy_prereq POSIX_TIMEZONE '
diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh
index 569cf23104..932d5ad759 100755
--- a/t/t9902-completion.sh
+++ b/t/t9902-completion.sh
@@ -73,7 +73,7 @@ _get_comp_words_by_ref ()
print_comp ()
{
local IFS=$'\n'
- echo "${COMPREPLY[*]}" > out
+ printf '%s\n' "${COMPREPLY[*]}" > out
}
run_completion ()
@@ -2518,6 +2518,29 @@ test_expect_success 'complete tree filename with metacharacters' '
EOF
'
+test_expect_success 'symbolic-ref completes builtin options' '
+ test_completion "git symbolic-ref --d" <<-\EOF
+ --delete Z
+ EOF
+'
+
+test_expect_success 'symbolic-ref completes short ref names' '
+ test_completion "git symbolic-ref foo m" <<-\EOF
+ main Z
+ mybranch Z
+ mytag Z
+ EOF
+'
+
+test_expect_success 'symbolic-ref completes full ref names' '
+ test_completion "git symbolic-ref foo refs/" <<-\EOF
+ refs/heads/main Z
+ refs/heads/mybranch Z
+ refs/tags/mytag Z
+ refs/tags/A Z
+ EOF
+'
+
test_expect_success PERL 'send-email' '
test_completion "git send-email --cov" <<-\EOF &&
--cover-from-description=Z
@@ -2719,30 +2742,58 @@ do
'
done
-test_expect_success 'git config - section' '
- test_completion "git config br" <<-\EOF
+test_expect_success 'git config subcommand' '
+ test_completion "git config " <<-\EOF
+ edit Z
+ get Z
+ list Z
+ remove-section Z
+ rename-section Z
+ set Z
+ unset Z
+ EOF
+'
+
+test_expect_success 'git config subcommand options' '
+ test_completion "git config get --show-" <<-\EOF
+ --show-names Z
+ --show-origin Z
+ --show-scope Z
+ EOF
+'
+
+test_expect_success 'git config get' '
+ test_when_finished "rm -f cfgfile" &&
+ git config set --file cfgfile foo.bar baz &&
+ test_completion "git config get --file cfgfile foo." <<-\EOF
+ foo.bar Z
+ EOF
+'
+
+test_expect_success 'git config set - section' '
+ test_completion "git config set br" <<-\EOF
branch.Z
browser.Z
EOF
'
-test_expect_success 'git config - section include, includeIf' '
- test_completion "git config inclu" <<-\EOF
+test_expect_success 'git config set - section include, includeIf' '
+ test_completion "git config set inclu" <<-\EOF
include.Z
includeIf.Z
EOF
'
-test_expect_success 'git config - variable name' '
- test_completion "git config log.d" <<-\EOF
+test_expect_success 'git config set - variable name' '
+ test_completion "git config set log.d" <<-\EOF
log.date Z
log.decorate Z
log.diffMerges Z
EOF
'
-test_expect_success 'git config - variable name include' '
- test_completion "git config include.p" <<-\EOF
+test_expect_success 'git config set - variable name include' '
+ test_completion "git config set include.p" <<-\EOF
include.path Z
EOF
'
@@ -2753,8 +2804,8 @@ test_expect_success 'setup for git config submodule tests' '
git submodule add ./sub
'
-test_expect_success 'git config - variable name - submodule and __git_compute_first_level_config_vars_for_section' '
- test_completion "git config submodule." <<-\EOF
+test_expect_success 'git config set - variable name - submodule and __git_compute_first_level_config_vars_for_section' '
+ test_completion "git config set submodule." <<-\EOF
submodule.active Z
submodule.alternateErrorStrategy Z
submodule.alternateLocation Z
@@ -2765,8 +2816,8 @@ test_expect_success 'git config - variable name - submodule and __git_compute_fi
EOF
'
-test_expect_success 'git config - variable name - __git_compute_second_level_config_vars_for_section' '
- test_completion "git config submodule.sub." <<-\EOF
+test_expect_success 'git config set - variable name - __git_compute_second_level_config_vars_for_section' '
+ test_completion "git config set submodule.sub." <<-\EOF
submodule.sub.url Z
submodule.sub.update Z
submodule.sub.branch Z
@@ -2776,8 +2827,8 @@ test_expect_success 'git config - variable name - __git_compute_second_level_con
EOF
'
-test_expect_success 'git config - value' '
- test_completion "git config color.pager " <<-\EOF
+test_expect_success 'git config set - value' '
+ test_completion "git config set color.pager " <<-\EOF
false Z
true Z
EOF
diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh
index 862d80c974..fde9bf54fc 100644
--- a/t/test-lib-functions.sh
+++ b/t/test-lib-functions.sh
@@ -458,6 +458,7 @@ test_commit_bulk () {
indir=.
ref=HEAD
n=1
+ notick=
message='commit %s'
filename='%s.t'
contents='content %s'
@@ -488,6 +489,9 @@ test_commit_bulk () {
filename="${1#--*=}-%s.t"
contents="${1#--*=} %s"
;;
+ --notick)
+ notick=yes
+ ;;
-*)
BUG "invalid test_commit_bulk option: $1"
;;
@@ -507,7 +511,10 @@ test_commit_bulk () {
while test "$total" -gt 0
do
- test_tick &&
+ if test -z "$notick"
+ then
+ test_tick
+ fi &&
echo "commit $ref"
printf 'author %s <%s> %s\n' \
"$GIT_AUTHOR_NAME" \
@@ -865,6 +872,24 @@ test_verify_prereq () {
BUG "'$test_prereq' does not look like a prereq"
}
+# assign the variable named by "$1" with the contents of "$2";
+# if "$2" is "-", then read stdin into "$1" instead
+test_body_or_stdin () {
+ if test "$2" != "-"
+ then
+ eval "$1=\$2"
+ return
+ fi
+
+ # start with a newline, to match hanging newline from open-quote style
+ eval "$1=\$LF"
+ local test_line
+ while IFS= read -r test_line
+ do
+ eval "$1=\${$1}\${test_line}\${LF}"
+ done
+}
+
test_expect_failure () {
test_start_ "$@"
test "$#" = 3 && { test_prereq=$1; shift; } || test_prereq=
@@ -874,9 +899,11 @@ test_expect_failure () {
export test_prereq
if ! test_skip "$@"
then
+ local test_body
+ test_body_or_stdin test_body "$2"
test -n "$test_skip_test_preamble" ||
- say >&3 "checking known breakage of $TEST_NUMBER.$test_count '$1': $2"
- if test_run_ "$2" expecting_failure
+ say >&3 "checking known breakage of $TEST_NUMBER.$test_count '$1': $test_body"
+ if test_run_ "$test_body" expecting_failure
then
test_known_broken_ok_ "$1"
else
@@ -895,13 +922,15 @@ test_expect_success () {
export test_prereq
if ! test_skip "$@"
then
+ local test_body
+ test_body_or_stdin test_body "$2"
test -n "$test_skip_test_preamble" ||
- say >&3 "expecting success of $TEST_NUMBER.$test_count '$1': $2"
- if test_run_ "$2"
+ say >&3 "expecting success of $TEST_NUMBER.$test_count '$1': $test_body"
+ if test_run_ "$test_body"
then
test_ok_ "$1"
else
- test_failure_ "$@"
+ test_failure_ "$1" "$test_body"
fi
fi
test_finish_
@@ -1096,6 +1125,11 @@ test_must_fail_acceptable () {
done
fi
+ if test "$1" = "nongit"
+ then
+ shift
+ fi
+
case "$1" in
git|__git*|scalar|test-tool|test_terminal)
return 0
diff --git a/t/test-lib.sh b/t/test-lib.sh
index 79d3e0e7d9..54247604cb 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -1269,9 +1269,12 @@ check_test_results_san_file_ () {
then
say "As TEST_PASSES_SANITIZE_LEAK=true isn't set the above leak is 'ok' with GIT_TEST_PASSING_SANITIZE_LEAK=check" &&
invert_exit_code=t
- else
- say "With GIT_TEST_SANITIZE_LEAK_LOG=true our logs revealed a memory leak, exit non-zero!" &&
+ elif test "$test_failure" = 0
+ then
+ say "Our logs revealed a memory leak, exit non-zero!" &&
invert_exit_code=t
+ else
+ say "Our logs revealed a memory leak..."
fi
}
@@ -1575,33 +1578,28 @@ then
test_done
fi
- if test_bool_env GIT_TEST_SANITIZE_LEAK_LOG false
+ if ! mkdir -p "$TEST_RESULTS_SAN_DIR"
then
- if ! mkdir -p "$TEST_RESULTS_SAN_DIR"
- then
- BAIL_OUT "cannot create $TEST_RESULTS_SAN_DIR"
- fi &&
- TEST_RESULTS_SAN_FILE="$TEST_RESULTS_SAN_DIR/$TEST_RESULTS_SAN_FILE_PFX"
+ BAIL_OUT "cannot create $TEST_RESULTS_SAN_DIR"
+ fi &&
+ TEST_RESULTS_SAN_FILE="$TEST_RESULTS_SAN_DIR/$TEST_RESULTS_SAN_FILE_PFX"
- # In case "test-results" is left over from a previous
- # run: Only report if new leaks show up.
- TEST_RESULTS_SAN_DIR_NR_LEAKS_STARTUP=$(nr_san_dir_leaks_)
+ # In case "test-results" is left over from a previous
+ # run: Only report if new leaks show up.
+ TEST_RESULTS_SAN_DIR_NR_LEAKS_STARTUP=$(nr_san_dir_leaks_)
- # Don't litter *.leak dirs if there was nothing to report
- test_atexit "rmdir \"$TEST_RESULTS_SAN_DIR\" 2>/dev/null || :"
+ # Don't litter *.leak dirs if there was nothing to report
+ test_atexit "rmdir \"$TEST_RESULTS_SAN_DIR\" 2>/dev/null || :"
+
+ prepend_var LSAN_OPTIONS : dedup_token_length=9999
+ prepend_var LSAN_OPTIONS : log_exe_name=1
+ prepend_var LSAN_OPTIONS : log_path=\"$TEST_RESULTS_SAN_FILE\"
+ export LSAN_OPTIONS
- prepend_var LSAN_OPTIONS : dedup_token_length=9999
- prepend_var LSAN_OPTIONS : log_exe_name=1
- prepend_var LSAN_OPTIONS : log_path=\"$TEST_RESULTS_SAN_FILE\"
- export LSAN_OPTIONS
- fi
elif test "$GIT_TEST_PASSING_SANITIZE_LEAK" = "check" ||
test_bool_env GIT_TEST_PASSING_SANITIZE_LEAK false
then
BAIL_OUT_ENV_NEEDS_SANITIZE_LEAK "GIT_TEST_PASSING_SANITIZE_LEAK=true"
-elif test_bool_env GIT_TEST_SANITIZE_LEAK_LOG false
-then
- BAIL_OUT_ENV_NEEDS_SANITIZE_LEAK "GIT_TEST_SANITIZE_LEAK_LOG=true"
fi
if test "${GIT_TEST_CHAIN_LINT:-1}" != 0 &&
diff --git a/t/test-terminal.perl b/t/test-terminal.perl
index 3810e9bb43..b8fd6a4f13 100755
--- a/t/test-terminal.perl
+++ b/t/test-terminal.perl
@@ -5,17 +5,15 @@ use warnings;
use IO::Pty;
use File::Copy;
-# Run @$argv in the background with stdio redirected to $in, $out and $err.
+# Run @$argv in the background with stdio redirected to $out and $err.
sub start_child {
- my ($argv, $in, $out, $err) = @_;
+ my ($argv, $out, $err) = @_;
my $pid = fork;
if (not defined $pid) {
die "fork failed: $!"
} elsif ($pid == 0) {
- open STDIN, "<&", $in;
open STDOUT, ">&", $out;
open STDERR, ">&", $err;
- close $in;
close $out;
exec(@$argv) or die "cannot exec '$argv->[0]': $!"
}
@@ -51,17 +49,6 @@ sub xsendfile {
copy($in, $out, 4096) or $!{EIO} or die "cannot copy from child: $!";
}
-sub copy_stdin {
- my ($in) = @_;
- my $pid = fork;
- if (!$pid) {
- xsendfile($in, \*STDIN);
- exit 0;
- }
- close($in);
- return $pid;
-}
-
sub copy_stdio {
my ($out, $err) = @_;
my $pid = fork;
@@ -81,25 +68,15 @@ if ($#ARGV < 1) {
die "usage: test-terminal program args";
}
$ENV{TERM} = 'vt100';
-my $parent_in = new IO::Pty;
my $parent_out = new IO::Pty;
my $parent_err = new IO::Pty;
-$parent_in->set_raw();
$parent_out->set_raw();
$parent_err->set_raw();
-$parent_in->slave->set_raw();
$parent_out->slave->set_raw();
$parent_err->slave->set_raw();
-my $pid = start_child(\@ARGV, $parent_in->slave, $parent_out->slave, $parent_err->slave);
-close $parent_in->slave;
+my $pid = start_child(\@ARGV, $parent_out->slave, $parent_err->slave);
close $parent_out->slave;
close $parent_err->slave;
-my $in_pid = copy_stdin($parent_in);
copy_stdio($parent_out, $parent_err);
my $ret = finish_child($pid);
-# If the child process terminates before our copy_stdin() process is able to
-# write all of its data to $parent_in, the copy_stdin() process could stall.
-# Send SIGTERM to it to ensure it terminates.
-kill 'TERM', $in_pid;
-finish_child($in_pid);
exit($ret);
diff --git a/t/unit-tests/lib-oid.c b/t/unit-tests/lib-oid.c
new file mode 100644
index 0000000000..37105f0a8f
--- /dev/null
+++ b/t/unit-tests/lib-oid.c
@@ -0,0 +1,52 @@
+#include "test-lib.h"
+#include "lib-oid.h"
+#include "strbuf.h"
+#include "hex.h"
+
+static int init_hash_algo(void)
+{
+ static int algo = -1;
+
+ if (algo < 0) {
+ const char *algo_name = getenv("GIT_TEST_DEFAULT_HASH");
+ algo = algo_name ? hash_algo_by_name(algo_name) : GIT_HASH_SHA1;
+
+ if (!check(algo != GIT_HASH_UNKNOWN))
+ test_msg("BUG: invalid GIT_TEST_DEFAULT_HASH value ('%s')",
+ algo_name);
+ }
+ return algo;
+}
+
+static int get_oid_arbitrary_hex_algop(const char *hex, struct object_id *oid,
+ const struct git_hash_algo *algop)
+{
+ int ret;
+ size_t sz = strlen(hex);
+ struct strbuf buf = STRBUF_INIT;
+
+ if (!check(sz <= algop->hexsz)) {
+ test_msg("BUG: hex string (%s) bigger than maximum allowed (%lu)",
+ hex, (unsigned long)algop->hexsz);
+ return -1;
+ }
+
+ strbuf_add(&buf, hex, sz);
+ strbuf_addchars(&buf, '0', algop->hexsz - sz);
+
+ ret = get_oid_hex_algop(buf.buf, oid, algop);
+ if (!check_int(ret, ==, 0))
+ test_msg("BUG: invalid hex input (%s) provided", hex);
+
+ strbuf_release(&buf);
+ return ret;
+}
+
+int get_oid_arbitrary_hex(const char *hex, struct object_id *oid)
+{
+ int hash_algo = init_hash_algo();
+
+ if (!check_int(hash_algo, !=, GIT_HASH_UNKNOWN))
+ return -1;
+ return get_oid_arbitrary_hex_algop(hex, oid, &hash_algos[hash_algo]);
+}
diff --git a/t/unit-tests/lib-oid.h b/t/unit-tests/lib-oid.h
new file mode 100644
index 0000000000..8d2acca768
--- /dev/null
+++ b/t/unit-tests/lib-oid.h
@@ -0,0 +1,17 @@
+#ifndef LIB_OID_H
+#define LIB_OID_H
+
+#include "hash.h"
+
+/*
+ * Convert arbitrary hex string to object_id.
+ * For example, passing "abc12" will generate
+ * "abc1200000000000000000000000000000000000" hex of length 40 for SHA-1 and
+ * create object_id with that.
+ * WARNING: passing a string of length more than the hexsz of respective hash
+ * algo is not allowed. The hash algo is decided based on GIT_TEST_DEFAULT_HASH
+ * environment variable.
+ */
+int get_oid_arbitrary_hex(const char *s, struct object_id *oid);
+
+#endif /* LIB_OID_H */
diff --git a/t/unit-tests/t-example-decorate.c b/t/unit-tests/t-example-decorate.c
new file mode 100644
index 0000000000..a4a75db735
--- /dev/null
+++ b/t/unit-tests/t-example-decorate.c
@@ -0,0 +1,82 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
+#include "test-lib.h"
+#include "object.h"
+#include "decorate.h"
+#include "repository.h"
+
+struct test_vars {
+ struct object *one, *two, *three;
+ struct decoration n;
+ int decoration_a, decoration_b;
+};
+
+static void t_add(struct test_vars *vars)
+{
+ void *ret = add_decoration(&vars->n, vars->one, &vars->decoration_a);
+
+ if (!check(ret == NULL))
+ test_msg("when adding a brand-new object, NULL should be returned");
+ ret = add_decoration(&vars->n, vars->two, NULL);
+ if (!check(ret == NULL))
+ test_msg("when adding a brand-new object, NULL should be returned");
+}
+
+static void t_readd(struct test_vars *vars)
+{
+ void *ret = add_decoration(&vars->n, vars->one, NULL);
+
+ if (!check(ret == &vars->decoration_a))
+ test_msg("when readding an already existing object, existing decoration should be returned");
+ ret = add_decoration(&vars->n, vars->two, &vars->decoration_b);
+ if (!check(ret == NULL))
+ test_msg("when readding an already existing object, existing decoration should be returned");
+}
+
+static void t_lookup(struct test_vars *vars)
+{
+ void *ret = lookup_decoration(&vars->n, vars->one);
+
+ if (!check(ret == NULL))
+ test_msg("lookup should return added declaration");
+ ret = lookup_decoration(&vars->n, vars->two);
+ if (!check(ret == &vars->decoration_b))
+ test_msg("lookup should return added declaration");
+ ret = lookup_decoration(&vars->n, vars->three);
+ if (!check(ret == NULL))
+ test_msg("lookup for unknown object should return NULL");
+}
+
+static void t_loop(struct test_vars *vars)
+{
+ int i, objects_noticed = 0;
+
+ for (i = 0; i < vars->n.size; i++) {
+ if (vars->n.entries[i].base)
+ objects_noticed++;
+ }
+ if (!check_int(objects_noticed, ==, 2))
+ test_msg("should have 2 objects");
+}
+
+int cmd_main(int argc UNUSED, const char **argv UNUSED)
+{
+ struct object_id one_oid = { { 1 } }, two_oid = { { 2 } }, three_oid = { { 3 } };
+ struct test_vars vars = { 0 };
+
+ vars.one = lookup_unknown_object(the_repository, &one_oid);
+ vars.two = lookup_unknown_object(the_repository, &two_oid);
+ vars.three = lookup_unknown_object(the_repository, &three_oid);
+
+ TEST(t_add(&vars),
+ "Add 2 objects, one with a non-NULL decoration and one with a NULL decoration.");
+ TEST(t_readd(&vars),
+ "When re-adding an already existing object, the old decoration is returned.");
+ TEST(t_lookup(&vars),
+ "Lookup returns the added declarations, or NULL if the object was never added.");
+ TEST(t_loop(&vars), "The user can also loop through all entries.");
+
+ clear_decoration(&vars.n, NULL);
+
+ return test_done();
+}
diff --git a/t/unit-tests/t-hash.c b/t/unit-tests/t-hash.c
new file mode 100644
index 0000000000..e9a78bf2c0
--- /dev/null
+++ b/t/unit-tests/t-hash.c
@@ -0,0 +1,84 @@
+#include "test-lib.h"
+#include "hex.h"
+#include "strbuf.h"
+
+static void check_hash_data(const void *data, size_t data_length,
+ const char *expected_hashes[])
+{
+ if (!check(data != NULL)) {
+ test_msg("BUG: NULL data pointer provided");
+ return;
+ }
+
+ for (size_t i = 1; i < ARRAY_SIZE(hash_algos); i++) {
+ git_hash_ctx ctx;
+ unsigned char hash[GIT_MAX_HEXSZ];
+ const struct git_hash_algo *algop = &hash_algos[i];
+
+ algop->init_fn(&ctx);
+ algop->update_fn(&ctx, data, data_length);
+ algop->final_fn(hash, &ctx);
+
+ if (!check_str(hash_to_hex_algop(hash, algop), expected_hashes[i - 1]))
+ test_msg("result does not match with the expected for %s\n", hash_algos[i].name);
+ }
+}
+
+/* Works with a NUL terminated string. Doesn't work if it should contain a NUL character. */
+#define TEST_HASH_STR(data, expected_sha1, expected_sha256) do { \
+ const char *expected_hashes[] = { expected_sha1, expected_sha256 }; \
+ TEST(check_hash_data(data, strlen(data), expected_hashes), \
+ "SHA1 and SHA256 (%s) works", #data); \
+ } while (0)
+
+/* Only works with a literal string, useful when it contains a NUL character. */
+#define TEST_HASH_LITERAL(literal, expected_sha1, expected_sha256) do { \
+ const char *expected_hashes[] = { expected_sha1, expected_sha256 }; \
+ TEST(check_hash_data(literal, (sizeof(literal) - 1), expected_hashes), \
+ "SHA1 and SHA256 (%s) works", #literal); \
+ } while (0)
+
+int cmd_main(int argc, const char **argv)
+{
+ struct strbuf aaaaaaaaaa_100000 = STRBUF_INIT;
+ struct strbuf alphabet_100000 = STRBUF_INIT;
+
+ strbuf_addstrings(&aaaaaaaaaa_100000, "aaaaaaaaaa", 100000);
+ strbuf_addstrings(&alphabet_100000, "abcdefghijklmnopqrstuvwxyz", 100000);
+
+ TEST_HASH_STR("",
+ "da39a3ee5e6b4b0d3255bfef95601890afd80709",
+ "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855");
+ TEST_HASH_STR("a",
+ "86f7e437faa5a7fce15d1ddcb9eaeaea377667b8",
+ "ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb");
+ TEST_HASH_STR("abc",
+ "a9993e364706816aba3e25717850c26c9cd0d89d",
+ "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad");
+ TEST_HASH_STR("message digest",
+ "c12252ceda8be8994d5fa0290a47231c1d16aae3",
+ "f7846f55cf23e14eebeab5b4e1550cad5b509e3348fbc4efa3a1413d393cb650");
+ TEST_HASH_STR("abcdefghijklmnopqrstuvwxyz",
+ "32d10c7b8cf96570ca04ce37f2a19d84240d3a89",
+ "71c480df93d6ae2f1efad1447c66c9525e316218cf51fc8d9ed832f2daf18b73");
+ TEST_HASH_STR(aaaaaaaaaa_100000.buf,
+ "34aa973cd4c4daa4f61eeb2bdbad27316534016f",
+ "cdc76e5c9914fb9281a1c7e284d73e67f1809a48a497200e046d39ccc7112cd0");
+ TEST_HASH_STR(alphabet_100000.buf,
+ "e7da7c55b3484fdf52aebec9cbe7b85a98f02fd4",
+ "e406ba321ca712ad35a698bf0af8d61fc4dc40eca6bdcea4697962724ccbde35");
+ TEST_HASH_LITERAL("blob 0\0",
+ "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391",
+ "473a0f4c3be8a93681a267e3b1e9a7dcda1185436fe141f7749120a303721813");
+ TEST_HASH_LITERAL("blob 3\0abc",
+ "f2ba8f84ab5c1bce84a7b441cb1959cfc7093b7f",
+ "c1cf6e465077930e88dc5136641d402f72a229ddd996f627d60e9639eaba35a6");
+ TEST_HASH_LITERAL("tree 0\0",
+ "4b825dc642cb6eb9a060e54bf8d69288fbee4904",
+ "6ef19b41225c5369f1c104d45d8d85efa9b057b53b14b4b9b939dd74decc5321");
+
+ strbuf_release(&aaaaaaaaaa_100000);
+ strbuf_release(&alphabet_100000);
+
+ return test_done();
+}
diff --git a/t/unit-tests/t-oidmap.c b/t/unit-tests/t-oidmap.c
new file mode 100644
index 0000000000..b22e52d08b
--- /dev/null
+++ b/t/unit-tests/t-oidmap.c
@@ -0,0 +1,181 @@
+#include "test-lib.h"
+#include "lib-oid.h"
+#include "oidmap.h"
+#include "hash.h"
+#include "hex.h"
+
+/*
+ * Elements we will put in oidmap structs are made of a key: the entry.oid
+ * field, which is of type struct object_id, and a value: the name field (could
+ * be a refname for example).
+ */
+struct test_entry {
+ struct oidmap_entry entry;
+ char name[FLEX_ARRAY];
+};
+
+static const char *const key_val[][2] = { { "11", "one" },
+ { "22", "two" },
+ { "33", "three" } };
+
+static void setup(void (*f)(struct oidmap *map))
+{
+ struct oidmap map = OIDMAP_INIT;
+ int ret = 0;
+
+ for (size_t i = 0; i < ARRAY_SIZE(key_val); i++){
+ struct test_entry *entry;
+
+ FLEX_ALLOC_STR(entry, name, key_val[i][1]);
+ if ((ret = get_oid_arbitrary_hex(key_val[i][0], &entry->entry.oid))) {
+ free(entry);
+ break;
+ }
+ entry = oidmap_put(&map, entry);
+ if (!check(entry == NULL))
+ free(entry);
+ }
+
+ if (!ret)
+ f(&map);
+ oidmap_free(&map, 1);
+}
+
+static void t_replace(struct oidmap *map)
+{
+ struct test_entry *entry, *prev;
+
+ FLEX_ALLOC_STR(entry, name, "un");
+ if (get_oid_arbitrary_hex("11", &entry->entry.oid))
+ return;
+ prev = oidmap_put(map, entry);
+ if (!check(prev != NULL))
+ return;
+ check_str(prev->name, "one");
+ free(prev);
+
+ FLEX_ALLOC_STR(entry, name, "deux");
+ if (get_oid_arbitrary_hex("22", &entry->entry.oid))
+ return;
+ prev = oidmap_put(map, entry);
+ if (!check(prev != NULL))
+ return;
+ check_str(prev->name, "two");
+ free(prev);
+}
+
+static void t_get(struct oidmap *map)
+{
+ struct test_entry *entry;
+ struct object_id oid;
+
+ if (get_oid_arbitrary_hex("22", &oid))
+ return;
+ entry = oidmap_get(map, &oid);
+ if (!check(entry != NULL))
+ return;
+ check_str(entry->name, "two");
+
+ if (get_oid_arbitrary_hex("44", &oid))
+ return;
+ check(oidmap_get(map, &oid) == NULL);
+
+ if (get_oid_arbitrary_hex("11", &oid))
+ return;
+ entry = oidmap_get(map, &oid);
+ if (!check(entry != NULL))
+ return;
+ check_str(entry->name, "one");
+}
+
+static void t_remove(struct oidmap *map)
+{
+ struct test_entry *entry;
+ struct object_id oid;
+
+ if (get_oid_arbitrary_hex("11", &oid))
+ return;
+ entry = oidmap_remove(map, &oid);
+ if (!check(entry != NULL))
+ return;
+ check_str(entry->name, "one");
+ check(oidmap_get(map, &oid) == NULL);
+ free(entry);
+
+ if (get_oid_arbitrary_hex("22", &oid))
+ return;
+ entry = oidmap_remove(map, &oid);
+ if (!check(entry != NULL))
+ return;
+ check_str(entry->name, "two");
+ check(oidmap_get(map, &oid) == NULL);
+ free(entry);
+
+ if (get_oid_arbitrary_hex("44", &oid))
+ return;
+ check(oidmap_remove(map, &oid) == NULL);
+}
+
+static int key_val_contains(struct test_entry *entry, char seen[])
+{
+ for (size_t i = 0; i < ARRAY_SIZE(key_val); i++) {
+ struct object_id oid;
+
+ if (get_oid_arbitrary_hex(key_val[i][0], &oid))
+ return -1;
+
+ if (oideq(&entry->entry.oid, &oid)) {
+ if (seen[i])
+ return 2;
+ seen[i] = 1;
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static void t_iterate(struct oidmap *map)
+{
+ struct oidmap_iter iter;
+ struct test_entry *entry;
+ char seen[ARRAY_SIZE(key_val)] = { 0 };
+ int count = 0;
+
+ oidmap_iter_init(map, &iter);
+ while ((entry = oidmap_iter_next(&iter))) {
+ int ret;
+ if (!check_int((ret = key_val_contains(entry, seen)), ==, 0)) {
+ switch (ret) {
+ case -1:
+ break; /* error message handled by get_oid_arbitrary_hex() */
+ case 1:
+ test_msg("obtained entry was not given in the input\n"
+ " name: %s\n oid: %s\n",
+ entry->name, oid_to_hex(&entry->entry.oid));
+ break;
+ case 2:
+ test_msg("duplicate entry detected\n"
+ " name: %s\n oid: %s\n",
+ entry->name, oid_to_hex(&entry->entry.oid));
+ break;
+ default:
+ test_msg("BUG: invalid return value (%d) from key_val_contains()",
+ ret);
+ break;
+ }
+ } else {
+ count++;
+ }
+ }
+ check_int(count, ==, ARRAY_SIZE(key_val));
+ check_int(hashmap_get_size(&map->map), ==, ARRAY_SIZE(key_val));
+}
+
+int cmd_main(int argc UNUSED, const char **argv UNUSED)
+{
+ TEST(setup(t_replace), "replace works");
+ TEST(setup(t_get), "get works");
+ TEST(setup(t_remove), "remove works");
+ TEST(setup(t_iterate), "iterate works");
+ return test_done();
+}
diff --git a/t/unit-tests/t-oidtree.c b/t/unit-tests/t-oidtree.c
new file mode 100644
index 0000000000..a38754b066
--- /dev/null
+++ b/t/unit-tests/t-oidtree.c
@@ -0,0 +1,122 @@
+#include "test-lib.h"
+#include "lib-oid.h"
+#include "oidtree.h"
+#include "hash.h"
+#include "hex.h"
+#include "strvec.h"
+
+#define FILL_TREE(tree, ...) \
+ do { \
+ const char *hexes[] = { __VA_ARGS__ }; \
+ if (fill_tree_loc(tree, hexes, ARRAY_SIZE(hexes))) \
+ return; \
+ } while (0)
+
+static int fill_tree_loc(struct oidtree *ot, const char *hexes[], size_t n)
+{
+ for (size_t i = 0; i < n; i++) {
+ struct object_id oid;
+ if (!check_int(get_oid_arbitrary_hex(hexes[i], &oid), ==, 0))
+ return -1;
+ oidtree_insert(ot, &oid);
+ }
+ return 0;
+}
+
+static void check_contains(struct oidtree *ot, const char *hex, int expected)
+{
+ struct object_id oid;
+
+ if (!check_int(get_oid_arbitrary_hex(hex, &oid), ==, 0))
+ return;
+ if (!check_int(oidtree_contains(ot, &oid), ==, expected))
+ test_msg("oid: %s", oid_to_hex(&oid));
+}
+
+struct expected_hex_iter {
+ size_t i;
+ struct strvec expected_hexes;
+ const char *query;
+};
+
+static enum cb_next check_each_cb(const struct object_id *oid, void *data)
+{
+ struct expected_hex_iter *hex_iter = data;
+ struct object_id expected;
+
+ if (!check_int(hex_iter->i, <, hex_iter->expected_hexes.nr)) {
+ test_msg("error: extraneous callback for query: ('%s'), object_id: ('%s')",
+ hex_iter->query, oid_to_hex(oid));
+ return CB_BREAK;
+ }
+
+ if (!check_int(get_oid_arbitrary_hex(hex_iter->expected_hexes.v[hex_iter->i],
+ &expected), ==, 0))
+ ; /* the data is bogus and cannot be used */
+ else if (!check(oideq(oid, &expected)))
+ test_msg("expected: %s\n got: %s\n query: %s",
+ oid_to_hex(&expected), oid_to_hex(oid), hex_iter->query);
+
+ hex_iter->i += 1;
+ return CB_CONTINUE;
+}
+
+LAST_ARG_MUST_BE_NULL
+static void check_each(struct oidtree *ot, const char *query, ...)
+{
+ struct object_id oid;
+ struct expected_hex_iter hex_iter = { .expected_hexes = STRVEC_INIT,
+ .query = query };
+ const char *arg;
+ va_list hex_args;
+
+ va_start(hex_args, query);
+ while ((arg = va_arg(hex_args, const char *)))
+ strvec_push(&hex_iter.expected_hexes, arg);
+ va_end(hex_args);
+
+ if (!check_int(get_oid_arbitrary_hex(query, &oid), ==, 0))
+ return;
+ oidtree_each(ot, &oid, strlen(query), check_each_cb, &hex_iter);
+
+ if (!check_int(hex_iter.i, ==, hex_iter.expected_hexes.nr))
+ test_msg("error: could not find some 'object_id's for query ('%s')", query);
+ strvec_clear(&hex_iter.expected_hexes);
+}
+
+static void setup(void (*f)(struct oidtree *ot))
+{
+ struct oidtree ot;
+
+ oidtree_init(&ot);
+ f(&ot);
+ oidtree_clear(&ot);
+}
+
+static void t_contains(struct oidtree *ot)
+{
+ FILL_TREE(ot, "444", "1", "2", "3", "4", "5", "a", "b", "c", "d", "e");
+ check_contains(ot, "44", 0);
+ check_contains(ot, "441", 0);
+ check_contains(ot, "440", 0);
+ check_contains(ot, "444", 1);
+ check_contains(ot, "4440", 1);
+ check_contains(ot, "4444", 0);
+}
+
+static void t_each(struct oidtree *ot)
+{
+ FILL_TREE(ot, "f", "9", "8", "123", "321", "320", "a", "b", "c", "d", "e");
+ check_each(ot, "12300", "123", NULL);
+ check_each(ot, "3211", NULL); /* should not reach callback */
+ check_each(ot, "3210", "321", NULL);
+ check_each(ot, "32100", "321", NULL);
+ check_each(ot, "32", "320", "321", NULL);
+}
+
+int cmd_main(int argc UNUSED, const char **argv UNUSED)
+{
+ TEST(setup(t_contains), "oidtree insert and contains works");
+ TEST(setup(t_each), "oidtree each works");
+ return test_done();
+}
diff --git a/t/unit-tests/t-reftable-basics.c b/t/unit-tests/t-reftable-basics.c
new file mode 100644
index 0000000000..4e80bdf16d
--- /dev/null
+++ b/t/unit-tests/t-reftable-basics.c
@@ -0,0 +1,160 @@
+/*
+Copyright 2020 Google LLC
+
+Use of this source code is governed by a BSD-style
+license that can be found in the LICENSE file or at
+https://developers.google.com/open-source/licenses/bsd
+*/
+
+#include "test-lib.h"
+#include "reftable/basics.h"
+
+struct integer_needle_lesseq_args {
+ int needle;
+ int *haystack;
+};
+
+static int integer_needle_lesseq(size_t i, void *_args)
+{
+ struct integer_needle_lesseq_args *args = _args;
+ return args->needle <= args->haystack[i];
+}
+
+static void test_binsearch(void)
+{
+ int haystack[] = { 2, 4, 6, 8, 10 };
+ struct {
+ int needle;
+ size_t expected_idx;
+ } testcases[] = {
+ {-9000, 0},
+ {-1, 0},
+ {0, 0},
+ {2, 0},
+ {3, 1},
+ {4, 1},
+ {7, 3},
+ {9, 4},
+ {10, 4},
+ {11, 5},
+ {9000, 5},
+ };
+
+ for (size_t i = 0; i < ARRAY_SIZE(testcases); i++) {
+ struct integer_needle_lesseq_args args = {
+ .haystack = haystack,
+ .needle = testcases[i].needle,
+ };
+ size_t idx;
+
+ idx = binsearch(ARRAY_SIZE(haystack), &integer_needle_lesseq, &args);
+ check_int(idx, ==, testcases[i].expected_idx);
+ }
+}
+
+static void test_names_length(void)
+{
+ const char *a[] = { "a", "b", NULL };
+ check_int(names_length(a), ==, 2);
+}
+
+static void test_names_equal(void)
+{
+ const char *a[] = { "a", "b", "c", NULL };
+ const char *b[] = { "a", "b", "d", NULL };
+ const char *c[] = { "a", "b", NULL };
+
+ check(names_equal(a, a));
+ check(!names_equal(a, b));
+ check(!names_equal(a, c));
+}
+
+static void test_parse_names_normal(void)
+{
+ char in1[] = "line\n";
+ char in2[] = "a\nb\nc";
+ char **out = NULL;
+ parse_names(in1, strlen(in1), &out);
+ check_str(out[0], "line");
+ check(!out[1]);
+ free_names(out);
+
+ parse_names(in2, strlen(in2), &out);
+ check_str(out[0], "a");
+ check_str(out[1], "b");
+ check_str(out[2], "c");
+ check(!out[3]);
+ free_names(out);
+}
+
+static void test_parse_names_drop_empty(void)
+{
+ char in[] = "a\n\nb\n";
+ char **out = NULL;
+ parse_names(in, strlen(in), &out);
+ check_str(out[0], "a");
+ /* simply '\n' should be dropped as empty string */
+ check_str(out[1], "b");
+ check(!out[2]);
+ free_names(out);
+}
+
+static void test_common_prefix(void)
+{
+ struct strbuf a = STRBUF_INIT;
+ struct strbuf b = STRBUF_INIT;
+ struct {
+ const char *a, *b;
+ int want;
+ } cases[] = {
+ {"abcdef", "abc", 3},
+ { "abc", "ab", 2 },
+ { "", "abc", 0 },
+ { "abc", "abd", 2 },
+ { "abc", "pqr", 0 },
+ };
+
+ for (size_t i = 0; i < ARRAY_SIZE(cases); i++) {
+ strbuf_addstr(&a, cases[i].a);
+ strbuf_addstr(&b, cases[i].b);
+ check_int(common_prefix_size(&a, &b), ==, cases[i].want);
+ strbuf_reset(&a);
+ strbuf_reset(&b);
+ }
+ strbuf_release(&a);
+ strbuf_release(&b);
+}
+
+static void test_u24_roundtrip(void)
+{
+ uint32_t in = 0x112233;
+ uint8_t dest[3];
+ uint32_t out;
+ put_be24(dest, in);
+ out = get_be24(dest);
+ check_int(in, ==, out);
+}
+
+static void test_u16_roundtrip(void)
+{
+ uint32_t in = 0xfef1;
+ uint8_t dest[3];
+ uint32_t out;
+ put_be16(dest, in);
+ out = get_be16(dest);
+ check_int(in, ==, out);
+}
+
+int cmd_main(int argc, const char *argv[])
+{
+ TEST(test_common_prefix(), "common_prefix_size works");
+ TEST(test_parse_names_normal(), "parse_names works for basic input");
+ TEST(test_parse_names_drop_empty(), "parse_names drops empty string");
+ TEST(test_binsearch(), "binary search with binsearch works");
+ TEST(test_names_length(), "names_length retuns size of a NULL-terminated string array");
+ TEST(test_names_equal(), "names_equal compares NULL-terminated string arrays");
+ TEST(test_u24_roundtrip(), "put_be24 and get_be24 work");
+ TEST(test_u16_roundtrip(), "put_be16 and get_be16 work");
+
+ return test_done();
+}
diff --git a/t/unit-tests/t-reftable-record.c b/t/unit-tests/t-reftable-record.c
new file mode 100644
index 0000000000..cb649ee419
--- /dev/null
+++ b/t/unit-tests/t-reftable-record.c
@@ -0,0 +1,551 @@
+/*
+ Copyright 2020 Google LLC
+
+ Use of this source code is governed by a BSD-style
+ license that can be found in the LICENSE file or at
+ https://developers.google.com/open-source/licenses/bsd
+*/
+
+#include "test-lib.h"
+#include "reftable/constants.h"
+#include "reftable/record.h"
+
+static void t_copy(struct reftable_record *rec)
+{
+ struct reftable_record copy;
+ uint8_t typ;
+
+ typ = reftable_record_type(rec);
+ reftable_record_init(&copy, typ);
+ reftable_record_copy_from(&copy, rec, GIT_SHA1_RAWSZ);
+ /* do it twice to catch memory leaks */
+ reftable_record_copy_from(&copy, rec, GIT_SHA1_RAWSZ);
+ check(reftable_record_equal(rec, &copy, GIT_SHA1_RAWSZ));
+
+ reftable_record_release(&copy);
+}
+
+static void t_varint_roundtrip(void)
+{
+ uint64_t inputs[] = { 0,
+ 1,
+ 27,
+ 127,
+ 128,
+ 257,
+ 4096,
+ ((uint64_t)1 << 63),
+ ((uint64_t)1 << 63) + ((uint64_t)1 << 63) - 1 };
+
+ for (size_t i = 0; i < ARRAY_SIZE(inputs); i++) {
+ uint8_t dest[10];
+
+ struct string_view out = {
+ .buf = dest,
+ .len = sizeof(dest),
+ };
+ uint64_t in = inputs[i];
+ int n = put_var_int(&out, in);
+ uint64_t got = 0;
+
+ check_int(n, >, 0);
+ out.len = n;
+ n = get_var_int(&got, &out);
+ check_int(n, >, 0);
+
+ check_int(got, ==, in);
+ }
+}
+
+static void set_hash(uint8_t *h, int j)
+{
+ for (int i = 0; i < hash_size(GIT_SHA1_FORMAT_ID); i++)
+ h[i] = (j >> i) & 0xff;
+}
+
+static void t_reftable_ref_record_comparison(void)
+{
+ struct reftable_record in[3] = {
+ {
+ .type = BLOCK_TYPE_REF,
+ .u.ref.refname = (char *) "refs/heads/master",
+ .u.ref.value_type = REFTABLE_REF_VAL1,
+ },
+ {
+ .type = BLOCK_TYPE_REF,
+ .u.ref.refname = (char *) "refs/heads/master",
+ .u.ref.value_type = REFTABLE_REF_DELETION,
+ },
+ {
+ .type = BLOCK_TYPE_REF,
+ .u.ref.refname = (char *) "HEAD",
+ .u.ref.value_type = REFTABLE_REF_SYMREF,
+ .u.ref.value.symref = (char *) "refs/heads/master",
+ },
+ };
+
+ check(!reftable_record_equal(&in[0], &in[1], GIT_SHA1_RAWSZ));
+ check(!reftable_record_cmp(&in[0], &in[1]));
+
+ check(!reftable_record_equal(&in[1], &in[2], GIT_SHA1_RAWSZ));
+ check_int(reftable_record_cmp(&in[1], &in[2]), >, 0);
+
+ in[1].u.ref.value_type = in[0].u.ref.value_type;
+ check(reftable_record_equal(&in[0], &in[1], GIT_SHA1_RAWSZ));
+ check(!reftable_record_cmp(&in[0], &in[1]));
+}
+
+static void t_reftable_ref_record_compare_name(void)
+{
+ struct reftable_ref_record recs[3] = {
+ {
+ .refname = (char *) "refs/heads/a"
+ },
+ {
+ .refname = (char *) "refs/heads/b"
+ },
+ {
+ .refname = (char *) "refs/heads/a"
+ },
+ };
+
+ check_int(reftable_ref_record_compare_name(&recs[0], &recs[1]), <, 0);
+ check_int(reftable_ref_record_compare_name(&recs[1], &recs[0]), >, 0);
+ check_int(reftable_ref_record_compare_name(&recs[0], &recs[2]), ==, 0);
+}
+
+static void t_reftable_ref_record_roundtrip(void)
+{
+ struct strbuf scratch = STRBUF_INIT;
+
+ for (int i = REFTABLE_REF_DELETION; i < REFTABLE_NR_REF_VALUETYPES; i++) {
+ struct reftable_record in = {
+ .type = BLOCK_TYPE_REF,
+ .u.ref.value_type = i,
+ };
+ struct reftable_record out = { .type = BLOCK_TYPE_REF };
+ struct strbuf key = STRBUF_INIT;
+ uint8_t buffer[1024] = { 0 };
+ struct string_view dest = {
+ .buf = buffer,
+ .len = sizeof(buffer),
+ };
+ int n, m;
+
+ in.u.ref.value_type = i;
+ switch (i) {
+ case REFTABLE_REF_DELETION:
+ break;
+ case REFTABLE_REF_VAL1:
+ set_hash(in.u.ref.value.val1, 1);
+ break;
+ case REFTABLE_REF_VAL2:
+ set_hash(in.u.ref.value.val2.value, 1);
+ set_hash(in.u.ref.value.val2.target_value, 2);
+ break;
+ case REFTABLE_REF_SYMREF:
+ in.u.ref.value.symref = xstrdup("target");
+ break;
+ }
+ in.u.ref.refname = xstrdup("refs/heads/master");
+
+ t_copy(&in);
+
+ check_int(reftable_record_val_type(&in), ==, i);
+ check_int(reftable_record_is_deletion(&in), ==, i == REFTABLE_REF_DELETION);
+
+ reftable_record_key(&in, &key);
+ n = reftable_record_encode(&in, dest, GIT_SHA1_RAWSZ);
+ check_int(n, >, 0);
+
+ /* decode into a non-zero reftable_record to test for leaks. */
+ m = reftable_record_decode(&out, key, i, dest, GIT_SHA1_RAWSZ, &scratch);
+ check_int(n, ==, m);
+
+ check(reftable_ref_record_equal(&in.u.ref, &out.u.ref,
+ GIT_SHA1_RAWSZ));
+ reftable_record_release(&in);
+
+ strbuf_release(&key);
+ reftable_record_release(&out);
+ }
+
+ strbuf_release(&scratch);
+}
+
+static void t_reftable_log_record_comparison(void)
+{
+ struct reftable_record in[3] = {
+ {
+ .type = BLOCK_TYPE_LOG,
+ .u.log.refname = (char *) "refs/heads/master",
+ .u.log.update_index = 42,
+ },
+ {
+ .type = BLOCK_TYPE_LOG,
+ .u.log.refname = (char *) "refs/heads/master",
+ .u.log.update_index = 22,
+ },
+ {
+ .type = BLOCK_TYPE_LOG,
+ .u.log.refname = (char *) "refs/heads/main",
+ .u.log.update_index = 22,
+ },
+ };
+
+ check(!reftable_record_equal(&in[0], &in[1], GIT_SHA1_RAWSZ));
+ check(!reftable_record_equal(&in[1], &in[2], GIT_SHA1_RAWSZ));
+ check_int(reftable_record_cmp(&in[1], &in[2]), >, 0);
+ /* comparison should be reversed for equal keys, because
+ * comparison is now performed on the basis of update indices */
+ check_int(reftable_record_cmp(&in[0], &in[1]), <, 0);
+
+ in[1].u.log.update_index = in[0].u.log.update_index;
+ check(reftable_record_equal(&in[0], &in[1], GIT_SHA1_RAWSZ));
+ check(!reftable_record_cmp(&in[0], &in[1]));
+}
+
+static void t_reftable_log_record_compare_key(void)
+{
+ struct reftable_log_record logs[3] = {
+ {
+ .refname = (char *) "refs/heads/a",
+ .update_index = 1,
+ },
+ {
+ .refname = (char *) "refs/heads/b",
+ .update_index = 2,
+ },
+ {
+ .refname = (char *) "refs/heads/a",
+ .update_index = 3,
+ },
+ };
+
+ check_int(reftable_log_record_compare_key(&logs[0], &logs[1]), <, 0);
+ check_int(reftable_log_record_compare_key(&logs[1], &logs[0]), >, 0);
+
+ logs[1].update_index = logs[0].update_index;
+ check_int(reftable_log_record_compare_key(&logs[0], &logs[1]), <, 0);
+
+ check_int(reftable_log_record_compare_key(&logs[0], &logs[2]), >, 0);
+ check_int(reftable_log_record_compare_key(&logs[2], &logs[0]), <, 0);
+ logs[2].update_index = logs[0].update_index;
+ check_int(reftable_log_record_compare_key(&logs[0], &logs[2]), ==, 0);
+}
+
+static void t_reftable_log_record_roundtrip(void)
+{
+ struct reftable_log_record in[] = {
+ {
+ .refname = xstrdup("refs/heads/master"),
+ .update_index = 42,
+ .value_type = REFTABLE_LOG_UPDATE,
+ .value = {
+ .update = {
+ .name = xstrdup("han-wen"),
+ .email = xstrdup("hanwen@google.com"),
+ .message = xstrdup("test"),
+ .time = 1577123507,
+ .tz_offset = 100,
+ },
+ }
+ },
+ {
+ .refname = xstrdup("refs/heads/master"),
+ .update_index = 22,
+ .value_type = REFTABLE_LOG_DELETION,
+ },
+ {
+ .refname = xstrdup("branch"),
+ .update_index = 33,
+ .value_type = REFTABLE_LOG_UPDATE,
+ }
+ };
+ struct strbuf scratch = STRBUF_INIT;
+ set_hash(in[0].value.update.new_hash, 1);
+ set_hash(in[0].value.update.old_hash, 2);
+ set_hash(in[2].value.update.new_hash, 3);
+ set_hash(in[2].value.update.old_hash, 4);
+
+ check(!reftable_log_record_is_deletion(&in[0]));
+ check(reftable_log_record_is_deletion(&in[1]));
+ check(!reftable_log_record_is_deletion(&in[2]));
+
+ for (size_t i = 0; i < ARRAY_SIZE(in); i++) {
+ struct reftable_record rec = { .type = BLOCK_TYPE_LOG };
+ struct strbuf key = STRBUF_INIT;
+ uint8_t buffer[1024] = { 0 };
+ struct string_view dest = {
+ .buf = buffer,
+ .len = sizeof(buffer),
+ };
+ /* populate out, to check for leaks. */
+ struct reftable_record out = {
+ .type = BLOCK_TYPE_LOG,
+ .u.log = {
+ .refname = xstrdup("old name"),
+ .value_type = REFTABLE_LOG_UPDATE,
+ .value = {
+ .update = {
+ .name = xstrdup("old name"),
+ .email = xstrdup("old@email"),
+ .message = xstrdup("old message"),
+ },
+ },
+ },
+ };
+ int n, m, valtype;
+
+ rec.u.log = in[i];
+
+ t_copy(&rec);
+
+ reftable_record_key(&rec, &key);
+
+ n = reftable_record_encode(&rec, dest, GIT_SHA1_RAWSZ);
+ check_int(n, >=, 0);
+ valtype = reftable_record_val_type(&rec);
+ m = reftable_record_decode(&out, key, valtype, dest,
+ GIT_SHA1_RAWSZ, &scratch);
+ check_int(n, ==, m);
+
+ check(reftable_log_record_equal(&in[i], &out.u.log,
+ GIT_SHA1_RAWSZ));
+ reftable_log_record_release(&in[i]);
+ strbuf_release(&key);
+ reftable_record_release(&out);
+ }
+
+ strbuf_release(&scratch);
+}
+
+static void t_key_roundtrip(void)
+{
+ uint8_t buffer[1024] = { 0 };
+ struct string_view dest = {
+ .buf = buffer,
+ .len = sizeof(buffer),
+ };
+ struct strbuf last_key = STRBUF_INIT;
+ struct strbuf key = STRBUF_INIT;
+ struct strbuf roundtrip = STRBUF_INIT;
+ int restart;
+ uint8_t extra;
+ int n, m;
+ uint8_t rt_extra;
+
+ strbuf_addstr(&last_key, "refs/heads/master");
+ strbuf_addstr(&key, "refs/tags/bla");
+ extra = 6;
+ n = reftable_encode_key(&restart, dest, last_key, key, extra);
+ check(!restart);
+ check_int(n, >, 0);
+
+ strbuf_addstr(&roundtrip, "refs/heads/master");
+ m = reftable_decode_key(&roundtrip, &rt_extra, dest);
+ check_int(n, ==, m);
+ check(!strbuf_cmp(&key, &roundtrip));
+ check_int(rt_extra, ==, extra);
+
+ strbuf_release(&last_key);
+ strbuf_release(&key);
+ strbuf_release(&roundtrip);
+}
+
+static void t_reftable_obj_record_comparison(void)
+{
+
+ uint8_t id_bytes[] = { 0, 1, 2, 3, 4, 5, 6 };
+ uint64_t offsets[] = { 0, 16, 32, 48, 64, 80, 96, 112};
+ struct reftable_record in[3] = {
+ {
+ .type = BLOCK_TYPE_OBJ,
+ .u.obj.hash_prefix = id_bytes,
+ .u.obj.hash_prefix_len = 7,
+ .u.obj.offsets = offsets,
+ .u.obj.offset_len = 8,
+ },
+ {
+ .type = BLOCK_TYPE_OBJ,
+ .u.obj.hash_prefix = id_bytes,
+ .u.obj.hash_prefix_len = 7,
+ .u.obj.offsets = offsets,
+ .u.obj.offset_len = 5,
+ },
+ {
+ .type = BLOCK_TYPE_OBJ,
+ .u.obj.hash_prefix = id_bytes,
+ .u.obj.hash_prefix_len = 5,
+ },
+ };
+
+ check(!reftable_record_equal(&in[0], &in[1], GIT_SHA1_RAWSZ));
+ check(!reftable_record_cmp(&in[0], &in[1]));
+
+ check(!reftable_record_equal(&in[1], &in[2], GIT_SHA1_RAWSZ));
+ check_int(reftable_record_cmp(&in[1], &in[2]), >, 0);
+
+ in[1].u.obj.offset_len = in[0].u.obj.offset_len;
+ check(reftable_record_equal(&in[0], &in[1], GIT_SHA1_RAWSZ));
+ check(!reftable_record_cmp(&in[0], &in[1]));
+}
+
+static void t_reftable_obj_record_roundtrip(void)
+{
+ uint8_t testHash1[GIT_SHA1_RAWSZ] = { 1, 2, 3, 4, 0 };
+ uint64_t till9[] = { 1, 2, 3, 4, 500, 600, 700, 800, 9000 };
+ struct reftable_obj_record recs[3] = {
+ {
+ .hash_prefix = testHash1,
+ .hash_prefix_len = 5,
+ .offsets = till9,
+ .offset_len = 3,
+ },
+ {
+ .hash_prefix = testHash1,
+ .hash_prefix_len = 5,
+ .offsets = till9,
+ .offset_len = 9,
+ },
+ {
+ .hash_prefix = testHash1,
+ .hash_prefix_len = 5,
+ },
+ };
+ struct strbuf scratch = STRBUF_INIT;
+
+ for (size_t i = 0; i < ARRAY_SIZE(recs); i++) {
+ uint8_t buffer[1024] = { 0 };
+ struct string_view dest = {
+ .buf = buffer,
+ .len = sizeof(buffer),
+ };
+ struct reftable_record in = {
+ .type = BLOCK_TYPE_OBJ,
+ .u = {
+ .obj = recs[i],
+ },
+ };
+ struct strbuf key = STRBUF_INIT;
+ struct reftable_record out = { .type = BLOCK_TYPE_OBJ };
+ int n, m;
+ uint8_t extra;
+
+ check(!reftable_record_is_deletion(&in));
+ t_copy(&in);
+ reftable_record_key(&in, &key);
+ n = reftable_record_encode(&in, dest, GIT_SHA1_RAWSZ);
+ check_int(n, >, 0);
+ extra = reftable_record_val_type(&in);
+ m = reftable_record_decode(&out, key, extra, dest,
+ GIT_SHA1_RAWSZ, &scratch);
+ check_int(n, ==, m);
+
+ check(reftable_record_equal(&in, &out, GIT_SHA1_RAWSZ));
+ strbuf_release(&key);
+ reftable_record_release(&out);
+ }
+
+ strbuf_release(&scratch);
+}
+
+static void t_reftable_index_record_comparison(void)
+{
+ struct reftable_record in[3] = {
+ {
+ .type = BLOCK_TYPE_INDEX,
+ .u.idx.offset = 22,
+ .u.idx.last_key = STRBUF_INIT,
+ },
+ {
+ .type = BLOCK_TYPE_INDEX,
+ .u.idx.offset = 32,
+ .u.idx.last_key = STRBUF_INIT,
+ },
+ {
+ .type = BLOCK_TYPE_INDEX,
+ .u.idx.offset = 32,
+ .u.idx.last_key = STRBUF_INIT,
+ },
+ };
+ strbuf_addstr(&in[0].u.idx.last_key, "refs/heads/master");
+ strbuf_addstr(&in[1].u.idx.last_key, "refs/heads/master");
+ strbuf_addstr(&in[2].u.idx.last_key, "refs/heads/branch");
+
+ check(!reftable_record_equal(&in[0], &in[1], GIT_SHA1_RAWSZ));
+ check(!reftable_record_cmp(&in[0], &in[1]));
+
+ check(!reftable_record_equal(&in[1], &in[2], GIT_SHA1_RAWSZ));
+ check_int(reftable_record_cmp(&in[1], &in[2]), >, 0);
+
+ in[1].u.idx.offset = in[0].u.idx.offset;
+ check(reftable_record_equal(&in[0], &in[1], GIT_SHA1_RAWSZ));
+ check(!reftable_record_cmp(&in[0], &in[1]));
+
+ for (size_t i = 0; i < ARRAY_SIZE(in); i++)
+ reftable_record_release(&in[i]);
+}
+
+static void t_reftable_index_record_roundtrip(void)
+{
+ struct reftable_record in = {
+ .type = BLOCK_TYPE_INDEX,
+ .u.idx = {
+ .offset = 42,
+ .last_key = STRBUF_INIT,
+ },
+ };
+ uint8_t buffer[1024] = { 0 };
+ struct string_view dest = {
+ .buf = buffer,
+ .len = sizeof(buffer),
+ };
+ struct strbuf scratch = STRBUF_INIT;
+ struct strbuf key = STRBUF_INIT;
+ struct reftable_record out = {
+ .type = BLOCK_TYPE_INDEX,
+ .u.idx = { .last_key = STRBUF_INIT },
+ };
+ int n, m;
+ uint8_t extra;
+
+ strbuf_addstr(&in.u.idx.last_key, "refs/heads/master");
+ reftable_record_key(&in, &key);
+ t_copy(&in);
+
+ check(!reftable_record_is_deletion(&in));
+ check(!strbuf_cmp(&key, &in.u.idx.last_key));
+ n = reftable_record_encode(&in, dest, GIT_SHA1_RAWSZ);
+ check_int(n, >, 0);
+
+ extra = reftable_record_val_type(&in);
+ m = reftable_record_decode(&out, key, extra, dest, GIT_SHA1_RAWSZ,
+ &scratch);
+ check_int(m, ==, n);
+
+ check(reftable_record_equal(&in, &out, GIT_SHA1_RAWSZ));
+
+ reftable_record_release(&out);
+ strbuf_release(&key);
+ strbuf_release(&scratch);
+ strbuf_release(&in.u.idx.last_key);
+}
+
+int cmd_main(int argc, const char *argv[])
+{
+ TEST(t_reftable_ref_record_comparison(), "comparison operations work on ref record");
+ TEST(t_reftable_log_record_comparison(), "comparison operations work on log record");
+ TEST(t_reftable_index_record_comparison(), "comparison operations work on index record");
+ TEST(t_reftable_obj_record_comparison(), "comparison operations work on obj record");
+ TEST(t_reftable_ref_record_compare_name(), "reftable_ref_record_compare_name works");
+ TEST(t_reftable_log_record_compare_key(), "reftable_log_record_compare_key works");
+ TEST(t_reftable_log_record_roundtrip(), "record operations work on log record");
+ TEST(t_reftable_ref_record_roundtrip(), "record operations work on ref record");
+ TEST(t_varint_roundtrip(), "put_var_int and get_var_int work");
+ TEST(t_key_roundtrip(), "reftable_encode_key and reftable_decode_key work");
+ TEST(t_reftable_obj_record_roundtrip(), "record operations work on obj record");
+ TEST(t_reftable_index_record_roundtrip(), "record operations work on index record");
+
+ return test_done();
+}
diff --git a/t/unit-tests/t-strbuf.c b/t/unit-tests/t-strbuf.c
index de434a4441..6027dafef7 100644
--- a/t/unit-tests/t-strbuf.c
+++ b/t/unit-tests/t-strbuf.c
@@ -2,7 +2,8 @@
#include "strbuf.h"
/* wrapper that supplies tests with an empty, initialized strbuf */
-static void setup(void (*f)(struct strbuf*, void*), void *data)
+static void setup(void (*f)(struct strbuf*, const void*),
+ const void *data)
{
struct strbuf buf = STRBUF_INIT;
@@ -13,7 +14,8 @@ static void setup(void (*f)(struct strbuf*, void*), void *data)
}
/* wrapper that supplies tests with a populated, initialized strbuf */
-static void setup_populated(void (*f)(struct strbuf*, void*), char *init_str, void *data)
+static void setup_populated(void (*f)(struct strbuf*, const void*),
+ const char *init_str, const void *data)
{
struct strbuf buf = STRBUF_INIT;
@@ -64,7 +66,7 @@ static void t_dynamic_init(void)
strbuf_release(&buf);
}
-static void t_addch(struct strbuf *buf, void *data)
+static void t_addch(struct strbuf *buf, const void *data)
{
const char *p_ch = data;
const char ch = *p_ch;
@@ -83,7 +85,7 @@ static void t_addch(struct strbuf *buf, void *data)
check_char(buf->buf[buf->len], ==, '\0');
}
-static void t_addstr(struct strbuf *buf, void *data)
+static void t_addstr(struct strbuf *buf, const void *data)
{
const char *text = data;
size_t len = strlen(text);
diff --git a/t/unit-tests/t-strcmp-offset.c b/t/unit-tests/t-strcmp-offset.c
new file mode 100644
index 0000000000..fe4c2706b1
--- /dev/null
+++ b/t/unit-tests/t-strcmp-offset.c
@@ -0,0 +1,35 @@
+#include "test-lib.h"
+#include "read-cache-ll.h"
+
+static void check_strcmp_offset(const char *string1, const char *string2,
+ int expect_result, uintmax_t expect_offset)
+{
+ size_t offset;
+ int result = strcmp_offset(string1, string2, &offset);
+
+ /*
+ * Because different CRTs behave differently, only rely on signs of the
+ * result values.
+ */
+ result = (result < 0 ? -1 :
+ result > 0 ? 1 :
+ 0);
+
+ check_int(result, ==, expect_result);
+ check_uint((uintmax_t)offset, ==, expect_offset);
+}
+
+#define TEST_STRCMP_OFFSET(string1, string2, expect_result, expect_offset) \
+ TEST(check_strcmp_offset(string1, string2, expect_result, \
+ expect_offset), \
+ "strcmp_offset(%s, %s) works", #string1, #string2)
+
+int cmd_main(int argc, const char **argv)
+{
+ TEST_STRCMP_OFFSET("abc", "abc", 0, 3);
+ TEST_STRCMP_OFFSET("abc", "def", -1, 0);
+ TEST_STRCMP_OFFSET("abc", "abz", -1, 2);
+ TEST_STRCMP_OFFSET("abc", "abcdef", -1, 3);
+
+ return test_done();
+}
diff --git a/t/unit-tests/t-strvec.c b/t/unit-tests/t-strvec.c
new file mode 100644
index 0000000000..d4615ab06d
--- /dev/null
+++ b/t/unit-tests/t-strvec.c
@@ -0,0 +1,272 @@
+#include "test-lib.h"
+#include "strbuf.h"
+#include "strvec.h"
+
+#define check_strvec(vec, ...) \
+ check_strvec_loc(TEST_LOCATION(), vec, __VA_ARGS__)
+LAST_ARG_MUST_BE_NULL
+static void check_strvec_loc(const char *loc, struct strvec *vec, ...)
+{
+ va_list ap;
+ size_t nr = 0;
+
+ va_start(ap, vec);
+ while (1) {
+ const char *str = va_arg(ap, const char *);
+ if (!str)
+ break;
+
+ if (!check_uint(vec->nr, >, nr) ||
+ !check_uint(vec->alloc, >, nr) ||
+ !check_str(vec->v[nr], str)) {
+ struct strbuf msg = STRBUF_INIT;
+ strbuf_addf(&msg, "strvec index %"PRIuMAX, (uintmax_t) nr);
+ test_assert(loc, msg.buf, 0);
+ strbuf_release(&msg);
+ va_end(ap);
+ return;
+ }
+
+ nr++;
+ }
+ va_end(ap);
+
+ check_uint(vec->nr, ==, nr);
+ check_uint(vec->alloc, >=, nr);
+ check_pointer_eq(vec->v[nr], NULL);
+}
+
+static void t_static_init(void)
+{
+ struct strvec vec = STRVEC_INIT;
+ check_pointer_eq(vec.v, empty_strvec);
+ check_uint(vec.nr, ==, 0);
+ check_uint(vec.alloc, ==, 0);
+}
+
+static void t_dynamic_init(void)
+{
+ struct strvec vec;
+ strvec_init(&vec);
+ check_pointer_eq(vec.v, empty_strvec);
+ check_uint(vec.nr, ==, 0);
+ check_uint(vec.alloc, ==, 0);
+}
+
+static void t_clear(void)
+{
+ struct strvec vec = STRVEC_INIT;
+ strvec_push(&vec, "foo");
+ strvec_clear(&vec);
+ check_pointer_eq(vec.v, empty_strvec);
+ check_uint(vec.nr, ==, 0);
+ check_uint(vec.alloc, ==, 0);
+}
+
+static void t_push(void)
+{
+ struct strvec vec = STRVEC_INIT;
+
+ strvec_push(&vec, "foo");
+ check_strvec(&vec, "foo", NULL);
+
+ strvec_push(&vec, "bar");
+ check_strvec(&vec, "foo", "bar", NULL);
+
+ strvec_clear(&vec);
+}
+
+static void t_pushf(void)
+{
+ struct strvec vec = STRVEC_INIT;
+ strvec_pushf(&vec, "foo: %d", 1);
+ check_strvec(&vec, "foo: 1", NULL);
+ strvec_clear(&vec);
+}
+
+static void t_pushl(void)
+{
+ struct strvec vec = STRVEC_INIT;
+ strvec_pushl(&vec, "foo", "bar", "baz", NULL);
+ check_strvec(&vec, "foo", "bar", "baz", NULL);
+ strvec_clear(&vec);
+}
+
+static void t_pushv(void)
+{
+ const char *strings[] = {
+ "foo", "bar", "baz", NULL,
+ };
+ struct strvec vec = STRVEC_INIT;
+
+ strvec_pushv(&vec, strings);
+ check_strvec(&vec, "foo", "bar", "baz", NULL);
+
+ strvec_clear(&vec);
+}
+
+static void t_replace_at_head(void)
+{
+ struct strvec vec = STRVEC_INIT;
+ strvec_pushl(&vec, "foo", "bar", "baz", NULL);
+ strvec_replace(&vec, 0, "replaced");
+ check_strvec(&vec, "replaced", "bar", "baz", NULL);
+ strvec_clear(&vec);
+}
+
+static void t_replace_at_tail(void)
+{
+ struct strvec vec = STRVEC_INIT;
+ strvec_pushl(&vec, "foo", "bar", "baz", NULL);
+ strvec_replace(&vec, 2, "replaced");
+ check_strvec(&vec, "foo", "bar", "replaced", NULL);
+ strvec_clear(&vec);
+}
+
+static void t_replace_in_between(void)
+{
+ struct strvec vec = STRVEC_INIT;
+ strvec_pushl(&vec, "foo", "bar", "baz", NULL);
+ strvec_replace(&vec, 1, "replaced");
+ check_strvec(&vec, "foo", "replaced", "baz", NULL);
+ strvec_clear(&vec);
+}
+
+static void t_replace_with_substring(void)
+{
+ struct strvec vec = STRVEC_INIT;
+ strvec_pushl(&vec, "foo", NULL);
+ strvec_replace(&vec, 0, vec.v[0] + 1);
+ check_strvec(&vec, "oo", NULL);
+ strvec_clear(&vec);
+}
+
+static void t_remove_at_head(void)
+{
+ struct strvec vec = STRVEC_INIT;
+ strvec_pushl(&vec, "foo", "bar", "baz", NULL);
+ strvec_remove(&vec, 0);
+ check_strvec(&vec, "bar", "baz", NULL);
+ strvec_clear(&vec);
+}
+
+static void t_remove_at_tail(void)
+{
+ struct strvec vec = STRVEC_INIT;
+ strvec_pushl(&vec, "foo", "bar", "baz", NULL);
+ strvec_remove(&vec, 2);
+ check_strvec(&vec, "foo", "bar", NULL);
+ strvec_clear(&vec);
+}
+
+static void t_remove_in_between(void)
+{
+ struct strvec vec = STRVEC_INIT;
+ strvec_pushl(&vec, "foo", "bar", "baz", NULL);
+ strvec_remove(&vec, 1);
+ check_strvec(&vec, "foo", "baz", NULL);
+ strvec_clear(&vec);
+}
+
+static void t_pop_empty_array(void)
+{
+ struct strvec vec = STRVEC_INIT;
+ strvec_pop(&vec);
+ check_strvec(&vec, NULL);
+ strvec_clear(&vec);
+}
+
+static void t_pop_non_empty_array(void)
+{
+ struct strvec vec = STRVEC_INIT;
+ strvec_pushl(&vec, "foo", "bar", "baz", NULL);
+ strvec_pop(&vec);
+ check_strvec(&vec, "foo", "bar", NULL);
+ strvec_clear(&vec);
+}
+
+static void t_split_empty_string(void)
+{
+ struct strvec vec = STRVEC_INIT;
+ strvec_split(&vec, "");
+ check_strvec(&vec, NULL);
+ strvec_clear(&vec);
+}
+
+static void t_split_single_item(void)
+{
+ struct strvec vec = STRVEC_INIT;
+ strvec_split(&vec, "foo");
+ check_strvec(&vec, "foo", NULL);
+ strvec_clear(&vec);
+}
+
+static void t_split_multiple_items(void)
+{
+ struct strvec vec = STRVEC_INIT;
+ strvec_split(&vec, "foo bar baz");
+ check_strvec(&vec, "foo", "bar", "baz", NULL);
+ strvec_clear(&vec);
+}
+
+static void t_split_whitespace_only(void)
+{
+ struct strvec vec = STRVEC_INIT;
+ strvec_split(&vec, " \t\n");
+ check_strvec(&vec, NULL);
+ strvec_clear(&vec);
+}
+
+static void t_split_multiple_consecutive_whitespaces(void)
+{
+ struct strvec vec = STRVEC_INIT;
+ strvec_split(&vec, "foo\n\t bar");
+ check_strvec(&vec, "foo", "bar", NULL);
+ strvec_clear(&vec);
+}
+
+static void t_detach(void)
+{
+ struct strvec vec = STRVEC_INIT;
+ const char **detached;
+
+ strvec_push(&vec, "foo");
+
+ detached = strvec_detach(&vec);
+ check_str(detached[0], "foo");
+ check_pointer_eq(detached[1], NULL);
+
+ check_pointer_eq(vec.v, empty_strvec);
+ check_uint(vec.nr, ==, 0);
+ check_uint(vec.alloc, ==, 0);
+
+ free((char *) detached[0]);
+ free(detached);
+}
+
+int cmd_main(int argc, const char **argv)
+{
+ TEST(t_static_init(), "static initialization");
+ TEST(t_dynamic_init(), "dynamic initialization");
+ TEST(t_clear(), "clear");
+ TEST(t_push(), "push");
+ TEST(t_pushf(), "pushf");
+ TEST(t_pushl(), "pushl");
+ TEST(t_pushv(), "pushv");
+ TEST(t_replace_at_head(), "replace at head");
+ TEST(t_replace_in_between(), "replace in between");
+ TEST(t_replace_at_tail(), "replace at tail");
+ TEST(t_replace_with_substring(), "replace with substring");
+ TEST(t_remove_at_head(), "remove at head");
+ TEST(t_remove_in_between(), "remove in between");
+ TEST(t_remove_at_tail(), "remove at tail");
+ TEST(t_pop_empty_array(), "pop with empty array");
+ TEST(t_pop_non_empty_array(), "pop with non-empty array");
+ TEST(t_split_empty_string(), "split empty string");
+ TEST(t_split_single_item(), "split single item");
+ TEST(t_split_multiple_items(), "split multiple items");
+ TEST(t_split_whitespace_only(), "split whitespace only");
+ TEST(t_split_multiple_consecutive_whitespaces(), "split multiple consecutive whitespaces");
+ TEST(t_detach(), "detach");
+ return test_done();
+}
diff --git a/t/unit-tests/t-trailer.c b/t/unit-tests/t-trailer.c
new file mode 100644
index 0000000000..2ecca359d9
--- /dev/null
+++ b/t/unit-tests/t-trailer.c
@@ -0,0 +1,315 @@
+#include "test-lib.h"
+#include "trailer.h"
+
+struct contents {
+ const char *raw;
+ const char *key;
+ const char *val;
+};
+
+static void t_trailer_iterator(const char *msg, size_t num_expected,
+ struct contents *contents)
+{
+ struct trailer_iterator iter;
+ size_t i = 0;
+
+ trailer_iterator_init(&iter, msg);
+ while (trailer_iterator_advance(&iter)) {
+ if (num_expected) {
+ check_str(iter.raw, contents[i].raw);
+ check_str(iter.key.buf, contents[i].key);
+ check_str(iter.val.buf, contents[i].val);
+ }
+ i++;
+ }
+ trailer_iterator_release(&iter);
+
+ check_uint(i, ==, num_expected);
+}
+
+static void run_t_trailer_iterator(void)
+{
+
+ static struct test_cases {
+ const char *name;
+ const char *msg;
+ size_t num_expected;
+ struct contents contents[10];
+ } tc[] = {
+ {
+ "empty input",
+ "",
+ 0,
+ {{0}},
+ },
+ {
+ "no newline at beginning",
+ "Fixes: x\n"
+ "Acked-by: x\n"
+ "Reviewed-by: x\n",
+ 0,
+ {{0}},
+ },
+ {
+ "newline at beginning",
+ "\n"
+ "Fixes: x\n"
+ "Acked-by: x\n"
+ "Reviewed-by: x\n",
+ 3,
+ {
+ {
+ .raw = "Fixes: x\n",
+ .key = "Fixes",
+ .val = "x",
+ },
+ {
+ .raw = "Acked-by: x\n",
+ .key = "Acked-by",
+ .val = "x",
+ },
+ {
+ .raw = "Reviewed-by: x\n",
+ .key = "Reviewed-by",
+ .val = "x",
+ },
+ {
+ 0
+ },
+ },
+ },
+ {
+ "without body text",
+ "subject: foo bar\n"
+ "\n"
+ "Fixes: x\n"
+ "Acked-by: x\n"
+ "Reviewed-by: x\n",
+ 3,
+ {
+ {
+ .raw = "Fixes: x\n",
+ .key = "Fixes",
+ .val = "x",
+ },
+ {
+ .raw = "Acked-by: x\n",
+ .key = "Acked-by",
+ .val = "x",
+ },
+ {
+ .raw = "Reviewed-by: x\n",
+ .key = "Reviewed-by",
+ .val = "x",
+ },
+ {
+ 0
+ },
+ },
+ },
+ {
+ "with body text, without divider",
+ "my subject\n"
+ "\n"
+ "my body which is long\n"
+ "and contains some special\n"
+ "chars like : = ? !\n"
+ "hello\n"
+ "\n"
+ "Fixes: x\n"
+ "Acked-by: x\n"
+ "Reviewed-by: x\n"
+ "Signed-off-by: x\n",
+ 4,
+ {
+ {
+ .raw = "Fixes: x\n",
+ .key = "Fixes",
+ .val = "x",
+ },
+ {
+ .raw = "Acked-by: x\n",
+ .key = "Acked-by",
+ .val = "x",
+ },
+ {
+ .raw = "Reviewed-by: x\n",
+ .key = "Reviewed-by",
+ .val = "x",
+ },
+ {
+ .raw = "Signed-off-by: x\n",
+ .key = "Signed-off-by",
+ .val = "x",
+ },
+ {
+ 0
+ },
+ },
+ },
+ {
+ "with body text, without divider (second trailer block)",
+ "my subject\n"
+ "\n"
+ "my body which is long\n"
+ "and contains some special\n"
+ "chars like : = ? !\n"
+ "hello\n"
+ "\n"
+ "Fixes: x\n"
+ "Acked-by: x\n"
+ "Reviewed-by: x\n"
+ "Signed-off-by: x\n"
+ "\n"
+ /*
+ * Because this is the last trailer block, it takes
+ * precedence over the first one encountered above.
+ */
+ "Helped-by: x\n"
+ "Signed-off-by: x\n",
+ 2,
+ {
+ {
+ .raw = "Helped-by: x\n",
+ .key = "Helped-by",
+ .val = "x",
+ },
+ {
+ .raw = "Signed-off-by: x\n",
+ .key = "Signed-off-by",
+ .val = "x",
+ },
+ {
+ 0
+ },
+ },
+ },
+ {
+ "with body text, with divider",
+ "my subject\n"
+ "\n"
+ "my body which is long\n"
+ "and contains some special\n"
+ "chars like : = ? !\n"
+ "hello\n"
+ "\n"
+ "---\n"
+ "\n"
+ /*
+ * This trailer still counts because the iterator
+ * always ignores the divider.
+ */
+ "Signed-off-by: x\n",
+ 1,
+ {
+ {
+ .raw = "Signed-off-by: x\n",
+ .key = "Signed-off-by",
+ .val = "x",
+ },
+ {
+ 0
+ },
+ },
+ },
+ {
+ "with non-trailer lines in trailer block",
+ "subject: foo bar\n"
+ "\n"
+ /*
+ * Even though this trailer block has a non-trailer line
+ * in it, it's still a valid trailer block because it's
+ * at least 25% trailers and is Git-generated (see
+ * git_generated_prefixes[] in trailer.c).
+ */
+ "not a trailer line\n"
+ "not a trailer line\n"
+ "not a trailer line\n"
+ "Signed-off-by: x\n",
+ /*
+ * Even though there is only really 1 real "trailer"
+ * (Signed-off-by), we still have 4 trailer objects
+ * because we still want to iterate through the entire
+ * block.
+ */
+ 4,
+ {
+ {
+ .raw = "not a trailer line\n",
+ .key = "not a trailer line",
+ .val = "",
+ },
+ {
+ .raw = "not a trailer line\n",
+ .key = "not a trailer line",
+ .val = "",
+ },
+ {
+ .raw = "not a trailer line\n",
+ .key = "not a trailer line",
+ .val = "",
+ },
+ {
+ .raw = "Signed-off-by: x\n",
+ .key = "Signed-off-by",
+ .val = "x",
+ },
+ {
+ 0
+ },
+ },
+ },
+ {
+ "with non-trailer lines (one too many) in trailer block",
+ "subject: foo bar\n"
+ "\n"
+ /*
+ * This block has only 20% trailers, so it's below the
+ * 25% threshold.
+ */
+ "not a trailer line\n"
+ "not a trailer line\n"
+ "not a trailer line\n"
+ "not a trailer line\n"
+ "Signed-off-by: x\n",
+ 0,
+ {{0}},
+ },
+ {
+ "with non-trailer lines (only 1) in trailer block, but no Git-generated trailers",
+ "subject: foo bar\n"
+ "\n"
+ /*
+ * This block has only 1 non-trailer out of 10 (IOW, 90%
+ * trailers) but is not considered a trailer block
+ * because the 25% threshold only applies to cases where
+ * there was a Git-generated trailer.
+ */
+ "Reviewed-by: x\n"
+ "Reviewed-by: x\n"
+ "Reviewed-by: x\n"
+ "Helped-by: x\n"
+ "Helped-by: x\n"
+ "Helped-by: x\n"
+ "Acked-by: x\n"
+ "Acked-by: x\n"
+ "Acked-by: x\n"
+ "not a trailer line\n",
+ 0,
+ {{0}},
+ },
+ };
+
+ for (int i = 0; i < sizeof(tc) / sizeof(tc[0]); i++) {
+ TEST(t_trailer_iterator(tc[i].msg,
+ tc[i].num_expected,
+ tc[i].contents),
+ "%s", tc[i].name);
+ }
+}
+
+int cmd_main(int argc, const char **argv)
+{
+ run_t_trailer_iterator();
+ return test_done();
+}
diff --git a/t/unit-tests/test-lib.c b/t/unit-tests/test-lib.c
index 66d6980ffb..3c513ce59a 100644
--- a/t/unit-tests/test-lib.c
+++ b/t/unit-tests/test-lib.c
@@ -318,6 +318,19 @@ int check_bool_loc(const char *loc, const char *check, int ok)
union test__tmp test__tmp[2];
+int check_pointer_eq_loc(const char *loc, const char *check, int ok,
+ const void *a, const void *b)
+{
+ int ret = test_assert(loc, check, ok);
+
+ if (!ret) {
+ test_msg(" left: %p", a);
+ test_msg(" right: %p", b);
+ }
+
+ return ret;
+}
+
int check_int_loc(const char *loc, const char *check, int ok,
intmax_t a, intmax_t b)
{
diff --git a/t/unit-tests/test-lib.h b/t/unit-tests/test-lib.h
index a8f07ae0b7..2de6d715d5 100644
--- a/t/unit-tests/test-lib.h
+++ b/t/unit-tests/test-lib.h
@@ -79,6 +79,18 @@ int check_bool_loc(const char *loc, const char *check, int ok);
* Compare two integers. Prints a message with the two values if the
* comparison fails. NB this is not thread safe.
*/
+#define check_pointer_eq(a, b) \
+ (test__tmp[0].p = (a), test__tmp[1].p = (b), \
+ check_pointer_eq_loc(TEST_LOCATION(), #a" == "#b, \
+ test__tmp[0].p == test__tmp[1].p, \
+ test__tmp[0].p, test__tmp[1].p))
+int check_pointer_eq_loc(const char *loc, const char *check, int ok,
+ const void *a, const void *b);
+
+/*
+ * Compare two integers. Prints a message with the two values if the
+ * comparison fails. NB this is not thread safe.
+ */
#define check_int(a, op, b) \
(test__tmp[0].i = (a), test__tmp[1].i = (b), \
check_int_loc(TEST_LOCATION(), #a" "#op" "#b, \
@@ -136,6 +148,7 @@ union test__tmp {
intmax_t i;
uintmax_t u;
char c;
+ const void *p;
};
extern union test__tmp test__tmp[2];
diff --git a/tag.c b/tag.c
index fc3834db46..d24170e340 100644
--- a/tag.c
+++ b/tag.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "environment.h"
#include "tag.h"
@@ -91,10 +93,10 @@ struct object *deref_tag(struct repository *r, struct object *o, const char *war
return o;
}
-struct object *deref_tag_noverify(struct object *o)
+struct object *deref_tag_noverify(struct repository *r, struct object *o)
{
while (o && o->type == OBJ_TAG) {
- o = parse_object(the_repository, &o->oid);
+ o = parse_object(r, &o->oid);
if (o && o->type == OBJ_TAG && ((struct tag *)o)->tagged)
o = ((struct tag *)o)->tagged;
else
diff --git a/tag.h b/tag.h
index 3ce8e72192..c49d7c19ad 100644
--- a/tag.h
+++ b/tag.h
@@ -16,7 +16,7 @@ int parse_tag_buffer(struct repository *r, struct tag *item, const void *data, u
int parse_tag(struct tag *item);
void release_tag_memory(struct tag *t);
struct object *deref_tag(struct repository *r, struct object *, const char *, int);
-struct object *deref_tag_noverify(struct object *);
+struct object *deref_tag_noverify(struct repository *r, struct object *);
int gpg_verify_tag(const struct object_id *oid,
const char *name_to_report, unsigned flags);
struct object_id *get_tagged_oid(struct tag *tag);
diff --git a/tmp-objdir.c b/tmp-objdir.c
index 3509258be5..a8e4553f27 100644
--- a/tmp-objdir.c
+++ b/tmp-objdir.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "tmp-objdir.h"
#include "abspath.h"
diff --git a/trace2.h b/trace2.h
index 1f0669bbd2..19e04bf040 100644
--- a/trace2.h
+++ b/trace2.h
@@ -390,6 +390,7 @@ void trace2_region_enter_printf_va_fl(const char *file, int line,
trace2_region_enter_printf_va_fl(__FILE__, __LINE__, (category), \
(label), (repo), (fmt), (ap))
+__attribute__((format (printf, 6, 7)))
void trace2_region_enter_printf_fl(const char *file, int line,
const char *category, const char *label,
const struct repository *repo,
diff --git a/trailer.c b/trailer.c
index c72ae68709..72e5136c73 100644
--- a/trailer.c
+++ b/trailer.c
@@ -11,6 +11,27 @@
* Copyright (c) 2013, 2014 Christian Couder <chriscool@tuxfamily.org>
*/
+struct trailer_info {
+ /*
+ * True if there is a blank line before the location pointed to by
+ * trailer_block_start.
+ */
+ int blank_line_before_trailer;
+
+ /*
+ * Offsets to the trailer block start and end positions in the input
+ * string. If no trailer block is found, these are both set to the
+ * "true" end of the input (find_end_of_log_message()).
+ */
+ size_t trailer_block_start, trailer_block_end;
+
+ /*
+ * Array of trailers found.
+ */
+ char **trailers;
+ size_t trailer_nr;
+};
+
struct conf_info {
char *name;
char *key;
@@ -42,7 +63,7 @@ struct arg_item {
static LIST_HEAD(conf_head);
-static char *separators = ":";
+static const char *separators = ":";
static int configured;
@@ -952,20 +973,72 @@ static void unfold_value(struct strbuf *val)
strbuf_release(&out);
}
+static struct trailer_info *trailer_info_new(void)
+{
+ struct trailer_info *info = xcalloc(1, sizeof(*info));
+ return info;
+}
+
+static struct trailer_info *trailer_info_get(const struct process_trailer_options *opts,
+ const char *str)
+{
+ struct trailer_info *info = trailer_info_new();
+ size_t end_of_log_message = 0, trailer_block_start = 0;
+ struct strbuf **trailer_lines, **ptr;
+ char **trailer_strings = NULL;
+ size_t nr = 0, alloc = 0;
+ char **last = NULL;
+
+ trailer_config_init();
+
+ end_of_log_message = find_end_of_log_message(str, opts->no_divider);
+ trailer_block_start = find_trailer_block_start(str, end_of_log_message);
+
+ trailer_lines = strbuf_split_buf(str + trailer_block_start,
+ end_of_log_message - trailer_block_start,
+ '\n',
+ 0);
+ for (ptr = trailer_lines; *ptr; ptr++) {
+ if (last && isspace((*ptr)->buf[0])) {
+ struct strbuf sb = STRBUF_INIT;
+ strbuf_attach(&sb, *last, strlen(*last), strlen(*last));
+ strbuf_addbuf(&sb, *ptr);
+ *last = strbuf_detach(&sb, NULL);
+ continue;
+ }
+ ALLOC_GROW(trailer_strings, nr + 1, alloc);
+ trailer_strings[nr] = strbuf_detach(*ptr, NULL);
+ last = find_separator(trailer_strings[nr], separators) >= 1
+ ? &trailer_strings[nr]
+ : NULL;
+ nr++;
+ }
+ strbuf_list_free(trailer_lines);
+
+ info->blank_line_before_trailer = ends_with_blank_line(str,
+ trailer_block_start);
+ info->trailer_block_start = trailer_block_start;
+ info->trailer_block_end = end_of_log_message;
+ info->trailers = trailer_strings;
+ info->trailer_nr = nr;
+
+ return info;
+}
+
/*
- * Parse trailers in "str", populating the trailer info and "head"
+ * Parse trailers in "str", populating the trailer info and "trailer_objects"
* linked list structure.
*/
-void parse_trailers(const struct process_trailer_options *opts,
- struct trailer_info *info,
- const char *str,
- struct list_head *head)
+struct trailer_info *parse_trailers(const struct process_trailer_options *opts,
+ const char *str,
+ struct list_head *trailer_objects)
{
+ struct trailer_info *info;
struct strbuf tok = STRBUF_INIT;
struct strbuf val = STRBUF_INIT;
size_t i;
- trailer_info_get(opts, str, info);
+ info = trailer_info_get(opts, str);
for (i = 0; i < info->trailer_nr; i++) {
int separator_pos;
@@ -978,17 +1051,19 @@ void parse_trailers(const struct process_trailer_options *opts,
separator_pos);
if (opts->unfold)
unfold_value(&val);
- add_trailer_item(head,
+ add_trailer_item(trailer_objects,
strbuf_detach(&tok, NULL),
strbuf_detach(&val, NULL));
} else if (!opts->only_trailers) {
strbuf_addstr(&val, trailer);
strbuf_strip_suffix(&val, "\n");
- add_trailer_item(head,
+ add_trailer_item(trailer_objects,
NULL,
strbuf_detach(&val, NULL));
}
}
+
+ return info;
}
void free_trailers(struct list_head *trailers)
@@ -1000,48 +1075,19 @@ void free_trailers(struct list_head *trailers)
}
}
-void trailer_info_get(const struct process_trailer_options *opts,
- const char *str,
- struct trailer_info *info)
+size_t trailer_block_start(struct trailer_info *info)
{
- size_t end_of_log_message = 0, trailer_block_start = 0;
- struct strbuf **trailer_lines, **ptr;
- char **trailer_strings = NULL;
- size_t nr = 0, alloc = 0;
- char **last = NULL;
-
- trailer_config_init();
-
- end_of_log_message = find_end_of_log_message(str, opts->no_divider);
- trailer_block_start = find_trailer_block_start(str, end_of_log_message);
+ return info->trailer_block_start;
+}
- trailer_lines = strbuf_split_buf(str + trailer_block_start,
- end_of_log_message - trailer_block_start,
- '\n',
- 0);
- for (ptr = trailer_lines; *ptr; ptr++) {
- if (last && isspace((*ptr)->buf[0])) {
- struct strbuf sb = STRBUF_INIT;
- strbuf_attach(&sb, *last, strlen(*last), strlen(*last));
- strbuf_addbuf(&sb, *ptr);
- *last = strbuf_detach(&sb, NULL);
- continue;
- }
- ALLOC_GROW(trailer_strings, nr + 1, alloc);
- trailer_strings[nr] = strbuf_detach(*ptr, NULL);
- last = find_separator(trailer_strings[nr], separators) >= 1
- ? &trailer_strings[nr]
- : NULL;
- nr++;
- }
- strbuf_list_free(trailer_lines);
+size_t trailer_block_end(struct trailer_info *info)
+{
+ return info->trailer_block_end;
+}
- info->blank_line_before_trailer = ends_with_blank_line(str,
- trailer_block_start);
- info->trailer_block_start = trailer_block_start;
- info->trailer_block_end = end_of_log_message;
- info->trailers = trailer_strings;
- info->trailer_nr = nr;
+int blank_line_before_trailer_block(struct trailer_info *info)
+{
+ return info->blank_line_before_trailer;
}
void trailer_info_release(struct trailer_info *info)
@@ -1050,6 +1096,7 @@ void trailer_info_release(struct trailer_info *info)
for (i = 0; i < info->trailer_nr; i++)
free(info->trailers[i]);
free(info->trailers);
+ free(info);
}
void format_trailers(const struct process_trailer_options *opts,
@@ -1117,21 +1164,19 @@ void format_trailers_from_commit(const struct process_trailer_options *opts,
struct strbuf *out)
{
LIST_HEAD(trailer_objects);
- struct trailer_info info;
-
- parse_trailers(opts, &info, msg, &trailer_objects);
+ struct trailer_info *info = parse_trailers(opts, msg, &trailer_objects);
/* If we want the whole block untouched, we can take the fast path. */
if (!opts->only_trailers && !opts->unfold && !opts->filter &&
!opts->separator && !opts->key_only && !opts->value_only &&
!opts->key_value_separator) {
- strbuf_add(out, msg + info.trailer_block_start,
- info.trailer_block_end - info.trailer_block_start);
+ strbuf_add(out, msg + info->trailer_block_start,
+ info->trailer_block_end - info->trailer_block_start);
} else
format_trailers(opts, &trailer_objects, out);
free_trailers(&trailer_objects);
- trailer_info_release(&info);
+ trailer_info_release(info);
}
void trailer_iterator_init(struct trailer_iterator *iter, const char *msg)
@@ -1140,23 +1185,21 @@ void trailer_iterator_init(struct trailer_iterator *iter, const char *msg)
strbuf_init(&iter->key, 0);
strbuf_init(&iter->val, 0);
opts.no_divider = 1;
- trailer_info_get(&opts, msg, &iter->internal.info);
+ iter->internal.info = trailer_info_get(&opts, msg);
iter->internal.cur = 0;
}
int trailer_iterator_advance(struct trailer_iterator *iter)
{
- while (iter->internal.cur < iter->internal.info.trailer_nr) {
- char *trailer = iter->internal.info.trailers[iter->internal.cur++];
- int separator_pos = find_separator(trailer, separators);
-
- if (separator_pos < 1)
- continue; /* not a real trailer */
+ if (iter->internal.cur < iter->internal.info->trailer_nr) {
+ char *line = iter->internal.info->trailers[iter->internal.cur++];
+ int separator_pos = find_separator(line, separators);
+ iter->raw = line;
strbuf_reset(&iter->key);
strbuf_reset(&iter->val);
parse_trailer(&iter->key, &iter->val, NULL,
- trailer, separator_pos);
+ line, separator_pos);
/* Always unfold values during iteration. */
unfold_value(&iter->val);
return 1;
@@ -1166,7 +1209,19 @@ int trailer_iterator_advance(struct trailer_iterator *iter)
void trailer_iterator_release(struct trailer_iterator *iter)
{
- trailer_info_release(&iter->internal.info);
+ trailer_info_release(iter->internal.info);
strbuf_release(&iter->val);
strbuf_release(&iter->key);
}
+
+int amend_file_with_trailers(const char *path, const struct strvec *trailer_args)
+{
+ struct child_process run_trailer = CHILD_PROCESS_INIT;
+
+ run_trailer.git_cmd = 1;
+ strvec_pushl(&run_trailer.args, "interpret-trailers",
+ "--in-place", "--no-divider",
+ path, NULL);
+ strvec_pushv(&run_trailer.args, trailer_args->v);
+ return run_command(&run_trailer);
+}
diff --git a/trailer.h b/trailer.h
index 9f42aa7599..6eb53df155 100644
--- a/trailer.h
+++ b/trailer.h
@@ -4,6 +4,9 @@
#include "list.h"
#include "strbuf.h"
+struct trailer_info;
+struct strvec;
+
enum trailer_where {
WHERE_DEFAULT,
WHERE_END,
@@ -29,27 +32,6 @@ int trailer_set_where(enum trailer_where *item, const char *value);
int trailer_set_if_exists(enum trailer_if_exists *item, const char *value);
int trailer_set_if_missing(enum trailer_if_missing *item, const char *value);
-struct trailer_info {
- /*
- * True if there is a blank line before the location pointed to by
- * trailer_block_start.
- */
- int blank_line_before_trailer;
-
- /*
- * Offsets to the trailer block start and end positions in the input
- * string. If no trailer block is found, these are both set to the
- * "true" end of the input (find_end_of_log_message()).
- */
- size_t trailer_block_start, trailer_block_end;
-
- /*
- * Array of trailers found.
- */
- char **trailers;
- size_t trailer_nr;
-};
-
/*
* A list that represents newly-added trailers, such as those provided
* with the --trailer command line option of git-interpret-trailers.
@@ -89,15 +71,63 @@ void parse_trailers_from_command_line_args(struct list_head *arg_head,
void process_trailers_lists(struct list_head *head,
struct list_head *arg_head);
-void parse_trailers(const struct process_trailer_options *,
- struct trailer_info *,
- const char *str,
- struct list_head *head);
+/*
+ * Given some input string "str", return a pointer to an opaque trailer_info
+ * structure. Also populate the trailer_objects list with parsed trailer
+ * objects. Internally this calls trailer_info_get() to get the opaque pointer,
+ * but does some extra work to populate the trailer_objects linked list.
+ *
+ * The opaque trailer_info pointer can be used to check the position of the
+ * trailer block as offsets relative to the beginning of "str" in
+ * trailer_block_start() and trailer_block_end().
+ * blank_line_before_trailer_block() returns 1 if there is a blank line just
+ * before the trailer block. All of these functions are useful for preserving
+ * the input before and after the trailer block, if we were to write out the
+ * original input (but with the trailer block itself modified); see
+ * builtin/interpret-trailers.c for an example.
+ *
+ * For iterating through the parsed trailer block (if you don't care about the
+ * position of the trailer block itself in the context of the larger string text
+ * from which it was parsed), please see trailer_iterator_init() which uses the
+ * trailer_info struct internally.
+ *
+ * Lastly, callers should call trailer_info_release() when they are done using
+ * the opaque pointer.
+ *
+ * NOTE: Callers should treat both trailer_info and trailer_objects as
+ * read-only items, because there is some overlap between the two (trailer_info
+ * has "char **trailers" string array, and trailer_objects will have the same
+ * data but as a linked list of trailer_item objects). This API does not perform
+ * any synchronization between the two. In the future we should be able to
+ * reduce the duplication and use just the linked list.
+ */
+struct trailer_info *parse_trailers(const struct process_trailer_options *,
+ const char *str,
+ struct list_head *trailer_objects);
+
+/*
+ * Return the offset of the start of the trailer block. That is, 0 is the start
+ * of the input ("str" in parse_trailers()) and some other positive number
+ * indicates how many bytes we have to skip over before we get to the beginning
+ * of the trailer block.
+ */
+size_t trailer_block_start(struct trailer_info *);
+
+/*
+ * Return the end of the trailer block, again relative to the start of the
+ * input.
+ */
+size_t trailer_block_end(struct trailer_info *);
-void trailer_info_get(const struct process_trailer_options *,
- const char *str,
- struct trailer_info *);
+/*
+ * Return 1 if the trailer block had an extra newline (blank line) just before
+ * it.
+ */
+int blank_line_before_trailer_block(struct trailer_info *);
+/*
+ * Free trailer_info struct.
+ */
void trailer_info_release(struct trailer_info *info);
void trailer_config_init(void);
@@ -125,12 +155,19 @@ void format_trailers_from_commit(const struct process_trailer_options *,
* trailer_iterator_release(&iter);
*/
struct trailer_iterator {
+ /*
+ * Raw line (e.g., "foo: bar baz") before being parsed as a trailer
+ * key/val pair as part of a trailer block (as the "key" and "val"
+ * fields below). If a line fails to parse as a trailer, then the "key"
+ * will be the entire line and "val" will be the empty string.
+ */
+ const char *raw;
struct strbuf key;
struct strbuf val;
/* private */
struct {
- struct trailer_info info;
+ struct trailer_info *info;
size_t cur;
} internal;
};
@@ -158,4 +195,11 @@ int trailer_iterator_advance(struct trailer_iterator *iter);
*/
void trailer_iterator_release(struct trailer_iterator *iter);
+/*
+ * Augment a file to add trailers to it by running git-interpret-trailers.
+ * This calls run_command() and its return value is the same (i.e. 0 for
+ * success, various non-zero for other errors). See run-command.h.
+ */
+int amend_file_with_trailers(const char *path, const struct strvec *trailer_args);
+
#endif /* TRAILER_H */
diff --git a/transport-helper.c b/transport-helper.c
index 8d284b24d5..09b3560ffd 100644
--- a/transport-helper.c
+++ b/transport-helper.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "transport.h"
#include "quote.h"
@@ -22,7 +24,7 @@
static int debug;
struct helper_data {
- const char *name;
+ char *name;
struct child_process *helper;
FILE *out;
unsigned fetch : 1,
@@ -111,6 +113,7 @@ static void do_take_over(struct transport *transport)
data = (struct helper_data *)transport->data;
transport_take_over(transport, data->helper);
fclose(data->out);
+ free(data->name);
free(data);
}
@@ -253,6 +256,7 @@ static int disconnect_helper(struct transport *transport)
close(data->helper->out);
fclose(data->out);
res = finish_command(data->helper);
+ FREE_AND_NULL(data->name);
FREE_AND_NULL(data->helper);
}
return res;
@@ -551,7 +555,7 @@ static int fetch_with_import(struct transport *transport,
else
private = xstrdup(name);
if (private) {
- if (read_ref(private, &posn->old_oid) < 0)
+ if (refs_read_ref(get_main_ref_store(the_repository), private, &posn->old_oid) < 0)
die(_("could not read ref %s"), private);
free(private);
}
@@ -923,8 +927,10 @@ static int push_update_refs_status(struct helper_data *data,
private = apply_refspecs(&data->rs, ref->name);
if (!private)
continue;
- update_ref("update by helper", private, &(ref->new_oid),
- NULL, 0, 0);
+ refs_update_ref(get_main_ref_store(the_repository),
+ "update by helper", private,
+ &(ref->new_oid),
+ NULL, 0, 0);
free(private);
} else {
for (report = ref->report; report; report = report->next) {
@@ -934,11 +940,12 @@ static int push_update_refs_status(struct helper_data *data,
: ref->name);
if (!private)
continue;
- update_ref("update by helper", private,
- report->new_oid
- ? report->new_oid
- : &(ref->new_oid),
- NULL, 0, 0);
+ refs_update_ref(get_main_ref_store(the_repository),
+ "update by helper", private,
+ report->new_oid
+ ? report->new_oid
+ : &(ref->new_oid),
+ NULL, 0, 0);
free(private);
}
}
@@ -1105,9 +1112,11 @@ static int push_refs_with_export(struct transport *transport,
int flag;
/* Follow symbolic refs (mainly for HEAD). */
- name = resolve_ref_unsafe(ref->peer_ref->name,
- RESOLVE_REF_READING,
- &oid, &flag);
+ name = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
+ ref->peer_ref->name,
+ RESOLVE_REF_READING,
+ &oid,
+ &flag);
if (!name || !(flag & REF_ISSYMREF))
name = ref->peer_ref->name;
@@ -1252,7 +1261,7 @@ static struct ref *get_refs_list_using_list(struct transport *transport,
if (eon) {
if (has_attribute(eon + 1, "unchanged")) {
(*tail)->status |= REF_STATUS_UPTODATE;
- if (read_ref((*tail)->name, &(*tail)->old_oid) < 0)
+ if (refs_read_ref(get_main_ref_store(the_repository), (*tail)->name, &(*tail)->old_oid) < 0)
die(_("could not read ref %s"),
(*tail)->name);
}
@@ -1292,7 +1301,7 @@ static struct transport_vtable vtable = {
int transport_helper_init(struct transport *transport, const char *name)
{
struct helper_data *data = xcalloc(1, sizeof(*data));
- data->name = name;
+ data->name = xstrdup(name);
transport_check_allowed(name);
diff --git a/transport.c b/transport.c
index df518ead70..12cc5b4d96 100644
--- a/transport.c
+++ b/transport.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "advice.h"
#include "config.h"
@@ -100,8 +102,9 @@ static void set_upstreams(struct transport *transport, struct ref *refs,
/* Follow symbolic refs (mainly for HEAD). */
localname = ref->peer_ref->name;
remotename = ref->name;
- tmp = resolve_ref_unsafe(localname, RESOLVE_REF_READING,
- NULL, &flag);
+ tmp = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
+ localname, RESOLVE_REF_READING,
+ NULL, &flag);
if (tmp && flag & REF_ISSYMREF &&
starts_with(tmp, "refs/heads/"))
localname = tmp;
@@ -183,7 +186,8 @@ static int fetch_refs_from_bundle(struct transport *transport,
if (!data->get_refs_from_bundle_called)
get_refs_from_bundle_inner(transport);
ret = unbundle(the_repository, &data->header, data->fd,
- &extra_index_pack_args, 0);
+ &extra_index_pack_args,
+ fetch_pack_fsck_objects() ? VERIFY_BUNDLE_FSCK : 0);
transport->hash_algo = data->header.hash_algo;
return ret;
}
@@ -543,10 +547,12 @@ static void update_one_tracking_ref(struct remote *remote, char *refname,
if (verbose)
fprintf(stderr, "updating local tracking ref '%s'\n", rs.dst);
if (deletion)
- delete_ref(NULL, rs.dst, NULL, 0);
+ refs_delete_ref(get_main_ref_store(the_repository),
+ NULL, rs.dst, NULL, 0);
else
- update_ref("update by push", rs.dst, new_oid,
- NULL, 0, 0);
+ refs_update_ref(get_main_ref_store(the_repository),
+ "update by push", rs.dst, new_oid,
+ NULL, 0, 0);
free(rs.dst);
}
}
@@ -814,7 +820,8 @@ void transport_print_push_status(const char *dest, struct ref *refs,
if (transport_color_config() < 0)
warning(_("could not parse transport.color.* config"));
- head = resolve_refdup("HEAD", RESOLVE_REF_READING, NULL, NULL);
+ head = refs_resolve_refdup(get_main_ref_store(the_repository), "HEAD",
+ RESOLVE_REF_READING, NULL, NULL);
if (verbose) {
for (ref = refs; ref; ref = ref->next)
@@ -1108,6 +1115,7 @@ static struct transport_vtable builtin_smart_vtable = {
struct transport *transport_get(struct remote *remote, const char *url)
{
const char *helper;
+ const char *p;
struct transport *ret = xcalloc(1, sizeof(*ret));
ret->progress = isatty(2);
@@ -1123,19 +1131,15 @@ struct transport *transport_get(struct remote *remote, const char *url)
ret->remote = remote;
helper = remote->foreign_vcs;
- if (!url && remote->url)
- url = remote->url[0];
+ if (!url)
+ url = remote->url.v[0];
ret->url = url;
- /* maybe it is a foreign URL? */
- if (url) {
- const char *p = url;
-
- while (is_urlschemechar(p == url, *p))
- p++;
- if (starts_with(p, "::"))
- helper = xstrndup(url, p - url);
- }
+ p = url;
+ while (is_urlschemechar(p == url, *p))
+ p++;
+ if (starts_with(p, "::"))
+ helper = xstrndup(url, p - url);
if (helper) {
transport_helper_init(ret, helper);
@@ -1172,6 +1176,7 @@ struct transport *transport_get(struct remote *remote, const char *url)
int len = external_specification_len(url);
char *handler = xmemdupz(url, len);
transport_helper_init(ret, handler);
+ free(handler);
}
if (ret->smart_options) {
diff --git a/tree-diff.c b/tree-diff.c
index 46107772d1..9252481df3 100644
--- a/tree-diff.c
+++ b/tree-diff.c
@@ -8,6 +8,7 @@
#include "tree.h"
#include "tree-walk.h"
#include "environment.h"
+#include "repository.h"
/*
* Some mode bits are also used internally for computations.
diff --git a/tree-walk.c b/tree-walk.c
index 6565d9ad99..a033397965 100644
--- a/tree-walk.c
+++ b/tree-walk.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "tree-walk.h"
#include "dir.h"
@@ -38,8 +40,8 @@ static int decode_tree_entry(struct tree_desc *desc, const char *buf, unsigned l
desc->entry.path = path;
desc->entry.mode = (desc->flags & TREE_DESC_RAW_MODES) ? mode : canon_mode(mode);
desc->entry.pathlen = len - 1;
- oidread_algop(&desc->entry.oid, (const unsigned char *)path + len,
- desc->algo);
+ oidread(&desc->entry.oid, (const unsigned char *)path + len,
+ desc->algo);
return 0;
}
diff --git a/tree-walk.h b/tree-walk.h
index 0b1067fbc5..aaea689f9a 100644
--- a/tree-walk.h
+++ b/tree-walk.h
@@ -1,7 +1,7 @@
#ifndef TREE_WALK_H
#define TREE_WALK_H
-#include "hash-ll.h"
+#include "hash.h"
struct index_state;
struct repository;
diff --git a/tree.c b/tree.c
index 7973d3f9a8..ad86ad1ba9 100644
--- a/tree.c
+++ b/tree.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "hex.h"
#include "tree.h"
diff --git a/unpack-trees.c b/unpack-trees.c
index c2b20b80d5..7dc884fafd 100644
--- a/unpack-trees.c
+++ b/unpack-trees.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "advice.h"
#include "strvec.h"
@@ -2318,7 +2320,8 @@ static int verify_clean_subdirectory(const struct cache_entry *ce,
if (S_ISGITLINK(ce->ce_mode)) {
struct object_id oid;
- int sub_head = resolve_gitlink_ref(ce->name, "HEAD", &oid);
+ int sub_head = repo_resolve_gitlink_ref(the_repository, ce->name,
+ "HEAD", &oid);
/*
* If we are not going to update the submodule, then
* we don't care.
diff --git a/upload-pack.c b/upload-pack.c
index 902144b9d3..0052c6a4dc 100644
--- a/upload-pack.c
+++ b/upload-pack.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "config.h"
#include "environment.h"
@@ -94,7 +96,7 @@ struct upload_pack_data {
struct packet_writer writer;
- const char *pack_objects_hook;
+ char *pack_objects_hook;
unsigned stateless_rpc : 1; /* v0 only */
unsigned no_done : 1; /* v0 only */
@@ -618,7 +620,8 @@ static void for_each_namespaced_ref_1(each_ref_fn fn,
if (allow_hidden_refs(data->allow_uor))
excludes = hidden_refs_to_excludes(&data->hidden_refs);
- for_each_namespaced_ref(excludes, fn, data);
+ refs_for_each_namespaced_ref(get_main_ref_store(the_repository),
+ excludes, fn, data);
}
@@ -873,7 +876,8 @@ static void deepen(struct upload_pack_data *data, int depth)
* Checking for reachable shallows requires that our refs be
* marked with OUR_REF.
*/
- head_ref_namespaced(check_ref, data);
+ refs_head_ref_namespaced(get_main_ref_store(the_repository),
+ check_ref, data);
for_each_namespaced_ref_1(check_ref, data);
get_reachable_list(data, &reachable_shallows);
@@ -1267,7 +1271,7 @@ static void write_v0_ref(struct upload_pack_data *data,
packet_fwrite_fmt(stdout, "%s %s\n", oid_to_hex(oid), refname_nons);
}
capabilities = NULL;
- if (!peel_iterated_oid(oid, &peeled))
+ if (!peel_iterated_oid(the_repository, oid, &peeled))
packet_fwrite_fmt(stdout, "%s %s^{}\n", oid_to_hex(&peeled), refname_nons);
return;
}
@@ -1288,7 +1292,8 @@ static int find_symref(const char *refname,
if ((flag & REF_ISSYMREF) == 0)
return 0;
- symref_target = resolve_ref_unsafe(refname, 0, NULL, &flag);
+ symref_target = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
+ refname, 0, NULL, &flag);
if (!symref_target || (flag & REF_ISSYMREF) == 0)
die("'%s' is a symref but it is not?", refname);
item = string_list_append(cb_data, strip_namespace(refname));
@@ -1413,13 +1418,15 @@ void upload_pack(const int advertise_refs, const int stateless_rpc,
if (data.timeout)
data.daemon_mode = 1;
- head_ref_namespaced(find_symref, &data.symref);
+ refs_head_ref_namespaced(get_main_ref_store(the_repository),
+ find_symref, &data.symref);
if (advertise_refs || !data.stateless_rpc) {
reset_timeout(data.timeout);
if (advertise_refs)
data.no_done = 1;
- head_ref_namespaced(send_ref, &data);
+ refs_head_ref_namespaced(get_main_ref_store(the_repository),
+ send_ref, &data);
for_each_namespaced_ref_1(send_ref, &data);
if (!data.sent_capabilities) {
const char *refname = "capabilities^{}";
@@ -1433,7 +1440,8 @@ void upload_pack(const int advertise_refs, const int stateless_rpc,
advertise_shallow_grafts(1);
packet_flush(1);
} else {
- head_ref_namespaced(check_ref, &data);
+ refs_head_ref_namespaced(get_main_ref_store(the_repository),
+ check_ref, &data);
for_each_namespaced_ref_1(check_ref, &data);
}
@@ -1511,7 +1519,7 @@ static int parse_want_ref(struct packet_writer *writer, const char *line,
strbuf_addf(&refname, "%s%s", get_git_namespace(), refname_nons);
if (ref_is_hidden(refname_nons, refname.buf, hidden_refs) ||
- read_ref(refname.buf, &oid)) {
+ refs_read_ref(get_main_ref_store(the_repository), refname.buf, &oid)) {
packet_writer_error(writer, "unknown ref %s", refname_nons);
die("unknown ref %s", refname_nons);
}
diff --git a/userdiff.c b/userdiff.c
index 82bc76b910..c4ebb9ff73 100644
--- a/userdiff.c
+++ b/userdiff.c
@@ -333,7 +333,7 @@ PATTERNS("scheme",
"|([^][)(}{[ \t])+"),
PATTERNS("tex", "^(\\\\((sub)*section|chapter|part)\\*{0,1}\\{.*)$",
"\\\\[a-zA-Z@]+|\\\\.|([a-zA-Z0-9]|[^\x01-\x7f])+"),
-{ "default", NULL, NULL, -1, { NULL, 0 } },
+{ .name = "default", .binary = -1 },
};
#undef PATTERNS
#undef IPATTERN
@@ -399,7 +399,7 @@ static struct userdiff_driver *userdiff_find_by_namelen(const char *name, size_t
static int parse_funcname(struct userdiff_funcname *f, const char *k,
const char *v, int cflags)
{
- if (git_config_string(&f->pattern, k, v) < 0)
+ if (git_config_string((char **) &f->pattern, k, v) < 0)
return -1;
f->cflags = cflags;
return 0;
@@ -445,15 +445,19 @@ int userdiff_config(const char *k, const char *v)
if (!strcmp(type, "binary"))
return parse_tristate(&drv->binary, k, v);
if (!strcmp(type, "command"))
- return git_config_string(&drv->external, k, v);
+ return git_config_string((char **) &drv->external.cmd, k, v);
+ if (!strcmp(type, "trustexitcode")) {
+ drv->external.trust_exit_code = git_config_bool(k, v);
+ return 0;
+ }
if (!strcmp(type, "textconv"))
- return git_config_string(&drv->textconv, k, v);
+ return git_config_string((char **) &drv->textconv, k, v);
if (!strcmp(type, "cachetextconv"))
return parse_bool(&drv->textconv_want_cache, k, v);
if (!strcmp(type, "wordregex"))
- return git_config_string(&drv->word_regex, k, v);
+ return git_config_string((char **) &drv->word_regex, k, v);
if (!strcmp(type, "algorithm"))
- return git_config_string(&drv->algorithm, k, v);
+ return git_config_string((char **) &drv->algorithm, k, v);
return 0;
}
diff --git a/userdiff.h b/userdiff.h
index d726804c3e..7565930337 100644
--- a/userdiff.h
+++ b/userdiff.h
@@ -11,9 +11,14 @@ struct userdiff_funcname {
int cflags;
};
+struct external_diff {
+ char *cmd;
+ unsigned trust_exit_code:1;
+};
+
struct userdiff_driver {
const char *name;
- const char *external;
+ struct external_diff external;
const char *algorithm;
int binary;
struct userdiff_funcname funcname;
diff --git a/walker.c b/walker.c
index c0fd632d92..0fafdc97cf 100644
--- a/walker.c
+++ b/walker.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "gettext.h"
#include "hex.h"
@@ -286,7 +288,8 @@ int walker_fetch(struct walker *walker, int targets, char **target,
ALLOC_ARRAY(oids, targets);
if (write_ref) {
- transaction = ref_transaction_begin(&err);
+ transaction = ref_store_transaction_begin(get_main_ref_store(the_repository),
+ &err);
if (!transaction) {
error("%s", err.buf);
goto done;
@@ -294,7 +297,8 @@ int walker_fetch(struct walker *walker, int targets, char **target,
}
if (!walker->get_recover) {
- for_each_ref(mark_complete, NULL);
+ refs_for_each_ref(get_main_ref_store(the_repository),
+ mark_complete, NULL);
commit_list_sort_by_date(&complete);
}
@@ -324,7 +328,7 @@ int walker_fetch(struct walker *walker, int targets, char **target,
strbuf_reset(&refname);
strbuf_addf(&refname, "refs/%s", write_ref[i]);
if (ref_transaction_update(transaction, refname.buf,
- oids + i, NULL, 0,
+ oids + i, NULL, NULL, NULL, 0,
msg ? msg : "fetch (unknown)",
&err)) {
error("%s", err.buf);
diff --git a/worktree.c b/worktree.c
index cf5eea8c93..f3c4c8ec54 100644
--- a/worktree.c
+++ b/worktree.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "abspath.h"
#include "environment.h"
@@ -53,6 +55,15 @@ static void add_head_info(struct worktree *wt)
wt->is_detached = 1;
}
+static int is_current_worktree(struct worktree *wt)
+{
+ char *git_dir = absolute_pathdup(get_git_dir());
+ const char *wt_git_dir = get_worktree_git_dir(wt);
+ int is_current = !fspathcmp(git_dir, absolute_path(wt_git_dir));
+ free(git_dir);
+ return is_current;
+}
+
/**
* get the main worktree
*/
@@ -65,6 +76,7 @@ static struct worktree *get_main_worktree(int skip_reading_head)
strbuf_strip_suffix(&worktree_path, "/.git");
CALLOC_ARRAY(worktree, 1);
+ worktree->repo = the_repository;
worktree->path = strbuf_detach(&worktree_path, NULL);
/*
* NEEDSWORK: If this function is called from a secondary worktree and
@@ -75,6 +87,7 @@ static struct worktree *get_main_worktree(int skip_reading_head)
*/
worktree->is_bare = (is_bare_repository_cfg == 1) ||
is_bare_repository();
+ worktree->is_current = is_current_worktree(worktree);
if (!skip_reading_head)
add_head_info(worktree);
return worktree;
@@ -98,8 +111,10 @@ struct worktree *get_linked_worktree(const char *id,
strbuf_strip_suffix(&worktree_path, "/.git");
CALLOC_ARRAY(worktree, 1);
+ worktree->repo = the_repository;
worktree->path = strbuf_detach(&worktree_path, NULL);
worktree->id = xstrdup(id);
+ worktree->is_current = is_current_worktree(worktree);
if (!skip_reading_head)
add_head_info(worktree);
@@ -109,23 +124,6 @@ done:
return worktree;
}
-static void mark_current_worktree(struct worktree **worktrees)
-{
- char *git_dir = absolute_pathdup(get_git_dir());
- int i;
-
- for (i = 0; worktrees[i]; i++) {
- struct worktree *wt = worktrees[i];
- const char *wt_git_dir = get_worktree_git_dir(wt);
-
- if (!fspathcmp(git_dir, absolute_path(wt_git_dir))) {
- wt->is_current = 1;
- break;
- }
- }
- free(git_dir);
-}
-
/*
* NEEDSWORK: This function exists so that we can look up metadata of a
* worktree without trying to access any of its internals like the refdb. It
@@ -162,7 +160,6 @@ static struct worktree **get_worktrees_internal(int skip_reading_head)
ALLOC_GROW(list, counter + 1, alloc);
list[counter] = NULL;
- mark_current_worktree(list);
return list;
}
diff --git a/worktree.h b/worktree.h
index f14784a2ff..11279d0c8f 100644
--- a/worktree.h
+++ b/worktree.h
@@ -6,6 +6,8 @@
struct strbuf;
struct worktree {
+ /* The repository this worktree belongs to. */
+ struct repository *repo;
char *path;
char *id;
char *head_ref; /* NULL if HEAD is broken or detached */
@@ -176,14 +178,6 @@ int is_worktree_being_rebased(const struct worktree *wt, const char *target);
int is_worktree_being_bisected(const struct worktree *wt, const char *target);
/*
- * Similar to git_path() but can produce paths for a specified
- * worktree instead of current one
- */
-const char *worktree_git_path(const struct worktree *wt,
- const char *fmt, ...)
- __attribute__((format (printf, 2, 3)));
-
-/*
* Return a refname suitable for access from the current ref store.
*/
void strbuf_worktree_ref(const struct worktree *wt,
diff --git a/wt-status.c b/wt-status.c
index bdfc23e2ae..b778eef989 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "advice.h"
#include "wt-status.h"
@@ -126,6 +128,7 @@ void status_printf(struct wt_status *s, const char *color,
va_end(ap);
}
+__attribute__((format (printf, 3, 4)))
static void status_printf_more(struct wt_status *s, const char *color,
const char *fmt, ...)
{
@@ -145,7 +148,8 @@ void wt_status_prepare(struct repository *r, struct wt_status *s)
s->show_untracked_files = SHOW_NORMAL_UNTRACKED_FILES;
s->use_color = -1;
s->relative_paths = 1;
- s->branch = resolve_refdup("HEAD", 0, NULL, NULL);
+ s->branch = refs_resolve_refdup(get_main_ref_store(the_repository),
+ "HEAD", 0, NULL, NULL);
s->reference = "HEAD";
s->fp = stdout;
s->index_file = get_index_file();
@@ -640,7 +644,7 @@ static void wt_status_collect_changes_index(struct wt_status *s)
repo_init_revisions(s->repo, &rev, NULL);
memset(&opt, 0, sizeof(opt));
- opt.def = s->is_initial ? empty_tree_oid_hex() : s->reference;
+ opt.def = s->is_initial ? empty_tree_oid_hex(the_repository->hash_algo) : s->reference;
setup_revisions(0, NULL, &rev, &opt);
rev.diffopt.flags.override_submodule_config = 1;
@@ -976,7 +980,8 @@ static int stash_count_refs(struct object_id *ooid UNUSED,
static int count_stash_entries(void)
{
int n = 0;
- for_each_reflog_ent("refs/stash", stash_count_refs, &n);
+ refs_for_each_reflog_ent(get_main_ref_store(the_repository),
+ "refs/stash", stash_count_refs, &n);
return n;
}
@@ -1134,7 +1139,7 @@ static void wt_longstatus_print_verbose(struct wt_status *s)
rev.diffopt.ita_invisible_in_index = 1;
memset(&opt, 0, sizeof(opt));
- opt.def = s->is_initial ? empty_tree_oid_hex() : s->reference;
+ opt.def = s->is_initial ? empty_tree_oid_hex(the_repository->hash_algo) : s->reference;
setup_revisions(0, NULL, &rev, &opt);
rev.diffopt.output_format |= DIFF_FORMAT_PATCH;
@@ -1304,10 +1309,10 @@ static int split_commit_in_progress(struct wt_status *s)
!s->branch || strcmp(s->branch, "HEAD"))
return 0;
- if (read_ref_full("HEAD", RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,
- &head_oid, &head_flags) ||
- read_ref_full("ORIG_HEAD", RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,
- &orig_head_oid, &orig_head_flags))
+ if (refs_read_ref_full(get_main_ref_store(the_repository), "HEAD", RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,
+ &head_oid, &head_flags) ||
+ refs_read_ref_full(get_main_ref_store(the_repository), "ORIG_HEAD", RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,
+ &orig_head_oid, &orig_head_flags))
return 0;
if (head_flags & REF_ISSYMREF || orig_head_flags & REF_ISSYMREF)
return 0;
@@ -1679,7 +1684,7 @@ static void wt_status_get_detached_from(struct repository *r,
char *ref = NULL;
strbuf_init(&cb.buf, 0);
- if (for_each_reflog_ent_reverse("HEAD", grab_1st_switch, &cb) <= 0) {
+ if (refs_for_each_reflog_ent_reverse(get_main_ref_store(the_repository), "HEAD", grab_1st_switch, &cb) <= 0) {
strbuf_release(&cb.buf);
return;
}
@@ -2087,7 +2092,8 @@ static void wt_shortstatus_print_tracking(struct wt_status *s)
upstream_is_gone = 1;
}
- short_base = shorten_unambiguous_ref(base, 0);
+ short_base = refs_shorten_unambiguous_ref(get_main_ref_store(the_repository),
+ base, 0);
color_fprintf(s->fp, header_color, "...");
color_fprintf(s->fp, branch_color_remote, "%s", short_base);
free(short_base);
@@ -2220,7 +2226,8 @@ static void wt_porcelain_v2_print_tracking(struct wt_status *s)
ab_info = stat_tracking_info(branch, &nr_ahead, &nr_behind,
&base, 0, s->ahead_behind_flags);
if (base) {
- base = shorten_unambiguous_ref(base, 0);
+ base = refs_shorten_unambiguous_ref(get_main_ref_store(the_repository),
+ base, 0);
fprintf(s->fp, "# branch.upstream %s%c", base, eol);
free((char *)base);
@@ -2404,7 +2411,7 @@ static void wt_porcelain_v2_print_unmerged_entry(
int mode;
struct object_id oid;
} stages[3];
- char *key;
+ const char *key;
char submodule_token[5];
char unmerged_prefix = 'u';
char eol_char = s->null_termination ? '\0' : '\n';
diff --git a/xdiff-interface.c b/xdiff-interface.c
index 16ed8ac492..d5dc88661e 100644
--- a/xdiff-interface.c
+++ b/xdiff-interface.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "gettext.h"
#include "config.h"
diff --git a/xdiff-interface.h b/xdiff-interface.h
index 38537169b7..1ed430b622 100644
--- a/xdiff-interface.h
+++ b/xdiff-interface.h
@@ -1,7 +1,7 @@
#ifndef XDIFF_INTERFACE_H
#define XDIFF_INTERFACE_H
-#include "hash-ll.h"
+#include "hash.h"
#include "xdiff/xdiff.h"
/*