]> git.ipfire.org Git - thirdparty/git.git/commitdiff
Merge branch 'jc/show-untracked-false'
authorJunio C Hamano <gitster@pobox.com>
Thu, 28 Mar 2024 21:13:50 +0000 (14:13 -0700)
committerJunio C Hamano <gitster@pobox.com>
Thu, 28 Mar 2024 21:13:50 +0000 (14:13 -0700)
The status.showUntrackedFiles configuration variable had a name
that tempts users to set a Boolean value expressed in our usual
"false", "off", and "0", but it only took "no".  This has been
corrected so "true" and its synonyms are taken as "normal", while
"false" and its synonyms are taken as "no".

* jc/show-untracked-false:
  status: allow --untracked=false and friends
  status: unify parsing of --untracked= and status.showUntrackedFiles

199 files changed:
.github/workflows/main.yml
Documentation/RelNotes/2.45.0.txt
Documentation/config/advice.txt
Documentation/config/clean.txt
Documentation/config/core.txt
Documentation/config/diff.txt
Documentation/config/extensions.txt
Documentation/config/init.txt
Documentation/diff-options.txt
Documentation/fetch-options.txt
Documentation/git-bugreport.txt
Documentation/git-clean.txt
Documentation/git-clone.txt
Documentation/git-config.txt
Documentation/git-init.txt
Documentation/git-interpret-trailers.txt
Documentation/git-pull.txt
Documentation/git-rebase.txt
Documentation/git-rev-parse.txt
Documentation/git-send-email.txt
Documentation/git-status.txt
Documentation/git.txt
Documentation/gitremote-helpers.txt
Documentation/howto/update-hook-example.txt
Documentation/pretty-formats.txt
Documentation/rev-list-options.txt
Documentation/urls.txt
Documentation/user-manual.txt
Makefile
advice.c
advice.h
archive.c
branch.c
builtin/am.c
builtin/branch.c
builtin/bugreport.c
builtin/cat-file.c
builtin/checkout.c
builtin/clean.c
builtin/clone.c
builtin/commit.c
builtin/fast-import.c
builtin/grep.c
builtin/index-pack.c
builtin/interpret-trailers.c
builtin/ls-tree.c
builtin/merge.c
builtin/pack-objects.c
builtin/read-tree.c
builtin/rebase.c
builtin/remote.c
builtin/repack.c
builtin/rev-parse.c
builtin/stash.c
builtin/tag.c
builtin/unpack-objects.c
cache-tree.c
commit.c
commit.h
config.mak.uname
contrib/completion/git-completion.bash
contrib/coverage-diff.sh
contrib/subtree/t/t7900-subtree.sh
date.c
delta-islands.c
diff-lib.c
diff.c
dir.c
dir.h
fsck.c
fsmonitor.c
git-quiltimport.sh
git.c
hash-ll.h
hash.h
http-push.c
list-objects.c
lockfile.h
loose.c [new file with mode: 0644]
loose.h [new file with mode: 0644]
match-trees.c
merge-ort.c
merge-recursive.c
merge.c
name-hash.c
name-hash.h
object-file-convert.c [new file with mode: 0644]
object-file-convert.h [new file with mode: 0644]
object-file.c
object-name.c
object-name.h
object-store-ll.h
object.c
object.h
oid-array.c
pack-bitmap-write.c
packfile.c
parse-options.c
pretty.c
ref-filter.c
reflog.c
refs/reftable-backend.c
reftable/block.c
reftable/block.h
reftable/iter.c
reftable/iter.h
reftable/merged.c
reftable/merged.h
reftable/merged_test.c
reftable/pq.c
reftable/pq.h
reftable/pq_test.c
reftable/readwrite_test.c
reftable/record.c
reftable/record.h
reftable/record_test.c
reftable/reftable-record.h
reftable/stack.c
reftable/stack_test.c
reftable/system.h
repository.c
repository.h
revision.c
sequencer.c
sequencer.h
setup.c
setup.h
sideband.c
t/annotate-tests.sh
t/helper/test-delete-gpgsig.c [new file with mode: 0644]
t/helper/test-tool.c
t/helper/test-tool.h
t/lib-cvs.sh
t/perf/repos/inflate-repo.sh
t/t0002-gitfile.sh
t/t0006-date.sh
t/t0010-racy-git.sh
t/t0011-hashmap.sh
t/t0028-working-tree-encoding.sh
t/t0035-safe-bare-repository.sh
t/t0040-parse-options.sh
t/t0204-gettext-reencode-sanity.sh
t/t0211-trace2-perf.sh
t/t1006-cat-file.sh
t/t1007-hash-object.sh
t/t1016-compatObjectFormat.sh [new file with mode: 0755]
t/t1016/gpg [new file with mode: 0755]
t/t1091-sparse-checkout-builtin.sh
t/t1301-shared-repo.sh
t/t1502-rev-parse-parseopt.sh
t/t1509/prepare-chroot.sh
t/t2070-restore.sh
t/t2071-restore-patch.sh
t/t2072-restore-pathspec-file.sh
t/t3200-branch.sh
t/t3321-notes-stripspace.sh
t/t3920-crlf-messages.sh
t/t4002-diff-basic.sh
t/t4013-diff-various.sh
t/t4020-diff-external.sh
t/t4201-shortlog.sh
t/t4205-log-pretty-formats.sh
t/t4301-merge-tree-write-tree.sh
t/t5100-mailinfo.sh
t/t5300-pack-object.sh
t/t5317-pack-objects-filter-objects.sh
t/t5401-update-hooks.sh
t/t5534-push-signed.sh
t/t5606-clone-options.sh
t/t6112-rev-list-filters-objects.sh
t/t6413-merge-crlf.sh
t/t6418-merge-text-auto.sh
t/t7300-clean.sh
t/t7301-clean-interactive.sh
t/t7513-interpret-trailers.sh
t/t7527-builtin-fsmonitor.sh
t/t7704-repack-cruft.sh
t/t8010-cat-file-filters.sh
t/t8013-blame-ignore-revs.sh
t/t9118-git-svn-funky-branch-names.sh
t/t9300-fast-import.sh
t/t9350-fast-export.sh
t/t9400-git-cvsserver-server.sh
t/t9802-git-p4-filetype.sh
t/t9807-git-p4-submit.sh
t/t9824-git-p4-git-lfs.sh
t/t9902-completion.sh
t/test-lib-functions.sh
tempfile.c
tempfile.h
trace2.c
trailer.c
trailer.h
transport-helper.c
tree-walk.c
tree-walk.h
tree.c
walker.c
wt-status.c

index 683a2d633ed5a95f7daab4ffb8ba5cf1ed8d0634..3428773b096e9ea0da0488b9d75171f09b7ca961 100644 (file)
@@ -159,7 +159,7 @@ jobs:
       if: failure() && env.FAILED_TEST_ARTIFACTS != ''
       uses: actions/upload-artifact@v4
       with:
-        name: failed-tests-windows
+        name: failed-tests-windows-${{ matrix.nr }}
         path: ${{env.FAILED_TEST_ARTIFACTS}}
   vs-build:
     name: win+VS build
@@ -250,7 +250,7 @@ jobs:
       if: failure() && env.FAILED_TEST_ARTIFACTS != ''
       uses: actions/upload-artifact@v4
       with:
-        name: failed-tests-windows
+        name: failed-tests-windows-vs-${{ matrix.nr }}
         path: ${{env.FAILED_TEST_ARTIFACTS}}
   regular:
     name: ${{matrix.vector.jobname}} (${{matrix.vector.pool}})
index 2e759325393add7b2ac88fb69eaf501685e567bd..903c6659603c648e67e47cde3e8a922fb402ed30 100644 (file)
@@ -34,6 +34,17 @@ UI, Workflows & Features
  * "git log --merge" learned to pay attention to CHERRY_PICK_HEAD and
    other kinds of *_HEAD pseudorefs.
 
+ * Platform specific tweaks for OS/390 has been added to
+   config.mak.uname.
+
+ * Users with safe.bareRepository=explicit can still work from within
+   $GIT_DIR of a seconary worktree (which resides at .git/worktrees/$name/)
+   of the primary worktree without explicitly specifying the $GIT_DIR
+   environment variable or the --git-dir=<path> option.
+
+ * The output format for dates "iso-strict" has been tweaked to show
+   a time in the Zulu timezone with "Z" suffix, instead of "+00:00".
+
 
 Performance, Internal Implementation, Development Support etc.
 
@@ -54,6 +65,34 @@ Performance, Internal Implementation, Development Support etc.
    fetching of objects from the promisor remote, which may be handy
    for debugging.
 
+ * The implementation in "git clean" that makes "-n" and "-i" ignore
+   clean.requireForce has been simplified, together with the
+   documentation.
+
+ * The code to iterate over refs with the reftable backend has seen
+   some optimization.
+
+ * Uses of xwrite() helper have been audited and updated for better
+   error checking and simpler code.
+
+ * Some trace2 events that lacked def_param have learned to show it,
+   enriching the output.
+
+ * The parse-options code that deals with abbreviated long option
+   names have been cleaned up.
+
+ * The code in reftable backend that creates new table files works
+   better with the tempfile framework to avoid leaving cruft after a
+   failure.
+
+ * The reftable code has its own custom binary search function whose
+   comparison callback has an unusual interface, which caused the
+   binary search to degenerate into a linear search, which has been
+   corrected.
+
+ * The code to iterate over reflogs in the reftable has been optimized
+   to reduce memory allocation and deallocation.
+
 
 Fixes since v2.44
 -----------------
@@ -130,6 +169,63 @@ Fixes since v2.44
  * Make sure failure return from merge_bases_many() is properly caught.
    (merge 25fd20eb44 js/merge-base-with-missing-commit later to maint).
 
+ * FSMonitor client code was confused when FSEvents were given in a
+   different case on a case-insensitive filesystem, which has been
+   corrected.
+   (merge 29c139ce78 jh/fsmonitor-icase-corner-case-fix later to maint).
+
+ * The "core.commentChar" configuration variable only allows an ASCII
+   character, which was not clearly documented, which has been
+   corrected.
+   (merge fb7c556f58 kh/doc-commentchar-is-a-byte later to maint).
+
+ * With release 2.44 we got rid of all uses of test_i18ngrep and there
+   is no in-flight topic that adds a new use of it.  Make a call to
+   test_i18ngrep a hard failure, so that we can remove it at the end
+   of this release cycle.
+   (merge 381a83dfa3 jc/test-i18ngrep later to maint).
+
+ * The command line completion script (in contrib/) learned to
+   complete "git reflog" better.
+   (merge 1284f9cc11 rj/complete-reflog later to maint).
+
+ * The logic to complete the command line arguments to "git worktree"
+   subcommand (in contrib/) has been updated to correctly honor things
+   like "git -C dir" etc.
+   (merge 3574816d98 rj/complete-worktree-paths-fix later to maint).
+
+ * When git refuses to create a branch because the proposed branch
+   name is not a valid refname, an advice message is given to refer
+   the user to exact naming rules.
+   (merge 8fbd903e58 kh/branch-ref-syntax-advice later to maint).
+
+ * Code simplification by getting rid of code that sets an environment
+   variable that is no longer used.
+   (merge 72a8d3f027 pw/rebase-i-ignore-cherry-pick-help-environment later to maint).
+
+ * The code to find the effective end of log message can fall into an
+   endless loop, which has been corrected.
+   (merge 2541cba2d6 fs/find-end-of-log-message-fix later to maint).
+
+ * Mark-ups used in the documentation has been improved for
+   consistency.
+   (merge 45d5ed3e50 ja/doc-markup-fixes later to maint).
+
+ * The status.showUntrackedFiles configuration variable was
+   incorrectly documented to accept "false", which has been corrected.
+
+ * Leaks from "git restore" have been plugged.
+   (merge 2f64da0790 rj/restore-plug-leaks later to maint).
+
+ * "git bugreport --no-suffix" was not supported and instead
+   segfaulted, which has been corrected.
+   (merge b3b57c69da js/bugreport-no-suffix-fix later to maint).
+
+ * The documentation for "%(trailers[:options])" placeholder in the
+   "--pretty" option of commands in the "git log" family has been
+   updated.
+   (merge bff85a338c bl/doc-key-val-sep-fix later to maint).
+
  * Other code cleanup, docfix, build fix, etc.
    (merge f0e578c69c rs/use-xstrncmpz later to maint).
    (merge 83e6eb7d7a ba/credential-test-clean-fix later to maint).
@@ -146,3 +242,9 @@ Fixes since v2.44
    (merge 40b8076462 ak/rebase-autosquash later to maint).
    (merge 3223204456 eg/add-uflags later to maint).
    (merge 5f78d52dce es/config-doc-sort-sections later to maint).
+   (merge 781fb7b4c2 as/option-names-in-messages later to maint).
+   (merge 51d41dc243 jk/doc-remote-helpers-markup-fix later to maint).
+   (merge e1aaf309db pb/ci-win-artifact-names-fix later to maint).
+   (merge ad538c61da jc/index-pack-fsck-levels later to maint).
+   (merge 67471bc704 ja/doc-formatting-fix later to maint).
+   (merge 86f9ce7dd6 bl/doc-config-fixes later to maint).
index dde8e7840ef38225995549411fbcf9305b160c37..f83341165367baac4f02779eedf432822644e615 100644 (file)
@@ -2,27 +2,27 @@ advice.*::
        These variables control various optional help messages designed to
        aid new users.  When left unconfigured, Git will give the message
        alongside instructions on how to squelch it.  You can tell Git
-       that you do not need the help message by setting these to 'false':
+       that you do not need the help message by setting these to `false`:
 +
 --
        addEmbeddedRepo::
-               Advice on what to do when you've accidentally added one
+               Shown when the user accidentally adds one
                git repo inside of another.
        addEmptyPathspec::
-               Advice shown if a user runs the add command without providing
+               Shown when the user runs `git add` without providing
                the pathspec parameter.
        addIgnoredFile::
-               Advice shown if a user attempts to add an ignored file to
+               Shown when the user attempts to add an ignored file to
                the index.
        amWorkDir::
-               Advice that shows the location of the patch file when
-               linkgit:git-am[1] fails to apply it.
+               Shown when linkgit:git-am[1] fails to apply a patch
+               file, to tell the user the location of the file.
        ambiguousFetchRefspec::
-               Advice shown when a fetch refspec for multiple remotes maps to
+               Shown when a fetch refspec for multiple remotes maps to
                the same remote-tracking branch namespace and causes branch
                tracking set-up to fail.
        checkoutAmbiguousRemoteBranchName::
-               Advice shown when the argument to
+               Shown when the argument to
                linkgit:git-checkout[1] and linkgit:git-switch[1]
                ambiguously resolves to a
                remote tracking branch on more than one remote in
@@ -33,31 +33,31 @@ advice.*::
                to be used by default in some situations where this
                advice would be printed.
        commitBeforeMerge::
-               Advice shown when linkgit:git-merge[1] refuses to
+               Shown when linkgit:git-merge[1] refuses to
                merge to avoid overwriting local changes.
        detachedHead::
-               Advice shown when you used
+               Shown when the user uses
                linkgit:git-switch[1] or linkgit:git-checkout[1]
-               to move to the detached HEAD state, to instruct how to
-               create a local branch after the fact.
+               to move to the detached HEAD state, to tell the user how
+               to create a local branch after the fact.
        diverging::
-               Advice shown when a fast-forward is not possible.
+               Shown when a fast-forward is not possible.
        fetchShowForcedUpdates::
-               Advice shown when linkgit:git-fetch[1] takes a long time
+               Shown when linkgit:git-fetch[1] takes a long time
                to calculate forced updates after ref updates, or to warn
                that the check is disabled.
        forceDeleteBranch::
-               Advice shown when a user tries to delete a not fully merged
+               Shown when the user tries to delete a not fully merged
                branch without the force option set.
        ignoredHook::
-               Advice shown if a hook is ignored because the hook is not
+               Shown when a hook is ignored because the hook is not
                set as executable.
        implicitIdentity::
-               Advice on how to set your identity configuration when
-               your information is guessed from the system username and
-               domain name.
+               Shown when the user's information is guessed from the
+               system username and domain name, to tell the user how to
+               set their identity configuration.
        nestedTag::
-               Advice shown if a user attempts to recursively tag a tag object.
+               Shown when a user attempts to recursively tag a tag object.
        pushAlreadyExists::
                Shown when linkgit:git-push[1] rejects an update that
                does not qualify for fast-forwarding (e.g., a tag.)
@@ -71,12 +71,12 @@ advice.*::
                object that is not a commit-ish, or make the remote
                ref point at an object that is not a commit-ish.
        pushNonFFCurrent::
-               Advice shown when linkgit:git-push[1] fails due to a
+               Shown when linkgit:git-push[1] fails due to a
                non-fast-forward update to the current branch.
        pushNonFFMatching::
-               Advice shown when you ran linkgit:git-push[1] and pushed
-               'matching refs' explicitly (i.e. you used ':', or
-               specified a refspec that isn't your current branch) and
+               Shown when the user ran linkgit:git-push[1] and pushed
+               "matching refs" explicitly (i.e. used `:`, or
+               specified a refspec that isn't the current branch) and
                it resulted in a non-fast-forward error.
        pushRefNeedsUpdate::
                Shown when linkgit:git-push[1] rejects a forced update of
@@ -87,25 +87,28 @@ advice.*::
                guess based on the source and destination refs what
                remote ref namespace the source belongs in, but where
                we can still suggest that the user push to either
-               refs/heads/* or refs/tags/* based on the type of the
+               `refs/heads/*` or `refs/tags/*` based on the type of the
                source object.
        pushUpdateRejected::
-               Set this variable to 'false' if you want to disable
-               'pushNonFFCurrent', 'pushNonFFMatching', 'pushAlreadyExists',
-               'pushFetchFirst', 'pushNeedsForce', and 'pushRefNeedsUpdate'
+               Set this variable to `false` if you want to disable
+               `pushNonFFCurrent`, `pushNonFFMatching`, `pushAlreadyExists`,
+               `pushFetchFirst`, `pushNeedsForce`, and `pushRefNeedsUpdate`
                simultaneously.
+       refSyntax::
+               Shown when the user provides an illegal ref name, to
+               tell the user about the ref syntax documentation.
        resetNoRefresh::
-               Advice to consider using the `--no-refresh` option to
-               linkgit:git-reset[1] when the command takes more than 2 seconds
-               to refresh the index after reset.
+               Shown when linkgit:git-reset[1] takes more than 2
+               seconds to refresh the index after reset, to tell the user
+               that they can use the `--no-refresh` option.
        resolveConflict::
-               Advice shown by various commands when conflicts
+               Shown by various commands when conflicts
                prevent the operation from being performed.
        rmHints::
-               In case of failure in the output of linkgit:git-rm[1],
-               show directions on how to proceed from the current state.
+               Shown on failure in the output of linkgit:git-rm[1], to
+               give directions on how to proceed from the current state.
        sequencerInUse::
-               Advice shown when a sequencer command is already in progress.
+               Shown when a sequencer command is already in progress.
        skippedCherryPicks::
                Shown when linkgit:git-rebase[1] skips a commit that has already
                been cherry-picked onto the upstream branch.
@@ -123,30 +126,30 @@ advice.*::
                by linkgit:git-switch[1] or
                linkgit:git-checkout[1] when switching branches.
        statusUoption::
-               Advise to consider using the `-u` option to linkgit:git-status[1]
-               when the command takes more than 2 seconds to enumerate untracked
-               files.
+               Shown when linkgit:git-status[1] takes more than 2
+               seconds to enumerate untracked files, to tell the user that
+               they can use the `-u` option.
        submoduleAlternateErrorStrategyDie::
-               Advice shown when a submodule.alternateErrorStrategy option
+               Shown when a submodule.alternateErrorStrategy option
                configured to "die" causes a fatal error.
        submoduleMergeConflict::
                Advice shown when a non-trivial submodule merge conflict is
                encountered.
        submodulesNotUpdated::
-               Advice shown when a user runs a submodule command that fails
+               Shown when a user runs a submodule command that fails
                because `git submodule update --init` was not run.
        suggestDetachingHead::
-               Advice shown when linkgit:git-switch[1] refuses to detach HEAD
+               Shown when linkgit:git-switch[1] refuses to detach HEAD
                without the explicit `--detach` option.
        updateSparsePath::
-               Advice shown when either linkgit:git-add[1] or linkgit:git-rm[1]
+               Shown when either linkgit:git-add[1] or linkgit:git-rm[1]
                is asked to update index entries outside the current sparse
                checkout.
        waitingForEditor::
-               Print a message to the terminal whenever Git is waiting for
-               editor input from the user.
+               Shown when Git is waiting for editor input. Relevant
+               when e.g. the editor is not launched inside the terminal.
        worktreeAddOrphan::
-               Advice shown when a user tries to create a worktree from an
-               invalid reference, to instruct how to create a new unborn
+               Shown when the user tries to create a worktree from an
+               invalid reference, to tell the user how to create a new unborn
                branch instead.
 --
index f05b9403b5ad903ea68d68e896acaff253bfde37..c0188ead4e6cd8b2b38a5feecb20c24897e03161 100644 (file)
@@ -1,3 +1,3 @@
 clean.requireForce::
-       A boolean to make git-clean do nothing unless given -f,
-       -i, or -n.  Defaults to true.
+       A boolean to make git-clean refuse to delete files unless -f
+       is given. Defaults to true.
index 0e8c2832bf9b8a5251c51f346ecfcf47c7e8d530..2d4bbdb25fa3104bf5151ca98be42f0b4c438b2a 100644 (file)
@@ -521,7 +521,7 @@ core.editor::
 
 core.commentChar::
        Commands such as `commit` and `tag` that let you edit
-       messages consider a line that begins with this character
+       messages consider a line that begins with this ASCII character
        commented, and removes them after the editor returns
        (default '#').
 +
index 6c7e09a1ef5eb481b2abb26abd93edf38806cf76..5ce7b91f1d0d2356928ccc5e59195a6db205d81b 100644 (file)
@@ -108,9 +108,15 @@ diff.mnemonicPrefix::
 `git diff --no-index a b`;;
        compares two non-git things (1) and (2).
 
-diff.noprefix::
+diff.noPrefix::
        If set, 'git diff' does not show any source or destination prefix.
 
+diff.srcPrefix::
+       If set, 'git diff' uses this source prefix. Defaults to "a/".
+
+diff.dstPrefix::
+       If set, 'git diff' uses this destination prefix. Defaults to "b/".
+
 diff.relative::
        If set to 'true', 'git diff' does not show changes outside of the directory
        and show pathnames relative to the current directory.
index 66db0e15da7db82819de941626b7abc5983862c5..38dce3df359761b54dca8afcb13f77e1919de098 100644 (file)
@@ -7,6 +7,18 @@ Note that this setting should only be set by linkgit:git-init[1] or
 linkgit:git-clone[1].  Trying to change it after initialization will not
 work and will produce hard-to-diagnose issues.
 
+extensions.compatObjectFormat::
+
+       Specify a compatitbility hash algorithm to use.  The acceptable values
+       are `sha1` and `sha256`.  The value specified must be different from the
+       value of extensions.objectFormat.  This allows client level
+       interoperability between git repositories whose objectFormat matches
+       this compatObjectFormat.  In particular when fully implemented the
+       pushes and pulls from a repository in whose objectFormat matches
+       compatObjectFormat.  As well as being able to use oids encoded in
+       compatObjectFormat in addition to oids encoded with objectFormat to
+       locally specify objects.
+
 extensions.refStorage::
        Specify the ref storage format to use. The acceptable values are:
 +
index 79c79d66174ebd9f2dfb867a6a39a339529b7fc0..dd1d8332737fe89a2feca44ce59adad4f64bce5a 100644 (file)
@@ -1,7 +1,10 @@
-init.templateDir::
-       Specify the directory from which templates will be copied.
-       (See the "TEMPLATE DIRECTORY" section of linkgit:git-init[1].)
+:see-git-init:
+ifndef::git-init[]
+:see-git-init: (See the "TEMPLATE DIRECTORY" section of linkgit:git-init[1].)
+endif::[]
 
+init.templateDir::
+       Specify the directory from which templates will be copied. {see-git-init}
 init.defaultBranch::
        Allows overriding the default branch name e.g. when initializing
        a new repository.
index aaaff0d46f0c6f04ca4e3182872b47d2bd2f07b6..0e9456957e37847c8d7fe342d0d8d4186e3095d7 100644 (file)
@@ -865,8 +865,9 @@ endif::git-format-patch[]
 
 --default-prefix::
        Use the default source and destination prefixes ("a/" and "b/").
-       This is usually the default already, but may be used to override
-       config such as `diff.noprefix`.
+       This overrides configuration variables such as `diff.noprefix`,
+       `diff.srcPrefix`, `diff.dstPrefix`, and `diff.mnemonicPrefix`
+       (see `git-config`(1)).
 
 --line-prefix=<prefix>::
        Prepend an additional prefix to every line of output.
index 54ebb4452e997f8c62c9dd2421948086249102bd..e22b217fba9e2c6c30ad1f7becea238d0f2a9916 100644 (file)
@@ -202,7 +202,7 @@ endif::git-pull[]
        destination of an explicit refspec; see `--prune`).
 
 ifndef::git-pull[]
---recurse-submodules[=yes|on-demand|no]::
+--recurse-submodules[=(yes|on-demand|no)]::
        This option controls if and under what conditions new commits of
        submodules should be fetched too. When recursing through submodules,
        `git fetch` always attempts to fetch "changed" submodules, that is, a
index ca626f7fc68611de05319d78ddcc16e94b3375e1..112658b3c3bb722d91715a32590a3789302feda7 100644 (file)
@@ -8,7 +8,8 @@ git-bugreport - Collect information for user to file a bug report
 SYNOPSIS
 --------
 [verse]
-'git bugreport' [(-o | --output-directory) <path>] [(-s | --suffix) <format>]
+'git bugreport' [(-o | --output-directory) <path>]
+               [(-s | --suffix) <format> | --no-suffix]
                [--diagnose[=<mode>]]
 
 DESCRIPTION
@@ -51,9 +52,12 @@ OPTIONS
 
 -s <format>::
 --suffix <format>::
+--no-suffix::
        Specify an alternate suffix for the bugreport name, to create a file
        named 'git-bugreport-<formatted-suffix>'. This should take the form of a
        strftime(3) format string; the current local time will be used.
+       `--no-suffix` disables the suffix and the file is just named
+       `git-bugreport` without any disambiguation measure.
 
 --no-diagnose::
 --diagnose[=<mode>]::
index 69331e3f05a13eed6ffb084e292f8452b241f13c..fd171654163c1fc4e34de3b24cdd4063a604dae1 100644 (file)
@@ -37,7 +37,7 @@ OPTIONS
 --force::
        If the Git configuration variable clean.requireForce is not set
        to false, 'git clean' will refuse to delete files or directories
-       unless given -f or -i.  Git will refuse to modify untracked
+       unless given -f.  Git will refuse to modify untracked
        nested git repositories (directories with a .git subdirectory)
        unless a second -f is given.
 
@@ -45,10 +45,14 @@ OPTIONS
 --interactive::
        Show what would be done and clean files interactively. See
        ``Interactive mode'' for details.
+       Configuration variable `clean.requireForce` is ignored, as
+       this mode gives its own safety protection by going interactive.
 
 -n::
 --dry-run::
        Don't actually remove anything, just show what would be done.
+       Configuration variable `clean.requireForce` is ignored, as
+       nothing will be deleted anyway.
 
 -q::
 --quiet::
index 0c07720c6f4e4a53a20a9db8c210143e881a2347..f90977a8519b4c9c057438afa042b4bd25855567 100644 (file)
@@ -102,9 +102,9 @@ its source repository, you can simply run `git repack -a` to copy all
 objects from the source repository into a pack in the cloned repository.
 
 --reference[-if-able] <repository>::
-       If the reference repository is on the local machine,
+       If the reference _<repository>_ is on the local machine,
        automatically setup `.git/objects/info/alternates` to
-       obtain objects from the reference repository.  Using
+       obtain objects from the reference _<repository>_.  Using
        an already existing repository as an alternate will
        require fewer objects to be copied from the repository
        being cloned, reducing network and local storage costs.
@@ -156,13 +156,13 @@ objects from the source repository into a pack in the cloned repository.
 
 --[no-]reject-shallow::
        Fail if the source repository is a shallow repository.
-       The 'clone.rejectShallow' configuration variable can be used to
+       The `clone.rejectShallow` configuration variable can be used to
        specify the default.
 
 --bare::
        Make a 'bare' Git repository.  That is, instead of
-       creating `<directory>` and placing the administrative
-       files in `<directory>/.git`, make the `<directory>`
+       creating _<directory>_ and placing the administrative
+       files in `<directory>/.git`, make the _<directory>_
        itself the `$GIT_DIR`. This obviously implies the `--no-checkout`
        because there is nowhere to check out the working tree.
        Also the branch heads at the remote are copied directly
@@ -180,11 +180,11 @@ objects from the source repository into a pack in the cloned repository.
 --filter=<filter-spec>::
        Use the partial clone feature and request that the server sends
        a subset of reachable objects according to a given object filter.
-       When using `--filter`, the supplied `<filter-spec>` is used for
+       When using `--filter`, the supplied _<filter-spec>_ is used for
        the partial clone filter. For example, `--filter=blob:none` will
        filter out all blobs (file contents) until needed by Git. Also,
        `--filter=blob:limit=<size>` will filter out all blobs of size
-       at least `<size>`. For more details on filter specifications, see
+       at least _<size>_. For more details on filter specifications, see
        the `--filter` option in linkgit:git-rev-list[1].
 
 --also-filter-submodules::
@@ -203,13 +203,13 @@ objects from the source repository into a pack in the cloned repository.
 -o <name>::
 --origin <name>::
        Instead of using the remote name `origin` to keep track of the upstream
-       repository, use `<name>`.  Overrides `clone.defaultRemoteName` from the
+       repository, use _<name>_.  Overrides `clone.defaultRemoteName` from the
        config.
 
 -b <name>::
 --branch <name>::
        Instead of pointing the newly created HEAD to the branch pointed
-       to by the cloned repository's HEAD, point to `<name>` branch
+       to by the cloned repository's HEAD, point to _<name>_ branch
        instead. In a non-bare repository, this is the branch that will
        be checked out.
        `--branch` can also take tags and detaches the HEAD at that commit
@@ -230,7 +230,7 @@ objects from the source repository into a pack in the cloned repository.
        Set a configuration variable in the newly-created repository;
        this takes effect immediately after the repository is
        initialized, but before the remote history is fetched or any
-       files checked out.  The key is in the same format as expected by
+       files checked out.  The _<key>_ is in the same format as expected by
        linkgit:git-config[1] (e.g., `core.eol=true`). If multiple
        values are given for the same key, each value will be written to
        the config file. This makes it safe, for example, to add
@@ -263,7 +263,7 @@ corresponding `--mirror` and `--no-tags` options instead.
        branch remote's `HEAD` points at.
        Further fetches into the resulting repository will only update the
        remote-tracking branch for the branch this option was used for the
-       initial cloning.  If the HEAD at the remote did not point at any
+       initial cloning.  If the `HEAD` at the remote did not point at any
        branch when `--single-branch` clone was made, no remote-tracking
        branch is created.
 
@@ -281,7 +281,7 @@ branch of some repository for search indexing.
 
 --recurse-submodules[=<pathspec>]::
        After the clone is created, initialize and clone submodules
-       within based on the provided pathspec.  If no pathspec is
+       within based on the provided _<pathspec>_.  If no _=<pathspec>_ is
        provided, all submodules are initialized and cloned.
        This option can be given multiple times for pathspecs consisting
        of multiple entries.  The resulting clone has `submodule.active` set to
@@ -323,20 +323,20 @@ include::ref-storage-format.txt[]
        Defaults to the `submodule.fetchJobs` option.
 
 <repository>::
-       The (possibly remote) repository to clone from.  See the
+       The (possibly remote) _<repository>_ to clone from.  See the
        <<URLS,GIT URLS>> section below for more information on specifying
        repositories.
 
 <directory>::
        The name of a new directory to clone into.  The "humanish"
-       part of the source repository is used if no directory is
+       part of the source repository is used if no _<directory>_ is
        explicitly given (`repo` for `/path/to/repo.git` and `foo`
        for `host.xz:foo/.git`).  Cloning into an existing directory
        is only allowed if the directory is empty.
 
 --bundle-uri=<uri>::
        Before fetching from the remote, fetch a bundle from the given
-       `<uri>` and unbundle the data into the local repository. The refs
+       _<uri>_ and unbundle the data into the local repository. The refs
        in the bundle will be stored under the hidden `refs/bundle/*`
        namespace. This option is incompatible with `--depth`,
        `--shallow-since`, and `--shallow-exclude`.
index dff39093b5ef322ce115afc8f4bee0c728b6173a..a6e82b871b52f7a6c8eb282c8c126f00f1917f87 100644 (file)
@@ -275,7 +275,8 @@ Valid `<type>`'s include:
 -e::
 --edit::
        Opens an editor to modify the specified config file; either
-       `--system`, `--global`, or repository (default).
+       `--system`, `--global`, `--local` (default), `--worktree`, or
+       `--file <config-file>`.
 
 --[no-]includes::
        Respect `include.*` directives in config files when looking up
@@ -285,7 +286,7 @@ Valid `<type>`'s include:
 
 --default <value>::
   When using `--get`, and the requested variable is not found, behave as if
-  <value> were the value assigned to the that variable.
+  <value> were the value assigned to that variable.
 
 CONFIGURATION
 -------------
index e8dc645bb59a8664865c9eed00698ee4374ea4a3..2f864e11ed9719a2443ece695534636fcb6e2631 100644 (file)
@@ -33,10 +33,10 @@ If the object storage directory is specified via the
 are created underneath; otherwise, the default `$GIT_DIR/objects`
 directory is used.
 
-Running 'git init' in an existing repository is safe. It will not
+Running `git init` in an existing repository is safe. It will not
 overwrite things that are already there. The primary reason for
-rerunning 'git init' is to pick up newly added templates (or to move
-the repository to another place if --separate-git-dir is given).
+rerunning `git init` is to pick up newly added templates (or to move
+the repository to another place if `--separate-git-dir` is given).
 
 OPTIONS
 -------
@@ -53,14 +53,14 @@ current working directory.
 
 --object-format=<format>::
 
-Specify the given object format (hash algorithm) for the repository.  The valid
-values are 'sha1' and (if enabled) 'sha256'.  'sha1' is the default.
+Specify the given object _<format>_ (hash algorithm) for the repository.  The valid
+values are `sha1` and (if enabled) `sha256`.  `sha1` is the default.
 +
 include::object-format-disclaimer.txt[]
 
 --ref-format=<format>::
 
-Specify the given ref storage format for the repository. The valid values are:
+Specify the given ref storage _<format>_ for the repository. The valid values are:
 +
 include::ref-storage-format.txt[]
 
@@ -81,7 +81,7 @@ If this is a reinitialization, the repository will be moved to the specified pat
 -b <branch-name>::
 --initial-branch=<branch-name>::
 
-Use the specified name for the initial branch in the newly created
+Use _<branch-name>_ for the initial branch in the newly created
 repository.  If not specified, fall back to the default name (currently
 `master`, but this is subject to change in the future; the name can be
 customized via the `init.defaultBranch` configuration variable).
@@ -90,40 +90,44 @@ customized via the `init.defaultBranch` configuration variable).
 
 Specify that the Git repository is to be shared amongst several users.  This
 allows users belonging to the same group to push into that
-repository.  When specified, the config variable "core.sharedRepository" is
+repository.  When specified, the config variable `core.sharedRepository` is
 set so that files and directories under `$GIT_DIR` are created with the
 requested permissions.  When not specified, Git will use permissions reported
-by umask(2).
+by `umask(2)`.
 +
-The option can have the following values, defaulting to 'group' if no value
+The option can have the following values, defaulting to `group` if no value
 is given:
 +
 --
-'umask' (or 'false')::
+umask::
+false::
 
 Use permissions reported by umask(2). The default, when `--shared` is not
 specified.
 
-'group' (or 'true')::
+group::
+true::
 
 Make the repository group-writable, (and g+sx, since the git group may not be
 the primary group of all users). This is used to loosen the permissions of an
 otherwise safe umask(2) value. Note that the umask still applies to the other
-permission bits (e.g. if umask is '0022', using 'group' will not remove read
-privileges from other (non-group) users). See '0xxx' for how to exactly specify
+permission bits (e.g. if umask is `0022`, using `group` will not remove read
+privileges from other (non-group) users). See `0xxx` for how to exactly specify
 the repository permissions.
 
-'all' (or 'world' or 'everybody')::
+all::
+world::
+everybody::
 
-Same as 'group', but make the repository readable by all users.
+Same as `group`, but make the repository readable by all users.
 
-'<perm>'::
+<perm>::
 
-'<perm>' is a 3-digit octal number prefixed with `0` and each file
-will have mode '<perm>'. '<perm>' will override users' umask(2)
-value (and not only loosen permissions as 'group' and 'all'
-do). '0640' will create a repository which is group-readable, but
-not group-writable or accessible to others. '0660' will create a repo
+_<perm>_ is a 3-digit octal number prefixed with `0` and each file
+will have mode _<perm>_. _<perm>_ will override users'`umask(2)`
+value (and not only loosen permissions as `group` and `all`
+do). `0640` will create a repository which is group-readable, but
+not group-writable or accessible to others. `0660` will create a repo
 that is readable and writable to the current user and group, but
 inaccessible to others (directories and executable files get their
 `x` bit from the `r` bit for corresponding classes of users).
@@ -133,7 +137,7 @@ By default, the configuration flag `receive.denyNonFastForwards` is enabled
 in shared repositories, so that you cannot force a non fast-forwarding push
 into it.
 
-If you provide a 'directory', the command is run inside it. If this directory
+If you provide a _<directory>_, the command is run inside it. If this directory
 does not exist, it will be created.
 
 TEMPLATE DIRECTORY
@@ -172,7 +176,7 @@ $ git add .     <2>
 $ git commit    <3>
 ----------------
 +
-<1> Create a /path/to/my/codebase/.git directory.
+<1> Create a `/path/to/my/codebase/.git` directory.
 <2> Add all existing files to the index.
 <3> Record the pristine state as the first commit in the history.
 
@@ -181,6 +185,8 @@ CONFIGURATION
 
 include::includes/cmd-config-section-all.txt[]
 
+:git-init:
+
 include::config/init.txt[]
 
 GIT
index 418265f044d65000e9ae22235b9801644f9c2170..d9dfb75fef525f4a8379e0c52a78e2642e843858 100644 (file)
@@ -9,7 +9,7 @@ SYNOPSIS
 --------
 [verse]
 'git interpret-trailers' [--in-place] [--trim-empty]
-                       [(--trailer (<key>|<keyAlias>)[(=|:)<value>])...]
+                       [(--trailer (<key>|<key-alias>)[(=|:)<value>])...]
                        [--parse] [<file>...]
 
 DESCRIPTION
@@ -67,9 +67,9 @@ key: value
 This means that the trimmed <key> and <value> will be separated by
 `': '` (one colon followed by one space).
 
-For convenience, a <keyAlias> can be configured to make using `--trailer`
+For convenience, a <key-alias> can be configured to make using `--trailer`
 shorter to type on the command line. This can be configured using the
-'trailer.<keyAlias>.key' configuration variable. The <keyAlias> must be a prefix
+'trailer.<key-alias>.key' configuration variable. The <keyAlias> must be a prefix
 of the full <key> string, although case sensitivity does not matter. For
 example, if you have
 
index 0e14f8b5b25924c98c4bda92729787f2361ada9c..b2ae496e488c1ebf3aaf79609987ab0bdbc5f34a 100644 (file)
@@ -87,7 +87,7 @@ OPTIONS
 --verbose::
        Pass --verbose to git-fetch and git-merge.
 
---[no-]recurse-submodules[=yes|on-demand|no]::
+--[no-]recurse-submodules[=(yes|on-demand|no)]::
        This option controls if new commits of populated submodules should
        be fetched, and if the working trees of active submodules should be
        updated, too (see linkgit:git-fetch[1], linkgit:git-config[1] and
@@ -105,7 +105,7 @@ Options related to merging
 include::merge-options.txt[]
 
 -r::
---rebase[=false|true|merges|interactive]::
+--rebase[=(false|true|merges|interactive)]::
        When true, rebase the current branch on top of the upstream
        branch after fetching. If there is a remote-tracking branch
        corresponding to the upstream branch and the upstream branch
index e7e725044db418845cd8ee53aed60fba2374634f..03d5e9936a0113896b456afc16e365e540f4c78b 100644 (file)
@@ -12,7 +12,7 @@ SYNOPSIS
        [--onto <newbase> | --keep-base] [<upstream> [<branch>]]
 'git rebase' [-i | --interactive] [<options>] [--exec <cmd>] [--onto <newbase>]
        --root [<branch>]
-'git rebase' (--continue | --skip | --abort | --quit | --edit-todo | --show-current-patch)
+'git rebase' (--continue|--skip|--abort|--quit|--edit-todo|--show-current-patch)
 
 DESCRIPTION
 -----------
index 5d83dd36da11f3a7e5294bc5825e131c81d17633..f9d5a35fa00d7b779e43e884ca228e5a8a23425e 100644 (file)
@@ -159,6 +159,18 @@ for another option.
        unfortunately named tag "master"), and shows them as full
        refnames (e.g. "refs/heads/master").
 
+--output-object-format=(sha1|sha256|storage)::
+
+       Allow oids to be input from any object format that the current
+       repository supports.
+
+       Specifying "sha1" translates if necessary and returns a sha1 oid.
+
+       Specifying "sha256" translates if necessary and returns a sha256 oid.
+
+       Specifying "storage" translates if necessary and returns an oid in
+       encoded in the storage hash algorithm.
+
 Options for Objects
 ~~~~~~~~~~~~~~~~~~~
 
index 8264f8738093cf64b7e32201826fb170dd512c73..c5d664f4519ba61824c212a8fb2510df17c8bf03 100644 (file)
@@ -9,7 +9,7 @@ git-send-email - Send a collection of patches as emails
 SYNOPSIS
 --------
 [verse]
-'git send-email' [<options>] <file|directory>...
+'git send-email' [<options>] (<file>|<directory>)...
 'git send-email' [<options>] <format-patch-options>
 'git send-email' --dump-aliases
 
@@ -278,7 +278,7 @@ must be used for each option.
        if a username is not specified (with `--smtp-user` or `sendemail.smtpUser`),
        then authentication is not attempted.
 
---smtp-debug=0|1::
+--smtp-debug=(0|1)::
        Enable (1) or disable (0) debug output. If enabled, SMTP
        commands and replies will be printed. Useful to debug TLS
        connection and authentication problems.
@@ -301,7 +301,9 @@ must be used for each option.
 Automating
 ~~~~~~~~~~
 
---no-[to|cc|bcc]::
+--no-to::
+--no-cc::
+--no-bcc::
        Clears any list of "To:", "Cc:", "Bcc:" addresses previously
        set via config.
 
index 1561319eb274a3f65693306b35b9d8c7d913993b..9a376886a5867a63d173d85162c57027f336f915 100644 (file)
@@ -474,7 +474,7 @@ again, because your configuration may already be caching `git status`
 results, so it could be faster on subsequent runs.
 
 * The `--untracked-files=no` flag or the
-       `status.showUntrackedfiles=false` config (see above for both):
+       `status.showUntrackedFiles=no` config (see above for both):
        indicate that `git status` should not report untracked
        files. This is the fastest option. `git status` will not list
        the untracked files, so you need to be careful to remember if
index e6b766d5c3ab54e6fc97d3cf31f9093a6eac5a41..7a1b112a3e70203a8294952333c05845bfcb4da6 100644 (file)
@@ -960,7 +960,7 @@ will never be returned from the commit-graph at the cost of performance.
 `GIT_PROTOCOL`::
        For internal use only.  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
+       '<key>[=<value>]'.  Presence of unknown keys and values must be
        ignored.
 +
 Note that servers may need to be configured to allow this variable to
index ed8da428c98bc96cafdbbcc8543ed0c8479a13a3..07c8439a6f784bc818108fad6fdd82891509dd84 100644 (file)
@@ -526,7 +526,7 @@ set by Git if the remote helper has the 'option' capability.
 'option pushcert' {'true'|'false'}::
        GPG sign pushes.
 
-'option push-option <string>::
+'option push-option' <string>::
        Transmit <string> as a push option. As the push option
        must not contain LF or NUL characters, the string is not encoded.
 
index 151ee84cebcef3ca549f3150e69e91b29cc2f372..4e727deedd21235403c4a3b02e8c826780d39d8b 100644 (file)
@@ -100,7 +100,7 @@ info "The user is: '$username'"
 
 if test -f "$allowed_users_file"
 then
-  rc=$(cat $allowed_users_file | grep -v '^#' | grep -v '^$' |
+  rc=$(grep -Ev '^(#|$)' $allowed_users_file |
     while read heads user_patterns
     do
       # does this rule apply to us?
@@ -138,7 +138,7 @@ info "'$groups'"
 
 if test -f "$allowed_groups_file"
 then
-  rc=$(cat $allowed_groups_file | grep -v '^#' | grep -v '^$' |
+  rc=$(grep -Ev '^(#|$)' $allowed_groups_file |
     while read heads group_patterns
     do
       # does this rule apply to us?
index d38b4ab5666c35e0ae83680abca0e067c7b14792..8ee940b6a452daba6202f1b76747c7eeea8a6f48 100644 (file)
@@ -316,9 +316,8 @@ multiple times, the last occurrence wins.
    `Reviewed-by`.
 ** 'only[=<bool>]': select whether non-trailer lines from the trailer
    block should be included.
-** 'separator=<sep>': specify a separator inserted between trailer
-   lines. When this option is not given each trailer line is
-   terminated with a line feed character. The string <sep> may contain
+** 'separator=<sep>': specify the separator inserted between trailer
+   lines. Defaults to a line feed character. The string <sep> may contain
    the literal formatting codes described above. To use comma as
    separator one must use `%x2C` as it would otherwise be parsed as
    next option. E.g., `%(trailers:key=Ticket,separator=%x2C )`
@@ -329,10 +328,9 @@ multiple times, the last occurrence wins.
    `%(trailers:only,unfold=true)` unfolds and shows all trailer lines.
 ** 'keyonly[=<bool>]': only show the key part of the trailer.
 ** 'valueonly[=<bool>]': only show the value part of the trailer.
-** 'key_value_separator=<sep>': specify a separator inserted between
-   trailer lines. When this option is not given each trailer key-value
-   pair is separated by ": ". Otherwise it shares the same semantics
-   as 'separator=<sep>' above.
+** 'key_value_separator=<sep>': specify the separator inserted between
+   the key and value of each trailer. Defaults to ": ". Otherwise it
+   shares the same semantics as 'separator=<sep>' above.
 
 NOTE: Some placeholders may depend on other options given to the
 revision traversal engine. For example, the `%g*` reflog options will
index 408d9314d0887969a8cb485b59265a3c4b570ced..00ccf68744103d4dc6ffdce5c582c726c2d9ea9d 100644 (file)
@@ -316,12 +316,12 @@ list.
 With `--pretty` format other than `oneline` and `reference` (for obvious reasons),
 this causes the output to have two extra lines of information
 taken from the reflog.  The reflog designator in the output may be shown
-as `ref@{Nth}` (where `Nth` is the reverse-chronological index in the
-reflog) or as `ref@{timestamp}` (with the timestamp for that entry),
+as `ref@{<Nth>}` (where _<Nth>_ is the reverse-chronological index in the
+reflog) or as `ref@{<timestamp>}` (with the _<timestamp>_ for that entry),
 depending on a few rules:
 +
 --
-1. If the starting point is specified as `ref@{Nth}`, show the index
+1. If the starting point is specified as `ref@{<Nth>}`, show the index
    format.
 +
 2. If the starting point was specified as `ref@{now}`, show the
index ce671f812d4ce32b156361333155f459c9521647..0b9e0c4302d850a7a38044d03bd1146764bc83ca 100644 (file)
@@ -44,26 +44,26 @@ syntaxes may be used:
 
 ifndef::git-clone[]
 These two syntaxes are mostly equivalent, except when cloning, when
-the former implies --local option. See linkgit:git-clone[1] for
+the former implies `--local` option. See linkgit:git-clone[1] for
 details.
 endif::git-clone[]
 
 ifdef::git-clone[]
 These two syntaxes are mostly equivalent, except the former implies
---local option.
+`--local` option.
 endif::git-clone[]
 
-'git clone', 'git fetch' and 'git pull', but not 'git push', will also
+`git clone`, `git fetch` and `git pull`, but not `git push`, will also
 accept a suitable bundle file. See linkgit:git-bundle[1].
 
 When Git doesn't know how to handle a certain transport protocol, it
-attempts to use the 'remote-<transport>' remote helper, if one
+attempts to use the `remote-<transport>` remote helper, if one
 exists. To explicitly request a remote helper, the following syntax
 may be used:
 
-- <transport>::<address>
+- _<transport>_::_<address>_
 
-where <address> may be a path, a server and path, or an arbitrary
+where _<address>_ may be a path, a server and path, or an arbitrary
 URL-like string recognized by the specific remote helper being
 invoked. See linkgit:gitremote-helpers[7] for details.
 
index 6433903491054874c826880ea46e0fbc1fdb02bd..90a4189358300b0b594cdb18a90ce3b25d03de44 100644 (file)
@@ -4093,7 +4093,38 @@ that not only specifies their type, but also provides size information
 about the data in the object.  It's worth noting that the SHA-1 hash
 that is used to name the object is the hash of the original data
 plus this header, so `sha1sum` 'file' does not match the object name
-for 'file'.
+for 'file' (the earliest versions of Git hashed slightly differently
+but the conclusion is still the same).
+
+The following is a short example that demonstrates how these hashes
+can be generated manually:
+
+Let's assume a small text file with some simple content:
+
+-------------------------------------------------
+$ echo "Hello world" >hello.txt
+-------------------------------------------------
+
+We can now manually generate the hash Git would use for this file:
+
+- The object we want the hash for is of type "blob" and its size is
+  12 bytes.
+
+- Prepend the object header to the file content and feed this to
+  `sha1sum`:
+
+-------------------------------------------------
+$ { printf "blob 12\0"; cat hello.txt; } | sha1sum
+802992c4220de19a90767f3000a79a31b98d0df7  -
+-------------------------------------------------
+
+This manually constructed hash can be verified using `git hash-object`
+which of course hides the addition of the header:
+
+-------------------------------------------------
+$ git hash-object hello.txt
+802992c4220de19a90767f3000a79a31b98d0df7
+-------------------------------------------------
 
 As a result, the general consistency of an object can always be tested
 independently of the contents or the type of the object: all objects can
@@ -4123,7 +4154,8 @@ $ git switch --detach e83c5163
 ----------------------------------------------------
 
 The initial revision lays the foundation for almost everything Git has
-today, but is small enough to read in one sitting.
+today (even though details may differ in a few places), but is small
+enough to read in one sitting.
 
 Note that terminology has changed since that revision.  For example, the
 README in that revision uses the word "changeset" to describe what we
index 4e255c81f22386389c7460d8f5e59426673b5a5a..e9556789087f9fedde4ffeb51d3c52864fd45441 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -797,6 +797,7 @@ TEST_BUILTINS_OBJS += test-config.o
 TEST_BUILTINS_OBJS += test-crontab.o
 TEST_BUILTINS_OBJS += test-csprng.o
 TEST_BUILTINS_OBJS += test-date.o
+TEST_BUILTINS_OBJS += test-delete-gpgsig.o
 TEST_BUILTINS_OBJS += test-delta.o
 TEST_BUILTINS_OBJS += test-dir-iterator.o
 TEST_BUILTINS_OBJS += test-drop-caches.o
@@ -1060,6 +1061,7 @@ LIB_OBJS += list-objects-filter.o
 LIB_OBJS += list-objects.o
 LIB_OBJS += lockfile.o
 LIB_OBJS += log-tree.o
+LIB_OBJS += loose.o
 LIB_OBJS += ls-refs.o
 LIB_OBJS += mailinfo.o
 LIB_OBJS += mailmap.o
@@ -1080,6 +1082,7 @@ LIB_OBJS += notes-cache.o
 LIB_OBJS += notes-merge.o
 LIB_OBJS += notes-utils.o
 LIB_OBJS += notes.o
+LIB_OBJS += object-file-convert.o
 LIB_OBJS += object-file.o
 LIB_OBJS += object-name.o
 LIB_OBJS += object.o
index 4ba64ee5b2d315eeeaa6143f5c8c2053d066a830..b0e05506871b9c402b75ad7ba52c47776797611d 100644 (file)
--- a/advice.c
+++ b/advice.c
@@ -68,6 +68,7 @@ static struct {
        [ADVICE_PUSH_UNQUALIFIED_REF_NAME]              = { "pushUnqualifiedRefName" },
        [ADVICE_PUSH_UPDATE_REJECTED]                   = { "pushUpdateRejected" },
        [ADVICE_PUSH_UPDATE_REJECTED_ALIAS]             = { "pushNonFastForward" }, /* backwards compatibility */
+       [ADVICE_REF_SYNTAX]                             = { "refSyntax" },
        [ADVICE_RESET_NO_REFRESH_WARNING]               = { "resetNoRefresh" },
        [ADVICE_RESOLVE_CONFLICT]                       = { "resolveConflict" },
        [ADVICE_RM_HINTS]                               = { "rmHints" },
index 7d0a821f5cb8a1818154852bbb76d7e15d4fe97b..bf630ee3ac3eae23b05b9554f4cc20ce2ea7a280 100644 (file)
--- a/advice.h
+++ b/advice.h
@@ -36,6 +36,7 @@ enum advice_type {
        ADVICE_PUSH_UNQUALIFIED_REF_NAME,
        ADVICE_PUSH_UPDATE_REJECTED,
        ADVICE_PUSH_UPDATE_REJECTED_ALIAS,
+       ADVICE_REF_SYNTAX,
        ADVICE_RESET_NO_REFRESH_WARNING,
        ADVICE_RESOLVE_CONFLICT,
        ADVICE_RM_HINTS,
index a6730bebfa07d65d003b5594c63081e6ef7d3e88..5287fcdd8e0460063f40e4e3f7a812d248e41afd 100644 (file)
--- a/archive.c
+++ b/archive.c
@@ -339,7 +339,8 @@ int write_archive_entries(struct archiver_args *args,
                opts.src_index = args->repo->index;
                opts.dst_index = args->repo->index;
                opts.fn = oneway_merge;
-               init_tree_desc(&t, args->tree->buffer, args->tree->size);
+               init_tree_desc(&t, &args->tree->object.oid,
+                              args->tree->buffer, args->tree->size);
                if (unpack_trees(1, &t, &opts))
                        return -1;
                git_attr_set_direction(GIT_ATTR_INDEX);
index 6719a181bd1f03af21b92d8be71a93142ef700e7..621019fcf4bde0a0568dbae2a1055e12cf8df030 100644 (file)
--- a/branch.c
+++ b/branch.c
@@ -370,8 +370,12 @@ int read_branch_desc(struct strbuf *buf, const char *branch_name)
  */
 int validate_branchname(const char *name, struct strbuf *ref)
 {
-       if (strbuf_check_branch_ref(ref, name))
-               die(_("'%s' is not a valid branch name"), name);
+       if (strbuf_check_branch_ref(ref, name)) {
+               int code = die_message(_("'%s' is not a valid branch name"), name);
+               advise_if_enabled(ADVICE_REF_SYNTAX,
+                                 _("See `man git check-ref-format`"));
+               exit(code);
+       }
 
        return ref_exists(ref->buf);
 }
index d1990d7edcbe37467c59a420a9b03a5894e6da93..e8fb27a8ef5d0505e14f53926af789282895d523 100644 (file)
@@ -1994,8 +1994,8 @@ static int fast_forward_to(struct tree *head, struct tree *remote, int reset)
        opts.reset = reset ? UNPACK_RESET_PROTECT_UNTRACKED : 0;
        opts.preserve_ignored = 0; /* FIXME: !overwrite_ignore */
        opts.fn = twoway_merge;
-       init_tree_desc(&t[0], head->buffer, head->size);
-       init_tree_desc(&t[1], remote->buffer, remote->size);
+       init_tree_desc(&t[0], &head->object.oid, head->buffer, head->size);
+       init_tree_desc(&t[1], &remote->object.oid, remote->buffer, remote->size);
 
        if (unpack_trees(2, t, &opts)) {
                rollback_lock_file(&lock_file);
@@ -2029,7 +2029,7 @@ static int merge_tree(struct tree *tree)
        opts.dst_index = &the_index;
        opts.merge = 1;
        opts.fn = oneway_merge;
-       init_tree_desc(&t[0], tree->buffer, tree->size);
+       init_tree_desc(&t[0], &tree->object.oid, tree->buffer, tree->size);
 
        if (unpack_trees(1, t, &opts)) {
                rollback_lock_file(&lock_file);
index b3cbb7fd440486300b14e76400ac58f927532995..8c2305ad2c85586c960c986582f1d9e874a26a24 100644 (file)
@@ -582,8 +582,12 @@ static void copy_or_rename_branch(const char *oldname, const char *newname, int
                 */
                if (ref_exists(oldref.buf))
                        recovery = 1;
-               else
-                       die(_("invalid branch name: '%s'"), oldname);
+               else {
+                       int code = die_message(_("invalid branch name: '%s'"), oldname);
+                       advise_if_enabled(ADVICE_REF_SYNTAX,
+                                         _("See `man git check-ref-format`"));
+                       exit(code);
+               }
        }
 
        for (int i = 0; worktrees[i]; i++) {
index 3106e56a130c58994fced2afd8dbd337134861ad..25f860a0d973caca44593cb28919e47afb13089d 100644 (file)
@@ -64,7 +64,8 @@ static void get_populated_hooks(struct strbuf *hook_info, int nongit)
 }
 
 static const char * const bugreport_usage[] = {
-       N_("git bugreport [(-o | --output-directory) <path>] [(-s | --suffix) <format>]\n"
+       N_("git bugreport [(-o | --output-directory) <path>]\n"
+          "              [(-s | --suffix) <format> | --no-suffix]\n"
           "              [--diagnose[=<mode>]]"),
        NULL
 };
@@ -138,8 +139,11 @@ int cmd_bugreport(int argc, const char **argv, const char *prefix)
        strbuf_complete(&report_path, '/');
        output_path_len = report_path.len;
 
-       strbuf_addstr(&report_path, "git-bugreport-");
-       strbuf_addftime(&report_path, option_suffix, localtime_r(&now, &tm), 0, 0);
+       strbuf_addstr(&report_path, "git-bugreport");
+       if (option_suffix) {
+               strbuf_addch(&report_path, '-');
+               strbuf_addftime(&report_path, option_suffix, localtime_r(&now, &tm), 0, 0);
+       }
        strbuf_addstr(&report_path, ".txt");
 
        switch (safe_create_leading_directories(report_path.buf)) {
index bbf851138ec40875367aba94bc5b5de29b8dbe77..3f5ce7d34c3c05b7a49b7ce08b8a9433ece57254 100644 (file)
@@ -106,7 +106,10 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
        struct object_info oi = OBJECT_INFO_INIT;
        struct strbuf sb = STRBUF_INIT;
        unsigned flags = OBJECT_INFO_LOOKUP_REPLACE;
-       unsigned get_oid_flags = GET_OID_RECORD_PATH | GET_OID_ONLY_TO_DIE;
+       unsigned get_oid_flags =
+               GET_OID_RECORD_PATH |
+               GET_OID_ONLY_TO_DIE |
+               GET_OID_HASH_ANY;
        const char *path = force_path;
        const int opt_cw = (opt == 'c' || opt == 'w');
        if (!path && opt_cw)
@@ -226,7 +229,8 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
                                        die(_("unable to read %s"), oid_to_hex(&oid));
 
                                if (!skip_prefix(buffer, "object ", &target) ||
-                                   get_oid_hex(target, &blob_oid))
+                                   get_oid_hex_algop(target, &blob_oid,
+                                                     &hash_algos[oid.algo]))
                                        die("%s not a valid tag", oid_to_hex(&oid));
                                free(buffer);
                        } else
@@ -517,7 +521,9 @@ static void batch_one_object(const char *obj_name,
                             struct expand_data *data)
 {
        struct object_context ctx;
-       int flags = opt->follow_symlinks ? GET_OID_FOLLOW_SYMLINKS : 0;
+       int flags =
+               GET_OID_HASH_ANY |
+               (opt->follow_symlinks ? GET_OID_FOLLOW_SYMLINKS : 0);
        enum get_oid_result result;
 
        result = get_oid_with_context(the_repository, obj_name,
index 15293a30134094f699d15035232bbc644277910c..2e8b0d18f445b1307e264634f69c8ce0a3a5c68a 100644 (file)
@@ -706,7 +706,7 @@ static int reset_tree(struct tree *tree, const struct checkout_opts *o,
                               NULL);
        if (parse_tree(tree) < 0)
                return 128;
-       init_tree_desc(&tree_desc, tree->buffer, tree->size);
+       init_tree_desc(&tree_desc, &tree->object.oid, tree->buffer, tree->size);
        switch (unpack_trees(1, &tree_desc, &opts)) {
        case -2:
                *writeout_error = 1;
@@ -826,11 +826,13 @@ static int merge_working_tree(const struct checkout_opts *opts,
                        die(_("unable to parse commit %s"),
                                oid_to_hex(old_commit_oid));
 
-               init_tree_desc(&trees[0], tree->buffer, tree->size);
+               init_tree_desc(&trees[0], &tree->object.oid,
+                              tree->buffer, tree->size);
                if (parse_tree(new_tree) < 0)
                        exit(128);
                tree = new_tree;
-               init_tree_desc(&trees[1], tree->buffer, tree->size);
+               init_tree_desc(&trees[1], &tree->object.oid,
+                              tree->buffer, tree->size);
 
                ret = unpack_trees(2, trees, &topts);
                clear_unpack_trees_porcelain(&topts);
@@ -1702,10 +1704,11 @@ static char cb_option = 'b';
 
 static int checkout_main(int argc, const char **argv, const char *prefix,
                         struct checkout_opts *opts, struct option *options,
-                        const char * const usagestr[],
-                        struct branch_info *new_branch_info)
+                        const char * const usagestr[])
 {
        int parseopt_flags = 0;
+       struct branch_info new_branch_info = { 0 };
+       int ret;
 
        opts->overwrite_ignore = 1;
        opts->prefix = prefix;
@@ -1821,7 +1824,7 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
                        opts->track == BRANCH_TRACK_UNSPECIFIED &&
                        !opts->new_branch;
                int n = parse_branchname_arg(argc, argv, dwim_ok,
-                                            new_branch_info, opts, &rev);
+                                            &new_branch_info, opts, &rev);
                argv += n;
                argc -= n;
        } else if (!opts->accept_ref && opts->from_treeish) {
@@ -1830,7 +1833,7 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
                if (repo_get_oid_mb(the_repository, opts->from_treeish, &rev))
                        die(_("could not resolve %s"), opts->from_treeish);
 
-               setup_new_branch_info_and_source_tree(new_branch_info,
+               setup_new_branch_info_and_source_tree(&new_branch_info,
                                                      opts, &rev,
                                                      opts->from_treeish);
 
@@ -1850,7 +1853,7 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
                 * Try to give more helpful suggestion.
                 * new_branch && argc > 1 will be caught later.
                 */
-               if (opts->new_branch && argc == 1 && !new_branch_info->commit)
+               if (opts->new_branch && argc == 1 && !new_branch_info.commit)
                        die(_("'%s' is not a commit and a branch '%s' cannot be created from it"),
                                argv[0], opts->new_branch);
 
@@ -1900,9 +1903,16 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
        }
 
        if (opts->patch_mode || opts->pathspec.nr)
-               return checkout_paths(opts, new_branch_info);
+               ret = checkout_paths(opts, &new_branch_info);
        else
-               return checkout_branch(opts, new_branch_info);
+               ret = checkout_branch(opts, &new_branch_info);
+
+       branch_info_release(&new_branch_info);
+       clear_pathspec(&opts->pathspec);
+       free(opts->pathspec_from_file);
+       free(options);
+
+       return ret;
 }
 
 int cmd_checkout(int argc, const char **argv, const char *prefix)
@@ -1920,8 +1930,6 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
                OPT_BOOL(0, "overlay", &opts.overlay_mode, N_("use overlay mode (default)")),
                OPT_END()
        };
-       int ret;
-       struct branch_info new_branch_info = { 0 };
 
        memset(&opts, 0, sizeof(opts));
        opts.dwim_new_local_branch = 1;
@@ -1951,13 +1959,8 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
        options = add_common_switch_branch_options(&opts, options);
        options = add_checkout_path_options(&opts, options);
 
-       ret = checkout_main(argc, argv, prefix, &opts,
-                           options, checkout_usage, &new_branch_info);
-       branch_info_release(&new_branch_info);
-       clear_pathspec(&opts.pathspec);
-       free(opts.pathspec_from_file);
-       FREE_AND_NULL(options);
-       return ret;
+       return checkout_main(argc, argv, prefix, &opts, options,
+                            checkout_usage);
 }
 
 int cmd_switch(int argc, const char **argv, const char *prefix)
@@ -1975,8 +1978,6 @@ int cmd_switch(int argc, const char **argv, const char *prefix)
                         N_("throw away local modifications")),
                OPT_END()
        };
-       int ret;
-       struct branch_info new_branch_info = { 0 };
 
        memset(&opts, 0, sizeof(opts));
        opts.dwim_new_local_branch = 1;
@@ -1995,11 +1996,8 @@ int cmd_switch(int argc, const char **argv, const char *prefix)
 
        cb_option = 'c';
 
-       ret = checkout_main(argc, argv, prefix, &opts,
-                           options, switch_branch_usage, &new_branch_info);
-       branch_info_release(&new_branch_info);
-       FREE_AND_NULL(options);
-       return ret;
+       return checkout_main(argc, argv, prefix, &opts, options,
+                            switch_branch_usage);
 }
 
 int cmd_restore(int argc, const char **argv, const char *prefix)
@@ -2018,8 +2016,6 @@ int cmd_restore(int argc, const char **argv, const char *prefix)
                OPT_BOOL(0, "overlay", &opts.overlay_mode, N_("use overlay mode")),
                OPT_END()
        };
-       int ret;
-       struct branch_info new_branch_info = { 0 };
 
        memset(&opts, 0, sizeof(opts));
        opts.accept_ref = 0;
@@ -2034,9 +2030,6 @@ int cmd_restore(int argc, const char **argv, const char *prefix)
        options = add_common_options(&opts, options);
        options = add_checkout_path_options(&opts, options);
 
-       ret = checkout_main(argc, argv, prefix, &opts,
-                           options, restore_usage, &new_branch_info);
-       branch_info_release(&new_branch_info);
-       FREE_AND_NULL(options);
-       return ret;
+       return checkout_main(argc, argv, prefix, &opts, options,
+                            restore_usage);
 }
index d90766cad3a0ba13c41dd0b90c03254ba5eb83ee..29efe841537b341776bdca2fc70f3df4d333e066 100644 (file)
@@ -25,7 +25,7 @@
 #include "help.h"
 #include "prompt.h"
 
-static int force = -1; /* unset */
+static int require_force = -1; /* unset */
 static int interactive;
 static struct string_list del_list = STRING_LIST_INIT_DUP;
 static unsigned int colopts;
@@ -128,7 +128,7 @@ static int git_clean_config(const char *var, const char *value,
        }
 
        if (!strcmp(var, "clean.requireforce")) {
-               force = !git_config_bool(var, value);
+               require_force = git_config_bool(var, value);
                return 0;
        }
 
@@ -920,7 +920,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
 {
        int i, res;
        int dry_run = 0, remove_directories = 0, quiet = 0, ignored = 0;
-       int ignored_only = 0, config_set = 0, errors = 0, gone = 1;
+       int ignored_only = 0, force = 0, errors = 0, gone = 1;
        int rm_flags = REMOVE_DIR_KEEP_NESTED_GIT;
        struct strbuf abs_path = STRBUF_INIT;
        struct dir_struct dir = DIR_INIT;
@@ -946,22 +946,12 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
        };
 
        git_config(git_clean_config, NULL);
-       if (force < 0)
-               force = 0;
-       else
-               config_set = 1;
 
        argc = parse_options(argc, argv, prefix, options, builtin_clean_usage,
                             0);
 
-       if (!interactive && !dry_run && !force) {
-               if (config_set)
-                       die(_("clean.requireForce set to true and neither -i, -n, nor -f given; "
-                                 "refusing to clean"));
-               else
-                       die(_("clean.requireForce defaults to true and neither -i, -n, nor -f given;"
-                                 " refusing to clean"));
-       }
+       if (require_force != 0 && !force && !interactive && !dry_run)
+               die(_("clean.requireForce is true and -f not given: refusing to clean"));
 
        if (force > 1)
                rm_flags = 0;
index 93892ab5682ce51e1a131d845386e555a99b893c..74ec14542e811d10f0dc88c9bfe935fc41af1491 100644 (file)
@@ -116,7 +116,7 @@ static struct option builtin_clone_options[] = {
        OPT_HIDDEN_BOOL(0, "naked", &option_bare,
                        N_("create a bare repository")),
        OPT_BOOL(0, "mirror", &option_mirror,
-                N_("create a mirror repository (implies bare)")),
+                N_("create a mirror repository (implies --bare)")),
        OPT_BOOL('l', "local", &option_local,
                N_("to clone from a local repository")),
        OPT_BOOL(0, "no-hardlinks", &option_no_hardlinks,
@@ -740,7 +740,7 @@ static int checkout(int submodule_progress, int filter_submodules)
                die(_("unable to parse commit %s"), oid_to_hex(&oid));
        if (parse_tree(tree) < 0)
                exit(128);
-       init_tree_desc(&t, tree->buffer, tree->size);
+       init_tree_desc(&t, &tree->object.oid, tree->buffer, tree->size);
        if (unpack_trees(1, &t, &opts) < 0)
                die(_("unable to checkout working tree"));
 
index ddf4198c92c280664286d471dbd6a94c28bc3233..b27b56c8bef3ea0c968392095519017151709ca2 100644 (file)
@@ -333,7 +333,7 @@ static void create_base_index(const struct commit *current_head)
                die(_("failed to unpack HEAD tree object"));
        if (parse_tree(tree) < 0)
                exit(128);
-       init_tree_desc(&t, tree->buffer, tree->size);
+       init_tree_desc(&t, &tree->object.oid, tree->buffer, tree->size);
        if (unpack_trees(1, &t, &opts))
                exit(128); /* We've already reported the error, finish dying */
 }
index 71a195ca227315d799c03dd766d4723f7ffbec46..782bda007c29e7f23393d1d5543dc47497ee2d14 100644 (file)
@@ -1236,20 +1236,6 @@ static void *gfi_unpack_entry(
        return unpack_entry(the_repository, p, oe->idx.offset, &type, sizep);
 }
 
-static const char *get_mode(const char *str, uint16_t *modep)
-{
-       unsigned char c;
-       uint16_t mode = 0;
-
-       while ((c = *str++) != ' ') {
-               if (c < '0' || c > '7')
-                       return NULL;
-               mode = (mode << 3) + (c - '0');
-       }
-       *modep = mode;
-       return str;
-}
-
 static void load_tree(struct tree_entry *root)
 {
        struct object_id *oid = &root->versions[1].oid;
@@ -1287,7 +1273,7 @@ static void load_tree(struct tree_entry *root)
                t->entries[t->entry_count++] = e;
 
                e->tree = NULL;
-               c = get_mode(c, &e->versions[1].mode);
+               c = parse_mode(c, &e->versions[1].mode);
                if (!c)
                        die("Corrupt mode in %s", oid_to_hex(oid));
                e->versions[0].mode = e->versions[1].mode;
@@ -2280,7 +2266,7 @@ static void file_change_m(const char *p, struct branch *b)
        struct object_id oid;
        uint16_t mode, inline_data = 0;
 
-       p = get_mode(p, &mode);
+       p = parse_mode(p, &mode);
        if (!p)
                die("Corrupt mode: %s", command_buf.buf);
        switch (mode) {
index 982bcfc4b1dfb81d35c2af2bf6118f289ec376ef..5777ba82a988e8a26dcae2e77ab8fb3423b70a6f 100644 (file)
@@ -527,7 +527,7 @@ static int grep_submodule(struct grep_opt *opt,
                strbuf_addstr(&base, filename);
                strbuf_addch(&base, '/');
 
-               init_tree_desc(&tree, data, size);
+               init_tree_desc(&tree, oid, data, size);
                hit = grep_tree(&subopt, pathspec, &tree, &base, base.len,
                                object_type == OBJ_COMMIT);
                strbuf_release(&base);
@@ -573,7 +573,7 @@ static int grep_cache(struct grep_opt *opt,
                                                     &type, &size);
                        if (!data)
                                die(_("unable to read tree %s"), oid_to_hex(&ce->oid));
-                       init_tree_desc(&tree, data, size);
+                       init_tree_desc(&tree, &ce->oid, data, size);
 
                        hit |= grep_tree(opt, pathspec, &tree, &name, 0, 0);
                        strbuf_setlen(&name, name_base_len);
@@ -669,7 +669,7 @@ static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec,
                                    oid_to_hex(&entry.oid));
 
                        strbuf_addch(base, '/');
-                       init_tree_desc(&sub, data, size);
+                       init_tree_desc(&sub, &entry.oid, data, size);
                        hit |= grep_tree(opt, pathspec, &sub, base, tn_len,
                                         check_attr);
                        free(data);
@@ -713,7 +713,7 @@ static int grep_object(struct grep_opt *opt, const struct pathspec *pathspec,
                        strbuf_add(&base, name, len);
                        strbuf_addch(&base, ':');
                }
-               init_tree_desc(&tree, data, size);
+               init_tree_desc(&tree, &obj->oid, data, size);
                hit = grep_tree(opt, pathspec, &tree, &base, base.len,
                                obj->type == OBJ_COMMIT);
                strbuf_release(&base);
index a3a37bd215d844806814d677d2c7dfa71c8f31c6..856428fef9577eaa7f4407d598e073e488e4d089 100644 (file)
@@ -1524,14 +1524,12 @@ static void final(const char *final_pack_name, const char *curr_pack_name,
        struct strbuf pack_name = STRBUF_INIT;
        struct strbuf index_name = STRBUF_INIT;
        struct strbuf rev_index_name = STRBUF_INIT;
-       int err;
 
        if (!from_stdin) {
                close(input_fd);
        } else {
                fsync_component_or_die(FSYNC_COMPONENT_PACK, output_fd, curr_pack_name);
-               err = close(output_fd);
-               if (err)
+               if (close(output_fd))
                        die_errno(_("error while closing pack file"));
        }
 
@@ -1566,17 +1564,8 @@ static void final(const char *final_pack_name, const char *curr_pack_name,
                write_or_die(1, buf.buf, buf.len);
                strbuf_release(&buf);
 
-               /*
-                * Let's just mimic git-unpack-objects here and write
-                * the last part of the input buffer to stdout.
-                */
-               while (input_len) {
-                       err = xwrite(1, input_buffer + input_offset, input_len);
-                       if (err <= 0)
-                               break;
-                       input_len -= err;
-                       input_offset += err;
-               }
+               /* Write the last part of the buffer to stdout */
+               write_in_full(1, input_buffer + input_offset, input_len);
        }
 
        strbuf_release(&rev_index_name);
index 033bd1556cf9f8d6e56c2730a3e868500388144e..8768bfea3c43fe43d8a41c2dd148912b1546d5b7 100644 (file)
@@ -9,12 +9,13 @@
 #include "gettext.h"
 #include "parse-options.h"
 #include "string-list.h"
+#include "tempfile.h"
 #include "trailer.h"
 #include "config.h"
 
 static const char * const git_interpret_trailers_usage[] = {
        N_("git interpret-trailers [--in-place] [--trim-empty]\n"
-          "                       [(--trailer (<key>|<keyAlias>)[(=|:)<value>])...]\n"
+          "                       [(--trailer (<key>|<key-alias>)[(=|:)<value>])...]\n"
           "                       [--parse] [<file>...]"),
        NULL
 };
@@ -91,6 +92,102 @@ static int parse_opt_parse(const struct option *opt, const char *arg,
        return 0;
 }
 
+static struct tempfile *trailers_tempfile;
+
+static FILE *create_in_place_tempfile(const char *file)
+{
+       struct stat st;
+       struct strbuf filename_template = STRBUF_INIT;
+       const char *tail;
+       FILE *outfile;
+
+       if (stat(file, &st))
+               die_errno(_("could not stat %s"), file);
+       if (!S_ISREG(st.st_mode))
+               die(_("file %s is not a regular file"), file);
+       if (!(st.st_mode & S_IWUSR))
+               die(_("file %s is not writable by user"), file);
+
+       /* Create temporary file in the same directory as the original */
+       tail = strrchr(file, '/');
+       if (tail)
+               strbuf_add(&filename_template, file, tail - file + 1);
+       strbuf_addstr(&filename_template, "git-interpret-trailers-XXXXXX");
+
+       trailers_tempfile = xmks_tempfile_m(filename_template.buf, st.st_mode);
+       strbuf_release(&filename_template);
+       outfile = fdopen_tempfile(trailers_tempfile, "w");
+       if (!outfile)
+               die_errno(_("could not open temporary file"));
+
+       return outfile;
+}
+
+static void read_input_file(struct strbuf *sb, const char *file)
+{
+       if (file) {
+               if (strbuf_read_file(sb, file, 0) < 0)
+                       die_errno(_("could not read input file '%s'"), file);
+       } else {
+               if (strbuf_read(sb, fileno(stdin), 0) < 0)
+                       die_errno(_("could not read from stdin"));
+       }
+}
+
+static void interpret_trailers(const struct process_trailer_options *opts,
+                              struct list_head *new_trailer_head,
+                              const char *file)
+{
+       LIST_HEAD(head);
+       struct strbuf sb = STRBUF_INIT;
+       struct strbuf trailer_block = STRBUF_INIT;
+       struct trailer_info info;
+       FILE *outfile = stdout;
+
+       trailer_config_init();
+
+       read_input_file(&sb, file);
+
+       if (opts->in_place)
+               outfile = create_in_place_tempfile(file);
+
+       parse_trailers(opts, &info, sb.buf, &head);
+
+       /* Print the lines before the trailers */
+       if (!opts->only_trailers)
+               fwrite(sb.buf, 1, info.trailer_block_start, outfile);
+
+       if (!opts->only_trailers && !info.blank_line_before_trailer)
+               fprintf(outfile, "\n");
+
+
+       if (!opts->only_input) {
+               LIST_HEAD(config_head);
+               LIST_HEAD(arg_head);
+               parse_trailers_from_config(&config_head);
+               parse_trailers_from_command_line_args(&arg_head, new_trailer_head);
+               list_splice(&config_head, &arg_head);
+               process_trailers_lists(&head, &arg_head);
+       }
+
+       /* Print trailer block. */
+       format_trailers(opts, &head, &trailer_block);
+       free_trailers(&head);
+       fwrite(trailer_block.buf, 1, trailer_block.len, outfile);
+       strbuf_release(&trailer_block);
+
+       /* 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);
+
+       if (opts->in_place)
+               if (rename_tempfile(&trailers_tempfile, file))
+                       die_errno(_("could not rename temporary file to %s"), file);
+
+       strbuf_release(&sb);
+}
+
 int cmd_interpret_trailers(int argc, const char **argv, const char *prefix)
 {
        struct process_trailer_options opts = PROCESS_TRAILER_OPTIONS_INIT;
@@ -132,11 +229,11 @@ int cmd_interpret_trailers(int argc, const char **argv, const char *prefix)
        if (argc) {
                int i;
                for (i = 0; i < argc; i++)
-                       process_trailers(argv[i], &opts, &trailers);
+                       interpret_trailers(&opts, &trailers, argv[i]);
        } else {
                if (opts.in_place)
                        die(_("no input file given for in-place editing"));
-               process_trailers(NULL, &opts, &trailers);
+               interpret_trailers(&opts, &trailers, NULL);
        }
 
        new_trailers_clear(&trailers);
index e4a891337c3c619112af77bac014139b520e463f..61a2965c8a74c6f938eb01b507541056c408cad7 100644 (file)
@@ -375,6 +375,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;
        int ret;
 
        git_config(git_default_config, NULL);
@@ -406,7 +407,9 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)
                        ls_tree_usage, ls_tree_options);
        if (argc < 1)
                usage_with_options(ls_tree_usage, ls_tree_options);
-       if (repo_get_oid(the_repository, argv[0], &oid))
+       if (get_oid_with_context(the_repository, argv[0],
+                                GET_OID_HASH_ANY, &oid,
+                                &obj_context))
                die("Not a valid object name %s", argv[0]);
 
        /*
index a0ba1f9815d9f45a1525bab8932810c9f5919aff..1cbd6a899ccf2f3a3d92c7f14dcec3074634a3df 100644 (file)
@@ -677,7 +677,8 @@ static int read_tree_trivial(struct object_id *common, struct object_id *head,
        cache_tree_free(&the_index.cache_tree);
        for (i = 0; i < nr_trees; i++) {
                parse_tree(trees[i]);
-               init_tree_desc(t+i, trees[i]->buffer, trees[i]->size);
+               init_tree_desc(t+i, &trees[i]->object.oid,
+                              trees[i]->buffer, trees[i]->size);
        }
        if (unpack_trees(nr_trees, t, &opts))
                return -1;
index 329aeac80437502a49b48fe5383aa40e90cd16c3..baf0090fc8dac79201a682f1d2c56257d8587283 100644 (file)
@@ -1826,7 +1826,8 @@ static void add_pbase_object(struct tree_desc *tree,
                        tree = pbase_tree_get(&entry.oid);
                        if (!tree)
                                return;
-                       init_tree_desc(&sub, tree->tree_data, tree->tree_size);
+                       init_tree_desc(&sub, &tree->oid,
+                                      tree->tree_data, tree->tree_size);
 
                        add_pbase_object(&sub, down, downlen, fullname);
                        pbase_tree_put(tree);
@@ -1886,7 +1887,8 @@ static void add_preferred_base_object(const char *name)
                }
                else {
                        struct tree_desc tree;
-                       init_tree_desc(&tree, it->pcache.tree_data, it->pcache.tree_size);
+                       init_tree_desc(&tree, &it->pcache.oid,
+                                      it->pcache.tree_data, it->pcache.tree_size);
                        add_pbase_object(&tree, name, cmplen, name);
                }
        }
index 1ffd863cff6701af8013130d2e2801366f4e7228..6f89cec0fbb6b181b8feae32d7d3da6ff45b7ef3 100644 (file)
@@ -263,7 +263,7 @@ int cmd_read_tree(int argc, const char **argv, const char *cmd_prefix)
                struct tree *tree = trees[i];
                if (parse_tree(tree) < 0)
                        return 128;
-               init_tree_desc(t+i, tree->buffer, tree->size);
+               init_tree_desc(t+i, &tree->object.oid, tree->buffer, tree->size);
        }
        if (unpack_trees(nr_trees, t, &opts))
                return 128;
index be787690bd7c2b6396784a1476314b8f757ab749..e444ab102dfe5c99dae8b004960b27a8a600a16b 100644 (file)
@@ -567,13 +567,6 @@ static int move_to_original_branch(struct rebase_options *opts)
        return ret;
 }
 
-static const char *resolvemsg =
-N_("Resolve all conflicts manually, mark them as resolved with\n"
-"\"git add/rm <conflicted_files>\", then run \"git rebase --continue\".\n"
-"You can instead skip this commit: run \"git rebase --skip\".\n"
-"To abort and get back to the state before \"git rebase\", run "
-"\"git rebase --abort\".");
-
 static int run_am(struct rebase_options *opts)
 {
        struct child_process am = CHILD_PROCESS_INIT;
@@ -587,7 +580,7 @@ static int run_am(struct rebase_options *opts)
                     opts->reflog_action);
        if (opts->action == ACTION_CONTINUE) {
                strvec_push(&am.args, "--resolved");
-               strvec_pushf(&am.args, "--resolvemsg=%s", resolvemsg);
+               strvec_pushf(&am.args, "--resolvemsg=%s", rebase_resolvemsg);
                if (opts->gpg_sign_opt)
                        strvec_push(&am.args, opts->gpg_sign_opt);
                status = run_command(&am);
@@ -598,7 +591,7 @@ static int run_am(struct rebase_options *opts)
        }
        if (opts->action == ACTION_SKIP) {
                strvec_push(&am.args, "--skip");
-               strvec_pushf(&am.args, "--resolvemsg=%s", resolvemsg);
+               strvec_pushf(&am.args, "--resolvemsg=%s", rebase_resolvemsg);
                status = run_command(&am);
                if (status)
                        return status;
@@ -672,7 +665,7 @@ static int run_am(struct rebase_options *opts)
 
        strvec_pushv(&am.args, opts->git_am_opts.v);
        strvec_push(&am.args, "--rebasing");
-       strvec_pushf(&am.args, "--resolvemsg=%s", resolvemsg);
+       strvec_pushf(&am.args, "--resolvemsg=%s", rebase_resolvemsg);
        strvec_push(&am.args, "--patch-format=mboxrd");
        if (opts->allow_rerere_autoupdate == RERERE_AUTOUPDATE)
                strvec_push(&am.args, "--rerere-autoupdate");
@@ -700,7 +693,6 @@ static int run_specific_rebase(struct rebase_options *opts)
 
        if (opts->type == REBASE_MERGE) {
                /* Run sequencer-based rebase */
-               setenv("GIT_CHERRY_PICK_HELP", resolvemsg, 1);
                if (!(opts->flags & REBASE_INTERACTIVE_EXPLICIT))
                        setenv("GIT_SEQUENCE_EDITOR", ":", 1);
                if (opts->gpg_sign_opt) {
index d91bbe728d739e074e5f4fa54f70c186f371c1b7..8412d12fa55100132c69ae24914b2db536bf77c9 100644 (file)
@@ -150,7 +150,7 @@ static int parse_mirror_opt(const struct option *opt, const char *arg, int not)
        else if (!strcmp(arg, "push"))
                *mirror = MIRROR_PUSH;
        else
-               return error(_("unknown mirror argument: %s"), arg);
+               return error(_("unknown --mirror argument: %s"), arg);
        return 0;
 }
 
index ede36328a3cab9170bc59cb98dda097ea6eb24f7..15e4cccc45687d4902979911e7162e7c82567e6e 100644 (file)
@@ -314,8 +314,9 @@ static int write_oid(const struct object_id *oid,
                        die(_("could not start pack-objects to repack promisor objects"));
        }
 
-       xwrite(cmd->in, oid_to_hex(oid), the_hash_algo->hexsz);
-       xwrite(cmd->in, "\n", 1);
+       if (write_in_full(cmd->in, oid_to_hex(oid), the_hash_algo->hexsz) < 0 ||
+           write_in_full(cmd->in, "\n", 1) < 0)
+               die(_("failed to feed promisor objects to pack-objects"));
        return 0;
 }
 
index 181c703d4c00bcce1f1715f1a8913fad0892828d..624182e507e5af9ac34a67f18d7a5dcf6fe2e58c 100644 (file)
@@ -25,6 +25,7 @@
 #include "submodule.h"
 #include "commit-reach.h"
 #include "shallow.h"
+#include "object-file-convert.h"
 
 #define DO_REVS                1
 #define DO_NOREV       2
@@ -676,6 +677,8 @@ static void print_path(const char *path, const char *prefix, enum format_type fo
 int cmd_rev_parse(int argc, const char **argv, const char *prefix)
 {
        int i, as_is = 0, verify = 0, quiet = 0, revs_count = 0, type = 0;
+       const struct git_hash_algo *output_algo = NULL;
+       const struct git_hash_algo *compat = NULL;
        int did_repo_setup = 0;
        int has_dashdash = 0;
        int output_prefix = 0;
@@ -747,6 +750,7 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
 
                        prepare_repo_settings(the_repository);
                        the_repository->settings.command_requires_full_index = 0;
+                       compat = the_repository->compat_hash_algo;
                }
 
                if (!strcmp(arg, "--")) {
@@ -834,6 +838,22 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
                                flags |= GET_OID_QUIETLY;
                                continue;
                        }
+                       if (opt_with_value(arg, "--output-object-format", &arg)) {
+                               if (!arg)
+                                       die(_("no object format specified"));
+                               if (!strcmp(arg, the_hash_algo->name) ||
+                                   !strcmp(arg, "storage")) {
+                                       flags |= GET_OID_HASH_ANY;
+                                       output_algo = the_hash_algo;
+                                       continue;
+                               }
+                               else if (compat && !strcmp(arg, compat->name)) {
+                                       flags |= GET_OID_HASH_ANY;
+                                       output_algo = compat;
+                                       continue;
+                               }
+                               else die(_("unsupported object format: %s"), arg);
+                       }
                        if (opt_with_value(arg, "--short", &arg)) {
                                filter &= ~(DO_FLAGS|DO_NOREV);
                                verify = 1;
@@ -883,7 +903,7 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
                                continue;
                        }
                        if (skip_prefix(arg, "--disambiguate=", &arg)) {
-                               repo_for_each_abbrev(the_repository, arg,
+                               repo_for_each_abbrev(the_repository, arg, the_hash_algo,
                                                     show_abbrev, NULL);
                                continue;
                        }
@@ -1091,6 +1111,9 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
                }
                if (!get_oid_with_context(the_repository, name,
                                          flags, &oid, &unused)) {
+                       if (output_algo)
+                               repo_oid_to_algop(the_repository, &oid,
+                                                 output_algo, &oid);
                        if (verify)
                                revs_count++;
                        else
index 7fb355bff0a628d1d7a1a08646175ac33d8fe9d7..062be1fbc07b0c2ecfcc6f41f5d995ae13c0aecb 100644 (file)
@@ -284,7 +284,7 @@ static int reset_tree(struct object_id *i_tree, int update, int reset)
        if (parse_tree(tree))
                return -1;
 
-       init_tree_desc(t, tree->buffer, tree->size);
+       init_tree_desc(t, &tree->object.oid, tree->buffer, tree->size);
 
        opts.head_idx = 1;
        opts.src_index = &the_index;
@@ -870,7 +870,8 @@ static void diff_include_untracked(const struct stash_info *info, struct diff_op
                tree[i] = parse_tree_indirect(oid[i]);
                if (parse_tree(tree[i]) < 0)
                        die(_("failed to parse tree"));
-               init_tree_desc(&tree_desc[i], tree[i]->buffer, tree[i]->size);
+               init_tree_desc(&tree_desc[i], &tree[i]->object.oid,
+                              tree[i]->buffer, tree[i]->size);
        }
 
        unpack_tree_opt.head_idx = -1;
index 19a7e06bf414ccf811c93f0ac941a142a7fabef2..e2ff749832c282dc12ce6c5accf6389775d846d2 100644 (file)
@@ -27,6 +27,7 @@
 #include "ref-filter.h"
 #include "date.h"
 #include "write-or-die.h"
+#include "object-file-convert.h"
 
 static const char * const git_tag_usage[] = {
        N_("git tag [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>] [-e]\n"
@@ -151,9 +152,43 @@ static int verify_tag(const char *name, const char *ref UNUSED,
        return 0;
 }
 
-static int do_sign(struct strbuf *buffer)
+static int do_sign(struct strbuf *buffer, struct object_id **compat_oid,
+                  struct object_id *compat_oid_buf)
 {
-       return sign_buffer(buffer, buffer, get_signing_key()) ? -1 : 0;
+       const struct git_hash_algo *compat = the_repository->compat_hash_algo;
+       struct strbuf sig = STRBUF_INIT, compat_sig = STRBUF_INIT;
+       struct strbuf compat_buf = STRBUF_INIT;
+       const char *keyid = get_signing_key();
+       int ret = -1;
+
+       if (sign_buffer(buffer, &sig, keyid))
+               return -1;
+
+       if (compat) {
+               const struct git_hash_algo *algo = the_repository->hash_algo;
+
+               if (convert_object_file(&compat_buf, algo, compat,
+                                       buffer->buf, buffer->len, OBJ_TAG, 1))
+                       goto out;
+               if (sign_buffer(&compat_buf, &compat_sig, keyid))
+                       goto out;
+               add_header_signature(&compat_buf, &sig, algo);
+               strbuf_addbuf(&compat_buf, &compat_sig);
+               hash_object_file(compat, compat_buf.buf, compat_buf.len,
+                                OBJ_TAG, compat_oid_buf);
+               *compat_oid = compat_oid_buf;
+       }
+
+       if (compat_sig.len)
+               add_header_signature(buffer, &compat_sig, compat);
+
+       strbuf_addbuf(buffer, &sig);
+       ret = 0;
+out:
+       strbuf_release(&sig);
+       strbuf_release(&compat_sig);
+       strbuf_release(&compat_buf);
+       return ret;
 }
 
 static const char tag_template[] =
@@ -226,9 +261,11 @@ static void write_tag_body(int fd, const struct object_id *oid)
 
 static int build_tag_object(struct strbuf *buf, int sign, struct object_id *result)
 {
-       if (sign && do_sign(buf) < 0)
+       struct object_id *compat_oid = NULL, compat_oid_buf;
+       if (sign && do_sign(buf, &compat_oid, &compat_oid_buf) < 0)
                return error(_("unable to sign the tag"));
-       if (write_object_file(buf->buf, buf->len, OBJ_TAG, result) < 0)
+       if (write_object_file_flags(buf->buf, buf->len, OBJ_TAG, result,
+                                   compat_oid, 0) < 0)
                return error(_("unable to write tag file"));
        return 0;
 }
index e0a701f2b383bbbd95fc1e7fef3fec30c3783c37..f1c85a00ae9bb6586527a845c302cba47c4c8daf 100644 (file)
@@ -679,13 +679,7 @@ int cmd_unpack_objects(int argc, const char **argv, const char *prefix UNUSED)
        use(the_hash_algo->rawsz);
 
        /* Write the last part of the buffer to stdout */
-       while (len) {
-               int ret = xwrite(1, buffer + offset, len);
-               if (ret <= 0)
-                       break;
-               len -= ret;
-               offset += ret;
-       }
+       write_in_full(1, buffer + offset, len);
 
        /* All done */
        return has_errors;
index 80ca8324773d2dc352bf4d47cfc4d0036316180e..387c0a3e5b7b8bfc895f380e8c3976fcf9b777d8 100644 (file)
@@ -447,7 +447,7 @@ static int update_one(struct cache_tree *it,
                hash_object_file(the_hash_algo, buffer.buf, buffer.len,
                                 OBJ_TREE, &it->oid);
        } else if (write_object_file_flags(buffer.buf, buffer.len, OBJ_TREE,
-                                          &it->oid, flags & WRITE_TREE_SILENT
+                                          &it->oid, NULL, flags & WRITE_TREE_SILENT
                                           ? HASH_SILENT : 0)) {
                strbuf_release(&buffer);
                return -1;
@@ -769,7 +769,7 @@ static void prime_cache_tree_rec(struct repository *r,
 
        oidcpy(&it->oid, &tree->object.oid);
 
-       init_tree_desc(&desc, tree->buffer, tree->size);
+       init_tree_desc(&desc, &tree->object.oid, tree->buffer, tree->size);
        cnt = 0;
        while (tree_entry(&desc, &entry)) {
                if (!S_ISDIR(entry.mode))
index 467be9f7f99408edbe1a34e2f21c491dd50e2813..1da10ec916c5e08a542da4bdc3b70518e91f113d 100644 (file)
--- a/commit.c
+++ b/commit.c
@@ -27,6 +27,7 @@
 #include "tree.h"
 #include "hook.h"
 #include "parse.h"
+#include "object-file-convert.h"
 
 static struct commit_extra_header *read_commit_extra_header_lines(const char *buf, size_t len, const char **);
 
@@ -1113,12 +1114,11 @@ static const char *gpg_sig_headers[] = {
        "gpgsig-sha256",
 };
 
-int sign_with_header(struct strbuf *buf, const char *keyid)
+int add_header_signature(struct strbuf *buf, struct strbuf *sig, const struct git_hash_algo *algo)
 {
-       struct strbuf sig = STRBUF_INIT;
        int inspos, copypos;
        const char *eoh;
-       const char *gpg_sig_header = gpg_sig_headers[hash_algo_by_ptr(the_hash_algo)];
+       const char *gpg_sig_header = gpg_sig_headers[hash_algo_by_ptr(algo)];
        int gpg_sig_header_len = strlen(gpg_sig_header);
 
        /* find the end of the header */
@@ -1128,15 +1128,8 @@ int sign_with_header(struct strbuf *buf, const char *keyid)
        else
                inspos = eoh - buf->buf + 1;
 
-       if (!keyid || !*keyid)
-               keyid = get_signing_key();
-       if (sign_buffer(buf, &sig, keyid)) {
-               strbuf_release(&sig);
-               return -1;
-       }
-
-       for (copypos = 0; sig.buf[copypos]; ) {
-               const char *bol = sig.buf + copypos;
+       for (copypos = 0; sig->buf[copypos]; ) {
+               const char *bol = sig->buf + copypos;
                const char *eol = strchrnul(bol, '\n');
                int len = (eol - bol) + !!*eol;
 
@@ -1149,11 +1142,17 @@ int sign_with_header(struct strbuf *buf, const char *keyid)
                inspos += len;
                copypos += len;
        }
-       strbuf_release(&sig);
        return 0;
 }
 
-
+static int sign_commit_to_strbuf(struct strbuf *sig, struct strbuf *buf, const char *keyid)
+{
+       if (!keyid || !*keyid)
+               keyid = get_signing_key();
+       if (sign_buffer(buf, sig, keyid))
+               return -1;
+       return 0;
+}
 
 int parse_signed_commit(const struct commit *commit,
                        struct strbuf *payload, struct strbuf *signature,
@@ -1369,6 +1368,39 @@ void append_merge_tag_headers(struct commit_list *parents,
        }
 }
 
+static int convert_commit_extra_headers(struct commit_extra_header *orig,
+                                       struct commit_extra_header **result)
+{
+       const struct git_hash_algo *compat = the_repository->compat_hash_algo;
+       const struct git_hash_algo *algo = the_repository->hash_algo;
+       struct commit_extra_header *extra = NULL, **tail = &extra;
+       struct strbuf out = STRBUF_INIT;
+       while (orig) {
+               struct commit_extra_header *new;
+               CALLOC_ARRAY(new, 1);
+               if (!strcmp(orig->key, "mergetag")) {
+                       if (convert_object_file(&out, algo, compat,
+                                               orig->value, orig->len,
+                                               OBJ_TAG, 1)) {
+                               free(new);
+                               free_commit_extra_headers(extra);
+                               return -1;
+                       }
+                       new->key = xstrdup("mergetag");
+                       new->value = strbuf_detach(&out, &new->len);
+               } else {
+                       new->key = xstrdup(orig->key);
+                       new->len = orig->len;
+                       new->value = xmemdupz(orig->value, orig->len);
+               }
+               *tail = new;
+               tail = &new->next;
+               orig = orig->next;
+       }
+       *result = extra;
+       return 0;
+}
+
 static void add_extra_header(struct strbuf *buffer,
                             struct commit_extra_header *extra)
 {
@@ -1612,70 +1644,169 @@ N_("Warning: commit message did not conform to UTF-8.\n"
    "You may want to amend it after fixing the message, or set the config\n"
    "variable i18n.commitEncoding to the encoding your project uses.\n");
 
-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 char *author, const char *committer,
-                        const char *sign_commit,
-                        struct commit_extra_header *extra)
+static void write_commit_tree(struct strbuf *buffer, const char *msg, size_t msg_len,
+                             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)
 {
-       int result;
        int encoding_is_utf8;
-       struct strbuf buffer;
-
-       assert_oid_type(tree, OBJ_TREE);
-
-       if (memchr(msg, '\0', msg_len))
-               return error("a NUL byte in commit log message not allowed.");
+       size_t i;
 
        /* Not having i18n.commitencoding is the same as having utf-8 */
        encoding_is_utf8 = is_encoding_utf8(git_commit_encoding);
 
-       strbuf_init(&buffer, 8192); /* should avoid reallocs for the headers */
-       strbuf_addf(&buffer, "tree %s\n", oid_to_hex(tree));
+       strbuf_grow(buffer, 8192); /* should avoid reallocs for the headers */
+       strbuf_addf(buffer, "tree %s\n", oid_to_hex(tree));
 
        /*
         * NOTE! This ordering means that the same exact tree merged with a
         * different order of parents will be a _different_ changeset even
         * if everything else stays the same.
         */
-       while (parents) {
-               struct commit *parent = pop_commit(&parents);
-               strbuf_addf(&buffer, "parent %s\n",
-                           oid_to_hex(&parent->object.oid));
-       }
+       for (i = 0; i < parents_len; i++)
+               strbuf_addf(buffer, "parent %s\n", oid_to_hex(&parents[i]));
 
        /* Person/date information */
        if (!author)
                author = git_author_info(IDENT_STRICT);
-       strbuf_addf(&buffer, "author %s\n", author);
+       strbuf_addf(buffer, "author %s\n", author);
        if (!committer)
                committer = git_committer_info(IDENT_STRICT);
-       strbuf_addf(&buffer, "committer %s\n", committer);
+       strbuf_addf(buffer, "committer %s\n", committer);
        if (!encoding_is_utf8)
-               strbuf_addf(&buffer, "encoding %s\n", git_commit_encoding);
+               strbuf_addf(buffer, "encoding %s\n", git_commit_encoding);
 
        while (extra) {
-               add_extra_header(&buffer, extra);
+               add_extra_header(buffer, extra);
                extra = extra->next;
        }
-       strbuf_addch(&buffer, '\n');
+       strbuf_addch(buffer, '\n');
 
        /* And add the comment */
-       strbuf_add(&buffer, msg, msg_len);
+       strbuf_add(buffer, msg, msg_len);
+}
 
-       /* And check the encoding */
-       if (encoding_is_utf8 && !verify_utf8(&buffer))
-               fprintf(stderr, _(commit_utf8_warn));
+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 char *author, const char *committer,
+                        const char *sign_commit,
+                        struct commit_extra_header *extra)
+{
+       struct repository *r = the_repository;
+       int result = 0;
+       int encoding_is_utf8;
+       struct strbuf buffer = STRBUF_INIT, compat_buffer = STRBUF_INIT;
+       struct strbuf sig = STRBUF_INIT, compat_sig = STRBUF_INIT;
+       struct object_id *parent_buf = NULL, *compat_oid = NULL;
+       struct object_id compat_oid_buf;
+       size_t i, nparents;
+
+       /* Not having i18n.commitencoding is the same as having utf-8 */
+       encoding_is_utf8 = is_encoding_utf8(git_commit_encoding);
+
+       assert_oid_type(tree, OBJ_TREE);
 
-       if (sign_commit && sign_with_header(&buffer, sign_commit)) {
+       if (memchr(msg, '\0', msg_len))
+               return error("a NUL byte in commit log message not allowed.");
+
+       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);
+       }
+
+       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)) {
                result = -1;
                goto out;
        }
+       if (r->compat_hash_algo) {
+               struct commit_extra_header *compat_extra = NULL;
+               struct object_id mapped_tree;
+               struct object_id *mapped_parents;
+
+               CALLOC_ARRAY(mapped_parents, nparents);
 
-       result = write_object_file(buffer.buf, buffer.len, OBJ_COMMIT, ret);
+               if (repo_oid_to_algop(r, tree, r->compat_hash_algo, &mapped_tree)) {
+                       result = -1;
+                       free(mapped_parents);
+                       goto out;
+               }
+               for (i = 0; i < nparents; i++)
+                       if (repo_oid_to_algop(r, &parent_buf[i], r->compat_hash_algo, &mapped_parents[i])) {
+                               result = -1;
+                               free(mapped_parents);
+                               goto out;
+                       }
+               if (convert_commit_extra_headers(extra, &compat_extra)) {
+                       result = -1;
+                       free(mapped_parents);
+                       goto out;
+               }
+               write_commit_tree(&compat_buffer, msg, msg_len, &mapped_tree,
+                                 mapped_parents, nparents, author, committer, compat_extra);
+               free_commit_extra_headers(compat_extra);
+               free(mapped_parents);
+
+               if (sign_commit && sign_commit_to_strbuf(&compat_sig, &compat_buffer, sign_commit)) {
+                       result = -1;
+                       goto out;
+               }
+       }
+
+       if (sign_commit) {
+               struct sig_pairs {
+                       struct strbuf *sig;
+                       const struct git_hash_algo *algo;
+               } bufs [2] = {
+                       { &compat_sig, r->compat_hash_algo },
+                       { &sig, r->hash_algo },
+               };
+               int i;
+
+               /*
+                * We write algorithms in the order they were implemented in
+                * Git to produce a stable hash when multiple algorithms are
+                * used.
+                */
+               if (r->compat_hash_algo && hash_algo_by_ptr(bufs[0].algo) > hash_algo_by_ptr(bufs[1].algo))
+                       SWAP(bufs[0], bufs[1]);
+
+               /*
+                * We traverse each algorithm in order, and apply the signature
+                * to each buffer.
+                */
+               for (i = 0; i < ARRAY_SIZE(bufs); i++) {
+                       if (!bufs[i].algo)
+                               continue;
+                       add_header_signature(&buffer, bufs[i].sig, bufs[i].algo);
+                       if (r->compat_hash_algo)
+                               add_header_signature(&compat_buffer, bufs[i].sig, bufs[i].algo);
+               }
+       }
+
+       /* And check the encoding. */
+       if (encoding_is_utf8 && (!verify_utf8(&buffer) || !verify_utf8(&compat_buffer)))
+               fprintf(stderr, _(commit_utf8_warn));
+
+       if (r->compat_hash_algo) {
+               hash_object_file(r->compat_hash_algo, compat_buffer.buf, compat_buffer.len,
+                       OBJ_COMMIT, &compat_oid_buf);
+               compat_oid = &compat_oid_buf;
+       }
+
+       result = write_object_file_flags(buffer.buf, buffer.len, OBJ_COMMIT,
+                                        ret, compat_oid, 0);
 out:
+       free(parent_buf);
        strbuf_release(&buffer);
+       strbuf_release(&compat_buffer);
+       strbuf_release(&sig);
+       strbuf_release(&compat_sig);
        return result;
 }
 
index 1cc872f225f438be7dc03a9b4dc8f207da0deb5b..62fe0d77a70738048858e4087533b045fcc40613 100644 (file)
--- a/commit.h
+++ b/commit.h
@@ -370,5 +370,6 @@ int parse_buffer_signed_by_header(const char *buffer,
                                  struct strbuf *payload,
                                  struct strbuf *signature,
                                  const struct git_hash_algo *algop);
+int add_header_signature(struct strbuf *buf, struct strbuf *sig, const struct git_hash_algo *algo);
 
 #endif /* COMMIT_H */
index dacc95172dcdcbd85086d9dacf57d2be588c90e1..d0dcca2ec554cddf61447215357a7e15877b0aca 100644 (file)
@@ -638,6 +638,18 @@ ifeq ($(uname_S),NONSTOP_KERNEL)
        SANE_TOOL_PATH = /usr/coreutils/bin:/usr/local/bin
        SHELL_PATH = /usr/coreutils/bin/bash
 endif
+ifeq ($(uname_S),OS/390)
+       NO_SYS_POLL_H = YesPlease
+       NO_STRCASESTR = YesPlease
+       NO_REGEX = YesPlease
+       NO_MMAP = YesPlease
+       NO_NSEC = YesPlease
+       NO_STRLCPY = YesPlease
+       NO_MEMMEM = YesPlease
+       NO_GECOS_IN_PWENT = YesPlease
+       HAVE_STRINGS_H = YesPlease
+       NEEDS_MODE_TRANSLATION = YesPlease
+endif
 ifeq ($(uname_S),MINGW)
        ifeq ($(shell expr "$(uname_R)" : '1\.'),2)
                $(error "Building with MSys is no longer supported")
index fcf1afd75de4eb14a0123a2a75692298f2c39a0b..75193ded4bdeda01fc440db26b08e40ae2d3a73f 100644 (file)
@@ -454,16 +454,18 @@ fi
 
 # This function is equivalent to
 #
-#    __gitcomp "$(git xxx --git-completion-helper) ..."
+#    ___git_resolved_builtins=$(git xxx --git-completion-helper)
 #
-# except that the output is cached. Accept 1-3 arguments:
+# except that the result of the execution is cached.
+#
+# Accept 1-3 arguments:
 # 1: the git command to execute, this is also the cache key
+#    (use "_" when the command contains spaces, e.g. "remote add"
+#    becomes "remote_add")
 # 2: extra options to be added on top (e.g. negative forms)
 # 3: options to be excluded
-__gitcomp_builtin ()
+__git_resolve_builtins ()
 {
-       # spaces must be replaced with underscore for multi-word
-       # commands, e.g. "git remote add" becomes remote_add.
        local cmd="$1"
        local incl="${2-}"
        local excl="${3-}"
@@ -489,7 +491,24 @@ __gitcomp_builtin ()
                eval "$var=\"$options\""
        fi
 
-       __gitcomp "$options"
+       ___git_resolved_builtins="$options"
+}
+
+# This function is equivalent to
+#
+#    __gitcomp "$(git xxx --git-completion-helper) ..."
+#
+# except that the output is cached. Accept 1-3 arguments:
+# 1: the git command to execute, this is also the cache key
+#    (use "_" when the command contains spaces, e.g. "remote add"
+#    becomes "remote_add")
+# 2: extra options to be added on top (e.g. negative forms)
+# 3: options to be excluded
+__gitcomp_builtin ()
+{
+       __git_resolve_builtins "$1" "$2" "$3"
+
+       __gitcomp "$___git_resolved_builtins"
 }
 
 # Variation of __gitcomp_nl () that appends to the existing list of
@@ -556,6 +575,26 @@ __gitcomp_file ()
        true
 }
 
+# Find the current subcommand for commands that follow the syntax:
+#
+#    git <command> <subcommand>
+#
+# 1: List of possible subcommands.
+# 2: Optional subcommand to return when none is found.
+__git_find_subcommand ()
+{
+       local subcommand subcommands="$1" default_subcommand="$2"
+
+       for subcommand in $subcommands; do
+               if [ "$subcommand" = "${words[__git_cmd_idx+1]}" ]; then
+                       echo $subcommand
+                       return
+               fi
+       done
+
+       echo $default_subcommand
+}
+
 # Execute 'git ls-files', unless the --committable option is specified, in
 # which case it runs 'git diff-index' to find out the files that can be
 # committed.  It return paths relative to the directory specified in the first
@@ -2471,13 +2510,30 @@ _git_rebase ()
 
 _git_reflog ()
 {
-       local subcommands="show delete expire"
-       local subcommand="$(__git_find_on_cmdline "$subcommands")"
+       local subcommands subcommand
 
-       if [ -z "$subcommand" ]; then
-               __gitcomp "$subcommands"
-       else
-               __git_complete_refs
+       __git_resolve_builtins "reflog"
+
+       subcommands="$___git_resolved_builtins"
+       subcommand="$(__git_find_subcommand "$subcommands" "show")"
+
+       case "$subcommand,$cur" in
+       show,--*)
+               __gitcomp "
+                       $__git_log_common_options
+                       "
+               return
+               ;;
+       $subcommand,--*)
+               __gitcomp_builtin "reflog_$subcommand"
+               return
+               ;;
+       esac
+
+       __git_complete_refs
+
+       if [ $((cword - __git_cmd_idx)) -eq 1 ]; then
+               __gitcompappend "$subcommands" "" "$cur" " "
        fi
 }
 
@@ -3573,7 +3629,7 @@ __git_complete_worktree_paths ()
        # Generate completion reply from worktree list skipping the first
        # entry: it's the path of the main worktree, which can't be moved,
        # removed, locked, etc.
-       __gitcomp_nl "$(git worktree list --porcelain |
+       __gitcomp_nl "$(__git worktree list --porcelain |
                sed -n -e '2,$ s/^worktree //p')"
 }
 
index 4ec419f90048babf0b18a0f8ca0fce82fc2b3f30..6ce9603568ef7d4b41d7eed9247e8512a2f45a74 100755 (executable)
@@ -74,8 +74,7 @@ do
        sort >uncovered_lines.txt
 
        comm -12 uncovered_lines.txt new_lines.txt |
-       sed -e 's/$/\)/' |
-       sed -e 's/^/ /' >uncovered_new_lines.txt
+       sed -e 's/$/\)/' -e 's/^/ /' >uncovered_new_lines.txt
 
        grep -q '[^[:space:]]' <uncovered_new_lines.txt &&
        echo $file >>coverage-data.txt &&
@@ -91,11 +90,7 @@ cat coverage-data.txt
 
 echo "Commits introducing uncovered code:"
 
-commit_list=$(cat coverage-data.txt |
-       grep -E '^[0-9a-f]{7,} ' |
-       awk '{print $1;}' |
-       sort |
-       uniq)
+commit_list=$(awk '/^[0-9a-f]{7,}/ { print $1 }' coverage-data.txt | sort -u)
 
 (
        for commit in $commit_list
index ca4df5be83245fe9a37a0b46737b61071f3b78b0..c3bd2a58b941f0bda8673a707fb0da71e4cbca88 100755 (executable)
@@ -63,7 +63,7 @@ test_create_pre2_32_repo () {
        git -C "$1" log -1 --format=%B HEAD^2 >msg &&
        test_commit -C "$1-sub" --annotate sub2 &&
        git clone --no-local "$1" "$1-clone" &&
-       new_commit=$(cat msg | sed -e "s/$commit/$tag/" | git -C "$1-clone" commit-tree HEAD^2^{tree}) &&
+       new_commit=$(sed -e "s/$commit/$tag/" msg | git -C "$1-clone" commit-tree HEAD^2^{tree}) &&
        git -C "$1-clone" replace HEAD^2 $new_commit
 }
 
diff --git a/date.c b/date.c
index 619ada5b20442046ef50a38d4e88136b14419a42..44cf2221d81f61760fa26605765eaea5eee9ee4d 100644 (file)
--- a/date.c
+++ b/date.c
@@ -342,14 +342,18 @@ const char *show_date(timestamp_t time, int tz, const struct date_mode *mode)
                                tm->tm_hour, tm->tm_min, tm->tm_sec,
                                tz);
        else if (mode->type == DATE_ISO8601_STRICT) {
-               char sign = (tz >= 0) ? '+' : '-';
-               tz = abs(tz);
-               strbuf_addf(&timebuf, "%04d-%02d-%02dT%02d:%02d:%02d%c%02d:%02d",
+               strbuf_addf(&timebuf, "%04d-%02d-%02dT%02d:%02d:%02d",
                                tm->tm_year + 1900,
                                tm->tm_mon + 1,
                                tm->tm_mday,
-                               tm->tm_hour, tm->tm_min, tm->tm_sec,
-                               sign, tz / 100, tz % 100);
+                               tm->tm_hour, tm->tm_min, tm->tm_sec);
+               if (tz == 0) {
+                       strbuf_addch(&timebuf, 'Z');
+               } else {
+                       strbuf_addch(&timebuf, tz >= 0 ? '+' : '-');
+                       tz = abs(tz);
+                       strbuf_addf(&timebuf, "%02d:%02d", tz / 100, tz % 100);
+               }
        } else if (mode->type == DATE_RFC2822)
                strbuf_addf(&timebuf, "%.3s, %d %.3s %d %02d:%02d:%02d %+05d",
                        weekday_names[tm->tm_wday], tm->tm_mday,
index ee2318d45a1c7898b39999b1f5fc81be8ec49f00..f7e079425fe45d70ead6ea191d2ecc37ee7a339b 100644 (file)
@@ -284,7 +284,7 @@ void resolve_tree_islands(struct repository *r,
                if (!tree || parse_tree(tree) < 0)
                        die(_("bad tree object %s"), oid_to_hex(&ent->idx.oid));
 
-               init_tree_desc(&desc, tree->buffer, tree->size);
+               init_tree_desc(&desc, &tree->object.oid, tree->buffer, tree->size);
                while (tree_entry(&desc, &entry)) {
                        struct object *obj;
 
index 5e8717c774eff4bb2364344c440a4efb4686e4f4..1cd790a4d2bef6a3df8a6eb080622ccaa6749fa3 100644 (file)
@@ -562,7 +562,7 @@ static int diff_cache(struct rev_info *revs,
        opts.pathspec = &revs->diffopt.pathspec;
        opts.pathspec->recursive = 1;
 
-       init_tree_desc(&t, tree->buffer, tree->size);
+       init_tree_desc(&t, &tree->object.oid, tree->buffer, tree->size);
        return unpack_trees(1, &t, &opts);
 }
 
diff --git a/diff.c b/diff.c
index e50def45383eba4af74300802a161bdeeb6c4e8f..108c1875775df223c4ca4f9dec27194033a7cfaa 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -62,6 +62,8 @@ static const 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 int diff_relative;
 static int diff_stat_name_width;
 static int diff_stat_graph_width;
@@ -408,6 +410,12 @@ int git_diff_ui_config(const char *var, const char *value,
                diff_no_prefix = git_config_bool(var, value);
                return 0;
        }
+       if (!strcmp(var, "diff.srcprefix")) {
+               return git_config_string(&diff_src_prefix, var, value);
+       }
+       if (!strcmp(var, "diff.dstprefix")) {
+               return git_config_string(&diff_dst_prefix, var, value);
+       }
        if (!strcmp(var, "diff.relative")) {
                diff_relative = git_config_bool(var, value);
                return 0;
@@ -3425,8 +3433,8 @@ void diff_set_noprefix(struct diff_options *options)
 
 void diff_set_default_prefix(struct diff_options *options)
 {
-       options->a_prefix = "a/";
-       options->b_prefix = "b/";
+       options->a_prefix = diff_src_prefix;
+       options->b_prefix = diff_dst_prefix;
 }
 
 struct userdiff_driver *get_textconv(struct repository *r,
@@ -5362,6 +5370,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/";
        diff_set_default_prefix(options);
        return 0;
 }
diff --git a/dir.c b/dir.c
index ac699542302cece1d1aea426cd4cc37940e6fead..20ebe4cba2687e027765876ddc57b086fa85dd71 100644 (file)
--- a/dir.c
+++ b/dir.c
@@ -3918,6 +3918,26 @@ void untracked_cache_invalidate_path(struct index_state *istate,
                                 path, strlen(path));
 }
 
+void untracked_cache_invalidate_trimmed_path(struct index_state *istate,
+                                            const char *path,
+                                            int safe_path)
+{
+       size_t len = strlen(path);
+
+       if (!len)
+               BUG("untracked_cache_invalidate_trimmed_path given zero length path");
+
+       if (path[len - 1] != '/') {
+               untracked_cache_invalidate_path(istate, path, safe_path);
+       } else {
+               struct strbuf tmp = STRBUF_INIT;
+
+               strbuf_add(&tmp, path, len - 1);
+               untracked_cache_invalidate_path(istate, tmp.buf, safe_path);
+               strbuf_release(&tmp);
+       }
+}
+
 void untracked_cache_remove_from_index(struct index_state *istate,
                                       const char *path)
 {
diff --git a/dir.h b/dir.h
index 98aa85fcc0ee357a2df50014008c3e5ec12acb25..45a7b9ec5f2d52214e8000dbd68913056a1a2d77 100644 (file)
--- a/dir.h
+++ b/dir.h
@@ -576,6 +576,13 @@ int cmp_dir_entry(const void *p1, const void *p2);
 int check_dir_entry_contains(const struct dir_entry *out, const struct dir_entry *in);
 
 void untracked_cache_invalidate_path(struct index_state *, const char *, int safe_path);
+/*
+ * Invalidate the untracked-cache for this path, but first strip
+ * off a trailing slash, if present.
+ */
+void untracked_cache_invalidate_trimmed_path(struct index_state *,
+                                            const char *path,
+                                            int safe_path);
 void untracked_cache_remove_from_index(struct index_state *, const char *);
 void untracked_cache_add_to_index(struct index_state *, const char *);
 
diff --git a/fsck.c b/fsck.c
index 8ded0a473a47fb45c223d8736f58c599b66cbb93..78af29d26459e1d392d8a64ff72dd5fed575daff 100644 (file)
--- a/fsck.c
+++ b/fsck.c
@@ -327,7 +327,8 @@ static int fsck_walk_tree(struct tree *tree, void *data, struct fsck_options *op
                return -1;
 
        name = fsck_get_object_name(options, &tree->object.oid);
-       if (init_tree_desc_gently(&desc, tree->buffer, tree->size, 0))
+       if (init_tree_desc_gently(&desc, &tree->object.oid,
+                                 tree->buffer, tree->size, 0))
                return -1;
        while (tree_entry_gently(&desc, &entry)) {
                struct object *obj;
@@ -598,7 +599,8 @@ static int fsck_tree(const struct object_id *tree_oid,
        const char *o_name;
        struct name_stack df_dup_candidates = { NULL };
 
-       if (init_tree_desc_gently(&desc, buffer, size, TREE_DESC_RAW_MODES)) {
+       if (init_tree_desc_gently(&desc, tree_oid, buffer, size,
+                                 TREE_DESC_RAW_MODES)) {
                retval += report(options, tree_oid, OBJ_TREE,
                                 FSCK_MSG_BAD_TREE,
                                 "cannot be parsed as a tree");
index f670c50937898342f693708c706a0db270be3a6d..2b17d60bbbecb0e0e53a934b4376e2207649e826 100644 (file)
@@ -5,6 +5,7 @@
 #include "ewah/ewok.h"
 #include "fsmonitor.h"
 #include "fsmonitor-ipc.h"
+#include "name-hash.h"
 #include "run-command.h"
 #include "strbuf.h"
 #include "trace2.h"
@@ -183,79 +184,282 @@ static int query_fsmonitor_hook(struct repository *r,
        return result;
 }
 
-static void fsmonitor_refresh_callback(struct index_state *istate, char *name)
+/*
+ * Invalidate the FSM bit on this CE.  This is like mark_fsmonitor_invalid()
+ * but we've already handled the untracked-cache, so let's not repeat that
+ * work.  This also lets us have a different trace message so that we can
+ * see everything that was done as part of the refresh-callback.
+ */
+static void invalidate_ce_fsm(struct cache_entry *ce)
 {
-       int i, len = strlen(name);
-       int pos = index_name_pos(istate, name, len);
+       if (ce->ce_flags & CE_FSMONITOR_VALID) {
+               trace_printf_key(&trace_fsmonitor,
+                                "fsmonitor_refresh_callback INV: '%s'",
+                                ce->name);
+               ce->ce_flags &= ~CE_FSMONITOR_VALID;
+       }
+}
+
+static size_t handle_path_with_trailing_slash(
+       struct index_state *istate, const char *name, int pos);
+
+/*
+ * Use the name-hash to do a case-insensitive cache-entry lookup with
+ * the pathname and invalidate the cache-entry.
+ *
+ * Returns the number of cache-entries that we invalidated.
+ */
+static size_t handle_using_name_hash_icase(
+       struct index_state *istate, const char *name)
+{
+       struct cache_entry *ce = NULL;
+
+       ce = index_file_exists(istate, name, strlen(name), 1);
+       if (!ce)
+               return 0;
 
+       /*
+        * A case-insensitive search in the name-hash using the
+        * observed pathname found a cache-entry, so the observed path
+        * is case-incorrect.  Invalidate the cache-entry and use the
+        * correct spelling from the cache-entry to invalidate the
+        * untracked-cache.  Since we now have sparse-directories in
+        * the index, the observed pathname may represent a regular
+        * file or a sparse-index directory.
+        *
+        * Note that we should not have seen FSEvents for a
+        * sparse-index directory, but we handle it just in case.
+        *
+        * Either way, we know that there are not any cache-entries for
+        * children inside the cone of the directory, so we don't need to
+        * do the usual scan.
+        */
        trace_printf_key(&trace_fsmonitor,
-                        "fsmonitor_refresh_callback '%s' (pos %d)",
-                        name, pos);
+                        "fsmonitor_refresh_callback MAP: '%s' '%s'",
+                        name, ce->name);
 
-       if (name[len - 1] == '/') {
-               /*
-                * The daemon can decorate directory events, such as
-                * moves or renames, with a trailing slash if the OS
-                * FS Event contains sufficient information, such as
-                * MacOS.
-                *
-                * Use this to invalidate the entire cone under that
-                * directory.
-                *
-                * We do not expect an exact match because the index
-                * does not normally contain directory entries, so we
-                * start at the insertion point and scan.
-                */
-               if (pos < 0)
-                       pos = -pos - 1;
+       /*
+        * NEEDSWORK: We used the name-hash to find the correct
+        * case-spelling of the pathname in the cache-entry[], so
+        * technically this is a tracked file or a sparse-directory.
+        * It should not have any entries in the untracked-cache, so
+        * we should not need to use the case-corrected spelling to
+        * invalidate the the untracked-cache.  So we may not need to
+        * do this.  For now, I'm going to be conservative and always
+        * do it; we can revisit this later.
+        */
+       untracked_cache_invalidate_trimmed_path(istate, ce->name, 0);
 
-               /* Mark all entries for the folder invalid */
-               for (i = pos; i < istate->cache_nr; i++) {
-                       if (!starts_with(istate->cache[i]->name, name))
-                               break;
-                       istate->cache[i]->ce_flags &= ~CE_FSMONITOR_VALID;
-               }
+       invalidate_ce_fsm(ce);
+       return 1;
+}
+
+/*
+ * Use the dir-name-hash to find the correct-case spelling of the
+ * directory.  Use the canonical spelling to invalidate all of the
+ * cache-entries within the matching cone.
+ *
+ * Returns the number of cache-entries that we invalidated.
+ */
+static size_t handle_using_dir_name_hash_icase(
+       struct index_state *istate, const char *name)
+{
+       struct strbuf canonical_path = STRBUF_INIT;
+       int pos;
+       size_t len = strlen(name);
+       size_t nr_in_cone;
+
+       if (name[len - 1] == '/')
+               len--;
+
+       if (!index_dir_find(istate, name, len, &canonical_path))
+               return 0; /* name is untracked */
 
+       if (!memcmp(name, canonical_path.buf, canonical_path.len)) {
+               strbuf_release(&canonical_path);
                /*
-                * We need to remove the traling "/" from the path
-                * for the untracked cache.
+                * NEEDSWORK: Our caller already tried an exact match
+                * and failed to find one.  They called us to do an
+                * ICASE match, so we should never get an exact match,
+                * so we could promote this to a BUG() here if we
+                * wanted to.  It doesn't hurt anything to just return
+                * 0 and go on because we should never get here.  Or we
+                * could just get rid of the memcmp() and this "if"
+                * clause completely.
                 */
-               name[len - 1] = '\0';
-       } else if (pos >= 0) {
+               BUG("handle_using_dir_name_hash_icase(%s) did not exact match",
+                   name);
+       }
+
+       trace_printf_key(&trace_fsmonitor,
+                        "fsmonitor_refresh_callback MAP: '%s' '%s'",
+                        name, canonical_path.buf);
+
+       /*
+        * The dir-name-hash only tells us the corrected spelling of
+        * the prefix.  We have to use this canonical path to do a
+        * lookup in the cache-entry array so that we repeat the
+        * original search using the case-corrected spelling.
+        */
+       strbuf_addch(&canonical_path, '/');
+       pos = index_name_pos(istate, canonical_path.buf,
+                            canonical_path.len);
+       nr_in_cone = handle_path_with_trailing_slash(
+               istate, canonical_path.buf, pos);
+       strbuf_release(&canonical_path);
+       return nr_in_cone;
+}
+
+/*
+ * The daemon sent an observed pathname without a trailing slash.
+ * (This is the normal case.)  We do not know if it is a tracked or
+ * untracked file, a sparse-directory, or a populated directory (on a
+ * platform such as Windows where FSEvents are not qualified).
+ *
+ * The pathname contains the observed case reported by the FS. We
+ * do not know it is case-correct or -incorrect.
+ *
+ * Assume it is case-correct and try an exact match.
+ *
+ * Return the number of cache-entries that we invalidated.
+ */
+static size_t handle_path_without_trailing_slash(
+       struct index_state *istate, const char *name, int pos)
+{
+       /*
+        * Mark the untracked cache dirty for this path (regardless of
+        * whether or not we find an exact match for it in the index).
+        * Since the path is unqualified (no trailing slash hint in the
+        * FSEvent), it may refer to a file or directory. So we should
+        * not assume one or the other and should always let the untracked
+        * cache decide what needs to invalidated.
+        */
+       untracked_cache_invalidate_trimmed_path(istate, name, 0);
+
+       if (pos >= 0) {
                /*
-                * We have an exact match for this path and can just
-                * invalidate it.
+                * An exact match on a tracked file. We assume that we
+                * do not need to scan forward for a sparse-directory
+                * cache-entry with the same pathname, nor for a cone
+                * at that directory. (That is, assume no D/F conflicts.)
                 */
-               istate->cache[pos]->ce_flags &= ~CE_FSMONITOR_VALID;
+               invalidate_ce_fsm(istate->cache[pos]);
+               return 1;
        } else {
+               size_t nr_in_cone;
+               struct strbuf work_path = STRBUF_INIT;
+
                /*
-                * The path is not a tracked file -or- it is a
-                * directory event on a platform that cannot
-                * distinguish between file and directory events in
-                * the event handler, such as Windows.
-                *
-                * Scan as if it is a directory and invalidate the
-                * cone under it.  (But remember to ignore items
-                * between "name" and "name/", such as "name-" and
-                * "name.".
+                * The negative "pos" gives us the suggested insertion
+                * point for the pathname (without the trailing slash).
+                * We need to see if there is a directory with that
+                * prefix, but there can be lots of pathnames between
+                * "foo" and "foo/" like "foo-" or "foo-bar", so we
+                * don't want to do our own scan.
                 */
+               strbuf_add(&work_path, name, strlen(name));
+               strbuf_addch(&work_path, '/');
+               pos = index_name_pos(istate, work_path.buf, work_path.len);
+               nr_in_cone = handle_path_with_trailing_slash(
+                       istate, work_path.buf, pos);
+               strbuf_release(&work_path);
+               return nr_in_cone;
+       }
+}
+
+/*
+ * The daemon can decorate directory events, such as a move or rename,
+ * by adding a trailing slash to the observed name.  Use this to
+ * explicitly invalidate the entire cone under that directory.
+ *
+ * The daemon can only reliably do that if the OS FSEvent contains
+ * sufficient information in the event.
+ *
+ * macOS FSEvents have enough information.
+ *
+ * Other platforms may or may not be able to do it (and it might
+ * depend on the type of event (for example, a daemon could lstat() an
+ * observed pathname after a rename, but not after a delete)).
+ *
+ * If we find an exact match in the index for a path with a trailing
+ * slash, it means that we matched a sparse-index directory in a
+ * cone-mode sparse-checkout (since that's the only time we have
+ * directories in the index).  We should never see this in practice
+ * (because sparse directories should not be present and therefore
+ * not generating FS events).  Either way, we can treat them in the
+ * same way and just invalidate the cache-entry and the untracked
+ * cache (and in this case, the forward cache-entry scan won't find
+ * anything and it doesn't hurt to let it run).
+ *
+ * Return the number of cache-entries that we invalidated.  We will
+ * use this later to determine if we need to attempt a second
+ * case-insensitive search on case-insensitive file systems.  That is,
+ * if the search using the observed-case in the FSEvent yields any
+ * results, we assume the prefix is case-correct.  If there are no
+ * matches, we still don't know if the observed path is simply
+ * untracked or case-incorrect.
+ */
+static size_t handle_path_with_trailing_slash(
+       struct index_state *istate, const char *name, int pos)
+{
+       int i;
+       size_t nr_in_cone = 0;
+
+       /*
+        * Mark the untracked cache dirty for this directory path
+        * (regardless of whether or not we find an exact match for it
+        * in the index or find it to be proper prefix of one or more
+        * files in the index), since the FSEvent is hinting that
+        * there may be changes on or within the directory.
+        */
+       untracked_cache_invalidate_trimmed_path(istate, name, 0);
+
+       if (pos < 0)
                pos = -pos - 1;
 
-               for (i = pos; i < istate->cache_nr; i++) {
-                       if (!starts_with(istate->cache[i]->name, name))
-                               break;
-                       if ((unsigned char)istate->cache[i]->name[len] > '/')
-                               break;
-                       if (istate->cache[i]->name[len] == '/')
-                               istate->cache[i]->ce_flags &= ~CE_FSMONITOR_VALID;
-               }
+       /* Mark all entries for the folder invalid */
+       for (i = pos; i < istate->cache_nr; i++) {
+               if (!starts_with(istate->cache[i]->name, name))
+                       break;
+               invalidate_ce_fsm(istate->cache[i]);
+               nr_in_cone++;
        }
 
+       return nr_in_cone;
+}
+
+static void fsmonitor_refresh_callback(struct index_state *istate, char *name)
+{
+       int len = strlen(name);
+       int pos = index_name_pos(istate, name, len);
+       size_t nr_in_cone;
+
+       trace_printf_key(&trace_fsmonitor,
+                        "fsmonitor_refresh_callback '%s' (pos %d)",
+                        name, pos);
+
+       if (name[len - 1] == '/')
+               nr_in_cone = handle_path_with_trailing_slash(istate, name, pos);
+       else
+               nr_in_cone = handle_path_without_trailing_slash(istate, name, pos);
+
        /*
-        * Mark the untracked cache dirty even if it wasn't found in the index
-        * as it could be a new untracked file.
+        * If we did not find an exact match for this pathname or any
+        * cache-entries with this directory prefix and we're on a
+        * case-insensitive file system, try again using the name-hash
+        * and dir-name-hash.
         */
-       untracked_cache_invalidate_path(istate, name, 0);
+       if (!nr_in_cone && ignore_case) {
+               nr_in_cone = handle_using_name_hash_icase(istate, name);
+               if (!nr_in_cone)
+                       nr_in_cone = handle_using_dir_name_hash_icase(
+                               istate, name);
+       }
+
+       if (nr_in_cone)
+               trace_printf_key(&trace_fsmonitor,
+                                "fsmonitor_refresh_callback CNT: %d",
+                                (int)nr_in_cone);
 }
 
 /*
index e3d390974331e83261ba32e757724723b9bea724..eb34cda4092aaf0a396893cd424ca358d93b36aa 100755 (executable)
@@ -148,7 +148,7 @@ do
        if [ -z "$dry_run" ] ; then
                git apply --index -C1 ${level:+"$level"} "$tmp_patch" &&
                tree=$(git write-tree) &&
-               commit=$( (echo "$SUBJECT"; echo; cat "$tmp_msg") | git commit-tree $tree -p $commit) &&
+               commit=$( { echo "$SUBJECT"; echo; cat "$tmp_msg"; } | git commit-tree $tree -p $commit) &&
                git update-ref -m "quiltimport: $patch_name" HEAD $commit || exit 4
        fi
 done 3<"$QUILT_SERIES"
diff --git a/git.c b/git.c
index 5265f920f165d99f01584eed54554dd51563acca..654d615a18845185e2ece17a7ca6229cea431a07 100644 (file)
--- a/git.c
+++ b/git.c
@@ -379,8 +379,6 @@ static int handle_alias(int *argcp, const char ***argv)
                        strvec_pushv(&child.args, (*argv) + 1);
 
                        trace2_cmd_alias(alias_command, child.args.v);
-                       trace2_cmd_list_config();
-                       trace2_cmd_list_env_vars();
                        trace2_cmd_name("_run_shell_alias_");
 
                        ret = run_command(&child);
@@ -417,8 +415,6 @@ static int handle_alias(int *argcp, const char ***argv)
                COPY_ARRAY(new_argv + count, *argv + 1, *argcp);
 
                trace2_cmd_alias(alias_command, new_argv);
-               trace2_cmd_list_config();
-               trace2_cmd_list_env_vars();
 
                *argv = new_argv;
                *argcp += count - 1;
@@ -468,8 +464,6 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv)
 
        trace_argv_printf(argv, "trace: built-in: git");
        trace2_cmd_name(p->cmd);
-       trace2_cmd_list_config();
-       trace2_cmd_list_env_vars();
 
        validate_cache_entries(the_repository->index);
        status = p->fn(argc, argv, prefix);
index 10d84cc208886187d044b0b69fbfc397f4d28864..2cfde63ae1cf029e9700185247fc833c82d54407 100644 (file)
--- a/hash-ll.h
+++ b/hash-ll.h
@@ -145,6 +145,7 @@ struct object_id {
 #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 | \
diff --git a/hash.h b/hash.h
index 615ae0691d070b4bdc110fecad3a0c484d2b234c..e064807c17333ab1ab5572adb4f331305bcad954 100644 (file)
--- a/hash.h
+++ b/hash.h
@@ -73,10 +73,15 @@ static inline void oidclr(struct object_id *oid)
        oid->algo = hash_algo_by_ptr(the_hash_algo);
 }
 
+static inline void oidread_algop(struct object_id *oid, const unsigned char *hash, const struct git_hash_algo *algop)
+{
+       memcpy(oid->hash, hash, algop->rawsz);
+       oid->algo = hash_algo_by_ptr(algop);
+}
+
 static inline void oidread(struct object_id *oid, const unsigned char *hash)
 {
-       memcpy(oid->hash, hash, the_hash_algo->rawsz);
-       oid->algo = hash_algo_by_ptr(the_hash_algo);
+       oidread_algop(oid, hash, the_hash_algo);
 }
 
 static inline int is_empty_blob_sha1(const unsigned char *sha1)
index 33db41bfac5f977747ce7af864207df3f255e491..1fe51226fd28a84fefe621cd2cf0a593afa1105f 100644 (file)
@@ -1307,7 +1307,7 @@ static struct object_list **process_tree(struct tree *tree,
        obj->flags |= SEEN;
        p = add_one_object(obj, p);
 
-       init_tree_desc(&desc, tree->buffer, tree->size);
+       init_tree_desc(&desc, &tree->object.oid, tree->buffer, tree->size);
 
        while (tree_entry(&desc, &entry))
                switch (object_type(entry.mode)) {
index f39b68faf54889144abd81e2c1f9c6f19edd8d4f..11ad8be411eeb29b3f1bb0fd798ab71e71791014 100644 (file)
@@ -102,7 +102,7 @@ static void process_tree_contents(struct traversal_context *ctx,
        enum interesting match = ctx->revs->diffopt.pathspec.nr == 0 ?
                all_entries_interesting : entry_not_interesting;
 
-       init_tree_desc(&desc, tree->buffer, tree->size);
+       init_tree_desc(&desc, &tree->object.oid, tree->buffer, tree->size);
 
        while (tree_entry(&desc, &entry)) {
                if (match != all_entries_interesting) {
index 90af4e66b28c8f338cb62cf228a2b8d80000e8a8..1bb99264976d27095f348caf0fc35efe5598348b 100644 (file)
@@ -321,11 +321,11 @@ static inline int commit_lock_file_to(struct lock_file *lk, const char *path)
  * Roll back `lk`: close the file descriptor and/or file pointer and
  * remove the lockfile. It is a NOOP to call `rollback_lock_file()`
  * for a `lock_file` object that has already been committed or rolled
- * back.
+ * back. No error will be returned in this case.
  */
-static inline void rollback_lock_file(struct lock_file *lk)
+static inline int rollback_lock_file(struct lock_file *lk)
 {
-       delete_tempfile(&lk->tempfile);
+       return delete_tempfile(&lk->tempfile);
 }
 
 #endif /* LOCKFILE_H */
diff --git a/loose.c b/loose.c
new file mode 100644 (file)
index 0000000..f6faa62
--- /dev/null
+++ b/loose.c
@@ -0,0 +1,259 @@
+#include "git-compat-util.h"
+#include "hash.h"
+#include "path.h"
+#include "object-store.h"
+#include "hex.h"
+#include "wrapper.h"
+#include "gettext.h"
+#include "loose.h"
+#include "lockfile.h"
+#include "oidtree.h"
+
+static const char *loose_object_header = "# loose-object-idx\n";
+
+static inline int should_use_loose_object_map(struct repository *repo)
+{
+       return repo->compat_hash_algo && repo->gitdir;
+}
+
+void loose_object_map_init(struct loose_object_map **map)
+{
+       struct loose_object_map *m;
+       m = xmalloc(sizeof(**map));
+       m->to_compat = kh_init_oid_map();
+       m->to_storage = kh_init_oid_map();
+       *map = m;
+}
+
+static int insert_oid_pair(kh_oid_map_t *map, const struct object_id *key, const struct object_id *value)
+{
+       khiter_t pos;
+       int ret;
+       struct object_id *stored;
+
+       pos = kh_put_oid_map(map, *key, &ret);
+
+       /* This item already exists in the map. */
+       if (ret == 0)
+               return 0;
+
+       stored = xmalloc(sizeof(*stored));
+       oidcpy(stored, value);
+       kh_value(map, pos) = stored;
+       return 1;
+}
+
+static int insert_loose_map(struct object_directory *odb,
+                           const struct object_id *oid,
+                           const struct object_id *compat_oid)
+{
+       struct loose_object_map *map = odb->loose_map;
+       int inserted = 0;
+
+       inserted |= insert_oid_pair(map->to_compat, oid, compat_oid);
+       inserted |= insert_oid_pair(map->to_storage, compat_oid, oid);
+       if (inserted)
+               oidtree_insert(odb->loose_objects_cache, compat_oid);
+
+       return inserted;
+}
+
+static int load_one_loose_object_map(struct repository *repo, struct object_directory *dir)
+{
+       struct strbuf buf = STRBUF_INIT, path = STRBUF_INIT;
+       FILE *fp;
+
+       if (!dir->loose_map)
+               loose_object_map_init(&dir->loose_map);
+       if (!dir->loose_objects_cache) {
+               ALLOC_ARRAY(dir->loose_objects_cache, 1);
+               oidtree_init(dir->loose_objects_cache);
+       }
+
+       insert_loose_map(dir, repo->hash_algo->empty_tree, repo->compat_hash_algo->empty_tree);
+       insert_loose_map(dir, repo->hash_algo->empty_blob, repo->compat_hash_algo->empty_blob);
+       insert_loose_map(dir, repo->hash_algo->null_oid, repo->compat_hash_algo->null_oid);
+
+       strbuf_git_common_path(&path, repo, "objects/loose-object-idx");
+       fp = fopen(path.buf, "rb");
+       if (!fp) {
+               strbuf_release(&path);
+               return 0;
+       }
+
+       errno = 0;
+       if (strbuf_getwholeline(&buf, fp, '\n') || strcmp(buf.buf, loose_object_header))
+               goto err;
+       while (!strbuf_getline_lf(&buf, fp)) {
+               const char *p;
+               struct object_id oid, compat_oid;
+               if (parse_oid_hex_algop(buf.buf, &oid, &p, repo->hash_algo) ||
+                   *p++ != ' ' ||
+                   parse_oid_hex_algop(p, &compat_oid, &p, repo->compat_hash_algo) ||
+                   p != buf.buf + buf.len)
+                       goto err;
+               insert_loose_map(dir, &oid, &compat_oid);
+       }
+
+       strbuf_release(&buf);
+       strbuf_release(&path);
+       return errno ? -1 : 0;
+err:
+       strbuf_release(&buf);
+       strbuf_release(&path);
+       return -1;
+}
+
+int repo_read_loose_object_map(struct repository *repo)
+{
+       struct object_directory *dir;
+
+       if (!should_use_loose_object_map(repo))
+               return 0;
+
+       prepare_alt_odb(repo);
+
+       for (dir = repo->objects->odb; dir; dir = dir->next) {
+               if (load_one_loose_object_map(repo, dir) < 0) {
+                       return -1;
+               }
+       }
+       return 0;
+}
+
+int repo_write_loose_object_map(struct repository *repo)
+{
+       kh_oid_map_t *map = repo->objects->odb->loose_map->to_compat;
+       struct lock_file lock;
+       int fd;
+       khiter_t iter;
+       struct strbuf buf = STRBUF_INIT, path = STRBUF_INIT;
+
+       if (!should_use_loose_object_map(repo))
+               return 0;
+
+       strbuf_git_common_path(&path, repo, "objects/loose-object-idx");
+       fd = hold_lock_file_for_update_timeout(&lock, path.buf, LOCK_DIE_ON_ERROR, -1);
+       iter = kh_begin(map);
+       if (write_in_full(fd, loose_object_header, strlen(loose_object_header)) < 0)
+               goto errout;
+
+       for (; iter != kh_end(map); iter++) {
+               if (kh_exist(map, iter)) {
+                       if (oideq(&kh_key(map, iter), the_hash_algo->empty_tree) ||
+                           oideq(&kh_key(map, iter), the_hash_algo->empty_blob))
+                               continue;
+                       strbuf_addf(&buf, "%s %s\n", oid_to_hex(&kh_key(map, iter)), oid_to_hex(kh_value(map, iter)));
+                       if (write_in_full(fd, buf.buf, buf.len) < 0)
+                               goto errout;
+                       strbuf_reset(&buf);
+               }
+       }
+       strbuf_release(&buf);
+       if (commit_lock_file(&lock) < 0) {
+               error_errno(_("could not write loose object index %s"), path.buf);
+               strbuf_release(&path);
+               return -1;
+       }
+       strbuf_release(&path);
+       return 0;
+errout:
+       rollback_lock_file(&lock);
+       strbuf_release(&buf);
+       error_errno(_("failed to write loose object index %s\n"), path.buf);
+       strbuf_release(&path);
+       return -1;
+}
+
+static int write_one_object(struct repository *repo, const struct object_id *oid,
+                           const struct object_id *compat_oid)
+{
+       struct lock_file lock;
+       int fd;
+       struct stat st;
+       struct strbuf buf = STRBUF_INIT, path = STRBUF_INIT;
+
+       strbuf_git_common_path(&path, repo, "objects/loose-object-idx");
+       hold_lock_file_for_update_timeout(&lock, path.buf, LOCK_DIE_ON_ERROR, -1);
+
+       fd = open(path.buf, O_WRONLY | O_CREAT | O_APPEND, 0666);
+       if (fd < 0)
+               goto errout;
+       if (fstat(fd, &st) < 0)
+               goto errout;
+       if (!st.st_size && write_in_full(fd, loose_object_header, strlen(loose_object_header)) < 0)
+               goto errout;
+
+       strbuf_addf(&buf, "%s %s\n", oid_to_hex(oid), oid_to_hex(compat_oid));
+       if (write_in_full(fd, buf.buf, buf.len) < 0)
+               goto errout;
+       if (close(fd))
+               goto errout;
+       adjust_shared_perm(path.buf);
+       rollback_lock_file(&lock);
+       strbuf_release(&buf);
+       strbuf_release(&path);
+       return 0;
+errout:
+       error_errno(_("failed to write loose object index %s\n"), path.buf);
+       close(fd);
+       rollback_lock_file(&lock);
+       strbuf_release(&buf);
+       strbuf_release(&path);
+       return -1;
+}
+
+int repo_add_loose_object_map(struct repository *repo, const struct object_id *oid,
+                             const struct object_id *compat_oid)
+{
+       int inserted = 0;
+
+       if (!should_use_loose_object_map(repo))
+               return 0;
+
+       inserted = insert_loose_map(repo->objects->odb, oid, compat_oid);
+       if (inserted)
+               return write_one_object(repo, oid, compat_oid);
+       return 0;
+}
+
+int repo_loose_object_map_oid(struct repository *repo,
+                             const struct object_id *src,
+                             const struct git_hash_algo *to,
+                             struct object_id *dest)
+{
+       struct object_directory *dir;
+       kh_oid_map_t *map;
+       khiter_t pos;
+
+       for (dir = repo->objects->odb; dir; dir = dir->next) {
+               struct loose_object_map *loose_map = dir->loose_map;
+               if (!loose_map)
+                       continue;
+               map = (to == repo->compat_hash_algo) ?
+                       loose_map->to_compat :
+                       loose_map->to_storage;
+               pos = kh_get_oid_map(map, *src);
+               if (pos < kh_end(map)) {
+                       oidcpy(dest, kh_value(map, pos));
+                       return 0;
+               }
+       }
+       return -1;
+}
+
+void loose_object_map_clear(struct loose_object_map **map)
+{
+       struct loose_object_map *m = *map;
+       struct object_id *oid;
+
+       if (!m)
+               return;
+
+       kh_foreach_value(m->to_compat, oid, free(oid));
+       kh_foreach_value(m->to_storage, oid, free(oid));
+       kh_destroy_oid_map(m->to_compat);
+       kh_destroy_oid_map(m->to_storage);
+       free(m);
+       *map = NULL;
+}
diff --git a/loose.h b/loose.h
new file mode 100644 (file)
index 0000000..2c29570
--- /dev/null
+++ b/loose.h
@@ -0,0 +1,22 @@
+#ifndef LOOSE_H
+#define LOOSE_H
+
+#include "khash.h"
+
+struct loose_object_map {
+       kh_oid_map_t *to_compat;
+       kh_oid_map_t *to_storage;
+};
+
+void loose_object_map_init(struct loose_object_map **map);
+void loose_object_map_clear(struct loose_object_map **map);
+int repo_loose_object_map_oid(struct repository *repo,
+                             const struct object_id *src,
+                             const struct git_hash_algo *dest_algo,
+                             struct object_id *dest);
+int repo_add_loose_object_map(struct repository *repo, const struct object_id *oid,
+                             const struct object_id *compat_oid);
+int repo_read_loose_object_map(struct repository *repo);
+int repo_write_loose_object_map(struct repository *repo);
+
+#endif
index 0885ac681cd5055f80fb5088b04c92eed8e7f291..3412b6a1401df13f25069999e7384396558fbd34 100644 (file)
@@ -63,7 +63,7 @@ static void *fill_tree_desc_strict(struct tree_desc *desc,
                die("unable to read tree (%s)", oid_to_hex(hash));
        if (type != OBJ_TREE)
                die("%s is not a tree", oid_to_hex(hash));
-       init_tree_desc(desc, buffer, size);
+       init_tree_desc(desc, hash, buffer, size);
        return buffer;
 }
 
@@ -194,7 +194,7 @@ static int splice_tree(const struct object_id *oid1, const char *prefix,
        buf = repo_read_object_file(the_repository, oid1, &type, &sz);
        if (!buf)
                die("cannot read tree %s", oid_to_hex(oid1));
-       init_tree_desc(&desc, buf, sz);
+       init_tree_desc(&desc, oid1, buf, sz);
 
        rewrite_here = NULL;
        while (desc.size) {
index 201f8f77755b439ad1b91b5b692c16aaeba5d032..ac225cc33c27d30629fad541ba4375d5707ec11c 100644 (file)
@@ -1665,9 +1665,10 @@ static int collect_merge_info(struct merge_options *opt,
            parse_tree(side1) < 0 ||
            parse_tree(side2) < 0)
                return -1;
-       init_tree_desc(t + 0, merge_base->buffer, merge_base->size);
-       init_tree_desc(t + 1, side1->buffer, side1->size);
-       init_tree_desc(t + 2, side2->buffer, side2->size);
+       init_tree_desc(t + 0, &merge_base->object.oid,
+                      merge_base->buffer, merge_base->size);
+       init_tree_desc(t + 1, &side1->object.oid, side1->buffer, side1->size);
+       init_tree_desc(t + 2, &side2->object.oid, side2->buffer, side2->size);
 
        trace2_region_enter("merge", "traverse_trees", opt->repo);
        ret = traverse_trees(NULL, 3, t, &info);
@@ -4446,10 +4447,10 @@ static int checkout(struct merge_options *opt,
        unpack_opts.preserve_ignored = 0; /* FIXME: !opts->overwrite_ignore */
        if (parse_tree(prev) < 0)
                return -1;
-       init_tree_desc(&trees[0], prev->buffer, prev->size);
+       init_tree_desc(&trees[0], &prev->object.oid, prev->buffer, prev->size);
        if (parse_tree(next) < 0)
                return -1;
-       init_tree_desc(&trees[1], next->buffer, next->size);
+       init_tree_desc(&trees[1], &next->object.oid, next->buffer, next->size);
 
        ret = unpack_trees(2, trees, &unpack_opts);
        clear_unpack_trees_porcelain(&unpack_opts);
index 103ee321aeb8cd8c169d35316bb20cfd9eea1693..69d67bef5a96da9e9b843267a74a84f29eef70c7 100644 (file)
@@ -407,7 +407,7 @@ static void init_tree_desc_from_tree(struct tree_desc *desc, struct tree *tree)
 {
        if (parse_tree(tree) < 0)
                exit(128);
-       init_tree_desc(desc, tree->buffer, tree->size);
+       init_tree_desc(desc, &tree->object.oid, tree->buffer, tree->size);
 }
 
 static int unpack_trees_start(struct merge_options *opt,
diff --git a/merge.c b/merge.c
index 563281b10f1d04bd6b51a79f2852a61aa49552fd..752a937fa93dd3bf8703400c6311afe7a5265e02 100644 (file)
--- a/merge.c
+++ b/merge.c
@@ -81,7 +81,8 @@ int checkout_fast_forward(struct repository *r,
                        rollback_lock_file(&lock_file);
                        return -1;
                }
-               init_tree_desc(t+i, trees[i]->buffer, trees[i]->size);
+               init_tree_desc(t+i, &trees[i]->object.oid,
+                              trees[i]->buffer, trees[i]->size);
        }
 
        memset(&opts, 0, sizeof(opts));
index 251f036eef6983a66ac13013e5bee3ef770e8f9f..3a58ce03d9c4a6721941f0ff8b262b6ddb73acd2 100644 (file)
@@ -685,13 +685,20 @@ static int same_name(const struct cache_entry *ce, const char *name, int namelen
        return slow_same_name(name, namelen, ce->name, len);
 }
 
-int index_dir_exists(struct index_state *istate, const char *name, int namelen)
+int index_dir_find(struct index_state *istate, const char *name, int namelen,
+                  struct strbuf *canonical_path)
 {
        struct dir_entry *dir;
 
        lazy_init_name_hash(istate);
        expand_to_path(istate, name, namelen, 0);
        dir = find_dir_entry(istate, name, namelen);
+
+       if (canonical_path && dir && dir->nr) {
+               strbuf_reset(canonical_path);
+               strbuf_add(canonical_path, dir->name, dir->namelen);
+       }
+
        return dir && dir->nr;
 }
 
index b1b4b0fb337f12eb5878dbedcaa66ae394cba425..0cbfc4286316b244a0902c7fffd1168ba320d52f 100644 (file)
@@ -4,7 +4,12 @@
 struct cache_entry;
 struct index_state;
 
-int index_dir_exists(struct index_state *istate, const char *name, int namelen);
+
+int index_dir_find(struct index_state *istate, const char *name, int namelen,
+                  struct strbuf *canonical_path);
+
+#define index_dir_exists(i, n, l) index_dir_find((i), (n), (l), NULL)
+
 void adjust_dirname_case(struct index_state *istate, char *name);
 struct cache_entry *index_file_exists(struct index_state *istate, const char *name, int namelen, int igncase);
 
diff --git a/object-file-convert.c b/object-file-convert.c
new file mode 100644 (file)
index 0000000..4f61890
--- /dev/null
@@ -0,0 +1,277 @@
+#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 "object.h"
+#include "loose.h"
+#include "commit.h"
+#include "gpg-interface.h"
+#include "object-file-convert.h"
+
+int repo_oid_to_algop(struct repository *repo, const struct object_id *src,
+                     const struct git_hash_algo *to, struct object_id *dest)
+{
+       /*
+        * If the source algorithm is not set, then we're using the
+        * default hash algorithm for that object.
+        */
+       const struct git_hash_algo *from =
+               src->algo ? &hash_algos[src->algo] : repo->hash_algo;
+
+       if (from == to) {
+               if (src != dest)
+                       oidcpy(dest, src);
+               return 0;
+       }
+       if (repo_loose_object_map_oid(repo, src, to, dest)) {
+               /*
+                * We may have loaded the object map at repo initialization but
+                * another process (perhaps upstream of a pipe from us) may have
+                * written a new object into the map.  If the object is missing,
+                * let's reload the map to see if the object has appeared.
+                */
+               repo_read_loose_object_map(repo);
+               if (repo_loose_object_map_oid(repo, src, to, dest))
+                       return -1;
+       }
+       return 0;
+}
+
+static int decode_tree_entry_raw(struct object_id *oid, const char **path,
+                                size_t *len, const struct git_hash_algo *algo,
+                                const char *buf, unsigned long size)
+{
+       uint16_t mode;
+       const unsigned hashsz = algo->rawsz;
+
+       if (size < hashsz + 3 || buf[size - (hashsz + 1)]) {
+               return -1;
+       }
+
+       *path = parse_mode(buf, &mode);
+       if (!*path || !**path)
+               return -1;
+       *len = strlen(*path) + 1;
+
+       oidread_algop(oid, (const unsigned char *)*path + *len, algo);
+       return 0;
+}
+
+static int convert_tree_object(struct strbuf *out,
+                              const struct git_hash_algo *from,
+                              const struct git_hash_algo *to,
+                              const char *buffer, size_t size)
+{
+       const char *p = buffer, *end = buffer + size;
+
+       while (p < end) {
+               struct object_id entry_oid, mapped_oid;
+               const char *path = NULL;
+               size_t pathlen;
+
+               if (decode_tree_entry_raw(&entry_oid, &path, &pathlen, from, p,
+                                         end - p))
+                       return error(_("failed to decode tree entry"));
+               if (repo_oid_to_algop(the_repository, &entry_oid, to, &mapped_oid))
+                       return error(_("failed to map tree entry for %s"), oid_to_hex(&entry_oid));
+               strbuf_add(out, p, path - p);
+               strbuf_add(out, path, pathlen);
+               strbuf_add(out, mapped_oid.hash, to->rawsz);
+               p = path + pathlen + from->rawsz;
+       }
+       return 0;
+}
+
+static int convert_tag_object(struct strbuf *out,
+                             const struct git_hash_algo *from,
+                             const struct git_hash_algo *to,
+                             const char *buffer, size_t size)
+{
+       struct strbuf payload = STRBUF_INIT, oursig = STRBUF_INIT, othersig = STRBUF_INIT;
+       const int entry_len = from->hexsz + 7;
+       size_t payload_size;
+       struct object_id oid, mapped_oid;
+       const char *p;
+
+       /* Consume the object line */
+       if ((entry_len >= size) ||
+           memcmp(buffer, "object ", 7) || buffer[entry_len] != '\n')
+               return error("bogus tag object");
+       if (parse_oid_hex_algop(buffer + 7, &oid, &p, from) < 0)
+               return error("bad tag object ID");
+       if (repo_oid_to_algop(the_repository, &oid, to, &mapped_oid))
+               return error("unable to map tree %s in tag object",
+                            oid_to_hex(&oid));
+       size -= ((p + 1) - buffer);
+       buffer = p + 1;
+
+       /* Is there a signature for our algorithm? */
+       payload_size = parse_signed_buffer(buffer, size);
+       if (payload_size != size) {
+               /* Yes, there is. */
+               strbuf_add(&oursig, buffer + payload_size, size - payload_size);
+       }
+
+       /* Now, is there a signature for the other algorithm? */
+       parse_buffer_signed_by_header(buffer, payload_size, &payload, &othersig, to);
+       /*
+        * Our payload is now in payload and we may have up to two signatrures
+        * in oursig and othersig.
+        */
+
+       /* Add some slop for longer signature header in the new algorithm. */
+       strbuf_grow(out, (7 + to->hexsz + 1) + size + 7);
+       strbuf_addf(out, "object %s\n", oid_to_hex(&mapped_oid));
+       strbuf_addbuf(out, &payload);
+       if (oursig.len)
+               add_header_signature(out, &oursig, from);
+       strbuf_addbuf(out, &othersig);
+
+       strbuf_release(&payload);
+       strbuf_release(&othersig);
+       strbuf_release(&oursig);
+       return 0;
+}
+
+static int convert_commit_object(struct strbuf *out,
+                                const struct git_hash_algo *from,
+                                const struct git_hash_algo *to,
+                                const char *buffer, size_t size)
+{
+       const char *tail = buffer;
+       const char *bufptr = buffer;
+       const int tree_entry_len = from->hexsz + 5;
+       const int parent_entry_len = from->hexsz + 7;
+       struct object_id oid, mapped_oid;
+       const char *p, *eol;
+
+       tail += size;
+
+       while ((bufptr < tail) && (*bufptr != '\n')) {
+               eol = memchr(bufptr, '\n', tail - bufptr);
+               if (!eol)
+                       return error(_("bad %s in commit"), "line");
+
+               if (((bufptr + 5) < eol) && !memcmp(bufptr, "tree ", 5))
+               {
+                       if (((bufptr + tree_entry_len) != eol) ||
+                           parse_oid_hex_algop(bufptr + 5, &oid, &p, from) ||
+                           (p != eol))
+                               return error(_("bad %s in commit"), "tree");
+
+                       if (repo_oid_to_algop(the_repository, &oid, to, &mapped_oid))
+                               return error(_("unable to map %s %s in commit object"),
+                                            "tree", oid_to_hex(&oid));
+                       strbuf_addf(out, "tree %s\n", oid_to_hex(&mapped_oid));
+               }
+               else if (((bufptr + 7) < eol) && !memcmp(bufptr, "parent ", 7))
+               {
+                       if (((bufptr + parent_entry_len) != eol) ||
+                           parse_oid_hex_algop(bufptr + 7, &oid, &p, from) ||
+                           (p != eol))
+                               return error(_("bad %s in commit"), "parent");
+
+                       if (repo_oid_to_algop(the_repository, &oid, to, &mapped_oid))
+                               return error(_("unable to map %s %s in commit object"),
+                                            "parent", oid_to_hex(&oid));
+
+                       strbuf_addf(out, "parent %s\n", oid_to_hex(&mapped_oid));
+               }
+               else if (((bufptr + 9) < eol) && !memcmp(bufptr, "mergetag ", 9))
+               {
+                       struct strbuf tag = STRBUF_INIT, new_tag = STRBUF_INIT;
+
+                       /* Recover the tag object from the mergetag */
+                       strbuf_add(&tag, bufptr + 9, (eol - (bufptr + 9)) + 1);
+
+                       bufptr = eol + 1;
+                       while ((bufptr < tail) && (*bufptr == ' ')) {
+                               eol = memchr(bufptr, '\n', tail - bufptr);
+                               if (!eol) {
+                                       strbuf_release(&tag);
+                                       return error(_("bad %s in commit"), "mergetag continuation");
+                               }
+                               strbuf_add(&tag, bufptr + 1, (eol - (bufptr + 1)) + 1);
+                               bufptr = eol + 1;
+                       }
+
+                       /* Compute the new tag object */
+                       if (convert_tag_object(&new_tag, from, to, tag.buf, tag.len)) {
+                               strbuf_release(&tag);
+                               strbuf_release(&new_tag);
+                               return -1;
+                       }
+
+                       /* Write the new mergetag */
+                       strbuf_addstr(out, "mergetag");
+                       strbuf_add_lines(out, " ", new_tag.buf, new_tag.len);
+                       strbuf_release(&tag);
+                       strbuf_release(&new_tag);
+               }
+               else if (((bufptr + 7) < tail) && !memcmp(bufptr, "author ", 7))
+                       strbuf_add(out, bufptr, (eol - bufptr) + 1);
+               else if (((bufptr + 10) < tail) && !memcmp(bufptr, "committer ", 10))
+                       strbuf_add(out, bufptr, (eol - bufptr) + 1);
+               else if (((bufptr + 9) < tail) && !memcmp(bufptr, "encoding ", 9))
+                       strbuf_add(out, bufptr, (eol - bufptr) + 1);
+               else if (((bufptr + 6) < tail) && !memcmp(bufptr, "gpgsig", 6))
+                       strbuf_add(out, bufptr, (eol - bufptr) + 1);
+               else {
+                       /* Unknown line fail it might embed an oid */
+                       return -1;
+               }
+               /* Consume any trailing continuation lines */
+               bufptr = eol + 1;
+               while ((bufptr < tail) && (*bufptr == ' ')) {
+                       eol = memchr(bufptr, '\n', tail - bufptr);
+                       if (!eol)
+                               return error(_("bad %s in commit"), "continuation");
+                       strbuf_add(out, bufptr, (eol - bufptr) + 1);
+                       bufptr = eol + 1;
+               }
+       }
+       if (bufptr < tail)
+               strbuf_add(out, bufptr, tail - bufptr);
+       return 0;
+}
+
+int convert_object_file(struct strbuf *outbuf,
+                       const struct git_hash_algo *from,
+                       const struct git_hash_algo *to,
+                       const void *buf, size_t len,
+                       enum object_type type,
+                       int gentle)
+{
+       int ret;
+
+       /* Don't call this function when no conversion is necessary */
+       if ((from == to) || (type == OBJ_BLOB))
+               BUG("Refusing noop object file conversion");
+
+       switch (type) {
+       case OBJ_COMMIT:
+               ret = convert_commit_object(outbuf, from, to, buf, len);
+               break;
+       case OBJ_TREE:
+               ret = convert_tree_object(outbuf, from, to, buf, len);
+               break;
+       case OBJ_TAG:
+               ret = convert_tag_object(outbuf, from, to, buf, len);
+               break;
+       default:
+               /* Not implemented yet, so fail. */
+               ret = -1;
+               break;
+       }
+       if (!ret)
+               return 0;
+       if (gentle) {
+               strbuf_release(outbuf);
+               return ret;
+       }
+       die(_("Failed to convert object from %s to %s"),
+               from->name, to->name);
+}
diff --git a/object-file-convert.h b/object-file-convert.h
new file mode 100644 (file)
index 0000000..a4f802a
--- /dev/null
@@ -0,0 +1,24 @@
+#ifndef OBJECT_CONVERT_H
+#define OBJECT_CONVERT_H
+
+struct repository;
+struct object_id;
+struct git_hash_algo;
+struct strbuf;
+#include "object.h"
+
+int repo_oid_to_algop(struct repository *repo, const struct object_id *src,
+                     const struct git_hash_algo *to, struct object_id *dest);
+
+/*
+ * Convert an object file from one hash algorithm to another algorithm.
+ * Return -1 on failure, 0 on success.
+ */
+int convert_object_file(struct strbuf *outbuf,
+                       const struct git_hash_algo *from,
+                       const struct git_hash_algo *to,
+                       const void *buf, size_t len,
+                       enum object_type type,
+                       int gentle);
+
+#endif /* OBJECT_CONVERT_H */
index 619f039ebc7ceb52467e4810c5c4cf3afabb907a..610b1f465c4248e8c0520687049a707a8497195e 100644 (file)
@@ -35,6 +35,8 @@
 #include "setup.h"
 #include "submodule.h"
 #include "fsck.h"
+#include "loose.h"
+#include "object-file-convert.h"
 
 /* The maximum size for an object header. */
 #define MAX_HEADER_LEN 32
@@ -1084,9 +1086,11 @@ int check_object_signature(struct repository *r, const struct object_id *oid,
                           void *buf, unsigned long size,
                           enum object_type type)
 {
+       const struct git_hash_algo *algo =
+               oid->algo ? &hash_algos[oid->algo] : r->hash_algo;
        struct object_id real_oid;
 
-       hash_object_file(r->hash_algo, buf, size, type, &real_oid);
+       hash_object_file(algo, buf, size, type, &real_oid);
 
        return !oideq(oid, &real_oid) ? -1 : 0;
 }
@@ -1652,10 +1656,101 @@ static int do_oid_object_info_extended(struct repository *r,
        return 0;
 }
 
+static int oid_object_info_convert(struct repository *r,
+                                  const struct object_id *input_oid,
+                                  struct object_info *input_oi, unsigned flags)
+{
+       const struct git_hash_algo *input_algo = &hash_algos[input_oid->algo];
+       int do_die = flags & OBJECT_INFO_DIE_IF_CORRUPT;
+       struct strbuf type_name = STRBUF_INIT;
+       struct object_id oid, delta_base_oid;
+       struct object_info new_oi, *oi;
+       unsigned long size;
+       void *content;
+       int ret;
+
+       if (repo_oid_to_algop(r, input_oid, the_hash_algo, &oid)) {
+               if (do_die)
+                       die(_("missing mapping of %s to %s"),
+                           oid_to_hex(input_oid), the_hash_algo->name);
+               return -1;
+       }
+
+       /* Is new_oi needed? */
+       oi = input_oi;
+       if (input_oi && (input_oi->delta_base_oid || input_oi->sizep ||
+                        input_oi->contentp)) {
+               new_oi = *input_oi;
+               /* Does delta_base_oid need to be converted? */
+               if (input_oi->delta_base_oid)
+                       new_oi.delta_base_oid = &delta_base_oid;
+               /* Will the attributes differ when converted? */
+               if (input_oi->sizep || input_oi->contentp) {
+                       new_oi.contentp = &content;
+                       new_oi.sizep = &size;
+                       new_oi.type_name = &type_name;
+               }
+               oi = &new_oi;
+       }
+
+       ret = oid_object_info_extended(r, &oid, oi, flags);
+       if (ret)
+               return -1;
+       if (oi == input_oi)
+               return ret;
+
+       if (new_oi.contentp) {
+               struct strbuf outbuf = STRBUF_INIT;
+               enum object_type type;
+
+               type = type_from_string_gently(type_name.buf, type_name.len,
+                                              !do_die);
+               if (type == -1)
+                       return -1;
+               if (type != OBJ_BLOB) {
+                       ret = convert_object_file(&outbuf,
+                                                 the_hash_algo, input_algo,
+                                                 content, size, type, !do_die);
+                       if (ret == -1)
+                               return -1;
+                       free(content);
+                       size = outbuf.len;
+                       content = strbuf_detach(&outbuf, NULL);
+               }
+               if (input_oi->sizep)
+                       *input_oi->sizep = size;
+               if (input_oi->contentp)
+                       *input_oi->contentp = content;
+               else
+                       free(content);
+               if (input_oi->type_name)
+                       *input_oi->type_name = type_name;
+               else
+                       strbuf_release(&type_name);
+       }
+       if (new_oi.delta_base_oid == &delta_base_oid) {
+               if (repo_oid_to_algop(r, &delta_base_oid, input_algo,
+                                input_oi->delta_base_oid)) {
+                       if (do_die)
+                               die(_("missing mapping of %s to %s"),
+                                   oid_to_hex(&delta_base_oid),
+                                   input_algo->name);
+                       return -1;
+               }
+       }
+       input_oi->whence = new_oi.whence;
+       input_oi->u = new_oi.u;
+       return ret;
+}
+
 int oid_object_info_extended(struct repository *r, const struct object_id *oid,
                             struct object_info *oi, unsigned flags)
 {
        int ret;
+
+       if (oid->algo && (hash_algo_by_ptr(r->hash_algo) != oid->algo))
+               return oid_object_info_convert(r, oid, oi, flags);
+
        obj_read_lock();
        ret = do_oid_object_info_extended(r, oid, oi, flags);
        obj_read_unlock();
@@ -1944,9 +2039,12 @@ static int start_loose_object_common(struct strbuf *tmp_file,
                                     const char *filename, unsigned flags,
                                     git_zstream *stream,
                                     unsigned char *buf, size_t buflen,
-                                    git_hash_ctx *c,
+                                    git_hash_ctx *c, git_hash_ctx *compat_c,
                                     char *hdr, int hdrlen)
 {
+       struct repository *repo = the_repository;
+       const struct git_hash_algo *algo = repo->hash_algo;
+       const struct git_hash_algo *compat = repo->compat_hash_algo;
        int fd;
 
        fd = create_tmpfile(tmp_file, filename);
@@ -1966,14 +2064,18 @@ static int start_loose_object_common(struct strbuf *tmp_file,
        git_deflate_init(stream, zlib_compression_level);
        stream->next_out = buf;
        stream->avail_out = buflen;
-       the_hash_algo->init_fn(c);
+       algo->init_fn(c);
+       if (compat && compat_c)
+               compat->init_fn(compat_c);
 
        /*  Start to feed header to zlib stream */
        stream->next_in = (unsigned char *)hdr;
        stream->avail_in = hdrlen;
        while (git_deflate(stream, 0) == Z_OK)
                ; /* nothing */
-       the_hash_algo->update_fn(c, hdr, hdrlen);
+       algo->update_fn(c, hdr, hdrlen);
+       if (compat && compat_c)
+               compat->update_fn(compat_c, hdr, hdrlen);
 
        return fd;
 }
@@ -1982,16 +2084,21 @@ static int start_loose_object_common(struct strbuf *tmp_file,
  * Common steps for the inner git_deflate() loop for writing loose
  * objects. Returns what git_deflate() returns.
  */
-static int write_loose_object_common(git_hash_ctx *c,
+static int write_loose_object_common(git_hash_ctx *c, git_hash_ctx *compat_c,
                                     git_zstream *stream, const int flush,
                                     unsigned char *in0, const int fd,
                                     unsigned char *compressed,
                                     const size_t compressed_len)
 {
+       struct repository *repo = the_repository;
+       const struct git_hash_algo *algo = repo->hash_algo;
+       const struct git_hash_algo *compat = repo->compat_hash_algo;
        int ret;
 
        ret = git_deflate(stream, flush ? Z_FINISH : 0);
-       the_hash_algo->update_fn(c, in0, stream->next_in - in0);
+       algo->update_fn(c, in0, stream->next_in - in0);
+       if (compat && compat_c)
+               compat->update_fn(compat_c, in0, stream->next_in - in0);
        if (write_in_full(fd, compressed, stream->next_out - compressed) < 0)
                die_errno(_("unable to write loose object file"));
        stream->next_out = compressed;
@@ -2006,15 +2113,21 @@ static int write_loose_object_common(git_hash_ctx *c,
  * - End the compression of zlib stream.
  * - Get the calculated oid to "oid".
  */
-static int end_loose_object_common(git_hash_ctx *c, git_zstream *stream,
-                                  struct object_id *oid)
+static int end_loose_object_common(git_hash_ctx *c, git_hash_ctx *compat_c,
+                                  git_zstream *stream, struct object_id *oid,
+                                  struct object_id *compat_oid)
 {
+       struct repository *repo = the_repository;
+       const struct git_hash_algo *algo = repo->hash_algo;
+       const struct git_hash_algo *compat = repo->compat_hash_algo;
        int ret;
 
        ret = git_deflate_end_gently(stream);
        if (ret != Z_OK)
                return ret;
-       the_hash_algo->final_oid_fn(oid, c);
+       algo->final_oid_fn(oid, c);
+       if (compat && compat_c)
+               compat->final_oid_fn(compat_oid, compat_c);
 
        return Z_OK;
 }
@@ -2038,7 +2151,7 @@ static int write_loose_object(const struct object_id *oid, char *hdr,
 
        fd = start_loose_object_common(&tmp_file, filename.buf, flags,
                                       &stream, compressed, sizeof(compressed),
-                                      &c, hdr, hdrlen);
+                                      &c, NULL, hdr, hdrlen);
        if (fd < 0)
                return -1;
 
@@ -2048,14 +2161,14 @@ static int write_loose_object(const struct object_id *oid, char *hdr,
        do {
                unsigned char *in0 = stream.next_in;
 
-               ret = write_loose_object_common(&c, &stream, 1, in0, fd,
+               ret = write_loose_object_common(&c, NULL, &stream, 1, in0, fd,
                                                compressed, sizeof(compressed));
        } while (ret == Z_OK);
 
        if (ret != Z_STREAM_END)
                die(_("unable to deflate new object %s (%d)"), oid_to_hex(oid),
                    ret);
-       ret = end_loose_object_common(&c, &stream, &parano_oid);
+       ret = end_loose_object_common(&c, NULL, &stream, &parano_oid, NULL);
        if (ret != Z_OK)
                die(_("deflateEnd on object %s failed (%d)"), oid_to_hex(oid),
                    ret);
@@ -2100,10 +2213,12 @@ static int freshen_packed_object(const struct object_id *oid)
 int stream_loose_object(struct input_stream *in_stream, size_t len,
                        struct object_id *oid)
 {
+       const struct git_hash_algo *compat = the_repository->compat_hash_algo;
+       struct object_id compat_oid;
        int fd, ret, err = 0, flush = 0;
        unsigned char compressed[4096];
        git_zstream stream;
-       git_hash_ctx c;
+       git_hash_ctx c, compat_c;
        struct strbuf tmp_file = STRBUF_INIT;
        struct strbuf filename = STRBUF_INIT;
        int dirlen;
@@ -2127,7 +2242,7 @@ int stream_loose_object(struct input_stream *in_stream, size_t len,
         */
        fd = start_loose_object_common(&tmp_file, filename.buf, 0,
                                       &stream, compressed, sizeof(compressed),
-                                      &c, hdr, hdrlen);
+                                      &c, &compat_c, hdr, hdrlen);
        if (fd < 0) {
                err = -1;
                goto cleanup;
@@ -2145,7 +2260,7 @@ int stream_loose_object(struct input_stream *in_stream, size_t len,
                        if (in_stream->is_finished)
                                flush = 1;
                }
-               ret = write_loose_object_common(&c, &stream, flush, in0, fd,
+               ret = write_loose_object_common(&c, &compat_c, &stream, flush, in0, fd,
                                                compressed, sizeof(compressed));
                /*
                 * Unlike write_loose_object(), we do not have the entire
@@ -2168,7 +2283,7 @@ int stream_loose_object(struct input_stream *in_stream, size_t len,
         */
        if (ret != Z_STREAM_END)
                die(_("unable to stream deflate new object (%d)"), ret);
-       ret = end_loose_object_common(&c, &stream, oid);
+       ret = end_loose_object_common(&c, &compat_c, &stream, oid, &compat_oid);
        if (ret != Z_OK)
                die(_("deflateEnd on stream object failed (%d)"), ret);
        close_loose_object(fd, tmp_file.buf);
@@ -2195,6 +2310,8 @@ int stream_loose_object(struct input_stream *in_stream, size_t len,
        }
 
        err = finalize_object_file(tmp_file.buf, filename.buf);
+       if (!err && compat)
+               err = repo_add_loose_object_map(the_repository, oid, &compat_oid);
 cleanup:
        strbuf_release(&tmp_file);
        strbuf_release(&filename);
@@ -2203,19 +2320,42 @@ cleanup:
 
 int write_object_file_flags(const void *buf, unsigned long len,
                            enum object_type type, struct object_id *oid,
-                           unsigned flags)
+                           struct object_id *compat_oid_in, unsigned flags)
 {
+       struct repository *repo = the_repository;
+       const struct git_hash_algo *algo = repo->hash_algo;
+       const struct git_hash_algo *compat = repo->compat_hash_algo;
+       struct object_id compat_oid;
        char hdr[MAX_HEADER_LEN];
        int hdrlen = sizeof(hdr);
 
+       /* Generate compat_oid */
+       if (compat) {
+               if (compat_oid_in)
+                       oidcpy(&compat_oid, compat_oid_in);
+               else if (type == OBJ_BLOB)
+                       hash_object_file(compat, buf, len, type, &compat_oid);
+               else {
+                       struct strbuf converted = STRBUF_INIT;
+                       convert_object_file(&converted, algo, compat,
+                                           buf, len, type, 0);
+                       hash_object_file(compat, converted.buf, converted.len,
+                                        type, &compat_oid);
+                       strbuf_release(&converted);
+               }
+       }
+
        /* Normally if we have it in the pack then we do not bother writing
         * it out into .git/objects/??/?{38} file.
         */
-       write_object_file_prepare(the_hash_algo, buf, len, type, oid, hdr,
-                                 &hdrlen);
+       write_object_file_prepare(algo, buf, len, type, oid, hdr, &hdrlen);
        if (freshen_packed_object(oid) || freshen_loose_object(oid))
                return 0;
-       return write_loose_object(oid, hdr, hdrlen, buf, len, 0, flags);
+       if (write_loose_object(oid, hdr, hdrlen, buf, len, 0, flags))
+               return -1;
+       if (compat)
+               return repo_add_loose_object_map(repo, oid, &compat_oid);
+       return 0;
 }
 
 int write_object_file_literally(const void *buf, unsigned long len,
@@ -2223,7 +2363,27 @@ int write_object_file_literally(const void *buf, unsigned long len,
                                unsigned flags)
 {
        char *header;
+       struct repository *repo = the_repository;
+       const struct git_hash_algo *algo = repo->hash_algo;
+       const struct git_hash_algo *compat = repo->compat_hash_algo;
+       struct object_id compat_oid;
        int hdrlen, status = 0;
+       int compat_type = -1;
+
+       if (compat) {
+               compat_type = type_from_string_gently(type, -1, 1);
+               if (compat_type == OBJ_BLOB)
+                       hash_object_file(compat, buf, len, compat_type,
+                                        &compat_oid);
+               else if (compat_type != -1) {
+                       struct strbuf converted = STRBUF_INIT;
+                       convert_object_file(&converted, algo, compat,
+                                           buf, len, compat_type, 0);
+                       hash_object_file(compat, converted.buf, converted.len,
+                                        compat_type, &compat_oid);
+                       strbuf_release(&converted);
+               }
+       }
 
        /* type string, SP, %lu of the length plus NUL must fit this */
        hdrlen = strlen(type) + MAX_HEADER_LEN;
@@ -2236,6 +2396,8 @@ int write_object_file_literally(const void *buf, unsigned long len,
        if (freshen_packed_object(oid) || freshen_loose_object(oid))
                goto cleanup;
        status = write_loose_object(oid, header, hdrlen, buf, len, 0, 0);
+       if (compat_type != -1)
+               return repo_add_loose_object_map(repo, oid, &compat_oid);
 
 cleanup:
        free(header);
@@ -2244,9 +2406,12 @@ cleanup:
 
 int force_object_loose(const struct object_id *oid, time_t mtime)
 {
+       struct repository *repo = the_repository;
+       const struct git_hash_algo *compat = repo->compat_hash_algo;
        void *buf;
        unsigned long len;
        struct object_info oi = OBJECT_INFO_INIT;
+       struct object_id compat_oid;
        enum object_type type;
        char hdr[MAX_HEADER_LEN];
        int hdrlen;
@@ -2259,8 +2424,15 @@ int force_object_loose(const struct object_id *oid, time_t mtime)
        oi.contentp = &buf;
        if (oid_object_info_extended(the_repository, oid, &oi, 0))
                return error(_("cannot read object for %s"), oid_to_hex(oid));
+       if (compat) {
+               if (repo_oid_to_algop(repo, oid, compat, &compat_oid))
+                       return error(_("cannot map object %s to %s"),
+                                    oid_to_hex(oid), compat->name);
+       }
        hdrlen = format_object_header(hdr, sizeof(hdr), type, len);
        ret = write_loose_object(oid, hdr, hdrlen, buf, len, mtime, 0);
+       if (!ret && compat)
+               ret = repo_add_loose_object_map(the_repository, oid, &compat_oid);
        free(buf);
 
        return ret;
index bd77695d7eac539ff114c4b6a031cdd81931d7d8..523af6f64f33512d1d7587bf0ce96cbe23b63b94 100644 (file)
@@ -23,6 +23,7 @@
 #include "midx.h"
 #include "commit-reach.h"
 #include "date.h"
+#include "object-file-convert.h"
 
 static int get_oid_oneline(struct repository *r, const char *, struct object_id *, struct commit_list *);
 
@@ -47,6 +48,7 @@ struct disambiguate_state {
 
 static void update_candidates(struct disambiguate_state *ds, const struct object_id *current)
 {
+       /* The hash algorithm of current has already been filtered */
        if (ds->always_call_fn) {
                ds->ambiguous = ds->fn(ds->repo, current, ds->cb_data) ? 1 : 0;
                return;
@@ -132,6 +134,8 @@ static void unique_in_midx(struct multi_pack_index *m,
 {
        uint32_t num, i, first = 0;
        const struct object_id *current = NULL;
+       int len = ds->len > ds->repo->hash_algo->hexsz ?
+               ds->repo->hash_algo->hexsz : ds->len;
        num = m->num_objects;
 
        if (!num)
@@ -147,7 +151,7 @@ static void unique_in_midx(struct multi_pack_index *m,
        for (i = first; i < num && !ds->ambiguous; i++) {
                struct object_id oid;
                current = nth_midxed_object_oid(&oid, m, i);
-               if (!match_hash(ds->len, ds->bin_pfx.hash, current->hash))
+               if (!match_hash(len, ds->bin_pfx.hash, current->hash))
                        break;
                update_candidates(ds, current);
        }
@@ -157,6 +161,8 @@ static void unique_in_pack(struct packed_git *p,
                           struct disambiguate_state *ds)
 {
        uint32_t num, i, first = 0;
+       int len = ds->len > ds->repo->hash_algo->hexsz ?
+               ds->repo->hash_algo->hexsz : ds->len;
 
        if (p->multi_pack_index)
                return;
@@ -175,7 +181,7 @@ static void unique_in_pack(struct packed_git *p,
        for (i = first; i < num && !ds->ambiguous; i++) {
                struct object_id oid;
                nth_packed_object_id(&oid, p, i);
-               if (!match_hash(ds->len, ds->bin_pfx.hash, oid.hash))
+               if (!match_hash(len, ds->bin_pfx.hash, oid.hash))
                        break;
                update_candidates(ds, &oid);
        }
@@ -186,6 +192,10 @@ static void find_short_packed_object(struct disambiguate_state *ds)
        struct multi_pack_index *m;
        struct packed_git *p;
 
+       /* Skip, unless oids from the storage hash algorithm are wanted */
+       if (ds->bin_pfx.algo && (&hash_algos[ds->bin_pfx.algo] != ds->repo->hash_algo))
+               return;
+
        for (m = get_multi_pack_index(ds->repo); m && !ds->ambiguous;
             m = m->next)
                unique_in_midx(m, ds);
@@ -324,11 +334,12 @@ int set_disambiguate_hint_config(const char *var, const char *value)
 
 static int init_object_disambiguation(struct repository *r,
                                      const char *name, int len,
+                                     const struct git_hash_algo *algo,
                                      struct disambiguate_state *ds)
 {
        int i;
 
-       if (len < MINIMUM_ABBREV || len > the_hash_algo->hexsz)
+       if (len < MINIMUM_ABBREV || len > GIT_MAX_HEXSZ)
                return -1;
 
        memset(ds, 0, sizeof(*ds));
@@ -355,6 +366,7 @@ static int init_object_disambiguation(struct repository *r,
        ds->len = len;
        ds->hex_pfx[len] = '\0';
        ds->repo = r;
+       ds->bin_pfx.algo = algo ? hash_algo_by_ptr(algo) : GIT_HASH_UNKNOWN;
        prepare_alt_odb(r);
        return 0;
 }
@@ -489,9 +501,10 @@ static int repo_collect_ambiguous(struct repository *r UNUSED,
        return collect_ambiguous(oid, data);
 }
 
-static int sort_ambiguous(const void *a, const void *b, void *ctx)
+static int sort_ambiguous(const void *va, const void *vb, void *ctx)
 {
        struct repository *sort_ambiguous_repo = ctx;
+       const struct object_id *a = va, *b = vb;
        int a_type = oid_object_info(sort_ambiguous_repo, a, NULL);
        int b_type = oid_object_info(sort_ambiguous_repo, b, NULL);
        int a_type_sort;
@@ -501,8 +514,12 @@ static int sort_ambiguous(const void *a, const void *b, void *ctx)
         * Sorts by hash within the same object type, just as
         * oid_array_for_each_unique() would do.
         */
-       if (a_type == b_type)
-               return oidcmp(a, b);
+       if (a_type == b_type) {
+               if (a->algo == b->algo)
+                       return oidcmp(a, b);
+               else
+                       return a->algo > b->algo ? 1 : -1;
+       }
 
        /*
         * Between object types show tags, then commits, and finally
@@ -531,8 +548,12 @@ static enum get_oid_result get_short_oid(struct repository *r,
        int status;
        struct disambiguate_state ds;
        int quietly = !!(flags & GET_OID_QUIETLY);
+       const struct git_hash_algo *algo = r->hash_algo;
+
+       if (flags & GET_OID_HASH_ANY)
+               algo = NULL;
 
-       if (init_object_disambiguation(r, name, len, &ds) < 0)
+       if (init_object_disambiguation(r, name, len, algo, &ds) < 0)
                return -1;
 
        if (HAS_MULTI_BITS(flags & GET_OID_DISAMBIGUATORS))
@@ -586,7 +607,7 @@ static enum get_oid_result get_short_oid(struct repository *r,
                if (!ds.ambiguous)
                        ds.fn = NULL;
 
-               repo_for_each_abbrev(r, ds.hex_pfx, collect_ambiguous, &collect);
+               repo_for_each_abbrev(r, ds.hex_pfx, algo, collect_ambiguous, &collect);
                sort_ambiguous_oid_array(r, &collect);
 
                if (oid_array_for_each(&collect, show_ambiguous_object, &out))
@@ -608,13 +629,14 @@ static enum get_oid_result get_short_oid(struct repository *r,
 }
 
 int repo_for_each_abbrev(struct repository *r, const char *prefix,
+                        const struct git_hash_algo *algo,
                         each_abbrev_fn fn, void *cb_data)
 {
        struct oid_array collect = OID_ARRAY_INIT;
        struct disambiguate_state ds;
        int ret;
 
-       if (init_object_disambiguation(r, prefix, strlen(prefix), &ds) < 0)
+       if (init_object_disambiguation(r, prefix, strlen(prefix), algo, &ds) < 0)
                return -1;
 
        ds.always_call_fn = 1;
@@ -785,10 +807,12 @@ void strbuf_add_unique_abbrev(struct strbuf *sb, const struct object_id *oid,
 int repo_find_unique_abbrev_r(struct repository *r, char *hex,
                              const struct object_id *oid, int len)
 {
+       const struct git_hash_algo *algo =
+               oid->algo ? &hash_algos[oid->algo] : r->hash_algo;
        struct disambiguate_state ds;
        struct min_abbrev_data mad;
        struct object_id oid_ret;
-       const unsigned hexsz = r->hash_algo->hexsz;
+       const unsigned hexsz = algo->hexsz;
 
        if (len < 0) {
                unsigned long count = repo_approximate_object_count(r);
@@ -824,7 +848,7 @@ int repo_find_unique_abbrev_r(struct repository *r, char *hex,
 
        find_abbrev_len_packed(&mad);
 
-       if (init_object_disambiguation(r, hex, mad.cur_len, &ds) < 0)
+       if (init_object_disambiguation(r, hex, mad.cur_len, algo, &ds) < 0)
                return -1;
 
        ds.fn = repo_extend_abbrev_len;
index 9ae52230714876b719dd3d48755ad3885030d07a..064ddc97d1fe991c98272802c9eff65c8445a49b 100644 (file)
@@ -67,7 +67,8 @@ enum get_oid_result get_oid_with_context(struct repository *repo, const char *st
 
 
 typedef int each_abbrev_fn(const struct object_id *oid, void *);
-int repo_for_each_abbrev(struct repository *r, const char *prefix, each_abbrev_fn, void *);
+int repo_for_each_abbrev(struct repository *r, const char *prefix,
+                        const struct git_hash_algo *algo, each_abbrev_fn, void *);
 
 int set_disambiguate_hint_config(const char *var, const char *value);
 
index 26a3895c821c61620d5988b2e62a77dc9513d025..c5f2bb2fc2fe6eb36cafa559b038dcd3095395da 100644 (file)
@@ -26,6 +26,9 @@ struct object_directory {
        uint32_t loose_objects_subdir_seen[8]; /* 256 bits */
        struct oidtree *loose_objects_cache;
 
+       /* Map between object IDs for loose objects. */
+       struct loose_object_map *loose_map;
+
        /*
         * This is a temporary object store created by the tmp_objdir
         * facility. Disable ref updates since the objects in the store
@@ -252,11 +255,11 @@ void hash_object_file(const struct git_hash_algo *algo, const void *buf,
 
 int write_object_file_flags(const void *buf, unsigned long len,
                            enum object_type type, struct object_id *oid,
-                           unsigned flags);
+                           struct object_id *comapt_oid_in, unsigned flags);
 static inline int write_object_file(const void *buf, unsigned long len,
                                    enum object_type type, struct object_id *oid)
 {
-       return write_object_file_flags(buf, len, type, oid, 0);
+       return write_object_file_flags(buf, len, type, oid, NULL, 0);
 }
 
 int write_object_file_literally(const void *buf, unsigned long len,
index f11c59ac0cf4d41e3c19f48e04af47237797db03..51e384828e96efa9b3a9cbf96f5cf8b1b719856e 100644 (file)
--- a/object.c
+++ b/object.c
@@ -13,6 +13,7 @@
 #include "alloc.h"
 #include "packfile.h"
 #include "commit-graph.h"
+#include "loose.h"
 
 unsigned int get_max_object_index(void)
 {
@@ -553,6 +554,7 @@ void free_object_directory(struct object_directory *odb)
 {
        free(odb->path);
        odb_clear_loose_cache(odb);
+       loose_object_map_clear(&odb->loose_map);
        free(odb);
 }
 
index c7123cade622cd2a5b6aafc99ad924aafeaaa301..9293e703cccc6acf6205be9c5fae440fff441d5a 100644 (file)
--- a/object.h
+++ b/object.h
@@ -190,6 +190,24 @@ void *create_object(struct repository *r, const struct object_id *oid, void *obj
 
 void *object_as_type(struct object *obj, enum object_type type, int quiet);
 
+
+static inline const char *parse_mode(const char *str, uint16_t *modep)
+{
+       unsigned char c;
+       unsigned int mode = 0;
+
+       if (*str == ' ')
+               return NULL;
+
+       while ((c = *str++) != ' ') {
+               if (c < '0' || c > '7')
+                       return NULL;
+               mode = (mode << 3) + (c - '0');
+       }
+       *modep = mode;
+       return str;
+}
+
 /*
  * Returns the object, having parsed it to find out what it is.
  *
index 8e4717746c3183bd47c3c746c747efd83c61168c..1f36651754edc47f733bc51bbecd716deee31ed4 100644 (file)
@@ -6,12 +6,20 @@ void oid_array_append(struct oid_array *array, const struct object_id *oid)
 {
        ALLOC_GROW(array->oid, array->nr + 1, array->alloc);
        oidcpy(&array->oid[array->nr++], oid);
+       if (!oid->algo)
+               oid_set_algo(&array->oid[array->nr - 1], the_hash_algo);
        array->sorted = 0;
 }
 
-static int void_hashcmp(const void *a, const void *b)
+static int void_hashcmp(const void *va, const void *vb)
 {
-       return oidcmp(a, b);
+       const struct object_id *a = va, *b = vb;
+       int ret;
+       if (a->algo == b->algo)
+               ret = oidcmp(a, b);
+       else
+               ret = a->algo > b->algo ? 1 : -1;
+       return ret;
 }
 
 void oid_array_sort(struct oid_array *array)
index 990a9498d731942bf2b01580a8ac77d08ba04d5f..c6c8f94cc514476a5a6f543cbb86a260020e9753 100644 (file)
@@ -370,7 +370,7 @@ static int fill_bitmap_tree(struct bitmap *bitmap,
        if (parse_tree(tree) < 0)
                die("unable to load tree object %s",
                    oid_to_hex(&tree->object.oid));
-       init_tree_desc(&desc, tree->buffer, tree->size);
+       init_tree_desc(&desc, &tree->object.oid, tree->buffer, tree->size);
 
        while (tree_entry(&desc, &entry)) {
                switch (object_type(entry.mode)) {
index 84a005674d8749407afffc1f6fd670501ce764d4..d4df7fdeea56ffe63fd1738d227b509bca2d553a 100644 (file)
@@ -2249,7 +2249,8 @@ static int add_promisor_object(const struct object_id *oid,
                struct tree *tree = (struct tree *)obj;
                struct tree_desc desc;
                struct name_entry entry;
-               if (init_tree_desc_gently(&desc, tree->buffer, tree->size, 0))
+               if (init_tree_desc_gently(&desc, &tree->object.oid,
+                                         tree->buffer, tree->size, 0))
                        /*
                         * Error messages are given when packs are
                         * verified, so do not print any here.
index 63a99dea6ef06b19bbb679a3ddd76087abc2a028..30b9e68f8ac85df1c6700d7a512eef618988b06c 100644 (file)
@@ -350,98 +350,107 @@ static int is_alias(struct parse_opt_ctx_t *ctx,
        return 0;
 }
 
+struct parsed_option {
+       const struct option *option;
+       enum opt_parsed flags;
+};
+
+static void register_abbrev(struct parse_opt_ctx_t *p,
+                           const struct option *option, enum opt_parsed flags,
+                           struct parsed_option *abbrev,
+                           struct parsed_option *ambiguous)
+{
+       if (p->flags & PARSE_OPT_KEEP_UNKNOWN_OPT)
+               return;
+       if (abbrev->option &&
+           !(abbrev->flags == flags && is_alias(p, abbrev->option, option))) {
+               /*
+                * If this is abbreviated, it is
+                * ambiguous. So when there is no
+                * exact match later, we need to
+                * error out.
+                */
+               ambiguous->option = abbrev->option;
+               ambiguous->flags = abbrev->flags;
+       }
+       abbrev->option = option;
+       abbrev->flags = flags;
+}
+
 static enum parse_opt_result parse_long_opt(
        struct parse_opt_ctx_t *p, const char *arg,
        const struct option *options)
 {
        const char *arg_end = strchrnul(arg, '=');
-       const struct option *abbrev_option = NULL, *ambiguous_option = NULL;
-       enum opt_parsed abbrev_flags = OPT_LONG, ambiguous_flags = OPT_LONG;
-       int allow_abbrev = !(p->flags & PARSE_OPT_KEEP_UNKNOWN_OPT);
+       const char *arg_start = arg;
+       enum opt_parsed flags = OPT_LONG;
+       int arg_starts_with_no_no = 0;
+       struct parsed_option abbrev = { .option = NULL, .flags = OPT_LONG };
+       struct parsed_option ambiguous = { .option = NULL, .flags = OPT_LONG };
+
+       if (skip_prefix(arg_start, "no-", &arg_start)) {
+               if (skip_prefix(arg_start, "no-", &arg_start))
+                       arg_starts_with_no_no = 1;
+               else
+                       flags |= OPT_UNSET;
+       }
 
        for (; options->type != OPTION_END; options++) {
                const char *rest, *long_name = options->long_name;
-               enum opt_parsed flags = OPT_LONG, opt_flags = OPT_LONG;
+               enum opt_parsed opt_flags = OPT_LONG;
+               int allow_unset = !(options->flags & PARSE_OPT_NONEG);
 
                if (options->type == OPTION_SUBCOMMAND)
                        continue;
                if (!long_name)
                        continue;
 
-               if (!starts_with(arg, "no-") &&
-                   !(options->flags & PARSE_OPT_NONEG) &&
-                   skip_prefix(long_name, "no-", &long_name))
+               if (skip_prefix(long_name, "no-", &long_name))
                        opt_flags |= OPT_UNSET;
+               else if (arg_starts_with_no_no)
+                       continue;
 
-               if (!skip_prefix(arg, long_name, &rest))
-                       rest = NULL;
-               if (!rest) {
-                       /* abbreviated? */
-                       if (allow_abbrev &&
-                           !strncmp(long_name, arg, arg_end - arg)) {
-is_abbreviated:
-                               if (abbrev_option &&
-                                   !is_alias(p, abbrev_option, options)) {
-                                       /*
-                                        * If this is abbreviated, it is
-                                        * ambiguous. So when there is no
-                                        * exact match later, we need to
-                                        * error out.
-                                        */
-                                       ambiguous_option = abbrev_option;
-                                       ambiguous_flags = abbrev_flags;
-                               }
-                               if (!(flags & OPT_UNSET) && *arg_end)
-                                       p->opt = arg_end + 1;
-                               abbrev_option = options;
-                               abbrev_flags = flags ^ opt_flags;
-                               continue;
-                       }
-                       /* negation allowed? */
-                       if (options->flags & PARSE_OPT_NONEG)
-                               continue;
-                       /* negated and abbreviated very much? */
-                       if (allow_abbrev && starts_with("no-", arg)) {
-                               flags |= OPT_UNSET;
-                               goto is_abbreviated;
-                       }
-                       /* negated? */
-                       if (!starts_with(arg, "no-"))
-                               continue;
-                       flags |= OPT_UNSET;
-                       if (!skip_prefix(arg + 3, long_name, &rest)) {
-                               /* abbreviated and negated? */
-                               if (allow_abbrev &&
-                                   starts_with(long_name, arg + 3))
-                                       goto is_abbreviated;
-                               else
-                                       continue;
-                       }
-               }
-               if (*rest) {
-                       if (*rest != '=')
+               if (((flags ^ opt_flags) & OPT_UNSET) && !allow_unset)
+                       continue;
+
+               if (skip_prefix(arg_start, long_name, &rest)) {
+                       if (*rest == '=')
+                               p->opt = rest + 1;
+                       else if (*rest)
                                continue;
-                       p->opt = rest + 1;
+                       return get_value(p, options, flags ^ opt_flags);
                }
-               return get_value(p, options, flags ^ opt_flags);
+
+               /* abbreviated? */
+               if (!strncmp(long_name, arg_start, arg_end - arg_start))
+                       register_abbrev(p, options, flags ^ opt_flags,
+                                       &abbrev, &ambiguous);
+
+               /* negated and abbreviated very much? */
+               if (allow_unset && starts_with("no-", arg))
+                       register_abbrev(p, options, OPT_UNSET ^ opt_flags,
+                                       &abbrev, &ambiguous);
        }
 
-       if (disallow_abbreviated_options && (ambiguous_option || abbrev_option))
+       if (disallow_abbreviated_options && (ambiguous.option || abbrev.option))
                die("disallowed abbreviated or ambiguous option '%.*s'",
                    (int)(arg_end - arg), arg);
 
-       if (ambiguous_option) {
+       if (ambiguous.option) {
                error(_("ambiguous option: %s "
                        "(could be --%s%s or --%s%s)"),
                        arg,
-                       (ambiguous_flags & OPT_UNSET) ?  "no-" : "",
-                       ambiguous_option->long_name,
-                       (abbrev_flags & OPT_UNSET) ?  "no-" : "",
-                       abbrev_option->long_name);
+                       (ambiguous.flags & OPT_UNSET) ?  "no-" : "",
+                       ambiguous.option->long_name,
+                       (abbrev.flags & OPT_UNSET) ?  "no-" : "",
+                       abbrev.option->long_name);
                return PARSE_OPT_HELP;
        }
-       if (abbrev_option)
-               return get_value(p, abbrev_option, abbrev_flags);
+       if (abbrev.option) {
+               if (*arg_end)
+                       p->opt = arg_end + 1;
+               return get_value(p, abbrev.option, abbrev.flags);
+       }
        return PARSE_OPT_UNKNOWN;
 }
 
index cf964b060cd128e2bc271c07ed8bb8b4c28bfe29..bdbed4295aab2f0309eb872170251e2e9b185bbf 100644 (file)
--- a/pretty.c
+++ b/pretty.c
@@ -1759,7 +1759,7 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
                                goto trailer_out;
                }
                if (*arg == ')') {
-                       format_trailers_from_commit(sb, msg + c->subject_off, &opts);
+                       format_trailers_from_commit(&opts, msg + c->subject_off, sb);
                        ret = arg - placeholder + 1;
                }
        trailer_out:
index 0ec29f7385480ad4ee71a6cc684307b2bf19c814..03542d023686f8b9d7089dddef0d17caf6c1ae44 100644 (file)
@@ -1991,7 +1991,7 @@ static void grab_sub_body_contents(struct atom_value *val, int deref, struct exp
                        struct strbuf s = STRBUF_INIT;
 
                        /* Format the trailer info according to the trailer_opts given */
-                       format_trailers_from_commit(&s, subpos, &atom->u.contents.trailer_opts);
+                       format_trailers_from_commit(&atom->u.contents.trailer_opts, subpos, &s);
 
                        v->s = strbuf_detach(&s, NULL);
                } else if (atom->u.contents.option == C_BARE)
index 0a1bc35e8cd2c82771c0272dcdc08f8b99ba73e7..647f3ca398a32711eb940281fc05bad43cb303a6 100644 (file)
--- a/reflog.c
+++ b/reflog.c
@@ -39,7 +39,7 @@ static int tree_is_complete(const struct object_id *oid)
                tree->buffer = data;
                tree->size = size;
        }
-       init_tree_desc(&desc, tree->buffer, tree->size);
+       init_tree_desc(&desc, &tree->object.oid, tree->buffer, tree->size);
        complete = 1;
        while (tree_entry(&desc, &entry)) {
                if (!repo_has_object_file(the_repository, &entry.oid) ||
index 2c88bbd448b18785200a83b7a9e4e794cc364d60..e206d5a073ced9c24d9020f03a674ca207abdd06 100644 (file)
@@ -171,23 +171,6 @@ static int should_write_log(struct ref_store *refs, const char *refname)
        }
 }
 
-static void clear_reftable_log_record(struct reftable_log_record *log)
-{
-       switch (log->value_type) {
-       case REFTABLE_LOG_UPDATE:
-               /*
-                * When we write log records, the hashes are owned by the
-                * caller and thus shouldn't be free'd.
-                */
-               log->value.update.old_hash = NULL;
-               log->value.update.new_hash = NULL;
-               break;
-       case REFTABLE_LOG_DELETION:
-               break;
-       }
-       reftable_log_record_release(log);
-}
-
 static void fill_reftable_log_record(struct reftable_log_record *log)
 {
        const char *info = git_committer_info(0);
@@ -346,6 +329,7 @@ struct reftable_ref_iterator {
        struct object_id oid;
 
        const char *prefix;
+       size_t prefix_len;
        unsigned int flags;
        int err;
 };
@@ -374,8 +358,8 @@ static int reftable_ref_iterator_advance(struct ref_iterator *ref_iterator)
                        continue;
                }
 
-               if (iter->prefix &&
-                   strncmp(iter->prefix, iter->ref.refname, strlen(iter->prefix))) {
+               if (iter->prefix_len &&
+                   strncmp(iter->prefix, iter->ref.refname, iter->prefix_len)) {
                        iter->err = 1;
                        break;
                }
@@ -484,6 +468,7 @@ static struct reftable_ref_iterator *ref_iterator_for_stack(struct reftable_ref_
        iter = xcalloc(1, sizeof(*iter));
        base_ref_iterator_init(&iter->base, &reftable_ref_iterator_vtable);
        iter->prefix = prefix;
+       iter->prefix_len = prefix ? strlen(prefix) : 0;
        iter->base.oid = &iter->oid;
        iter->flags = flags;
        iter->refs = refs;
@@ -1104,8 +1089,8 @@ static int write_transaction_table(struct reftable_writer *writer, void *cb_data
                        fill_reftable_log_record(log);
                        log->update_index = ts;
                        log->refname = xstrdup(u->refname);
-                       log->value.update.new_hash = u->new_oid.hash;
-                       log->value.update.old_hash = tx_update->current_oid.hash;
+                       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);
                }
@@ -1160,7 +1145,7 @@ static int write_transaction_table(struct reftable_writer *writer, void *cb_data
 done:
        assert(ret != REFTABLE_API_ERROR);
        for (i = 0; i < logs_nr; i++)
-               clear_reftable_log_record(&logs[i]);
+               reftable_log_record_release(&logs[i]);
        free(logs);
        return ret;
 }
@@ -1277,13 +1262,13 @@ static int write_create_symref_table(struct reftable_writer *writer, void *cb_da
        log.update_index = ts;
        log.value.update.message = xstrndup(create->logmsg,
                                            create->refs->write_options.block_size / 2);
-       log.value.update.new_hash = new_oid.hash;
+       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))
-               log.value.update.old_hash = old_oid.hash;
+               memcpy(log.value.update.old_hash, old_oid.hash, GIT_MAX_RAWSZ);
 
        ret = reftable_writer_add_log(writer, &log);
-       clear_reftable_log_record(&log);
+       reftable_log_record_release(&log);
        return ret;
 }
 
@@ -1422,7 +1407,7 @@ static int write_copy_table(struct reftable_writer *writer, void *cb_data)
                logs[logs_nr].update_index = deletion_ts;
                logs[logs_nr].value.update.message =
                        xstrndup(arg->logmsg, arg->refs->write_options.block_size / 2);
-               logs[logs_nr].value.update.old_hash = old_ref.value.val1;
+               memcpy(logs[logs_nr].value.update.old_hash, old_ref.value.val1, GIT_MAX_RAWSZ);
                logs_nr++;
 
                ret = read_ref_without_reload(arg->stack, "HEAD", &head_oid, &head_referent, &head_type);
@@ -1454,7 +1439,7 @@ static int write_copy_table(struct reftable_writer *writer, void *cb_data)
        logs[logs_nr].update_index = creation_ts;
        logs[logs_nr].value.update.message =
                xstrndup(arg->logmsg, arg->refs->write_options.block_size / 2);
-       logs[logs_nr].value.update.new_hash = old_ref.value.val1;
+       memcpy(logs[logs_nr].value.update.new_hash, old_ref.value.val1, GIT_MAX_RAWSZ);
        logs_nr++;
 
        /*
@@ -1517,10 +1502,6 @@ done:
        for (i = 0; i < logs_nr; i++) {
                if (!strcmp(logs[i].refname, "HEAD"))
                        continue;
-               if (logs[i].value.update.old_hash == old_ref.value.val1)
-                       logs[i].value.update.old_hash = NULL;
-               if (logs[i].value.update.new_hash == old_ref.value.val1)
-                       logs[i].value.update.new_hash = NULL;
                logs[i].refname = NULL;
                reftable_log_record_release(&logs[i]);
        }
@@ -1598,7 +1579,7 @@ struct reftable_reflog_iterator {
        struct reftable_ref_store *refs;
        struct reftable_iterator iter;
        struct reftable_log_record log;
-       char *last_name;
+       struct strbuf last_name;
        int err;
 };
 
@@ -1617,15 +1598,15 @@ static int reftable_reflog_iterator_advance(struct ref_iterator *ref_iterator)
                 * we've already produced this name. This could be faster by
                 * seeking directly to reflog@update_index==0.
                 */
-               if (iter->last_name && !strcmp(iter->log.refname, iter->last_name))
+               if (!strcmp(iter->log.refname, iter->last_name.buf))
                        continue;
 
                if (check_refname_format(iter->log.refname,
                                         REFNAME_ALLOW_ONELEVEL))
                        continue;
 
-               free(iter->last_name);
-               iter->last_name = xstrdup(iter->log.refname);
+               strbuf_reset(&iter->last_name);
+               strbuf_addstr(&iter->last_name, iter->log.refname);
                iter->base.refname = iter->log.refname;
 
                break;
@@ -1658,7 +1639,7 @@ static int reftable_reflog_iterator_abort(struct ref_iterator *ref_iterator)
                (struct reftable_reflog_iterator *)ref_iterator;
        reftable_log_record_release(&iter->log);
        reftable_iterator_destroy(&iter->iter);
-       free(iter->last_name);
+       strbuf_release(&iter->last_name);
        free(iter);
        return ITER_DONE;
 }
@@ -1678,13 +1659,14 @@ static struct reftable_reflog_iterator *reflog_iterator_for_stack(struct reftabl
 
        iter = xcalloc(1, sizeof(*iter));
        base_ref_iterator_init(&iter->base, &reftable_reflog_iterator_vtable);
+       strbuf_init(&iter->last_name, 0);
        iter->refs = refs;
 
        ret = refs->err;
        if (ret)
                goto done;
 
-       ret = reftable_stack_reload(refs->main_stack);
+       ret = reftable_stack_reload(stack);
        if (ret < 0)
                goto done;
 
@@ -2182,7 +2164,7 @@ static int reftable_be_reflog_expire(struct ref_store *ref_store,
                        dest->value_type = REFTABLE_LOG_DELETION;
                } else {
                        if ((flags & EXPIRE_REFLOGS_REWRITE) && last_hash)
-                               dest->value.update.old_hash = last_hash;
+                               memcpy(dest->value.update.old_hash, last_hash, GIT_MAX_RAWSZ);
                        last_hash = logs[i].value.update.new_hash;
                }
        }
index 72eb73b3806d623010c0e224f4f8ce77d555ce43..e2a2cee58d2a35c6183e7083d3320d2520a397f4 100644 (file)
@@ -291,9 +291,8 @@ static int restart_key_less(size_t idx, void *args)
        /* the restart key is verbatim in the block, so this could avoid the
           alloc for decoding the key */
        struct strbuf rkey = STRBUF_INIT;
-       struct strbuf last_key = STRBUF_INIT;
        uint8_t unused_extra;
-       int n = reftable_decode_key(&rkey, &unused_extra, last_key, in);
+       int n = reftable_decode_key(&rkey, &unused_extra, in);
        int result;
        if (n < 0) {
                a->error = 1;
@@ -302,7 +301,7 @@ static int restart_key_less(size_t idx, void *args)
 
        result = strbuf_cmp(&a->key, &rkey);
        strbuf_release(&rkey);
-       return result;
+       return result < 0;
 }
 
 void block_iter_copy_from(struct block_iter *dest, struct block_iter *src)
@@ -326,35 +325,35 @@ int block_iter_next(struct block_iter *it, struct reftable_record *rec)
        if (it->next_off >= it->br->block_len)
                return 1;
 
-       n = reftable_decode_key(&it->key, &extra, it->last_key, in);
+       n = reftable_decode_key(&it->last_key, &extra, in);
        if (n < 0)
                return -1;
-
-       if (!it->key.len)
+       if (!it->last_key.len)
                return REFTABLE_FORMAT_ERROR;
 
        string_view_consume(&in, n);
-       n = reftable_record_decode(rec, it->key, extra, in, it->br->hash_size);
+       n = reftable_record_decode(rec, it->last_key, extra, in, it->br->hash_size,
+                                  &it->scratch);
        if (n < 0)
                return -1;
        string_view_consume(&in, n);
 
-       strbuf_swap(&it->last_key, &it->key);
        it->next_off += start.len - in.len;
        return 0;
 }
 
 int block_reader_first_key(struct block_reader *br, struct strbuf *key)
 {
-       struct strbuf empty = STRBUF_INIT;
-       int off = br->header_off + 4;
+       int off = br->header_off + 4, n;
        struct string_view in = {
                .buf = br->block.data + off,
                .len = br->block_len - off,
        };
-
        uint8_t extra = 0;
-       int n = reftable_decode_key(key, &extra, empty, in);
+
+       strbuf_reset(key);
+
+       n = reftable_decode_key(key, &extra, in);
        if (n < 0)
                return n;
        if (!key->len)
@@ -371,7 +370,7 @@ int block_iter_seek(struct block_iter *it, struct strbuf *want)
 void block_iter_close(struct block_iter *it)
 {
        strbuf_release(&it->last_key);
-       strbuf_release(&it->key);
+       strbuf_release(&it->scratch);
 }
 
 int block_reader_seek(struct block_reader *br, struct block_iter *it,
@@ -408,8 +407,8 @@ int block_reader_seek(struct block_reader *br, struct block_iter *it,
                if (err < 0)
                        goto done;
 
-               reftable_record_key(&rec, &it->key);
-               if (err > 0 || strbuf_cmp(&it->key, want) >= 0) {
+               reftable_record_key(&rec, &it->last_key);
+               if (err > 0 || strbuf_cmp(&it->last_key, want) >= 0) {
                        err = 0;
                        goto done;
                }
index 17481e6331979cc31972ee3dad576e9c594a1769..47acc62c0ab8cdd6816d49152312b1e61dfe3d6d 100644 (file)
@@ -84,12 +84,12 @@ struct block_iter {
 
        /* key for last entry we read. */
        struct strbuf last_key;
-       struct strbuf key;
+       struct strbuf scratch;
 };
 
 #define BLOCK_ITER_INIT { \
        .last_key = STRBUF_INIT, \
-       .key = STRBUF_INIT, \
+       .scratch = STRBUF_INIT, \
 }
 
 /* initializes a block reader. */
index 8b5ebf6183a97f3ea16a0773880ccd3b835ef68e..7aa30c4a51e54ec5442b9d8a3bf90b8119c5a4f0 100644 (file)
@@ -16,11 +16,6 @@ https://developers.google.com/open-source/licenses/bsd
 #include "reader.h"
 #include "reftable-error.h"
 
-int iterator_is_null(struct reftable_iterator *it)
-{
-       return !it->ops;
-}
-
 static void filtering_ref_iterator_close(void *iter_arg)
 {
        struct filtering_ref_iterator *fri = iter_arg;
index 47d67d84df679c522ce50d1d7aade2d1be683b5a..537431baba075ad72614b1c68eff1fd28fa51288 100644 (file)
@@ -16,10 +16,6 @@ https://developers.google.com/open-source/licenses/bsd
 #include "reftable-iterator.h"
 #include "reftable-generic.h"
 
-/* Returns true for a zeroed out iterator, such as the one returned from
- * iterator_destroy. */
-int iterator_is_null(struct reftable_iterator *it);
-
 /* iterator that produces only ref records that point to `oid` */
 struct filtering_ref_iterator {
        int double_check;
index 1aa6cd31b74954da9b1b5140cd350459c37c1378..f85a24c6786c6fb8e102ffc9ceb35f23706c8c9b 100644 (file)
@@ -17,23 +17,37 @@ https://developers.google.com/open-source/licenses/bsd
 #include "reftable-error.h"
 #include "system.h"
 
+struct merged_subiter {
+       struct reftable_iterator iter;
+       struct reftable_record rec;
+};
+
+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)
 {
        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(&e.rec, mi->typ);
-               err = iterator_next(&mi->stack[i], &e.rec);
+               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) {
-                       reftable_iterator_destroy(&mi->stack[i]);
-                       reftable_record_release(&e.rec);
+               if (err > 0)
                        continue;
-               }
 
                merged_iter_pqueue_add(&mi->pq, &e);
        }
@@ -46,54 +60,66 @@ static void merged_iter_close(void *p)
        struct merged_iter *mi = p;
 
        merged_iter_pqueue_release(&mi->pq);
-       for (size_t i = 0; i < mi->stack_len; i++)
-               reftable_iterator_destroy(&mi->stack[i]);
-       reftable_free(mi->stack);
+       for (size_t i = 0; i < mi->stack_len; i++) {
+               reftable_iterator_destroy(&mi->subiters[i].iter);
+               reftable_record_release(&mi->subiters[i].rec);
+       }
+       reftable_free(mi->subiters);
 }
 
-static int merged_iter_advance_nonnull_subiter(struct merged_iter *mi,
-                                              size_t idx)
+static int merged_iter_advance_subiter(struct merged_iter *mi, size_t idx)
 {
        struct pq_entry e = {
                .index = idx,
+               .rec = &mi->subiters[idx].rec,
        };
        int err;
 
-       reftable_record_init(&e.rec, mi->typ);
-       err = iterator_next(&mi->stack[idx], &e.rec);
-       if (err < 0)
+       err = iterator_next(&mi->subiters[idx].iter, &mi->subiters[idx].rec);
+       if (err)
                return err;
 
-       if (err > 0) {
-               reftable_iterator_destroy(&mi->stack[idx]);
-               reftable_record_release(&e.rec);
-               return 0;
-       }
-
        merged_iter_pqueue_add(&mi->pq, &e);
        return 0;
 }
 
-static int merged_iter_advance_subiter(struct merged_iter *mi, size_t idx)
-{
-       if (iterator_is_null(&mi->stack[idx]))
-               return 0;
-       return merged_iter_advance_nonnull_subiter(mi, idx);
-}
-
 static int merged_iter_next_entry(struct merged_iter *mi,
                                  struct reftable_record *rec)
 {
        struct pq_entry entry = { 0 };
-       int err = 0;
+       int err = 0, empty;
+
+       empty = merged_iter_pqueue_is_empty(mi->pq);
+
+       if (mi->advance_index >= 0) {
+               /*
+                * When there are no pqueue entries then we only have a single
+                * subiter left. There is no need to use the pqueue in that
+                * case anymore as we know that the subiter will return entries
+                * in the correct order already.
+                *
+                * While this may sound like a very specific edge case, it may
+                * happen more frequently than you think. Most repositories
+                * will end up having a single large base table that contains
+                * most of the refs. It's thus likely that we exhaust all
+                * subiters but the one from that base ref.
+                */
+               if (empty)
+                       return iterator_next(&mi->subiters[mi->advance_index].iter,
+                                            rec);
+
+               err = merged_iter_advance_subiter(mi, mi->advance_index);
+               if (err < 0)
+                       return err;
+               if (!err)
+                       empty = 0;
+               mi->advance_index = -1;
+       }
 
-       if (merged_iter_pqueue_is_empty(mi->pq))
+       if (empty)
                return 1;
 
        entry = merged_iter_pqueue_remove(&mi->pq);
-       err = merged_iter_advance_subiter(mi, entry.index);
-       if (err < 0)
-               return err;
 
        /*
          One can also use reftable as datacenter-local storage, where the ref
@@ -107,56 +133,34 @@ static int merged_iter_next_entry(struct merged_iter *mi,
                struct pq_entry top = merged_iter_pqueue_top(mi->pq);
                int cmp;
 
-               /*
-                * When the next entry comes from the same queue as the current
-                * entry then it must by definition be larger. This avoids a
-                * comparison in the most common case.
-                */
-               if (top.index == entry.index)
-                       break;
-
-               cmp = reftable_record_cmp(&top.rec, &entry.rec);
+               cmp = reftable_record_cmp(top.rec, entry.rec);
                if (cmp > 0)
                        break;
 
                merged_iter_pqueue_remove(&mi->pq);
                err = merged_iter_advance_subiter(mi, top.index);
                if (err < 0)
-                       goto done;
-               reftable_record_release(&top.rec);
+                       return err;
        }
 
-       reftable_record_release(rec);
-       *rec = entry.rec;
-
-done:
-       if (err)
-               reftable_record_release(&entry.rec);
-       return err;
+       mi->advance_index = entry.index;
+       SWAP(*rec, *entry.rec);
+       return 0;
 }
 
-static int merged_iter_next(struct merged_iter *mi, struct reftable_record *rec)
+static int merged_iter_next_void(void *p, struct reftable_record *rec)
 {
+       struct merged_iter *mi = p;
        while (1) {
                int err = merged_iter_next_entry(mi, rec);
-               if (err == 0 && mi->suppress_deletions &&
-                   reftable_record_is_deletion(rec)) {
+               if (err)
+                       return err;
+               if (mi->suppress_deletions && reftable_record_is_deletion(rec))
                        continue;
-               }
-
-               return err;
+               return 0;
        }
 }
 
-static int merged_iter_next_void(void *p, struct reftable_record *rec)
-{
-       struct merged_iter *mi = p;
-       if (merged_iter_pqueue_is_empty(mi->pq))
-               return 1;
-
-       return merged_iter_next(mi, rec);
-}
-
 static struct reftable_iterator_vtable merged_iter_vtable = {
        .next = &merged_iter_next_void,
        .close = &merged_iter_close,
@@ -246,14 +250,15 @@ static int merged_table_seek_record(struct reftable_merged_table *mt,
                .typ = reftable_record_type(rec),
                .hash_id = mt->hash_id,
                .suppress_deletions = mt->suppress_deletions,
+               .advance_index = -1,
        };
        struct merged_iter *p;
        int err;
 
-       REFTABLE_CALLOC_ARRAY(merged.stack, mt->stack_len);
+       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.stack[merged.stack_len], rec);
+                                                &merged.subiters[merged.stack_len].iter, rec);
                if (err < 0)
                        goto out;
                if (!err)
index 7d9f95d27ed0a44c4208e743b6b83ba4cdc5536c..a2571dbc99d68c9954e9d07372bae984cf5ccccc 100644 (file)
@@ -9,7 +9,7 @@ https://developers.google.com/open-source/licenses/bsd
 #ifndef MERGED_H
 #define MERGED_H
 
-#include "pq.h"
+#include "system.h"
 
 struct reftable_merged_table {
        struct reftable_table *stack;
@@ -24,15 +24,6 @@ struct reftable_merged_table {
        uint64_t max;
 };
 
-struct merged_iter {
-       struct reftable_iterator *stack;
-       uint32_t hash_id;
-       size_t stack_len;
-       uint8_t typ;
-       int suppress_deletions;
-       struct merged_iter_pqueue pq;
-};
-
 void merged_table_release(struct reftable_merged_table *mt);
 
 #endif
index d0f77a3b8f51e0abb9af1162106c757d3fc3e553..530fc82d1c2458d57826281d4865bc0aa4127adf 100644 (file)
@@ -289,16 +289,13 @@ merged_table_from_log_records(struct reftable_log_record **logs,
 
 static void test_merged_logs(void)
 {
-       uint8_t hash1[GIT_SHA1_RAWSZ] = { 1 };
-       uint8_t hash2[GIT_SHA1_RAWSZ] = { 2 };
-       uint8_t hash3[GIT_SHA1_RAWSZ] = { 3 };
        struct reftable_log_record r1[] = {
                {
                        .refname = "a",
                        .update_index = 2,
                        .value_type = REFTABLE_LOG_UPDATE,
                        .value.update = {
-                               .old_hash = hash2,
+                               .old_hash = { 2 },
                                /* deletion */
                                .name = "jane doe",
                                .email = "jane@invalid",
@@ -310,8 +307,8 @@ static void test_merged_logs(void)
                        .update_index = 1,
                        .value_type = REFTABLE_LOG_UPDATE,
                        .value.update = {
-                               .old_hash = hash1,
-                               .new_hash = hash2,
+                               .old_hash = { 1 },
+                               .new_hash = { 2 },
                                .name = "jane doe",
                                .email = "jane@invalid",
                                .message = "message1",
@@ -324,7 +321,7 @@ static void test_merged_logs(void)
                        .update_index = 3,
                        .value_type = REFTABLE_LOG_UPDATE,
                        .value.update = {
-                               .new_hash = hash3,
+                               .new_hash = { 3 },
                                .name = "jane doe",
                                .email = "jane@invalid",
                                .message = "message3",
index e0ccce2b9779a8bfd1acac6d42bc7d0e46e3fe0e..7fb45d8c60dbca6106c51732005ce15c59d2c7e0 100644 (file)
@@ -14,22 +14,12 @@ https://developers.google.com/open-source/licenses/bsd
 
 int pq_less(struct pq_entry *a, struct pq_entry *b)
 {
-       int cmp = reftable_record_cmp(&a->rec, &b->rec);
+       int cmp = reftable_record_cmp(a->rec, b->rec);
        if (cmp == 0)
                return a->index > b->index;
        return cmp < 0;
 }
 
-struct pq_entry merged_iter_pqueue_top(struct merged_iter_pqueue pq)
-{
-       return pq.heap[0];
-}
-
-int merged_iter_pqueue_is_empty(struct merged_iter_pqueue pq)
-{
-       return pq.len == 0;
-}
-
 struct pq_entry merged_iter_pqueue_remove(struct merged_iter_pqueue *pq)
 {
        int i = 0;
@@ -82,10 +72,6 @@ void merged_iter_pqueue_add(struct merged_iter_pqueue *pq, const struct pq_entry
 
 void merged_iter_pqueue_release(struct merged_iter_pqueue *pq)
 {
-       int i = 0;
-       for (i = 0; i < pq->len; i++) {
-               reftable_record_release(&pq->heap[i].rec);
-       }
        FREE_AND_NULL(pq->heap);
-       pq->len = pq->cap = 0;
+       memset(pq, 0, sizeof(*pq));
 }
index e85bac9b52e0039bd378cb1073215af4d48196c7..f796c2317948be2c82aaa15c29b5e9aba1730168 100644 (file)
@@ -12,8 +12,8 @@ https://developers.google.com/open-source/licenses/bsd
 #include "record.h"
 
 struct pq_entry {
-       int index;
-       struct reftable_record rec;
+       size_t index;
+       struct reftable_record *rec;
 };
 
 struct merged_iter_pqueue {
@@ -22,12 +22,20 @@ struct merged_iter_pqueue {
        size_t cap;
 };
 
-struct pq_entry merged_iter_pqueue_top(struct merged_iter_pqueue pq);
-int merged_iter_pqueue_is_empty(struct merged_iter_pqueue pq);
 void merged_iter_pqueue_check(struct merged_iter_pqueue pq);
 struct pq_entry merged_iter_pqueue_remove(struct merged_iter_pqueue *pq);
 void merged_iter_pqueue_add(struct merged_iter_pqueue *pq, const struct pq_entry *e);
 void merged_iter_pqueue_release(struct merged_iter_pqueue *pq);
 int pq_less(struct pq_entry *a, struct pq_entry *b);
 
+static inline struct pq_entry merged_iter_pqueue_top(struct merged_iter_pqueue pq)
+{
+       return pq.heap[0];
+}
+
+static inline int merged_iter_pqueue_is_empty(struct merged_iter_pqueue pq)
+{
+       return pq.len == 0;
+}
+
 #endif
index c202eff84801820b28c56ddb7de28a2d041f3c08..b7d3c80cc7260298749800696e4a8bcc5be3284c 100644 (file)
@@ -27,48 +27,43 @@ void merged_iter_pqueue_check(struct merged_iter_pqueue pq)
 
 static void test_pq(void)
 {
-       char *names[54] = { NULL };
-       int N = ARRAY_SIZE(names) - 1;
-
        struct merged_iter_pqueue pq = { NULL };
+       struct reftable_record recs[54];
+       int N = ARRAY_SIZE(recs) - 1, i;
        char *last = NULL;
 
-       int i = 0;
        for (i = 0; i < N; i++) {
-               char name[100];
-               snprintf(name, sizeof(name), "%02d", i);
-               names[i] = xstrdup(name);
+               struct strbuf refname = STRBUF_INIT;
+               strbuf_addf(&refname, "%02d", i);
+
+               reftable_record_init(&recs[i], BLOCK_TYPE_REF);
+               recs[i].u.ref.refname = strbuf_detach(&refname, NULL);
        }
 
        i = 1;
        do {
-               struct pq_entry e = { .rec = { .type = BLOCK_TYPE_REF,
-                                              .u.ref = {
-                                                      .refname = names[i],
-                                              } } };
+               struct pq_entry e = {
+                       .rec = &recs[i],
+               };
+
                merged_iter_pqueue_add(&pq, &e);
                merged_iter_pqueue_check(pq);
+
                i = (i * 7) % N;
        } while (i != 1);
 
        while (!merged_iter_pqueue_is_empty(pq)) {
                struct pq_entry e = merged_iter_pqueue_remove(&pq);
-               struct reftable_record *rec = &e.rec;
                merged_iter_pqueue_check(pq);
 
-               EXPECT(reftable_record_type(rec) == BLOCK_TYPE_REF);
-               if (last) {
-                       EXPECT(strcmp(last, rec->u.ref.refname) < 0);
-               }
-               /* this is names[i], so don't dealloc. */
-               last = rec->u.ref.refname;
-               rec->u.ref.refname = NULL;
-               reftable_record_release(rec);
-       }
-       for (i = 0; i < N; i++) {
-               reftable_free(names[i]);
+               EXPECT(reftable_record_type(e.rec) == BLOCK_TYPE_REF);
+               if (last)
+                       EXPECT(strcmp(last, e.rec->u.ref.refname) < 0);
+               last = e.rec->u.ref.refname;
        }
 
+       for (i = 0; i < N; i++)
+               reftable_record_release(&recs[i]);
        merged_iter_pqueue_release(&pq);
 }
 
index 363fe0f998b91f72002f819c396893b8086dc992..a6dbd214c5d2f8c9a82632776691fe1c292533bd 100644 (file)
@@ -77,18 +77,15 @@ static void write_table(char ***names, struct strbuf *buf, int N,
        }
 
        for (i = 0; i < N; i++) {
-               uint8_t hash[GIT_SHA256_RAWSZ] = { 0 };
                char name[100];
                int n;
 
-               set_test_hash(hash, i);
-
                snprintf(name, sizeof(name), "refs/heads/branch%02d", i);
 
                log.refname = name;
                log.update_index = update_index;
                log.value_type = REFTABLE_LOG_UPDATE;
-               log.value.update.new_hash = hash;
+               set_test_hash(log.value.update.new_hash, i);
                log.value.update.message = "message";
 
                n = reftable_writer_add_log(w, &log);
@@ -137,13 +134,10 @@ static void test_log_buffer_size(void)
        /* This tests buffer extension for log compression. Must use a random
           hash, to ensure that the compressed part is larger than the original.
        */
-       uint8_t hash1[GIT_SHA1_RAWSZ], hash2[GIT_SHA1_RAWSZ];
        for (i = 0; i < GIT_SHA1_RAWSZ; i++) {
-               hash1[i] = (uint8_t)(git_rand() % 256);
-               hash2[i] = (uint8_t)(git_rand() % 256);
+               log.value.update.old_hash[i] = (uint8_t)(git_rand() % 256);
+               log.value.update.new_hash[i] = (uint8_t)(git_rand() % 256);
        }
-       log.value.update.old_hash = hash1;
-       log.value.update.new_hash = hash2;
        reftable_writer_set_limits(w, update_index, update_index);
        err = reftable_writer_add_log(w, &log);
        EXPECT_ERR(err);
@@ -161,25 +155,26 @@ static void test_log_overflow(void)
                .block_size = ARRAY_SIZE(msg),
        };
        int err;
-       struct reftable_log_record
-               log = { .refname = "refs/heads/master",
-                       .update_index = 0xa,
-                       .value_type = REFTABLE_LOG_UPDATE,
-                       .value = { .update = {
-                                          .name = "Han-Wen Nienhuys",
-                                          .email = "hanwen@google.com",
-                                          .tz_offset = 100,
-                                          .time = 0x5e430672,
-                                          .message = msg,
-                                  } } };
+       struct reftable_log_record log = {
+               .refname = "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",
+                               .tz_offset = 100,
+                               .time = 0x5e430672,
+                               .message = msg,
+                       },
+               },
+       };
        struct reftable_writer *w =
                reftable_new_writer(&strbuf_add_void, &noop_flush, &buf, &opts);
 
-       uint8_t hash1[GIT_SHA1_RAWSZ]  = {1}, hash2[GIT_SHA1_RAWSZ] = { 2 };
-
        memset(msg, 'x', sizeof(msg) - 1);
-       log.value.update.old_hash = hash1;
-       log.value.update.new_hash = hash2;
        reftable_writer_set_limits(w, update_index, update_index);
        err = reftable_writer_add_log(w, &log);
        EXPECT(err == REFTABLE_ENTRY_TOO_BIG_ERROR);
@@ -219,16 +214,13 @@ static void test_log_write_read(void)
                EXPECT_ERR(err);
        }
        for (i = 0; i < N; i++) {
-               uint8_t hash1[GIT_SHA1_RAWSZ], hash2[GIT_SHA1_RAWSZ];
                struct reftable_log_record log = { NULL };
-               set_test_hash(hash1, i);
-               set_test_hash(hash2, i + 1);
 
                log.refname = names[i];
                log.update_index = i;
                log.value_type = REFTABLE_LOG_UPDATE;
-               log.value.update.old_hash = hash1;
-               log.value.update.new_hash = hash2;
+               set_test_hash(log.value.update.old_hash, i);
+               set_test_hash(log.value.update.new_hash, i + 1);
 
                err = reftable_writer_add_log(w, &log);
                EXPECT_ERR(err);
@@ -298,18 +290,15 @@ static void test_log_zlib_corruption(void)
        struct reftable_writer *w =
                reftable_new_writer(&strbuf_add_void, &noop_flush, &buf, &opts);
        const struct reftable_stats *stats = NULL;
-       uint8_t hash1[GIT_SHA1_RAWSZ] = { 1 };
-       uint8_t hash2[GIT_SHA1_RAWSZ] = { 2 };
        char message[100] = { 0 };
        int err, i, n;
-
        struct reftable_log_record log = {
                .refname = "refname",
                .value_type = REFTABLE_LOG_UPDATE,
                .value = {
                        .update = {
-                               .new_hash = hash1,
-                               .old_hash = hash2,
+                               .new_hash = { 1 },
+                               .old_hash = { 2 },
                                .name = "My Name",
                                .email = "myname@invalid",
                                .message = message,
@@ -821,13 +810,12 @@ static void test_write_multiple_indices(void)
        }
 
        for (i = 0; i < 100; i++) {
-               unsigned char hash[GIT_SHA1_RAWSZ] = {i};
                struct reftable_log_record log = {
                        .update_index = 1,
                        .value_type = REFTABLE_LOG_UPDATE,
                        .value.update = {
-                               .old_hash = hash,
-                               .new_hash = hash,
+                               .old_hash = { i },
+                               .new_hash = { i },
                        },
                };
 
index d6bb42e887423d95f2fcc1b2b146505ab4bef62a..23b497adab8b3478026959a802bddb58fb1c6002 100644 (file)
@@ -159,20 +159,19 @@ int reftable_encode_key(int *restart, struct string_view dest,
        return start.len - dest.len;
 }
 
-int reftable_decode_key(struct strbuf *key, uint8_t *extra,
-                       struct strbuf last_key, struct string_view in)
+int reftable_decode_key(struct strbuf *last_key, uint8_t *extra,
+                       struct string_view in)
 {
        int start_len = in.len;
        uint64_t prefix_len = 0;
        uint64_t suffix_len = 0;
-       int n = get_var_int(&prefix_len, &in);
+       int n;
+
+       n = get_var_int(&prefix_len, &in);
        if (n < 0)
                return -1;
        string_view_consume(&in, n);
 
-       if (prefix_len > last_key.len)
-               return -1;
-
        n = get_var_int(&suffix_len, &in);
        if (n <= 0)
                return -1;
@@ -181,12 +180,12 @@ int reftable_decode_key(struct strbuf *key, uint8_t *extra,
        *extra = (uint8_t)(suffix_len & 0x7);
        suffix_len >>= 3;
 
-       if (in.len < suffix_len)
+       if (in.len < suffix_len ||
+           prefix_len > last_key->len)
                return -1;
 
-       strbuf_reset(key);
-       strbuf_add(key, last_key.buf, prefix_len);
-       strbuf_add(key, in.buf, suffix_len);
+       strbuf_setlen(last_key, prefix_len);
+       strbuf_add(last_key, in.buf, suffix_len);
        string_view_consume(&in, suffix_len);
 
        return start_len - in.len;
@@ -205,14 +204,26 @@ static void reftable_ref_record_copy_from(void *rec, const void *src_rec,
 {
        struct reftable_ref_record *ref = rec;
        const struct reftable_ref_record *src = src_rec;
+       char *refname = NULL;
+       size_t refname_cap = 0;
+
        assert(hash_size > 0);
 
-       /* This is simple and correct, but we could probably reuse the hash
-        * fields. */
+       SWAP(refname, ref->refname);
+       SWAP(refname_cap, ref->refname_cap);
        reftable_ref_record_release(ref);
+       SWAP(ref->refname, refname);
+       SWAP(ref->refname_cap, refname_cap);
+
        if (src->refname) {
-               ref->refname = xstrdup(src->refname);
+               size_t refname_len = strlen(src->refname);
+
+               REFTABLE_ALLOC_GROW(ref->refname, refname_len + 1,
+                                   ref->refname_cap);
+               memcpy(ref->refname, src->refname, refname_len);
+               ref->refname[refname_len] = 0;
        }
+
        ref->update_index = src->update_index;
        ref->value_type = src->value_type;
        switch (src->value_type) {
@@ -363,21 +374,29 @@ static int reftable_ref_record_encode(const void *rec, struct string_view s,
 
 static int reftable_ref_record_decode(void *rec, struct strbuf key,
                                      uint8_t val_type, struct string_view in,
-                                     int hash_size)
+                                     int hash_size, struct strbuf *scratch)
 {
        struct reftable_ref_record *r = rec;
        struct string_view start = in;
        uint64_t update_index = 0;
-       int n = get_var_int(&update_index, &in);
+       const char *refname = NULL;
+       size_t refname_cap = 0;
+       int n;
+
+       assert(hash_size > 0);
+
+       n = get_var_int(&update_index, &in);
        if (n < 0)
                return n;
        string_view_consume(&in, n);
 
+       SWAP(refname, r->refname);
+       SWAP(refname_cap, r->refname_cap);
        reftable_ref_record_release(r);
+       SWAP(r->refname, refname);
+       SWAP(r->refname_cap, refname_cap);
 
-       assert(hash_size > 0);
-
-       r->refname = reftable_malloc(key.len + 1);
+       REFTABLE_ALLOC_GROW(r->refname, key.len + 1, r->refname_cap);
        memcpy(r->refname, key.buf, key.len);
        r->refname[key.len] = 0;
 
@@ -406,13 +425,12 @@ static int reftable_ref_record_decode(void *rec, struct strbuf key,
                break;
 
        case REFTABLE_REF_SYMREF: {
-               struct strbuf dest = STRBUF_INIT;
-               int n = decode_string(&dest, in);
+               int n = decode_string(scratch, in);
                if (n < 0) {
                        return -1;
                }
                string_view_consume(&in, n);
-               r->value.symref = dest.buf;
+               r->value.symref = strbuf_detach(scratch, NULL);
        } break;
 
        case REFTABLE_REF_DELETION:
@@ -560,7 +578,7 @@ static int reftable_obj_record_encode(const void *rec, struct string_view s,
 
 static int reftable_obj_record_decode(void *rec, struct strbuf key,
                                      uint8_t val_type, struct string_view in,
-                                     int hash_size)
+                                     int hash_size, struct strbuf *scratch UNUSED)
 {
        struct string_view start = in;
        struct reftable_obj_record *r = rec;
@@ -569,6 +587,8 @@ static int reftable_obj_record_decode(void *rec, struct strbuf key,
        uint64_t last;
        int j;
 
+       reftable_obj_record_release(r);
+
        REFTABLE_ALLOC_ARRAY(r->hash_prefix, key.len);
        memcpy(r->hash_prefix, key.buf, key.len);
        r->hash_prefix_len = key.len;
@@ -744,16 +764,10 @@ static void reftable_log_record_copy_from(void *rec, const void *src_rec,
                                xstrdup(dst->value.update.message);
                }
 
-               if (dst->value.update.new_hash) {
-                       REFTABLE_ALLOC_ARRAY(dst->value.update.new_hash, hash_size);
-                       memcpy(dst->value.update.new_hash,
-                              src->value.update.new_hash, hash_size);
-               }
-               if (dst->value.update.old_hash) {
-                       REFTABLE_ALLOC_ARRAY(dst->value.update.old_hash, hash_size);
-                       memcpy(dst->value.update.old_hash,
-                              src->value.update.old_hash, hash_size);
-               }
+               memcpy(dst->value.update.new_hash,
+                      src->value.update.new_hash, hash_size);
+               memcpy(dst->value.update.old_hash,
+                      src->value.update.old_hash, hash_size);
                break;
        }
 }
@@ -771,8 +785,6 @@ void reftable_log_record_release(struct reftable_log_record *r)
        case REFTABLE_LOG_DELETION:
                break;
        case REFTABLE_LOG_UPDATE:
-               reftable_free(r->value.update.new_hash);
-               reftable_free(r->value.update.old_hash);
                reftable_free(r->value.update.name);
                reftable_free(r->value.update.email);
                reftable_free(r->value.update.message);
@@ -789,33 +801,20 @@ static uint8_t reftable_log_record_val_type(const void *rec)
        return reftable_log_record_is_deletion(log) ? 0 : 1;
 }
 
-static uint8_t zero[GIT_SHA256_RAWSZ] = { 0 };
-
 static int reftable_log_record_encode(const void *rec, struct string_view s,
                                      int hash_size)
 {
        const struct reftable_log_record *r = rec;
        struct string_view start = s;
        int n = 0;
-       uint8_t *oldh = NULL;
-       uint8_t *newh = NULL;
        if (reftable_log_record_is_deletion(r))
                return 0;
 
-       oldh = r->value.update.old_hash;
-       newh = r->value.update.new_hash;
-       if (!oldh) {
-               oldh = zero;
-       }
-       if (!newh) {
-               newh = zero;
-       }
-
        if (s.len < 2 * hash_size)
                return -1;
 
-       memcpy(s.buf, oldh, hash_size);
-       memcpy(s.buf + hash_size, newh, hash_size);
+       memcpy(s.buf, r->value.update.old_hash, hash_size);
+       memcpy(s.buf + hash_size, r->value.update.new_hash, hash_size);
        string_view_consume(&s, 2 * hash_size);
 
        n = encode_string(r->value.update.name ? r->value.update.name : "", s);
@@ -851,19 +850,18 @@ static int reftable_log_record_encode(const void *rec, struct string_view s,
 
 static int reftable_log_record_decode(void *rec, struct strbuf key,
                                      uint8_t val_type, struct string_view in,
-                                     int hash_size)
+                                     int hash_size, struct strbuf *scratch)
 {
        struct string_view start = in;
        struct reftable_log_record *r = rec;
        uint64_t max = 0;
        uint64_t ts = 0;
-       struct strbuf dest = STRBUF_INIT;
        int n;
 
        if (key.len <= 9 || key.buf[key.len - 9] != 0)
                return REFTABLE_FORMAT_ERROR;
 
-       r->refname = reftable_realloc(r->refname, key.len - 8);
+       REFTABLE_ALLOC_GROW(r->refname, key.len - 8, r->refname_cap);
        memcpy(r->refname, key.buf, key.len - 8);
        ts = get_be64(key.buf + key.len - 8);
 
@@ -872,9 +870,8 @@ static int reftable_log_record_decode(void *rec, struct strbuf key,
        if (val_type != r->value_type) {
                switch (r->value_type) {
                case REFTABLE_LOG_UPDATE:
-                       FREE_AND_NULL(r->value.update.old_hash);
-                       FREE_AND_NULL(r->value.update.new_hash);
                        FREE_AND_NULL(r->value.update.message);
+                       r->value.update.message_cap = 0;
                        FREE_AND_NULL(r->value.update.email);
                        FREE_AND_NULL(r->value.update.name);
                        break;
@@ -890,36 +887,43 @@ static int reftable_log_record_decode(void *rec, struct strbuf key,
        if (in.len < 2 * hash_size)
                return REFTABLE_FORMAT_ERROR;
 
-       r->value.update.old_hash =
-               reftable_realloc(r->value.update.old_hash, hash_size);
-       r->value.update.new_hash =
-               reftable_realloc(r->value.update.new_hash, hash_size);
-
        memcpy(r->value.update.old_hash, in.buf, hash_size);
        memcpy(r->value.update.new_hash, in.buf + hash_size, hash_size);
 
        string_view_consume(&in, 2 * hash_size);
 
-       n = decode_string(&dest, in);
+       n = decode_string(scratch, in);
        if (n < 0)
                goto done;
        string_view_consume(&in, n);
 
-       r->value.update.name =
-               reftable_realloc(r->value.update.name, dest.len + 1);
-       memcpy(r->value.update.name, dest.buf, dest.len);
-       r->value.update.name[dest.len] = 0;
+       /*
+        * In almost all cases we can expect the reflog name to not change for
+        * reflog entries as they are tied to the local identity, not to the
+        * target commits. As an optimization for this common case we can thus
+        * skip copying over the name in case it's accurate already.
+        */
+       if (!r->value.update.name ||
+           strcmp(r->value.update.name, scratch->buf)) {
+               r->value.update.name =
+                       reftable_realloc(r->value.update.name, scratch->len + 1);
+               memcpy(r->value.update.name, scratch->buf, scratch->len);
+               r->value.update.name[scratch->len] = 0;
+       }
 
-       strbuf_reset(&dest);
-       n = decode_string(&dest, in);
+       n = decode_string(scratch, in);
        if (n < 0)
                goto done;
        string_view_consume(&in, n);
 
-       r->value.update.email =
-               reftable_realloc(r->value.update.email, dest.len + 1);
-       memcpy(r->value.update.email, dest.buf, dest.len);
-       r->value.update.email[dest.len] = 0;
+       /* Same as above, but for the reflog email. */
+       if (!r->value.update.email ||
+           strcmp(r->value.update.email, scratch->buf)) {
+               r->value.update.email =
+                       reftable_realloc(r->value.update.email, scratch->len + 1);
+               memcpy(r->value.update.email, scratch->buf, scratch->len);
+               r->value.update.email[scratch->len] = 0;
+       }
 
        ts = 0;
        n = get_var_int(&ts, &in);
@@ -933,22 +937,19 @@ static int reftable_log_record_decode(void *rec, struct strbuf key,
        r->value.update.tz_offset = get_be16(in.buf);
        string_view_consume(&in, 2);
 
-       strbuf_reset(&dest);
-       n = decode_string(&dest, in);
+       n = decode_string(scratch, in);
        if (n < 0)
                goto done;
        string_view_consume(&in, n);
 
-       r->value.update.message =
-               reftable_realloc(r->value.update.message, dest.len + 1);
-       memcpy(r->value.update.message, dest.buf, dest.len);
-       r->value.update.message[dest.len] = 0;
+       REFTABLE_ALLOC_GROW(r->value.update.message, scratch->len + 1,
+                           r->value.update.message_cap);
+       memcpy(r->value.update.message, scratch->buf, scratch->len);
+       r->value.update.message[scratch->len] = 0;
 
-       strbuf_release(&dest);
        return start.len - in.len;
 
 done:
-       strbuf_release(&dest);
        return REFTABLE_FORMAT_ERROR;
 }
 
@@ -964,17 +965,6 @@ static int null_streq(char *a, char *b)
        return 0 == strcmp(a, b);
 }
 
-static int zero_hash_eq(uint8_t *a, uint8_t *b, int sz)
-{
-       if (!a)
-               a = zero;
-
-       if (!b)
-               b = zero;
-
-       return !memcmp(a, b, sz);
-}
-
 static int reftable_log_record_equal_void(const void *a,
                                          const void *b, int hash_size)
 {
@@ -1018,10 +1008,10 @@ int reftable_log_record_equal(const struct reftable_log_record *a,
                                  b->value.update.email) &&
                       null_streq(a->value.update.message,
                                  b->value.update.message) &&
-                      zero_hash_eq(a->value.update.old_hash,
-                                   b->value.update.old_hash, hash_size) &&
-                      zero_hash_eq(a->value.update.new_hash,
-                                   b->value.update.new_hash, hash_size);
+                      !memcmp(a->value.update.old_hash,
+                              b->value.update.old_hash, hash_size) &&
+                      !memcmp(a->value.update.new_hash,
+                              b->value.update.new_hash, hash_size);
        }
 
        abort();
@@ -1099,7 +1089,7 @@ static int reftable_index_record_encode(const void *rec, struct string_view out,
 
 static int reftable_index_record_decode(void *rec, struct strbuf key,
                                        uint8_t val_type, struct string_view in,
-                                       int hash_size)
+                                       int hash_size, struct strbuf *scratch UNUSED)
 {
        struct string_view start = in;
        struct reftable_index_record *r = rec;
@@ -1157,11 +1147,6 @@ void reftable_record_key(struct reftable_record *rec, struct strbuf *dest)
        reftable_record_vtable(rec)->key(reftable_record_data(rec), dest);
 }
 
-uint8_t reftable_record_type(struct reftable_record *rec)
-{
-       return rec->type;
-}
-
 int reftable_record_encode(struct reftable_record *rec, struct string_view dest,
                           int hash_size)
 {
@@ -1185,10 +1170,12 @@ uint8_t reftable_record_val_type(struct reftable_record *rec)
 }
 
 int reftable_record_decode(struct reftable_record *rec, struct strbuf key,
-                          uint8_t extra, struct string_view src, int hash_size)
+                          uint8_t extra, struct string_view src, int hash_size,
+                          struct strbuf *scratch)
 {
        return reftable_record_vtable(rec)->decode(reftable_record_data(rec),
-                                                  key, extra, src, hash_size);
+                                                  key, extra, src, hash_size,
+                                                  scratch);
 }
 
 void reftable_record_release(struct reftable_record *rec)
@@ -1283,12 +1270,6 @@ int reftable_log_record_is_deletion(const struct reftable_log_record *log)
        return (log->value_type == REFTABLE_LOG_DELETION);
 }
 
-void string_view_consume(struct string_view *s, int n)
-{
-       s->buf += n;
-       s->len -= n;
-}
-
 static void *reftable_record_data(struct reftable_record *rec)
 {
        switch (rec->type) {
index a05e2be17971d5e26f27871159cda293aa2640ad..826ee1c55c3b64d2bf4cfe1a170cf99ca3511259 100644 (file)
@@ -25,7 +25,11 @@ struct string_view {
 };
 
 /* Advance `s.buf` by `n`, and decrease length. */
-void string_view_consume(struct string_view *s, int n);
+static inline void string_view_consume(struct string_view *s, int n)
+{
+       s->buf += n;
+       s->len -= n;
+}
 
 /* utilities for de/encoding varints */
 
@@ -51,7 +55,8 @@ struct reftable_record_vtable {
 
        /* decode data from `src` into the record. */
        int (*decode)(void *rec, struct strbuf key, uint8_t extra,
-                     struct string_view src, int hash_size);
+                     struct string_view src, int hash_size,
+                     struct strbuf *scratch);
 
        /* deallocate and null the record. */
        void (*release)(void *rec);
@@ -81,9 +86,12 @@ int reftable_encode_key(int *is_restart, struct string_view dest,
                        struct strbuf prev_key, struct strbuf key,
                        uint8_t extra);
 
-/* Decode into `key` and `extra` from `in` */
-int reftable_decode_key(struct strbuf *key, uint8_t *extra,
-                       struct strbuf last_key, struct string_view in);
+/*
+ * Decode into `last_key` and `extra` from `in`. `last_key` is expected to
+ * contain the decoded key of the preceding record, if any.
+ */
+int reftable_decode_key(struct strbuf *last_key, uint8_t *extra,
+                       struct string_view in);
 
 /* reftable_index_record are used internally to speed up lookups. */
 struct reftable_index_record {
@@ -124,7 +132,6 @@ int reftable_record_cmp(struct reftable_record *a, struct reftable_record *b);
 int reftable_record_equal(struct reftable_record *a, struct reftable_record *b, int hash_size);
 void reftable_record_print(struct reftable_record *rec, int hash_size);
 void reftable_record_key(struct reftable_record *rec, struct strbuf *dest);
-uint8_t reftable_record_type(struct reftable_record *rec);
 void reftable_record_copy_from(struct reftable_record *rec,
                               struct reftable_record *src, int hash_size);
 uint8_t reftable_record_val_type(struct reftable_record *rec);
@@ -132,9 +139,14 @@ int reftable_record_encode(struct reftable_record *rec, struct string_view dest,
                           int hash_size);
 int reftable_record_decode(struct reftable_record *rec, struct strbuf key,
                           uint8_t extra, struct string_view src,
-                          int hash_size);
+                          int hash_size, struct strbuf *scratch);
 int reftable_record_is_deletion(struct reftable_record *rec);
 
+static inline uint8_t reftable_record_type(struct reftable_record *rec)
+{
+       return rec->type;
+}
+
 /* frees and zeroes out the embedded record */
 void reftable_record_release(struct reftable_record *rec);
 
index a86cff552661ba0f00be32200bf8b0c800a9fa87..c158ee79ffe36a0bdf13259f83c4eef119f36981 100644 (file)
@@ -99,6 +99,7 @@ static void set_hash(uint8_t *h, int j)
 
 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++) {
@@ -140,7 +141,7 @@ static void test_reftable_ref_record_roundtrip(void)
                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);
+               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,
@@ -150,6 +151,8 @@ static void test_reftable_ref_record_roundtrip(void)
                strbuf_release(&key);
                reftable_record_release(&out);
        }
+
+       strbuf_release(&scratch);
 }
 
 static void test_reftable_log_record_equal(void)
@@ -175,7 +178,6 @@ static void test_reftable_log_record_equal(void)
 static void test_reftable_log_record_roundtrip(void)
 {
        int i;
-
        struct reftable_log_record in[] = {
                {
                        .refname = xstrdup("refs/heads/master"),
@@ -183,8 +185,6 @@ static void test_reftable_log_record_roundtrip(void)
                        .value_type = REFTABLE_LOG_UPDATE,
                        .value = {
                                .update = {
-                                       .old_hash = reftable_malloc(GIT_SHA1_RAWSZ),
-                                       .new_hash = reftable_malloc(GIT_SHA1_RAWSZ),
                                        .name = xstrdup("han-wen"),
                                        .email = xstrdup("hanwen@google.com"),
                                        .message = xstrdup("test"),
@@ -202,15 +202,10 @@ static void test_reftable_log_record_roundtrip(void)
                        .refname = xstrdup("branch"),
                        .update_index = 33,
                        .value_type = REFTABLE_LOG_UPDATE,
-                       .value = {
-                               .update = {
-                                       .old_hash = reftable_malloc(GIT_SHA1_RAWSZ),
-                                       .new_hash = reftable_malloc(GIT_SHA1_RAWSZ),
-                                       /* rest of fields left empty. */
-                               },
-                       },
                }
        };
+       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);
@@ -231,8 +226,6 @@ static void test_reftable_log_record_roundtrip(void)
                                .value_type = REFTABLE_LOG_UPDATE,
                                .value = {
                                        .update = {
-                                               .new_hash = reftable_calloc(GIT_SHA1_RAWSZ, 1),
-                                               .old_hash = reftable_calloc(GIT_SHA1_RAWSZ, 1),
                                                .name = xstrdup("old name"),
                                                .email = xstrdup("old@email"),
                                                .message = xstrdup("old message"),
@@ -252,7 +245,7 @@ static void test_reftable_log_record_roundtrip(void)
                EXPECT(n >= 0);
                valtype = reftable_record_val_type(&rec);
                m = reftable_record_decode(&out, key, valtype, dest,
-                                          GIT_SHA1_RAWSZ);
+                                          GIT_SHA1_RAWSZ, &scratch);
                EXPECT(n == m);
 
                EXPECT(reftable_log_record_equal(&in[i], &out.u.log,
@@ -261,6 +254,8 @@ static void test_reftable_log_record_roundtrip(void)
                strbuf_release(&key);
                reftable_record_release(&out);
        }
+
+       strbuf_release(&scratch);
 }
 
 static void test_u24_roundtrip(void)
@@ -295,7 +290,8 @@ static void test_key_roundtrip(void)
        EXPECT(!restart);
        EXPECT(n > 0);
 
-       m = reftable_decode_key(&roundtrip, &rt_extra, last_key, dest);
+       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);
@@ -309,23 +305,27 @@ 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 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 = {
@@ -349,13 +349,15 @@ static void test_reftable_obj_record_roundtrip(void)
                EXPECT(n > 0);
                extra = reftable_record_val_type(&in);
                m = reftable_record_decode(&out, key, extra, dest,
-                                          GIT_SHA1_RAWSZ);
+                                          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)
@@ -372,6 +374,7 @@ static void test_reftable_index_record_roundtrip(void)
                .buf = buffer,
                .len = sizeof(buffer),
        };
+       struct strbuf scratch = STRBUF_INIT;
        struct strbuf key = STRBUF_INIT;
        struct reftable_record out = {
                .type = BLOCK_TYPE_INDEX,
@@ -389,13 +392,15 @@ static void test_reftable_index_record_roundtrip(void)
        EXPECT(n > 0);
 
        extra = reftable_record_val_type(&in);
-       m = reftable_record_decode(&out, key, extra, dest, GIT_SHA1_RAWSZ);
+       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);
 }
 
index bb6e99acd3151ec75a7a2dc60e930064b7322e60..2a2943cd134cebf1e6b9713613ba228d49576281 100644 (file)
@@ -22,6 +22,7 @@ https://developers.google.com/open-source/licenses/bsd
 /* reftable_ref_record holds a ref database entry target_value */
 struct reftable_ref_record {
        char *refname; /* Name of the ref, malloced. */
+       size_t refname_cap;
        uint64_t update_index; /* Logical timestamp at which this value is
                                * written */
 
@@ -73,6 +74,7 @@ int reftable_ref_record_equal(const struct reftable_ref_record *a,
 /* reftable_log_record holds a reflog entry */
 struct reftable_log_record {
        char *refname;
+       size_t refname_cap;
        uint64_t update_index; /* logical timestamp of a transactional update.
                                */
 
@@ -87,13 +89,14 @@ struct reftable_log_record {
 
        union {
                struct {
-                       uint8_t *new_hash;
-                       uint8_t *old_hash;
+                       unsigned char new_hash[GIT_MAX_RAWSZ];
+                       unsigned char old_hash[GIT_MAX_RAWSZ];
                        char *name;
                        char *email;
                        uint64_t time;
                        int16_t tz_offset;
                        char *message;
+                       size_t message_cap;
                } update;
        } value;
 };
index b64e55648aa8798054bb7b8a781376e7fa3bc2af..1ecf1b9751ce4975580ab7deed2bbe2a9a7847bc 100644 (file)
@@ -737,8 +737,9 @@ int reftable_addition_add(struct reftable_addition *add,
        struct strbuf tab_file_name = STRBUF_INIT;
        struct strbuf next_name = STRBUF_INIT;
        struct reftable_writer *wr = NULL;
+       struct tempfile *tab_file = NULL;
        int err = 0;
-       int tab_fd = 0;
+       int tab_fd;
 
        strbuf_reset(&next_name);
        format_name(&next_name, add->next_update_index, add->next_update_index);
@@ -746,17 +747,20 @@ int reftable_addition_add(struct reftable_addition *add,
        stack_filename(&temp_tab_file_name, add->stack, next_name.buf);
        strbuf_addstr(&temp_tab_file_name, ".temp.XXXXXX");
 
-       tab_fd = mkstemp(temp_tab_file_name.buf);
-       if (tab_fd < 0) {
+       tab_file = mks_tempfile(temp_tab_file_name.buf);
+       if (!tab_file) {
                err = REFTABLE_IO_ERROR;
                goto done;
        }
        if (add->stack->config.default_permissions) {
-               if (chmod(temp_tab_file_name.buf, add->stack->config.default_permissions)) {
+               if (chmod(get_tempfile_path(tab_file),
+                         add->stack->config.default_permissions)) {
                        err = REFTABLE_IO_ERROR;
                        goto done;
                }
        }
+       tab_fd = get_tempfile_fd(tab_file);
+
        wr = reftable_new_writer(reftable_fd_write, reftable_fd_flush, &tab_fd,
                                 &add->stack->config);
        err = write_table(wr, arg);
@@ -771,14 +775,13 @@ int reftable_addition_add(struct reftable_addition *add,
        if (err < 0)
                goto done;
 
-       err = close(tab_fd);
-       tab_fd = 0;
+       err = close_tempfile_gently(tab_file);
        if (err < 0) {
                err = REFTABLE_IO_ERROR;
                goto done;
        }
 
-       err = stack_check_addition(add->stack, temp_tab_file_name.buf);
+       err = stack_check_addition(add->stack, get_tempfile_path(tab_file));
        if (err < 0)
                goto done;
 
@@ -789,14 +792,13 @@ int reftable_addition_add(struct reftable_addition *add,
 
        format_name(&next_name, wr->min_update_index, wr->max_update_index);
        strbuf_addstr(&next_name, ".ref");
-
        stack_filename(&tab_file_name, add->stack, next_name.buf);
 
        /*
          On windows, this relies on rand() picking a unique destination name.
          Maybe we should do retry loop as well?
         */
-       err = rename(temp_tab_file_name.buf, tab_file_name.buf);
+       err = rename_tempfile(&tab_file, tab_file_name.buf);
        if (err < 0) {
                err = REFTABLE_IO_ERROR;
                goto done;
@@ -806,14 +808,7 @@ int reftable_addition_add(struct reftable_addition *add,
                            add->new_tables_cap);
        add->new_tables[add->new_tables_len++] = strbuf_detach(&next_name, NULL);
 done:
-       if (tab_fd > 0) {
-               close(tab_fd);
-               tab_fd = 0;
-       }
-       if (temp_tab_file_name.len > 0) {
-               unlink(temp_tab_file_name.buf);
-       }
-
+       delete_tempfile(&tab_file);
        strbuf_release(&temp_tab_file_name);
        strbuf_release(&tab_file_name);
        strbuf_release(&next_name);
@@ -832,51 +827,56 @@ uint64_t reftable_stack_next_update_index(struct reftable_stack *st)
 
 static int stack_compact_locked(struct reftable_stack *st,
                                size_t first, size_t last,
-                               struct strbuf *temp_tab,
-                               struct reftable_log_expiry_config *config)
+                               struct reftable_log_expiry_config *config,
+                               struct tempfile **tab_file_out)
 {
        struct strbuf next_name = STRBUF_INIT;
-       int tab_fd = -1;
+       struct strbuf tab_file_path = STRBUF_INIT;
        struct reftable_writer *wr = NULL;
-       int err = 0;
+       struct tempfile *tab_file;
+       int tab_fd, err = 0;
 
        format_name(&next_name,
                    reftable_reader_min_update_index(st->readers[first]),
                    reftable_reader_max_update_index(st->readers[last]));
+       stack_filename(&tab_file_path, st, next_name.buf);
+       strbuf_addstr(&tab_file_path, ".temp.XXXXXX");
 
-       stack_filename(temp_tab, st, next_name.buf);
-       strbuf_addstr(temp_tab, ".temp.XXXXXX");
+       tab_file = mks_tempfile(tab_file_path.buf);
+       if (!tab_file) {
+               err = REFTABLE_IO_ERROR;
+               goto done;
+       }
+       tab_fd = get_tempfile_fd(tab_file);
 
-       tab_fd = mkstemp(temp_tab->buf);
        if (st->config.default_permissions &&
-           chmod(temp_tab->buf, st->config.default_permissions) < 0) {
+           chmod(get_tempfile_path(tab_file), st->config.default_permissions) < 0) {
                err = REFTABLE_IO_ERROR;
                goto done;
        }
 
-       wr = reftable_new_writer(reftable_fd_write, reftable_fd_flush, &tab_fd, &st->config);
-
+       wr = reftable_new_writer(reftable_fd_write, reftable_fd_flush,
+                                &tab_fd, &st->config);
        err = stack_write_compact(st, wr, first, last, config);
        if (err < 0)
                goto done;
+
        err = reftable_writer_close(wr);
        if (err < 0)
                goto done;
 
-       err = close(tab_fd);
-       tab_fd = 0;
+       err = close_tempfile_gently(tab_file);
+       if (err < 0)
+               goto done;
+
+       *tab_file_out = tab_file;
+       tab_file = NULL;
 
 done:
+       delete_tempfile(&tab_file);
        reftable_writer_free(wr);
-       if (tab_fd > 0) {
-               close(tab_fd);
-               tab_fd = 0;
-       }
-       if (err != 0 && temp_tab->len > 0) {
-               unlink(temp_tab->buf);
-               strbuf_release(temp_tab);
-       }
        strbuf_release(&next_name);
+       strbuf_release(&tab_file_path);
        return err;
 }
 
@@ -983,212 +983,200 @@ static int stack_compact_range(struct reftable_stack *st,
                               size_t first, size_t last,
                               struct reftable_log_expiry_config *expiry)
 {
-       char **delete_on_success = NULL, **subtable_locks = NULL, **listp = NULL;
-       struct strbuf temp_tab_file_name = STRBUF_INIT;
+       struct strbuf tables_list_buf = STRBUF_INIT;
        struct strbuf new_table_name = STRBUF_INIT;
-       struct strbuf lock_file_name = STRBUF_INIT;
-       struct strbuf ref_list_contents = STRBUF_INIT;
        struct strbuf new_table_path = STRBUF_INIT;
-       size_t i, j, compact_count;
-       int err = 0;
-       int have_lock = 0;
-       int lock_file_fd = -1;
-       int is_empty_table = 0;
+       struct strbuf table_name = STRBUF_INIT;
+       struct lock_file tables_list_lock = LOCK_INIT;
+       struct lock_file *table_locks = NULL;
+       struct tempfile *new_table = NULL;
+       int is_empty_table = 0, err = 0;
+       size_t i;
 
        if (first > last || (!expiry && first == last)) {
                err = 0;
                goto done;
        }
 
-       compact_count = last - first + 1;
-       REFTABLE_CALLOC_ARRAY(delete_on_success, compact_count + 1);
-       REFTABLE_CALLOC_ARRAY(subtable_locks, compact_count + 1);
-
        st->stats.attempts++;
 
-       strbuf_reset(&lock_file_name);
-       strbuf_addstr(&lock_file_name, st->list_file);
-       strbuf_addstr(&lock_file_name, ".lock");
-
-       lock_file_fd =
-               open(lock_file_name.buf, O_EXCL | O_CREAT | O_WRONLY, 0666);
-       if (lock_file_fd < 0) {
-               if (errno == EEXIST) {
+       /*
+        * Hold the lock so that we can read "tables.list" and lock all tables
+        * which are part of the user-specified range.
+        */
+       err = hold_lock_file_for_update(&tables_list_lock, st->list_file,
+                                       LOCK_NO_DEREF);
+       if (err < 0) {
+               if (errno == EEXIST)
                        err = 1;
-               } else {
+               else
                        err = REFTABLE_IO_ERROR;
-               }
                goto done;
        }
-       /* Don't want to write to the lock for now.  */
-       close(lock_file_fd);
-       lock_file_fd = -1;
 
-       have_lock = 1;
        err = stack_uptodate(st);
-       if (err != 0)
+       if (err)
                goto done;
 
-       for (i = first, j = 0; i <= last; i++) {
-               struct strbuf subtab_file_name = STRBUF_INIT;
-               struct strbuf subtab_lock = STRBUF_INIT;
-               int sublock_file_fd = -1;
-
-               stack_filename(&subtab_file_name, st,
-                              reader_name(st->readers[i]));
-
-               strbuf_reset(&subtab_lock);
-               strbuf_addbuf(&subtab_lock, &subtab_file_name);
-               strbuf_addstr(&subtab_lock, ".lock");
+       /*
+        * Lock all tables in the user-provided range. This is the slice of our
+        * stack which we'll compact.
+        */
+       REFTABLE_CALLOC_ARRAY(table_locks, last - first + 1);
+       for (i = first; i <= last; i++) {
+               stack_filename(&table_name, st, reader_name(st->readers[i]));
 
-               sublock_file_fd = open(subtab_lock.buf,
-                                      O_EXCL | O_CREAT | O_WRONLY, 0666);
-               if (sublock_file_fd >= 0) {
-                       close(sublock_file_fd);
-               } else if (sublock_file_fd < 0) {
-                       if (errno == EEXIST) {
+               err = hold_lock_file_for_update(&table_locks[i - first],
+                                               table_name.buf, LOCK_NO_DEREF);
+               if (err < 0) {
+                       if (errno == EEXIST)
                                err = 1;
-                       } else {
+                       else
                                err = REFTABLE_IO_ERROR;
-                       }
+                       goto done;
                }
 
-               subtable_locks[j] = subtab_lock.buf;
-               delete_on_success[j] = subtab_file_name.buf;
-               j++;
-
-               if (err != 0)
+               /*
+                * We need to close the lockfiles as we might otherwise easily
+                * run into file descriptor exhaustion when we compress a lot
+                * of tables.
+                */
+               err = close_lock_file_gently(&table_locks[i - first]);
+               if (err < 0) {
+                       err = REFTABLE_IO_ERROR;
                        goto done;
+               }
        }
 
-       err = unlink(lock_file_name.buf);
-       if (err < 0)
+       /*
+        * We have locked all tables in our range and can thus release the
+        * "tables.list" lock while compacting the locked tables. This allows
+        * concurrent updates to the stack to proceed.
+        */
+       err = rollback_lock_file(&tables_list_lock);
+       if (err < 0) {
+               err = REFTABLE_IO_ERROR;
                goto done;
-       have_lock = 0;
-
-       err = stack_compact_locked(st, first, last, &temp_tab_file_name,
-                                  expiry);
-       /* Compaction + tombstones can create an empty table out of non-empty
-        * tables. */
-       is_empty_table = (err == REFTABLE_EMPTY_TABLE_ERROR);
-       if (is_empty_table) {
-               err = 0;
        }
-       if (err < 0)
-               goto done;
 
-       lock_file_fd =
-               open(lock_file_name.buf, O_EXCL | O_CREAT | O_WRONLY, 0666);
-       if (lock_file_fd < 0) {
-               if (errno == EEXIST) {
+       /*
+        * Compact the now-locked tables into a new table. Note that compacting
+        * these tables may end up with an empty new table in case tombstones
+        * end up cancelling out all refs in that range.
+        */
+       err = stack_compact_locked(st, first, last, expiry, &new_table);
+       if (err < 0) {
+               if (err != REFTABLE_EMPTY_TABLE_ERROR)
+                       goto done;
+               is_empty_table = 1;
+       }
+
+       /*
+        * Now that we have written the new, compacted table we need to re-lock
+        * "tables.list". We'll then replace the compacted range of tables with
+        * the new table.
+        */
+       err = hold_lock_file_for_update(&tables_list_lock, st->list_file,
+                                       LOCK_NO_DEREF);
+       if (err < 0) {
+               if (errno == EEXIST)
                        err = 1;
-               } else {
+               else
                        err = REFTABLE_IO_ERROR;
-               }
                goto done;
        }
-       have_lock = 1;
+
        if (st->config.default_permissions) {
-               if (chmod(lock_file_name.buf, st->config.default_permissions) < 0) {
+               if (chmod(get_lock_file_path(&tables_list_lock),
+                         st->config.default_permissions) < 0) {
                        err = REFTABLE_IO_ERROR;
                        goto done;
                }
        }
 
-       format_name(&new_table_name, st->readers[first]->min_update_index,
-                   st->readers[last]->max_update_index);
-       strbuf_addstr(&new_table_name, ".ref");
-
-       stack_filename(&new_table_path, st, new_table_name.buf);
-
+       /*
+        * If the resulting compacted table is not empty, then we need to move
+        * it into place now.
+        */
        if (!is_empty_table) {
-               /* retry? */
-               err = rename(temp_tab_file_name.buf, new_table_path.buf);
+               format_name(&new_table_name, st->readers[first]->min_update_index,
+                           st->readers[last]->max_update_index);
+               strbuf_addstr(&new_table_name, ".ref");
+               stack_filename(&new_table_path, st, new_table_name.buf);
+
+               err = rename_tempfile(&new_table, new_table_path.buf);
                if (err < 0) {
                        err = REFTABLE_IO_ERROR;
                        goto done;
                }
        }
 
-       for (i = 0; i < first; i++) {
-               strbuf_addstr(&ref_list_contents, st->readers[i]->name);
-               strbuf_addstr(&ref_list_contents, "\n");
-       }
-       if (!is_empty_table) {
-               strbuf_addbuf(&ref_list_contents, &new_table_name);
-               strbuf_addstr(&ref_list_contents, "\n");
-       }
-       for (i = last + 1; i < st->merged->stack_len; i++) {
-               strbuf_addstr(&ref_list_contents, st->readers[i]->name);
-               strbuf_addstr(&ref_list_contents, "\n");
-       }
-
-       err = write_in_full(lock_file_fd, ref_list_contents.buf, ref_list_contents.len);
-       if (err < 0) {
-               err = REFTABLE_IO_ERROR;
-               unlink(new_table_path.buf);
-               goto done;
-       }
-
-       err = fsync_component(FSYNC_COMPONENT_REFERENCE, lock_file_fd);
+       /*
+        * Write the new "tables.list" contents with the compacted table we
+        * have just written. In case the compacted table became empty we
+        * simply skip writing it.
+        */
+       for (i = 0; i < first; i++)
+               strbuf_addf(&tables_list_buf, "%s\n", st->readers[i]->name);
+       if (!is_empty_table)
+               strbuf_addf(&tables_list_buf, "%s\n", new_table_name.buf);
+       for (i = last + 1; i < st->merged->stack_len; i++)
+               strbuf_addf(&tables_list_buf, "%s\n", st->readers[i]->name);
+
+       err = write_in_full(get_lock_file_fd(&tables_list_lock),
+                           tables_list_buf.buf, tables_list_buf.len);
        if (err < 0) {
                err = REFTABLE_IO_ERROR;
                unlink(new_table_path.buf);
                goto done;
        }
 
-       err = close(lock_file_fd);
-       lock_file_fd = -1;
+       err = fsync_component(FSYNC_COMPONENT_REFERENCE, get_lock_file_fd(&tables_list_lock));
        if (err < 0) {
                err = REFTABLE_IO_ERROR;
                unlink(new_table_path.buf);
                goto done;
        }
 
-       err = rename(lock_file_name.buf, st->list_file);
+       err = commit_lock_file(&tables_list_lock);
        if (err < 0) {
                err = REFTABLE_IO_ERROR;
                unlink(new_table_path.buf);
                goto done;
        }
-       have_lock = 0;
 
-       /* Reload the stack before deleting. On windows, we can only delete the
-          files after we closed them.
-       */
+       /*
+        * Reload the stack before deleting the compacted tables. We can only
+        * delete the files after we closed them on Windows, so this needs to
+        * happen first.
+        */
        err = reftable_stack_reload_maybe_reuse(st, first < last);
+       if (err < 0)
+               goto done;
 
-       listp = delete_on_success;
-       while (*listp) {
-               if (strcmp(*listp, new_table_path.buf)) {
-                       unlink(*listp);
-               }
-               listp++;
+       /*
+        * Delete the old tables. They may still be in use by concurrent
+        * readers, so it is expected that unlinking tables may fail.
+        */
+       for (i = first; i <= last; i++) {
+               struct lock_file *table_lock = &table_locks[i - first];
+               char *table_path = get_locked_file_path(table_lock);
+               unlink(table_path);
+               free(table_path);
        }
 
 done:
-       free_names(delete_on_success);
+       rollback_lock_file(&tables_list_lock);
+       for (i = first; table_locks && i <= last; i++)
+               rollback_lock_file(&table_locks[i - first]);
+       reftable_free(table_locks);
 
-       if (subtable_locks) {
-               listp = subtable_locks;
-               while (*listp) {
-                       unlink(*listp);
-                       listp++;
-               }
-               free_names(subtable_locks);
-       }
-       if (lock_file_fd >= 0) {
-               close(lock_file_fd);
-               lock_file_fd = -1;
-       }
-       if (have_lock) {
-               unlink(lock_file_name.buf);
-       }
+       delete_tempfile(&new_table);
        strbuf_release(&new_table_name);
        strbuf_release(&new_table_path);
-       strbuf_release(&ref_list_contents);
-       strbuf_release(&temp_tab_file_name);
-       strbuf_release(&lock_file_name);
+
+       strbuf_release(&tables_list_buf);
+       strbuf_release(&table_name);
        return err;
 }
 
index 509f4866236024f5546100da7121996f1c963e08..7336757cf534058dd6f3e8346d15874036c5b763 100644 (file)
@@ -468,8 +468,6 @@ static void test_reftable_stack_add(void)
                logs[i].refname = xstrdup(buf);
                logs[i].update_index = N + i + 1;
                logs[i].value_type = REFTABLE_LOG_UPDATE;
-
-               logs[i].value.update.new_hash = reftable_malloc(GIT_SHA1_RAWSZ);
                logs[i].value.update.email = xstrdup("identity@invalid");
                set_test_hash(logs[i].value.update.new_hash, i);
        }
@@ -547,16 +545,17 @@ static void test_reftable_stack_log_normalize(void)
        };
        struct reftable_stack *st = NULL;
        char *dir = get_tmp_dir(__LINE__);
-
-       uint8_t h1[GIT_SHA1_RAWSZ] = { 0x01 }, h2[GIT_SHA1_RAWSZ] = { 0x02 };
-
-       struct reftable_log_record input = { .refname = "branch",
-                                            .update_index = 1,
-                                            .value_type = REFTABLE_LOG_UPDATE,
-                                            .value = { .update = {
-                                                               .new_hash = h1,
-                                                               .old_hash = h2,
-                                                       } } };
+       struct reftable_log_record input = {
+               .refname = "branch",
+               .update_index = 1,
+               .value_type = REFTABLE_LOG_UPDATE,
+               .value = {
+                       .update = {
+                               .new_hash = { 1 },
+                               .old_hash = { 2 },
+                       },
+               },
+       };
        struct reftable_log_record dest = {
                .update_index = 0,
        };
@@ -627,8 +626,6 @@ static void test_reftable_stack_tombstone(void)
                logs[i].update_index = 42;
                if (i % 2 == 0) {
                        logs[i].value_type = REFTABLE_LOG_UPDATE;
-                       logs[i].value.update.new_hash =
-                               reftable_malloc(GIT_SHA1_RAWSZ);
                        set_test_hash(logs[i].value.update.new_hash, i);
                        logs[i].value.update.email =
                                xstrdup("identity@invalid");
@@ -810,7 +807,6 @@ static void test_reflog_expire(void)
                logs[i].update_index = i;
                logs[i].value_type = REFTABLE_LOG_UPDATE;
                logs[i].value.update.time = i;
-               logs[i].value.update.new_hash = reftable_malloc(GIT_SHA1_RAWSZ);
                logs[i].value.update.email = xstrdup("identity@invalid");
                set_test_hash(logs[i].value.update.new_hash, i);
        }
index 6b74a8151436144225c2e579340dfc3426a26470..5d8b6dede50414b750f39778c6070731b096d218 100644 (file)
@@ -12,7 +12,9 @@ https://developers.google.com/open-source/licenses/bsd
 /* This header glues the reftable library to the rest of Git */
 
 #include "git-compat-util.h"
+#include "lockfile.h"
 #include "strbuf.h"
+#include "tempfile.h"
 #include "hash-ll.h" /* hash ID, sizes.*/
 #include "dir.h" /* remove_dir_recursively, for tests.*/
 
index 7aacb51b65cca69ec6acd0c879dd0aa5b15978b3..e15b416944dfb21f752093fab777fabf475d4f31 100644 (file)
@@ -14,6 +14,7 @@
 #include "read-cache-ll.h"
 #include "remote.h"
 #include "setup.h"
+#include "loose.h"
 #include "submodule-config.h"
 #include "sparse-index.h"
 #include "trace2.h"
@@ -104,6 +105,15 @@ void repo_set_hash_algo(struct repository *repo, int hash_algo)
        repo->hash_algo = &hash_algos[hash_algo];
 }
 
+void repo_set_compat_hash_algo(struct repository *repo, int algo)
+{
+       if (hash_algo_by_ptr(repo->hash_algo) == algo)
+               BUG("hash_algo and compat_hash_algo match");
+       repo->compat_hash_algo = algo ? &hash_algos[algo] : NULL;
+       if (repo->compat_hash_algo)
+               repo_read_loose_object_map(repo);
+}
+
 void repo_set_ref_storage_format(struct repository *repo, unsigned int format)
 {
        repo->ref_storage_format = format;
@@ -189,6 +199,7 @@ int repo_init(struct repository *repo,
                goto error;
 
        repo_set_hash_algo(repo, format.hash_algo);
+       repo_set_compat_hash_algo(repo, format.compat_hash_algo);
        repo_set_ref_storage_format(repo, format.ref_storage_format);
        repo->repository_format_worktree_config = format.worktree_config;
 
@@ -199,6 +210,9 @@ int repo_init(struct repository *repo,
        if (worktree)
                repo_set_worktree(repo, worktree);
 
+       if (repo->compat_hash_algo)
+               repo_read_loose_object_map(repo);
+
        clear_repository_format(&format);
        return 0;
 
index 9bf1e33d2591bbd8d62c971dbc70790d250b021f..268436779c8f315228aef0dde9039f9ebf4f723e 100644 (file)
@@ -163,6 +163,9 @@ struct repository {
        /* Repository's current hash algorithm, as serialized on disk. */
        const struct git_hash_algo *hash_algo;
 
+       /* Repository's compatibility hash algorithm. */
+       const struct git_hash_algo *compat_hash_algo;
+
        /* Repository's reference storage format, as serialized on disk. */
        unsigned int ref_storage_format;
 
@@ -205,6 +208,7 @@ void repo_set_gitdir(struct repository *repo, const char *root,
                     const struct set_gitdir_args *extra_args);
 void repo_set_worktree(struct repository *repo, const char *path);
 void repo_set_hash_algo(struct repository *repo, int algo);
+void repo_set_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);
 RESULT_MUST_BE_USED
index 45893651c22f2669da8d5fe38a0b809e650d6f6d..7e45f765d9fe16c8380f457c0d40e385cbf88f55 100644 (file)
@@ -81,7 +81,7 @@ static void mark_tree_contents_uninteresting(struct repository *r,
        if (parse_tree_gently(tree, 1) < 0)
                return;
 
-       init_tree_desc(&desc, tree->buffer, tree->size);
+       init_tree_desc(&desc, &tree->object.oid, tree->buffer, tree->size);
        while (tree_entry(&desc, &entry)) {
                switch (object_type(entry.mode)) {
                case OBJ_TREE:
@@ -188,7 +188,7 @@ static void add_children_by_path(struct repository *r,
        if (parse_tree_gently(tree, 1) < 0)
                return;
 
-       init_tree_desc(&desc, tree->buffer, tree->size);
+       init_tree_desc(&desc, &tree->object.oid, tree->buffer, tree->size);
        while (tree_entry(&desc, &entry)) {
                switch (object_type(entry.mode)) {
                case OBJ_TREE:
@@ -2358,7 +2358,7 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
        } else if (skip_prefix(arg, "--ancestry-path=", &optarg)) {
                struct commit *c;
                struct object_id oid;
-               const char *msg = _("could not get commit for ancestry-path argument %s");
+               const char *msg = _("could not get commit for --ancestry-path argument %s");
 
                revs->ancestry_path = 1;
                revs->simplify_history = 0;
index 5c6f541126c74bb1c70adef36520213b31d3265b..4e14fa6541c7012c8549e84957c47f386efbc7ec 100644 (file)
@@ -332,7 +332,7 @@ static int has_conforming_footer(struct strbuf *sb, struct strbuf *sob,
                sb->buf[sb->len - ignore_footer] = '\0';
        }
 
-       trailer_info_get(&info, sb->buf, &opts);
+       trailer_info_get(&opts, sb->buf, &info);
 
        if (ignore_footer)
                sb->buf[sb->len - ignore_footer] = saved_char;
@@ -461,10 +461,22 @@ static void free_message(struct commit *commit, struct commit_message *msg)
        repo_unuse_commit_buffer(the_repository, commit, msg->message);
 }
 
+const char *rebase_resolvemsg =
+N_("Resolve all conflicts manually, mark them as resolved with\n"
+"\"git add/rm <conflicted_files>\", then run \"git rebase --continue\".\n"
+"You can instead skip this commit: run \"git rebase --skip\".\n"
+"To abort and get back to the state before \"git rebase\", run "
+"\"git rebase --abort\".");
+
 static void print_advice(struct repository *r, int show_hint,
                         struct replay_opts *opts)
 {
-       char *msg = getenv("GIT_CHERRY_PICK_HELP");
+       const char *msg;
+
+       if (is_rebase_i(opts))
+               msg = rebase_resolvemsg;
+       else
+               msg = getenv("GIT_CHERRY_PICK_HELP");
 
        if (msg) {
                advise("%s\n", msg);
index dcef7bb99c08b0f8e5a29905f8f3a4d6d2d34a45..437eabd38af0398ae9f72b5badd2f691290aa694 100644 (file)
@@ -14,6 +14,8 @@ const char *rebase_path_todo(void);
 const char *rebase_path_todo_backup(void);
 const char *rebase_path_dropped(void);
 
+extern const char *rebase_resolvemsg;
+
 #define APPEND_SIGNOFF_DEDUP (1u << 0)
 
 enum replay_action {
diff --git a/setup.c b/setup.c
index e3b76e84b5b8676fdd23b7709800e3df24d8c08d..f4b32f76e3d86b46dbd7713195592906b73b9571 100644 (file)
--- a/setup.c
+++ b/setup.c
@@ -591,6 +591,25 @@ static enum extension_result handle_extension(const char *var,
                                     "extensions.objectformat", value);
                data->hash_algo = format;
                return EXTENSION_OK;
+       } else if (!strcmp(ext, "compatobjectformat")) {
+               struct string_list_item *item;
+               int format;
+
+               if (!value)
+                       return config_error_nonbool(var);
+               format = hash_algo_by_name(value);
+               if (format == GIT_HASH_UNKNOWN)
+                       return error(_("invalid value for '%s': '%s'"),
+                                    "extensions.compatobjectformat", value);
+               /* For now only support compatObjectFormat being specified once. */
+               for_each_string_list_item(item, &data->v1_only_extensions) {
+                       if (!strcmp(item->string, "compatobjectformat"))
+                               return error(_("'%s' already specified as '%s'"),
+                                       "extensions.compatobjectformat",
+                                       hash_algos[data->compat_hash_algo].name);
+               }
+               data->compat_hash_algo = format;
+               return EXTENSION_OK;
        } else if (!strcmp(ext, "refstorage")) {
                unsigned int format;
 
@@ -1243,6 +1262,32 @@ static const char *allowed_bare_repo_to_string(
        return NULL;
 }
 
+static int is_implicit_bare_repo(const char *path)
+{
+       /*
+        * what we found is a ".git" directory at the root of
+        * the working tree.
+        */
+       if (ends_with_path_components(path, ".git"))
+               return 1;
+
+       /*
+        * we are inside $GIT_DIR of a secondary worktree of a
+        * non-bare repository.
+        */
+       if (strstr(path, "/.git/worktrees/"))
+               return 1;
+
+       /*
+        * we are inside $GIT_DIR of a worktree of a non-embedded
+        * submodule, whose superproject is not a bare repository.
+        */
+       if (strstr(path, "/.git/modules/"))
+               return 1;
+
+       return 0;
+}
+
 /*
  * We cannot decide in this function whether we are in the work tree or
  * not, since the config can only be read _after_ this function was called.
@@ -1372,7 +1417,7 @@ static enum discovery_result setup_git_directory_gently_1(struct strbuf *dir,
                if (is_git_directory(dir->buf)) {
                        trace2_data_string("setup", NULL, "implicit-bare-repository", dir->buf);
                        if (get_allowed_bare_repo() == ALLOWED_BARE_REPO_EXPLICIT &&
-                           !ends_with_path_components(dir->buf, ".git"))
+                           !is_implicit_bare_repo(dir->buf))
                                return GIT_DIR_DISALLOWED_BARE;
                        if (!ensure_valid_ownership(NULL, NULL, dir->buf, report))
                                return GIT_DIR_INVALID_OWNERSHIP;
@@ -1577,6 +1622,8 @@ const char *setup_git_directory_gently(int *nongit_ok)
                }
                if (startup_info->have_repository) {
                        repo_set_hash_algo(the_repository, repo_fmt.hash_algo);
+                       repo_set_compat_hash_algo(the_repository,
+                                                 repo_fmt.compat_hash_algo);
                        repo_set_ref_storage_format(the_repository,
                                                    repo_fmt.ref_storage_format);
                        the_repository->repository_format_worktree_config =
@@ -1672,6 +1719,7 @@ void check_repository_format(struct repository_format *fmt)
        check_repository_format_gently(get_git_dir(), fmt, NULL);
        startup_info->have_repository = 1;
        repo_set_hash_algo(the_repository, fmt->hash_algo);
+       repo_set_compat_hash_algo(the_repository, fmt->compat_hash_algo);
        repo_set_ref_storage_format(the_repository,
                                    fmt->ref_storage_format);
        the_repository->repository_format_worktree_config =
@@ -1968,7 +2016,6 @@ void create_reference_database(unsigned int ref_storage_format,
 static int create_default_files(const char *template_path,
                                const char *original_git_dir,
                                const struct repository_format *fmt,
-                               int prev_bare_repository,
                                int init_shared_repository)
 {
        struct stat st1;
@@ -2003,34 +2050,8 @@ static int create_default_files(const char *template_path,
         */
        if (init_shared_repository != -1)
                set_shared_repository(init_shared_repository);
-       /*
-        * TODO: heed core.bare from config file in templates if no
-        *       command-line override given
-        */
-       is_bare_repository_cfg = prev_bare_repository || !work_tree;
-       /* TODO (continued):
-        *
-        * Unfortunately, the line above is equivalent to
-        *    is_bare_repository_cfg = !work_tree;
-        * which ignores the config entirely even if no `--[no-]bare`
-        * command line option was present.
-        *
-        * To see why, note that before this function, there was this call:
-        *    prev_bare_repository = is_bare_repository()
-        * expanding the right hand side:
-        *                 = is_bare_repository_cfg && !get_git_work_tree()
-        *                 = is_bare_repository_cfg && !work_tree
-        * note that the last simplification above is valid because nothing
-        * calls repo_init() or set_git_work_tree() between any of the
-        * relevant calls in the code, and thus the !get_git_work_tree()
-        * calls will return the same result each time.  So, what we are
-        * interested in computing is the right hand side of the line of
-        * code just above this comment:
-        *     prev_bare_repository || !work_tree
-        *        = is_bare_repository_cfg && !work_tree || !work_tree
-        *        = !work_tree
-        * because "A && !B || !B == !B" for all boolean values of A & B.
-        */
+
+       is_bare_repository_cfg = !work_tree;
 
        /*
         * We would have created the above under user's umask -- under
@@ -2182,7 +2203,6 @@ int init_db(const char *git_dir, const char *real_git_dir,
        int exist_ok = flags & INIT_DB_EXIST_OK;
        char *original_git_dir = real_pathdup(git_dir, 1);
        struct repository_format repo_fmt = REPOSITORY_FORMAT_INIT;
-       int prev_bare_repository;
 
        if (real_git_dir) {
                struct stat st;
@@ -2208,7 +2228,6 @@ int init_db(const char *git_dir, const char *real_git_dir,
 
        safe_create_dir(git_dir, 0);
 
-       prev_bare_repository = is_bare_repository();
 
        /* Check to see if the repository version is right.
         * Note that a newly created repository does not have
@@ -2221,8 +2240,7 @@ int init_db(const char *git_dir, const char *real_git_dir,
        validate_ref_storage_format(&repo_fmt, ref_storage_format);
 
        reinit = create_default_files(template_dir, original_git_dir,
-                                     &repo_fmt, prev_bare_repository,
-                                     init_shared_repository);
+                                     &repo_fmt, init_shared_repository);
 
        /*
         * Now that we have set up both the hash algorithm and the ref storage
diff --git a/setup.h b/setup.h
index 3599aec93c5ac0b72aafaf3cdcb030831cc53e3b..d88bb37aafb3c59cb091e7f7b02fc421cde6c2b2 100644 (file)
--- a/setup.h
+++ b/setup.h
@@ -115,6 +115,7 @@ struct repository_format {
        int worktree_config;
        int is_bare;
        int hash_algo;
+       int compat_hash_algo;
        unsigned int ref_storage_format;
        int sparse_index;
        char *work_tree;
index 266a67342be7245ae3c1dfae5a1a5ad6647f95df..5d8907151fec3982e906025fc19c2165ef4572d3 100644 (file)
@@ -220,7 +220,7 @@ int demultiplex_sideband(const char *me, int status,
                        }
 
                        strbuf_addch(scratch, *brk);
-                       xwrite(2, scratch->buf, scratch->len);
+                       write_in_full(2, scratch->buf, scratch->len);
                        strbuf_reset(scratch);
 
                        b = brk + 1;
@@ -247,7 +247,7 @@ cleanup:
                die("%s", scratch->buf);
        if (scratch->len) {
                strbuf_addch(scratch, '\n');
-               xwrite(2, scratch->buf, scratch->len);
+               write_in_full(2, scratch->buf, scratch->len);
        }
        strbuf_release(scratch);
        return 1;
index 5e21e84f3884eb8c787ab82a5d1360d2b7cffb74..87572459e4b873cdc78f36867ba782e022eb13f4 100644 (file)
@@ -532,7 +532,7 @@ test_expect_success 'blame -L :funcname with userdiff driver' '
                "$(cat file.template)" &&
        test_commit --author "B <B@test.git>" \
                "change" "$fortran_file" \
-               "$(cat file.template | sed -e s/ChangeMe/IWasChanged/)" &&
+               "$(sed -e s/ChangeMe/IWasChanged/ file.template)" &&
        check_count -f "$fortran_file" -L:RIGHT A 3 B 1
 '
 
diff --git a/t/helper/test-delete-gpgsig.c b/t/helper/test-delete-gpgsig.c
new file mode 100644 (file)
index 0000000..e36831a
--- /dev/null
@@ -0,0 +1,62 @@
+#include "test-tool.h"
+#include "gpg-interface.h"
+#include "strbuf.h"
+
+
+int cmd__delete_gpgsig(int argc, const char **argv)
+{
+       struct strbuf buf = STRBUF_INIT;
+       const char *pattern = "gpgsig";
+       const char *bufptr, *tail, *eol;
+       int deleting = 0;
+       size_t plen;
+
+       if (argc >= 2) {
+               pattern = argv[1];
+               argv++;
+               argc--;
+       }
+
+       plen = strlen(pattern);
+       strbuf_read(&buf, 0, 0);
+
+       if (!strcmp(pattern, "trailer")) {
+               size_t payload_size = parse_signed_buffer(buf.buf, buf.len);
+               fwrite(buf.buf, 1, payload_size, stdout);
+               fflush(stdout);
+               return 0;
+       }
+
+       bufptr = buf.buf;
+       tail = bufptr + buf.len;
+
+       while (bufptr < tail) {
+               /* Find the end of the line */
+               eol = memchr(bufptr, '\n', tail - bufptr);
+               if (!eol)
+                       eol = tail;
+
+               /* Drop continuation lines */
+               if (deleting && (bufptr < eol) && (bufptr[0] == ' ')) {
+                       bufptr = eol + 1;
+                       continue;
+               }
+               deleting = 0;
+
+               /* Does the line match the prefix? */
+               if (((bufptr + plen) < eol) &&
+                   !memcmp(bufptr, pattern, plen) &&
+                   (bufptr[plen] == ' ')) {
+                       deleting = 1;
+                       bufptr = eol + 1;
+                       continue;
+               }
+
+               /* Print all other lines */
+               fwrite(bufptr, 1, (eol - bufptr) + 1, stdout);
+               bufptr = eol + 1;
+       }
+       fflush(stdout);
+
+       return 0;
+}
index 482a1e58a4b6ec922f1d5abd39848acda79a78bf..80a946b847e67da00d8a6fc9884ab6d7cdfe2903 100644 (file)
@@ -20,6 +20,7 @@ static struct test_cmd cmds[] = {
        { "crontab", cmd__crontab },
        { "csprng", cmd__csprng },
        { "date", cmd__date },
+       { "delete-gpgsig", cmd__delete_gpgsig },
        { "delta", cmd__delta },
        { "dir-iterator", cmd__dir_iterator },
        { "drop-caches", cmd__drop_caches },
index b1be7cfcf593d0d34ac5f851738ae2fbdf93478f..2808b924191f21ee4ac18462397efd86289eb0c0 100644 (file)
@@ -14,6 +14,7 @@ int cmd__crontab(int argc, const char **argv);
 int cmd__csprng(int argc, const char **argv);
 int cmd__date(int argc, const char **argv);
 int cmd__delta(int argc, const char **argv);
+int cmd__delete_gpgsig(int argc, const char **argv);
 int cmd__dir_iterator(int argc, const char **argv);
 int cmd__drop_caches(int argc, const char **argv);
 int cmd__dump_cache_tree(int argc, const char **argv);
index 32b34733790834362612816eb5eac63cc6acdf33..57b9b2db9b3f8244dfec5a6cf5b6dc6a6dcb1138 100644 (file)
@@ -71,8 +71,8 @@ test_cmp_branch_tree () {
                find . -type d -name .git -prune -o -type f -print
        ) | sort >module-git-"$1".list &&
        test_cmp module-cvs-"$1".list module-git-"$1".list &&
-       cat module-cvs-"$1".list | while read f
+       while read f
        do
                test_cmp_branch_file "$1" "$f" || return 1
-       done
+       done <module-cvs-"$1".list
 }
index fcfc992b5b02e885fb3b8c337dfdc41d986a9052..412e4b450b16fbd63300e70faf026593fc073f57 100755 (executable)
@@ -33,7 +33,7 @@ do
 done
 
 git ls-tree -r HEAD >GEN_src_list
-nr_src_files=$(cat GEN_src_list | wc -l)
+nr_src_files=$(wc -l <GEN_src_list)
 
 src_branch=$(git symbolic-ref --short HEAD)
 
index 736516cc6a478ee548b79f7d9a0f2cae0b51430e..bf3bf604abe3470f19cc5a10e5c88c905cb28329 100755 (executable)
@@ -40,7 +40,7 @@ test_expect_success 'final setup + check rev-parse --git-dir' '
 
 test_expect_success 'check hash-object' '
        echo "foo" >bar &&
-       SHA=$(cat bar | git hash-object -w --stdin) &&
+       SHA=$(git hash-object -w --stdin <bar) &&
        test_path_is_file "$REAL/objects/$(objpath $SHA)"
 '
 
index e18b1602864ec432553266e89d3796e94741e8e0..3031256d143b154dc1ff5e55ae46a7ffd4c7cf6d 100755 (executable)
@@ -46,6 +46,7 @@ check_show () {
 TIME='1466000000 +0200'
 check_show iso8601 "$TIME" '2016-06-15 16:13:20 +0200'
 check_show iso8601-strict "$TIME" '2016-06-15T16:13:20+02:00'
+check_show iso8601-strict "$(echo "$TIME" | sed 's/+0200$/+0000/')" '2016-06-15T14:13:20Z'
 check_show rfc2822 "$TIME" 'Wed, 15 Jun 2016 16:13:20 +0200'
 check_show short "$TIME" '2016-06-15'
 check_show default "$TIME" 'Wed Jun 15 16:13:20 2016 +0200'
@@ -69,6 +70,14 @@ check_show 'format:%s' '123456789 +1234' 123456789
 check_show 'format:%s' '123456789 -1234' 123456789
 check_show 'format-local:%s' '123456789 -1234' 123456789
 
+# negative TZ offset
+TIME='1466000000 -0200'
+check_show iso8601 "$TIME" '2016-06-15 12:13:20 -0200'
+check_show iso8601-strict "$TIME" '2016-06-15T12:13:20-02:00'
+check_show rfc2822 "$TIME" 'Wed, 15 Jun 2016 12:13:20 -0200'
+check_show default "$TIME" 'Wed Jun 15 12:13:20 2016 -0200'
+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
index 837c8b7228b98e8e7b3dbfecd5046673554ef464..84172a3739094adc6cdf6496b016fe68e0b2a771 100755 (executable)
@@ -10,25 +10,24 @@ TEST_PASSES_SANITIZE_LEAK=true
 
 for trial in 0 1 2 3 4
 do
-       rm -f .git/index
-       echo frotz >infocom
-       git update-index --add infocom
-       echo xyzzy >infocom
-
-       files=$(git diff-files -p)
-       test_expect_success \
-       "Racy GIT trial #$trial part A" \
-       'test "" != "$files"'
-
+       test_expect_success "Racy git trial #$trial part A" '
+               rm -f .git/index &&
+               echo frotz >infocom &&
+               git update-index --add infocom &&
+               echo xyzzy >infocom &&
+
+               git diff-files -p >out &&
+               test_file_not_empty out
+       '
        sleep 1
-       echo xyzzy >cornerstone
-       git update-index --add cornerstone
 
-       files=$(git diff-files -p)
-       test_expect_success \
-       "Racy GIT trial #$trial part B" \
-       'test "" != "$files"'
+       test_expect_success "Racy git trial #$trial part B" '
+               echo xyzzy >cornerstone &&
+               git update-index --add cornerstone &&
 
+               git diff-files -p >out &&
+               test_file_not_empty out
+       '
 done
 
 test_done
index 1cb6aa6824321656264e427f299899acf0754357..46e74ad1072b1d662297204960d38d823534c208 100755 (executable)
@@ -239,7 +239,7 @@ test_expect_success 'grow / shrink' '
        echo value40 >> expect &&
        echo size >> in &&
        echo 64 39 >> expect &&
-       cat in | test-tool hashmap > out &&
+       test-tool hashmap <in >out &&
        test_cmp expect out
 
 '
index 1b55f59c237c59d625241aea56679fbf9bb3bb25..ad151a346708a5898eb5bdc536131baddafdf987 100755 (executable)
@@ -131,8 +131,8 @@ do
                test_when_finished "rm -f crlf.utf${i}.raw lf.utf${i}.raw" &&
                test_when_finished "git reset --hard HEAD^" &&
 
-               cat lf.utf8.raw | write_utf${i} >lf.utf${i}.raw &&
-               cat crlf.utf8.raw | write_utf${i} >crlf.utf${i}.raw &&
+               write_utf${i} <lf.utf8.raw >lf.utf${i}.raw &&
+               write_utf${i} <crlf.utf8.raw >crlf.utf${i}.raw &&
                cp crlf.utf${i}.raw eol.utf${i} &&
 
                cat >expectIndexLF <<-EOF &&
index 804885637954a57ba85e139591e6a0f5a5167034..d3cb2a1cb9edb8f9ad7480be6ec3e02b464046bd 100755 (executable)
@@ -29,9 +29,20 @@ expect_rejected () {
        grep -F "implicit-bare-repository:$pwd" "$pwd/trace.perf"
 }
 
-test_expect_success 'setup bare repo in worktree' '
+test_expect_success 'setup an embedded bare repo, secondary worktree and submodule' '
        git init outer-repo &&
-       git init --bare outer-repo/bare-repo
+       git init --bare --initial-branch=main outer-repo/bare-repo &&
+       git -C outer-repo worktree add ../outer-secondary &&
+       test_path_is_dir outer-secondary &&
+       (
+               cd outer-repo &&
+               test_commit A &&
+               git push bare-repo +HEAD:refs/heads/main &&
+               git -c protocol.file.allow=always \
+                       submodule add --name subn -- ./bare-repo subd
+       ) &&
+       test_path_is_dir outer-repo/.git/worktrees/outer-secondary &&
+       test_path_is_dir outer-repo/.git/modules/subn
 '
 
 test_expect_success 'safe.bareRepository unset' '
@@ -53,8 +64,7 @@ test_expect_success 'safe.bareRepository in the repository' '
        # safe.bareRepository must not be "explicit", otherwise
        # git config fails with "fatal: not in a git directory" (like
        # safe.directory)
-       test_config -C outer-repo/bare-repo safe.bareRepository \
-               all &&
+       test_config -C outer-repo/bare-repo safe.bareRepository all &&
        test_config_global safe.bareRepository explicit &&
        expect_rejected -C outer-repo/bare-repo
 '
@@ -86,4 +96,12 @@ test_expect_success 'no trace when "bare repository" is a subdir of .git' '
        expect_accepted_implicit -C outer-repo/.git/objects
 '
 
+test_expect_success 'no trace in $GIT_DIR of secondary worktree' '
+       expect_accepted_implicit -C outer-repo/.git/worktrees/outer-secondary
+'
+
+test_expect_success 'no trace in $GIT_DIR of a submodule' '
+       expect_accepted_implicit -C outer-repo/.git/modules/subn
+'
+
 test_done
index ec974867e4337ca503cb817fd79926193c518710..8bb2a8b453ce0bd6ce1960542e21d3bcd74567e0 100755 (executable)
@@ -210,6 +210,22 @@ test_expect_success 'superfluous value provided: boolean' '
        test_cmp expect actual
 '
 
+test_expect_success 'superfluous value provided: boolean, abbreviated' '
+       cat >expect <<-\EOF &&
+       error: option `yes'\'' takes no value
+       EOF
+       test_expect_code 129 env GIT_TEST_DISALLOW_ABBREVIATED_OPTIONS=false \
+       test-tool parse-options --ye=hi 2>actual &&
+       test_cmp expect actual &&
+
+       cat >expect <<-\EOF &&
+       error: option `no-yes'\'' takes no value
+       EOF
+       test_expect_code 129 env GIT_TEST_DISALLOW_ABBREVIATED_OPTIONS=false \
+       test-tool parse-options --no-ye=hi 2>actual &&
+       test_cmp expect actual
+'
+
 test_expect_success 'superfluous value provided: cmdmode' '
        cat >expect <<-\EOF &&
        error: option `mode1'\'' takes no value
index 4f2e0dcb02bda84409789b54f33237798c8f7e19..310a4500125f4a0d18f6c68a10cf1e8fe7d3beff 100755 (executable)
@@ -82,7 +82,7 @@ test_expect_success GETTEXT_ISO_LOCALE 'gettext.c: git init UTF-8 -> ISO-8859-1'
     printf "Bjó til tóma Git lind" >expect &&
     LANGUAGE=is LC_ALL="$is_IS_iso_locale" git init repo >actual &&
     test_when_finished "rm -rf repo" &&
-    grep "^$(cat expect | iconv -f UTF-8 -t ISO8859-1) " actual
+    grep "^$(iconv -f UTF-8 -t ISO8859-1 <expect) " actual
 '
 
 test_done
index 290b6eaaab16052b84ca3c4e5527c8910f67ebf0..13ef69b92f897b0e4bd5019ea3a7b48f15061e37 100755 (executable)
@@ -287,4 +287,235 @@ test_expect_success 'unsafe URLs are redacted by default' '
        grep "d0|main|def_param|.*|remote.origin.url:https://user:pwd@example.com" actual
 '
 
+# Confirm that the requested command produces a "cmd_name" and a
+# set of "def_param" events.
+#
+try_simple () {
+       test_when_finished "rm prop.perf actual" &&
+
+       cmd=$1 &&
+       cmd_name=$2 &&
+
+       test_config_global "trace2.configParams" "cfg.prop.*" &&
+       test_config_global "trace2.envvars" "ENV_PROP_FOO,ENV_PROP_BAR" &&
+
+       test_config_global "cfg.prop.foo" "red" &&
+
+       ENV_PROP_FOO=blue \
+               GIT_TRACE2_PERF="$(pwd)/prop.perf" \
+                       $cmd &&
+       perl "$TEST_DIRECTORY/t0211/scrub_perf.perl" <prop.perf >actual &&
+       grep "d0|main|cmd_name|.*|$cmd_name" actual &&
+       grep "d0|main|def_param|.*|cfg.prop.foo:red" actual &&
+       grep "d0|main|def_param|.*|ENV_PROP_FOO:blue" actual
+}
+
+# Representative mainstream builtin Git command dispatched
+# in run_builtin() in git.c
+#
+test_expect_success 'expect def_params for normal builtin command' '
+       try_simple "git version" "version"
+'
+
+# Representative query command dispatched in handle_options()
+# in git.c
+#
+test_expect_success 'expect def_params for query command' '
+       try_simple "git --man-path" "_query_"
+'
+
+# remote-curl.c does not use the builtin setup in git.c, so confirm
+# that executables built from remote-curl.c emit def_params.
+#
+# Also tests the dashed-command handling where "git foo" silently
+# spawns "git-foo".  Make sure that both commands should emit
+# def_params.
+#
+# Pass bogus arguments to remote-https and allow the command to fail
+# because we don't actually have a remote to fetch from.  We just want
+# to see the run-dashed code run an executable built from
+# remote-curl.c rather than git.c.  Confirm that we get def_param
+# events from both layers.
+#
+test_expect_success 'expect def_params for remote-curl and _run_dashed_' '
+       test_when_finished "rm prop.perf actual" &&
+
+       test_config_global "trace2.configParams" "cfg.prop.*" &&
+       test_config_global "trace2.envvars" "ENV_PROP_FOO,ENV_PROP_BAR" &&
+
+       test_config_global "cfg.prop.foo" "red" &&
+
+       test_might_fail env \
+               ENV_PROP_FOO=blue \
+               GIT_TRACE2_PERF="$(pwd)/prop.perf" \
+               git remote-http x y &&
+
+       perl "$TEST_DIRECTORY/t0211/scrub_perf.perl" <prop.perf >actual &&
+
+       grep "d0|main|cmd_name|.*|_run_dashed_" actual &&
+       grep "d0|main|def_param|.*|cfg.prop.foo:red" actual &&
+       grep "d0|main|def_param|.*|ENV_PROP_FOO:blue" actual &&
+
+       grep "d1|main|cmd_name|.*|remote-curl" actual &&
+       grep "d1|main|def_param|.*|cfg.prop.foo:red" actual &&
+       grep "d1|main|def_param|.*|ENV_PROP_FOO:blue" actual
+'
+
+# Similarly, `git-http-fetch` is not built from git.c so do a
+# trivial fetch so that the main git.c run-dashed code spawns
+# an executable built from http-fetch.c.  Confirm that we get
+# def_param events from both layers.
+#
+test_expect_success 'expect def_params for http-fetch and _run_dashed_' '
+       test_when_finished "rm prop.perf actual" &&
+
+       test_config_global "trace2.configParams" "cfg.prop.*" &&
+       test_config_global "trace2.envvars" "ENV_PROP_FOO,ENV_PROP_BAR" &&
+
+       test_config_global "cfg.prop.foo" "red" &&
+
+       test_might_fail env \
+               ENV_PROP_FOO=blue \
+               GIT_TRACE2_PERF="$(pwd)/prop.perf" \
+               git http-fetch --stdin file:/// <<-EOF &&
+       EOF
+
+       perl "$TEST_DIRECTORY/t0211/scrub_perf.perl" <prop.perf >actual &&
+
+       grep "d0|main|cmd_name|.*|_run_dashed_" actual &&
+       grep "d0|main|def_param|.*|cfg.prop.foo:red" actual &&
+       grep "d0|main|def_param|.*|ENV_PROP_FOO:blue" actual &&
+
+       grep "d1|main|cmd_name|.*|http-fetch" actual &&
+       grep "d1|main|def_param|.*|cfg.prop.foo:red" actual &&
+       grep "d1|main|def_param|.*|ENV_PROP_FOO:blue" actual
+'
+
+# Historically, alias expansion explicitly emitted the def_param
+# events (independent of whether the command was a builtin, a Git
+# command or arbitrary shell command) so that it wasn't dependent
+# upon the unpeeling of the alias. Let's make sure that we preserve
+# the net effect.
+#
+test_expect_success 'expect def_params during git alias expansion' '
+       test_when_finished "rm prop.perf actual" &&
+
+       test_config_global "trace2.configParams" "cfg.prop.*" &&
+       test_config_global "trace2.envvars" "ENV_PROP_FOO,ENV_PROP_BAR" &&
+
+       test_config_global "cfg.prop.foo" "red" &&
+
+       test_config_global "alias.xxx" "version" &&
+
+       ENV_PROP_FOO=blue \
+               GIT_TRACE2_PERF="$(pwd)/prop.perf" \
+                       git xxx &&
+
+       perl "$TEST_DIRECTORY/t0211/scrub_perf.perl" <prop.perf >actual &&
+
+       # "git xxx" is first mapped to "git-xxx" and the child will fail.
+       grep "d0|main|cmd_name|.*|_run_dashed_ (_run_dashed_)" actual &&
+
+       # We unpeel that and substitute "version" into "xxx" (giving
+       # "git version") and update the cmd_name event.
+       grep "d0|main|cmd_name|.*|_run_git_alias_ (_run_dashed_/_run_git_alias_)" actual &&
+
+       # These def_param events could be associated with either of the
+       # above cmd_name events.  It does not matter.
+       grep "d0|main|def_param|.*|cfg.prop.foo:red" actual &&
+       grep "d0|main|def_param|.*|ENV_PROP_FOO:blue" actual &&
+
+       # The "git version" child sees a different cmd_name hierarchy.
+       # Also test the def_param (only for completeness).
+       grep "d1|main|cmd_name|.*|version (_run_dashed_/_run_git_alias_/version)" actual &&
+       grep "d1|main|def_param|.*|cfg.prop.foo:red" actual &&
+       grep "d1|main|def_param|.*|ENV_PROP_FOO:blue" actual
+'
+
+test_expect_success 'expect def_params during shell alias expansion' '
+       test_when_finished "rm prop.perf actual" &&
+
+       test_config_global "trace2.configParams" "cfg.prop.*" &&
+       test_config_global "trace2.envvars" "ENV_PROP_FOO,ENV_PROP_BAR" &&
+
+       test_config_global "cfg.prop.foo" "red" &&
+
+       test_config_global "alias.xxx" "!git version" &&
+
+       ENV_PROP_FOO=blue \
+               GIT_TRACE2_PERF="$(pwd)/prop.perf" \
+                       git xxx &&
+
+       perl "$TEST_DIRECTORY/t0211/scrub_perf.perl" <prop.perf >actual &&
+
+       # "git xxx" is first mapped to "git-xxx" and the child will fail.
+       grep "d0|main|cmd_name|.*|_run_dashed_ (_run_dashed_)" actual &&
+
+       # We unpeel that and substitute "git version" for "git xxx" (as a
+       # shell command.  Another cmd_name event is emitted as we unpeel.
+       grep "d0|main|cmd_name|.*|_run_shell_alias_ (_run_dashed_/_run_shell_alias_)" actual &&
+
+       # These def_param events could be associated with either of the
+       # above cmd_name events.  It does not matter.
+       grep "d0|main|def_param|.*|cfg.prop.foo:red" actual &&
+       grep "d0|main|def_param|.*|ENV_PROP_FOO:blue" actual &&
+
+       # We get the following only because we used a git command for the
+       # shell command. In general, it could have been a shell script and
+       # we would see nothing.
+       #
+       # The child knows the cmd_name hierarchy so it includes it.
+       grep "d1|main|cmd_name|.*|version (_run_dashed_/_run_shell_alias_/version)" actual &&
+       grep "d1|main|def_param|.*|cfg.prop.foo:red" actual &&
+       grep "d1|main|def_param|.*|ENV_PROP_FOO:blue" actual
+'
+
+test_expect_success 'expect def_params during nested git alias expansion' '
+       test_when_finished "rm prop.perf actual" &&
+
+       test_config_global "trace2.configParams" "cfg.prop.*" &&
+       test_config_global "trace2.envvars" "ENV_PROP_FOO,ENV_PROP_BAR" &&
+
+       test_config_global "cfg.prop.foo" "red" &&
+
+       test_config_global "alias.xxx" "yyy" &&
+       test_config_global "alias.yyy" "version" &&
+
+       ENV_PROP_FOO=blue \
+               GIT_TRACE2_PERF="$(pwd)/prop.perf" \
+                       git xxx &&
+
+       perl "$TEST_DIRECTORY/t0211/scrub_perf.perl" <prop.perf >actual &&
+
+       # "git xxx" is first mapped to "git-xxx" and try to spawn "git-xxx"
+       # and the child will fail.
+       grep "d0|main|cmd_name|.*|_run_dashed_ (_run_dashed_)" actual &&
+       grep "d0|main|child_start|.*|.* class:dashed argv:\[git-xxx\]" actual &&
+
+       # We unpeel that and substitute "yyy" into "xxx" (giving "git yyy")
+       # and spawn "git-yyy" and the child will fail.
+       grep "d0|main|alias|.*|alias:xxx argv:\[yyy\]" actual &&
+       grep "d0|main|cmd_name|.*|_run_dashed_ (_run_dashed_/_run_dashed_)" actual &&
+       grep "d0|main|child_start|.*|.* class:dashed argv:\[git-yyy\]" actual &&
+
+       # We unpeel that and substitute "version" into "xxx" (giving
+       # "git version") and update the cmd_name event.
+       grep "d0|main|alias|.*|alias:yyy argv:\[version\]" actual &&
+       grep "d0|main|cmd_name|.*|_run_git_alias_ (_run_dashed_/_run_dashed_/_run_git_alias_)" actual &&
+
+       # These def_param events could be associated with any of the
+       # above cmd_name events.  It does not matter.
+       grep "d0|main|def_param|.*|cfg.prop.foo:red" actual >actual.matches &&
+       grep "d0|main|def_param|.*|ENV_PROP_FOO:blue" actual &&
+
+       # However, we do not want them repeated each time we unpeel.
+       test_line_count = 1 actual.matches &&
+
+       # The "git version" child sees a different cmd_name hierarchy.
+       # Also test the def_param (only for completeness).
+       grep "d1|main|cmd_name|.*|version (_run_dashed_/_run_dashed_/_run_git_alias_/version)" actual &&
+       grep "d1|main|def_param|.*|cfg.prop.foo:red" actual &&
+       grep "d1|main|def_param|.*|ENV_PROP_FOO:blue" actual
+'
+
 test_done
index e0c6482797e1203a9e97ec27714d5ea48aaf1b6c..e12b2219721c4e9e1f29dd5eeb2e9b787d9b78db 100755 (executable)
@@ -112,65 +112,65 @@ strlen () {
 
 run_tests () {
     type=$1
-    sha1=$2
+    oid=$2
     size=$3
     content=$4
     pretty_content=$5
 
-    batch_output="$sha1 $type $size
+    batch_output="$oid $type $size
 $content"
 
     test_expect_success "$type exists" '
-       git cat-file -e $sha1
+       git cat-file -e $oid
     '
 
     test_expect_success "Type of $type is correct" '
        echo $type >expect &&
-       git cat-file -t $sha1 >actual &&
+       git cat-file -t $oid >actual &&
        test_cmp expect actual
     '
 
     test_expect_success "Size of $type is correct" '
        echo $size >expect &&
-       git cat-file -s $sha1 >actual &&
+       git cat-file -s $oid >actual &&
        test_cmp expect actual
     '
 
     test_expect_success "Type of $type is correct using --allow-unknown-type" '
        echo $type >expect &&
-       git cat-file -t --allow-unknown-type $sha1 >actual &&
+       git cat-file -t --allow-unknown-type $oid >actual &&
        test_cmp expect actual
     '
 
     test_expect_success "Size of $type is correct using --allow-unknown-type" '
        echo $size >expect &&
-       git cat-file -s --allow-unknown-type $sha1 >actual &&
+       git cat-file -s --allow-unknown-type $oid >actual &&
        test_cmp expect actual
     '
 
     test -z "$content" ||
     test_expect_success "Content of $type is correct" '
        echo_without_newline "$content" >expect &&
-       git cat-file $type $sha1 >actual &&
+       git cat-file $type $oid >actual &&
        test_cmp expect actual
     '
 
     test_expect_success "Pretty content of $type is correct" '
        echo_without_newline "$pretty_content" >expect &&
-       git cat-file -p $sha1 >actual &&
+       git cat-file -p $oid >actual &&
        test_cmp expect actual
     '
 
     test -z "$content" ||
     test_expect_success "--batch output of $type is correct" '
        echo "$batch_output" >expect &&
-       echo $sha1 | git cat-file --batch >actual &&
+       echo $oid | git cat-file --batch >actual &&
        test_cmp expect actual
     '
 
     test_expect_success "--batch-check output of $type is correct" '
-       echo "$sha1 $type $size" >expect &&
-       echo_without_newline $sha1 | git cat-file --batch-check >actual &&
+       echo "$oid $type $size" >expect &&
+       echo_without_newline $oid | git cat-file --batch-check >actual &&
        test_cmp expect actual
     '
 
@@ -179,33 +179,33 @@ $content"
        test -z "$content" ||
                test_expect_success "--batch-command $opt output of $type content is correct" '
                echo "$batch_output" >expect &&
-               test_write_lines "contents $sha1" | git cat-file --batch-command $opt >actual &&
+               test_write_lines "contents $oid" | git cat-file --batch-command $opt >actual &&
                test_cmp expect actual
        '
 
        test_expect_success "--batch-command $opt output of $type info is correct" '
-               echo "$sha1 $type $size" >expect &&
-               test_write_lines "info $sha1" |
+               echo "$oid $type $size" >expect &&
+               test_write_lines "info $oid" |
                git cat-file --batch-command $opt >actual &&
                test_cmp expect actual
        '
     done
 
     test_expect_success "custom --batch-check format" '
-       echo "$type $sha1" >expect &&
-       echo $sha1 | git cat-file --batch-check="%(objecttype) %(objectname)" >actual &&
+       echo "$type $oid" >expect &&
+       echo $oid | git cat-file --batch-check="%(objecttype) %(objectname)" >actual &&
        test_cmp expect actual
     '
 
     test_expect_success "custom --batch-command format" '
-       echo "$type $sha1" >expect &&
-       echo "info $sha1" | git cat-file --batch-command="%(objecttype) %(objectname)" >actual &&
+       echo "$type $oid" >expect &&
+       echo "info $oid" | git cat-file --batch-command="%(objecttype) %(objectname)" >actual &&
        test_cmp expect actual
     '
 
     test_expect_success '--batch-check with %(rest)' '
        echo "$type this is some extra content" >expect &&
-       echo "$sha1    this is some extra content" |
+       echo "$oid    this is some extra content" |
                git cat-file --batch-check="%(objecttype) %(rest)" >actual &&
        test_cmp expect actual
     '
@@ -216,7 +216,7 @@ $content"
                echo "$size" &&
                echo "$content"
        } >expect &&
-       echo $sha1 | git cat-file --batch="%(objectsize)" >actual &&
+       echo $oid | git cat-file --batch="%(objectsize)" >actual &&
        test_cmp expect actual
     '
 
@@ -226,114 +226,154 @@ $content"
                echo "$type" &&
                echo "$content"
        } >expect &&
-       echo $sha1 | git cat-file --batch="%(objecttype)" >actual &&
+       echo $oid | git cat-file --batch="%(objecttype)" >actual &&
        test_cmp expect actual
     '
 }
 
 hello_content="Hello World"
 hello_size=$(strlen "$hello_content")
-hello_sha1=$(echo_without_newline "$hello_content" | git hash-object --stdin)
+hello_oid=$(echo_without_newline "$hello_content" | git hash-object --stdin)
 
 test_expect_success "setup" '
+       git config core.repositoryformatversion 1 &&
+       git config extensions.objectformat $test_hash_algo &&
+       git config extensions.compatobjectformat $test_compat_hash_algo &&
        echo_without_newline "$hello_content" > hello &&
        git update-index --add hello
 '
 
-run_tests 'blob' $hello_sha1 $hello_size "$hello_content" "$hello_content"
+run_blob_tests () {
+    oid=$1
 
-test_expect_success '--batch-command --buffer with flush for blob info' '
-       echo "$hello_sha1 blob $hello_size" >expect &&
-       test_write_lines "info $hello_sha1" "flush" |
+    run_tests 'blob' $oid $hello_size "$hello_content" "$hello_content"
+
+    test_expect_success '--batch-command --buffer with flush for blob info' '
+       echo "$oid blob $hello_size" >expect &&
+       test_write_lines "info $oid" "flush" |
        GIT_TEST_CAT_FILE_NO_FLUSH_ON_EXIT=1 \
        git cat-file --batch-command --buffer >actual &&
        test_cmp expect actual
-'
+    '
 
-test_expect_success '--batch-command --buffer without flush for blob info' '
+    test_expect_success '--batch-command --buffer without flush for blob info' '
        touch output &&
-       test_write_lines "info $hello_sha1" |
+       test_write_lines "info $oid" |
        GIT_TEST_CAT_FILE_NO_FLUSH_ON_EXIT=1 \
        git cat-file --batch-command --buffer >>output &&
        test_must_be_empty output
-'
+    '
+}
+
+hello_compat_oid=$(git rev-parse --output-object-format=$test_compat_hash_algo $hello_oid)
+run_blob_tests $hello_oid
+run_blob_tests $hello_compat_oid
 
 test_expect_success '--batch-check without %(rest) considers whole line' '
-       echo "$hello_sha1 blob $hello_size" >expect &&
-       git update-index --add --cacheinfo 100644 $hello_sha1 "white space" &&
+       echo "$hello_oid blob $hello_size" >expect &&
+       git update-index --add --cacheinfo 100644 $hello_oid "white space" &&
        test_when_finished "git update-index --remove \"white space\"" &&
        echo ":white space" | git cat-file --batch-check >actual &&
        test_cmp expect actual
 '
 
-tree_sha1=$(git write-tree)
+tree_oid=$(git write-tree)
+tree_compat_oid=$(git rev-parse --output-object-format=$test_compat_hash_algo $tree_oid)
 tree_size=$(($(test_oid rawsz) + 13))
-tree_pretty_content="100644 blob $hello_sha1   hello${LF}"
+tree_compat_size=$(($(test_oid --hash=compat rawsz) + 13))
+tree_pretty_content="100644 blob $hello_oid    hello${LF}"
+tree_compat_pretty_content="100644 blob $hello_compat_oid      hello${LF}"
 
-run_tests 'tree' $tree_sha1 $tree_size "" "$tree_pretty_content"
+run_tests 'tree' $tree_oid $tree_size "" "$tree_pretty_content"
+run_tests 'tree' $tree_compat_oid $tree_compat_size "" "$tree_compat_pretty_content"
 
 commit_message="Initial commit"
-commit_sha1=$(echo_without_newline "$commit_message" | git commit-tree $tree_sha1)
+commit_oid=$(echo_without_newline "$commit_message" | git commit-tree $tree_oid)
+commit_compat_oid=$(git rev-parse --output-object-format=$test_compat_hash_algo $commit_oid)
 commit_size=$(($(test_oid hexsz) + 137))
-commit_content="tree $tree_sha1
+commit_compat_size=$(($(test_oid --hash=compat hexsz) + 137))
+commit_content="tree $tree_oid
+author $GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL> $GIT_AUTHOR_DATE
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+
+$commit_message"
+
+commit_compat_content="tree $tree_compat_oid
 author $GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL> $GIT_AUTHOR_DATE
 committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
 
 $commit_message"
 
-run_tests 'commit' $commit_sha1 $commit_size "$commit_content" "$commit_content"
+run_tests 'commit' $commit_oid $commit_size "$commit_content" "$commit_content"
+run_tests 'commit' $commit_compat_oid $commit_compat_size "$commit_compat_content" "$commit_compat_content"
 
-tag_header_without_timestamp="object $hello_sha1
-type blob
+tag_header_without_oid="type blob
 tag hellotag
 tagger $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>"
+tag_header_without_timestamp="object $hello_oid
+$tag_header_without_oid"
+tag_compat_header_without_timestamp="object $hello_compat_oid
+$tag_header_without_oid"
 tag_description="This is a tag"
 tag_content="$tag_header_without_timestamp 0 +0000
 
 $tag_description"
+tag_compat_content="$tag_compat_header_without_timestamp 0 +0000
 
-tag_sha1=$(echo_without_newline "$tag_content" | git hash-object -t tag --stdin -w)
+$tag_description"
+
+tag_oid=$(echo_without_newline "$tag_content" | git hash-object -t tag --stdin -w)
 tag_size=$(strlen "$tag_content")
 
-run_tests 'tag' $tag_sha1 $tag_size "$tag_content" "$tag_content"
+tag_compat_oid=$(git rev-parse --output-object-format=$test_compat_hash_algo $tag_oid)
+tag_compat_size=$(strlen "$tag_compat_content")
+
+run_tests 'tag' $tag_oid $tag_size "$tag_content" "$tag_content"
+run_tests 'tag' $tag_compat_oid $tag_compat_size "$tag_compat_content" "$tag_compat_content"
 
 test_expect_success "Reach a blob from a tag pointing to it" '
        echo_without_newline "$hello_content" >expect &&
-       git cat-file blob $tag_sha1 >actual &&
+       git cat-file blob $tag_oid >actual &&
        test_cmp expect actual
 '
 
-for batch in batch batch-check batch-command
+for oid in $hello_oid $hello_compat_oid
 do
-    for opt in t s e p
+    for batch in batch batch-check batch-command
     do
+       for opt in t s e p
+       do
        test_expect_success "Passing -$opt with --$batch fails" '
-           test_must_fail git cat-file --$batch -$opt $hello_sha1
+           test_must_fail git cat-file --$batch -$opt $oid
        '
 
        test_expect_success "Passing --$batch with -$opt fails" '
-           test_must_fail git cat-file -$opt --$batch $hello_sha1
+           test_must_fail git cat-file -$opt --$batch $oid
        '
-    done
+       done
 
-    test_expect_success "Passing <type> with --$batch fails" '
-       test_must_fail git cat-file --$batch blob $hello_sha1
-    '
+       test_expect_success "Passing <type> with --$batch fails" '
+       test_must_fail git cat-file --$batch blob $oid
+       '
 
-    test_expect_success "Passing --$batch with <type> fails" '
-       test_must_fail git cat-file blob --$batch $hello_sha1
-    '
+       test_expect_success "Passing --$batch with <type> fails" '
+       test_must_fail git cat-file blob --$batch $oid
+       '
 
-    test_expect_success "Passing sha1 with --$batch fails" '
-       test_must_fail git cat-file --$batch $hello_sha1
-    '
+       test_expect_success "Passing oid with --$batch fails" '
+       test_must_fail git cat-file --$batch $oid
+       '
+    done
 done
 
-for opt in t s e p
+for oid in $hello_oid $hello_compat_oid
 do
-    test_expect_success "Passing -$opt with --follow-symlinks fails" '
-           test_must_fail git cat-file --follow-symlinks -$opt $hello_sha1
+    for opt in t s e p
+    do
+       test_expect_success "Passing -$opt with --follow-symlinks fails" '
+           test_must_fail git cat-file --follow-symlinks -$opt $oid
        '
+    done
 done
 
 test_expect_success "--batch-check for a non-existent named object" '
@@ -360,12 +400,12 @@ test_expect_success "--batch-check for a non-existent hash" '
 
 test_expect_success "--batch for an existent and a non-existent hash" '
        cat >expect <<-EOF &&
-       $tag_sha1 tag $tag_size
+       $tag_oid tag $tag_size
        $tag_content
        0000000000000000000000000000000000000000 missing
        EOF
 
-       printf "$tag_sha1\n0000000000000000000000000000000000000000" >in &&
+       printf "$tag_oid\n0000000000000000000000000000000000000000" >in &&
        git cat-file --batch <in >actual &&
        test_cmp expect actual
 '
@@ -386,112 +426,102 @@ test_expect_success 'empty --batch-check notices missing object' '
        test_cmp expect actual
 '
 
-batch_input="$hello_sha1
-$commit_sha1
-$tag_sha1
+batch_tests () {
+    boid=$1
+    loid=$2
+    lsize=$3
+    coid=$4
+    csize=$5
+    ccontent=$6
+    toid=$7
+    tsize=$8
+    tcontent=$9
+
+    batch_input="$boid
+$coid
+$toid
 deadbeef
 
 "
 
-printf "%s\0" \
-       "$hello_sha1 blob $hello_size" \
+    printf "%s\0" \
+       "$boid blob $hello_size" \
        "$hello_content" \
-       "$commit_sha1 commit $commit_size" \
-       "$commit_content" \
-       "$tag_sha1 tag $tag_size" \
-       "$tag_content" \
+       "$coid commit $csize" \
+       "$ccontent" \
+       "$toid tag $tsize" \
+       "$tcontent" \
        "deadbeef missing" \
        " missing" >batch_output
 
-test_expect_success '--batch with multiple sha1s gives correct format' '
+    test_expect_success '--batch with multiple oids gives correct format' '
        tr "\0" "\n" <batch_output >expect &&
        echo_without_newline "$batch_input" >in &&
        git cat-file --batch <in >actual &&
        test_cmp expect actual
-'
+    '
 
-test_expect_success '--batch, -z with multiple sha1s gives correct format' '
+    test_expect_success '--batch, -z with multiple oids gives correct format' '
        echo_without_newline_nul "$batch_input" >in &&
        tr "\0" "\n" <batch_output >expect &&
        git cat-file --batch -z <in >actual &&
        test_cmp expect actual
-'
+    '
 
-test_expect_success '--batch, -Z with multiple sha1s gives correct format' '
+    test_expect_success '--batch, -Z with multiple oids gives correct format' '
        echo_without_newline_nul "$batch_input" >in &&
        git cat-file --batch -Z <in >actual &&
        test_cmp batch_output actual
-'
+    '
 
-batch_check_input="$hello_sha1
-$tree_sha1
-$commit_sha1
-$tag_sha1
+batch_check_input="$boid
+$loid
+$coid
+$toid
 deadbeef
 
 "
 
-printf "%s\0" \
-       "$hello_sha1 blob $hello_size" \
-       "$tree_sha1 tree $tree_size" \
-       "$commit_sha1 commit $commit_size" \
-       "$tag_sha1 tag $tag_size" \
+    printf "%s\0" \
+       "$boid blob $hello_size" \
+       "$loid tree $lsize" \
+       "$coid commit $csize" \
+       "$toid tag $tsize" \
        "deadbeef missing" \
        " missing" >batch_check_output
 
-test_expect_success "--batch-check with multiple sha1s gives correct format" '
+    test_expect_success "--batch-check with multiple oids gives correct format" '
        tr "\0" "\n" <batch_check_output >expect &&
        echo_without_newline "$batch_check_input" >in &&
        git cat-file --batch-check <in >actual &&
        test_cmp expect actual
-'
+    '
 
-test_expect_success "--batch-check, -z with multiple sha1s gives correct format" '
+    test_expect_success "--batch-check, -z with multiple oids gives correct format" '
        tr "\0" "\n" <batch_check_output >expect &&
        echo_without_newline_nul "$batch_check_input" >in &&
        git cat-file --batch-check -z <in >actual &&
        test_cmp expect actual
-'
+    '
 
-test_expect_success "--batch-check, -Z with multiple sha1s gives correct format" '
+    test_expect_success "--batch-check, -Z with multiple oids gives correct format" '
        echo_without_newline_nul "$batch_check_input" >in &&
        git cat-file --batch-check -Z <in >actual &&
        test_cmp batch_check_output actual
-'
-
-test_expect_success FUNNYNAMES 'setup with newline in input' '
-       touch -- "newline${LF}embedded" &&
-       git add -- "newline${LF}embedded" &&
-       git commit -m "file with newline embedded" &&
-       test_tick &&
-
-       printf "HEAD:newline${LF}embedded" >in
-'
-
-test_expect_success FUNNYNAMES '--batch-check, -z with newline in input' '
-       git cat-file --batch-check -z <in >actual &&
-       echo "$(git rev-parse "HEAD:newline${LF}embedded") blob 0" >expect &&
-       test_cmp expect actual
-'
-
-test_expect_success FUNNYNAMES '--batch-check, -Z with newline in input' '
-       git cat-file --batch-check -Z <in >actual &&
-       printf "%s\0" "$(git rev-parse "HEAD:newline${LF}embedded") blob 0" >expect &&
-       test_cmp expect actual
-'
+    '
 
-batch_command_multiple_info="info $hello_sha1
-info $tree_sha1
-info $commit_sha1
-info $tag_sha1
+batch_command_multiple_info="info $boid
+info $loid
+info $coid
+info $toid
 info deadbeef"
 
-test_expect_success '--batch-command with multiple info calls gives correct format' '
+    test_expect_success '--batch-command with multiple info calls gives correct format' '
        cat >expect <<-EOF &&
-       $hello_sha1 blob $hello_size
-       $tree_sha1 tree $tree_size
-       $commit_sha1 commit $commit_size
-       $tag_sha1 tag $tag_size
+       $boid blob $hello_size
+       $loid tree $lsize
+       $coid commit $csize
+       $toid tag $tsize
        deadbeef missing
        EOF
 
@@ -510,22 +540,22 @@ test_expect_success '--batch-command with multiple info calls gives correct form
        git cat-file --batch-command --buffer -Z <in >actual &&
 
        test_cmp expect_nul actual
-'
+    '
 
-batch_command_multiple_contents="contents $hello_sha1
-contents $commit_sha1
-contents $tag_sha1
+batch_command_multiple_contents="contents $boid
+contents $coid
+contents $toid
 contents deadbeef
 flush"
 
-test_expect_success '--batch-command with multiple command calls gives correct format' '
+    test_expect_success '--batch-command with multiple command calls gives correct format' '
        printf "%s\0" \
-               "$hello_sha1 blob $hello_size" \
+               "$boid blob $hello_size" \
                "$hello_content" \
-               "$commit_sha1 commit $commit_size" \
-               "$commit_content" \
-               "$tag_sha1 tag $tag_size" \
-               "$tag_content" \
+               "$coid commit $csize" \
+               "$ccontent" \
+               "$toid tag $tsize" \
+               "$tcontent" \
                "deadbeef missing" >expect_nul &&
        tr "\0" "\n" <expect_nul >expect &&
 
@@ -543,6 +573,33 @@ test_expect_success '--batch-command with multiple command calls gives correct f
        git cat-file --batch-command --buffer -Z <in >actual &&
 
        test_cmp expect_nul actual
+    '
+
+}
+
+batch_tests $hello_oid $tree_oid $tree_size $commit_oid $commit_size "$commit_content" $tag_oid $tag_size "$tag_content"
+batch_tests $hello_compat_oid $tree_compat_oid $tree_compat_size $commit_compat_oid $commit_compat_size "$commit_compat_content" $tag_compat_oid $tag_compat_size "$tag_compat_content"
+
+
+test_expect_success FUNNYNAMES 'setup with newline in input' '
+       touch -- "newline${LF}embedded" &&
+       git add -- "newline${LF}embedded" &&
+       git commit -m "file with newline embedded" &&
+       test_tick &&
+
+       printf "HEAD:newline${LF}embedded" >in
+'
+
+test_expect_success FUNNYNAMES '--batch-check, -z with newline in input' '
+       git cat-file --batch-check -z <in >actual &&
+       echo "$(git rev-parse "HEAD:newline${LF}embedded") blob 0" >expect &&
+       test_cmp expect actual
+'
+
+test_expect_success FUNNYNAMES '--batch-check, -Z with newline in input' '
+       git cat-file --batch-check -Z <in >actual &&
+       printf "%s\0" "$(git rev-parse "HEAD:newline${LF}embedded") blob 0" >expect &&
+       test_cmp expect actual
 '
 
 test_expect_success 'setup blobs which are likely to delta' '
@@ -569,7 +626,7 @@ test_expect_success 'confirm that neither loose blob is a delta' '
 # we will check only that one of the two objects is a delta
 # against the other, but not the order. We can do so by just
 # asking for the base of both, and checking whether either
-# sha1 appears in the output.
+# oid appears in the output.
 test_expect_success '%(deltabase) reports packed delta bases' '
        git repack -ad &&
        git cat-file --batch-check="%(deltabase)" <blobs >actual &&
@@ -583,12 +640,12 @@ test_expect_success 'setup bogus data' '
        bogus_short_type="bogus" &&
        bogus_short_content="bogus" &&
        bogus_short_size=$(strlen "$bogus_short_content") &&
-       bogus_short_sha1=$(echo_without_newline "$bogus_short_content" | git hash-object -t $bogus_short_type --literally -w --stdin) &&
+       bogus_short_oid=$(echo_without_newline "$bogus_short_content" | git hash-object -t $bogus_short_type --literally -w --stdin) &&
 
        bogus_long_type="abcdefghijklmnopqrstuvwxyz1234679" &&
        bogus_long_content="bogus" &&
        bogus_long_size=$(strlen "$bogus_long_content") &&
-       bogus_long_sha1=$(echo_without_newline "$bogus_long_content" | git hash-object -t $bogus_long_type --literally -w --stdin)
+       bogus_long_oid=$(echo_without_newline "$bogus_long_content" | git hash-object -t $bogus_long_type --literally -w --stdin)
 '
 
 for arg1 in '' --allow-unknown-type
@@ -608,9 +665,9 @@ do
 
                        if test "$arg1" = "--allow-unknown-type"
                        then
-                               git cat-file $arg1 $arg2 $bogus_short_sha1
+                               git cat-file $arg1 $arg2 $bogus_short_oid
                        else
-                               test_must_fail git cat-file $arg1 $arg2 $bogus_short_sha1 >out 2>actual &&
+                               test_must_fail git cat-file $arg1 $arg2 $bogus_short_oid >out 2>actual &&
                                test_must_be_empty out &&
                                test_cmp expect actual
                        fi
@@ -620,21 +677,21 @@ do
                        if test "$arg2" = "-p"
                        then
                                cat >expect <<-EOF
-                               error: header for $bogus_long_sha1 too long, exceeds 32 bytes
-                               fatal: Not a valid object name $bogus_long_sha1
+                               error: header for $bogus_long_oid too long, exceeds 32 bytes
+                               fatal: Not a valid object name $bogus_long_oid
                                EOF
                        else
                                cat >expect <<-EOF
-                               error: header for $bogus_long_sha1 too long, exceeds 32 bytes
+                               error: header for $bogus_long_oid too long, exceeds 32 bytes
                                fatal: git cat-file: could not get object info
                                EOF
                        fi &&
 
                        if test "$arg1" = "--allow-unknown-type"
                        then
-                               git cat-file $arg1 $arg2 $bogus_short_sha1
+                               git cat-file $arg1 $arg2 $bogus_short_oid
                        else
-                               test_must_fail git cat-file $arg1 $arg2 $bogus_long_sha1 >out 2>actual &&
+                               test_must_fail git cat-file $arg1 $arg2 $bogus_long_oid >out 2>actual &&
                                test_must_be_empty out &&
                                test_cmp expect actual
                        fi
@@ -668,28 +725,28 @@ do
 done
 
 test_expect_success '-e is OK with a broken object without --allow-unknown-type' '
-       git cat-file -e $bogus_short_sha1
+       git cat-file -e $bogus_short_oid
 '
 
 test_expect_success '-e can not be combined with --allow-unknown-type' '
-       test_expect_code 128 git cat-file -e --allow-unknown-type $bogus_short_sha1
+       test_expect_code 128 git cat-file -e --allow-unknown-type $bogus_short_oid
 '
 
 test_expect_success '-p cannot print a broken object even with --allow-unknown-type' '
-       test_must_fail git cat-file -p $bogus_short_sha1 &&
-       test_expect_code 128 git cat-file -p --allow-unknown-type $bogus_short_sha1
+       test_must_fail git cat-file -p $bogus_short_oid &&
+       test_expect_code 128 git cat-file -p --allow-unknown-type $bogus_short_oid
 '
 
 test_expect_success '<type> <hash> does not work with objects of broken types' '
        cat >err.expect <<-\EOF &&
        fatal: invalid object type "bogus"
        EOF
-       test_must_fail git cat-file $bogus_short_type $bogus_short_sha1 2>err.actual &&
+       test_must_fail git cat-file $bogus_short_type $bogus_short_oid 2>err.actual &&
        test_cmp err.expect err.actual
 '
 
 test_expect_success 'broken types combined with --batch and --batch-check' '
-       echo $bogus_short_sha1 >bogus-oid &&
+       echo $bogus_short_oid >bogus-oid &&
 
        cat >err.expect <<-\EOF &&
        fatal: invalid object type
@@ -711,52 +768,52 @@ test_expect_success 'the --allow-unknown-type option does not consider replaceme
        cat >expect <<-EOF &&
        $bogus_short_type
        EOF
-       git cat-file -t --allow-unknown-type $bogus_short_sha1 >actual &&
+       git cat-file -t --allow-unknown-type $bogus_short_oid >actual &&
        test_cmp expect actual &&
 
        # Create it manually, as "git replace" will die on bogus
        # types.
        head=$(git rev-parse --verify HEAD) &&
-       test_when_finished "test-tool ref-store main delete-refs 0 msg refs/replace/$bogus_short_sha1" &&
-       test-tool ref-store main update-ref msg "refs/replace/$bogus_short_sha1" $head $ZERO_OID REF_SKIP_OID_VERIFICATION &&
+       test_when_finished "test-tool ref-store main delete-refs 0 msg refs/replace/$bogus_short_oid" &&
+       test-tool ref-store main update-ref msg "refs/replace/$bogus_short_oid" $head $ZERO_OID REF_SKIP_OID_VERIFICATION &&
 
        cat >expect <<-EOF &&
        commit
        EOF
-       git cat-file -t --allow-unknown-type $bogus_short_sha1 >actual &&
+       git cat-file -t --allow-unknown-type $bogus_short_oid >actual &&
        test_cmp expect actual
 '
 
 test_expect_success "Type of broken object is correct" '
        echo $bogus_short_type >expect &&
-       git cat-file -t --allow-unknown-type $bogus_short_sha1 >actual &&
+       git cat-file -t --allow-unknown-type $bogus_short_oid >actual &&
        test_cmp expect actual
 '
 
 test_expect_success "Size of broken object is correct" '
        echo $bogus_short_size >expect &&
-       git cat-file -s --allow-unknown-type $bogus_short_sha1 >actual &&
+       git cat-file -s --allow-unknown-type $bogus_short_oid >actual &&
        test_cmp expect actual
 '
 
 test_expect_success 'clean up broken object' '
-       rm .git/objects/$(test_oid_to_path $bogus_short_sha1)
+       rm .git/objects/$(test_oid_to_path $bogus_short_oid)
 '
 
 test_expect_success "Type of broken object is correct when type is large" '
        echo $bogus_long_type >expect &&
-       git cat-file -t --allow-unknown-type $bogus_long_sha1 >actual &&
+       git cat-file -t --allow-unknown-type $bogus_long_oid >actual &&
        test_cmp expect actual
 '
 
 test_expect_success "Size of large broken object is correct when type is large" '
        echo $bogus_long_size >expect &&
-       git cat-file -s --allow-unknown-type $bogus_long_sha1 >actual &&
+       git cat-file -s --allow-unknown-type $bogus_long_oid >actual &&
        test_cmp expect actual
 '
 
 test_expect_success 'clean up broken object' '
-       rm .git/objects/$(test_oid_to_path $bogus_long_sha1)
+       rm .git/objects/$(test_oid_to_path $bogus_long_oid)
 '
 
 test_expect_success 'cat-file -t and -s on corrupt loose object' '
@@ -853,7 +910,7 @@ test_expect_success 'prep for symlink tests' '
        test_ln_s_add loop2 loop1 &&
        git add morx dir/subdir/ind2 dir/ind1 &&
        git commit -am "test" &&
-       echo $hello_sha1 blob $hello_size >found
+       echo $hello_oid blob $hello_size >found
 '
 
 test_expect_success 'git cat-file --batch-check --follow-symlinks works for non-links' '
@@ -941,7 +998,7 @@ test_expect_success 'git cat-file --batch-check --follow-symlinks works for dir/
        echo HEAD:dirlink/morx >>expect &&
        echo HEAD:dirlink/morx | git cat-file --batch-check --follow-symlinks >actual &&
        test_cmp expect actual &&
-       echo $hello_sha1 blob $hello_size >expect &&
+       echo $hello_oid blob $hello_size >expect &&
        echo HEAD:dirlink/ind1 | git cat-file --batch-check --follow-symlinks >actual &&
        test_cmp expect actual
 '
index ac3d173767ae72be7bc43e4df269d88fb040f7ba..64aea3848606ccda03b5dd82f878e8d1e8351706 100755 (executable)
@@ -124,8 +124,8 @@ test_expect_success 'check that appropriate filter is invoke when --path is used
        path0_sha=$(git hash-object --path=file0 file1) &&
        test "$file0_sha" = "$path0_sha" &&
        test "$file1_sha" = "$path1_sha" &&
-       path1_sha=$(cat file0 | git hash-object --path=file1 --stdin) &&
-       path0_sha=$(cat file1 | git hash-object --path=file0 --stdin) &&
+       path1_sha=$(git hash-object --path=file1 --stdin <file0) &&
+       path0_sha=$(git hash-object --path=file0 --stdin <file1) &&
        test "$file0_sha" = "$path0_sha" &&
        test "$file1_sha" = "$path1_sha"
 '
@@ -154,7 +154,7 @@ test_expect_success '--path works in a subdirectory' '
 test_expect_success 'check that --no-filters option works' '
        nofilters_file1=$(git hash-object --no-filters file1) &&
        test "$file0_sha" = "$nofilters_file1" &&
-       nofilters_file1=$(cat file1 | git hash-object --stdin) &&
+       nofilters_file1=$(git hash-object --stdin <file1) &&
        test "$file0_sha" = "$nofilters_file1"
 '
 
diff --git a/t/t1016-compatObjectFormat.sh b/t/t1016-compatObjectFormat.sh
new file mode 100755 (executable)
index 0000000..8132cd3
--- /dev/null
@@ -0,0 +1,281 @@
+#!/bin/sh
+#
+# Copyright (c) 2023 Eric Biederman
+#
+
+test_description='Test how well compatObjectFormat works'
+
+TEST_PASSES_SANITIZE_LEAK=true
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-gpg.sh
+
+# All of the follow variables must be defined in the environment:
+# GIT_AUTHOR_NAME
+# GIT_AUTHOR_EMAIL
+# GIT_AUTHOR_DATE
+# GIT_COMMITTER_NAME
+# GIT_COMMITTER_EMAIL
+# GIT_COMMITTER_DATE
+#
+# The test relies on these variables being set so that the two
+# different commits in two different repositories encoded with two
+# different hash functions result in the same content in the commits.
+# This means that when the commit is translated between hash functions
+# the commit is identical to the commit in the other repository.
+
+compat_hash () {
+    case "$1" in
+    "sha1")
+       echo "sha256"
+       ;;
+    "sha256")
+       echo "sha1"
+       ;;
+    esac
+}
+
+hello_oid () {
+    case "$1" in
+    "sha1")
+       echo "$hello_sha1_oid"
+       ;;
+    "sha256")
+       echo "$hello_sha256_oid"
+       ;;
+    esac
+}
+
+tree_oid () {
+    case "$1" in
+    "sha1")
+       echo "$tree_sha1_oid"
+       ;;
+    "sha256")
+       echo "$tree_sha256_oid"
+       ;;
+    esac
+}
+
+commit_oid () {
+    case "$1" in
+    "sha1")
+       echo "$commit_sha1_oid"
+       ;;
+    "sha256")
+       echo "$commit_sha256_oid"
+       ;;
+    esac
+}
+
+commit2_oid () {
+    case "$1" in
+    "sha1")
+       echo "$commit2_sha1_oid"
+       ;;
+    "sha256")
+       echo "$commit2_sha256_oid"
+       ;;
+    esac
+}
+
+del_sigcommit () {
+    local delete=$1
+
+    if test "$delete" = "sha256" ; then
+       local pattern="gpgsig-sha256"
+    else
+       local pattern="gpgsig"
+    fi
+    test-tool delete-gpgsig "$pattern"
+}
+
+
+del_sigtag () {
+    local storage=$1
+    local delete=$2
+
+    if test "$storage" = "$delete" ; then
+       local pattern="trailer"
+    elif test "$storage" = "sha256" ; then
+       local pattern="gpgsig"
+    else
+       local pattern="gpgsig-sha256"
+    fi
+    test-tool delete-gpgsig "$pattern"
+}
+
+base=$(pwd)
+for hash in sha1 sha256
+do
+       cd "$base"
+       mkdir -p repo-$hash
+       cd repo-$hash
+
+       test_expect_success "setup $hash repository" '
+               git init --object-format=$hash &&
+               git config core.repositoryformatversion 1 &&
+               git config extensions.objectformat $hash &&
+               git config extensions.compatobjectformat $(compat_hash $hash) &&
+               git config gpg.program $TEST_DIRECTORY/t1016/gpg &&
+               echo "Hellow World!" > hello &&
+               eval hello_${hash}_oid=$(git hash-object hello) &&
+               git update-index --add hello &&
+               git commit -m "Initial commit" &&
+               eval commit_${hash}_oid=$(git rev-parse HEAD) &&
+               eval tree_${hash}_oid=$(git rev-parse HEAD^{tree})
+       '
+       test_expect_success "create a $hash  tagged blob" '
+               git tag --no-sign -m "This is a tag" hellotag $(hello_oid $hash) &&
+               eval hellotag_${hash}_oid=$(git rev-parse hellotag)
+       '
+       test_expect_success "create a $hash tagged tree" '
+               git tag --no-sign -m "This is a tag" treetag $(tree_oid $hash) &&
+               eval treetag_${hash}_oid=$(git rev-parse treetag)
+       '
+       test_expect_success "create a $hash tagged commit" '
+               git tag --no-sign -m "This is a tag" committag $(commit_oid $hash) &&
+               eval committag_${hash}_oid=$(git rev-parse committag)
+       '
+       test_expect_success GPG2 "create a $hash signed commit" '
+               git commit --gpg-sign --allow-empty -m "This is a signed commit" &&
+               eval signedcommit_${hash}_oid=$(git rev-parse HEAD)
+       '
+       test_expect_success GPG2 "create a $hash signed tag" '
+               git tag -s -m "This is a signed tag" signedtag HEAD &&
+               eval signedtag_${hash}_oid=$(git rev-parse signedtag)
+       '
+       test_expect_success "create a $hash branch" '
+               git checkout -b branch $(commit_oid $hash) &&
+               echo "More more more give me more!" > more &&
+               eval more_${hash}_oid=$(git hash-object more) &&
+               echo "Another and another and another" > another &&
+               eval another_${hash}_oid=$(git hash-object another) &&
+               git update-index --add more another &&
+               git commit -m "Add more files!" &&
+               eval commit2_${hash}_oid=$(git rev-parse HEAD) &&
+               eval tree2_${hash}_oid=$(git rev-parse HEAD^{tree})
+       '
+       test_expect_success GPG2 "create another $hash signed tag" '
+               git tag -s -m "This is another signed tag" signedtag2 $(commit2_oid $hash) &&
+               eval signedtag2_${hash}_oid=$(git rev-parse signedtag2)
+       '
+       test_expect_success GPG2 "merge the $hash branches together" '
+               git merge -S -m "merge some signed tags together" signedtag signedtag2 &&
+               eval signedcommit2_${hash}_oid=$(git rev-parse HEAD)
+       '
+       test_expect_success GPG2 "create additional $hash signed commits" '
+               git commit --gpg-sign --allow-empty -m "This is an additional signed commit" &&
+               git cat-file commit HEAD | del_sigcommit sha256 > "../${hash}_signedcommit3" &&
+               git cat-file commit HEAD | del_sigcommit sha1 > "../${hash}_signedcommit4" &&
+               eval signedcommit3_${hash}_oid=$(git hash-object -t commit -w ../${hash}_signedcommit3) &&
+               eval signedcommit4_${hash}_oid=$(git hash-object -t commit -w ../${hash}_signedcommit4)
+       '
+       test_expect_success GPG2 "create additional $hash signed tags" '
+               git tag -s -m "This is an additional signed tag" signedtag34 HEAD &&
+               git cat-file tag signedtag34 | del_sigtag "${hash}" sha256 > ../${hash}_signedtag3 &&
+               git cat-file tag signedtag34 | del_sigtag "${hash}" sha1 > ../${hash}_signedtag4 &&
+               eval signedtag3_${hash}_oid=$(git hash-object -t tag -w ../${hash}_signedtag3) &&
+               eval signedtag4_${hash}_oid=$(git hash-object -t tag -w ../${hash}_signedtag4)
+       '
+done
+cd "$base"
+
+compare_oids () {
+    test "$#" = 5 && { local PREREQ=$1; shift; } || PREREQ=
+    local type="$1"
+    local name="$2"
+    local sha1_oid="$3"
+    local sha256_oid="$4"
+
+    echo ${sha1_oid} > ${name}_sha1_expected
+    echo ${sha256_oid} > ${name}_sha256_expected
+    echo ${type} > ${name}_type_expected
+
+    git --git-dir=repo-sha1/.git rev-parse --output-object-format=sha256 ${sha1_oid} > ${name}_sha1_sha256_found
+    git --git-dir=repo-sha256/.git rev-parse --output-object-format=sha1 ${sha256_oid} > ${name}_sha256_sha1_found
+    local sha1_sha256_oid=$(cat ${name}_sha1_sha256_found)
+    local sha256_sha1_oid=$(cat ${name}_sha256_sha1_found)
+
+    test_expect_success $PREREQ "Verify ${type} ${name}'s sha1 oid" '
+       git --git-dir=repo-sha256/.git rev-parse --output-object-format=sha1 ${sha256_oid} > ${name}_sha1 &&
+       test_cmp ${name}_sha1 ${name}_sha1_expected
+'
+
+    test_expect_success $PREREQ "Verify ${type} ${name}'s sha256 oid" '
+       git --git-dir=repo-sha1/.git rev-parse --output-object-format=sha256 ${sha1_oid} > ${name}_sha256 &&
+       test_cmp ${name}_sha256 ${name}_sha256_expected
+'
+
+    test_expect_success $PREREQ "Verify ${name}'s sha1 type" '
+       git --git-dir=repo-sha1/.git cat-file -t ${sha1_oid} > ${name}_type1 &&
+       git --git-dir=repo-sha256/.git cat-file -t ${sha256_sha1_oid} > ${name}_type2 &&
+       test_cmp ${name}_type1 ${name}_type2 &&
+       test_cmp ${name}_type1 ${name}_type_expected
+'
+
+    test_expect_success $PREREQ "Verify ${name}'s sha256 type" '
+       git --git-dir=repo-sha256/.git cat-file -t ${sha256_oid} > ${name}_type3 &&
+       git --git-dir=repo-sha1/.git cat-file -t ${sha1_sha256_oid} > ${name}_type4 &&
+       test_cmp ${name}_type3 ${name}_type4 &&
+       test_cmp ${name}_type3 ${name}_type_expected
+'
+
+    test_expect_success $PREREQ "Verify ${name}'s sha1 size" '
+       git --git-dir=repo-sha1/.git cat-file -s ${sha1_oid} > ${name}_size1 &&
+       git --git-dir=repo-sha256/.git cat-file -s ${sha256_sha1_oid} > ${name}_size2 &&
+       test_cmp ${name}_size1 ${name}_size2
+'
+
+    test_expect_success $PREREQ "Verify ${name}'s sha256 size" '
+       git --git-dir=repo-sha256/.git cat-file -s ${sha256_oid} > ${name}_size3 &&
+       git --git-dir=repo-sha1/.git cat-file -s ${sha1_sha256_oid} > ${name}_size4 &&
+       test_cmp ${name}_size3 ${name}_size4
+'
+
+    test_expect_success $PREREQ "Verify ${name}'s sha1 pretty content" '
+       git --git-dir=repo-sha1/.git cat-file -p ${sha1_oid} > ${name}_content1 &&
+       git --git-dir=repo-sha256/.git cat-file -p ${sha256_sha1_oid} > ${name}_content2 &&
+       test_cmp ${name}_content1 ${name}_content2
+'
+
+    test_expect_success $PREREQ "Verify ${name}'s sha256 pretty content" '
+       git --git-dir=repo-sha256/.git cat-file -p ${sha256_oid} > ${name}_content3 &&
+       git --git-dir=repo-sha1/.git cat-file -p ${sha1_sha256_oid} > ${name}_content4 &&
+       test_cmp ${name}_content3 ${name}_content4
+'
+
+    test_expect_success $PREREQ "Verify ${name}'s sha1 content" '
+       git --git-dir=repo-sha1/.git cat-file ${type} ${sha1_oid} > ${name}_content5 &&
+       git --git-dir=repo-sha256/.git cat-file ${type} ${sha256_sha1_oid} > ${name}_content6 &&
+       test_cmp ${name}_content5 ${name}_content6
+'
+
+    test_expect_success $PREREQ "Verify ${name}'s sha256 content" '
+       git --git-dir=repo-sha256/.git cat-file ${type} ${sha256_oid} > ${name}_content7 &&
+       git --git-dir=repo-sha1/.git cat-file ${type} ${sha1_sha256_oid} > ${name}_content8 &&
+       test_cmp ${name}_content7 ${name}_content8
+'
+
+}
+
+compare_oids 'blob' hello "$hello_sha1_oid" "$hello_sha256_oid"
+compare_oids 'tree' tree "$tree_sha1_oid" "$tree_sha256_oid"
+compare_oids 'commit' commit "$commit_sha1_oid" "$commit_sha256_oid"
+compare_oids GPG2 'commit' signedcommit "$signedcommit_sha1_oid" "$signedcommit_sha256_oid"
+compare_oids 'tag' hellotag "$hellotag_sha1_oid" "$hellotag_sha256_oid"
+compare_oids 'tag' treetag "$treetag_sha1_oid" "$treetag_sha256_oid"
+compare_oids 'tag' committag "$committag_sha1_oid" "$committag_sha256_oid"
+compare_oids GPG2 'tag' signedtag "$signedtag_sha1_oid" "$signedtag_sha256_oid"
+
+compare_oids 'blob' more "$more_sha1_oid" "$more_sha256_oid"
+compare_oids 'blob' another "$another_sha1_oid" "$another_sha256_oid"
+compare_oids 'tree' tree2 "$tree2_sha1_oid" "$tree2_sha256_oid"
+compare_oids 'commit' commit2 "$commit2_sha1_oid" "$commit2_sha256_oid"
+compare_oids GPG2 'tag' signedtag2 "$signedtag2_sha1_oid" "$signedtag2_sha256_oid"
+compare_oids GPG2 'commit' signedcommit2 "$signedcommit2_sha1_oid" "$signedcommit2_sha256_oid"
+compare_oids GPG2 'commit' signedcommit3 "$signedcommit3_sha1_oid" "$signedcommit3_sha256_oid"
+compare_oids GPG2 'commit' signedcommit4 "$signedcommit4_sha1_oid" "$signedcommit4_sha256_oid"
+compare_oids GPG2 'tag' signedtag3 "$signedtag3_sha1_oid" "$signedtag3_sha256_oid"
+compare_oids GPG2 'tag' signedtag4 "$signedtag4_sha1_oid" "$signedtag4_sha256_oid"
+
+test_done
diff --git a/t/t1016/gpg b/t/t1016/gpg
new file mode 100755 (executable)
index 0000000..2601cb1
--- /dev/null
@@ -0,0 +1,2 @@
+#!/bin/sh
+exec gpg --faked-system-time "20230918T154812" "$@"
index e49b8024ac53659e17f22eb39e252aba73e56a6b..ab3a105ffff2532b35074611de9e65d60b8db082 100755 (executable)
@@ -968,7 +968,7 @@ test_expect_success 'check-rules non-cone mode' '
        git -C bare sparse-checkout check-rules --no-cone --rules-file ../rules\
                >check-rules-file <all-files &&
 
-       cat rules | git -C repo sparse-checkout set --no-cone --stdin &&
+       git -C repo sparse-checkout set --no-cone --stdin <rules &&
        git -C repo ls-files -t >out &&
        sed -n "/^S /!s/^. //p" out >ls-files &&
 
index b1eb5c01b899daf9a0075393e3e79d47da069d5d..29cf8a966133a7851722c8cdf828fe3c524a12ec 100755 (executable)
@@ -52,7 +52,7 @@ test_expect_success 'shared=all' '
        test 2 = $(git config core.sharedrepository)
 '
 
-test_expect_failure 'template can set core.bare' '
+test_expect_success 'template cannot set core.bare' '
        test_when_finished "rm -rf subdir" &&
        test_when_finished "rm -rf templates" &&
        test_config core.bare true &&
@@ -60,18 +60,7 @@ test_expect_failure 'template can set core.bare' '
        mkdir -p templates/ &&
        cp .git/config templates/config &&
        git init --template=templates subdir &&
-       test_path_exists subdir/HEAD
-'
-
-test_expect_success 'template can set core.bare but overridden by command line' '
-       test_when_finished "rm -rf subdir" &&
-       test_when_finished "rm -rf templates" &&
-       test_config core.bare true &&
-       umask 0022 &&
-       mkdir -p templates/ &&
-       cp .git/config templates/config &&
-       git init --no-bare --template=templates subdir &&
-       test_path_exists subdir/.git/HEAD
+       test_path_is_missing subdir/HEAD
 '
 
 test_expect_success POSIXPERM 'update-server-info honors core.sharedRepository' '
index f0737593c3fda734060fa9509d1b9561086a7376..b754b9fd74bd17e7a969026a93cd6e70c8771cb8 100755 (executable)
@@ -322,4 +322,15 @@ check_invalid_long_option optionspec-neg --no-positive-only
 check_invalid_long_option optionspec-neg --negative
 check_invalid_long_option optionspec-neg --no-no-negative
 
+test_expect_success 'ambiguous: --no matches both --noble and --no-noble' '
+       cat >spec <<-\EOF &&
+       some-command [options]
+       --
+       noble The feudal switch.
+       EOF
+       test_expect_code 129 env GIT_TEST_DISALLOW_ABBREVIATED_OPTIONS=false \
+       git rev-parse --parseopt -- <spec 2>err --no &&
+       grep "error: ambiguous option: no (could be --noble or --no-noble)" err
+'
+
 test_done
index 6d47e2c725f7c7842a604b7a72fd4c4ca64e71bb..dc997e0a643752708fb04a8f64032127f02a5cfd 100755 (executable)
@@ -43,7 +43,7 @@ rsync --exclude-from t/t1509/excludes -Ha . "$R$(pwd)"
 # env might slip through, see test-lib.sh, unset.*PERL_PATH
 sed 's|^PERL_PATH=.*|PERL_PATH=/bin/true|' GIT-BUILD-OPTIONS > "$R$(pwd)/GIT-BUILD-OPTIONS"
 for cmd in git $BB;do 
-       ldd $cmd | grep '/' | sed 's,.*\s\(/[^ ]*\).*,\1,' | while read i; do
+       ldd $cmd | sed -n '/\//s,.*\s\(/[^ ]*\).*,\1,p' | while read i; do
                mkdir -p "$R$(dirname $i)"
                cp "$i" "$R/$i"
        done
index 16d6348b692806ab25faf89d77a73cd62fab4d93..ac404945d4c4e28b608f664f64c4b422557b2684 100755 (executable)
@@ -5,6 +5,7 @@ test_description='restore 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' '
index 27e85be40ad6957f9070a609d83ae334776ed855..42d552211915754e14ddb79d869fc9ba8f517fce 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='git restore --patch'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./lib-patch-mode.sh
 
 test_expect_success 'setup' '
index 8198a1e5789cc963c32ecb028209a7a773144fd5..86c9c887881b94a586dc96b3f495a72c022fd827 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='restore --pathspec-from-file'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_tick
index e36f4d15f2ddd4f64d6aaf8e087370c3b9f92ce5..d3bbd00b818a4f26e2c19ae0ec432138200afc62 100755 (executable)
@@ -75,13 +75,13 @@ test_expect_success 'git branch HEAD should fail' '
        test_must_fail git branch HEAD
 '
 
-cat >expect <<EOF
-$HEAD refs/heads/d/e/f@{0}: branch: Created from main
-EOF
 test_expect_success 'git branch --create-reflog d/e/f should create a branch and a log' '
        GIT_COMMITTER_DATE="2005-05-26 23:30" \
        git -c core.logallrefupdates=false branch --create-reflog d/e/f &&
        test_ref_exists refs/heads/d/e/f &&
+       cat >expect <<-EOF &&
+       $HEAD refs/heads/d/e/f@{0}: branch: Created from main
+       EOF
        git reflog show --no-abbrev-commit refs/heads/d/e/f >actual &&
        test_cmp expect actual
 '
@@ -440,10 +440,10 @@ test_expect_success 'git branch --list -v with --abbrev' '
 
 test_expect_success 'git branch --column' '
        COLUMNS=81 git branch --column=column >actual &&
-       cat >expect <<\EOF &&
-  a/b/c   bam     foo     l     * main    n       o/p     r
-  abc     bar     j/k     m/m     mb      o/o     q       topic
-EOF
+       cat >expect <<-\EOF &&
+         a/b/c   bam     foo     l     * main    n       o/p     r
+         abc     bar     j/k     m/m     mb      o/o     q       topic
+       EOF
        test_cmp expect actual
 '
 
@@ -453,25 +453,25 @@ test_expect_success 'git branch --column with an extremely long branch name' '
        test_when_finished "git branch -d $long" &&
        git branch $long &&
        COLUMNS=80 git branch --column=column >actual &&
-       cat >expect <<EOF &&
-  a/b/c
-  abc
-  bam
-  bar
-  foo
-  j/k
-  l
-  m/m
-* main
-  mb
-  n
-  o/o
-  o/p
-  q
-  r
-  topic
-  $long
-EOF
+       cat >expect <<-EOF &&
+         a/b/c
+         abc
+         bam
+         bar
+         foo
+         j/k
+         l
+         m/m
+       * main
+         mb
+         n
+         o/o
+         o/p
+         q
+         r
+         topic
+         $long
+       EOF
        test_cmp expect actual
 '
 
@@ -481,10 +481,10 @@ test_expect_success 'git branch with column.*' '
        COLUMNS=80 git branch >actual &&
        git config --unset column.branch &&
        git config --unset column.ui &&
-       cat >expect <<\EOF &&
-  a/b/c   bam   foo   l   * main   n     o/p   r
-  abc     bar   j/k   m/m   mb     o/o   q     topic
-EOF
+       cat >expect <<-\EOF &&
+         a/b/c   bam   foo   l   * main   n     o/p   r
+         abc     bar   j/k   m/m   mb     o/o   q     topic
+       EOF
        test_cmp expect actual
 '
 
@@ -496,39 +496,36 @@ test_expect_success 'git branch -v with column.ui ignored' '
        git config column.ui column &&
        COLUMNS=80 git branch -v | cut -c -8 | sed "s/ *$//" >actual &&
        git config --unset column.ui &&
-       cat >expect <<\EOF &&
-  a/b/c
-  abc
-  bam
-  bar
-  foo
-  j/k
-  l
-  m/m
-* main
-  mb
-  n
-  o/o
-  o/p
-  q
-  r
-  topic
-EOF
+       cat >expect <<-\EOF &&
+         a/b/c
+         abc
+         bam
+         bar
+         foo
+         j/k
+         l
+         m/m
+       * main
+         mb
+         n
+         o/o
+         o/p
+         q
+         r
+         topic
+       EOF
        test_cmp expect actual
 '
 
-mv .git/config .git/config-saved
-
 test_expect_success DEFAULT_REPO_FORMAT 'git branch -m q q2 without config should succeed' '
+       test_when_finished mv .git/config-saved .git/config &&
+       mv .git/config .git/config-saved &&
        git branch -m q q2 &&
        git branch -m q2 q
 '
 
-mv .git/config-saved .git/config
-
-git config branch.s/s.dummy Hello
-
 test_expect_success 'git branch -m s/s s should work when s/t is deleted' '
+       git config branch.s/s.dummy Hello &&
        git branch --create-reflog s/s &&
        git reflog exists refs/heads/s/s &&
        git branch --create-reflog s/t &&
@@ -579,7 +576,7 @@ EOF
 
        # ...and that the comments for those sections are also
        # preserved.
-       cat config.branch | sed "s/\"source\"/\"dest\"/" >expect &&
+       sed "s/\"source\"/\"dest\"/" config.branch >expect &&
        sed -n -e "/Note the lack/,\$p" .git/config >actual &&
        test_cmp expect actual
 '
@@ -1112,14 +1109,14 @@ test_expect_success '--set-upstream-to notices an error to set branch as own ups
        test_cmp expect actual
 "
 
-# Keep this test last, as it changes the current branch
-cat >expect <<EOF
-$HEAD refs/heads/g/h/i@{0}: branch: Created from main
-EOF
 test_expect_success 'git checkout -b g/h/i -l should create a branch and a log' '
+       test_when_finished git checkout main &&
        GIT_COMMITTER_DATE="2005-05-26 23:30" \
        git checkout -b g/h/i -l main &&
        test_ref_exists refs/heads/g/h/i &&
+       cat >expect <<-EOF &&
+       $HEAD refs/heads/g/h/i@{0}: branch: Created from main
+       EOF
        git reflog show --no-abbrev-commit refs/heads/g/h/i >actual &&
        test_cmp expect actual
 '
@@ -1696,4 +1693,14 @@ test_expect_success '--track overrides branch.autoSetupMerge' '
        test_cmp_config "" --default "" branch.foo5.merge
 '
 
+test_expect_success 'errors if given a bad branch name' '
+       cat <<-\EOF >expect &&
+       fatal: '\''foo..bar'\'' is not a valid branch name
+       hint: See `man git check-ref-format`
+       hint: Disable this message with "git config advice.refSyntax false"
+       EOF
+       test_must_fail git branch foo..bar >actual 2>&1 &&
+       test_cmp expect actual
+'
+
 test_done
index 088a852dd47aa335d83424f21472f8384c91cf16..beca34605672d407244b26ebcffb1f70527f5915 100755 (executable)
@@ -442,7 +442,7 @@ test_expect_success 'add note by specifying "-C", "--no-stripspace" is the defau
        ${LF}
        EOF
 
-       cat expect | git hash-object -w --stdin >blob &&
+       git hash-object -w --stdin <expect >blob &&
        git notes add -C $(cat blob) &&
        git notes show >actual &&
        test_cmp expect actual &&
@@ -468,7 +468,7 @@ test_expect_success 'reuse note by specifying "-C" and "--stripspace"' '
        second-line
        EOF
 
-       cat data | git hash-object -w --stdin >blob &&
+       git hash-object -w --stdin <data >blob &&
        git notes add --stripspace -C $(cat blob) &&
        git notes show >actual &&
        test_cmp expect actual
@@ -492,7 +492,7 @@ test_expect_success 'reuse with "-C" and add note with "-m", "-m" will stripspac
        third-line
        EOF
 
-       cat data | git hash-object -w --stdin >blob &&
+       git hash-object -w --stdin <data >blob &&
        git notes add -C $(cat blob) -m "third-line" &&
        git notes show >actual &&
        test_cmp expect actual
@@ -511,7 +511,7 @@ test_expect_success 'add note with "-m" and reuse note with "-C", "-C" will not
        second-line
        EOF
 
-       cat data | git hash-object -w --stdin >blob &&
+       git hash-object -w --stdin <data >blob &&
        git notes add -m "first-line" -C $(cat blob)  &&
        git notes show >actual &&
        test_cmp expect actual
index 67fd2345affd78507a6cb714b48565ed190cb5af..50ae222f08424e4b4ed22a82c066526a3da3659c 100755 (executable)
@@ -10,7 +10,7 @@ LIB_CRLF_BRANCHES=""
 create_crlf_ref () {
        branch="$1" &&
        cat >.crlf-orig-$branch.txt &&
-       cat .crlf-orig-$branch.txt | append_cr >.crlf-message-$branch.txt &&
+       append_cr <.crlf-orig-$branch.txt >.crlf-message-$branch.txt &&
        grep 'Subject' .crlf-orig-$branch.txt | tr '\n' ' ' | sed 's/[ ]*$//' | tr -d '\n' >.crlf-subject-$branch.txt &&
        grep 'Body' .crlf-orig-$branch.txt | append_cr >.crlf-body-$branch.txt &&
        LIB_CRLF_BRANCHES="${LIB_CRLF_BRANCHES} ${branch}" &&
@@ -97,7 +97,7 @@ test_expect_success 'branch: --verbose works with messages using CRLF' '
        git branch -v >tmp &&
        # Remove first two columns, and the line for the currently checked out branch
        current=$(git branch --show-current) &&
-       grep -v $current <tmp | awk "{\$1=\$2=\"\"}1"  >actual &&
+       awk "/$current/ { next } { \$1 = \$2 = \"\" } 1" <tmp >actual &&
        test_cmp expect actual
 '
 
index 7afc883ec374e1c4b747ed80ba64575d05017502..cb3307010c1ed97f1b7bdb91b366f3e6d17a2ac9 100755 (executable)
@@ -405,7 +405,7 @@ test_expect_success 'diff-tree -r B A == diff-tree -r -R A B' '
 
 test_expect_success 'diff can read from stdin' '
        test_must_fail git diff --no-index -- MN - < NN |
-               grep -v "^index" | sed "s#/-#/NN#" >.test-a &&
+               sed "/^index/d; s#/-#/NN#" >.test-a &&
        test_must_fail git diff --no-index -- MN NN |
                grep -v "^index" >.test-b &&
        test_cmp .test-a .test-b
index 1e3b2dbea48488ecb3a68a8dba82d57b6fbca2a7..3855d68dbc0a64797ddbd665e675aa3d1549defc 100755 (executable)
@@ -633,8 +633,8 @@ check_prefix () {
        test_cmp expect actual.paths
 }
 
-test_expect_success 'diff-files does not respect diff.noprefix' '
-       git -c diff.noprefix diff-files -p >actual &&
+test_expect_success 'diff-files does not respect diff.noPrefix' '
+       git -c diff.noPrefix diff-files -p >actual &&
        check_prefix actual a/file0 b/file0
 '
 
@@ -643,23 +643,58 @@ test_expect_success 'diff-files respects --no-prefix' '
        check_prefix actual file0 file0
 '
 
-test_expect_success 'diff respects diff.noprefix' '
-       git -c diff.noprefix diff >actual &&
+test_expect_success 'diff respects diff.noPrefix' '
+       git -c diff.noPrefix diff >actual &&
        check_prefix actual file0 file0
 '
 
-test_expect_success 'diff --default-prefix overrides diff.noprefix' '
-       git -c diff.noprefix diff --default-prefix >actual &&
+test_expect_success 'diff --default-prefix overrides diff.noPrefix' '
+       git -c diff.noPrefix diff --default-prefix >actual &&
        check_prefix actual a/file0 b/file0
 '
 
-test_expect_success 'diff respects diff.mnemonicprefix' '
-       git -c diff.mnemonicprefix diff >actual &&
+test_expect_success 'diff respects diff.mnemonicPrefix' '
+       git -c diff.mnemonicPrefix diff >actual &&
        check_prefix actual i/file0 w/file0
 '
 
-test_expect_success 'diff --default-prefix overrides diff.mnemonicprefix' '
-       git -c diff.mnemonicprefix diff --default-prefix >actual &&
+test_expect_success 'diff --default-prefix overrides diff.mnemonicPrefix' '
+       git -c diff.mnemonicPrefix diff --default-prefix >actual &&
+       check_prefix actual a/file0 b/file0
+'
+
+test_expect_success 'diff respects diff.srcPrefix' '
+       git -c diff.srcPrefix=x/ diff >actual &&
+       check_prefix actual x/file0 b/file0
+'
+
+test_expect_success 'diff respects diff.dstPrefix' '
+       git -c diff.dstPrefix=y/ diff >actual &&
+       check_prefix actual a/file0 y/file0
+'
+
+test_expect_success 'diff --src-prefix overrides diff.srcPrefix' '
+       git -c diff.srcPrefix=y/ diff --src-prefix=z/ >actual &&
+       check_prefix actual z/file0 b/file0
+'
+
+test_expect_success 'diff --dst-prefix overrides diff.dstPrefix' '
+       git -c diff.dstPrefix=y/ diff --dst-prefix=z/ >actual &&
+       check_prefix actual a/file0 z/file0
+'
+
+test_expect_success 'diff.{src,dst}Prefix ignored with diff.noPrefix' '
+       git -c diff.dstPrefix=y/ -c diff.srcPrefix=x/ -c diff.noPrefix diff >actual &&
+       check_prefix actual file0 file0
+'
+
+test_expect_success 'diff.{src,dst}Prefix ignored with diff.mnemonicPrefix' '
+       git -c diff.dstPrefix=x/ -c diff.srcPrefix=y/ -c diff.mnemonicPrefix diff >actual &&
+       check_prefix actual i/file0 w/file0
+'
+
+test_expect_success 'diff.{src,dst}Prefix ignored with --default-prefix' '
+       git -c diff.dstPrefix=x/ -c diff.srcPrefix=y/ diff --default-prefix >actual &&
        check_prefix actual a/file0 b/file0
 '
 
index c1ac09ecc7140a3dcfcdf906bb4533ba131881de..fdd865f7c38dea5b60910b46c2a113ec7f5c2a09 100755 (executable)
@@ -232,7 +232,7 @@ keep_only_cr () {
 test_expect_success 'external diff with autocrlf = true' '
        test_config core.autocrlf true &&
        GIT_EXTERNAL_DIFF=./fake-diff.sh git diff &&
-       test $(wc -l < crlfed.txt) = $(cat crlfed.txt | keep_only_cr | wc -c)
+       test $(wc -l <crlfed.txt) = $(keep_only_cr <crlfed.txt | wc -c)
 '
 
 test_expect_success 'diff --cached' '
index d7382709fc11055be003751d802f953ed56b6eab..f698d0c9ad274137a9aa254c8e89bcece8f4cddd 100755 (executable)
@@ -312,6 +312,38 @@ test_expect_success 'shortlog de-duplicates trailers in a single commit' '
        test_cmp expect actual
 '
 
+# Trailers that have unfolded (single line) and folded (multiline) values which
+# are otherwise identical are treated as the same trailer for de-duplication.
+test_expect_success 'shortlog de-duplicates trailers in a single commit (folded/unfolded values)' '
+       git commit --allow-empty -F - <<-\EOF &&
+       subject one
+
+       this message has two distinct values, plus a repeat (folded)
+
+       Repeated-trailer: Foo foo foo
+       Repeated-trailer: Bar
+       Repeated-trailer: Foo
+         foo foo
+       EOF
+
+       git commit --allow-empty -F - <<-\EOF &&
+       subject two
+
+       similar to the previous, but without the second distinct value
+
+       Repeated-trailer: Foo foo foo
+       Repeated-trailer: Foo
+         foo foo
+       EOF
+
+       cat >expect <<-\EOF &&
+            2  Foo foo foo
+            1  Bar
+       EOF
+       git shortlog -ns --group=trailer:repeated-trailer -2 HEAD >actual &&
+       test_cmp expect actual
+'
+
 test_expect_success 'shortlog can match multiple groups' '
        git commit --allow-empty -F - <<-\EOF &&
        subject one
index e3d655e6b8b5565d619298b16da010ee671667a2..1409eebcd8557f20de38fee9c4a6166a959056b9 100755 (executable)
@@ -156,7 +156,7 @@ test_expect_success 'NUL termination with --reflog --pretty=oneline' '
        for r in $revs
        do
                git show -s --pretty=oneline "$r" >raw &&
-               cat raw | lf_to_nul || return 1
+               lf_to_nul <raw || return 1
        done >expect &&
        # the trailing NUL is already produced so we do not need to
        # output another one
index 29e9974cdfd421f6fb7dd17fa2bb12db8d5d6e04..eea19907b550c4a97a7ca7760f1be30bea7d8db0 100755 (executable)
@@ -313,7 +313,7 @@ test_expect_success 'rename/add handling' '
                # First, check that the bar that appears at stage 3 does not
                # correspond to an individual blob anywhere in history
                #
-               hash=$(cat out | tr "\0" "\n" | head -n 3 | grep 3.bar | cut -f 2 -d " ") &&
+               hash=$(tr "\0" "\n" <out | head -n 3 | grep 3.bar | cut -f 2 -d " ") &&
                git rev-list --objects --all >all_blobs &&
                ! grep $hash all_blobs &&
 
@@ -380,7 +380,7 @@ test_expect_success SYMLINKS 'rename/add, where add is a mode conflict' '
                # First, check that the bar that appears at stage 3 does not
                # correspond to an individual blob anywhere in history
                #
-               hash=$(cat out | tr "\0" "\n" | head -n 3 | grep 3.bar | cut -f 2 -d " ") &&
+               hash=$(tr "\0" "\n" <out | head -n 3 | grep 3.bar | cut -f 2 -d " ") &&
                git rev-list --objects --all >all_blobs &&
                ! grep $hash all_blobs &&
 
@@ -630,8 +630,8 @@ test_expect_success 'mod6: chains of rename/rename(1to2) and add/add via collidi
                # conflict entries do not appear as individual blobs anywhere
                # in history.
                #
-               hash1=$(cat out | tr "\0" "\n" | head | grep 2.four | cut -f 2 -d " ") &&
-               hash2=$(cat out | tr "\0" "\n" | head | grep 3.two | cut -f 2 -d " ") &&
+               hash1=$(tr "\0" "\n" <out | head | grep 2.four | cut -f 2 -d " ") &&
+               hash2=$(tr "\0" "\n" <out | head | grep 3.two | cut -f 2 -d " ") &&
                git rev-list --objects --all >all_blobs &&
                ! grep $hash1 all_blobs &&
                ! grep $hash2 all_blobs &&
index 654d8cf3ee003ee4e8d66b6e88df06312014cb47..c8d06554541cb5d7c575b69c83bd7d3828d49924 100755 (executable)
@@ -70,7 +70,7 @@ test_expect_success 'respect NULs' '
 
        git mailsplit -d3 -o. "$DATA/nul-plain" &&
        test_cmp "$DATA/nul-plain" 001 &&
-       (cat 001 | git mailinfo msg patch) &&
+       git mailinfo msg patch <001 &&
        test_line_count = 4 patch
 
 '
index a58f91035d124d6710438c5524c91bdbf66b4e84..61e2be2903d344563fe2ebea718de962e60d7b78 100755 (executable)
@@ -465,7 +465,7 @@ test_with_bad_commit () {
        must_pass_arg="$2" &&
        (
                cd strict &&
-               test_expect_fail git index-pack "$must_fail_arg" "test-$(cat pack-name).pack"
+               test_must_fail git index-pack "$must_fail_arg" "test-$(cat pack-name).pack" &&
                git index-pack "$must_pass_arg" "test-$(cat pack-name).pack"
        )
 }
index 2ff3eef9a3b8cad7f95253c79283d3ac542802fd..79552d6ef7f69751b8cfa1bd71edfda47973257b 100755 (executable)
@@ -455,7 +455,7 @@ test_expect_success 'setup r1 - delete loose blobs' '
        test_parse_ls_files_stage_oids <ls_files_result |
        sort >expected &&
 
-       for id in `cat expected | sed "s|..|&/|"`
+       for id in `sed "s|..|&/|" expected`
        do
                rm r1/.git/objects/$id || return 1
        done
index 8b8bc47dc0b9d6a8d90928c628bbe122d5d04f31..d8cadeec73310d36ad11a1427ec44cc9fc2d3a66 100755 (executable)
@@ -123,7 +123,7 @@ remote: STDOUT post-update
 remote: STDERR post-update
 EOF
 test_expect_success 'send-pack stderr contains hook messages' '
-       grep ^remote: send.err | sed "s/ *\$//" >actual &&
+       sed -n "/^remote:/s/ *\$//p" send.err >actual &&
        test_cmp expect actual
 '
 
index b4bc24691c8150e3b0829cb97af1911d13d02504..c91a62b77afcfba1bf1228c33717db77c7e45318 100755 (executable)
@@ -303,7 +303,7 @@ test_expect_success GPGSM 'fail without key and heed user.signingkey x509' '
                EOF
                sed -n -e "s/^nonce /NONCE=/p" -e "/^$/q" dst/push-cert
        ) >expect.in &&
-       key=$(cat "${GNUPGHOME}/trustlist.txt" | cut -d" " -f1 | tr -d ":") &&
+       key=$(cut -d" " -f1 <"${GNUPGHOME}/trustlist.txt" | tr -d ":") &&
        sed -e "s/^KEY=/KEY=${key}/" expect.in >expect &&
 
        noop=$(git rev-parse noop) &&
index a400bcca622f464f13fe4f0a57c4d44ec8dd827b..e93e0d0cc397a323bae99e9f92ee5d02cb026f2b 100755 (executable)
@@ -120,14 +120,14 @@ test_expect_success 'prefers -c config over --template config' '
 
 '
 
-test_expect_failure 'prefers --template config even for core.bare' '
+test_expect_success 'ignore --template config for core.bare' '
 
        template="$TRASH_DIRECTORY/template-with-bare-config" &&
        mkdir "$template" &&
        git config --file "$template/config" core.bare true &&
        git clone "--template=$template" parent clone-bare-config &&
-       test "$(git -C clone-bare-config config --local core.bare)" = "true" &&
-       test_path_is_file clone-bare-config/HEAD
+       test "$(git -C clone-bare-config config --local core.bare)" = "false" &&
+       test_path_is_missing clone-bare-config/HEAD
 '
 
 test_expect_success 'prefers config "clone.defaultRemoteName" over default' '
index 52822b9461a18c89faa35d7dc0be6a1e4690da18..43e1afd44c9b9f0a00ed34cb4144f8bb1946b602 100755 (executable)
@@ -670,7 +670,7 @@ test_expect_success 'rev-list W/ --missing=print' '
        awk -f print_2.awk ls_files_result |
        sort >expected &&
 
-       for id in `cat expected | sed "s|..|&/|"`
+       for id in `sed "s|..|&/|" expected`
        do
                rm r1/.git/objects/$id || return 1
        done &&
index b4f4a313f486a583ca62268f2106948cca8d7266..647ea1e8382913887e292977502181b223136b46 100755 (executable)
@@ -34,14 +34,14 @@ test_expect_success setup '
 test_expect_success 'Check "ours" is CRLF' '
        git reset --hard initial &&
        git merge side -s ours &&
-       cat file | remove_cr | append_cr >file.temp &&
+       remove_cr <file | append_cr >file.temp &&
        test_cmp file file.temp
 '
 
 test_expect_success 'Check that conflict file is CRLF' '
        git reset --hard a &&
        test_must_fail git merge side &&
-       cat file | remove_cr | append_cr >file.temp &&
+       remove_cr <file | append_cr >file.temp &&
        test_cmp file file.temp
 '
 
index 41288a60ceb549295699f2efd9cb8e187d3e297b..48a62cb85568bfb7f77a8c597096617dfa5fcf4c 100755 (executable)
@@ -15,6 +15,7 @@ test_description='CRLF merge conflict across text=auto change
 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
index 611b3dd3aedb44c9b5d4657f3058c72a60258cb0..1f7201eb60caf9b31fd3db1a83dc834bb145157c 100755 (executable)
@@ -407,6 +407,12 @@ test_expect_success 'clean.requireForce and -f' '
 
 '
 
+test_expect_success 'clean.requireForce and --interactive' '
+       git clean --interactive </dev/null >output 2>error &&
+       test_grep ! "requireForce is true and" error &&
+       test_grep "\*\*\* Commands \*\*\*" output
+'
+
 test_expect_success 'core.excludesfile' '
 
        echo excludes >excludes &&
index d82a3210a1db48288c54ce3c06d430546da70f48..4afe53c66ae57acdadc7177b98fd0aacefdd576c 100755 (executable)
@@ -25,18 +25,18 @@ test_expect_success 'git clean -i (c: clean hotkey)' '
        touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
        docs/manual.txt obj.o build/lib.so &&
        echo c | git clean -i &&
-       test -f Makefile &&
-       test -f README &&
-       test -f src/part1.c &&
-       test -f src/part2.c &&
-       test ! -f a.out &&
-       test -f docs/manual.txt &&
-       test ! -f src/part3.c &&
-       test ! -f src/part3.h &&
-       test ! -f src/part4.c &&
-       test ! -f src/part4.h &&
-       test -f obj.o &&
-       test -f build/lib.so
+       test_path_is_file Makefile &&
+       test_path_is_file README &&
+       test_path_is_file src/part1.c &&
+       test_path_is_file src/part2.c &&
+       test_path_is_missing a.out &&
+       test_path_is_file docs/manual.txt &&
+       test_path_is_missing src/part3.c &&
+       test_path_is_missing src/part3.h &&
+       test_path_is_missing src/part4.c &&
+       test_path_is_missing src/part4.h &&
+       test_path_is_file obj.o &&
+       test_path_is_file build/lib.so
 
 '
 
@@ -46,18 +46,18 @@ test_expect_success 'git clean -i (cl: clean prefix)' '
        touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
        docs/manual.txt obj.o build/lib.so &&
        echo cl | git clean -i &&
-       test -f Makefile &&
-       test -f README &&
-       test -f src/part1.c &&
-       test -f src/part2.c &&
-       test ! -f a.out &&
-       test -f docs/manual.txt &&
-       test ! -f src/part3.c &&
-       test ! -f src/part3.h &&
-       test ! -f src/part4.c &&
-       test ! -f src/part4.h &&
-       test -f obj.o &&
-       test -f build/lib.so
+       test_path_is_file Makefile &&
+       test_path_is_file README &&
+       test_path_is_file src/part1.c &&
+       test_path_is_file src/part2.c &&
+       test_path_is_missing a.out &&
+       test_path_is_file docs/manual.txt &&
+       test_path_is_missing src/part3.c &&
+       test_path_is_missing src/part3.h &&
+       test_path_is_missing src/part4.c &&
+       test_path_is_missing src/part4.h &&
+       test_path_is_file obj.o &&
+       test_path_is_file build/lib.so
 
 '
 
@@ -67,18 +67,18 @@ test_expect_success 'git clean -i (quit)' '
        touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
        docs/manual.txt obj.o build/lib.so &&
        echo quit | git clean -i &&
-       test -f Makefile &&
-       test -f README &&
-       test -f src/part1.c &&
-       test -f src/part2.c &&
-       test -f a.out &&
-       test -f docs/manual.txt &&
-       test -f src/part3.c &&
-       test -f src/part3.h &&
-       test -f src/part4.c &&
-       test -f src/part4.h &&
-       test -f obj.o &&
-       test -f build/lib.so
+       test_path_is_file Makefile &&
+       test_path_is_file README &&
+       test_path_is_file src/part1.c &&
+       test_path_is_file src/part2.c &&
+       test_path_is_file a.out &&
+       test_path_is_file docs/manual.txt &&
+       test_path_is_file src/part3.c &&
+       test_path_is_file src/part3.h &&
+       test_path_is_file src/part4.c &&
+       test_path_is_file src/part4.h &&
+       test_path_is_file obj.o &&
+       test_path_is_file build/lib.so
 
 '
 
@@ -88,18 +88,18 @@ test_expect_success 'git clean -i (Ctrl+D)' '
        touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
        docs/manual.txt obj.o build/lib.so &&
        echo "\04" | git clean -i &&
-       test -f Makefile &&
-       test -f README &&
-       test -f src/part1.c &&
-       test -f src/part2.c &&
-       test -f a.out &&
-       test -f docs/manual.txt &&
-       test -f src/part3.c &&
-       test -f src/part3.h &&
-       test -f src/part4.c &&
-       test -f src/part4.h &&
-       test -f obj.o &&
-       test -f build/lib.so
+       test_path_is_file Makefile &&
+       test_path_is_file README &&
+       test_path_is_file src/part1.c &&
+       test_path_is_file src/part2.c &&
+       test_path_is_file a.out &&
+       test_path_is_file docs/manual.txt &&
+       test_path_is_file src/part3.c &&
+       test_path_is_file src/part3.h &&
+       test_path_is_file src/part4.c &&
+       test_path_is_file src/part4.h &&
+       test_path_is_file obj.o &&
+       test_path_is_file build/lib.so
 
 '
 
@@ -110,18 +110,18 @@ test_expect_success 'git clean -id (filter all)' '
        docs/manual.txt obj.o build/lib.so &&
        test_write_lines f "*" "" c |
        git clean -id &&
-       test -f Makefile &&
-       test -f README &&
-       test -f src/part1.c &&
-       test -f src/part2.c &&
-       test -f a.out &&
-       test -f docs/manual.txt &&
-       test -f src/part3.c &&
-       test -f src/part3.h &&
-       test -f src/part4.c &&
-       test -f src/part4.h &&
-       test -f obj.o &&
-       test -f build/lib.so
+       test_path_is_file Makefile &&
+       test_path_is_file README &&
+       test_path_is_file src/part1.c &&
+       test_path_is_file src/part2.c &&
+       test_path_is_file a.out &&
+       test_path_is_file docs/manual.txt &&
+       test_path_is_file src/part3.c &&
+       test_path_is_file src/part3.h &&
+       test_path_is_file src/part4.c &&
+       test_path_is_file src/part4.h &&
+       test_path_is_file obj.o &&
+       test_path_is_file build/lib.so
 
 '
 
@@ -132,18 +132,18 @@ test_expect_success 'git clean -id (filter patterns)' '
        docs/manual.txt obj.o build/lib.so &&
        test_write_lines f "part3.* *.out" "" c |
        git clean -id &&
-       test -f Makefile &&
-       test -f README &&
-       test -f src/part1.c &&
-       test -f src/part2.c &&
-       test -f a.out &&
-       test ! -f docs/manual.txt &&
-       test -f src/part3.c &&
-       test -f src/part3.h &&
-       test ! -f src/part4.c &&
-       test ! -f src/part4.h &&
-       test -f obj.o &&
-       test -f build/lib.so
+       test_path_is_file Makefile &&
+       test_path_is_file README &&
+       test_path_is_file src/part1.c &&
+       test_path_is_file src/part2.c &&
+       test_path_is_file a.out &&
+       test_path_is_missing docs/manual.txt &&
+       test_path_is_file src/part3.c &&
+       test_path_is_file src/part3.h &&
+       test_path_is_missing src/part4.c &&
+       test_path_is_missing src/part4.h &&
+       test_path_is_file obj.o &&
+       test_path_is_file build/lib.so
 
 '
 
@@ -154,18 +154,18 @@ test_expect_success 'git clean -id (filter patterns 2)' '
        docs/manual.txt obj.o build/lib.so &&
        test_write_lines f "* !*.out" "" c |
        git clean -id &&
-       test -f Makefile &&
-       test -f README &&
-       test -f src/part1.c &&
-       test -f src/part2.c &&
-       test ! -f a.out &&
-       test -f docs/manual.txt &&
-       test -f src/part3.c &&
-       test -f src/part3.h &&
-       test -f src/part4.c &&
-       test -f src/part4.h &&
-       test -f obj.o &&
-       test -f build/lib.so
+       test_path_is_file Makefile &&
+       test_path_is_file README &&
+       test_path_is_file src/part1.c &&
+       test_path_is_file src/part2.c &&
+       test_path_is_missing a.out &&
+       test_path_is_file docs/manual.txt &&
+       test_path_is_file src/part3.c &&
+       test_path_is_file src/part3.h &&
+       test_path_is_file src/part4.c &&
+       test_path_is_file src/part4.h &&
+       test_path_is_file obj.o &&
+       test_path_is_file build/lib.so
 
 '
 
@@ -176,18 +176,18 @@ test_expect_success 'git clean -id (select - all)' '
        docs/manual.txt obj.o build/lib.so &&
        test_write_lines s "*" "" c |
        git clean -id &&
-       test -f Makefile &&
-       test -f README &&
-       test -f src/part1.c &&
-       test -f src/part2.c &&
-       test ! -f a.out &&
-       test ! -f docs/manual.txt &&
-       test ! -f src/part3.c &&
-       test ! -f src/part3.h &&
-       test ! -f src/part4.c &&
-       test ! -f src/part4.h &&
-       test -f obj.o &&
-       test -f build/lib.so
+       test_path_is_file Makefile &&
+       test_path_is_file README &&
+       test_path_is_file src/part1.c &&
+       test_path_is_file src/part2.c &&
+       test_path_is_missing a.out &&
+       test_path_is_missing docs/manual.txt &&
+       test_path_is_missing src/part3.c &&
+       test_path_is_missing src/part3.h &&
+       test_path_is_missing src/part4.c &&
+       test_path_is_missing src/part4.h &&
+       test_path_is_file obj.o &&
+       test_path_is_file build/lib.so
 
 '
 
@@ -198,18 +198,18 @@ test_expect_success 'git clean -id (select - none)' '
        docs/manual.txt obj.o build/lib.so &&
        test_write_lines s "" c |
        git clean -id &&
-       test -f Makefile &&
-       test -f README &&
-       test -f src/part1.c &&
-       test -f src/part2.c &&
-       test -f a.out &&
-       test -f docs/manual.txt &&
-       test -f src/part3.c &&
-       test -f src/part3.h &&
-       test -f src/part4.c &&
-       test -f src/part4.h &&
-       test -f obj.o &&
-       test -f build/lib.so
+       test_path_is_file Makefile &&
+       test_path_is_file README &&
+       test_path_is_file src/part1.c &&
+       test_path_is_file src/part2.c &&
+       test_path_is_file a.out &&
+       test_path_is_file docs/manual.txt &&
+       test_path_is_file src/part3.c &&
+       test_path_is_file src/part3.h &&
+       test_path_is_file src/part4.c &&
+       test_path_is_file src/part4.h &&
+       test_path_is_file obj.o &&
+       test_path_is_file build/lib.so
 
 '
 
@@ -220,18 +220,18 @@ test_expect_success 'git clean -id (select - number)' '
        docs/manual.txt obj.o build/lib.so &&
        test_write_lines s 3 "" c |
        git clean -id &&
-       test -f Makefile &&
-       test -f README &&
-       test -f src/part1.c &&
-       test -f src/part2.c &&
-       test -f a.out &&
-       test -f docs/manual.txt &&
-       test ! -f src/part3.c &&
-       test -f src/part3.h &&
-       test -f src/part4.c &&
-       test -f src/part4.h &&
-       test -f obj.o &&
-       test -f build/lib.so
+       test_path_is_file Makefile &&
+       test_path_is_file README &&
+       test_path_is_file src/part1.c &&
+       test_path_is_file src/part2.c &&
+       test_path_is_file a.out &&
+       test_path_is_file docs/manual.txt &&
+       test_path_is_missing src/part3.c &&
+       test_path_is_file src/part3.h &&
+       test_path_is_file src/part4.c &&
+       test_path_is_file src/part4.h &&
+       test_path_is_file obj.o &&
+       test_path_is_file build/lib.so
 
 '
 
@@ -242,18 +242,18 @@ test_expect_success 'git clean -id (select - number 2)' '
        docs/manual.txt obj.o build/lib.so &&
        test_write_lines s "2 3" 5 "" c |
        git clean -id &&
-       test -f Makefile &&
-       test -f README &&
-       test -f src/part1.c &&
-       test -f src/part2.c &&
-       test -f a.out &&
-       test ! -f docs/manual.txt &&
-       test ! -f src/part3.c &&
-       test -f src/part3.h &&
-       test ! -f src/part4.c &&
-       test -f src/part4.h &&
-       test -f obj.o &&
-       test -f build/lib.so
+       test_path_is_file Makefile &&
+       test_path_is_file README &&
+       test_path_is_file src/part1.c &&
+       test_path_is_file src/part2.c &&
+       test_path_is_file a.out &&
+       test_path_is_missing docs/manual.txt &&
+       test_path_is_missing src/part3.c &&
+       test_path_is_file src/part3.h &&
+       test_path_is_missing src/part4.c &&
+       test_path_is_file src/part4.h &&
+       test_path_is_file obj.o &&
+       test_path_is_file build/lib.so
 
 '
 
@@ -264,18 +264,18 @@ test_expect_success 'git clean -id (select - number 3)' '
        docs/manual.txt obj.o build/lib.so &&
        test_write_lines s "3,4 5" "" c |
        git clean -id &&
-       test -f Makefile &&
-       test -f README &&
-       test -f src/part1.c &&
-       test -f src/part2.c &&
-       test -f a.out &&
-       test -f docs/manual.txt &&
-       test ! -f src/part3.c &&
-       test ! -f src/part3.h &&
-       test ! -f src/part4.c &&
-       test -f src/part4.h &&
-       test -f obj.o &&
-       test -f build/lib.so
+       test_path_is_file Makefile &&
+       test_path_is_file README &&
+       test_path_is_file src/part1.c &&
+       test_path_is_file src/part2.c &&
+       test_path_is_file a.out &&
+       test_path_is_file docs/manual.txt &&
+       test_path_is_missing src/part3.c &&
+       test_path_is_missing src/part3.h &&
+       test_path_is_missing src/part4.c &&
+       test_path_is_file src/part4.h &&
+       test_path_is_file obj.o &&
+       test_path_is_file build/lib.so
 
 '
 
@@ -285,11 +285,11 @@ test_expect_success 'git clean -id (select - filenames)' '
        touch a.out foo.txt bar.txt baz.txt &&
        test_write_lines s "a.out fo ba bar" "" c |
        git clean -id &&
-       test -f Makefile &&
-       test ! -f a.out &&
-       test ! -f foo.txt &&
-       test ! -f bar.txt &&
-       test -f baz.txt &&
+       test_path_is_file Makefile &&
+       test_path_is_missing a.out &&
+       test_path_is_missing foo.txt &&
+       test_path_is_missing bar.txt &&
+       test_path_is_file baz.txt &&
        rm baz.txt
 
 '
@@ -301,18 +301,18 @@ test_expect_success 'git clean -id (select - range)' '
        docs/manual.txt obj.o build/lib.so &&
        test_write_lines s "1,3-4" 2 "" c |
        git clean -id &&
-       test -f Makefile &&
-       test -f README &&
-       test -f src/part1.c &&
-       test -f src/part2.c &&
-       test ! -f a.out &&
-       test ! -f src/part3.c &&
-       test ! -f src/part3.h &&
-       test -f src/part4.c &&
-       test -f src/part4.h &&
-       test ! -f docs/manual.txt &&
-       test -f obj.o &&
-       test -f build/lib.so
+       test_path_is_file Makefile &&
+       test_path_is_file README &&
+       test_path_is_file src/part1.c &&
+       test_path_is_file src/part2.c &&
+       test_path_is_missing a.out &&
+       test_path_is_missing src/part3.c &&
+       test_path_is_missing src/part3.h &&
+       test_path_is_file src/part4.c &&
+       test_path_is_file src/part4.h &&
+       test_path_is_missing docs/manual.txt &&
+       test_path_is_file obj.o &&
+       test_path_is_file build/lib.so
 
 '
 
@@ -323,18 +323,18 @@ test_expect_success 'git clean -id (select - range 2)' '
        docs/manual.txt obj.o build/lib.so &&
        test_write_lines s "4- 1" "" c |
        git clean -id &&
-       test -f Makefile &&
-       test -f README &&
-       test -f src/part1.c &&
-       test -f src/part2.c &&
-       test ! -f a.out &&
-       test -f docs/manual.txt &&
-       test -f src/part3.c &&
-       test ! -f src/part3.h &&
-       test ! -f src/part4.c &&
-       test ! -f src/part4.h &&
-       test -f obj.o &&
-       test -f build/lib.so
+       test_path_is_file Makefile &&
+       test_path_is_file README &&
+       test_path_is_file src/part1.c &&
+       test_path_is_file src/part2.c &&
+       test_path_is_missing a.out &&
+       test_path_is_file docs/manual.txt &&
+       test_path_is_file src/part3.c &&
+       test_path_is_missing src/part3.h &&
+       test_path_is_missing src/part4.c &&
+       test_path_is_missing src/part4.h &&
+       test_path_is_file obj.o &&
+       test_path_is_file build/lib.so
 
 '
 
@@ -345,18 +345,18 @@ test_expect_success 'git clean -id (inverse select)' '
        docs/manual.txt obj.o build/lib.so &&
        test_write_lines s "*" "-5- 1 -2" "" c |
        git clean -id &&
-       test -f Makefile &&
-       test -f README &&
-       test -f src/part1.c &&
-       test -f src/part2.c &&
-       test ! -f a.out &&
-       test -f docs/manual.txt &&
-       test ! -f src/part3.c &&
-       test ! -f src/part3.h &&
-       test -f src/part4.c &&
-       test -f src/part4.h &&
-       test -f obj.o &&
-       test -f build/lib.so
+       test_path_is_file Makefile &&
+       test_path_is_file README &&
+       test_path_is_file src/part1.c &&
+       test_path_is_file src/part2.c &&
+       test_path_is_missing a.out &&
+       test_path_is_file docs/manual.txt &&
+       test_path_is_missing src/part3.c &&
+       test_path_is_missing src/part3.h &&
+       test_path_is_file src/part4.c &&
+       test_path_is_file src/part4.h &&
+       test_path_is_file obj.o &&
+       test_path_is_file build/lib.so
 
 '
 
@@ -367,18 +367,18 @@ test_expect_success 'git clean -id (ask)' '
        docs/manual.txt obj.o build/lib.so &&
        test_write_lines a Y y no yes bad "" |
        git clean -id &&
-       test -f Makefile &&
-       test -f README &&
-       test -f src/part1.c &&
-       test -f src/part2.c &&
-       test ! -f a.out &&
-       test ! -f docs/manual.txt &&
-       test -f src/part3.c &&
-       test ! -f src/part3.h &&
-       test -f src/part4.c &&
-       test -f src/part4.h &&
-       test -f obj.o &&
-       test -f build/lib.so
+       test_path_is_file Makefile &&
+       test_path_is_file README &&
+       test_path_is_file src/part1.c &&
+       test_path_is_file src/part2.c &&
+       test_path_is_missing a.out &&
+       test_path_is_missing docs/manual.txt &&
+       test_path_is_file src/part3.c &&
+       test_path_is_missing src/part3.h &&
+       test_path_is_file src/part4.c &&
+       test_path_is_file src/part4.h &&
+       test_path_is_file obj.o &&
+       test_path_is_file build/lib.so
 
 '
 
@@ -389,18 +389,18 @@ test_expect_success 'git clean -id (ask - Ctrl+D)' '
        docs/manual.txt obj.o build/lib.so &&
        test_write_lines a Y no yes "\04" |
        git clean -id &&
-       test -f Makefile &&
-       test -f README &&
-       test -f src/part1.c &&
-       test -f src/part2.c &&
-       test ! -f a.out &&
-       test -f docs/manual.txt &&
-       test ! -f src/part3.c &&
-       test -f src/part3.h &&
-       test -f src/part4.c &&
-       test -f src/part4.h &&
-       test -f obj.o &&
-       test -f build/lib.so
+       test_path_is_file Makefile &&
+       test_path_is_file README &&
+       test_path_is_file src/part1.c &&
+       test_path_is_file src/part2.c &&
+       test_path_is_missing a.out &&
+       test_path_is_file docs/manual.txt &&
+       test_path_is_missing src/part3.c &&
+       test_path_is_file src/part3.h &&
+       test_path_is_file src/part4.c &&
+       test_path_is_file src/part4.h &&
+       test_path_is_file obj.o &&
+       test_path_is_file build/lib.so
 
 '
 
@@ -412,18 +412,18 @@ test_expect_success 'git clean -id with prefix and path (filter)' '
        (cd build/ &&
         test_write_lines f docs "*.h" "" c |
         git clean -id ..) &&
-       test -f Makefile &&
-       test -f README &&
-       test -f src/part1.c &&
-       test -f src/part2.c &&
-       test ! -f a.out &&
-       test -f docs/manual.txt &&
-       test ! -f src/part3.c &&
-       test -f src/part3.h &&
-       test ! -f src/part4.c &&
-       test -f src/part4.h &&
-       test -f obj.o &&
-       test -f build/lib.so
+       test_path_is_file Makefile &&
+       test_path_is_file README &&
+       test_path_is_file src/part1.c &&
+       test_path_is_file src/part2.c &&
+       test_path_is_missing a.out &&
+       test_path_is_file docs/manual.txt &&
+       test_path_is_missing src/part3.c &&
+       test_path_is_file src/part3.h &&
+       test_path_is_missing src/part4.c &&
+       test_path_is_file src/part4.h &&
+       test_path_is_file obj.o &&
+       test_path_is_file build/lib.so
 
 '
 
@@ -435,18 +435,18 @@ test_expect_success 'git clean -id with prefix and path (select by name)' '
        (cd build/ &&
         test_write_lines s ../docs/ ../src/part3.c ../src/part4.c "" c |
         git clean -id ..) &&
-       test -f Makefile &&
-       test -f README &&
-       test -f src/part1.c &&
-       test -f src/part2.c &&
-       test -f a.out &&
-       test ! -f docs/manual.txt &&
-       test ! -f src/part3.c &&
-       test -f src/part3.h &&
-       test ! -f src/part4.c &&
-       test -f src/part4.h &&
-       test -f obj.o &&
-       test -f build/lib.so
+       test_path_is_file Makefile &&
+       test_path_is_file README &&
+       test_path_is_file src/part1.c &&
+       test_path_is_file src/part2.c &&
+       test_path_is_file a.out &&
+       test_path_is_missing docs/manual.txt &&
+       test_path_is_missing src/part3.c &&
+       test_path_is_file src/part3.h &&
+       test_path_is_missing src/part4.c &&
+       test_path_is_file src/part4.h &&
+       test_path_is_file obj.o &&
+       test_path_is_file build/lib.so
 
 '
 
@@ -458,18 +458,18 @@ test_expect_success 'git clean -id with prefix and path (ask)' '
        (cd build/ &&
         test_write_lines a Y y no yes bad "" |
         git clean -id ..) &&
-       test -f Makefile &&
-       test -f README &&
-       test -f src/part1.c &&
-       test -f src/part2.c &&
-       test ! -f a.out &&
-       test ! -f docs/manual.txt &&
-       test -f src/part3.c &&
-       test ! -f src/part3.h &&
-       test -f src/part4.c &&
-       test -f src/part4.h &&
-       test -f obj.o &&
-       test -f build/lib.so
+       test_path_is_file Makefile &&
+       test_path_is_file README &&
+       test_path_is_file src/part1.c &&
+       test_path_is_file src/part2.c &&
+       test_path_is_missing a.out &&
+       test_path_is_missing docs/manual.txt &&
+       test_path_is_file src/part3.c &&
+       test_path_is_missing src/part3.h &&
+       test_path_is_file src/part4.c &&
+       test_path_is_file src/part4.h &&
+       test_path_is_file obj.o &&
+       test_path_is_file build/lib.so
 
 '
 
index ec9c6de114fddfdb7127434ae17f3ae419164f6c..3d3e13ccf87215adc1f8dcc8cc674e13d0f4bc91 100755 (executable)
@@ -1935,4 +1935,18 @@ test_expect_success 'suppressing --- does not disable cut-line handling' '
        test_cmp expected actual
 '
 
+test_expect_success 'handling of --- lines in conjunction with cut-lines' '
+       echo "my-trailer: here" >expected &&
+
+       git interpret-trailers --parse >actual <<-\EOF &&
+       subject
+
+       my-trailer: here
+       ---
+       # ------------------------ >8 ------------------------
+       EOF
+
+       test_cmp expected actual
+'
+
 test_done
index 363f9dc0e41b2686aa9892d5e21e153ca54727b9..730f3c7f81090e9d08d02b1ca0e370b05b3dd746 100755 (executable)
@@ -1037,4 +1037,227 @@ test_expect_success 'split-index and FSMonitor work well together' '
        )
 '
 
+# The FSMonitor daemon reports the OBSERVED pathname of modified files
+# and thus contains the OBSERVED spelling on case-insensitive file
+# systems.  The daemon does not (and should not) load the .git/index
+# file and therefore does not know the expected case-spelling.  Since
+# it is possible for the user to create files/subdirectories with the
+# incorrect case, a modified file event for a tracked will not have
+# the EXPECTED case. This can cause `index_name_pos()` to incorrectly
+# report that the file is untracked. This causes the client to fail to
+# mark the file as possibly dirty (keeping the CE_FSMONITOR_VALID bit
+# set) so that `git status` will avoid inspecting it and thus not
+# present in the status output.
+#
+# The setup is a little contrived.
+#
+test_expect_success CASE_INSENSITIVE_FS 'fsmonitor subdir case wrong on disk' '
+       test_when_finished "stop_daemon_delete_repo subdir_case_wrong" &&
+
+       git init subdir_case_wrong &&
+       (
+               cd subdir_case_wrong &&
+               echo x >AAA &&
+               echo x >BBB &&
+
+               mkdir dir1 &&
+               echo x >dir1/file1 &&
+               mkdir dir1/dir2 &&
+               echo x >dir1/dir2/file2 &&
+               mkdir dir1/dir2/dir3 &&
+               echo x >dir1/dir2/dir3/file3 &&
+
+               echo x >yyy &&
+               echo x >zzz &&
+               git add . &&
+               git commit -m "data" &&
+
+               # This will cause "dir1/" and everything under it
+               # to be deleted.
+               git sparse-checkout set --cone --sparse-index &&
+
+               # Create dir2 with the wrong case and then let Git
+               # repopulate dir3 -- it will not correct the spelling
+               # of dir2.
+               mkdir dir1 &&
+               mkdir dir1/DIR2 &&
+               git sparse-checkout add dir1/dir2/dir3
+       ) &&
+
+       start_daemon -C subdir_case_wrong --tf "$PWD/subdir_case_wrong.trace" &&
+
+       # Enable FSMonitor in the client. Run enough commands for
+       # the .git/index to sync up with the daemon with everything
+       # marked clean.
+       git -C subdir_case_wrong config core.fsmonitor true &&
+       git -C subdir_case_wrong update-index --fsmonitor &&
+       git -C subdir_case_wrong status &&
+
+       # Make some files dirty so that FSMonitor gets FSEvents for
+       # each of them.
+       echo xx >>subdir_case_wrong/AAA &&
+       echo xx >>subdir_case_wrong/dir1/DIR2/dir3/file3 &&
+       echo xx >>subdir_case_wrong/zzz &&
+
+       GIT_TRACE_FSMONITOR="$PWD/subdir_case_wrong.log" \
+               git -C subdir_case_wrong --no-optional-locks status --short \
+                       >"$PWD/subdir_case_wrong.out" &&
+
+       # "git status" should have gotten file events for each of
+       # the 3 files.
+       #
+       # "dir2" should be in the observed case on disk.
+       grep "fsmonitor_refresh_callback" \
+               <"$PWD/subdir_case_wrong.log" \
+               >"$PWD/subdir_case_wrong.log1" &&
+
+       grep -q "AAA.*pos 0" "$PWD/subdir_case_wrong.log1" &&
+       grep -q "zzz.*pos 6" "$PWD/subdir_case_wrong.log1" &&
+
+       grep -q "dir1/DIR2/dir3/file3.*pos -3" "$PWD/subdir_case_wrong.log1" &&
+
+       # Verify that we get a mapping event to correct the case.
+       grep -q "MAP:.*dir1/DIR2/dir3/file3.*dir1/dir2/dir3/file3" \
+               "$PWD/subdir_case_wrong.log1" &&
+
+       # The refresh-callbacks should have caused "git status" to clear
+       # the CE_FSMONITOR_VALID bit on each of those files and caused
+       # the worktree scan to visit them and mark them as modified.
+       grep -q " M AAA" "$PWD/subdir_case_wrong.out" &&
+       grep -q " M zzz" "$PWD/subdir_case_wrong.out" &&
+       grep -q " M dir1/dir2/dir3/file3" "$PWD/subdir_case_wrong.out"
+'
+
+test_expect_success CASE_INSENSITIVE_FS 'fsmonitor file case wrong on disk' '
+       test_when_finished "stop_daemon_delete_repo file_case_wrong" &&
+
+       git init file_case_wrong &&
+       (
+               cd file_case_wrong &&
+               echo x >AAA &&
+               echo x >BBB &&
+
+               mkdir dir1 &&
+               mkdir dir1/dir2 &&
+               mkdir dir1/dir2/dir3 &&
+               echo x >dir1/dir2/dir3/FILE-3-B &&
+               echo x >dir1/dir2/dir3/XXXX-3-X &&
+               echo x >dir1/dir2/dir3/file-3-a &&
+               echo x >dir1/dir2/dir3/yyyy-3-y &&
+               mkdir dir1/dir2/dir4 &&
+               echo x >dir1/dir2/dir4/FILE-4-A &&
+               echo x >dir1/dir2/dir4/XXXX-4-X &&
+               echo x >dir1/dir2/dir4/file-4-b &&
+               echo x >dir1/dir2/dir4/yyyy-4-y &&
+
+               echo x >yyy &&
+               echo x >zzz &&
+               git add . &&
+               git commit -m "data"
+       ) &&
+
+       start_daemon -C file_case_wrong --tf "$PWD/file_case_wrong.trace" &&
+
+       # Enable FSMonitor in the client. Run enough commands for
+       # the .git/index to sync up with the daemon with everything
+       # marked clean.
+       git -C file_case_wrong config core.fsmonitor true &&
+       git -C file_case_wrong update-index --fsmonitor &&
+       git -C file_case_wrong status &&
+
+       # Make some files dirty so that FSMonitor gets FSEvents for
+       # each of them.
+       echo xx >>file_case_wrong/AAA &&
+       echo xx >>file_case_wrong/zzz &&
+
+       # Rename some files so that FSMonitor sees a create and delete
+       # FSEvent for each.  (A simple "mv foo FOO" is not portable
+       # between macOS and Windows. It works on both platforms, but makes
+       # the test messy, since (1) one platform updates "ctime" on the
+       # moved file and one does not and (2) it causes a directory event
+       # on one platform and not on the other which causes additional
+       # scanning during "git status" which causes a "H" vs "h" discrepancy
+       # in "git ls-files -f".)  So old-school it and move it out of the
+       # way and copy it to the case-incorrect name so that we get fresh
+       # "ctime" and "mtime" values.
+
+       mv file_case_wrong/dir1/dir2/dir3/file-3-a file_case_wrong/dir1/dir2/dir3/ORIG &&
+       cp file_case_wrong/dir1/dir2/dir3/ORIG     file_case_wrong/dir1/dir2/dir3/FILE-3-A &&
+       rm file_case_wrong/dir1/dir2/dir3/ORIG &&
+       mv file_case_wrong/dir1/dir2/dir4/FILE-4-A file_case_wrong/dir1/dir2/dir4/ORIG &&
+       cp file_case_wrong/dir1/dir2/dir4/ORIG     file_case_wrong/dir1/dir2/dir4/file-4-a &&
+       rm file_case_wrong/dir1/dir2/dir4/ORIG &&
+
+       # Run status enough times to fully sync.
+       #
+       # The first instance should get the create and delete FSEvents
+       # for each pair.  Status should update the index with a new FSM
+       # token (so the next invocation will not see data for these
+       # events).
+
+       GIT_TRACE_FSMONITOR="$PWD/file_case_wrong-try1.log" \
+               git -C file_case_wrong status --short \
+                       >"$PWD/file_case_wrong-try1.out" &&
+       grep -q "fsmonitor_refresh_callback.*FILE-3-A.*pos -3" "$PWD/file_case_wrong-try1.log" &&
+       grep -q "fsmonitor_refresh_callback.*file-3-a.*pos 4"  "$PWD/file_case_wrong-try1.log" &&
+       grep -q "fsmonitor_refresh_callback.*FILE-4-A.*pos 6"  "$PWD/file_case_wrong-try1.log" &&
+       grep -q "fsmonitor_refresh_callback.*file-4-a.*pos -9" "$PWD/file_case_wrong-try1.log" &&
+
+       # FSM refresh will have invalidated the FSM bit and cause a regular
+       # (real) scan of these tracked files, so they should have "H" status.
+       # (We will not see a "h" status until the next refresh (on the next
+       # command).)
+
+       git -C file_case_wrong ls-files -f >"$PWD/file_case_wrong-lsf1.out" &&
+       grep -q "H dir1/dir2/dir3/file-3-a" "$PWD/file_case_wrong-lsf1.out" &&
+       grep -q "H dir1/dir2/dir4/FILE-4-A" "$PWD/file_case_wrong-lsf1.out" &&
+
+
+       # Try the status again. We assume that the above status command
+       # advanced the token so that the next one will not see those events.
+
+       GIT_TRACE_FSMONITOR="$PWD/file_case_wrong-try2.log" \
+               git -C file_case_wrong status --short \
+                       >"$PWD/file_case_wrong-try2.out" &&
+       ! grep -q "fsmonitor_refresh_callback.*FILE-3-A.*pos" "$PWD/file_case_wrong-try2.log" &&
+       ! grep -q "fsmonitor_refresh_callback.*file-3-a.*pos" "$PWD/file_case_wrong-try2.log" &&
+       ! grep -q "fsmonitor_refresh_callback.*FILE-4-A.*pos" "$PWD/file_case_wrong-try2.log" &&
+       ! grep -q "fsmonitor_refresh_callback.*file-4-a.*pos" "$PWD/file_case_wrong-try2.log" &&
+
+       # FSM refresh saw nothing, so it will mark all files as valid,
+       # so they should now have "h" status.
+
+       git -C file_case_wrong ls-files -f >"$PWD/file_case_wrong-lsf2.out" &&
+       grep -q "h dir1/dir2/dir3/file-3-a" "$PWD/file_case_wrong-lsf2.out" &&
+       grep -q "h dir1/dir2/dir4/FILE-4-A" "$PWD/file_case_wrong-lsf2.out" &&
+
+
+       # We now have files with clean content, but with case-incorrect
+       # file names.  Modify them to see if status properly reports
+       # them.
+
+       echo xx >>file_case_wrong/dir1/dir2/dir3/FILE-3-A &&
+       echo xx >>file_case_wrong/dir1/dir2/dir4/file-4-a &&
+
+       GIT_TRACE_FSMONITOR="$PWD/file_case_wrong-try3.log" \
+               git -C file_case_wrong --no-optional-locks status --short \
+                       >"$PWD/file_case_wrong-try3.out" &&
+
+       # Verify that we get a mapping event to correct the case.
+       grep -q "fsmonitor_refresh_callback MAP:.*dir1/dir2/dir3/FILE-3-A.*dir1/dir2/dir3/file-3-a" \
+               "$PWD/file_case_wrong-try3.log" &&
+       grep -q "fsmonitor_refresh_callback MAP:.*dir1/dir2/dir4/file-4-a.*dir1/dir2/dir4/FILE-4-A" \
+               "$PWD/file_case_wrong-try3.log" &&
+
+       # FSEvents are in observed case.
+       grep -q "fsmonitor_refresh_callback.*FILE-3-A.*pos -3" "$PWD/file_case_wrong-try3.log" &&
+       grep -q "fsmonitor_refresh_callback.*file-4-a.*pos -9" "$PWD/file_case_wrong-try3.log" &&
+
+       # The refresh-callbacks should have caused "git status" to clear
+       # the CE_FSMONITOR_VALID bit on each of those files and caused
+       # the worktree scan to visit them and mark them as modified.
+       grep -q " M dir1/dir2/dir3/file-3-a" "$PWD/file_case_wrong-try3.out" &&
+       grep -q " M dir1/dir2/dir4/FILE-4-A" "$PWD/file_case_wrong-try3.out"
+'
+
 test_done
index be3735dff0837a6050780082f3acc4e1f7da37dc..71e1ef3a103e780bd3f95f4e703ba23c29f71be1 100755 (executable)
@@ -48,7 +48,7 @@ test_expect_success '--expire-to stores pruned objects (now)' '
                # ...in other words, the combined contents of this
                # repository and expired.git should be the same as the
                # set of objects we started with.
-               cat expired.objects remaining.objects | sort >actual &&
+               sort expired.objects remaining.objects >actual &&
                test_cmp expect actual &&
 
                # The "moved" objects (i.e., those in expired.git)
index ca04242ca016368a5644ef7d04c8b3dab0569260..eb64b766bdfa24ee6fa3e8578188844b10e1ef66 100755 (executable)
@@ -43,7 +43,7 @@ test_expect_success 'cat-file --textconv --path=<path> works' '
        sha1=$(git rev-parse -q --verify HEAD:world.txt) &&
        test_config diff.txt.textconv "tr A-Za-z N-ZA-Mn-za-m <" &&
        git cat-file --textconv --path=hello.txt $sha1 >rot13 &&
-       test uryyb = "$(cat rot13 | remove_cr)"
+       test uryyb = "$(remove_cr <rot13)"
 '
 
 test_expect_success '--path=<path> complains without --textconv/--filters' '
index 9a03b0f361ff6db76fb9df2b8bd6dd8d50c4d57b..dbfbd86e83a756b1155925d3b739a6396d86af3c 100755 (executable)
@@ -25,11 +25,11 @@ test_expect_success setup '
 
        git blame --line-porcelain file >blame_raw &&
 
-       grep -E "^[0-9a-f]+ [0-9]+ 1" blame_raw | sed -e "s/ .*//" >actual &&
+       sed -ne "/^[0-9a-f][0-9a-f]* [0-9][0-9]* 1/s/ .*//p" blame_raw >actual &&
        git rev-parse X >expect &&
        test_cmp expect actual &&
 
-       grep -E "^[0-9a-f]+ [0-9]+ 2" blame_raw | sed -e "s/ .*//" >actual &&
+       sed -ne "/^[0-9a-f][0-9a-f]* [0-9][0-9]* 2/s/ .*//p" blame_raw >actual &&
        git rev-parse X >expect &&
        test_cmp expect actual
 '
@@ -53,11 +53,11 @@ do
        test_expect_success "ignore_rev_changing_lines ($I)" '
                git blame --line-porcelain --ignore-rev $I file >blame_raw &&
 
-               grep -E "^[0-9a-f]+ [0-9]+ 1" blame_raw | sed -e "s/ .*//" >actual &&
+               sed -ne "/^[0-9a-f][0-9a-f]* [0-9][0-9]* 1/s/ .*//p" blame_raw >actual &&
                git rev-parse A >expect &&
                test_cmp expect actual &&
 
-               grep -E "^[0-9a-f]+ [0-9]+ 2" blame_raw | sed -e "s/ .*//" >actual &&
+               sed -ne "/^[0-9a-f][0-9a-f]* [0-9][0-9]* 2/s/ .*//p" blame_raw >actual &&
                git rev-parse B >expect &&
                test_cmp expect actual
        '
@@ -79,10 +79,10 @@ test_expect_success ignore_rev_adding_unblamable_lines '
        git rev-parse Y >expect &&
        git blame --line-porcelain file --ignore-rev Y >blame_raw &&
 
-       grep -E "^[0-9a-f]+ [0-9]+ 3" blame_raw | sed -e "s/ .*//" >actual &&
+       sed -ne "/^[0-9a-f][0-9a-f]* [0-9][0-9]* 3/s/ .*//p" blame_raw >actual &&
        test_cmp expect actual &&
 
-       grep -E "^[0-9a-f]+ [0-9]+ 4" blame_raw | sed -e "s/ .*//" >actual &&
+       sed -ne "/^[0-9a-f][0-9a-f]* [0-9][0-9]* 4/s/ .*//p" blame_raw >actual &&
        test_cmp expect actual
 '
 
@@ -92,11 +92,11 @@ test_expect_success ignore_revs_from_files '
        git rev-parse Y >ignore_y &&
        git blame --line-porcelain file --ignore-revs-file ignore_x --ignore-revs-file ignore_y >blame_raw &&
 
-       grep -E "^[0-9a-f]+ [0-9]+ 1" blame_raw | sed -e "s/ .*//" >actual &&
+       sed -ne "/^[0-9a-f][0-9a-f]* [0-9][0-9]* 1/s/ .*//p" blame_raw >actual &&
        git rev-parse A >expect &&
        test_cmp expect actual &&
 
-       grep -E "^[0-9a-f]+ [0-9]+ 2" blame_raw | sed -e "s/ .*//" >actual &&
+       sed -ne "/^[0-9a-f][0-9a-f]* [0-9][0-9]* 2/s/ .*//p" blame_raw >actual &&
        git rev-parse B >expect &&
        test_cmp expect actual
 '
@@ -106,11 +106,11 @@ test_expect_success ignore_revs_from_configs_and_files '
        git config --add blame.ignoreRevsFile ignore_x &&
        git blame --line-porcelain file --ignore-revs-file ignore_y >blame_raw &&
 
-       grep -E "^[0-9a-f]+ [0-9]+ 1" blame_raw | sed -e "s/ .*//" >actual &&
+       sed -ne "/^[0-9a-f][0-9a-f]* [0-9][0-9]* 1/s/ .*//p" blame_raw >actual &&
        git rev-parse A >expect &&
        test_cmp expect actual &&
 
-       grep -E "^[0-9a-f]+ [0-9]+ 2" blame_raw | sed -e "s/ .*//" >actual &&
+       sed -ne "/^[0-9a-f][0-9a-f]* [0-9][0-9]* 2/s/ .*//p" blame_raw >actual &&
        git rev-parse B >expect &&
        test_cmp expect actual
 '
@@ -121,10 +121,10 @@ test_expect_success override_ignore_revs_file '
        git blame --line-porcelain file --ignore-revs-file "" --ignore-revs-file ignore_y >blame_raw &&
        git rev-parse X >expect &&
 
-       grep -E "^[0-9a-f]+ [0-9]+ 1" blame_raw | sed -e "s/ .*//" >actual &&
+       sed -ne "/^[0-9a-f][0-9a-f]* [0-9][0-9]* 1/s/ .*//p" blame_raw >actual &&
        test_cmp expect actual &&
 
-       grep -E "^[0-9a-f]+ [0-9]+ 2" blame_raw | sed -e "s/ .*//" >actual &&
+       sed -ne "/^[0-9a-f][0-9a-f]* [0-9][0-9]* 2/s/ .*//p" blame_raw >actual &&
        test_cmp expect actual
        '
 test_expect_success bad_files_and_revs '
@@ -279,11 +279,11 @@ test_expect_success ignore_merge '
        test_merge M B &&
        git blame --line-porcelain file --ignore-rev M >blame_raw &&
 
-       grep -E "^[0-9a-f]+ [0-9]+ 1" blame_raw | sed -e "s/ .*//" >actual &&
+       sed -ne "/^[0-9a-f][0-9a-f]* [0-9][0-9]* 1/s/ .*//p" blame_raw >actual &&
        git rev-parse B >expect &&
        test_cmp expect actual &&
 
-       grep -E "^[0-9a-f]+ [0-9]+ 9" blame_raw | sed -e "s/ .*//" >actual &&
+       sed -ne "/^[0-9a-f][0-9a-f]* [0-9][0-9]* 9/s/ .*//p" blame_raw >actual &&
        git rev-parse C >expect &&
        test_cmp expect actual
 '
index a159ff96b71882362eaa7d7856289561601674cf..d3261e35b818400f0242a18a4b024877b1307823 100755 (executable)
@@ -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" | grep not-a | sed 's/\///' | sed 's/@/%40/')
+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 &&
index dbb5042b0b8f1a00212ad01bcc8f8907ab47569a..60e30fed3c2cfc22b2b4190655685a428ce36289 100755 (executable)
@@ -986,7 +986,7 @@ test_expect_success 'L: nested tree copy does not corrupt deltas' '
        test_when_finished "git update-ref -d refs/heads/L2" &&
        git fast-import <input &&
        git ls-tree L2 g/b/ >tmp &&
-       cat tmp | cut -f 2 >actual &&
+       cut -f 2 <tmp >actual &&
        test_cmp expect actual &&
        git fsck $(git rev-parse L2)
 '
@@ -2007,12 +2007,11 @@ test_expect_success 'Q: verify first notes commit' '
 '
 
 test_expect_success 'Q: verify first notes tree' '
-       cat >expect.unsorted <<-EOF &&
+       sort >expect <<-EOF &&
        100644 blob $commit1
        100644 blob $commit2
        100644 blob $commit3
        EOF
-       cat expect.unsorted | sort >expect &&
        git cat-file -p refs/notes/foobar~2^{tree} | sed "s/ [0-9a-f]*  / /" >actual &&
        test_cmp expect actual
 '
@@ -2048,12 +2047,11 @@ test_expect_success 'Q: verify second notes commit' '
 '
 
 test_expect_success 'Q: verify second notes tree' '
-       cat >expect.unsorted <<-EOF &&
+       sort >expect <<-EOF &&
        100644 blob $commit1
        100644 blob $commit2
        100644 blob $commit3
        EOF
-       cat expect.unsorted | sort >expect &&
        git cat-file -p refs/notes/foobar^^{tree} | sed "s/ [0-9a-f]*   / /" >actual &&
        test_cmp expect actual
 '
@@ -2088,10 +2086,9 @@ test_expect_success 'Q: verify third notes commit' '
 '
 
 test_expect_success 'Q: verify third notes tree' '
-       cat >expect.unsorted <<-EOF &&
+       sort >expect <<-EOF &&
        100644 blob $commit1
        EOF
-       cat expect.unsorted | sort >expect &&
        git cat-file -p refs/notes/foobar2^{tree} | sed "s/ [0-9a-f]*   / /" >actual &&
        test_cmp expect actual
 '
@@ -2115,10 +2112,9 @@ test_expect_success 'Q: verify fourth notes commit' '
 '
 
 test_expect_success 'Q: verify fourth notes tree' '
-       cat >expect.unsorted <<-EOF &&
+       sort >expect <<-EOF &&
        100644 blob $commit2
        EOF
-       cat expect.unsorted | sort >expect &&
        git cat-file -p refs/notes/foobar^{tree} | sed "s/ [0-9a-f]*    / /" >actual &&
        test_cmp expect actual
 '
index e9a12c18bbd3f8bd43659d0b4eee0e4dfbad30ab..1eb035ee4ce547d059e07c43f04102e66ebcb000 100755 (executable)
@@ -236,7 +236,7 @@ EOF
 
 test_expect_success 'set up faked signed tag' '
 
-       cat signed-tag-import | git fast-import
+       git fast-import <signed-tag-import
 
 '
 
@@ -537,7 +537,7 @@ test_expect_success 'full-tree re-shows unmodified files'        '
 
 test_expect_success 'set-up a few more tags for tag export tests' '
        git checkout -f main &&
-       HEAD_TREE=$(git show -s --pretty=raw HEAD | grep tree | sed "s/tree //") &&
+       HEAD_TREE=$(git show -s --pretty=raw HEAD | sed -n "/tree/s/tree //p") &&
        git tag    tree_tag        -m "tagging a tree" $HEAD_TREE &&
        git tag -a tree_tag-obj    -m "tagging a tree" $HEAD_TREE &&
        git tag    tag-obj_tag     -m "tagging a tag" tree_tag-obj &&
index 003c0b61d0ff45864630782a509a018ff4079116..e499c7f955125eb25ce0dfdcd78012adf8cf4d7a 100755 (executable)
@@ -117,12 +117,12 @@ END VERIFICATION REQUEST
 EOF
 
 test_expect_success 'pserver authentication' '
-       cat request-anonymous | git-cvsserver pserver >log 2>&1 &&
+       git-cvsserver pserver <request-anonymous >log 2>&1 &&
        sed -ne \$p log | grep "^I LOVE YOU\$"
 '
 
 test_expect_success 'pserver authentication failure (non-anonymous user)' '
-       if cat request-git | git-cvsserver pserver >log 2>&1
+       if git-cvsserver pserver <request-git >log 2>&1
        then
            false
        else
@@ -132,17 +132,17 @@ test_expect_success 'pserver authentication failure (non-anonymous user)' '
 '
 
 test_expect_success 'pserver authentication success (non-anonymous user with password)' '
-       cat login-git-ok | git-cvsserver pserver >log 2>&1 &&
+       git-cvsserver pserver <login-git-ok >log 2>&1 &&
        sed -ne \$p log | grep "^I LOVE YOU\$"
 '
 
 test_expect_success 'pserver authentication (login)' '
-       cat login-anonymous | git-cvsserver pserver >log 2>&1 &&
+       git-cvsserver pserver <login-anonymous >log 2>&1 &&
        sed -ne \$p log | grep "^I LOVE YOU\$"
 '
 
 test_expect_success 'pserver authentication failure (login/non-anonymous user)' '
-       if cat login-git | git-cvsserver pserver >log 2>&1
+       if git-cvsserver pserver <login-git >log 2>&1
        then
            false
        else
@@ -172,7 +172,7 @@ Root $WORKDIR
 EOF
 
 test_expect_success 'req_Root failure (relative pathname)' '
-       if cat request-relative | git-cvsserver pserver >log 2>&1
+       if git-cvsserver pserver <request-relative >log 2>&1
        then
                echo unexpected success
                false
@@ -183,28 +183,26 @@ test_expect_success 'req_Root failure (relative pathname)' '
 '
 
 test_expect_success 'req_Root failure (conflicting roots)' '
-       cat request-conflict | git-cvsserver pserver >log 2>&1 &&
+       git-cvsserver pserver <request-conflict >log 2>&1 &&
        tail log | grep "^error 1 Conflicting roots specified$"
 '
 
 test_expect_success 'req_Root (strict paths)' '
-       cat request-anonymous | git-cvsserver --strict-paths pserver "$SERVERDIR" >log 2>&1 &&
+       git-cvsserver --strict-paths pserver "$SERVERDIR" <request-anonymous >log 2>&1 &&
        sed -ne \$p log | grep "^I LOVE YOU\$"
 '
 
 test_expect_success 'req_Root failure (strict-paths)' '
-       ! cat request-anonymous |
-       git-cvsserver --strict-paths pserver "$WORKDIR" >log 2>&1
+       ! git-cvsserver --strict-paths pserver "$WORKDIR" <request-anonymous >log 2>&1
 '
 
 test_expect_success 'req_Root (w/o strict-paths)' '
-       cat request-anonymous | git-cvsserver pserver "$WORKDIR/" >log 2>&1 &&
+       git-cvsserver pserver "$WORKDIR/" <request-anonymous >log 2>&1 &&
        sed -ne \$p log | grep "^I LOVE YOU\$"
 '
 
 test_expect_success 'req_Root failure (w/o strict-paths)' '
-       ! cat request-anonymous |
-       git-cvsserver pserver "$WORKDIR/gitcvs" >log 2>&1
+       ! git-cvsserver pserver "$WORKDIR/gitcvs" <request-anonymous >log 2>&1
 '
 
 cat >request-base  <<EOF
@@ -217,27 +215,26 @@ Root /gitcvs.git
 EOF
 
 test_expect_success 'req_Root (base-path)' '
-       cat request-base | git-cvsserver --strict-paths --base-path "$WORKDIR/" pserver "$SERVERDIR" >log 2>&1 &&
+       git-cvsserver --strict-paths --base-path "$WORKDIR/" pserver "$SERVERDIR" <request-base >log 2>&1 &&
        sed -ne \$p log | grep "^I LOVE YOU\$"
 '
 
 test_expect_success 'req_Root failure (base-path)' '
-       ! cat request-anonymous |
-       git-cvsserver --strict-paths --base-path "$WORKDIR" pserver "$SERVERDIR" >log 2>&1
+       ! git-cvsserver --strict-paths --base-path "$WORKDIR" pserver "$SERVERDIR" <request-anonymous >log 2>&1
 '
 
 GIT_DIR="$SERVERDIR" git config --bool gitcvs.enabled false || exit 1
 
 test_expect_success 'req_Root (export-all)' '
-       cat request-anonymous | git-cvsserver --export-all pserver "$WORKDIR" >log 2>&1 &&
+       git-cvsserver --export-all pserver "$WORKDIR" <request-anonymous >log 2>&1 &&
        sed -ne \$p log | grep "^I LOVE YOU\$"
 '
 
 test_expect_success 'req_Root failure (export-all w/o directory list)' '
-       ! (cat request-anonymous | git-cvsserver --export-all pserver >log 2>&1 || false)'
+       ! (git-cvsserver --export-all pserver <request-anonymous >log 2>&1 || false)'
 
 test_expect_success 'req_Root (everything together)' '
-       cat request-base | git-cvsserver --export-all --strict-paths --base-path "$WORKDIR/" pserver "$SERVERDIR" >log 2>&1 &&
+       git-cvsserver --export-all --strict-paths --base-path "$WORKDIR/" pserver "$SERVERDIR" <request-base >log 2>&1 &&
        sed -ne \$p log | grep "^I LOVE YOU\$"
 '
 
index 2a6ee2a46787f07c763c6d7608628c65d8181526..bb236cd2b57a3c21885237c5698dfa04ede92b55 100755 (executable)
@@ -175,7 +175,7 @@ test_expect_success 'keyword file create' '
                cp k-text-k k-text-ko &&
                p4 add -t text+ko k-text-ko &&
 
-               cat k-text-k | iconv -f ascii -t utf-16 >k-utf16-k &&
+               iconv -f ascii -t utf-16 <k-text-k >k-utf16-k &&
                p4 add -t utf16+k k-utf16-k &&
 
                cp k-utf16-k k-utf16-ko &&
index af4b286f9d51af7655e86036d9938a691780af23..6ae7ced51be1d4e0d649cea05f2373e592be73ef 100755 (executable)
@@ -418,7 +418,7 @@ test_expect_success 'description with Jobs and values on separate lines' '
                        marshal_dump job0 <change &&
                        marshal_dump job1 <change
                ) | sort >jobs &&
-               cat jobname1 jobname2 | sort >expected &&
+               sort jobname1 jobname2 >expected &&
                test_cmp expected jobs
        )
 '
index a28dbbdd566ca69212f23958056d95b735a0127c..80c8c31e320fd5bbf0912d093682dcad3f287fc3 100755 (executable)
@@ -17,8 +17,8 @@ test_file_in_lfs () {
        sed -n '2,2 p' "$FILE" | grep "^oid " &&
        sed -n '3,3 p' "$FILE" | grep "^size " &&
        test_line_count = 3 "$FILE" &&
-       cat "$FILE" | grep "size $SIZE" &&
-       HASH=$(cat "$FILE" | grep "oid sha256:" | sed -e "s/oid sha256://g") &&
+       grep "size $SIZE" "$FILE" &&
+       HASH=$(sed -ne "/oid sha256:/s/oid sha256://gp" "$FILE") &&
        LFS_FILE=".git/lfs/objects/$(echo "$HASH" | cut -c1-2)/$(echo "$HASH" | cut -c3-4)/$HASH" &&
        echo $EXPECTED_CONTENT >expect &&
        test_path_is_file "$FILE" &&
index b16c284181b8707ed21c85efa0e0a0fea128faae..569cf2310434358d268028ea8d26361727bf8652 100755 (executable)
@@ -1263,6 +1263,29 @@ test_expect_success '__git_complete_fetch_refspecs - fully qualified & prefix' '
        test_cmp expected out
 '
 
+test_expect_success '__git_complete_worktree_paths' '
+       test_when_finished "git worktree remove other_wt" &&
+       git worktree add --orphan other_wt &&
+       run_completion "git worktree remove " &&
+       grep other_wt out
+'
+
+test_expect_success '__git_complete_worktree_paths - not a git repository' '
+       (
+               cd non-repo &&
+               GIT_CEILING_DIRECTORIES="$ROOT" &&
+               export GIT_CEILING_DIRECTORIES &&
+               test_completion "git worktree remove " ""
+       )
+'
+
+test_expect_success '__git_complete_worktree_paths with -C' '
+       test_when_finished "git -C otherrepo worktree remove otherrepo_wt" &&
+       git -C otherrepo worktree add --orphan otherrepo_wt &&
+       run_completion "git -C otherrepo worktree remove " &&
+       grep otherrepo_wt out
+'
+
 test_expect_success 'git switch - with no options, complete local branches and unique remote branch names for DWIM logic' '
        test_completion "git switch " <<-\EOF
        branch-in-other Z
@@ -2804,6 +2827,20 @@ test_expect_success 'git clone --config= - value' '
        EOF
 '
 
+test_expect_success 'git reflog show' '
+       test_when_finished "git checkout - && git branch -d shown" &&
+       git checkout -b shown &&
+       test_completion "git reflog sho" <<-\EOF &&
+       show Z
+       shown Z
+       EOF
+       test_completion "git reflog show sho" "shown " &&
+       test_completion "git reflog shown sho" "shown " &&
+       test_completion "git reflog --unt" "--until=" &&
+       test_completion "git reflog show --unt" "--until=" &&
+       test_completion "git reflog shown --unt" "--until="
+'
+
 test_expect_success 'options with value' '
        test_completion "git merge -X diff-algorithm=" <<-\EOF
 
index b5eaf7fdc1186d4420388c355e794bfdba783f2f..2eccf100c024e21363ac799a1d032470ede16ae9 100644 (file)
@@ -1263,9 +1263,8 @@ test_cmp_bin () {
        cmp "$@"
 }
 
-# Deprecated - do not use this in new code
 test_i18ngrep () {
-       test_grep "$@"
+       BUG "do not use test_i18ngrep---use test_grep instead"
 }
 
 test_grep () {
@@ -1656,7 +1655,16 @@ test_set_hash () {
 
 # Detect the hash algorithm in use.
 test_detect_hash () {
-       test_hash_algo="${GIT_TEST_DEFAULT_HASH:-sha1}"
+       case "$GIT_TEST_DEFAULT_HASH" in
+       "sha256")
+           test_hash_algo=sha256
+           test_compat_hash_algo=sha1
+           ;;
+       *)
+           test_hash_algo=sha1
+           test_compat_hash_algo=sha256
+           ;;
+       esac
 }
 
 # Detect the hash algorithm in use.
@@ -1713,6 +1721,12 @@ test_oid () {
        local algo="${test_hash_algo}" &&
 
        case "$1" in
+       --hash=storage)
+               algo="$test_hash_algo" &&
+               shift;;
+       --hash=compat)
+               algo="$test_compat_hash_algo" &&
+               shift;;
        --hash=*)
                algo="${1#--hash=}" &&
                shift;;
index ecdebf1afb0d8465717c18409877fe310f980e46..ed88cf84314753443c8c42f0043320957d65d165 100644 (file)
 
 static VOLATILE_LIST_HEAD(tempfile_list);
 
-static void remove_template_directory(struct tempfile *tempfile,
+static int remove_template_directory(struct tempfile *tempfile,
                                      int in_signal_handler)
 {
        if (tempfile->directory) {
                if (in_signal_handler)
-                       rmdir(tempfile->directory);
+                       return rmdir(tempfile->directory);
                else
-                       rmdir_or_warn(tempfile->directory);
+                       return rmdir_or_warn(tempfile->directory);
        }
+
+       return 0;
 }
 
 static void remove_tempfiles(int in_signal_handler)
@@ -353,16 +355,19 @@ int rename_tempfile(struct tempfile **tempfile_p, const char *path)
        return 0;
 }
 
-void delete_tempfile(struct tempfile **tempfile_p)
+int delete_tempfile(struct tempfile **tempfile_p)
 {
        struct tempfile *tempfile = *tempfile_p;
+       int err = 0;
 
        if (!is_tempfile_active(tempfile))
-               return;
+               return 0;
 
-       close_tempfile_gently(tempfile);
-       unlink_or_warn(tempfile->filename.buf);
-       remove_template_directory(tempfile, 0);
+       err |= close_tempfile_gently(tempfile);
+       err |= unlink_or_warn(tempfile->filename.buf);
+       err |= remove_template_directory(tempfile, 0);
        deactivate_tempfile(tempfile);
        *tempfile_p = NULL;
+
+       return err ? -1 : 0;
 }
index d0413af733c81ad895669aab30937435cae0f2af..2d2ae5b657d4a97a7fe7b8e2e84c2062717fdde5 100644 (file)
@@ -269,7 +269,7 @@ int reopen_tempfile(struct tempfile *tempfile);
  * `delete_tempfile()` for a `tempfile` object that has already been
  * deleted or renamed.
  */
-void delete_tempfile(struct tempfile **tempfile_p);
+int delete_tempfile(struct tempfile **tempfile_p);
 
 /*
  * Close the file descriptor and/or file pointer if they are still
index f1e268bd159ecb3d491721e67c34eabd397a66ea..f894532d05331c48607fc3434b8d31937f951a4b 100644 (file)
--- a/trace2.c
+++ b/trace2.c
@@ -433,6 +433,9 @@ void trace2_cmd_name_fl(const char *file, int line, const char *name)
        for_each_wanted_builtin (j, tgt_j)
                if (tgt_j->pfn_command_name_fl)
                        tgt_j->pfn_command_name_fl(file, line, name, hierarchy);
+
+       trace2_cmd_list_config();
+       trace2_cmd_list_env_vars();
 }
 
 void trace2_cmd_mode_fl(const char *file, int line, const char *mode)
@@ -464,17 +467,29 @@ void trace2_cmd_alias_fl(const char *file, int line, const char *alias,
 
 void trace2_cmd_list_config_fl(const char *file, int line)
 {
+       static int emitted = 0;
+
        if (!trace2_enabled)
                return;
 
+       if (emitted)
+               return;
+       emitted = 1;
+
        tr2_cfg_list_config_fl(file, line);
 }
 
 void trace2_cmd_list_env_vars_fl(const char *file, int line)
 {
+       static int emitted = 0;
+
        if (!trace2_enabled)
                return;
 
+       if (emitted)
+               return;
+       emitted = 1;
+
        tr2_list_env_vars_fl(file, line);
 }
 
index ef9df4af558fbb18849279cff2e67507026af106..57b4aa7d5acb515b03b2cf123744d8af34d30166 100644 (file)
--- a/trailer.c
+++ b/trailer.c
@@ -5,7 +5,6 @@
 #include "string-list.h"
 #include "run-command.h"
 #include "commit.h"
-#include "tempfile.h"
 #include "trailer.h"
 #include "list.h"
 /*
@@ -145,12 +144,12 @@ static char last_non_space_char(const char *s)
        return '\0';
 }
 
-static void print_tok_val(FILE *outfile, const char *tok, const char *val)
+static void print_tok_val(struct strbuf *out, const char *tok, const char *val)
 {
        char c;
 
        if (!tok) {
-               fprintf(outfile, "%s\n", val);
+               strbuf_addf(out, "%s\n", val);
                return;
        }
 
@@ -158,21 +157,22 @@ static void print_tok_val(FILE *outfile, const char *tok, const char *val)
        if (!c)
                return;
        if (strchr(separators, c))
-               fprintf(outfile, "%s%s\n", tok, val);
+               strbuf_addf(out, "%s%s\n", tok, val);
        else
-               fprintf(outfile, "%s%c %s\n", tok, separators[0], val);
+               strbuf_addf(out, "%s%c %s\n", tok, separators[0], val);
 }
 
-static void print_all(FILE *outfile, struct list_head *head,
-                     const struct process_trailer_options *opts)
+void format_trailers(const struct process_trailer_options *opts,
+                    struct list_head *trailers,
+                    struct strbuf *out)
 {
        struct list_head *pos;
        struct trailer_item *item;
-       list_for_each(pos, head) {
+       list_for_each(pos, trailers) {
                item = list_entry(pos, struct trailer_item, list);
                if ((!opts->trim_empty || strlen(item->value) > 0) &&
                    (!opts->only_trailers || item->token))
-                       print_tok_val(outfile, item->token, item->value);
+                       print_tok_val(out, item->token, item->value);
        }
 }
 
@@ -366,8 +366,8 @@ static int find_same_and_apply_arg(struct list_head *head,
        return 0;
 }
 
-static void process_trailers_lists(struct list_head *head,
-                                  struct list_head *arg_head)
+void process_trailers_lists(struct list_head *head,
+                           struct list_head *arg_head)
 {
        struct list_head *pos, *p;
        struct arg_item *arg_tok;
@@ -589,7 +589,7 @@ static int git_trailer_config(const char *conf_key, const char *value,
        return 0;
 }
 
-static void ensure_configured(void)
+void trailer_config_init(void)
 {
        if (configured)
                return;
@@ -719,7 +719,7 @@ static void add_arg_item(struct list_head *arg_head, char *tok, char *val,
        list_add_tail(&new_item->list, arg_head);
 }
 
-static void parse_trailers_from_config(struct list_head *config_head)
+void parse_trailers_from_config(struct list_head *config_head)
 {
        struct arg_item *item;
        struct list_head *pos;
@@ -735,8 +735,8 @@ static void parse_trailers_from_config(struct list_head *config_head)
        }
 }
 
-static void parse_trailers_from_command_line_args(struct list_head *arg_head,
-                                                 struct list_head *new_trailer_head)
+void parse_trailers_from_command_line_args(struct list_head *arg_head,
+                                          struct list_head *new_trailer_head)
 {
        struct strbuf tok = STRBUF_INIT;
        struct strbuf val = STRBUF_INIT;
@@ -775,17 +775,6 @@ static void parse_trailers_from_command_line_args(struct list_head *arg_head,
        free(cl_separators);
 }
 
-static void read_input_file(struct strbuf *sb, const char *file)
-{
-       if (file) {
-               if (strbuf_read_file(sb, file, 0) < 0)
-                       die_errno(_("could not read input file '%s'"), file);
-       } else {
-               if (strbuf_read(sb, fileno(stdin), 0) < 0)
-                       die_errno(_("could not read from stdin"));
-       }
-}
-
 static const char *next_line(const char *str)
 {
        const char *nl = strchrnul(str, '\n');
@@ -999,16 +988,16 @@ static void unfold_value(struct strbuf *val)
  * Parse trailers in "str", populating the trailer info and "head"
  * linked list structure.
  */
-static void parse_trailers(struct trailer_info *info,
-                            const char *str,
-                            struct list_head *head,
-                            const struct process_trailer_options *opts)
+void parse_trailers(const struct process_trailer_options *opts,
+                   struct trailer_info *info,
+                   const char *str,
+                   struct list_head *head)
 {
        struct strbuf tok = STRBUF_INIT;
        struct strbuf val = STRBUF_INIT;
        size_t i;
 
-       trailer_info_get(info, str, opts);
+       trailer_info_get(opts, str, info);
 
        for (i = 0; i < info->trailer_nr; i++) {
                int separator_pos;
@@ -1034,99 +1023,18 @@ static void parse_trailers(struct trailer_info *info,
        }
 }
 
-static void free_all(struct list_head *head)
+void free_trailers(struct list_head *trailers)
 {
        struct list_head *pos, *p;
-       list_for_each_safe(pos, p, head) {
+       list_for_each_safe(pos, p, trailers) {
                list_del(pos);
                free_trailer_item(list_entry(pos, struct trailer_item, list));
        }
 }
 
-static struct tempfile *trailers_tempfile;
-
-static FILE *create_in_place_tempfile(const char *file)
-{
-       struct stat st;
-       struct strbuf filename_template = STRBUF_INIT;
-       const char *tail;
-       FILE *outfile;
-
-       if (stat(file, &st))
-               die_errno(_("could not stat %s"), file);
-       if (!S_ISREG(st.st_mode))
-               die(_("file %s is not a regular file"), file);
-       if (!(st.st_mode & S_IWUSR))
-               die(_("file %s is not writable by user"), file);
-
-       /* Create temporary file in the same directory as the original */
-       tail = strrchr(file, '/');
-       if (tail)
-               strbuf_add(&filename_template, file, tail - file + 1);
-       strbuf_addstr(&filename_template, "git-interpret-trailers-XXXXXX");
-
-       trailers_tempfile = xmks_tempfile_m(filename_template.buf, st.st_mode);
-       strbuf_release(&filename_template);
-       outfile = fdopen_tempfile(trailers_tempfile, "w");
-       if (!outfile)
-               die_errno(_("could not open temporary file"));
-
-       return outfile;
-}
-
-void process_trailers(const char *file,
-                     const struct process_trailer_options *opts,
-                     struct list_head *new_trailer_head)
-{
-       LIST_HEAD(head);
-       struct strbuf sb = STRBUF_INIT;
-       struct trailer_info info;
-       FILE *outfile = stdout;
-
-       ensure_configured();
-
-       read_input_file(&sb, file);
-
-       if (opts->in_place)
-               outfile = create_in_place_tempfile(file);
-
-       parse_trailers(&info, sb.buf, &head, opts);
-
-       /* Print the lines before the trailers */
-       if (!opts->only_trailers)
-               fwrite(sb.buf, 1, info.trailer_block_start, outfile);
-
-       if (!opts->only_trailers && !info.blank_line_before_trailer)
-               fprintf(outfile, "\n");
-
-
-       if (!opts->only_input) {
-               LIST_HEAD(config_head);
-               LIST_HEAD(arg_head);
-               parse_trailers_from_config(&config_head);
-               parse_trailers_from_command_line_args(&arg_head, new_trailer_head);
-               list_splice(&config_head, &arg_head);
-               process_trailers_lists(&head, &arg_head);
-       }
-
-       print_all(outfile, &head, opts);
-
-       free_all(&head);
-       trailer_info_release(&info);
-
-       /* 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);
-
-       if (opts->in_place)
-               if (rename_tempfile(&trailers_tempfile, file))
-                       die_errno(_("could not rename temporary file to %s"), file);
-
-       strbuf_release(&sb);
-}
-
-void trailer_info_get(struct trailer_info *info, const char *str,
-                     const struct process_trailer_options *opts)
+void trailer_info_get(const struct process_trailer_options *opts,
+                     const char *str,
+                     struct trailer_info *info)
 {
        size_t end_of_log_message = 0, trailer_block_start = 0;
        struct strbuf **trailer_lines, **ptr;
@@ -1134,7 +1042,7 @@ void trailer_info_get(struct trailer_info *info, const char *str,
        size_t nr = 0, alloc = 0;
        char **last = NULL;
 
-       ensure_configured();
+       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);
@@ -1176,23 +1084,13 @@ void trailer_info_release(struct trailer_info *info)
        free(info->trailers);
 }
 
-static void format_trailer_info(struct strbuf *out,
+static void format_trailer_info(const struct process_trailer_options *opts,
                                const struct trailer_info *info,
-                               const char *msg,
-                               const struct process_trailer_options *opts)
+                               struct strbuf *out)
 {
        size_t origlen = out->len;
        size_t i;
 
-       /* 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);
-               return;
-       }
-
        for (i = 0; i < info->trailer_nr; i++) {
                char *trailer = info->trailers[i];
                ssize_t separator_pos = find_separator(trailer, separators);
@@ -1237,13 +1135,25 @@ static void format_trailer_info(struct strbuf *out,
 
 }
 
-void format_trailers_from_commit(struct strbuf *out, const char *msg,
-                                const struct process_trailer_options *opts)
+void format_trailers_from_commit(const struct process_trailer_options *opts,
+                                const char *msg,
+                                struct strbuf *out)
 {
+       LIST_HEAD(trailer_objects);
        struct trailer_info info;
 
-       trailer_info_get(&info, msg, opts);
-       format_trailer_info(out, &info, msg, opts);
+       parse_trailers(opts, &info, 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);
+       } else
+               format_trailer_info(opts, &info, out);
+
+       free_trailers(&trailer_objects);
        trailer_info_release(&info);
 }
 
@@ -1253,7 +1163,7 @@ 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(&iter->internal.info, msg, &opts);
+       trailer_info_get(&opts, msg, &iter->internal.info);
        iter->internal.cur = 0;
 }
 
@@ -1270,6 +1180,7 @@ int trailer_iterator_advance(struct trailer_iterator *iter)
                strbuf_reset(&iter->val);
                parse_trailer(&iter->key, &iter->val, NULL,
                              trailer, separator_pos);
+               /* Always unfold values during iteration. */
                unfold_value(&iter->val);
                return 1;
        }
index 1644cd05f60d9f3ceb89ffa66fb5c5b3da9e5cd4..1d106b6dd403b7755e4c81a1a40c1eea8eeae1d6 100644 (file)
--- a/trailer.h
+++ b/trailer.h
@@ -81,15 +81,31 @@ struct process_trailer_options {
 
 #define PROCESS_TRAILER_OPTIONS_INIT {0}
 
-void process_trailers(const char *file,
-                     const struct process_trailer_options *opts,
-                     struct list_head *new_trailer_head);
+void parse_trailers_from_config(struct list_head *config_head);
 
-void trailer_info_get(struct trailer_info *info, const char *str,
-                     const struct process_trailer_options *opts);
+void parse_trailers_from_command_line_args(struct list_head *arg_head,
+                                          struct list_head *new_trailer_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);
+
+void trailer_info_get(const struct process_trailer_options *,
+                     const char *str,
+                     struct trailer_info *);
 
 void trailer_info_release(struct trailer_info *info);
 
+void trailer_config_init(void);
+void format_trailers(const struct process_trailer_options *,
+                    struct list_head *trailers,
+                    struct strbuf *out);
+void free_trailers(struct list_head *);
+
 /*
  * Format the trailers from the commit msg "msg" into the strbuf "out".
  * Note two caveats about "opts":
@@ -101,8 +117,9 @@ void trailer_info_release(struct trailer_info *info);
  *     only the trailer block itself, even if the "only_trailers" option is not
  *     set.
  */
-void format_trailers_from_commit(struct strbuf *out, const char *msg,
-                                const struct process_trailer_options *opts);
+void format_trailers_from_commit(const struct process_trailer_options *opts,
+                                const char *msg,
+                                struct strbuf *out);
 
 /*
  * An interface for iterating over the trailers found in a particular commit
index dd6002b393738df81c7ecfc85deb4ed0abe4943c..b660b7942f9f9dbfc76193968d3511047b8afd84 100644 (file)
@@ -1078,7 +1078,7 @@ static int push_refs_with_export(struct transport *transport,
        set_common_push_options(transport, data->name, flags);
        if (flags & TRANSPORT_PUSH_FORCE) {
                if (set_helper_option(transport, "force", "true") != 0)
-                       warning(_("helper %s does not support 'force'"), data->name);
+                       warning(_("helper %s does not support '--force'"), data->name);
        }
 
        helper = get_helper(transport);
index 690fa6569bd7fe03ca104e3e789fd67b58e41270..6565d9ad993bd830446277cc35a2aa54567cd18f 100644 (file)
 #include "json-writer.h"
 #include "environment.h"
 
-static const char *get_mode(const char *str, unsigned int *modep)
-{
-       unsigned char c;
-       unsigned int mode = 0;
-
-       if (*str == ' ')
-               return NULL;
-
-       while ((c = *str++) != ' ') {
-               if (c < '0' || c > '7')
-                       return NULL;
-               mode = (mode << 3) + (c - '0');
-       }
-       *modep = mode;
-       return str;
-}
-
 static int decode_tree_entry(struct tree_desc *desc, const char *buf, unsigned long size, struct strbuf *err)
 {
        const char *path;
-       unsigned int mode, len;
-       const unsigned hashsz = the_hash_algo->rawsz;
+       unsigned int len;
+       uint16_t mode;
+       const unsigned hashsz = desc->algo->rawsz;
 
        if (size < hashsz + 3 || buf[size - (hashsz + 1)]) {
                strbuf_addstr(err, _("too-short tree object"));
                return -1;
        }
 
-       path = get_mode(buf, &mode);
+       path = parse_mode(buf, &mode);
        if (!path) {
                strbuf_addstr(err, _("malformed mode in tree entry"));
                return -1;
@@ -54,15 +38,19 @@ 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(&desc->entry.oid, (const unsigned char *)path + len);
+       oidread_algop(&desc->entry.oid, (const unsigned char *)path + len,
+                     desc->algo);
 
        return 0;
 }
 
-static int init_tree_desc_internal(struct tree_desc *desc, const void *buffer,
-                                  unsigned long size, struct strbuf *err,
+static int init_tree_desc_internal(struct tree_desc *desc,
+                                  const struct object_id *oid,
+                                  const void *buffer, unsigned long size,
+                                  struct strbuf *err,
                                   enum tree_desc_flags flags)
 {
+       desc->algo = (oid && oid->algo) ? &hash_algos[oid->algo] : the_hash_algo;
        desc->buffer = buffer;
        desc->size = size;
        desc->flags = flags;
@@ -71,19 +59,21 @@ static int init_tree_desc_internal(struct tree_desc *desc, const void *buffer,
        return 0;
 }
 
-void init_tree_desc(struct tree_desc *desc, const void *buffer, unsigned long size)
+void init_tree_desc(struct tree_desc *desc, const struct object_id *tree_oid,
+                   const void *buffer, unsigned long size)
 {
        struct strbuf err = STRBUF_INIT;
-       if (init_tree_desc_internal(desc, buffer, size, &err, 0))
+       if (init_tree_desc_internal(desc, tree_oid, buffer, size, &err, 0))
                die("%s", err.buf);
        strbuf_release(&err);
 }
 
-int init_tree_desc_gently(struct tree_desc *desc, const void *buffer, unsigned long size,
+int init_tree_desc_gently(struct tree_desc *desc, const struct object_id *oid,
+                         const void *buffer, unsigned long size,
                          enum tree_desc_flags flags)
 {
        struct strbuf err = STRBUF_INIT;
-       int result = init_tree_desc_internal(desc, buffer, size, &err, flags);
+       int result = init_tree_desc_internal(desc, oid, buffer, size, &err, flags);
        if (result)
                error("%s", err.buf);
        strbuf_release(&err);
@@ -102,7 +92,7 @@ void *fill_tree_descriptor(struct repository *r,
                if (!buf)
                        die(_("unable to read tree (%s)"), oid_to_hex(oid));
        }
-       init_tree_desc(desc, buf, size);
+       init_tree_desc(desc, oid, buf, size);
        return buf;
 }
 
@@ -119,7 +109,7 @@ static void entry_extract(struct tree_desc *t, struct name_entry *a)
 static int update_tree_entry_internal(struct tree_desc *desc, struct strbuf *err)
 {
        const void *buf = desc->buffer;
-       const unsigned char *end = (const unsigned char *)desc->entry.path + desc->entry.pathlen + 1 + the_hash_algo->rawsz;
+       const unsigned char *end = (const unsigned char *)desc->entry.path + desc->entry.pathlen + 1 + desc->algo->rawsz;
        unsigned long size = desc->size;
        unsigned long len = end - (const unsigned char *)buf;
 
@@ -633,7 +623,7 @@ int get_tree_entry(struct repository *r,
                retval = -1;
        } else {
                struct tree_desc t;
-               init_tree_desc(&t, tree, size);
+               init_tree_desc(&t, tree_oid, tree, size);
                retval = find_tree_entry(r, &t, name, oid, mode);
        }
        free(tree);
@@ -676,7 +666,7 @@ enum get_oid_result get_tree_entry_follow_symlinks(struct repository *r,
        struct tree_desc t;
        int follows_remaining = GET_TREE_ENTRY_FOLLOW_SYMLINKS_MAX_LINKS;
 
-       init_tree_desc(&t, NULL, 0UL);
+       init_tree_desc(&t, NULL, NULL, 0UL);
        strbuf_addstr(&namebuf, name);
        oidcpy(&current_tree_oid, tree_oid);
 
@@ -712,7 +702,7 @@ enum get_oid_result get_tree_entry_follow_symlinks(struct repository *r,
                                goto done;
 
                        /* descend */
-                       init_tree_desc(&t, tree, size);
+                       init_tree_desc(&t, &current_tree_oid, tree, size);
                }
 
                /* Handle symlinks to e.g. a//b by removing leading slashes */
@@ -746,7 +736,7 @@ enum get_oid_result get_tree_entry_follow_symlinks(struct repository *r,
                        free(parent->tree);
                        parents_nr--;
                        parent = &parents[parents_nr - 1];
-                       init_tree_desc(&t, parent->tree, parent->size);
+                       init_tree_desc(&t, &parent->oid, parent->tree, parent->size);
                        strbuf_remove(&namebuf, 0, remainder ? 3 : 2);
                        continue;
                }
@@ -826,7 +816,7 @@ enum get_oid_result get_tree_entry_follow_symlinks(struct repository *r,
                        contents_start = contents;
 
                        parent = &parents[parents_nr - 1];
-                       init_tree_desc(&t, parent->tree, parent->size);
+                       init_tree_desc(&t, &parent->oid, parent->tree, parent->size);
                        strbuf_splice(&namebuf, 0, len,
                                      contents_start, link_len);
                        if (remainder)
index a6bfa3da3a826bf5c218132b93582e53ace876be..0b1067fbc51affd9ff98ed44fcaad043f9870a22 100644 (file)
@@ -24,6 +24,7 @@ struct name_entry {
  * A semi-opaque data structure used to maintain the current state of the walk.
  */
 struct tree_desc {
+       const struct git_hash_algo *algo;
        /*
         * pointer into the memory representation of the tree. It always
         * points at the current entry being visited.
@@ -83,9 +84,11 @@ int update_tree_entry_gently(struct tree_desc *);
  * size parameters are assumed to be the same as the buffer and size
  * members of `struct tree`.
  */
-void init_tree_desc(struct tree_desc *desc, const void *buf, unsigned long size);
+void init_tree_desc(struct tree_desc *desc, const struct object_id *tree_oid,
+                   const void *buf, unsigned long size);
 
-int init_tree_desc_gently(struct tree_desc *desc, const void *buf, unsigned long size,
+int init_tree_desc_gently(struct tree_desc *desc, const struct object_id *oid,
+                         const void *buf, unsigned long size,
                          enum tree_desc_flags flags);
 
 /*
diff --git a/tree.c b/tree.c
index 508e5fd76fd5bb588796bb9c51a78557880ba074..7973d3f9a83228a70586cf211d8a8eec7b21f639 100644 (file)
--- a/tree.c
+++ b/tree.c
@@ -29,7 +29,7 @@ int read_tree_at(struct repository *r,
        if (parse_tree(tree))
                return -1;
 
-       init_tree_desc(&desc, tree->buffer, tree->size);
+       init_tree_desc(&desc, &tree->object.oid, tree->buffer, tree->size);
 
        while (tree_entry(&desc, &entry)) {
                if (retval != all_entries_interesting) {
index 65002a7220adc2e60aafe6a29555df1cee167ad0..c0fd632d921c4f229d56f5c406a4c97386f3788c 100644 (file)
--- a/walker.c
+++ b/walker.c
@@ -45,7 +45,7 @@ static int process_tree(struct walker *walker, struct tree *tree)
        if (parse_tree(tree))
                return -1;
 
-       init_tree_desc(&desc, tree->buffer, tree->size);
+       init_tree_desc(&desc, &tree->object.oid, tree->buffer, tree->size);
        while (tree_entry(&desc, &entry)) {
                struct object *obj = NULL;
 
index 7108a92b52ce06e5674aadfc5c89d05157a9db93..2db4bb3a1293bb69e081efcf98489a63ca2d812a 100644 (file)
@@ -1093,8 +1093,11 @@ size_t wt_status_locate_end(const char *s, size_t len)
        strbuf_addf(&pattern, "\n%c %s", comment_line_char, cut_line);
        if (starts_with(s, pattern.buf + 1))
                len = 0;
-       else if ((p = strstr(s, pattern.buf)))
-               len = p - s + 1;
+       else if ((p = strstr(s, pattern.buf))) {
+               size_t newlen = p - s + 1;
+               if (newlen < len)
+                       len = newlen;
+       }
        strbuf_release(&pattern);
        return len;
 }