]> git.ipfire.org Git - thirdparty/git.git/commitdiff
Merge branch 'ps/reftable-iteration-perf-part2'
authorJunio C Hamano <gitster@pobox.com>
Thu, 14 Mar 2024 21:05:23 +0000 (14:05 -0700)
committerJunio C Hamano <gitster@pobox.com>
Thu, 14 Mar 2024 21:05:23 +0000 (14:05 -0700)
The code to iterate over refs with the reftable backend has seen
some optimization.

* ps/reftable-iteration-perf-part2:
  refs/reftable: precompute prefix length
  reftable: allow inlining of a few functions
  reftable/record: decode keys in place
  reftable/record: reuse refname when copying
  reftable/record: reuse refname when decoding
  reftable/merged: avoid duplicate pqueue emptiness check
  reftable/merged: circumvent pqueue with single subiter
  reftable/merged: handle subiter cleanup on close only
  reftable/merged: remove unnecessary null check for subiters
  reftable/merged: make subiters own their records
  reftable/merged: advance subiter on subsequent iteration
  reftable/merged: make `merged_iter` structure private
  reftable/pq: use `size_t` to track iterator index

142 files changed:
Documentation/CodingGuidelines
Documentation/RelNotes/2.45.0.txt
Documentation/config.txt
Documentation/config/advice.txt
Documentation/config/clean.txt
Documentation/config/diff.txt
Documentation/config/interactive.txt
Documentation/config/mergetool.txt
Documentation/config/pack.txt
Documentation/config/sendemail.txt
Documentation/config/transfer.txt
Documentation/git-add.txt
Documentation/git-am.txt
Documentation/git-clean.txt
Documentation/git-clone.txt
Documentation/git-difftool.txt
Documentation/git-fast-export.txt
Documentation/git-for-each-ref.txt
Documentation/git-merge-tree.txt
Documentation/git-rebase.txt
Documentation/git-reflog.txt
Documentation/git-remote.txt
Documentation/git-rev-parse.txt
Documentation/git-send-email.txt
Documentation/git.txt
Documentation/gitcli.txt
Documentation/gitprotocol-v2.txt
Documentation/mergetools/vimdiff.txt
Documentation/rev-list-options.txt
GIT-VERSION-GEN
advice.c
advice.h
apply.c
bisect.c
builtin/add.c
builtin/branch.c
builtin/checkout.c
builtin/clean.c
builtin/clone.c
builtin/commit.c
builtin/fast-import.c
builtin/fetch.c
builtin/for-each-ref.c
builtin/fsck.c
builtin/log.c
builtin/merge-base.c
builtin/merge-tree.c
builtin/merge.c
builtin/name-rev.c
builtin/pull.c
builtin/read-tree.c
builtin/rebase.c
builtin/receive-pack.c
builtin/reflog.c
builtin/reset.c
builtin/rev-list.c
builtin/rev-parse.c
builtin/tag.c
builtin/upload-pack.c
cache-tree.c
commit-reach.c
commit-reach.h
commit.c
compat/compiler.h
compat/disk.h
contrib/completion/git-completion.bash
contrib/credential/libsecret/git-credential-libsecret.c
diff-lib.c
dir-iterator.c
dir-iterator.h
environment.c
environment.h
git-difftool--helper.sh
git.c
http-push.c
list-objects-filter.c
log-tree.c
mem-pool.c
mem-pool.h
merge-ort.c
merge-recursive.c
merge.c
mergetools/vimdiff
neue [deleted file]
notes-merge.c
object-name.c
object.c
object.h
oidset.c
oidset.h
ref-filter.c
ref-filter.h
refs.c
refs.h
refs/debug.c
refs/files-backend.c
refs/iterator.c
refs/packed-backend.c
refs/ref-cache.c
refs/refs-internal.h
refs/reftable-backend.c
remote.c
reset.c
revision.c
sequencer.c
serve.c
setup.c
shallow.c
submodule.c
t/helper/test-reach.c
t/helper/test-ref-store.c
t/lib-credential.sh
t/t0303-credential-external.sh
t/t0410-partial-clone.sh
t/t0600-reffiles-backend.sh
t/t0610-reftable-basics.sh
t/t1405-main-ref-store.sh
t/t1406-submodule-ref-store.sh
t/t1410-reflog.sh
t/t3202-show-branch.sh
t/t4042-diff-textconv-caching.sh
t/t4301-merge-tree-write-tree.sh
t/t5555-http-smart-common.sh
t/t5701-git-serve.sh
t/t5702-protocol-v2.sh
t/t5801/git-remote-testgit
t/t6022-rev-list-missing.sh
t/t6030-bisect-porcelain.sh
t/t6302-for-each-ref-filter.sh
t/t6437-submodule-merge.sh
t/t7300-clean.sh
t/t7402-submodule-rebase.sh
t/t7502-commit-porcelain.sh
t/t7800-difftool.sh
t/t9117-git-svn-init-clone.sh
t/t9146-git-svn-empty-dirs.sh
t/unit-tests/t-ctype.c
tree-walk.c
upload-pack.c
userdiff.c
wt-status.c
wt-status.h

index 578587a47155e929457e12862cd648c9fdf81acd..32e69f798ee7da4174c1f73910c899adf14ca63c 100644 (file)
@@ -446,12 +446,41 @@ For C programs:
    detail.
 
  - The first #include in C files, except in platform specific compat/
-   implementations and sha1dc/, must be either "git-compat-util.h" or
-   one of the approved headers that includes it first for you.  (The
-   approved headers currently include "builtin.h",
-   "t/helper/test-tool.h", "xdiff/xinclude.h", or
-   "reftable/system.h".)  You do not have to include more than one of
-   these.
+   implementations and sha1dc/, must be <git-compat-util.h>.  This
+   header file insulates other header files and source files from
+   platform differences, like which system header files must be
+   included in what order, and what C preprocessor feature macros must
+   be defined to trigger certain features we expect out of the system.
+   A collorary to this is that C files should not directly include
+   system header files themselves.
+
+   There are some exceptions, because certain group of files that
+   implement an API all have to include the same header file that
+   defines the API and it is convenient to include <git-compat-util.h>
+   there.  Namely:
+
+   - the implementation of the built-in commands in the "builtin/"
+     directory that include "builtin.h" for the cmd_foo() prototype
+     definition,
+
+   - the test helper programs in the "t/helper/" directory that include
+     "t/helper/test-tool.h" for the cmd__foo() prototype definition,
+
+   - the xdiff implementation in the "xdiff/" directory that includes
+     "xdiff/xinclude.h" for the xdiff machinery internals,
+
+   - the unit test programs in "t/unit-tests/" directory that include
+     "t/unit-tests/test-lib.h" that gives them the unit-tests
+     framework, and
+
+   - the source files that implement reftable in the "reftable/"
+     directory that include "reftable/system.h" for the reftable
+     internals,
+
+   are allowed to assume that they do not have to include
+   <git-compat-util.h> themselves, as it is included as the first
+   '#include' in these header files.  These headers must be the first
+   header file to be "#include"d in them, though.
 
  - A C file must directly include the header files that declare the
    functions and the types it uses, except for the functions and types
@@ -666,6 +695,11 @@ Writing Documentation:
    <new-branch-name>
    --template=<template-directory>
 
+ When a placeholder is cited in text paragraph, it is enclosed in angle
+ brackets to remind the reader the reference in the synopsis section.
+ For better visibility, the placeholder is typeset in italics:
+   The _<file>_ to be added.
+
  Possibility of multiple occurrences is indicated by three dots:
    <file>...
    (One or more of <file>.)
@@ -751,6 +785,8 @@ Writing Documentation:
    Incorrect:
       `\--pretty=oneline`
 
+A placeholder is not enclosed in backticks, as it is not a literal.
+
  If some place in the documentation needs to typeset a command usage
  example with inline substitutions, it is fine to use +monospaced and
  inline substituted text+ instead of `monospaced literal text`, and with
index 4f5a01c611c2f398c88a8507d2bbc42d45b819d4..2e759325393add7b2ac88fb69eaf501685e567bd 100644 (file)
@@ -12,6 +12,28 @@ UI, Workflows & Features
  * "git checkout -p" and friends learned that that "@" is a synonym
    for "HEAD".
 
+ * Variants of vimdiff learned to honor mergetool.<variant>.layout
+   settings.
+
+ * "git reflog" learned a "list" subcommand that enumerates known reflogs.
+
+ * When a merge conflicted at a submodule, merge-ort backend used to
+   unconditionally give a lengthy message to suggest how to resolve
+   it.  Now the message can be squelched as an advice message.
+
+ * "git for-each-ref" learned "--include-root-refs" option to show
+   even the stuff outside the 'refs/' hierarchy.
+
+ * "git rev-list --missing=print" has learned to optionally take
+   "--allow-missing-tips", which allows the objects at the starting
+   points to be missing.
+
+ * "git merge-tree" has learned that the three trees involved in the
+   3-way merge only need to be trees, not necessarily commits.
+
+ * "git log --merge" learned to pay attention to CHERRY_PICK_HEAD and
+   other kinds of *_HEAD pseudorefs.
+
 
 Performance, Internal Implementation, Development Support etc.
 
@@ -21,6 +43,17 @@ Performance, Internal Implementation, Development Support etc.
  * More tests that are marked as "ref-files only" have been updated to
    improve test coverage of reftable backend.
 
+ * Some parts of command line completion script (in contrib/) have
+   been micro-optimized.
+
+ * The way placeholders are to be marked-up in documentation have been
+   specified; use "_<placeholder>_" to typeset the word inside a pair
+   of <angle-brakets> emphasized.
+
+ * "git --no-lazy-fetch cmd" allows to run "cmd" while disabling lazy
+   fetching of objects from the promisor remote, which may be handy
+   for debugging.
+
 
 Fixes since v2.44
 -----------------
@@ -36,5 +69,80 @@ Fixes since v2.44
    it would lead to nonsense behaviour including division by zero.
    (merge 76fb807faa kh/column-reject-negative-padding later to maint).
 
+ * "git am --help" now tells readers what actions are available in
+   "git am --whitespace=<action>", in addition to saying that the
+   option is passed through to the underlying "git apply".
+   (merge a171dac734 jc/am-whitespace-doc later to maint).
+
+ * "git tag --column" failed to check the exit status of its "git
+   column" invocation, which has been corrected.
+   (merge 92e66478fc rj/tag-column-fix later to maint).
+
+ * Credential helper based on libsecret (in contrib/) has been updated
+   to handle an empty password correctly.
+   (merge 8f1f2023b7 mh/libsecret-empty-password-fix later to maint).
+
+ * "git difftool --dir-diff" learned to honor the "--trust-exit-code"
+   option; it used to always exit with 0 and signalled success.
+   (merge eb84c8b6ce ps/difftool-dir-diff-exit-code later to maint).
+
+ * The code incorrectly attempted to use textconv cache when asked,
+   even when we are not running in a repository, which has been
+   corrected.
+   (merge affe355fe7 jk/textconv-cache-outside-repo-fix later to maint).
+
+ * Remove an empty file that shouldn't have been added in the first
+   place.
+   (merge 4f66942215 js/remove-cruft-files later to maint).
+
+ * The logic to access reflog entries by date and number had ugly
+   corner cases at the boundaries, which have been cleaned up.
+   (merge 5edd126720 jk/reflog-special-cases-fix later to maint).
+
+ * An error message from "git upload-pack", which responds to "git
+   fetch" requests, had a trialing NUL in it, which has been
+   corrected.
+   (merge 3f4c7a0805 sg/upload-pack-error-message-fix later to maint).
+
+ * Clarify wording in the CodingGuidelines that requires <git-compat-util.h>
+   to be the first header file.
+   (merge 4e89f0e07c jc/doc-compat-util later to maint).
+
+ * "git commit -v --cleanup=scissors" used to add the scissors line
+   twice in the log message buffer, which has been corrected.
+   (merge e90cc075cc jt/commit-redundant-scissors-fix later to maint).
+
+ * A custom remote helper no longer cannot access the newly created
+   repository during "git clone", which is a regression in Git 2.44.
+   This has been corrected.
+   (merge 199f44cb2e ps/remote-helper-repo-initialization-fix later to maint).
+
+ * Various parts of upload-pack has been updated to bound the resource
+   consumption relative to the size of the repository to protect from
+   abusive clients.
+   (merge 6cd05e768b jk/upload-pack-bounded-resources later to maint).
+
+ * The upload-pack program, when talking over v2, accepted the
+   packfile-uris protocol extension from the client, even if it did
+   not advertise the capability, which has been corrected.
+   (merge a922bfa3b5 jk/upload-pack-v2-capability-cleanup later to maint).
+
+ * Make sure failure return from merge_bases_many() is properly caught.
+   (merge 25fd20eb44 js/merge-base-with-missing-commit 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).
+   (merge 64562d784d jb/doc-interactive-singlekey-do-not-need-perl later to maint).
+   (merge c431a235e2 cp/t9146-use-test-path-helpers later to maint).
+   (merge 82d75402d5 ds/doc-send-email-capitalization later to maint).
+   (merge 41bff66e35 jc/doc-add-placeholder-fix later to maint).
+   (merge 6835f0efe9 jw/remote-doc-typofix later to maint).
+   (merge 244001aa20 hs/rebase-not-in-progress later to maint).
+   (merge 2ca6c07db2 jc/no-include-of-compat-util-from-headers later to maint).
+   (merge 87bd7fbb9c rs/fetch-simplify-with-starts-with later to maint).
+   (merge f39addd0d9 rs/name-rev-with-mempool later to maint).
+   (merge 9a97b43e03 rs/submodule-prefix-simplify later to maint).
+   (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).
index e3a74dd1c19db44b3a0980cc778bf0ab4ba60b07..782c2bab906cf188e3cb21fa6d9fa8c4fe78663d 100644 (file)
@@ -369,20 +369,18 @@ inventing new variables for use in your own tool, make sure their
 names do not conflict with those that are used by Git itself and
 other popular tools, and describe them in your documentation.
 
-include::config/advice.txt[]
-
-include::config/attr.txt[]
-
-include::config/core.txt[]
-
 include::config/add.txt[]
 
+include::config/advice.txt[]
+
 include::config/alias.txt[]
 
 include::config/am.txt[]
 
 include::config/apply.txt[]
 
+include::config/attr.txt[]
+
 include::config/blame.txt[]
 
 include::config/branch.txt[]
@@ -405,10 +403,12 @@ include::config/commit.txt[]
 
 include::config/commitgraph.txt[]
 
-include::config/credential.txt[]
-
 include::config/completion.txt[]
 
+include::config/core.txt[]
+
+include::config/credential.txt[]
+
 include::config/diff.txt[]
 
 include::config/difftool.txt[]
@@ -421,10 +421,10 @@ include::config/feature.txt[]
 
 include::config/fetch.txt[]
 
-include::config/format.txt[]
-
 include::config/filter.txt[]
 
+include::config/format.txt[]
+
 include::config/fsck.txt[]
 
 include::config/fsmonitor--daemon.txt[]
@@ -435,10 +435,10 @@ include::config/gitcvs.txt[]
 
 include::config/gitweb.txt[]
 
-include::config/grep.txt[]
-
 include::config/gpg.txt[]
 
+include::config/grep.txt[]
+
 include::config/gui.txt[]
 
 include::config/guitool.txt[]
@@ -519,10 +519,10 @@ include::config/splitindex.txt[]
 
 include::config/ssh.txt[]
 
-include::config/status.txt[]
-
 include::config/stash.txt[]
 
+include::config/status.txt[]
+
 include::config/submodule.txt[]
 
 include::config/tag.txt[]
index c7ea70f2e2e9d281912567bfb0408200e40fe11b..dde8e7840ef38225995549411fbcf9305b160c37 100644 (file)
@@ -129,6 +129,9 @@ advice.*::
        submoduleAlternateErrorStrategyDie::
                Advice 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
                because `git submodule update --init` was not run.
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 bd5ae0c3378adbd5661a202dc1f8fcfbf27ac4a1..6c7e09a1ef5eb481b2abb26abd93edf38806cf76 100644 (file)
@@ -223,5 +223,5 @@ diff.colorMoved::
 
 diff.colorMovedWS::
        When moved lines are colored using e.g. the `diff.colorMoved` setting,
-       this option controls the `<mode>` how spaces are treated
-       for details of valid modes see '--color-moved-ws' in linkgit:git-diff[1].
+       this option controls the `<mode>` how spaces are treated.
+       For details of valid modes see '--color-moved-ws' in linkgit:git-diff[1].
index a2d3c7ec449e9b943a71f21891c2fd733a695dfc..5cc26555f19a4ff8f8a290a5316bfdd07b506e77 100644 (file)
@@ -4,9 +4,7 @@ interactive.singleKey::
        Currently this is used by the `--patch` mode of
        linkgit:git-add[1], linkgit:git-checkout[1],
        linkgit:git-restore[1], linkgit:git-commit[1],
-       linkgit:git-reset[1], and linkgit:git-stash[1]. Note that this
-       setting is silently ignored if portable keystroke input
-       is not available; requires the Perl module Term::ReadKey.
+       linkgit:git-reset[1], and linkgit:git-stash[1].
 
 interactive.diffFilter::
        When an interactive command (such as `git add --patch`) shows
index 294f61efd12fdf863e648b076462f4a9a1aa75b7..00bf665aa09bf353dd2c81220d2c83a31086e5b7 100644 (file)
@@ -45,14 +45,21 @@ mergetool.meld.useAutoMerge::
        value of `false` avoids using `--auto-merge` altogether, and is the
        default value.
 
-mergetool.vimdiff.layout::
-       The vimdiff backend uses this variable to control how its split
-       windows appear. Applies even if you are using Neovim (`nvim`) or
-       gVim (`gvim`) as the merge tool. See BACKEND SPECIFIC HINTS section
+mergetool.<vimdiff variant>.layout::
+       Configure the split window layout for vimdiff's `<variant>`, which is any of `vimdiff`,
+       `nvimdiff`, `gvimdiff`.
+       Upon launching `git mergetool` with `--tool=<variant>` (or without `--tool`
+       if `merge.tool` is configured as `<variant>`), Git will consult
+       `mergetool.<variant>.layout` to determine the tool's layout. If the
+       variant-specific configuration is not available, `vimdiff`'s is used as
+       fallback.  If that too is not available, a default layout with 4 windows
+       will be used.  To configure the layout, see the `BACKEND SPECIFIC HINTS`
+ifdef::git-mergetool[]
+       section.
+endif::[]
 ifndef::git-mergetool[]
-       in linkgit:git-mergetool[1].
+       section in linkgit:git-mergetool[1].
 endif::[]
-       for details.
 
 mergetool.hideResolved::
        During a merge, Git will automatically resolve as many conflicts as
index 9c630863e6ff18372172111619d4ad2a54f3264f..da527377fafcb629108676ed0becc489ed25095a 100644 (file)
@@ -34,11 +34,10 @@ pack.allowPackReuse::
        reachability bitmap is available, pack-objects will try to send
        parts of all packs in the MIDX.
 +
-       If only a single pack bitmap is available, and
-       `pack.allowPackReuse` is set to "multi", reuse parts of just the
-       bitmapped packfile. This can reduce memory and CPU usage to
-       serve fetches, but might result in sending a slightly larger
-       pack. Defaults to true.
+If only a single pack bitmap is available, and `pack.allowPackReuse`
+is set to "multi", reuse parts of just the bitmapped packfile. This
+can reduce memory and CPU usage to serve fetches, but might result in
+sending a slightly larger pack. Defaults to true.
 
 pack.island::
        An extended regular expression configuring a set of delta
index 7fc770ee9e69d77b3743c8bff1b8bef35bb96f36..6a869d67eb90c9992a1cbcf3efb372d3a6fb2f40 100644 (file)
@@ -8,7 +8,7 @@ sendemail.smtpEncryption::
        See linkgit:git-send-email[1] for description.  Note that this
        setting is not subject to the 'identity' mechanism.
 
-sendemail.smtpsslcertpath::
+sendemail.smtpSSLCertPath::
        Path to ca-certificates (either a directory or a single file).
        Set it to an empty string to disable certificate verification.
 
@@ -62,12 +62,12 @@ sendemail.chainReplyTo::
 sendemail.envelopeSender::
 sendemail.from::
 sendemail.headerCmd::
-sendemail.signedoffbycc::
+sendemail.signedOffByCc::
 sendemail.smtpPass::
-sendemail.suppresscc::
+sendemail.suppressCc::
 sendemail.suppressFrom::
 sendemail.to::
-sendemail.tocmd::
+sendemail.toCmd::
 sendemail.smtpDomain::
 sendemail.smtpServer::
 sendemail.smtpServerPort::
@@ -81,8 +81,8 @@ sendemail.xmailer::
        linkgit:git-send-email[1] command-line options. See its
        documentation for details.
 
-sendemail.signedoffcc (deprecated)::
-       Deprecated alias for `sendemail.signedoffbycc`.
+sendemail.signedOffCc (deprecated)::
+       Deprecated alias for `sendemail.signedOffByCc`.
 
 sendemail.smtpBatchSize::
        Number of messages to be sent per connection, after that a relogin
index a9cbdb88a1fe11f364da005bdfe490cd2e28dc0b..f1ce50f4a6e6baf4ffde131339e8527e34e2e90b 100644 (file)
@@ -121,3 +121,7 @@ transfer.bundleURI::
        information from the remote server (if advertised) and download
        bundles before continuing the clone through the Git protocol.
        Defaults to `false`.
+
+transfer.advertiseObjectInfo::
+       When `true`, the `object-info` capability is advertised by
+       servers. Defaults to false.
index 3d2e6707168b69d591e3bf2a50abe3a62fc2ee23..14a371fff3569eac4fd633ebc945b1d8793b1237 100644 (file)
@@ -63,7 +63,7 @@ OPTIONS
        to ignore removed files; use `--no-all` option if you want
        to add modified or new files but ignore removed ones.
 +
-For more details about the <pathspec> syntax, see the 'pathspec' entry
+For more details about the _<pathspec>_ syntax, see the 'pathspec' entry
 in linkgit:gitglossary[7].
 
 -n::
@@ -119,10 +119,10 @@ apply to the index. See EDITING PATCHES below.
 -u::
 --update::
        Update the index just where it already has an entry matching
-       <pathspec>.  This removes as well as modifies index entries to
+       _<pathspec>_.  This removes as well as modifies index entries to
        match the working tree, but adds no new files.
 +
-If no <pathspec> is given when `-u` option is used, all
+If no _<pathspec>_ is given when `-u` option is used, all
 tracked files in the entire working tree are updated (old versions
 of Git used to limit the update to the current directory and its
 subdirectories).
@@ -131,11 +131,11 @@ subdirectories).
 --all::
 --no-ignore-removal::
        Update the index not only where the working tree has a file
-       matching <pathspec> but also where the index already has an
+       matching _<pathspec>_ but also where the index already has an
        entry. This adds, modifies, and removes index entries to
        match the working tree.
 +
-If no <pathspec> is given when `-A` option is used, all
+If no _<pathspec>_ is given when `-A` option is used, all
 files in the entire working tree are updated (old versions
 of Git used to limit the update to the current directory and its
 subdirectories).
@@ -145,11 +145,11 @@ subdirectories).
        Update the index by adding new files that are unknown to the
        index and files modified in the working tree, but ignore
        files that have been removed from the working tree.  This
-       option is a no-op when no <pathspec> is used.
+       option is a no-op when no _<pathspec>_ is used.
 +
 This option is primarily to help users who are used to older
-versions of Git, whose "git add <pathspec>..." was a synonym
-for "git add --no-all <pathspec>...", i.e. ignored removed files.
+versions of Git, whose "git add _<pathspec>_..." was a synonym
+for "git add --no-all _<pathspec>_...", i.e. ignored removed files.
 
 -N::
 --intent-to-add::
@@ -198,8 +198,8 @@ for "git add --no-all <pathspec>...", i.e. ignored removed files.
        unchanged.
 
 --pathspec-from-file=<file>::
-       Pathspec is passed in `<file>` instead of commandline args. If
-       `<file>` is exactly `-` then standard input is used. Pathspec
+       Pathspec is passed in _<file>_ instead of commandline args. If
+       _<file>_ is exactly `-` then standard input is used. Pathspec
        elements are separated by LF or CR/LF. Pathspec elements can be
        quoted as explained for the configuration variable `core.quotePath`
        (see linkgit:git-config[1]). See also `--pathspec-file-nul` and
index e080458d6c45891ec1db74b9df155aeab111e79f..463a3c660024e08b494caa3e22b4d4c417f46e0c 100644 (file)
@@ -128,6 +128,9 @@ include::rerere-options.txt[]
        These flags are passed to the 'git apply' (see linkgit:git-apply[1])
        program that applies
        the patch.
++
+Valid <action> for the `--whitespace` option are:
+`nowarn`, `warn`, `fix`, `error`, and `error-all`.
 
 --patch-format::
        By default the command will try to detect the patch format
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 6e43eb9c205371548655c5abae5e59bb963c959a..0c07720c6f4e4a53a20a9db8c210143e881a2347 100644 (file)
@@ -311,7 +311,7 @@ or `--mirror` is given)
        The result is Git repository can be separated from working
        tree.
 
---ref-format=<ref-format::
+--ref-format=<ref-format>::
 
 Specify the given ref storage format for the repository. The valid values are:
 +
index c05f97aca96be62a6792681f8ccf62995d411ac2..a616f8b2e6f349d4fa8f7454c5ba48a219719842 100644 (file)
@@ -105,7 +105,6 @@ instead.  `--no-symlinks` is the default on Windows.
        `merge.tool` until a tool is found.
 
 --[no-]trust-exit-code::
-       'git-difftool' invokes a diff tool individually on each file.
        Errors reported by the diff tool are ignored by default.
        Use `--trust-exit-code` to make 'git-difftool' exit when an
        invoked diff tool returns a non-zero exit code.
index 4643ddbe68fd0c0eeb541df356ca3fc2bd2ee9d4..752e4b9b01d7d8a5527d1fe9a615ad8493e3f4bf 100644 (file)
@@ -48,7 +48,7 @@ When asking to 'abort' (which is the default), this program will die
 when encountering such a tag.  With 'drop' it will omit such tags from
 the output.  With 'rewrite', if the tagged object is a commit, it will
 rewrite the tag to tag an ancestor commit (via parent rewriting; see
-linkgit:git-rev-list[1])
+linkgit:git-rev-list[1]).
 
 -M::
 -C::
index 3a9ad91b7af89a66cdd460f5ce73eaf533dad719..c1dd12b93cfd5c44a8d3d97c78c5f2f330f7fe3f 100644 (file)
@@ -10,7 +10,7 @@ SYNOPSIS
 [verse]
 'git for-each-ref' [--count=<count>] [--shell|--perl|--python|--tcl]
                   [(--sort=<key>)...] [--format=<format>]
-                  [ --stdin | <pattern>... ]
+                  [--include-root-refs] [ --stdin | <pattern>... ]
                   [--points-at=<object>]
                   [--merged[=<object>]] [--no-merged[=<object>]]
                   [--contains[=<object>]] [--no-contains[=<object>]]
@@ -105,6 +105,9 @@ TAB %(refname)`.
        any excluded pattern(s) are shown. Matching is done using the
        same rules as `<pattern>` above.
 
+--include-root-refs::
+       List root refs (HEAD and pseudorefs) apart from regular refs.
+
 FIELD NAMES
 -----------
 
index b50acace3bc367ad7b73d2da10252e6b5f2e0d07..dd388fa21d5a51d1b6c3aca1b1d18b98f69a4f88 100644 (file)
@@ -64,10 +64,13 @@ OPTIONS
        share no common history.  This flag can be given to override that
        check and make the merge proceed anyway.
 
---merge-base=<commit>::
+--merge-base=<tree-ish>::
        Instead of finding the merge-bases for <branch1> and <branch2>,
        specify a merge-base for the merge, and specifying multiple bases is
        currently not supported. This option is incompatible with `--stdin`.
++
+As the merge-base is provided directly, <branch1> and <branch2> do not need
+to specify commits; trees are enough.
 
 [[OUTPUT]]
 OUTPUT
index 06206521fc322b04d6c874193d215e60d2368836..e7e725044db418845cd8ee53aed60fba2374634f 100644 (file)
@@ -607,7 +607,7 @@ The recommended way to create commits with squash markers is by using the
 linkgit:git-commit[1], which take the target commit as an argument and
 automatically fill in the subject line of the new commit from that.
 +
-Settting configuration variable `rebase.autoSquash` to true enables
+Setting configuration variable `rebase.autoSquash` to true enables
 auto-squashing by default for interactive rebase.  The `--no-autosquash`
 option can be used to override that setting.
 +
index ec64cbff4c6529d23fcf885ed5fa6f018404952f..a929c52982ff7629fdd32df1d008a0fb824847d0 100644 (file)
@@ -10,6 +10,7 @@ SYNOPSIS
 --------
 [verse]
 'git reflog' [show] [<log-options>] [<ref>]
+'git reflog list'
 'git reflog expire' [--expire=<time>] [--expire-unreachable=<time>]
        [--rewrite] [--updateref] [--stale-fix]
        [--dry-run | -n] [--verbose] [--all [--single-worktree] | <refs>...]
@@ -39,6 +40,8 @@ actions, and in addition the `HEAD` reflog records branch switching.
 `git reflog show` is an alias for `git log -g --abbrev-commit
 --pretty=oneline`; see linkgit:git-log[1] for more information.
 
+The "list" subcommand lists all refs which have a corresponding reflog.
+
 The "expire" subcommand prunes older reflog entries. Entries older
 than `expire` time, or entries older than `expire-unreachable` time
 and not reachable from the current tip, are removed from the reflog.
index 1dec3148348350bc6f26a53ca2add524d3f8b9bf..932a5c3ea4741c2abbfab9b7070a51fcf0b58a37 100644 (file)
@@ -35,7 +35,7 @@ OPTIONS
 -v::
 --verbose::
        Be a little more verbose and show remote url after name.
-       For promisor remotes, also show which filter (`blob:none` etc.)
+       For promisor remotes, also show which filters (`blob:none` etc.)
        are configured.
        NOTE: This must be placed between `remote` and subcommand.
 
index 546faf9017723581ea8e13bea6f5f90b00372403..5d83dd36da11f3a7e5294bc5825e131c81d17633 100644 (file)
@@ -9,7 +9,7 @@ git-rev-parse - Pick out and massage parameters
 SYNOPSIS
 --------
 [verse]
-'git rev-parse' [<options>] <args>...
+'git rev-parse' [<options>] <arg>...
 
 DESCRIPTION
 -----------
@@ -130,7 +130,7 @@ for another option.
        'git diff-{asterisk}'). In contrast to the `--sq-quote` option,
        the command input is still interpreted as usual.
 
---short[=length]::
+--short[=<length>]::
        Same as `--verify` but shortens the object name to a unique
        prefix with at least `length` characters. The minimum length
        is 4, the default is the effective value of the `core.abbrev`
@@ -165,9 +165,9 @@ Options for Objects
 --all::
        Show all refs found in `refs/`.
 
---branches[=pattern]::
---tags[=pattern]::
---remotes[=pattern]::
+--branches[=<pattern>]::
+--tags[=<pattern>]::
+--remotes[=<pattern>]::
        Show all branches, tags, or remote-tracking branches,
        respectively (i.e., refs found in `refs/heads`,
        `refs/tags`, or `refs/remotes`, respectively).
@@ -176,7 +176,7 @@ If a `pattern` is given, only refs matching the given shell glob are
 shown.  If the pattern does not contain a globbing character (`?`,
 `*`, or `[`), it is turned into a prefix match by appending `/*`.
 
---glob=pattern::
+--glob=<pattern>::
        Show all refs matching the shell glob pattern `pattern`. If
        the pattern does not start with `refs/`, this is automatically
        prepended.  If the pattern does not contain a globbing
@@ -197,7 +197,7 @@ respectively, and they must begin with `refs/` when applied to `--glob`
 or `--all`. If a trailing '/{asterisk}' is intended, it must be given
 explicitly.
 
---exclude-hidden=[fetch|receive|uploadpack]::
+--exclude-hidden=(fetch|receive|uploadpack)::
        Do not include refs that would be hidden by `git-fetch`,
        `git-receive-pack` or `git-upload-pack` by consulting the appropriate
        `fetch.hideRefs`, `receive.hideRefs` or `uploadpack.hideRefs`
@@ -314,17 +314,17 @@ The following options are unaffected by `--path-format`:
 Other Options
 ~~~~~~~~~~~~~
 
---since=datestring::
---after=datestring::
+--since=<datestring>::
+--after=<datestring>::
        Parse the date string, and output the corresponding
        --max-age= parameter for 'git rev-list'.
 
---until=datestring::
---before=datestring::
+--until=<datestring>::
+--before=<datestring>::
        Parse the date string, and output the corresponding
        --min-age= parameter for 'git rev-list'.
 
-<args>...::
+<arg>...::
        Flags and parameters to be parsed.
 
 
index d1ef6a204e6833e2e8a1c9d0e909568a7a017300..8264f8738093cf64b7e32201826fb170dd512c73 100644 (file)
@@ -138,7 +138,7 @@ Note that no attempts whatsoever are made to validate the encoding.
 
 --compose-encoding=<encoding>::
        Specify encoding of compose message. Default is the value of the
-       'sendemail.composeencoding'; if that is unspecified, UTF-8 is assumed.
+       'sendemail.composeEncoding'; if that is unspecified, UTF-8 is assumed.
 
 --transfer-encoding=(7bit|8bit|quoted-printable|base64|auto)::
        Specify the transfer encoding to be used to send the message over SMTP.
@@ -174,7 +174,7 @@ Sending
        Specify a command to run to send the email. The command should
        be sendmail-like; specifically, it must support the `-i` option.
        The command will be executed in the shell if necessary.  Default
-       is the value of `sendemail.sendmailcmd`.  If unspecified, and if
+       is the value of `sendemail.sendmailCmd`.  If unspecified, and if
        --smtp-server is also unspecified, git-send-email will search
        for `sendmail` in `/usr/sbin`, `/usr/lib` and $PATH.
 
@@ -269,7 +269,7 @@ must be used for each option.
        certificates concatenated together: see verify(1) -CAfile and
        -CApath for more information on these). Set it to an empty string
        to disable certificate verification. Defaults to the value of the
-       `sendemail.smtpsslcertpath` configuration variable, if set, or the
+       `sendemail.smtpSSLCertPath` configuration variable, if set, or the
        backing SSL library's compiled-in default otherwise (which should
        be the best choice on most platforms).
 
@@ -313,7 +313,7 @@ Automating
        Specify a command to execute once per patch file which
        should generate patch file specific "To:" entries.
        Output of this command must be single email address per line.
-       Default is the value of 'sendemail.tocmd' configuration value.
+       Default is the value of 'sendemail.toCmd' configuration value.
 
 --cc-cmd=<command>::
        Specify a command to execute once per patch file which
@@ -348,19 +348,19 @@ Automating
 
 --[no-]signed-off-by-cc::
        If this is set, add emails found in the `Signed-off-by` trailer or Cc: lines to the
-       cc list. Default is the value of `sendemail.signedoffbycc` configuration
+       cc list. Default is the value of `sendemail.signedOffByCc` configuration
        value; if that is unspecified, default to --signed-off-by-cc.
 
 --[no-]cc-cover::
        If this is set, emails found in Cc: headers in the first patch of
        the series (typically the cover letter) are added to the cc list
-       for each email set. Default is the value of 'sendemail.cccover'
+       for each email set. Default is the value of 'sendemail.ccCover'
        configuration value; if that is unspecified, default to --no-cc-cover.
 
 --[no-]to-cover::
        If this is set, emails found in To: headers in the first patch of
        the series (typically the cover letter) are added to the to list
-       for each email set. Default is the value of 'sendemail.tocover'
+       for each email set. Default is the value of 'sendemail.toCover'
        configuration value; if that is unspecified, default to --no-to-cover.
 
 --suppress-cc=<category>::
@@ -384,7 +384,7 @@ Automating
 - 'all' will suppress all auto cc values.
 --
 +
-Default is the value of `sendemail.suppresscc` configuration value; if
+Default is the value of `sendemail.suppressCc` configuration value; if
 that is unspecified, default to 'self' if --suppress-from is
 specified, as well as 'body' if --no-signed-off-cc is specified.
 
@@ -471,7 +471,7 @@ Information
        Instead of the normal operation, dump the shorthand alias names from
        the configured alias file(s), one per line in alphabetical order. Note
        that this only includes the alias name and not its expanded email addresses.
-       See 'sendemail.aliasesfile' for more information about aliases.
+       See 'sendemail.aliasesFile' for more information about aliases.
 
 
 CONFIGURATION
index 0d25224c9696942b797d64895d98bbcdd80db62a..e6b766d5c3ab54e6fc97d3cf31f9093a6eac5a41 100644 (file)
@@ -174,8 +174,17 @@ If you just want to run git as if it was started in `<path>` then use
        directory.
 
 --no-replace-objects::
-       Do not use replacement refs to replace Git objects. See
-       linkgit:git-replace[1] for more information.
+       Do not use replacement refs to replace Git objects.
+       This is equivalent to exporting the `GIT_NO_REPLACE_OBJECTS`
+       environment variable with any value.
+       See linkgit:git-replace[1] for more information.
+
+--no-lazy-fetch::
+       Do not fetch missing objects from the promisor remote on
+       demand.  Useful together with `git cat-file -e <object>` to
+       see if the object is locally available.
+       This is equivalent to setting the `GIT_NO_LAZY_FETCH`
+       environment variable to `1`.
 
 --literal-pathspecs::
        Treat pathspecs literally (i.e. no globbing, no pathspec magic).
@@ -872,6 +881,10 @@ for full details.
        header and packfile URIs. Set this Boolean environment variable to false to prevent this
        redaction.
 
+`GIT_NO_REPLACE_OBJECTS`::
+       Setting and exporting this environment variable tells Git to
+       ignore replacement refs and do not replace Git objects.
+
 `GIT_LITERAL_PATHSPECS`::
        Setting this Boolean environment variable to true will cause Git to treat all
        pathspecs literally, rather than as glob patterns. For example,
@@ -893,6 +906,11 @@ for full details.
        Setting this Boolean environment variable to true will cause Git to treat all
        pathspecs as case-insensitive.
 
+`GIT_NO_LAZY_FETCH`::
+       Setting this Boolean environment variable to true tells Git
+       not to lazily fetch missing objects from the promisor remote
+       on demand.
+
 `GIT_REFLOG_ACTION`::
        When a ref is updated, reflog entries are created to keep
        track of the reason why the ref was updated (which is
index e5fac943227a23a5bf9214cddb08d48b0ce3c5ca..7c709324ba904efe5f6a544086c767fbc1ab7cb4 100644 (file)
@@ -81,9 +81,6 @@ you will.
 Here are the rules regarding the "flags" that you should follow when you are
 scripting Git:
 
- * It's preferred to use the non-dashed form of Git commands, which means that
-   you should prefer `git foo` to `git-foo`.
-
  * Splitting short options to separate words (prefer `git foo -a -b`
    to `git foo -ab`, the latter may not even work).
 
index 0b800abd567a49b24be7c9feade0596ff758df92..414bc625d5dd219faefcf7fe06aa93d6443763f4 100644 (file)
@@ -346,7 +346,8 @@ the 'wanted-refs' section in the server's response as explained below.
     want-ref <ref>
        Indicates to the server that the client wants to retrieve a
        particular ref, where <ref> is the full name of a ref on the
-       server.
+       server.  It is a protocol error to send want-ref for the
+       same ref more than once.
 
 If the 'sideband-all' feature is advertised, the following argument can be
 included in the client's request:
@@ -361,7 +362,8 @@ included in the client's request:
 If the 'packfile-uris' feature is advertised, the following argument
 can be included in the client's request as well as the potential
 addition of the 'packfile-uris' section in the server's response as
-explained below.
+explained below. Note that at most one `packfile-uris` line can be sent
+to the server.
 
     packfile-uris <comma-separated-list-of-protocols>
        Indicates to the server that the client is willing to receive
index d1a4c468e6354e47f52579d6e44f2387b51ba811..befa86d69278b480610c6d0472c005ad6093cf83 100644 (file)
@@ -177,7 +177,8 @@ Instead of `--tool=vimdiff`, you can also use one of these other variants:
 
 When using these variants, in order to specify a custom layout you will have to
 set configuration variables `mergetool.gvimdiff.layout` and
-`mergetool.nvimdiff.layout` instead of `mergetool.vimdiff.layout`
+`mergetool.nvimdiff.layout` instead of `mergetool.vimdiff.layout` (though the
+latter will be used as fallback if the variant-specific one is not set).
 
 In addition, for backwards compatibility with previous Git versions, you can
 also append `1`, `2` or `3` to either `vimdiff` or any of the variants (ex:
index a583b52c612aece1f7d1fd24086c0f7961eecceb..408d9314d0887969a8cb485b59265a3c4b570ced 100644 (file)
@@ -341,8 +341,11 @@ See also linkgit:git-reflog[1].
 Under `--pretty=reference`, this information will not be shown at all.
 
 --merge::
-       After a failed merge, show refs that touch files having a
-       conflict and don't exist on all heads to merge.
+       Show commits touching conflicted paths in the range `HEAD...<other>`,
+       where `<other>` is the first existing pseudoref in `MERGE_HEAD`,
+       `CHERRY_PICK_HEAD`, `REVERT_HEAD` or `REBASE_HEAD`. Only works
+       when the index has unmerged entries. This option can be used to show
+       relevant commits when resolving conflicts from a 3-way merge.
 
 --boundary::
        Output excluded boundary commits. Boundary commits are
@@ -1019,6 +1022,10 @@ Unexpected missing objects will raise an error.
 +
 The form '--missing=print' is like 'allow-any', but will also print a
 list of the missing objects.  Object IDs are prefixed with a ``?'' character.
++
+If some tips passed to the traversal are missing, they will be
+considered as missing too, and the traversal will ignore them. In case
+we cannot get their Object ID though, an error will be raised.
 
 --exclude-promisor-objects::
        (For internal use only.)  Prefilter object traversal at
index c9d1d29082c2e03617fd518d269cb15d867bbf08..df788c764b7bb4cd61a1b2a6da277bfce82cdfca 100755 (executable)
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 GVF=GIT-VERSION-FILE
-DEF_VER=v2.44.0
+DEF_VER=v2.44.GIT
 
 LF='
 '
index 6e9098ff08935a2670b468d1fe1a36f3c268c055..4ba64ee5b2d315eeeaa6143f5c8c2053d066a830 100644 (file)
--- a/advice.c
+++ b/advice.c
@@ -79,6 +79,7 @@ static struct {
        [ADVICE_STATUS_U_OPTION]                        = { "statusUoption" },
        [ADVICE_SUBMODULES_NOT_UPDATED]                 = { "submodulesNotUpdated" },
        [ADVICE_SUBMODULE_ALTERNATE_ERROR_STRATEGY_DIE] = { "submoduleAlternateErrorStrategyDie" },
+       [ADVICE_SUBMODULE_MERGE_CONFLICT]               = { "submoduleMergeConflict" },
        [ADVICE_SUGGEST_DETACHING_HEAD]                 = { "suggestDetachingHead" },
        [ADVICE_UPDATE_SPARSE_PATH]                     = { "updateSparsePath" },
        [ADVICE_WAITING_FOR_EDITOR]                     = { "waitingForEditor" },
index 9d4f49ae38bcfe3b0bdf9999cf9d66ee6a95739e..7d0a821f5cb8a1818154852bbb76d7e15d4fe97b 100644 (file)
--- a/advice.h
+++ b/advice.h
@@ -47,6 +47,7 @@ enum advice_type {
        ADVICE_STATUS_U_OPTION,
        ADVICE_SUBMODULES_NOT_UPDATED,
        ADVICE_SUBMODULE_ALTERNATE_ERROR_STRATEGY_DIE,
+       ADVICE_SUBMODULE_MERGE_CONFLICT,
        ADVICE_SUGGEST_DETACHING_HEAD,
        ADVICE_UPDATE_SPARSE_PATH,
        ADVICE_WAITING_FOR_EDITOR,
diff --git a/apply.c b/apply.c
index 355e2b0f2f0cb848a41a739d797ccec4d044d6b1..432837a674c3cc559f762aa4b7766bd8f43177e0 100644 (file)
--- a/apply.c
+++ b/apply.c
@@ -77,7 +77,8 @@ static int parse_whitespace_option(struct apply_state *state, const char *option
                return 0;
        }
        /*
-        * Please update $__git_whitespacelist in git-completion.bash
+        * Please update $__git_whitespacelist in git-completion.bash,
+        * Documentation/git-apply.txt, and Documentation/git-am.txt
         * when you add new options.
         */
        return error(_("unrecognized whitespace option '%s'"), option);
index f75e50c339764db8a6b9f2266281541203d4e76f..60aae2fe50d4edaa520c9cf9f7e4a23ed6c60085 100644 (file)
--- a/bisect.c
+++ b/bisect.c
@@ -836,10 +836,11 @@ static void handle_skipped_merge_base(const struct object_id *mb)
 static enum bisect_error check_merge_bases(int rev_nr, struct commit **rev, int no_checkout)
 {
        enum bisect_error res = BISECT_OK;
-       struct commit_list *result;
+       struct commit_list *result = NULL;
 
-       result = repo_get_merge_bases_many(the_repository, rev[0], rev_nr - 1,
-                                          rev + 1);
+       if (repo_get_merge_bases_many(the_repository, rev[0], rev_nr - 1,
+                                     rev + 1, &result) < 0)
+               exit(128);
 
        for (; result; result = result->next) {
                const struct object_id *mb = &result->item->object.oid;
index ada7719561f0ec324238e613a9788a5f5a2981eb..393c10cbcf6315efb525b38db26e218bf6b1959d 100644 (file)
@@ -115,7 +115,7 @@ static int refresh(int verbose, const struct pathspec *pathspec)
        int i, ret = 0;
        char *skip_worktree_seen = NULL;
        struct string_list only_match_skip_worktree = STRING_LIST_INIT_NODUP;
-       int flags = REFRESH_IGNORE_SKIP_WORKTREE |
+       unsigned int flags = REFRESH_IGNORE_SKIP_WORKTREE |
                    (verbose ? REFRESH_IN_PORCELAIN : REFRESH_QUIET);
 
        seen = xcalloc(pathspec->nr, 1);
index cfb63cce5fb9dff64106907947d0df25a2c25489..b3cbb7fd440486300b14e76400ac58f927532995 100644 (file)
@@ -158,6 +158,8 @@ static int branch_merged(int kind, const char *name,
 
        merged = reference_rev ? repo_in_merge_bases(the_repository, rev,
                                                     reference_rev) : 0;
+       if (merged < 0)
+               exit(128);
 
        /*
         * After the safety valve is fully redefined to "check with
@@ -166,9 +168,13 @@ static int branch_merged(int kind, const char *name,
         * any of the following code, but during the transition period,
         * a gentle reminder is in order.
         */
-       if ((head_rev != reference_rev) &&
-           (head_rev ? repo_in_merge_bases(the_repository, rev, head_rev) : 0) != merged) {
-               if (merged)
+       if (head_rev != reference_rev) {
+               int expect = head_rev ? repo_in_merge_bases(the_repository, rev, head_rev) : 0;
+               if (expect < 0)
+                       exit(128);
+               if (expect == merged)
+                       ; /* okay */
+               else if (merged)
                        warning(_("deleting branch '%s' that has been merged to\n"
                                "         '%s', but not yet merged to HEAD"),
                                name, reference_name);
index 067c2519334db3198f4627e58469fd6ad48da16a..15293a30134094f699d15035232bbc644277910c 100644 (file)
@@ -704,7 +704,8 @@ static int reset_tree(struct tree *tree, const struct checkout_opts *o,
        init_checkout_metadata(&opts.meta, info->refname,
                               info->commit ? &info->commit->object.oid : null_oid(),
                               NULL);
-       parse_tree(tree);
+       if (parse_tree(tree) < 0)
+               return 128;
        init_tree_desc(&tree_desc, tree->buffer, tree->size);
        switch (unpack_trees(1, &tree_desc, &opts)) {
        case -2:
@@ -783,9 +784,15 @@ static int merge_working_tree(const struct checkout_opts *opts,
                if (new_branch_info->commit)
                        BUG("'switch --orphan' should never accept a commit as starting point");
                new_tree = parse_tree_indirect(the_hash_algo->empty_tree);
-       } else
+               if (!new_tree)
+                       BUG("unable to read empty tree");
+       } else {
                new_tree = repo_get_commit_tree(the_repository,
                                                new_branch_info->commit);
+               if (!new_tree)
+                       return error(_("unable to read tree (%s)"),
+                                    oid_to_hex(&new_branch_info->commit->object.oid));
+       }
        if (opts->discard_changes) {
                ret = reset_tree(new_tree, opts, 1, writeout_error, new_branch_info);
                if (ret)
@@ -820,7 +827,8 @@ static int merge_working_tree(const struct checkout_opts *opts,
                                oid_to_hex(old_commit_oid));
 
                init_tree_desc(&trees[0], tree->buffer, tree->size);
-               parse_tree(new_tree);
+               if (parse_tree(new_tree) < 0)
+                       exit(128);
                tree = new_tree;
                init_tree_desc(&trees[1], tree->buffer, tree->size);
 
@@ -1240,10 +1248,15 @@ static void setup_new_branch_info_and_source_tree(
        if (!new_branch_info->commit) {
                /* not a commit */
                *source_tree = parse_tree_indirect(rev);
+               if (!*source_tree)
+                       die(_("unable to read tree (%s)"), oid_to_hex(rev));
        } else {
                parse_commit_or_die(new_branch_info->commit);
                *source_tree = repo_get_commit_tree(the_repository,
                                                    new_branch_info->commit);
+               if (!*source_tree)
+                       die(_("unable to read tree (%s)"),
+                           oid_to_hex(&new_branch_info->commit->object.oid));
        }
 }
 
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 bad1b70ce8255156cf4745aca5e120371d39642d..93892ab5682ce51e1a131d845386e555a99b893c 100644 (file)
@@ -738,7 +738,8 @@ static int checkout(int submodule_progress, int filter_submodules)
        tree = parse_tree_indirect(&oid);
        if (!tree)
                die(_("unable to parse commit %s"), oid_to_hex(&oid));
-       parse_tree(tree);
+       if (parse_tree(tree) < 0)
+               exit(128);
        init_tree_desc(&t, tree->buffer, tree->size);
        if (unpack_trees(1, &t, &opts) < 0)
                die(_("unable to checkout working tree"));
@@ -926,6 +927,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
        struct ref *mapped_refs = NULL;
        const struct ref *ref;
        struct strbuf key = STRBUF_INIT;
+       struct strbuf buf = STRBUF_INIT;
        struct strbuf branch_top = STRBUF_INIT, reflog_msg = STRBUF_INIT;
        struct transport *transport = NULL;
        const char *src_ref_prefix = "refs/heads/";
@@ -1125,6 +1127,50 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
                git_dir = real_git_dir;
        }
 
+       /*
+        * We have a chicken-and-egg situation between initializing the refdb
+        * and spawning transport helpers:
+        *
+        *   - Initializing the refdb requires us to know about the object
+        *     format. We thus have to spawn the transport helper to learn
+        *     about it.
+        *
+        *   - The transport helper may want to access the Git repository. But
+        *     because the refdb has not been initialized, we don't have "HEAD"
+        *     or "refs/". Thus, the helper cannot find the Git repository.
+        *
+        * Ideally, we would have structured the helper protocol such that it's
+        * mandatory for the helper to first announce its capabilities without
+        * yet assuming a fully initialized repository. Like that, we could
+        * have added a "lazy-refdb-init" capability that announces whether the
+        * helper is ready to handle not-yet-initialized refdbs. If any helper
+        * didn't support them, we would have fully initialized the refdb with
+        * the SHA1 object format, but later on bailed out if we found out that
+        * the remote repository used a different object format.
+        *
+        * But we didn't, and thus we use the following workaround to partially
+        * initialize the repository's refdb such that it can be discovered by
+        * Git commands. To do so, we:
+        *
+        *   - Create an invalid HEAD ref pointing at "refs/heads/.invalid".
+        *
+        *   - Create the "refs/" directory.
+        *
+        *   - Set up the ref storage format and repository version as
+        *     required.
+        *
+        * This is sufficient for Git commands to discover the Git directory.
+        */
+       initialize_repository_version(GIT_HASH_UNKNOWN,
+                                     the_repository->ref_storage_format, 1);
+
+       strbuf_addf(&buf, "%s/HEAD", git_dir);
+       write_file(buf.buf, "ref: refs/heads/.invalid");
+
+       strbuf_reset(&buf);
+       strbuf_addf(&buf, "%s/refs", git_dir);
+       safe_create_dir(buf.buf, 1);
+
        /*
         * additional config can be injected with -c, make sure it's included
         * after init_db, which clears the entire config environment.
@@ -1453,6 +1499,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
        free(remote_name);
        strbuf_release(&reflog_msg);
        strbuf_release(&branch_top);
+       strbuf_release(&buf);
        strbuf_release(&key);
        free_refs(mapped_refs);
        free_refs(remote_head_points_at);
index 6d1fa71676f735b7ef1c3ab7bd56b767348ed992..a91197245f18ed7b73eee64d3d5d930ca164bb06 100644 (file)
@@ -331,7 +331,8 @@ static void create_base_index(const struct commit *current_head)
        tree = parse_tree_indirect(&current_head->object.oid);
        if (!tree)
                die(_("failed to unpack HEAD tree object"));
-       parse_tree(tree);
+       if (parse_tree(tree) < 0)
+               exit(128);
        init_tree_desc(&t, tree->buffer, tree->size);
        if (unpack_trees(1, &t, &opts))
                exit(128); /* We've already reported the error, finish dying */
@@ -737,7 +738,6 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
        const char *hook_arg2 = NULL;
        int clean_message_contents = (cleanup_mode != COMMIT_MSG_CLEANUP_NONE);
        int old_display_comment_prefix;
-       int merge_contains_scissors = 0;
        int invoked_hook;
 
        /* This checks and barfs if author is badly specified */
@@ -841,7 +841,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
                    wt_status_locate_end(sb.buf + merge_msg_start,
                                         sb.len - merge_msg_start) <
                                sb.len - merge_msg_start)
-                       merge_contains_scissors = 1;
+                       s->added_cut_line = 1;
        } else if (!stat(git_path_squash_msg(the_repository), &statbuf)) {
                if (strbuf_read_file(&sb, git_path_squash_msg(the_repository), 0) < 0)
                        die_errno(_("could not read SQUASH_MSG"));
@@ -924,9 +924,8 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
                          " yourself if you want to.\n"
                          "An empty message aborts the commit.\n");
                if (whence != FROM_COMMIT) {
-                       if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS &&
-                               !merge_contains_scissors)
-                               wt_status_add_cut_line(s->fp);
+                       if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS)
+                               wt_status_add_cut_line(s);
                        status_printf_ln(
                                s, GIT_COLOR_NORMAL,
                                whence == FROM_MERGE ?
@@ -946,8 +945,8 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
                if (cleanup_mode == COMMIT_MSG_CLEANUP_ALL)
                        status_printf(s, GIT_COLOR_NORMAL, hint_cleanup_all, comment_line_char);
                else if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS) {
-                       if (whence == FROM_COMMIT && !merge_contains_scissors)
-                               wt_status_add_cut_line(s->fp);
+                       if (whence == FROM_COMMIT)
+                               wt_status_add_cut_line(s);
                } else /* COMMIT_MSG_CLEANUP_SPACE, that is. */
                        status_printf(s, GIT_COLOR_NORMAL, hint_cleanup_space, comment_line_char);
 
index 92eda20683ca2d07c24a03f5903d1e604ce54763..71a195ca227315d799c03dd766d4723f7ffbec46 100644 (file)
@@ -1625,6 +1625,7 @@ static int update_branch(struct branch *b)
                oidclr(&old_oid);
        if (!force_update && !is_null_oid(&old_oid)) {
                struct commit *old_cmit, *new_cmit;
+               int ret;
 
                old_cmit = lookup_commit_reference_gently(the_repository,
                                                          &old_oid, 0);
@@ -1633,7 +1634,10 @@ static int update_branch(struct branch *b)
                if (!old_cmit || !new_cmit)
                        return error("Branch %s is missing commits.", b->name);
 
-               if (!repo_in_merge_bases(the_repository, old_cmit, new_cmit)) {
+               ret = repo_in_merge_bases(the_repository, old_cmit, new_cmit);
+               if (ret < 0)
+                       exit(128);
+               if (!ret) {
                        warning("Not updating %s"
                                " (new tip %s does not contain %s)",
                                b->name, oid_to_hex(&b->oid),
index 3aedfd1bb6361c6bbfd651970f9e9767d0595734..46a793411a437969b53c4f14d941df27358d00ed 100644 (file)
@@ -448,9 +448,8 @@ static void filter_prefetch_refspec(struct refspec *rs)
                        continue;
                if (!rs->items[i].dst ||
                    (rs->items[i].src &&
-                    !strncmp(rs->items[i].src,
-                             ref_namespace[NAMESPACE_TAGS].ref,
-                             strlen(ref_namespace[NAMESPACE_TAGS].ref)))) {
+                    starts_with(rs->items[i].src,
+                                ref_namespace[NAMESPACE_TAGS].ref))) {
                        int j;
 
                        free(rs->items[i].src);
@@ -982,6 +981,8 @@ static int update_local_ref(struct ref *ref,
                uint64_t t_before = getnanotime();
                fast_forward = repo_in_merge_bases(the_repository, current,
                                                   updated);
+               if (fast_forward < 0)
+                       exit(128);
                forced_updates_ms += (getnanotime() - t_before) / 1000000;
        } else {
                fast_forward = 1;
index 3885a9c28e149e5e4133bbf25aa557c38bd4193b..919282e12a335a5a7491b905f27dcfface0120a3 100644 (file)
@@ -20,10 +20,10 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
 {
        struct ref_sorting *sorting;
        struct string_list sorting_options = STRING_LIST_INIT_DUP;
-       int icase = 0;
+       int icase = 0, include_root_refs = 0, from_stdin = 0;
        struct ref_filter filter = REF_FILTER_INIT;
        struct ref_format format = REF_FORMAT_INIT;
-       int from_stdin = 0;
+       unsigned int flags = FILTER_REFS_REGULAR;
        struct strvec vec = STRVEC_INIT;
 
        struct option opts[] = {
@@ -53,6 +53,7 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
                OPT_NO_CONTAINS(&filter.no_commit, N_("print only refs which don't contain the commit")),
                OPT_BOOL(0, "ignore-case", &icase, N_("sorting and filtering are case insensitive")),
                OPT_BOOL(0, "stdin", &from_stdin, N_("read reference patterns from stdin")),
+               OPT_BOOL(0, "include-root-refs", &include_root_refs, N_("also include HEAD ref and pseudorefs")),
                OPT_END(),
        };
 
@@ -96,8 +97,11 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
                filter.name_patterns = argv;
        }
 
+       if (include_root_refs)
+               flags |= FILTER_REFS_ROOT_REFS;
+
        filter.match_as_path = 1;
-       filter_and_format_refs(&filter, FILTER_REFS_ALL, sorting, &format);
+       filter_and_format_refs(&filter, flags, sorting, &format);
 
        ref_filter_clear(&filter);
        ref_sorting_release(sorting);
index a7cf94f67edf3aaace9fdbbca29778d6857d28af..f892487c9b975dd5079b86143e635c6539770981 100644 (file)
@@ -509,9 +509,7 @@ static int fsck_handle_reflog_ent(struct object_id *ooid, struct object_id *noid
        return 0;
 }
 
-static int fsck_handle_reflog(const char *logname,
-                             const struct object_id *oid UNUSED,
-                             int flag UNUSED, void *cb_data)
+static int fsck_handle_reflog(const char *logname, void *cb_data)
 {
        struct strbuf refname = STRBUF_INIT;
 
index db1808d7c13dfd523e31d1da395122b52b6579ab..e5da0d10434dddc146ee00d9b035e76c28e3b214 100644 (file)
@@ -1625,7 +1625,7 @@ static struct commit *get_base_commit(const char *base_commit,
 {
        struct commit *base = NULL;
        struct commit **rev;
-       int i = 0, rev_nr = 0, auto_select, die_on_failure;
+       int i = 0, rev_nr = 0, auto_select, die_on_failure, ret;
 
        switch (auto_base) {
        case AUTO_BASE_NEVER:
@@ -1658,7 +1658,7 @@ static struct commit *get_base_commit(const char *base_commit,
                struct branch *curr_branch = branch_get(NULL);
                const char *upstream = branch_get_upstream(curr_branch, NULL);
                if (upstream) {
-                       struct commit_list *base_list;
+                       struct commit_list *base_list = NULL;
                        struct commit *commit;
                        struct object_id oid;
 
@@ -1669,11 +1669,12 @@ static struct commit *get_base_commit(const char *base_commit,
                                        return NULL;
                        }
                        commit = lookup_commit_or_die(&oid, "upstream base");
-                       base_list = repo_get_merge_bases_many(the_repository,
-                                                             commit, total,
-                                                             list);
-                       /* There should be one and only one merge base. */
-                       if (!base_list || base_list->next) {
+                       if (repo_get_merge_bases_many(the_repository,
+                                                     commit, total,
+                                                     list,
+                                                     &base_list) < 0 ||
+                           /* There should be one and only one merge base. */
+                           !base_list || base_list->next) {
                                if (die_on_failure) {
                                        die(_("could not find exact merge base"));
                                } else {
@@ -1704,11 +1705,11 @@ static struct commit *get_base_commit(const char *base_commit,
         */
        while (rev_nr > 1) {
                for (i = 0; i < rev_nr / 2; i++) {
-                       struct commit_list *merge_base;
-                       merge_base = repo_get_merge_bases(the_repository,
-                                                         rev[2 * i],
-                                                         rev[2 * i + 1]);
-                       if (!merge_base || merge_base->next) {
+                       struct commit_list *merge_base = NULL;
+                       if (repo_get_merge_bases(the_repository,
+                                                rev[2 * i],
+                                                rev[2 * i + 1], &merge_base) < 0 ||
+                           !merge_base || merge_base->next) {
                                if (die_on_failure) {
                                        die(_("failed to find exact merge base"));
                                } else {
@@ -1725,7 +1726,10 @@ static struct commit *get_base_commit(const char *base_commit,
                rev_nr = DIV_ROUND_UP(rev_nr, 2);
        }
 
-       if (!repo_in_merge_bases(the_repository, base, rev[0])) {
+       ret = repo_in_merge_bases(the_repository, base, rev[0]);
+       if (ret < 0)
+               exit(128);
+       if (!ret) {
                if (die_on_failure) {
                        die(_("base commit should be the ancestor of revision list"));
                } else {
index d26e8fbf6f75d9971762afdc0ea5c04e209db387..5a8e72950298c254d430664c6e931ca13c699590 100644 (file)
 
 static int show_merge_base(struct commit **rev, int rev_nr, int show_all)
 {
-       struct commit_list *result, *r;
+       struct commit_list *result = NULL, *r;
 
-       result = repo_get_merge_bases_many_dirty(the_repository, rev[0],
-                                                rev_nr - 1, rev + 1);
+       if (repo_get_merge_bases_many_dirty(the_repository, rev[0],
+                                           rev_nr - 1, rev + 1, &result) < 0) {
+               free_commit_list(result);
+               return -1;
+       }
 
        if (!result)
                return 1;
@@ -74,13 +77,17 @@ static int handle_independent(int count, const char **args)
 static int handle_octopus(int count, const char **args, int show_all)
 {
        struct commit_list *revs = NULL;
-       struct commit_list *result, *rev;
+       struct commit_list *result = NULL, *rev;
        int i;
 
        for (i = count - 1; i >= 0; i--)
                commit_list_insert(get_commit_reference(args[i]), &revs);
 
-       result = get_octopus_merge_bases(revs);
+       if (get_octopus_merge_bases(revs, &result) < 0) {
+               free_commit_list(revs);
+               free_commit_list(result);
+               return 128;
+       }
        free_commit_list(revs);
        reduce_heads_replace(&result);
 
@@ -100,12 +107,16 @@ static int handle_octopus(int count, const char **args, int show_all)
 static int handle_is_ancestor(int argc, const char **argv)
 {
        struct commit *one, *two;
+       int ret;
 
        if (argc != 2)
                die("--is-ancestor takes exactly two commits");
        one = get_commit_reference(argv[0]);
        two = get_commit_reference(argv[1]);
-       if (repo_in_merge_bases(the_repository, one, two))
+       ret = repo_in_merge_bases(the_repository, one, two);
+       if (ret < 0)
+               exit(128);
+       if (ret)
                return 0;
        else
                return 1;
index 3bdec53fbe58bb5edef6e4d425829a58ce87bffe..05d0cad55438a90b72c8e001dfe71b05ad8203d3 100644 (file)
@@ -429,41 +429,56 @@ static int real_merge(struct merge_tree_options *o,
        struct merge_options opt;
 
        copy_merge_options(&opt, &o->merge_options);
-       parent1 = get_merge_parent(branch1);
-       if (!parent1)
-               help_unknown_ref(branch1, "merge-tree",
-                                _("not something we can merge"));
-
-       parent2 = get_merge_parent(branch2);
-       if (!parent2)
-               help_unknown_ref(branch2, "merge-tree",
-                                _("not something we can merge"));
-
        opt.show_rename_progress = 0;
 
        opt.branch1 = branch1;
        opt.branch2 = branch2;
 
        if (merge_base) {
-               struct commit *base_commit;
                struct tree *base_tree, *parent1_tree, *parent2_tree;
 
-               base_commit = lookup_commit_reference_by_name(merge_base);
-               if (!base_commit)
-                       die(_("could not lookup commit '%s'"), merge_base);
+               /*
+                * We actually only need the trees because we already
+                * have a merge base.
+                */
+               struct object_id base_oid, head_oid, merge_oid;
+
+               if (repo_get_oid_treeish(the_repository, merge_base, &base_oid))
+                       die(_("could not parse as tree '%s'"), merge_base);
+               base_tree = parse_tree_indirect(&base_oid);
+               if (!base_tree)
+                       die(_("unable to read tree (%s)"), oid_to_hex(&base_oid));
+               if (repo_get_oid_treeish(the_repository, branch1, &head_oid))
+                       die(_("could not parse as tree '%s'"), branch1);
+               parent1_tree = parse_tree_indirect(&head_oid);
+               if (!parent1_tree)
+                       die(_("unable to read tree (%s)"), oid_to_hex(&head_oid));
+               if (repo_get_oid_treeish(the_repository, branch2, &merge_oid))
+                       die(_("could not parse as tree '%s'"), branch2);
+               parent2_tree = parse_tree_indirect(&merge_oid);
+               if (!parent2_tree)
+                       die(_("unable to read tree (%s)"), oid_to_hex(&merge_oid));
 
                opt.ancestor = merge_base;
-               base_tree = repo_get_commit_tree(the_repository, base_commit);
-               parent1_tree = repo_get_commit_tree(the_repository, parent1);
-               parent2_tree = repo_get_commit_tree(the_repository, parent2);
                merge_incore_nonrecursive(&opt, base_tree, parent1_tree, parent2_tree, &result);
        } else {
+               parent1 = get_merge_parent(branch1);
+               if (!parent1)
+                       help_unknown_ref(branch1, "merge-tree",
+                                        _("not something we can merge"));
+
+               parent2 = get_merge_parent(branch2);
+               if (!parent2)
+                       help_unknown_ref(branch2, "merge-tree",
+                                        _("not something we can merge"));
+
                /*
                 * Get the merge bases, in reverse order; see comment above
                 * merge_incore_recursive in merge-ort.h
                 */
-               merge_bases = repo_get_merge_bases(the_repository, parent1,
-                                                  parent2);
+               if (repo_get_merge_bases(the_repository, parent1,
+                                        parent2, &merge_bases) < 0)
+                       exit(128);
                if (!merge_bases && !o->allow_unrelated_histories)
                        die(_("refusing to merge unrelated histories"));
                merge_bases = reverse_commit_list(merge_bases);
index 935c8a57ddbcaa3b396bdcf633f34eb29d8860c1..a0ba1f9815d9f45a1525bab8932810c9f5919aff 100644 (file)
@@ -1513,13 +1513,20 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 
        if (!remoteheads)
                ; /* already up-to-date */
-       else if (!remoteheads->next)
-               common = repo_get_merge_bases(the_repository, head_commit,
-                                             remoteheads->item);
-       else {
+       else if (!remoteheads->next) {
+               if (repo_get_merge_bases(the_repository, head_commit,
+                                        remoteheads->item, &common) < 0) {
+                       ret = 2;
+                       goto done;
+               }
+       } else {
                struct commit_list *list = remoteheads;
                commit_list_insert(head_commit, &list);
-               common = get_octopus_merge_bases(list);
+               if (get_octopus_merge_bases(list, &common) < 0) {
+                       free(list);
+                       ret = 2;
+                       goto done;
+               }
                free(list);
        }
 
@@ -1626,7 +1633,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                struct commit_list *j;
 
                for (j = remoteheads; j; j = j->next) {
-                       struct commit_list *common_one;
+                       struct commit_list *common_one = NULL;
                        struct commit *common_item;
 
                        /*
@@ -1634,9 +1641,10 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                         * merge_bases again, otherwise "git merge HEAD^
                         * HEAD^^" would be missed.
                         */
-                       common_one = repo_get_merge_bases(the_repository,
-                                                         head_commit,
-                                                         j->item);
+                       if (repo_get_merge_bases(the_repository, head_commit,
+                                                j->item, &common_one) < 0)
+                               exit(128);
+
                        common_item = common_one->item;
                        free_commit_list(common_one);
                        if (!oideq(&common_item->object.oid, &j->item->object.oid)) {
index 2dd1807c4e09f8dc7ff557c628fdd850dc653361..ad9930c83112585f72a49883c4c8035529363fd5 100644 (file)
@@ -15,6 +15,7 @@
 #include "commit-slab.h"
 #include "commit-graph.h"
 #include "wildmatch.h"
+#include "mem-pool.h"
 
 /*
  * One day.  See the 'name a rev shortly after epoch' test in t6120 when
@@ -155,30 +156,25 @@ static struct rev_name *create_or_update_name(struct commit *commit,
        return name;
 }
 
-static char *get_parent_name(const struct rev_name *name, int parent_number)
+static char *get_parent_name(const struct rev_name *name, int parent_number,
+                            struct mem_pool *string_pool)
 {
-       struct strbuf sb = STRBUF_INIT;
        size_t len;
 
        strip_suffix(name->tip_name, "^0", &len);
        if (name->generation > 0) {
-               strbuf_grow(&sb, len +
-                           1 + decimal_width(name->generation) +
-                           1 + decimal_width(parent_number));
-               strbuf_addf(&sb, "%.*s~%d^%d", (int)len, name->tip_name,
-                           name->generation, parent_number);
+               return mem_pool_strfmt(string_pool, "%.*s~%d^%d",
+                                      (int)len, name->tip_name,
+                                      name->generation, parent_number);
        } else {
-               strbuf_grow(&sb, len +
-                           1 + decimal_width(parent_number));
-               strbuf_addf(&sb, "%.*s^%d", (int)len, name->tip_name,
-                           parent_number);
+               return mem_pool_strfmt(string_pool, "%.*s^%d",
+                                      (int)len, name->tip_name, parent_number);
        }
-       return strbuf_detach(&sb, NULL);
 }
 
 static void name_rev(struct commit *start_commit,
                const char *tip_name, timestamp_t taggerdate,
-               int from_tag, int deref)
+               int from_tag, int deref, struct mem_pool *string_pool)
 {
        struct prio_queue queue;
        struct commit *commit;
@@ -195,9 +191,10 @@ static void name_rev(struct commit *start_commit,
        if (!start_name)
                return;
        if (deref)
-               start_name->tip_name = xstrfmt("%s^0", tip_name);
+               start_name->tip_name = mem_pool_strfmt(string_pool, "%s^0",
+                                                      tip_name);
        else
-               start_name->tip_name = xstrdup(tip_name);
+               start_name->tip_name = mem_pool_strdup(string_pool, tip_name);
 
        memset(&queue, 0, sizeof(queue)); /* Use the prio_queue as LIFO */
        prio_queue_put(&queue, start_commit);
@@ -235,7 +232,8 @@ static void name_rev(struct commit *start_commit,
                                if (parent_number > 1)
                                        parent_name->tip_name =
                                                get_parent_name(name,
-                                                               parent_number);
+                                                               parent_number,
+                                                               string_pool);
                                else
                                        parent_name->tip_name = name->tip_name;
                                ALLOC_GROW(parents_to_queue,
@@ -415,7 +413,7 @@ static int name_ref(const char *path, const struct object_id *oid,
        return 0;
 }
 
-static void name_tips(void)
+static void name_tips(struct mem_pool *string_pool)
 {
        int i;
 
@@ -428,7 +426,7 @@ static void name_tips(void)
                struct tip_table_entry *e = &tip_table.table[i];
                if (e->commit) {
                        name_rev(e->commit, e->refname, e->taggerdate,
-                                e->from_tag, e->deref);
+                                e->from_tag, e->deref, string_pool);
                }
        }
 }
@@ -561,6 +559,7 @@ static void name_rev_line(char *p, struct name_ref_data *data)
 
 int cmd_name_rev(int argc, const char **argv, const char *prefix)
 {
+       struct mem_pool string_pool;
        struct object_array revs = OBJECT_ARRAY_INIT;
        int all = 0, annotate_stdin = 0, transform_stdin = 0, allow_undefined = 1, always = 0, peel_tag = 0;
        struct name_ref_data data = { 0, 0, STRING_LIST_INIT_NODUP, STRING_LIST_INIT_NODUP };
@@ -587,6 +586,7 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix)
                OPT_END(),
        };
 
+       mem_pool_init(&string_pool, 0);
        init_commit_rev_name(&rev_names);
        git_config(git_default_config, NULL);
        argc = parse_options(argc, argv, prefix, opts, name_rev_usage, 0);
@@ -648,7 +648,7 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix)
        adjust_cutoff_timestamp_for_slop();
 
        for_each_ref(name_ref, &data);
-       name_tips();
+       name_tips(&string_pool);
 
        if (annotate_stdin) {
                struct strbuf sb = STRBUF_INIT;
@@ -676,6 +676,7 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix)
                                  always, allow_undefined, data.name_only);
        }
 
+       UNLEAK(string_pool);
        UNLEAK(revs);
        return 0;
 }
index 73a68b75b0672487013299f992d382d1957a02a4..72cbb76d520718cfc2f23093dd5ef76ea2d682bf 100644 (file)
@@ -815,7 +815,7 @@ static int get_octopus_merge_base(struct object_id *merge_base,
                const struct object_id *merge_head,
                const struct object_id *fork_point)
 {
-       struct commit_list *revs = NULL, *result;
+       struct commit_list *revs = NULL, *result = NULL;
 
        commit_list_insert(lookup_commit_reference(the_repository, curr_head),
                           &revs);
@@ -825,7 +825,8 @@ static int get_octopus_merge_base(struct object_id *merge_base,
                commit_list_insert(lookup_commit_reference(the_repository, fork_point),
                                   &revs);
 
-       result = get_octopus_merge_bases(revs);
+       if (get_octopus_merge_bases(revs, &result) < 0)
+               exit(128);
        free_commit_list(revs);
        reduce_heads_replace(&result);
 
@@ -926,6 +927,8 @@ static int get_can_ff(struct object_id *orig_head,
        merge_head = lookup_commit_reference(the_repository, orig_merge_head);
        ret = repo_is_descendant_of(the_repository, merge_head, list);
        free_commit_list(list);
+       if (ret < 0)
+               exit(128);
        return ret;
 }
 
@@ -950,6 +953,8 @@ static int already_up_to_date(struct object_id *orig_head,
                commit_list_insert(theirs, &list);
                ok = repo_is_descendant_of(the_repository, ours, list);
                free_commit_list(list);
+               if (ok < 0)
+                       exit(128);
                if (!ok)
                        return 0;
        }
index 20e7db19737d5a5070068a131797042d37a936a5..1ffd863cff6701af8013130d2e2801366f4e7228 100644 (file)
@@ -261,7 +261,8 @@ int cmd_read_tree(int argc, const char **argv, const char *cmd_prefix)
        cache_tree_free(&the_index.cache_tree);
        for (i = 0; i < nr_trees; i++) {
                struct tree *tree = trees[i];
-               parse_tree(tree);
+               if (parse_tree(tree) < 0)
+                       return 128;
                init_tree_desc(t+i, tree->buffer, tree->size);
        }
        if (unpack_trees(nr_trees, t, &opts))
index 5b086f651a6fce7d97132da8ff36c80f00648db1..be787690bd7c2b6396784a1476314b8f757ab749 100644 (file)
@@ -867,7 +867,8 @@ static int can_fast_forward(struct commit *onto, struct commit *upstream,
        if (!upstream)
                goto done;
 
-       merge_bases = repo_get_merge_bases(the_repository, upstream, head);
+       if (repo_get_merge_bases(the_repository, upstream, head, &merge_bases) < 0)
+               exit(128);
        if (!merge_bases || merge_bases->next)
                goto done;
 
@@ -886,8 +887,9 @@ static void fill_branch_base(struct rebase_options *options,
 {
        struct commit_list *merge_bases = NULL;
 
-       merge_bases = repo_get_merge_bases(the_repository, options->onto,
-                                          options->orig_head);
+       if (repo_get_merge_bases(the_repository, options->onto,
+                                options->orig_head, &merge_bases) < 0)
+               exit(128);
        if (!merge_bases || merge_bases->next)
                oidcpy(branch_base, null_oid());
        else
@@ -1254,7 +1256,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
                die(_("options '%s' and '%s' cannot be used together"), "--root", "--fork-point");
 
        if (options.action != ACTION_NONE && !in_progress)
-               die(_("No rebase in progress?"));
+               die(_("no rebase in progress"));
 
        if (options.action == ACTION_EDIT_TODO && !is_merge(&options))
                die(_("The --edit-todo action can only be used during "
index db656074857e765c8b5d5c91ab1b872de0bad66d..56d8a77ed75f65c1bcf47bd580e8e4e7cbbc32d9 100644 (file)
@@ -1526,6 +1526,7 @@ static const char *update(struct command *cmd, struct shallow_info *si)
            starts_with(name, "refs/heads/")) {
                struct object *old_object, *new_object;
                struct commit *old_commit, *new_commit;
+               int ret2;
 
                old_object = parse_object(the_repository, old_oid);
                new_object = parse_object(the_repository, new_oid);
@@ -1539,7 +1540,10 @@ static const char *update(struct command *cmd, struct shallow_info *si)
                }
                old_commit = (struct commit *)old_object;
                new_commit = (struct commit *)new_object;
-               if (!repo_in_merge_bases(the_repository, old_commit, new_commit)) {
+               ret2 = repo_in_merge_bases(the_repository, old_commit, new_commit);
+               if (ret2 < 0)
+                       exit(128);
+               if (!ret2) {
                        rp_error("denying non-fast-forward %s"
                                 " (you should pull first)", name);
                        ret = "non-fast-forward";
index 2c3369fca54f66f1625b6bcd2b786f909f515b26..060eb3377e5d5b2e8af213aa1eec2b2decb33af4 100644 (file)
@@ -7,11 +7,15 @@
 #include "wildmatch.h"
 #include "worktree.h"
 #include "reflog.h"
+#include "refs.h"
 #include "parse-options.h"
 
 #define BUILTIN_REFLOG_SHOW_USAGE \
        N_("git reflog [show] [<log-options>] [<ref>]")
 
+#define BUILTIN_REFLOG_LIST_USAGE \
+       N_("git reflog list")
+
 #define BUILTIN_REFLOG_EXPIRE_USAGE \
        N_("git reflog expire [--expire=<time>] [--expire-unreachable=<time>]\n" \
           "                  [--rewrite] [--updateref] [--stale-fix]\n" \
@@ -29,6 +33,11 @@ static const char *const reflog_show_usage[] = {
        NULL,
 };
 
+static const char *const reflog_list_usage[] = {
+       BUILTIN_REFLOG_LIST_USAGE,
+       NULL,
+};
+
 static const char *const reflog_expire_usage[] = {
        BUILTIN_REFLOG_EXPIRE_USAGE,
        NULL
@@ -46,6 +55,7 @@ static const char *const reflog_exists_usage[] = {
 
 static const char *const reflog_usage[] = {
        BUILTIN_REFLOG_SHOW_USAGE,
+       BUILTIN_REFLOG_LIST_USAGE,
        BUILTIN_REFLOG_EXPIRE_USAGE,
        BUILTIN_REFLOG_DELETE_USAGE,
        BUILTIN_REFLOG_EXISTS_USAGE,
@@ -60,8 +70,7 @@ struct worktree_reflogs {
        struct string_list reflogs;
 };
 
-static int collect_reflog(const char *ref, const struct object_id *oid UNUSED,
-                         int flags UNUSED, void *cb_data)
+static int collect_reflog(const char *ref, void *cb_data)
 {
        struct worktree_reflogs *cb = cb_data;
        struct worktree *worktree = cb->worktree;
@@ -238,6 +247,29 @@ static int cmd_reflog_show(int argc, const char **argv, const char *prefix)
        return cmd_log_reflog(argc, argv, prefix);
 }
 
+static int show_reflog(const char *refname, void *cb_data UNUSED)
+{
+       printf("%s\n", refname);
+       return 0;
+}
+
+static int cmd_reflog_list(int argc, const char **argv, const char *prefix)
+{
+       struct option options[] = {
+               OPT_END()
+       };
+       struct ref_store *ref_store;
+
+       argc = parse_options(argc, argv, prefix, options, reflog_list_usage, 0);
+       if (argc)
+               return error(_("%s does not accept arguments: '%s'"),
+                            "list", argv[0]);
+
+       ref_store = get_main_ref_store(the_repository);
+
+       return refs_for_each_reflog(ref_store, show_reflog, NULL);
+}
+
 static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
 {
        struct cmd_reflog_expire_cb cmd = { 0 };
@@ -417,6 +449,7 @@ int cmd_reflog(int argc, const char **argv, const char *prefix)
        parse_opt_subcommand_fn *fn = NULL;
        struct option options[] = {
                OPT_SUBCOMMAND("show", &fn, cmd_reflog_show),
+               OPT_SUBCOMMAND("list", &fn, cmd_reflog_list),
                OPT_SUBCOMMAND("expire", &fn, cmd_reflog_expire),
                OPT_SUBCOMMAND("delete", &fn, cmd_reflog_delete),
                OPT_SUBCOMMAND("exists", &fn, cmd_reflog_exists),
index f0bf29a4783b81fde5fb97478b974dade7a310d0..1d62ff6332737c6449c76f66d2ebcbadb36bfb11 100644 (file)
@@ -116,6 +116,10 @@ static int reset_index(const char *ref, const struct object_id *oid, int reset_t
 
        if (reset_type == MIXED || reset_type == HARD) {
                tree = parse_tree_indirect(oid);
+               if (!tree) {
+                       error(_("unable to read tree (%s)"), oid_to_hex(oid));
+                       goto out;
+               }
                prime_cache_tree(the_repository, the_repository->index, tree);
        }
 
index b3f47838580c9dd0ed233a3ac34c19aac6b7fa0f..ec455aa97277dd58d106c10623ad36aa923bcd10 100644 (file)
@@ -545,6 +545,18 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
         *
         * Let "--missing" to conditionally set fetch_if_missing.
         */
+       /*
+        * NEEDSWORK: These loops that attempt to find presence of
+        * options without understanding that the options they are
+        * skipping are broken (e.g., it would not know "--grep
+        * --exclude-promisor-objects" is not triggering
+        * "--exclude-promisor-objects" option).  We really need
+        * setup_revisions() to have a mechanism to allow and disallow
+        * some sets of options for different commands (like rev-list,
+        * replay, etc). Such a mechanism should do an early parsing
+        * of options and be able to manage the `--missing=...` and
+        * `--exclude-promisor-objects` options below.
+        */
        for (i = 1; i < argc; i++) {
                const char *arg = argv[i];
                if (!strcmp(arg, "--exclude-promisor-objects")) {
@@ -753,8 +765,12 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
 
        if (arg_print_omitted)
                oidset_init(&omitted_objects, DEFAULT_OIDSET_SIZE);
-       if (arg_missing_action == MA_PRINT)
+       if (arg_missing_action == MA_PRINT) {
                oidset_init(&missing_objects, DEFAULT_OIDSET_SIZE);
+               /* Add missing tips */
+               oidset_insert_from_set(&missing_objects, &revs.missing_commits);
+               oidset_clear(&revs.missing_commits);
+       }
 
        traverse_commit_list_filtered(
                &revs, show_commit, show_object, &info,
index d08987646a0a533ad81480ae4667b577a335f9f7..181c703d4c00bcce1f1715f1a8913fad0892828d 100644 (file)
@@ -297,7 +297,7 @@ static int try_difference(const char *arg)
                show_rev(NORMAL, &end_oid, end);
                show_rev(symmetric ? NORMAL : REVERSED, &start_oid, start);
                if (symmetric) {
-                       struct commit_list *exclude;
+                       struct commit_list *exclude = NULL;
                        struct commit *a, *b;
                        a = lookup_commit_reference(the_repository, &start_oid);
                        b = lookup_commit_reference(the_repository, &end_oid);
@@ -305,7 +305,8 @@ static int try_difference(const char *arg)
                                *dotdot = '.';
                                return 0;
                        }
-                       exclude = repo_get_merge_bases(the_repository, a, b);
+                       if (repo_get_merge_bases(the_repository, a, b, &exclude) < 0)
+                               exit(128);
                        while (exclude) {
                                struct commit *commit = pop_commit(&exclude);
                                show_rev(REVERSED, &commit->object.oid, NULL);
index 37473ac21f560d8f38c710f20532f5ef41e9276a..19a7e06bf414ccf811c93f0ac941a142a7fabef2 100644 (file)
@@ -530,7 +530,8 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
                        struct column_options copts;
                        memset(&copts, 0, sizeof(copts));
                        copts.padding = 2;
-                       run_column_filter(colopts, &copts);
+                       if (run_column_filter(colopts, &copts))
+                               die(_("could not start 'git column'"));
                }
                filter.name_patterns = argv;
                ret = list_tags(&filter, sorting, &format);
index 9b021ef026c28c97ece12b924833dff87c15a103..15afb97260165bc51cc85bae59b08afb37bfc6a6 100644 (file)
@@ -8,6 +8,7 @@
 #include "replace-object.h"
 #include "upload-pack.h"
 #include "serve.h"
+#include "commit.h"
 
 static const char * const upload_pack_usage[] = {
        N_("git-upload-pack [--[no-]strict] [--timeout=<n>] [--stateless-rpc]\n"
@@ -37,6 +38,7 @@ int cmd_upload_pack(int argc, const char **argv, const char *prefix)
 
        packet_trace_identity("upload-pack");
        disable_replace_refs();
+       save_commit_buffer = 0;
 
        argc = parse_options(argc, argv, prefix, options, upload_pack_usage, 0);
 
index 64678fe19930402b8400cb05d3ccaad5bb5b00a6..80ca8324773d2dc352bf4d47cfc4d0036316180e 100644 (file)
@@ -778,8 +778,8 @@ static void prime_cache_tree_rec(struct repository *r,
                        struct cache_tree_sub *sub;
                        struct tree *subtree = lookup_tree(r, &entry.oid);
 
-                       if (!subtree->object.parsed)
-                               parse_tree(subtree);
+                       if (parse_tree(subtree) < 0)
+                               exit(128);
                        sub = cache_tree_sub(it, entry.path);
                        sub->cache_tree = cache_tree();
 
index ecc913fc99ba9e6b88df675462a2b8f912cbd0ce..8f9b008f876787abf12ca89af5541f0b3bdf6ba7 100644 (file)
@@ -49,13 +49,14 @@ static int queue_has_nonstale(struct prio_queue *queue)
 }
 
 /* all input commits in one and twos[] must have been parsed! */
-static struct commit_list *paint_down_to_common(struct repository *r,
-                                               struct commit *one, int n,
-                                               struct commit **twos,
-                                               timestamp_t min_generation)
+static int paint_down_to_common(struct repository *r,
+                               struct commit *one, int n,
+                               struct commit **twos,
+                               timestamp_t min_generation,
+                               int ignore_missing_commits,
+                               struct commit_list **result)
 {
        struct prio_queue queue = { compare_commits_by_gen_then_commit_date };
-       struct commit_list *result = NULL;
        int i;
        timestamp_t last_gen = GENERATION_NUMBER_INFINITY;
 
@@ -64,8 +65,8 @@ static struct commit_list *paint_down_to_common(struct repository *r,
 
        one->object.flags |= PARENT1;
        if (!n) {
-               commit_list_append(one, &result);
-               return result;
+               commit_list_append(one, result);
+               return 0;
        }
        prio_queue_put(&queue, one);
 
@@ -93,7 +94,7 @@ static struct commit_list *paint_down_to_common(struct repository *r,
                if (flags == (PARENT1 | PARENT2)) {
                        if (!(commit->object.flags & RESULT)) {
                                commit->object.flags |= RESULT;
-                               commit_list_insert_by_date(commit, &result);
+                               commit_list_insert_by_date(commit, result);
                        }
                        /* Mark parents of a found merge stale */
                        flags |= STALE;
@@ -104,67 +105,97 @@ static struct commit_list *paint_down_to_common(struct repository *r,
                        parents = parents->next;
                        if ((p->object.flags & flags) == flags)
                                continue;
-                       if (repo_parse_commit(r, p))
-                               return NULL;
+                       if (repo_parse_commit(r, p)) {
+                               clear_prio_queue(&queue);
+                               free_commit_list(*result);
+                               *result = NULL;
+                               /*
+                                * At this stage, we know that the commit is
+                                * missing: `repo_parse_commit()` uses
+                                * `OBJECT_INFO_DIE_IF_CORRUPT` and therefore
+                                * corrupt commits would already have been
+                                * dispatched with a `die()`.
+                                */
+                               if (ignore_missing_commits)
+                                       return 0;
+                               return error(_("could not parse commit %s"),
+                                            oid_to_hex(&p->object.oid));
+                       }
                        p->object.flags |= flags;
                        prio_queue_put(&queue, p);
                }
        }
 
        clear_prio_queue(&queue);
-       return result;
+       return 0;
 }
 
-static struct commit_list *merge_bases_many(struct repository *r,
-                                           struct commit *one, int n,
-                                           struct commit **twos)
+static int merge_bases_many(struct repository *r,
+                           struct commit *one, int n,
+                           struct commit **twos,
+                           struct commit_list **result)
 {
        struct commit_list *list = NULL;
-       struct commit_list *result = NULL;
        int i;
 
        for (i = 0; i < n; i++) {
-               if (one == twos[i])
+               if (one == twos[i]) {
                        /*
                         * We do not mark this even with RESULT so we do not
                         * have to clean it up.
                         */
-                       return commit_list_insert(one, &result);
+                       *result = commit_list_insert(one, result);
+                       return 0;
+               }
        }
 
+       if (!one)
+               return 0;
        if (repo_parse_commit(r, one))
-               return NULL;
+               return error(_("could not parse commit %s"),
+                            oid_to_hex(&one->object.oid));
        for (i = 0; i < n; i++) {
+               if (!twos[i])
+                       return 0;
                if (repo_parse_commit(r, twos[i]))
-                       return NULL;
+                       return error(_("could not parse commit %s"),
+                                    oid_to_hex(&twos[i]->object.oid));
        }
 
-       list = paint_down_to_common(r, one, n, twos, 0);
+       if (paint_down_to_common(r, one, n, twos, 0, 0, &list)) {
+               free_commit_list(list);
+               return -1;
+       }
 
        while (list) {
                struct commit *commit = pop_commit(&list);
                if (!(commit->object.flags & STALE))
-                       commit_list_insert_by_date(commit, &result);
+                       commit_list_insert_by_date(commit, result);
        }
-       return result;
+       return 0;
 }
 
-struct commit_list *get_octopus_merge_bases(struct commit_list *in)
+int get_octopus_merge_bases(struct commit_list *in, struct commit_list **result)
 {
-       struct commit_list *i, *j, *k, *ret = NULL;
+       struct commit_list *i, *j, *k;
 
        if (!in)
-               return ret;
+               return 0;
 
-       commit_list_insert(in->item, &ret);
+       commit_list_insert(in->item, result);
 
        for (i = in->next; i; i = i->next) {
                struct commit_list *new_commits = NULL, *end = NULL;
 
-               for (j = ret; j; j = j->next) {
-                       struct commit_list *bases;
-                       bases = repo_get_merge_bases(the_repository, i->item,
-                                                    j->item);
+               for (j = *result; j; j = j->next) {
+                       struct commit_list *bases = NULL;
+                       if (repo_get_merge_bases(the_repository, i->item,
+                                                j->item, &bases) < 0) {
+                               free_commit_list(bases);
+                               free_commit_list(*result);
+                               *result = NULL;
+                               return -1;
+                       }
                        if (!new_commits)
                                new_commits = bases;
                        else
@@ -172,10 +203,10 @@ struct commit_list *get_octopus_merge_bases(struct commit_list *in)
                        for (k = bases; k; k = k->next)
                                end = k;
                }
-               free_commit_list(ret);
-               ret = new_commits;
+               free_commit_list(*result);
+               *result = new_commits;
        }
-       return ret;
+       return 0;
 }
 
 static int remove_redundant_no_gen(struct repository *r,
@@ -193,7 +224,7 @@ static int remove_redundant_no_gen(struct repository *r,
        for (i = 0; i < cnt; i++)
                repo_parse_commit(r, array[i]);
        for (i = 0; i < cnt; i++) {
-               struct commit_list *common;
+               struct commit_list *common = NULL;
                timestamp_t min_generation = commit_graph_generation(array[i]);
 
                if (redundant[i])
@@ -209,8 +240,16 @@ static int remove_redundant_no_gen(struct repository *r,
                        if (curr_generation < min_generation)
                                min_generation = curr_generation;
                }
-               common = paint_down_to_common(r, array[i], filled,
-                                             work, min_generation);
+               if (paint_down_to_common(r, array[i], filled,
+                                        work, min_generation, 0, &common)) {
+                       clear_commit_marks(array[i], all_flags);
+                       clear_commit_marks_many(filled, work, all_flags);
+                       free_commit_list(common);
+                       free(work);
+                       free(redundant);
+                       free(filled_index);
+                       return -1;
+               }
                if (array[i]->object.flags & PARENT2)
                        redundant[i] = 1;
                for (j = 0; j < filled; j++)
@@ -375,69 +414,77 @@ static int remove_redundant(struct repository *r, struct commit **array, int cnt
        return remove_redundant_no_gen(r, array, cnt);
 }
 
-static struct commit_list *get_merge_bases_many_0(struct repository *r,
-                                                 struct commit *one,
-                                                 int n,
-                                                 struct commit **twos,
-                                                 int cleanup)
+static int get_merge_bases_many_0(struct repository *r,
+                                 struct commit *one,
+                                 int n,
+                                 struct commit **twos,
+                                 int cleanup,
+                                 struct commit_list **result)
 {
        struct commit_list *list;
        struct commit **rslt;
-       struct commit_list *result;
        int cnt, i;
 
-       result = merge_bases_many(r, one, n, twos);
+       if (merge_bases_many(r, one, n, twos, result) < 0)
+               return -1;
        for (i = 0; i < n; i++) {
                if (one == twos[i])
-                       return result;
+                       return 0;
        }
-       if (!result || !result->next) {
+       if (!*result || !(*result)->next) {
                if (cleanup) {
                        clear_commit_marks(one, all_flags);
                        clear_commit_marks_many(n, twos, all_flags);
                }
-               return result;
+               return 0;
        }
 
        /* There are more than one */
-       cnt = commit_list_count(result);
+       cnt = commit_list_count(*result);
        CALLOC_ARRAY(rslt, cnt);
-       for (list = result, i = 0; list; list = list->next)
+       for (list = *result, i = 0; list; list = list->next)
                rslt[i++] = list->item;
-       free_commit_list(result);
+       free_commit_list(*result);
+       *result = NULL;
 
        clear_commit_marks(one, all_flags);
        clear_commit_marks_many(n, twos, all_flags);
 
        cnt = remove_redundant(r, rslt, cnt);
-       result = NULL;
+       if (cnt < 0) {
+               free(rslt);
+               return -1;
+       }
        for (i = 0; i < cnt; i++)
-               commit_list_insert_by_date(rslt[i], &result);
+               commit_list_insert_by_date(rslt[i], result);
        free(rslt);
-       return result;
+       return 0;
 }
 
-struct commit_list *repo_get_merge_bases_many(struct repository *r,
-                                             struct commit *one,
-                                             int n,
-                                             struct commit **twos)
+int repo_get_merge_bases_many(struct repository *r,
+                             struct commit *one,
+                             int n,
+                             struct commit **twos,
+                             struct commit_list **result)
 {
-       return get_merge_bases_many_0(r, one, n, twos, 1);
+       return get_merge_bases_many_0(r, one, n, twos, 1, result);
 }
 
-struct commit_list *repo_get_merge_bases_many_dirty(struct repository *r,
-                                                   struct commit *one,
-                                                   int n,
-                                                   struct commit **twos)
+int repo_get_merge_bases_many_dirty(struct repository *r,
+                                   struct commit *one,
+                                   int n,
+                                   struct commit **twos,
+                                   struct commit_list **result)
 {
-       return get_merge_bases_many_0(r, one, n, twos, 0);
+       return get_merge_bases_many_0(r, one, n, twos, 0, result);
 }
 
-struct commit_list *repo_get_merge_bases(struct repository *r,
-                                        struct commit *one,
-                                        struct commit *two)
+int repo_get_merge_bases(struct repository *r,
+                        struct commit *one,
+                        struct commit *two,
+                        struct commit_list **result)
 {
-       return get_merge_bases_many_0(r, one, 1, &two, 1);
+       return get_merge_bases_many_0(r, one, 1, &two, 1, result);
 }
 
 /*
@@ -460,11 +507,13 @@ int repo_is_descendant_of(struct repository *r,
        } else {
                while (with_commit) {
                        struct commit *other;
+                       int ret;
 
                        other = with_commit->item;
                        with_commit = with_commit->next;
-                       if (repo_in_merge_bases_many(r, other, 1, &commit))
-                               return 1;
+                       ret = repo_in_merge_bases_many(r, other, 1, &commit, 0);
+                       if (ret)
+                               return ret;
                }
                return 0;
        }
@@ -474,17 +523,18 @@ int repo_is_descendant_of(struct repository *r,
  * Is "commit" an ancestor of one of the "references"?
  */
 int repo_in_merge_bases_many(struct repository *r, struct commit *commit,
-                            int nr_reference, struct commit **reference)
+                            int nr_reference, struct commit **reference,
+                            int ignore_missing_commits)
 {
-       struct commit_list *bases;
+       struct commit_list *bases = NULL;
        int ret = 0, i;
        timestamp_t generation, max_generation = GENERATION_NUMBER_ZERO;
 
        if (repo_parse_commit(r, commit))
-               return ret;
+               return ignore_missing_commits ? 0 : -1;
        for (i = 0; i < nr_reference; i++) {
                if (repo_parse_commit(r, reference[i]))
-                       return ret;
+                       return ignore_missing_commits ? 0 : -1;
 
                generation = commit_graph_generation(reference[i]);
                if (generation > max_generation)
@@ -495,10 +545,11 @@ int repo_in_merge_bases_many(struct repository *r, struct commit *commit,
        if (generation > max_generation)
                return ret;
 
-       bases = paint_down_to_common(r, commit,
-                                    nr_reference, reference,
-                                    generation);
-       if (commit->object.flags & PARENT2)
+       if (paint_down_to_common(r, commit,
+                                nr_reference, reference,
+                                generation, ignore_missing_commits, &bases))
+               ret = -1;
+       else if (commit->object.flags & PARENT2)
                ret = 1;
        clear_commit_marks(commit, all_flags);
        clear_commit_marks_many(nr_reference, reference, all_flags);
@@ -551,6 +602,10 @@ struct commit_list *reduce_heads(struct commit_list *heads)
                }
        }
        num_head = remove_redundant(the_repository, array, num_head);
+       if (num_head < 0) {
+               free(array);
+               return NULL;
+       }
        for (i = 0; i < num_head; i++)
                tail = &commit_list_insert(array[i], tail)->next;
        free(array);
@@ -593,6 +648,8 @@ int ref_newer(const struct object_id *new_oid, const struct object_id *old_oid)
        commit_list_insert(old_commit, &old_commit_list);
        ret = repo_is_descendant_of(the_repository,
                                    new_commit, old_commit_list);
+       if (ret < 0)
+               exit(128);
        free_commit_list(old_commit_list);
        return ret;
 }
index 35c4da4948122a6caea3a1757484b487db16b0fd..bf63cc468fd311a9ef658a23cec3db4fbbbac767 100644 (file)
@@ -9,18 +9,21 @@ struct ref_filter;
 struct object_id;
 struct object_array;
 
-struct commit_list *repo_get_merge_bases(struct repository *r,
-                                        struct commit *rev1,
-                                        struct commit *rev2);
-struct commit_list *repo_get_merge_bases_many(struct repository *r,
-                                             struct commit *one, int n,
-                                             struct commit **twos);
+int repo_get_merge_bases(struct repository *r,
+                        struct commit *rev1,
+                        struct commit *rev2,
+                        struct commit_list **result);
+int repo_get_merge_bases_many(struct repository *r,
+                             struct commit *one, int n,
+                             struct commit **twos,
+                             struct commit_list **result);
 /* To be used only when object flags after this call no longer matter */
-struct commit_list *repo_get_merge_bases_many_dirty(struct repository *r,
-                                                   struct commit *one, int n,
-                                                   struct commit **twos);
+int repo_get_merge_bases_many_dirty(struct repository *r,
+                                   struct commit *one, int n,
+                                   struct commit **twos,
+                                   struct commit_list **result);
 
-struct commit_list *get_octopus_merge_bases(struct commit_list *in);
+int get_octopus_merge_bases(struct commit_list *in, struct commit_list **result);
 
 int repo_is_descendant_of(struct repository *r,
                          struct commit *commit,
@@ -30,7 +33,8 @@ int repo_in_merge_bases(struct repository *r,
                        struct commit *reference);
 int repo_in_merge_bases_many(struct repository *r,
                             struct commit *commit,
-                            int nr_reference, struct commit **reference);
+                            int nr_reference, struct commit **reference,
+                            int ignore_missing_commits);
 
 /*
  * Takes a list of commits and returns a new list where those
index ef679a0b939046c4aac15567cdf3c0ae8c079d29..467be9f7f99408edbe1a34e2f21c491dd50e2813 100644 (file)
--- a/commit.c
+++ b/commit.c
@@ -1052,7 +1052,7 @@ struct commit *get_fork_point(const char *refname, struct commit *commit)
 {
        struct object_id oid;
        struct rev_collect revs;
-       struct commit_list *bases;
+       struct commit_list *bases = NULL;
        int i;
        struct commit *ret = NULL;
        char *full_refname;
@@ -1077,8 +1077,9 @@ struct commit *get_fork_point(const char *refname, struct commit *commit)
        for (i = 0; i < revs.nr; i++)
                revs.commit[i]->object.flags &= ~TMP_MARK;
 
-       bases = repo_get_merge_bases_many(the_repository, commit, revs.nr,
-                                         revs.commit);
+       if (repo_get_merge_bases_many(the_repository, commit, revs.nr,
+                                     revs.commit, &bases) < 0)
+               exit(128);
 
        /*
         * There should be one and only one merge base, when we found
index 10dbb65937d17cab381bc1bfab1dff099157a625..e9ad9db84f22797e745d7a6c4e77021c3cf1814c 100644 (file)
@@ -1,7 +1,6 @@
 #ifndef COMPILER_H
 #define COMPILER_H
 
-#include "git-compat-util.h"
 #include "strbuf.h"
 
 #ifdef __GLIBC__
index 6c979c27d89ec858b849f87e64505c2554cb121b..23bc1bef86c87a0853b87d75e0fc35ea1932f99d 100644 (file)
@@ -1,7 +1,6 @@
 #ifndef COMPAT_DISK_H
 #define COMPAT_DISK_H
 
-#include "git-compat-util.h"
 #include "abspath.h"
 #include "gettext.h"
 
index 444b3efa6303396acabe67a997fbf6a57fe0bcdb..fcf1afd75de4eb14a0123a2a75692298f2c39a0b 100644 (file)
@@ -2673,7 +2673,8 @@ __git_compute_first_level_config_vars_for_section ()
        __git_compute_config_vars
        local this_section="__git_first_level_config_vars_for_section_${section}"
        test -n "${!this_section}" ||
-       printf -v "__git_first_level_config_vars_for_section_${section}" %s "$(echo "$__git_config_vars" | grep -E "^${section}\.[a-z]" | awk -F. '{print $2}')"
+       printf -v "__git_first_level_config_vars_for_section_${section}" %s \
+               "$(echo "$__git_config_vars" | awk -F. "/^${section}\.[a-z]/ { print \$2 }")"
 }
 
 __git_compute_second_level_config_vars_for_section ()
@@ -2682,7 +2683,8 @@ __git_compute_second_level_config_vars_for_section ()
        __git_compute_config_vars_all
        local this_section="__git_second_level_config_vars_for_section_${section}"
        test -n "${!this_section}" ||
-       printf -v "__git_second_level_config_vars_for_section_${section}" %s "$(echo "$__git_config_vars_all" | grep -E "^${section}\.<" | awk -F. '{print $3}')"
+       printf -v "__git_second_level_config_vars_for_section_${section}" %s \
+               "$(echo "$__git_config_vars_all" | awk -F. "/^${section}\.</ { print \$3 }")"
 }
 
 __git_config_sections=
index 215a81d8bae59ca2364eb762c72089ff8b6b51d7..90034d0cf1eb3d04c04872f38f2c8ba3a25fd904 100644 (file)
@@ -164,6 +164,9 @@ static int keyring_get(struct credential *c)
                        if (g_strv_length(parts) >= 1) {
                                g_free(c->password);
                                c->password = g_strdup(parts[0]);
+                       } else {
+                               g_free(c->password);
+                               c->password = g_strdup("");
                        }
                        for (int i = 1; i < g_strv_length(parts); i++) {
                                if (g_str_has_prefix(parts[i], "password_expiry_utc=")) {
index 6c8df04273702fa47215dffa263477df347900ee..5e8717c774eff4bb2364344c440a4efb4686e4f4 100644 (file)
@@ -570,7 +570,7 @@ void diff_get_merge_base(const struct rev_info *revs, struct object_id *mb)
 {
        int i;
        struct commit *mb_child[2] = {0};
-       struct commit_list *merge_bases;
+       struct commit_list *merge_bases = NULL;
 
        for (i = 0; i < revs->pending.nr; i++) {
                struct object *obj = revs->pending.objects[i].item;
@@ -597,7 +597,8 @@ void diff_get_merge_base(const struct rev_info *revs, struct object_id *mb)
                mb_child[1] = lookup_commit_reference(the_repository, &oid);
        }
 
-       merge_bases = repo_get_merge_bases(the_repository, mb_child[0], mb_child[1]);
+       if (repo_get_merge_bases(the_repository, mb_child[0], mb_child[1], &merge_bases) < 0)
+               exit(128);
        if (!merge_bases)
                die(_("no merge base found"));
        if (merge_bases->next)
index 278b04243a3f40e63df433eea9549f90aed1c34d..de619846f29ad9c51d42b538d12c1b62e3958b5d 100644 (file)
@@ -2,10 +2,19 @@
 #include "dir.h"
 #include "iterator.h"
 #include "dir-iterator.h"
+#include "string-list.h"
 
 struct dir_iterator_level {
        DIR *dir;
 
+       /*
+        * The directory entries of the current level. This list will only be
+        * populated when the iterator is ordered. In that case, `dir` will be
+        * set to `NULL`.
+        */
+       struct string_list entries;
+       size_t entries_idx;
+
        /*
         * The length of the directory part of path at this level
         * (including a trailing '/'):
@@ -43,6 +52,31 @@ struct dir_iterator_int {
        unsigned int flags;
 };
 
+static int next_directory_entry(DIR *dir, const char *path,
+                               struct dirent **out)
+{
+       struct dirent *de;
+
+repeat:
+       errno = 0;
+       de = readdir(dir);
+       if (!de) {
+               if (errno) {
+                       warning_errno("error reading directory '%s'",
+                                     path);
+                       return -1;
+               }
+
+               return 1;
+       }
+
+       if (is_dot_or_dotdot(de->d_name))
+               goto repeat;
+
+       *out = de;
+       return 0;
+}
+
 /*
  * Push a level in the iter stack and initialize it with information from
  * the directory pointed by iter->base->path. It is assumed that this
@@ -72,6 +106,35 @@ static int push_level(struct dir_iterator_int *iter)
                return -1;
        }
 
+       string_list_init_dup(&level->entries);
+       level->entries_idx = 0;
+
+       /*
+        * When the iterator is sorted we read and sort all directory entries
+        * directly.
+        */
+       if (iter->flags & DIR_ITERATOR_SORTED) {
+               struct dirent *de;
+
+               while (1) {
+                       int ret = next_directory_entry(level->dir, iter->base.path.buf, &de);
+                       if (ret < 0) {
+                               if (errno != ENOENT &&
+                                   iter->flags & DIR_ITERATOR_PEDANTIC)
+                                       return -1;
+                               continue;
+                       } else if (ret > 0) {
+                               break;
+                       }
+
+                       string_list_append(&level->entries, de->d_name);
+               }
+               string_list_sort(&level->entries);
+
+               closedir(level->dir);
+               level->dir = NULL;
+       }
+
        return 0;
 }
 
@@ -88,21 +151,22 @@ static int pop_level(struct dir_iterator_int *iter)
                warning_errno("error closing directory '%s'",
                              iter->base.path.buf);
        level->dir = NULL;
+       string_list_clear(&level->entries, 0);
 
        return --iter->levels_nr;
 }
 
 /*
  * Populate iter->base with the necessary information on the next iteration
- * entry, represented by the given dirent de. Return 0 on success and -1
+ * entry, represented by the given name. Return 0 on success and -1
  * otherwise, setting errno accordingly.
  */
 static int prepare_next_entry_data(struct dir_iterator_int *iter,
-                                  struct dirent *de)
+                                  const char *name)
 {
        int err, saved_errno;
 
-       strbuf_addstr(&iter->base.path, de->d_name);
+       strbuf_addstr(&iter->base.path, name);
        /*
         * We have to reset these because the path strbuf might have
         * been realloc()ed at the previous strbuf_addstr().
@@ -139,27 +203,34 @@ int dir_iterator_advance(struct dir_iterator *dir_iterator)
                struct dirent *de;
                struct dir_iterator_level *level =
                        &iter->levels[iter->levels_nr - 1];
+               const char *name;
 
                strbuf_setlen(&iter->base.path, level->prefix_len);
-               errno = 0;
-               de = readdir(level->dir);
 
-               if (!de) {
-                       if (errno) {
-                               warning_errno("error reading directory '%s'",
-                                             iter->base.path.buf);
+               if (level->dir) {
+                       int ret = next_directory_entry(level->dir, iter->base.path.buf, &de);
+                       if (ret < 0) {
                                if (iter->flags & DIR_ITERATOR_PEDANTIC)
                                        goto error_out;
-                       } else if (pop_level(iter) == 0) {
-                               return dir_iterator_abort(dir_iterator);
+                               continue;
+                       } else if (ret > 0) {
+                               if (pop_level(iter) == 0)
+                                       return dir_iterator_abort(dir_iterator);
+                               continue;
                        }
-                       continue;
-               }
 
-               if (is_dot_or_dotdot(de->d_name))
-                       continue;
+                       name = de->d_name;
+               } else {
+                       if (level->entries_idx >= level->entries.nr) {
+                               if (pop_level(iter) == 0)
+                                       return dir_iterator_abort(dir_iterator);
+                               continue;
+                       }
 
-               if (prepare_next_entry_data(iter, de)) {
+                       name = level->entries.items[level->entries_idx++].string;
+               }
+
+               if (prepare_next_entry_data(iter, name)) {
                        if (errno != ENOENT && iter->flags & DIR_ITERATOR_PEDANTIC)
                                goto error_out;
                        continue;
@@ -188,6 +259,8 @@ int dir_iterator_abort(struct dir_iterator *dir_iterator)
                        warning_errno("error closing directory '%s'",
                                      iter->base.path.buf);
                }
+
+               string_list_clear(&level->entries, 0);
        }
 
        free(iter->levels);
index 479e1ec784dfa4081430ea5a104302e79d10eae3..6d438809b6ed51b5735080f878c08aa2302b7552 100644 (file)
  *   and ITER_ERROR is returned immediately. In both cases, a meaningful
  *   warning is emitted. Note: ENOENT errors are always ignored so that
  *   the API users may remove files during iteration.
+ *
+ * - DIR_ITERATOR_SORTED: sort directory entries alphabetically.
  */
 #define DIR_ITERATOR_PEDANTIC (1 << 0)
+#define DIR_ITERATOR_SORTED   (1 << 1)
 
 struct dir_iterator {
        /* The current path: */
index 90632a39bc995af8bf56166b4d89bba5d6dd5272..60706ea3987f2c4d11e5e2e2d21aa5cd5e9196e1 100644 (file)
@@ -207,6 +207,9 @@ void setup_git_env(const char *git_dir)
        shallow_file = getenv(GIT_SHALLOW_FILE_ENVIRONMENT);
        if (shallow_file)
                set_alternate_shallow_file(the_repository, shallow_file, 0);
+
+       if (git_env_bool(NO_LAZY_FETCH_ENVIRONMENT, 0))
+               fetch_if_missing = 0;
 }
 
 int is_bare_repository(void)
index e5351c9dd95ea6e7afe77b1db466d6ab30310491..5cec19cecc1a5a2c057f807f3b96dd2b29f0b355 100644 (file)
@@ -36,6 +36,7 @@ const char *getenv_safe(struct strvec *argv, const char *name);
 #define CEILING_DIRECTORIES_ENVIRONMENT "GIT_CEILING_DIRECTORIES"
 #define NO_REPLACE_OBJECTS_ENVIRONMENT "GIT_NO_REPLACE_OBJECTS"
 #define GIT_REPLACE_REF_BASE_ENVIRONMENT "GIT_REPLACE_REF_BASE"
+#define NO_LAZY_FETCH_ENVIRONMENT "GIT_NO_LAZY_FETCH"
 #define GITATTRIBUTES_FILE ".gitattributes"
 #define INFOATTRIBUTES_FILE "info/attributes"
 #define ATTRIBUTE_MACRO_PREFIX "[attr]"
index e4e820e68095928765940be51fba0cdb8e5d609c..dd0c9a5b7f2b078205000f3051df0bade8b0cfab 100755 (executable)
@@ -91,6 +91,19 @@ then
        # ignore the error from the above --- run_merge_tool
        # will diagnose unusable tool by itself
        run_merge_tool "$merge_tool" false
+
+       status=$?
+       if test $status -ge 126
+       then
+               # Command not found (127), not executable (126) or
+               # exited via a signal (>= 128).
+               exit $status
+       fi
+
+       if test "$GIT_DIFFTOOL_TRUST_EXIT_CODE" = true
+       then
+               exit $status
+       fi
 else
        # Launch the merge tool on each path provided by 'git diff'
        while test $# -gt 6
diff --git a/git.c b/git.c
index 7068a184b0a2947c6d62c3331c6f1a418708c7be..5265f920f165d99f01584eed54554dd51563acca 100644 (file)
--- a/git.c
+++ b/git.c
@@ -4,6 +4,7 @@
 #include "exec-cmd.h"
 #include "gettext.h"
 #include "help.h"
+#include "object-file.h"
 #include "pager.h"
 #include "read-cache-ll.h"
 #include "run-command.h"
@@ -186,6 +187,11 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
                        use_pager = 0;
                        if (envchanged)
                                *envchanged = 1;
+               } else if (!strcmp(cmd, "--no-lazy-fetch")) {
+                       fetch_if_missing = 0;
+                       setenv(NO_LAZY_FETCH_ENVIRONMENT, "1", 1);
+                       if (envchanged)
+                               *envchanged = 1;
                } else if (!strcmp(cmd, "--no-replace-objects")) {
                        disable_replace_refs();
                        setenv(NO_REPLACE_OBJECTS_ENVIRONMENT, "1", 1);
index 12d111374107a7a071ac90d035c5172af2b63c79..33db41bfac5f977747ce7af864207df3f255e491 100644 (file)
@@ -1575,8 +1575,11 @@ static int verify_merge_base(struct object_id *head_oid, struct ref *remote)
        struct commit *head = lookup_commit_or_die(head_oid, "HEAD");
        struct commit *branch = lookup_commit_or_die(&remote->old_oid,
                                                     remote->name);
+       int ret = repo_in_merge_bases(the_repository, branch, head);
 
-       return repo_in_merge_bases(the_repository, branch, head);
+       if (ret < 0)
+               exit(128);
+       return ret;
 }
 
 static int delete_remote_branch(const char *pattern, int force)
index da287cc8e0dd2935cb230dd256d5f268495ed40f..4346f8da4560fd518d73db7969faa9127e302090 100644 (file)
@@ -711,15 +711,6 @@ static void filter_combine__free(void *filter_data)
        free(d);
 }
 
-static void add_all(struct oidset *dest, struct oidset *src) {
-       struct oidset_iter iter;
-       struct object_id *src_oid;
-
-       oidset_iter_init(src, &iter);
-       while ((src_oid = oidset_iter_next(&iter)) != NULL)
-               oidset_insert(dest, src_oid);
-}
-
 static void filter_combine__finalize_omits(
        struct oidset *omits,
        void *filter_data)
@@ -728,7 +719,7 @@ static void filter_combine__finalize_omits(
        size_t sub;
 
        for (sub = 0; sub < d->nr; sub++) {
-               add_all(omits, &d->sub[sub].omits);
+               oidset_insert_from_set(omits, &d->sub[sub].omits);
                oidset_clear(&d->sub[sub].omits);
        }
 }
index 337b9334cdbafe0d3ffd2bf2fd36898f6d112410..e5438b029d90f352f0c434ebc8088c01fce11f3a 100644 (file)
@@ -1011,7 +1011,7 @@ static int do_remerge_diff(struct rev_info *opt,
                           struct object_id *oid)
 {
        struct merge_options o;
-       struct commit_list *bases;
+       struct commit_list *bases = NULL;
        struct merge_result res = {0};
        struct pretty_print_context ctx = {0};
        struct commit *parent1 = parents->item;
@@ -1036,7 +1036,8 @@ static int do_remerge_diff(struct rev_info *opt,
        /* Parse the relevant commits and get the merge bases */
        parse_commit_or_die(parent1);
        parse_commit_or_die(parent2);
-       bases = repo_get_merge_bases(the_repository, parent1, parent2);
+       if (repo_get_merge_bases(the_repository, parent1, parent2, &bases) < 0)
+               exit(128);
 
        /* Re-merge the parents */
        merge_incore_recursive(&o, bases, parent1, parent2, &res);
index c7d62560201984db8efcb675da9b4ede933c5cbd..2078c22b097a61c97faddb6689a75e6c10ccb9ee 100644 (file)
@@ -107,6 +107,45 @@ void *mem_pool_alloc(struct mem_pool *pool, size_t len)
        return r;
 }
 
+static char *mem_pool_strvfmt(struct mem_pool *pool, const char *fmt,
+                             va_list ap)
+{
+       struct mp_block *block = pool->mp_block;
+       char *next_free = block ? block->next_free : NULL;
+       size_t available = block ? block->end - block->next_free : 0;
+       va_list cp;
+       int len, len2;
+       char *ret;
+
+       va_copy(cp, ap);
+       len = vsnprintf(next_free, available, fmt, cp);
+       va_end(cp);
+       if (len < 0)
+               BUG("your vsnprintf is broken (returned %d)", len);
+
+       ret = mem_pool_alloc(pool, len + 1);  /* 1 for NUL */
+
+       /* Shortcut; relies on mem_pool_alloc() not touching buffer contents. */
+       if (ret == next_free)
+               return ret;
+
+       len2 = vsnprintf(ret, len + 1, fmt, ap);
+       if (len2 != len)
+               BUG("your vsnprintf is broken (returns inconsistent lengths)");
+       return ret;
+}
+
+char *mem_pool_strfmt(struct mem_pool *pool, const char *fmt, ...)
+{
+       va_list ap;
+       char *ret;
+
+       va_start(ap, fmt);
+       ret = mem_pool_strvfmt(pool, fmt, ap);
+       va_end(ap);
+       return ret;
+}
+
 void *mem_pool_calloc(struct mem_pool *pool, size_t count, size_t size)
 {
        size_t len = st_mult(count, size);
index fe7507f022bba40d74aab341e99269ead7171695..d1c66413ec322104f6199cc2248466c734cee868 100644 (file)
@@ -47,6 +47,11 @@ void *mem_pool_calloc(struct mem_pool *pool, size_t count, size_t size);
 char *mem_pool_strdup(struct mem_pool *pool, const char *str);
 char *mem_pool_strndup(struct mem_pool *pool, const char *str, size_t len);
 
+/*
+ * Allocate memory from the memory pool and format a string into it.
+ */
+char *mem_pool_strfmt(struct mem_pool *pool, const char *fmt, ...);
+
 /*
  * Move the memory associated with the 'src' pool to the 'dst' pool. The 'src'
  * pool will be empty and not contain any memory. It still needs to be free'd
index 8617babee41cb59aa5b3dac97a55a53f92e7bc20..201f8f77755b439ad1b91b5b692c16aaeba5d032 100644 (file)
@@ -18,6 +18,7 @@
 #include "merge-ort.h"
 
 #include "alloc.h"
+#include "advice.h"
 #include "attr.h"
 #include "cache-tree.h"
 #include "commit.h"
@@ -542,6 +543,7 @@ enum conflict_and_info_types {
        CONFLICT_SUBMODULE_HISTORY_NOT_AVAILABLE,
        CONFLICT_SUBMODULE_MAY_HAVE_REWINDS,
        CONFLICT_SUBMODULE_NULL_MERGE_BASE,
+       CONFLICT_SUBMODULE_CORRUPT,
 
        /* Keep this entry _last_ in the list */
        NB_CONFLICT_TYPES,
@@ -594,7 +596,9 @@ static const char *type_short_descriptions[] = {
        [CONFLICT_SUBMODULE_MAY_HAVE_REWINDS] =
                "CONFLICT (submodule may have rewinds)",
        [CONFLICT_SUBMODULE_NULL_MERGE_BASE] =
-               "CONFLICT (submodule lacks merge base)"
+               "CONFLICT (submodule lacks merge base)",
+       [CONFLICT_SUBMODULE_CORRUPT] =
+               "CONFLICT (submodule corrupt)"
 };
 
 struct logical_conflict_info {
@@ -1657,9 +1661,10 @@ static int collect_merge_info(struct merge_options *opt,
        info.data = opt;
        info.show_all_errors = 1;
 
-       parse_tree(merge_base);
-       parse_tree(side1);
-       parse_tree(side2);
+       if (parse_tree(merge_base) < 0 ||
+           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);
@@ -1708,7 +1713,14 @@ static int find_first_merges(struct repository *repo,
                die("revision walk setup failed");
        while ((commit = get_revision(&revs)) != NULL) {
                struct object *o = &(commit->object);
-               if (repo_in_merge_bases(repo, b, commit))
+               int ret = repo_in_merge_bases(repo, b, commit);
+
+               if (ret < 0) {
+                       object_array_clear(&merges);
+                       release_revisions(&revs);
+                       return ret;
+               }
+               if (ret > 0)
                        add_object_array(o, NULL, &merges);
        }
        reset_revision_walk();
@@ -1723,9 +1735,17 @@ static int find_first_merges(struct repository *repo,
                contains_another = 0;
                for (j = 0; j < merges.nr; j++) {
                        struct commit *m2 = (struct commit *) merges.objects[j].item;
-                       if (i != j && repo_in_merge_bases(repo, m2, m1)) {
-                               contains_another = 1;
-                               break;
+                       if (i != j) {
+                               int ret = repo_in_merge_bases(repo, m2, m1);
+                               if (ret < 0) {
+                                       object_array_clear(&merges);
+                                       release_revisions(&revs);
+                                       return ret;
+                               }
+                               if (ret > 0) {
+                                       contains_another = 1;
+                                       break;
+                               }
                        }
                }
 
@@ -1747,7 +1767,7 @@ static int merge_submodule(struct merge_options *opt,
 {
        struct repository subrepo;
        struct strbuf sb = STRBUF_INIT;
-       int ret = 0;
+       int ret = 0, ret2;
        struct commit *commit_o, *commit_a, *commit_b;
        int parent_count;
        struct object_array merges;
@@ -1794,8 +1814,28 @@ static int merge_submodule(struct merge_options *opt,
        }
 
        /* check whether both changes are forward */
-       if (!repo_in_merge_bases(&subrepo, commit_o, commit_a) ||
-           !repo_in_merge_bases(&subrepo, commit_o, commit_b)) {
+       ret2 = repo_in_merge_bases(&subrepo, commit_o, commit_a);
+       if (ret2 < 0) {
+               path_msg(opt, CONFLICT_SUBMODULE_CORRUPT, 0,
+                        path, NULL, NULL, NULL,
+                        _("Failed to merge submodule %s "
+                          "(repository corrupt)"),
+                        path);
+               ret = -1;
+               goto cleanup;
+       }
+       if (ret2 > 0)
+               ret2 = repo_in_merge_bases(&subrepo, commit_o, commit_b);
+       if (ret2 < 0) {
+               path_msg(opt, CONFLICT_SUBMODULE_CORRUPT, 0,
+                        path, NULL, NULL, NULL,
+                        _("Failed to merge submodule %s "
+                          "(repository corrupt)"),
+                        path);
+               ret = -1;
+               goto cleanup;
+       }
+       if (!ret2) {
                path_msg(opt, CONFLICT_SUBMODULE_MAY_HAVE_REWINDS, 0,
                         path, NULL, NULL, NULL,
                         _("Failed to merge submodule %s "
@@ -1805,7 +1845,17 @@ static int merge_submodule(struct merge_options *opt,
        }
 
        /* Case #1: a is contained in b or vice versa */
-       if (repo_in_merge_bases(&subrepo, commit_a, commit_b)) {
+       ret2 = repo_in_merge_bases(&subrepo, commit_a, commit_b);
+       if (ret2 < 0) {
+               path_msg(opt, CONFLICT_SUBMODULE_CORRUPT, 0,
+                        path, NULL, NULL, NULL,
+                        _("Failed to merge submodule %s "
+                          "(repository corrupt)"),
+                        path);
+               ret = -1;
+               goto cleanup;
+       }
+       if (ret2 > 0) {
                oidcpy(result, b);
                path_msg(opt, INFO_SUBMODULE_FAST_FORWARDING, 1,
                         path, NULL, NULL, NULL,
@@ -1814,7 +1864,17 @@ static int merge_submodule(struct merge_options *opt,
                ret = 1;
                goto cleanup;
        }
-       if (repo_in_merge_bases(&subrepo, commit_b, commit_a)) {
+       ret2 = repo_in_merge_bases(&subrepo, commit_b, commit_a);
+       if (ret2 < 0) {
+               path_msg(opt, CONFLICT_SUBMODULE_CORRUPT, 0,
+                        path, NULL, NULL, NULL,
+                        _("Failed to merge submodule %s "
+                          "(repository corrupt)"),
+                        path);
+               ret = -1;
+               goto cleanup;
+       }
+       if (ret2 > 0) {
                oidcpy(result, a);
                path_msg(opt, INFO_SUBMODULE_FAST_FORWARDING, 1,
                         path, NULL, NULL, NULL,
@@ -1839,6 +1899,14 @@ static int merge_submodule(struct merge_options *opt,
        parent_count = find_first_merges(&subrepo, path, commit_a, commit_b,
                                         &merges);
        switch (parent_count) {
+       case -1:
+               path_msg(opt, CONFLICT_SUBMODULE_CORRUPT, 0,
+                        path, NULL, NULL, NULL,
+                        _("Failed to merge submodule %s "
+                          "(repository corrupt)"),
+                        path);
+               ret = -1;
+               break;
        case 0:
                path_msg(opt, CONFLICT_SUBMODULE_FAILED_TO_MERGE, 0,
                         path, NULL, NULL, NULL,
@@ -4376,9 +4444,11 @@ static int checkout(struct merge_options *opt,
        unpack_opts.verbose_update = (opt->verbosity > 2);
        unpack_opts.fn = twoway_merge;
        unpack_opts.preserve_ignored = 0; /* FIXME: !opts->overwrite_ignore */
-       parse_tree(prev);
+       if (parse_tree(prev) < 0)
+               return -1;
        init_tree_desc(&trees[0], prev->buffer, prev->size);
-       parse_tree(next);
+       if (parse_tree(next) < 0)
+               return -1;
        init_tree_desc(&trees[1], next->buffer, next->size);
 
        ret = unpack_trees(2, trees, &unpack_opts);
@@ -4556,7 +4626,7 @@ static void print_submodule_conflict_suggestion(struct string_list *csub) {
                      " - commit the resulting index in the superproject\n"),
                    tmp.buf, subs.buf);
 
-       printf("%s", msg.buf);
+       advise_if_enabled(ADVICE_SUBMODULE_MERGE_CONFLICT, "%s", msg.buf);
 
        strbuf_release(&subs);
        strbuf_release(&tmp);
@@ -4982,6 +5052,9 @@ redo:
 
        if (result->clean >= 0) {
                result->tree = parse_tree_indirect(&working_tree_oid);
+               if (!result->tree)
+                       die(_("unable to read tree (%s)"),
+                           oid_to_hex(&working_tree_oid));
                /* existence of conflicted entries implies unclean */
                result->clean &= strmap_empty(&opt->priv->conflicted);
        }
@@ -5007,7 +5080,11 @@ static void merge_ort_internal(struct merge_options *opt,
        struct strbuf merge_base_abbrev = STRBUF_INIT;
 
        if (!merge_bases) {
-               merge_bases = repo_get_merge_bases(the_repository, h1, h2);
+               if (repo_get_merge_bases(the_repository, h1, h2,
+                                        &merge_bases) < 0) {
+                       result->clean = -1;
+                       return;
+               }
                /* See merge-ort.h:merge_incore_recursive() declaration NOTE */
                merge_bases = reverse_commit_list(merge_bases);
        }
index a0c3e7a2d9105dd895f0ae2613d0f87f00b8d794..103ee321aeb8cd8c169d35316bb20cfd9eea1693 100644 (file)
@@ -405,7 +405,8 @@ static inline int merge_detect_rename(struct merge_options *opt)
 
 static void init_tree_desc_from_tree(struct tree_desc *desc, struct tree *tree)
 {
-       parse_tree(tree);
+       if (parse_tree(tree) < 0)
+               exit(128);
        init_tree_desc(desc, tree->buffer, tree->size);
 }
 
@@ -1139,7 +1140,13 @@ static int find_first_merges(struct repository *repo,
                die("revision walk setup failed");
        while ((commit = get_revision(&revs)) != NULL) {
                struct object *o = &(commit->object);
-               if (repo_in_merge_bases(repo, b, commit))
+               int ret = repo_in_merge_bases(repo, b, commit);
+               if (ret < 0) {
+                       object_array_clear(&merges);
+                       release_revisions(&revs);
+                       return ret;
+               }
+               if (ret)
                        add_object_array(o, NULL, &merges);
        }
        reset_revision_walk();
@@ -1154,9 +1161,17 @@ static int find_first_merges(struct repository *repo,
                contains_another = 0;
                for (j = 0; j < merges.nr; j++) {
                        struct commit *m2 = (struct commit *) merges.objects[j].item;
-                       if (i != j && repo_in_merge_bases(repo, m2, m1)) {
-                               contains_another = 1;
-                               break;
+                       if (i != j) {
+                               int ret = repo_in_merge_bases(repo, m2, m1);
+                               if (ret < 0) {
+                                       object_array_clear(&merges);
+                                       release_revisions(&revs);
+                                       return ret;
+                               }
+                               if (ret > 0) {
+                                       contains_another = 1;
+                                       break;
+                               }
                        }
                }
 
@@ -1192,7 +1207,7 @@ static int merge_submodule(struct merge_options *opt,
                           const struct object_id *b)
 {
        struct repository subrepo;
-       int ret = 0;
+       int ret = 0, ret2;
        struct commit *commit_base, *commit_a, *commit_b;
        int parent_count;
        struct object_array merges;
@@ -1229,14 +1244,32 @@ static int merge_submodule(struct merge_options *opt,
        }
 
        /* check whether both changes are forward */
-       if (!repo_in_merge_bases(&subrepo, commit_base, commit_a) ||
-           !repo_in_merge_bases(&subrepo, commit_base, commit_b)) {
+       ret2 = repo_in_merge_bases(&subrepo, commit_base, commit_a);
+       if (ret2 < 0) {
+               output(opt, 1, _("Failed to merge submodule %s (repository corrupt)"), path);
+               ret = -1;
+               goto cleanup;
+       }
+       if (ret2 > 0)
+               ret2 = repo_in_merge_bases(&subrepo, commit_base, commit_b);
+       if (ret2 < 0) {
+               output(opt, 1, _("Failed to merge submodule %s (repository corrupt)"), path);
+               ret = -1;
+               goto cleanup;
+       }
+       if (!ret2) {
                output(opt, 1, _("Failed to merge submodule %s (commits don't follow merge-base)"), path);
                goto cleanup;
        }
 
        /* Case #1: a is contained in b or vice versa */
-       if (repo_in_merge_bases(&subrepo, commit_a, commit_b)) {
+       ret2 = repo_in_merge_bases(&subrepo, commit_a, commit_b);
+       if (ret2 < 0) {
+               output(opt, 1, _("Failed to merge submodule %s (repository corrupt)"), path);
+               ret = -1;
+               goto cleanup;
+       }
+       if (ret2) {
                oidcpy(result, b);
                if (show(opt, 3)) {
                        output(opt, 3, _("Fast-forwarding submodule %s to the following commit:"), path);
@@ -1249,7 +1282,13 @@ static int merge_submodule(struct merge_options *opt,
                ret = 1;
                goto cleanup;
        }
-       if (repo_in_merge_bases(&subrepo, commit_b, commit_a)) {
+       ret2 = repo_in_merge_bases(&subrepo, commit_b, commit_a);
+       if (ret2 < 0) {
+               output(opt, 1, _("Failed to merge submodule %s (repository corrupt)"), path);
+               ret = -1;
+               goto cleanup;
+       }
+       if (ret2) {
                oidcpy(result, a);
                if (show(opt, 3)) {
                        output(opt, 3, _("Fast-forwarding submodule %s to the following commit:"), path);
@@ -1278,6 +1317,10 @@ static int merge_submodule(struct merge_options *opt,
        parent_count = find_first_merges(&subrepo, &merges, path,
                                         commit_a, commit_b);
        switch (parent_count) {
+       case -1:
+               output(opt, 1,_("Failed to merge submodule %s (repository corrupt)"), path);
+               ret = -1;
+               break;
        case 0:
                output(opt, 1, _("Failed to merge submodule %s (merge following commits not found)"), path);
                break;
@@ -1392,11 +1435,14 @@ static int merge_mode_and_contents(struct merge_options *opt,
                        /* FIXME: bug, what if modes didn't match? */
                        result->clean = (merge_status == 0);
                } else if (S_ISGITLINK(a->mode)) {
-                       result->clean = merge_submodule(opt, &result->blob.oid,
-                                                       o->path,
-                                                       &o->oid,
-                                                       &a->oid,
-                                                       &b->oid);
+                       int clean = merge_submodule(opt, &result->blob.oid,
+                                                   o->path,
+                                                   &o->oid,
+                                                   &a->oid,
+                                                   &b->oid);
+                       if (clean < 0)
+                               return -1;
+                       result->clean = clean;
                } else if (S_ISLNK(a->mode)) {
                        switch (opt->recursive_variant) {
                        case MERGE_VARIANT_NORMAL:
@@ -3597,7 +3643,9 @@ static int merge_recursive_internal(struct merge_options *opt,
        }
 
        if (!merge_bases) {
-               merge_bases = repo_get_merge_bases(the_repository, h1, h2);
+               if (repo_get_merge_bases(the_repository, h1, h2,
+                                        &merge_bases) < 0)
+                       return -1;
                merge_bases = reverse_commit_list(merge_bases);
        }
 
diff --git a/merge.c b/merge.c
index ca89b312d173530f8c8a1e5d07470b9d2404e9d4..563281b10f1d04bd6b51a79f2852a61aa49552fd 100644 (file)
--- a/merge.c
+++ b/merge.c
@@ -77,7 +77,10 @@ int checkout_fast_forward(struct repository *r,
                return -1;
        }
        for (i = 0; i < nr_trees; i++) {
-               parse_tree(trees[i]);
+               if (parse_tree(trees[i]) < 0) {
+                       rollback_lock_file(&lock_file);
+                       return -1;
+               }
                init_tree_desc(t+i, trees[i]->buffer, trees[i]->size);
        }
 
index 06937acbf5497115c9ba4d9a2377634b1885db7e..97e376329bf510fce8c70c89a6de7a5465190d2b 100644 (file)
@@ -371,9 +371,17 @@ diff_cmd_help () {
 
 
 merge_cmd () {
-       layout=$(git config mergetool.vimdiff.layout)
+       TOOL=$1
 
-       case "$1" in
+       layout=$(git config "mergetool.$TOOL.layout")
+
+       # backward compatibility:
+       if test -z "$layout"
+       then
+               layout=$(git config mergetool.vimdiff.layout)
+       fi
+
+       case "$TOOL" in
        *vimdiff)
                if test -z "$layout"
                then
diff --git a/neue b/neue
deleted file mode 100644 (file)
index e69de29..0000000
index 8799b522a55f31869dcc8cbfd7229cc8db65af4c..51282934ae62b8e7daefcf8202b98e006c416c07 100644 (file)
@@ -607,7 +607,8 @@ int notes_merge(struct notes_merge_options *o,
        assert(local && remote);
 
        /* Find merge bases */
-       bases = repo_get_merge_bases(the_repository, local, remote);
+       if (repo_get_merge_bases(the_repository, local, remote, &bases) < 0)
+               exit(128);
        if (!bases) {
                base_oid = null_oid();
                base_tree_oid = the_hash_algo->empty_tree;
index 3a2ef5d6800173fa669bdfcb2612bf21a7c6417a..bd77695d7eac539ff114c4b6a031cdd81931d7d8 100644 (file)
@@ -1034,6 +1034,15 @@ static int get_oid_basic(struct repository *r, const char *str, int len,
                                                len, str,
                                                show_date(co_time, co_tz, DATE_MODE(RFC2822)));
                                }
+                       } else if (nth == co_cnt && !is_null_oid(oid)) {
+                               /*
+                                * We were asked for the Nth reflog (counting
+                                * from 0), but there were only N entries.
+                                * read_ref_at() will have returned "1" to tell
+                                * us it did not find an entry, but it did
+                                * still fill in the oid with the "old" value,
+                                * which we can use.
+                                */
                        } else {
                                if (flags & GET_OID_QUIETLY) {
                                        exit(128);
@@ -1479,7 +1488,7 @@ int repo_get_oid_mb(struct repository *r,
                    struct object_id *oid)
 {
        struct commit *one, *two;
-       struct commit_list *mbs;
+       struct commit_list *mbs = NULL;
        struct object_id oid_tmp;
        const char *dots;
        int st;
@@ -1507,7 +1516,10 @@ int repo_get_oid_mb(struct repository *r,
        two = lookup_commit_reference_gently(r, &oid_tmp, 0);
        if (!two)
                return -1;
-       mbs = repo_get_merge_bases(r, one, two);
+       if (repo_get_merge_bases(r, one, two, &mbs) < 0) {
+               free_commit_list(mbs);
+               return -1;
+       }
        if (!mbs || mbs->next)
                st = -1;
        else {
index e6a1c4d9059ab398f06f042a7b7cdd953ff6a943..f11c59ac0cf4d41e3c19f48e04af47237797db03 100644 (file)
--- a/object.c
+++ b/object.c
@@ -271,6 +271,7 @@ struct object *parse_object_with_flags(struct repository *r,
                                       enum parse_object_flags flags)
 {
        int skip_hash = !!(flags & PARSE_OBJECT_SKIP_HASH_CHECK);
+       int discard_tree = !!(flags & PARSE_OBJECT_DISCARD_TREE);
        unsigned long size;
        enum object_type type;
        int eaten;
@@ -298,6 +299,17 @@ struct object *parse_object_with_flags(struct repository *r,
                return lookup_object(r, oid);
        }
 
+       /*
+        * If the caller does not care about the tree buffer and does not
+        * care about checking the hash, we can simply verify that we
+        * have the on-disk object with the correct type.
+        */
+       if (skip_hash && discard_tree &&
+           (!obj || obj->type == OBJ_TREE) &&
+           oid_object_info(r, oid, NULL) == OBJ_TREE) {
+               return &lookup_tree(r, oid)->object;
+       }
+
        buffer = repo_read_object_file(r, oid, &type, &size);
        if (buffer) {
                if (!skip_hash &&
@@ -311,6 +323,8 @@ struct object *parse_object_with_flags(struct repository *r,
                                          buffer, &eaten);
                if (!eaten)
                        free(buffer);
+               if (discard_tree && type == OBJ_TREE)
+                       free_tree_buffer((struct tree *)obj);
                return obj;
        }
        return NULL;
index 114d45954d082229ed747dee16f2510387145771..c7123cade622cd2a5b6aafc99ad924aafeaaa301 100644 (file)
--- a/object.h
+++ b/object.h
@@ -197,6 +197,7 @@ void *object_as_type(struct object *obj, enum object_type type, int quiet);
  */
 enum parse_object_flags {
        PARSE_OBJECT_SKIP_HASH_CHECK = 1 << 0,
+       PARSE_OBJECT_DISCARD_TREE = 1 << 1,
 };
 struct object *parse_object(struct repository *r, const struct object_id *oid);
 struct object *parse_object_with_flags(struct repository *r,
index d1e5376316ecd5f9dcf549e1067697283bdc712c..91d13859106a1bc56632aeedd4030a007608a359 100644 (file)
--- a/oidset.c
+++ b/oidset.c
@@ -23,6 +23,16 @@ int oidset_insert(struct oidset *set, const struct object_id *oid)
        return !added;
 }
 
+void oidset_insert_from_set(struct oidset *dest, struct oidset *src)
+{
+       struct oidset_iter iter;
+       struct object_id *src_oid;
+
+       oidset_iter_init(src, &iter);
+       while ((src_oid = oidset_iter_next(&iter)))
+               oidset_insert(dest, src_oid);
+}
+
 int oidset_remove(struct oidset *set, const struct object_id *oid)
 {
        khiter_t pos = kh_get_oid_set(&set->set, *oid);
index ba4a5a2cd3a7a233bc9ca2cb7cf4a58a1e5a122c..262f4256d6ac5ad39e1cef4a39e36716e817d651 100644 (file)
--- a/oidset.h
+++ b/oidset.h
@@ -47,6 +47,12 @@ int oidset_contains(const struct oidset *set, const struct object_id *oid);
  */
 int oidset_insert(struct oidset *set, const struct object_id *oid);
 
+/**
+ * Insert all the oids that are in set 'src' into set 'dest'; a copy
+ * is made of each oid inserted into set 'dest'.
+ */
+void oidset_insert_from_set(struct oidset *dest, struct oidset *src);
+
 /**
  * Remove the oid from the set.
  *
index be14b56e32489dd76de97a099059aab95c8fef0c..0ec29f7385480ad4ee71a6cc684307b2bf19c814 100644 (file)
@@ -2628,6 +2628,12 @@ static int for_each_fullref_in_pattern(struct ref_filter *filter,
                                       each_ref_fn cb,
                                       void *cb_data)
 {
+       if (filter->kind == FILTER_REFS_KIND_MASK) {
+               /* In this case, we want to print all refs including root refs. */
+               return refs_for_each_include_root_refs(get_main_ref_store(the_repository),
+                                                      cb, cb_data);
+       }
+
        if (!filter->match_as_path) {
                /*
                 * in this case, the patterns are applied after
@@ -2750,6 +2756,9 @@ static int ref_kind_from_refname(const char *refname)
                        return ref_kind[i].kind;
        }
 
+       if (is_pseudoref(get_main_ref_store(the_repository), refname))
+               return FILTER_REFS_PSEUDOREFS;
+
        return FILTER_REFS_OTHERS;
 }
 
@@ -2781,7 +2790,16 @@ static struct ref_array_item *apply_ref_filter(const char *refname, const struct
 
        /* Obtain the current ref kind from filter_ref_kind() and ignore unwanted refs. */
        kind = filter_ref_kind(filter, refname);
-       if (!(kind & filter->kind))
+
+       /*
+        * Generally HEAD refs are printed with special description denoting a rebase,
+        * detached state and so forth. This is useful when only printing the HEAD ref
+        * But when it is being printed along with other pseudorefs, it makes sense to
+        * keep the formatting consistent. So we mask the type to act like a pseudoref.
+        */
+       if (filter->kind == FILTER_REFS_KIND_MASK && kind == FILTER_REFS_DETACHED_HEAD)
+               kind = FILTER_REFS_PSEUDOREFS;
+       else if (!(kind & filter->kind))
                return NULL;
 
        if (!filter_pattern_match(filter, refname))
@@ -3047,9 +3065,15 @@ static int do_filter_refs(struct ref_filter *filter, unsigned int type, each_ref
                        ret = for_each_fullref_in("refs/remotes/", fn, cb_data);
                else if (filter->kind == FILTER_REFS_TAGS)
                        ret = for_each_fullref_in("refs/tags/", fn, cb_data);
-               else if (filter->kind & FILTER_REFS_ALL)
+               else if (filter->kind & FILTER_REFS_REGULAR)
                        ret = for_each_fullref_in_pattern(filter, fn, cb_data);
-               if (!ret && (filter->kind & FILTER_REFS_DETACHED_HEAD))
+
+               /*
+                * When printing all ref types, HEAD is already included,
+                * so we don't want to print HEAD again.
+                */
+               if (!ret && (filter->kind != FILTER_REFS_KIND_MASK) &&
+                   (filter->kind & FILTER_REFS_DETACHED_HEAD))
                        head_ref(fn, cb_data);
        }
 
index 07cd6f6da3da7e3950dc77538baf0f71950630e1..0ca28d2bba6f2aed4b8d084972739f3a88f44caa 100644 (file)
 #define FILTER_REFS_BRANCHES       0x0004
 #define FILTER_REFS_REMOTES        0x0008
 #define FILTER_REFS_OTHERS         0x0010
-#define FILTER_REFS_ALL            (FILTER_REFS_TAGS | FILTER_REFS_BRANCHES | \
+#define FILTER_REFS_REGULAR        (FILTER_REFS_TAGS | FILTER_REFS_BRANCHES | \
                                    FILTER_REFS_REMOTES | FILTER_REFS_OTHERS)
 #define FILTER_REFS_DETACHED_HEAD  0x0020
-#define FILTER_REFS_KIND_MASK      (FILTER_REFS_ALL | FILTER_REFS_DETACHED_HEAD)
+#define FILTER_REFS_PSEUDOREFS     0x0040
+#define FILTER_REFS_ROOT_REFS      (FILTER_REFS_DETACHED_HEAD | FILTER_REFS_PSEUDOREFS)
+#define FILTER_REFS_KIND_MASK      (FILTER_REFS_REGULAR | FILTER_REFS_DETACHED_HEAD | \
+                                   FILTER_REFS_PSEUDOREFS)
 
 struct atom_value;
 struct ref_sorting;
diff --git a/refs.c b/refs.c
index fff343c25639911900249b418ceda3e5b8003175..55d2e0b2cb9e959443e98eb329fdf97eff9073a9 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -860,6 +860,47 @@ static int is_pseudoref_syntax(const char *refname)
        return 1;
 }
 
+int is_pseudoref(struct ref_store *refs, const char *refname)
+{
+       static const char *const irregular_pseudorefs[] = {
+               "AUTO_MERGE",
+               "BISECT_EXPECTED_REV",
+               "NOTES_MERGE_PARTIAL",
+               "NOTES_MERGE_REF",
+               "MERGE_AUTOSTASH",
+       };
+       struct object_id oid;
+       size_t i;
+
+       if (!is_pseudoref_syntax(refname))
+               return 0;
+
+       if (ends_with(refname, "_HEAD")) {
+               refs_resolve_ref_unsafe(refs, refname,
+                                       RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,
+                                       &oid, NULL);
+               return !is_null_oid(&oid);
+       }
+
+       for (i = 0; i < ARRAY_SIZE(irregular_pseudorefs); i++)
+               if (!strcmp(refname, irregular_pseudorefs[i])) {
+                       refs_resolve_ref_unsafe(refs, refname,
+                                               RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,
+                                               &oid, NULL);
+                       return !is_null_oid(&oid);
+               }
+
+       return 0;
+}
+
+int is_headref(struct ref_store *refs, const char *refname)
+{
+       if (!strcmp(refname, "HEAD"))
+               return refs_ref_exists(refs, refname);
+
+       return 0;
+}
+
 static int is_current_worktree_ref(const char *ref) {
        return is_pseudoref_syntax(ref) || is_per_worktree_ref(ref);
 }
@@ -1039,55 +1080,40 @@ static int read_ref_at_ent(struct object_id *ooid, struct object_id *noid,
                           const char *message, void *cb_data)
 {
        struct read_ref_at_cb *cb = cb_data;
-       int reached_count;
 
        cb->tz = tz;
        cb->date = timestamp;
 
-       /*
-        * It is not possible for cb->cnt == 0 on the first iteration because
-        * that special case is handled in read_ref_at().
-        */
-       if (cb->cnt > 0)
-               cb->cnt--;
-       reached_count = cb->cnt == 0 && !is_null_oid(ooid);
-       if (timestamp <= cb->at_time || reached_count) {
+       if (timestamp <= cb->at_time || cb->cnt == 0) {
                set_read_ref_cutoffs(cb, timestamp, tz, message);
                /*
                 * we have not yet updated cb->[n|o]oid so they still
                 * hold the values for the previous record.
                 */
-               if (!is_null_oid(&cb->ooid) && !oideq(&cb->ooid, noid))
-                       warning(_("log for ref %s has gap after %s"),
+               if (!is_null_oid(&cb->ooid)) {
+                       oidcpy(cb->oid, noid);
+                       if (!oideq(&cb->ooid, noid))
+                               warning(_("log for ref %s has gap after %s"),
                                        cb->refname, show_date(cb->date, cb->tz, DATE_MODE(RFC2822)));
-               if (reached_count)
-                       oidcpy(cb->oid, ooid);
-               else if (!is_null_oid(&cb->ooid) || cb->date == cb->at_time)
+               }
+               else if (cb->date == cb->at_time)
                        oidcpy(cb->oid, noid);
                else if (!oideq(noid, cb->oid))
                        warning(_("log for ref %s unexpectedly ended on %s"),
                                cb->refname, show_date(cb->date, cb->tz,
                                                       DATE_MODE(RFC2822)));
+               cb->reccnt++;
+               oidcpy(&cb->ooid, ooid);
+               oidcpy(&cb->noid, noid);
                cb->found_it = 1;
+               return 1;
        }
        cb->reccnt++;
        oidcpy(&cb->ooid, ooid);
        oidcpy(&cb->noid, noid);
-       return cb->found_it;
-}
-
-static int read_ref_at_ent_newest(struct object_id *ooid UNUSED,
-                                 struct object_id *noid,
-                                 const char *email UNUSED,
-                                 timestamp_t timestamp, int tz,
-                                 const char *message, void *cb_data)
-{
-       struct read_ref_at_cb *cb = cb_data;
-
-       set_read_ref_cutoffs(cb, timestamp, tz, message);
-       oidcpy(cb->oid, noid);
-       /* We just want the first entry */
-       return 1;
+       if (cb->cnt > 0)
+               cb->cnt--;
+       return 0;
 }
 
 static int read_ref_at_ent_oldest(struct object_id *ooid, struct object_id *noid,
@@ -1099,7 +1125,7 @@ static int read_ref_at_ent_oldest(struct object_id *ooid, struct object_id *noid
 
        set_read_ref_cutoffs(cb, timestamp, tz, message);
        oidcpy(cb->oid, ooid);
-       if (is_null_oid(cb->oid))
+       if (cb->at_time && is_null_oid(cb->oid))
                oidcpy(cb->oid, noid);
        /* We just want the first entry */
        return 1;
@@ -1122,14 +1148,24 @@ int read_ref_at(struct ref_store *refs, const char *refname,
        cb.cutoff_cnt = cutoff_cnt;
        cb.oid = oid;
 
-       if (cb.cnt == 0) {
-               refs_for_each_reflog_ent_reverse(refs, refname, read_ref_at_ent_newest, &cb);
-               return 0;
-       }
-
        refs_for_each_reflog_ent_reverse(refs, refname, read_ref_at_ent, &cb);
 
        if (!cb.reccnt) {
+               if (cnt == 0) {
+                       /*
+                        * The caller asked for ref@{0}, and we had no entries.
+                        * It's a bit subtle, but in practice all callers have
+                        * prepped the "oid" field with the current value of
+                        * the ref, which is the most reasonable fallback.
+                        *
+                        * We'll put dummy values into the out-parameters (so
+                        * they're not just uninitialized garbage), and the
+                        * caller can take our return value as a hint that
+                        * we did not find any such reflog.
+                        */
+                       set_read_ref_cutoffs(&cb, 0, 0, "empty reflog");
+                       return 1;
+               }
                if (flags & GET_OID_QUIETLY)
                        exit(128);
                else
@@ -1594,10 +1630,6 @@ struct ref_iterator *refs_ref_iterator_begin(
        if (trim)
                iter = prefix_ref_iterator_begin(iter, "", trim);
 
-       /* Sanity check for subclasses: */
-       if (!iter->ordered)
-               BUG("reference iterator is not ordered");
-
        return iter;
 }
 
@@ -1724,6 +1756,13 @@ int for_each_rawref(each_ref_fn fn, void *cb_data)
        return refs_for_each_rawref(get_main_ref_store(the_repository), fn, cb_data);
 }
 
+int refs_for_each_include_root_refs(struct ref_store *refs, each_ref_fn fn,
+                                   void *cb_data)
+{
+       return do_for_each_ref(refs, "", NULL, fn, 0,
+                              DO_FOR_EACH_INCLUDE_ROOT_REFS, cb_data);
+}
+
 static int qsort_strcmp(const void *va, const void *vb)
 {
        const char *a = *(const char **)va;
@@ -2516,18 +2555,33 @@ cleanup:
        return ret;
 }
 
-int refs_for_each_reflog(struct ref_store *refs, each_ref_fn fn, void *cb_data)
+struct do_for_each_reflog_help {
+       each_reflog_fn *fn;
+       void *cb_data;
+};
+
+static int do_for_each_reflog_helper(struct repository *r UNUSED,
+                                    const char *refname,
+                                    const struct object_id *oid UNUSED,
+                                    int flags,
+                                    void *cb_data)
+{
+       struct do_for_each_reflog_help *hp = cb_data;
+       return hp->fn(refname, hp->cb_data);
+}
+
+int refs_for_each_reflog(struct ref_store *refs, each_reflog_fn fn, void *cb_data)
 {
        struct ref_iterator *iter;
-       struct do_for_each_ref_help hp = { fn, cb_data };
+       struct do_for_each_reflog_help hp = { fn, cb_data };
 
        iter = refs->be->reflog_iterator_begin(refs);
 
        return do_for_each_repo_ref_iterator(the_repository, iter,
-                                            do_for_each_ref_helper, &hp);
+                                            do_for_each_reflog_helper, &hp);
 }
 
-int for_each_reflog(each_ref_fn fn, void *cb_data)
+int for_each_reflog(each_reflog_fn fn, void *cb_data)
 {
        return refs_for_each_reflog(get_main_ref_store(the_repository), fn, cb_data);
 }
diff --git a/refs.h b/refs.h
index 303c5fac4d08b9798fafb6d4c667e9a94bf91e72..298caf6c6184cc3a23acf78d1a0e3dc8c7d8614c 100644 (file)
--- a/refs.h
+++ b/refs.h
@@ -398,6 +398,12 @@ int for_each_namespaced_ref(const char **exclude_patterns,
 int refs_for_each_rawref(struct ref_store *refs, each_ref_fn fn, void *cb_data);
 int for_each_rawref(each_ref_fn fn, void *cb_data);
 
+/*
+ * Iterates over all refs including root refs, i.e. pseudorefs and HEAD.
+ */
+int refs_for_each_include_root_refs(struct ref_store *refs, each_ref_fn fn,
+                                   void *cb_data);
+
 /*
  * Normalizes partial refs to their fully qualified form.
  * Will prepend <prefix> to the <pattern> if it doesn't start with 'refs/'.
@@ -440,7 +446,20 @@ int refs_create_reflog(struct ref_store *refs, const char *refname,
                       struct strbuf *err);
 int safe_create_reflog(const char *refname, struct strbuf *err);
 
-/** Reads log for the value of ref during at_time. **/
+/**
+ * Reads log for the value of ref during at_time (in which case "cnt" should be
+ * negative) or the reflog "cnt" entries from the top (in which case "at_time"
+ * should be 0).
+ *
+ * If we found the reflog entry in question, returns 0 (and details of the
+ * entry can be found in the out-parameters).
+ *
+ * If we ran out of reflog entries, the out-parameters are filled with the
+ * details of the oldest entry we did find, and the function returns 1. Note
+ * that there is one important special case here! If the reflog was empty
+ * and the caller asked for the 0-th cnt, we will return "1" but leave the
+ * "oid" field untouched.
+ **/
 int read_ref_at(struct ref_store *refs,
                const char *refname, unsigned int flags,
                timestamp_t at_time, int cnt,
@@ -534,12 +553,19 @@ int for_each_reflog_ent(const char *refname, each_reflog_ent_fn fn, void *cb_dat
 /* youngest entry first */
 int for_each_reflog_ent_reverse(const char *refname, each_reflog_ent_fn fn, void *cb_data);
 
+/*
+ * The signature for the callback function for the {refs_,}for_each_reflog()
+ * functions below. The memory pointed to by the refname argument is only
+ * guaranteed to be valid for the duration of a single callback invocation.
+ */
+typedef int each_reflog_fn(const char *refname, void *cb_data);
+
 /*
  * Calls the specified function for each reflog file until it returns nonzero,
  * and returns the value. Reflog file order is unspecified.
  */
-int refs_for_each_reflog(struct ref_store *refs, each_ref_fn fn, void *cb_data);
-int for_each_reflog(each_ref_fn fn, void *cb_data);
+int refs_for_each_reflog(struct ref_store *refs, each_reflog_fn fn, void *cb_data);
+int for_each_reflog(each_reflog_fn fn, void *cb_data);
 
 #define REFNAME_ALLOW_ONELEVEL 1
 #define REFNAME_REFSPEC_PATTERN 2
@@ -1023,4 +1049,7 @@ extern struct ref_namespace_info ref_namespace[NAMESPACE__COUNT];
  */
 void update_ref_namespace(enum ref_namespace namespace, char *ref);
 
+int is_pseudoref(struct ref_store *refs, const char *refname);
+int is_headref(struct ref_store *refs, const char *refname);
+
 #endif /* REFS_H */
index 634681ca44e39b86129b2bbd305aa4c1771c3328..c7531b17f096c329353caf1fd456c726cded5ab8 100644 (file)
@@ -181,7 +181,6 @@ static int debug_ref_iterator_advance(struct ref_iterator *ref_iterator)
                trace_printf_key(&trace_refs, "iterator_advance: %s (0)\n",
                        diter->iter->refname);
 
-       diter->base.ordered = diter->iter->ordered;
        diter->base.refname = diter->iter->refname;
        diter->base.oid = diter->iter->oid;
        diter->base.flags = diter->iter->flags;
@@ -222,7 +221,7 @@ debug_ref_iterator_begin(struct ref_store *ref_store, const char *prefix,
                drefs->refs->be->iterator_begin(drefs->refs, prefix,
                                                exclude_patterns, flags);
        struct debug_ref_iterator *diter = xcalloc(1, sizeof(*diter));
-       base_ref_iterator_init(&diter->base, &debug_ref_iterator_vtable, 1);
+       base_ref_iterator_init(&diter->base, &debug_ref_iterator_vtable);
        diter->iter = res;
        trace_printf_key(&trace_refs, "ref_iterator_begin: \"%s\" (0x%x)\n",
                         prefix, flags);
index 75dcc21ecb5ab83cadd37a899a252618a0c66df0..a098d14ea00ed6db449e5d3cc8dff28594228977 100644 (file)
@@ -229,6 +229,38 @@ static void add_per_worktree_entries_to_dir(struct ref_dir *dir, const char *dir
        }
 }
 
+static void loose_fill_ref_dir_regular_file(struct files_ref_store *refs,
+                                           const char *refname,
+                                           struct ref_dir *dir)
+{
+       struct object_id oid;
+       int flag;
+
+       if (!refs_resolve_ref_unsafe(&refs->base, refname, RESOLVE_REF_READING,
+                                    &oid, &flag)) {
+               oidclr(&oid);
+               flag |= REF_ISBROKEN;
+       } else if (is_null_oid(&oid)) {
+               /*
+                * It is so astronomically unlikely
+                * that null_oid is the OID of an
+                * actual object that we consider its
+                * appearance in a loose reference
+                * file to be repo corruption
+                * (probably due to a software bug).
+                */
+               flag |= REF_ISBROKEN;
+       }
+
+       if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) {
+               if (!refname_is_safe(refname))
+                       die("loose refname is dangerous: %s", refname);
+               oidclr(&oid);
+               flag |= REF_BAD_NAME | REF_ISBROKEN;
+       }
+       add_entry_to_dir(dir, create_ref_entry(refname, &oid, flag));
+}
+
 /*
  * Read the loose references from the namespace dirname into dir
  * (without recursing).  dirname must end with '/'.  dir must be the
@@ -257,8 +289,6 @@ static void loose_fill_ref_dir(struct ref_store *ref_store,
        strbuf_add(&refname, dirname, dirnamelen);
 
        while ((de = readdir(d)) != NULL) {
-               struct object_id oid;
-               int flag;
                unsigned char dtype;
 
                if (de->d_name[0] == '.')
@@ -274,33 +304,7 @@ static void loose_fill_ref_dir(struct ref_store *ref_store,
                                         create_dir_entry(dir->cache, refname.buf,
                                                          refname.len));
                } else if (dtype == DT_REG) {
-                       if (!refs_resolve_ref_unsafe(&refs->base,
-                                                    refname.buf,
-                                                    RESOLVE_REF_READING,
-                                                    &oid, &flag)) {
-                               oidclr(&oid);
-                               flag |= REF_ISBROKEN;
-                       } else if (is_null_oid(&oid)) {
-                               /*
-                                * It is so astronomically unlikely
-                                * that null_oid is the OID of an
-                                * actual object that we consider its
-                                * appearance in a loose reference
-                                * file to be repo corruption
-                                * (probably due to a software bug).
-                                */
-                               flag |= REF_ISBROKEN;
-                       }
-
-                       if (check_refname_format(refname.buf,
-                                                REFNAME_ALLOW_ONELEVEL)) {
-                               if (!refname_is_safe(refname.buf))
-                                       die("loose refname is dangerous: %s", refname.buf);
-                               oidclr(&oid);
-                               flag |= REF_BAD_NAME | REF_ISBROKEN;
-                       }
-                       add_entry_to_dir(dir,
-                                        create_ref_entry(refname.buf, &oid, flag));
+                       loose_fill_ref_dir_regular_file(refs, refname.buf, dir);
                }
                strbuf_setlen(&refname, dirnamelen);
        }
@@ -311,9 +315,59 @@ static void loose_fill_ref_dir(struct ref_store *ref_store,
        add_per_worktree_entries_to_dir(dir, dirname);
 }
 
-static struct ref_cache *get_loose_ref_cache(struct files_ref_store *refs)
+/*
+ * Add pseudorefs to the ref dir by parsing the directory for any files
+ * which follow the pseudoref syntax.
+ */
+static void add_pseudoref_and_head_entries(struct ref_store *ref_store,
+                                        struct ref_dir *dir,
+                                        const char *dirname)
+{
+       struct files_ref_store *refs =
+               files_downcast(ref_store, REF_STORE_READ, "fill_ref_dir");
+       struct strbuf path = STRBUF_INIT, refname = STRBUF_INIT;
+       struct dirent *de;
+       size_t dirnamelen;
+       DIR *d;
+
+       files_ref_path(refs, &path, dirname);
+
+       d = opendir(path.buf);
+       if (!d) {
+               strbuf_release(&path);
+               return;
+       }
+
+       strbuf_addstr(&refname, dirname);
+       dirnamelen = refname.len;
+
+       while ((de = readdir(d)) != NULL) {
+               unsigned char dtype;
+
+               if (de->d_name[0] == '.')
+                       continue;
+               if (ends_with(de->d_name, ".lock"))
+                       continue;
+               strbuf_addstr(&refname, de->d_name);
+
+               dtype = get_dtype(de, &path, 1);
+               if (dtype == DT_REG && (is_pseudoref(ref_store, de->d_name) ||
+                                                               is_headref(ref_store, de->d_name)))
+                       loose_fill_ref_dir_regular_file(refs, refname.buf, dir);
+
+               strbuf_setlen(&refname, dirnamelen);
+       }
+       strbuf_release(&refname);
+       strbuf_release(&path);
+       closedir(d);
+}
+
+static struct ref_cache *get_loose_ref_cache(struct files_ref_store *refs,
+                                            unsigned int flags)
 {
        if (!refs->loose) {
+               struct ref_dir *dir;
+
                /*
                 * Mark the top-level directory complete because we
                 * are about to read the only subdirectory that can
@@ -324,12 +378,17 @@ static struct ref_cache *get_loose_ref_cache(struct files_ref_store *refs)
                /* We're going to fill the top level ourselves: */
                refs->loose->root->flag &= ~REF_INCOMPLETE;
 
+               dir = get_ref_dir(refs->loose->root);
+
+               if (flags & DO_FOR_EACH_INCLUDE_ROOT_REFS)
+                       add_pseudoref_and_head_entries(dir->cache->ref_store, dir,
+                                                      refs->loose->root->name);
+
                /*
                 * Add an incomplete entry for "refs/" (to be filled
                 * lazily):
                 */
-               add_entry_to_dir(get_ref_dir(refs->loose->root),
-                                create_dir_entry(refs->loose, "refs/", 5));
+               add_entry_to_dir(dir, create_dir_entry(refs->loose, "refs/", 5));
        }
        return refs->loose;
 }
@@ -857,7 +916,7 @@ static struct ref_iterator *files_ref_iterator_begin(
         * disk, and re-reads it if not.
         */
 
-       loose_iter = cache_ref_iterator_begin(get_loose_ref_cache(refs),
+       loose_iter = cache_ref_iterator_begin(get_loose_ref_cache(refs, flags),
                                              prefix, ref_store->repo, 1);
 
        /*
@@ -879,8 +938,7 @@ static struct ref_iterator *files_ref_iterator_begin(
 
        CALLOC_ARRAY(iter, 1);
        ref_iterator = &iter->base;
-       base_ref_iterator_init(ref_iterator, &files_ref_iterator_vtable,
-                              overlay_iter->ordered);
+       base_ref_iterator_init(ref_iterator, &files_ref_iterator_vtable);
        iter->iter0 = overlay_iter;
        iter->repo = ref_store->repo;
        iter->flags = flags;
@@ -1218,7 +1276,7 @@ static int files_pack_refs(struct ref_store *ref_store,
 
        packed_refs_lock(refs->packed_ref_store, LOCK_DIE_ON_ERROR, &err);
 
-       iter = cache_ref_iterator_begin(get_loose_ref_cache(refs), NULL,
+       iter = cache_ref_iterator_begin(get_loose_ref_cache(refs, 0), NULL,
                                        the_repository, 0);
        while ((ok = ref_iterator_advance(iter)) == ITER_OK) {
                /*
@@ -2116,10 +2174,8 @@ static int files_for_each_reflog_ent(struct ref_store *ref_store,
 
 struct files_reflog_iterator {
        struct ref_iterator base;
-
        struct ref_store *ref_store;
        struct dir_iterator *dir_iterator;
-       struct object_id oid;
 };
 
 static int files_reflog_iterator_advance(struct ref_iterator *ref_iterator)
@@ -2130,25 +2186,13 @@ static int files_reflog_iterator_advance(struct ref_iterator *ref_iterator)
        int ok;
 
        while ((ok = dir_iterator_advance(diter)) == ITER_OK) {
-               int flags;
-
                if (!S_ISREG(diter->st.st_mode))
                        continue;
-               if (diter->basename[0] == '.')
+               if (check_refname_format(diter->basename,
+                                        REFNAME_ALLOW_ONELEVEL))
                        continue;
-               if (ends_with(diter->basename, ".lock"))
-                       continue;
-
-               if (!refs_resolve_ref_unsafe(iter->ref_store,
-                                            diter->relative_path, 0,
-                                            &iter->oid, &flags)) {
-                       error("bad ref for %s", diter->path.buf);
-                       continue;
-               }
 
                iter->base.refname = diter->relative_path;
-               iter->base.oid = &iter->oid;
-               iter->base.flags = flags;
                return ITER_OK;
        }
 
@@ -2193,7 +2237,7 @@ static struct ref_iterator *reflog_iterator_begin(struct ref_store *ref_store,
 
        strbuf_addf(&sb, "%s/logs", gitdir);
 
-       diter = dir_iterator_begin(sb.buf, 0);
+       diter = dir_iterator_begin(sb.buf, DIR_ITERATOR_SORTED);
        if (!diter) {
                strbuf_release(&sb);
                return empty_ref_iterator_begin();
@@ -2202,7 +2246,7 @@ static struct ref_iterator *reflog_iterator_begin(struct ref_store *ref_store,
        CALLOC_ARRAY(iter, 1);
        ref_iterator = &iter->base;
 
-       base_ref_iterator_init(ref_iterator, &files_reflog_iterator_vtable, 0);
+       base_ref_iterator_init(ref_iterator, &files_reflog_iterator_vtable);
        iter->dir_iterator = diter;
        iter->ref_store = ref_store;
        strbuf_release(&sb);
@@ -2210,32 +2254,6 @@ static struct ref_iterator *reflog_iterator_begin(struct ref_store *ref_store,
        return ref_iterator;
 }
 
-static enum iterator_selection reflog_iterator_select(
-       struct ref_iterator *iter_worktree,
-       struct ref_iterator *iter_common,
-       void *cb_data UNUSED)
-{
-       if (iter_worktree) {
-               /*
-                * We're a bit loose here. We probably should ignore
-                * common refs if they are accidentally added as
-                * per-worktree refs.
-                */
-               return ITER_SELECT_0;
-       } else if (iter_common) {
-               if (parse_worktree_ref(iter_common->refname, NULL, NULL,
-                                      NULL) == REF_WORKTREE_SHARED)
-                       return ITER_SELECT_1;
-
-               /*
-                * The main ref store may contain main worktree's
-                * per-worktree refs, which should be ignored
-                */
-               return ITER_SKIP_1;
-       } else
-               return ITER_DONE;
-}
-
 static struct ref_iterator *files_reflog_iterator_begin(struct ref_store *ref_store)
 {
        struct files_ref_store *refs =
@@ -2246,9 +2264,9 @@ static struct ref_iterator *files_reflog_iterator_begin(struct ref_store *ref_st
                return reflog_iterator_begin(ref_store, refs->gitcommondir);
        } else {
                return merge_ref_iterator_begin(
-                       0, reflog_iterator_begin(ref_store, refs->base.gitdir),
+                       reflog_iterator_begin(ref_store, refs->base.gitdir),
                        reflog_iterator_begin(ref_store, refs->gitcommondir),
-                       reflog_iterator_select, refs);
+                       ref_iterator_select, refs);
        }
 }
 
index 6b680f610efbee4e144cfa41763f45834b39d210..9db8b056d56d190fe9ea2087bc327f0154b6ecd2 100644 (file)
@@ -25,11 +25,9 @@ int ref_iterator_abort(struct ref_iterator *ref_iterator)
 }
 
 void base_ref_iterator_init(struct ref_iterator *iter,
-                           struct ref_iterator_vtable *vtable,
-                           int ordered)
+                           struct ref_iterator_vtable *vtable)
 {
        iter->vtable = vtable;
-       iter->ordered = !!ordered;
        iter->refname = NULL;
        iter->oid = NULL;
        iter->flags = 0;
@@ -74,7 +72,7 @@ struct ref_iterator *empty_ref_iterator_begin(void)
        struct empty_ref_iterator *iter = xcalloc(1, sizeof(*iter));
        struct ref_iterator *ref_iterator = &iter->base;
 
-       base_ref_iterator_init(ref_iterator, &empty_ref_iterator_vtable, 1);
+       base_ref_iterator_init(ref_iterator, &empty_ref_iterator_vtable);
        return ref_iterator;
 }
 
@@ -98,6 +96,49 @@ struct merge_ref_iterator {
        struct ref_iterator **current;
 };
 
+enum iterator_selection ref_iterator_select(struct ref_iterator *iter_worktree,
+                                           struct ref_iterator *iter_common,
+                                           void *cb_data UNUSED)
+{
+       if (iter_worktree && !iter_common) {
+               /*
+                * Return the worktree ref if there are no more common refs.
+                */
+               return ITER_SELECT_0;
+       } else if (iter_common) {
+               /*
+                * In case we have pending worktree and common refs we need to
+                * yield them based on their lexicographical order. Worktree
+                * refs that have the same name as common refs shadow the
+                * latter.
+                */
+               if (iter_worktree) {
+                       int cmp = strcmp(iter_worktree->refname,
+                                        iter_common->refname);
+                       if (cmp < 0)
+                               return ITER_SELECT_0;
+                       else if (!cmp)
+                               return ITER_SELECT_0_SKIP_1;
+               }
+
+                /*
+                 * We now know that the lexicographically-next ref is a common
+                 * ref. When the common ref is a shared one we return it.
+                 */
+               if (parse_worktree_ref(iter_common->refname, NULL, NULL,
+                                      NULL) == REF_WORKTREE_SHARED)
+                       return ITER_SELECT_1;
+
+               /*
+                * Otherwise, if the common ref is a per-worktree ref we skip
+                * it because it would belong to the main worktree, not ours.
+                */
+               return ITER_SKIP_1;
+       } else {
+               return ITER_DONE;
+       }
+}
+
 static int merge_ref_iterator_advance(struct ref_iterator *ref_iterator)
 {
        struct merge_ref_iterator *iter =
@@ -207,7 +248,6 @@ static struct ref_iterator_vtable merge_ref_iterator_vtable = {
 };
 
 struct ref_iterator *merge_ref_iterator_begin(
-               int ordered,
                struct ref_iterator *iter0, struct ref_iterator *iter1,
                ref_iterator_select_fn *select, void *cb_data)
 {
@@ -222,7 +262,7 @@ struct ref_iterator *merge_ref_iterator_begin(
         * references through only if they exist in both iterators.
         */
 
-       base_ref_iterator_init(ref_iterator, &merge_ref_iterator_vtable, ordered);
+       base_ref_iterator_init(ref_iterator, &merge_ref_iterator_vtable);
        iter->iter0 = iter0;
        iter->iter1 = iter1;
        iter->select = select;
@@ -271,12 +311,9 @@ struct ref_iterator *overlay_ref_iterator_begin(
        } else if (is_empty_ref_iterator(back)) {
                ref_iterator_abort(back);
                return front;
-       } else if (!front->ordered || !back->ordered) {
-               BUG("overlay_ref_iterator requires ordered inputs");
        }
 
-       return merge_ref_iterator_begin(1, front, back,
-                                       overlay_iterator_select, NULL);
+       return merge_ref_iterator_begin(front, back, overlay_iterator_select, NULL);
 }
 
 struct prefix_ref_iterator {
@@ -315,16 +352,12 @@ static int prefix_ref_iterator_advance(struct ref_iterator *ref_iterator)
 
                if (cmp > 0) {
                        /*
-                        * If the source iterator is ordered, then we
+                        * As the source iterator is ordered, we
                         * can stop the iteration as soon as we see a
                         * refname that comes after the prefix:
                         */
-                       if (iter->iter0->ordered) {
-                               ok = ref_iterator_abort(iter->iter0);
-                               break;
-                       } else {
-                               continue;
-                       }
+                       ok = ref_iterator_abort(iter->iter0);
+                       break;
                }
 
                if (iter->trim) {
@@ -396,7 +429,7 @@ struct ref_iterator *prefix_ref_iterator_begin(struct ref_iterator *iter0,
        CALLOC_ARRAY(iter, 1);
        ref_iterator = &iter->base;
 
-       base_ref_iterator_init(ref_iterator, &prefix_ref_iterator_vtable, iter0->ordered);
+       base_ref_iterator_init(ref_iterator, &prefix_ref_iterator_vtable);
 
        iter->iter0 = iter0;
        iter->prefix = xstrdup(prefix);
index a499a91c7e0ac94e45a7c28264647802522b65c4..4e826c05ff2b34986a5844cd40734b10b0298204 100644 (file)
@@ -1111,7 +1111,7 @@ static struct ref_iterator *packed_ref_iterator_begin(
 
        CALLOC_ARRAY(iter, 1);
        ref_iterator = &iter->base;
-       base_ref_iterator_init(ref_iterator, &packed_ref_iterator_vtable, 1);
+       base_ref_iterator_init(ref_iterator, &packed_ref_iterator_vtable);
 
        if (exclude_patterns)
                populate_excluded_jump_list(iter, snapshot, exclude_patterns);
index a372a00941fda659cc60722452c8766f9c7de455..9f9797209a4545576f3ce5c9a9327c47e3319df1 100644 (file)
@@ -486,7 +486,7 @@ struct ref_iterator *cache_ref_iterator_begin(struct ref_cache *cache,
 
        CALLOC_ARRAY(iter, 1);
        ref_iterator = &iter->base;
-       base_ref_iterator_init(ref_iterator, &cache_ref_iterator_vtable, 1);
+       base_ref_iterator_init(ref_iterator, &cache_ref_iterator_vtable);
        ALLOC_GROW(iter->levels, 10, iter->levels_alloc);
 
        iter->levels_nr = 1;
index 83e0f0bba3e752a69bb7f2aa409b4ed7318fb82e..56641aa57a138da17037307d37e1ca28baa2a1ee 100644 (file)
@@ -260,6 +260,12 @@ enum do_for_each_ref_flags {
         * INCLUDE_BROKEN, since they are otherwise not included at all.
         */
        DO_FOR_EACH_OMIT_DANGLING_SYMREFS = (1 << 2),
+
+       /*
+        * Include root refs i.e. HEAD and pseudorefs along with the regular
+        * refs.
+        */
+       DO_FOR_EACH_INCLUDE_ROOT_REFS = (1 << 3),
 };
 
 /*
@@ -312,13 +318,6 @@ enum do_for_each_ref_flags {
  */
 struct ref_iterator {
        struct ref_iterator_vtable *vtable;
-
-       /*
-        * Does this `ref_iterator` iterate over references in order
-        * by refname?
-        */
-       unsigned int ordered : 1;
-
        const char *refname;
        const struct object_id *oid;
        unsigned int flags;
@@ -386,15 +385,22 @@ typedef enum iterator_selection ref_iterator_select_fn(
                struct ref_iterator *iter0, struct ref_iterator *iter1,
                void *cb_data);
 
+/*
+ * An implementation of ref_iterator_select_fn that merges worktree and common
+ * refs. Per-worktree refs from the common iterator are ignored, worktree refs
+ * override common refs. Refs are selected lexicographically.
+ */
+enum iterator_selection ref_iterator_select(struct ref_iterator *iter_worktree,
+                                           struct ref_iterator *iter_common,
+                                           void *cb_data);
+
 /*
  * Iterate over the entries from iter0 and iter1, with the values
  * interleaved as directed by the select function. The iterator takes
  * ownership of iter0 and iter1 and frees them when the iteration is
- * over. A derived class should set `ordered` to 1 or 0 based on
- * whether it generates its output in order by reference name.
+ * over.
  */
 struct ref_iterator *merge_ref_iterator_begin(
-               int ordered,
                struct ref_iterator *iter0, struct ref_iterator *iter1,
                ref_iterator_select_fn *select, void *cb_data);
 
@@ -423,8 +429,6 @@ struct ref_iterator *overlay_ref_iterator_begin(
  * As an convenience to callers, if prefix is the empty string and
  * trim is zero, this function returns iter0 directly, without
  * wrapping it.
- *
- * The resulting ref_iterator is ordered if iter0 is.
  */
 struct ref_iterator *prefix_ref_iterator_begin(struct ref_iterator *iter0,
                                               const char *prefix,
@@ -435,14 +439,11 @@ struct ref_iterator *prefix_ref_iterator_begin(struct ref_iterator *iter0,
 /*
  * Base class constructor for ref_iterators. Initialize the
  * ref_iterator part of iter, setting its vtable pointer as specified.
- * `ordered` should be set to 1 if the iterator will iterate over
- * references in order by refname; otherwise it should be set to 0.
  * This is meant to be called only by the initializers of derived
  * classes.
  */
 void base_ref_iterator_init(struct ref_iterator *iter,
-                           struct ref_iterator_vtable *vtable,
-                           int ordered);
+                           struct ref_iterator_vtable *vtable);
 
 /*
  * Base class destructor for ref_iterators. Destroy the ref_iterator
index 4d27fdde541f1ee5ab8f555e20b5de28b1c11144..74dab18eda50f8158f3e8c133a8dde70299ce3cb 100644 (file)
@@ -365,12 +365,15 @@ static int reftable_ref_iterator_advance(struct ref_iterator *ref_iterator)
                        break;
 
                /*
-                * The files backend only lists references contained in
-                * "refs/". We emulate the same behaviour here and thus skip
-                * all references that don't start with this prefix.
+                * The files backend only lists references contained in "refs/" unless
+                * the root refs are to be included. We emulate the same behaviour here.
                 */
-               if (!starts_with(iter->ref.refname, "refs/"))
+               if (!starts_with(iter->ref.refname, "refs/") &&
+                   !(iter->flags & DO_FOR_EACH_INCLUDE_ROOT_REFS &&
+                    (is_pseudoref(&iter->refs->base, iter->ref.refname) ||
+                     is_headref(&iter->refs->base, iter->ref.refname)))) {
                        continue;
+               }
 
                if (iter->prefix_len &&
                    strncmp(iter->prefix, iter->ref.refname, iter->prefix_len)) {
@@ -480,7 +483,7 @@ static struct reftable_ref_iterator *ref_iterator_for_stack(struct reftable_ref_
        int ret;
 
        iter = xcalloc(1, sizeof(*iter));
-       base_ref_iterator_init(&iter->base, &reftable_ref_iterator_vtable, 1);
+       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;
@@ -506,49 +509,6 @@ done:
        return iter;
 }
 
-static enum iterator_selection iterator_select(struct ref_iterator *iter_worktree,
-                                              struct ref_iterator *iter_common,
-                                              void *cb_data UNUSED)
-{
-       if (iter_worktree && !iter_common) {
-               /*
-                * Return the worktree ref if there are no more common refs.
-                */
-               return ITER_SELECT_0;
-       } else if (iter_common) {
-               /*
-                * In case we have pending worktree and common refs we need to
-                * yield them based on their lexicographical order. Worktree
-                * refs that have the same name as common refs shadow the
-                * latter.
-                */
-               if (iter_worktree) {
-                       int cmp = strcmp(iter_worktree->refname,
-                                        iter_common->refname);
-                       if (cmp < 0)
-                               return ITER_SELECT_0;
-                       else if (!cmp)
-                               return ITER_SELECT_0_SKIP_1;
-               }
-
-                /*
-                 * We now know that the lexicographically-next ref is a common
-                 * ref. When the common ref is a shared one we return it.
-                 */
-               if (parse_worktree_ref(iter_common->refname, NULL, NULL,
-                                      NULL) == REF_WORKTREE_SHARED)
-                       return ITER_SELECT_1;
-
-               /*
-                * Otherwise, if the common ref is a per-worktree ref we skip
-                * it because it would belong to the main worktree, not ours.
-                */
-               return ITER_SKIP_1;
-       } else {
-               return ITER_DONE;
-       }
-}
-
 static struct ref_iterator *reftable_be_iterator_begin(struct ref_store *ref_store,
                                                       const char *prefix,
                                                       const char **exclude_patterns,
@@ -577,8 +537,8 @@ static struct ref_iterator *reftable_be_iterator_begin(struct ref_store *ref_sto
         * single iterator.
         */
        worktree_iter = ref_iterator_for_stack(refs, refs->worktree_stack, prefix, flags);
-       return merge_ref_iterator_begin(1, &worktree_iter->base, &main_iter->base,
-                                       iterator_select, NULL);
+       return merge_ref_iterator_begin(&worktree_iter->base, &main_iter->base,
+                                       ref_iterator_select, NULL);
 }
 
 static int reftable_be_read_raw_ref(struct ref_store *ref_store,
@@ -823,6 +783,7 @@ static int reftable_be_transaction_prepare(struct ref_store *ref_store,
                                      &head_referent, &head_type);
        if (ret < 0)
                goto done;
+       ret = 0;
 
        for (i = 0; i < transaction->nr; i++) {
                struct ref_update *u = transaction->updates[i];
@@ -1639,7 +1600,6 @@ struct reftable_reflog_iterator {
        struct reftable_ref_store *refs;
        struct reftable_iterator iter;
        struct reftable_log_record log;
-       struct object_id oid;
        char *last_name;
        int err;
 };
@@ -1650,8 +1610,6 @@ static int reftable_reflog_iterator_advance(struct ref_iterator *ref_iterator)
                (struct reftable_reflog_iterator *)ref_iterator;
 
        while (!iter->err) {
-               int flags;
-
                iter->err = reftable_iterator_next_log(&iter->iter, &iter->log);
                if (iter->err)
                        break;
@@ -1664,17 +1622,13 @@ static int reftable_reflog_iterator_advance(struct ref_iterator *ref_iterator)
                if (iter->last_name && !strcmp(iter->log.refname, iter->last_name))
                        continue;
 
-               if (!refs_resolve_ref_unsafe(&iter->refs->base, iter->log.refname,
-                                            0, &iter->oid, &flags)) {
-                       error(_("bad ref for %s"), iter->log.refname);
+               if (check_refname_format(iter->log.refname,
+                                        REFNAME_ALLOW_ONELEVEL))
                        continue;
-               }
 
                free(iter->last_name);
                iter->last_name = xstrdup(iter->log.refname);
                iter->base.refname = iter->log.refname;
-               iter->base.oid = &iter->oid;
-               iter->base.flags = flags;
 
                break;
        }
@@ -1725,9 +1679,8 @@ static struct reftable_reflog_iterator *reflog_iterator_for_stack(struct reftabl
        int ret;
 
        iter = xcalloc(1, sizeof(*iter));
-       base_ref_iterator_init(&iter->base, &reftable_reflog_iterator_vtable, 1);
+       base_ref_iterator_init(&iter->base, &reftable_reflog_iterator_vtable);
        iter->refs = refs;
-       iter->base.oid = &iter->oid;
 
        ret = refs->err;
        if (ret)
@@ -1760,8 +1713,8 @@ static struct ref_iterator *reftable_be_reflog_iterator_begin(struct ref_store *
 
        worktree_iter = reflog_iterator_for_stack(refs, refs->worktree_stack);
 
-       return merge_ref_iterator_begin(1, &worktree_iter->base, &main_iter->base,
-                                       iterator_select, NULL);
+       return merge_ref_iterator_begin(&worktree_iter->base, &main_iter->base,
+                                       ref_iterator_select, NULL);
 }
 
 static int yield_log_record(struct reftable_log_record *log,
index 9090632e96daddfc1217bc64ae4e0dac11bdb993..2b650b813b741f722a5f6c69f359a1f53249bcb1 100644 (file)
--- a/remote.c
+++ b/remote.c
@@ -2679,7 +2679,7 @@ static int is_reachable_in_reflog(const char *local, const struct ref *remote)
                if (MERGE_BASES_BATCH_SIZE < size)
                        size = MERGE_BASES_BATCH_SIZE;
 
-               if ((ret = repo_in_merge_bases_many(the_repository, commit, size, chunk)))
+               if ((ret = repo_in_merge_bases_many(the_repository, commit, size, chunk, 0)))
                        break;
        }
 
diff --git a/reset.c b/reset.c
index 0f2ff0fe31531fc5602025cfcd866a9348cfcf63..d619cb7115323febadbf4ae84e6764200f962cab 100644 (file)
--- a/reset.c
+++ b/reset.c
@@ -157,6 +157,11 @@ int reset_head(struct repository *r, const struct reset_head_opts *opts)
        }
 
        tree = parse_tree_indirect(oid);
+       if (!tree) {
+               ret = error(_("unable to read tree (%s)"), oid_to_hex(oid));
+               goto leave_reset_head;
+       }
+
        prime_cache_tree(r, r->index, tree);
 
        if (write_locked_index(r->index, &lock, COMMIT_LOCK) < 0) {
index 2424c9bd674e534909df89e25c21b5eb119fda05..45893651c22f2669da8d5fe38a0b809e650d6f6d 100644 (file)
@@ -381,13 +381,18 @@ static struct object *get_reference(struct rev_info *revs, const char *name,
 
        object = parse_object_with_flags(revs->repo, oid,
                                         revs->verify_objects ? 0 :
-                                        PARSE_OBJECT_SKIP_HASH_CHECK);
+                                        PARSE_OBJECT_SKIP_HASH_CHECK |
+                                        PARSE_OBJECT_DISCARD_TREE);
 
        if (!object) {
                if (revs->ignore_missing)
-                       return object;
+                       return NULL;
                if (revs->exclude_promisor_objects && is_promisor_object(oid))
                        return NULL;
+               if (revs->do_not_die_on_missing_objects) {
+                       oidset_insert(&revs->missing_commits, oid);
+                       return NULL;
+               }
                die("bad object %s", name);
        }
        object->flags |= flags;
@@ -415,15 +420,21 @@ static struct commit *handle_commit(struct rev_info *revs,
         */
        while (object->type == OBJ_TAG) {
                struct tag *tag = (struct tag *) object;
+               struct object_id *oid;
                if (revs->tag_objects && !(flags & UNINTERESTING))
                        add_pending_object(revs, object, tag->tag);
-               object = parse_object(revs->repo, get_tagged_oid(tag));
+               oid = get_tagged_oid(tag);
+               object = parse_object(revs->repo, oid);
                if (!object) {
                        if (revs->ignore_missing_links || (flags & UNINTERESTING))
                                return NULL;
                        if (revs->exclude_promisor_objects &&
                            is_promisor_object(&tag->tagged->oid))
                                return NULL;
+                       if (revs->do_not_die_on_missing_objects && oid) {
+                               oidset_insert(&revs->missing_commits, oid);
+                               return NULL;
+                       }
                        die("bad object %s", oid_to_hex(&tag->tagged->oid));
                }
                object->flags |= flags;
@@ -1686,9 +1697,7 @@ static int handle_one_reflog_ent(struct object_id *ooid, struct object_id *noid,
        return 0;
 }
 
-static int handle_one_reflog(const char *refname_in_wt,
-                            const struct object_id *oid UNUSED,
-                            int flag UNUSED, void *cb_data)
+static int handle_one_reflog(const char *refname_in_wt, void *cb_data)
 {
        struct all_refs_cb *cb = cb_data;
        struct strbuf refname = STRBUF_INIT;
@@ -1947,6 +1956,7 @@ void repo_init_revisions(struct repository *r,
        init_display_notes(&revs->notes_opt);
        list_objects_filter_init(&revs->filter);
        init_ref_exclusions(&revs->ref_excludes);
+       oidset_init(&revs->missing_commits, 0);
 }
 
 static void add_pending_commit_list(struct rev_info *revs,
@@ -1961,11 +1971,31 @@ static void add_pending_commit_list(struct rev_info *revs,
        }
 }
 
+static const char *lookup_other_head(struct object_id *oid)
+{
+       int i;
+       static const char *const other_head[] = {
+               "MERGE_HEAD", "CHERRY_PICK_HEAD", "REVERT_HEAD", "REBASE_HEAD"
+       };
+
+       for (i = 0; i < ARRAY_SIZE(other_head); i++)
+               if (!read_ref_full(other_head[i],
+                               RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,
+                               oid, NULL)) {
+                       if (is_null_oid(oid))
+                               die(_("%s exists but is a symbolic ref"), other_head[i]);
+                       return other_head[i];
+               }
+
+       die(_("--merge requires one of the pseudorefs MERGE_HEAD, CHERRY_PICK_HEAD, REVERT_HEAD or REBASE_HEAD"));
+}
+
 static void prepare_show_merge(struct rev_info *revs)
 {
-       struct commit_list *bases;
+       struct commit_list *bases = NULL;
        struct commit *head, *other;
        struct object_id oid;
+       const char *other_name;
        const char **prune = NULL;
        int i, prune_num = 1; /* counting terminating NULL */
        struct index_state *istate = revs->repo->index;
@@ -1973,12 +2003,12 @@ static void prepare_show_merge(struct rev_info *revs)
        if (repo_get_oid(the_repository, "HEAD", &oid))
                die("--merge without HEAD?");
        head = lookup_commit_or_die(&oid, "HEAD");
-       if (repo_get_oid(the_repository, "MERGE_HEAD", &oid))
-               die("--merge without MERGE_HEAD?");
-       other = lookup_commit_or_die(&oid, "MERGE_HEAD");
+       other_name = lookup_other_head(&oid);
+       other = lookup_commit_or_die(&oid, other_name);
        add_pending_object(revs, &head->object, "HEAD");
-       add_pending_object(revs, &other->object, "MERGE_HEAD");
-       bases = repo_get_merge_bases(the_repository, head, other);
+       add_pending_object(revs, &other->object, other_name);
+       if (repo_get_merge_bases(the_repository, head, other, &bases) < 0)
+               exit(128);
        add_rev_cmdline_list(revs, bases, REV_CMD_MERGE_BASE, UNINTERESTING | BOTTOM);
        add_pending_commit_list(revs, bases, UNINTERESTING | BOTTOM);
        free_commit_list(bases);
@@ -2066,14 +2096,17 @@ static int handle_dotdot_1(const char *arg, char *dotdot,
        } else {
                /* A...B -- find merge bases between the two */
                struct commit *a, *b;
-               struct commit_list *exclude;
+               struct commit_list *exclude = NULL;
 
                a = lookup_commit_reference(revs->repo, &a_obj->oid);
                b = lookup_commit_reference(revs->repo, &b_obj->oid);
                if (!a || !b)
                        return dotdot_missing(arg, dotdot, revs, symmetric);
 
-               exclude = repo_get_merge_bases(the_repository, a, b);
+               if (repo_get_merge_bases(the_repository, a, b, &exclude) < 0) {
+                       free_commit_list(exclude);
+                       return -1;
+               }
                add_rev_cmdline_list(revs, exclude, REV_CMD_MERGE_BASE,
                                     flags_exclude);
                add_pending_commit_list(revs, exclude, flags_exclude);
@@ -2178,13 +2211,18 @@ static int handle_revision_arg_1(const char *arg_, struct rev_info *revs, int fl
        if (revarg_opt & REVARG_COMMITTISH)
                get_sha1_flags |= GET_OID_COMMITTISH;
 
+       /*
+        * Even if revs->do_not_die_on_missing_objects is set, we
+        * should error out if we can't even get an oid, as
+        * `--missing=print` should be able to report missing oids.
+        */
        if (get_oid_with_context(revs->repo, arg, get_sha1_flags, &oid, &oc))
                return revs->ignore_missing ? 0 : -1;
        if (!cant_be_filename)
                verify_non_filename(revs->prefix, arg);
        object = get_reference(revs, arg, &oid, flags ^ local_flags);
        if (!object)
-               return revs->ignore_missing ? 0 : -1;
+               return (revs->ignore_missing || revs->do_not_die_on_missing_objects) ? 0 : -1;
        add_rev_cmdline(revs, object, arg_, REV_CMD_REV, flags ^ local_flags);
        add_pending_object_with_path(revs, object, arg, oc.mode, oc.path);
        free(oc.path);
@@ -3830,8 +3868,6 @@ int prepare_revision_walk(struct rev_info *revs)
                                       FOR_EACH_OBJECT_PROMISOR_ONLY);
        }
 
-       oidset_init(&revs->missing_commits, 0);
-
        if (!revs->reflog_info)
                prepare_to_use_bloom_filter(revs);
        if (!revs->unsorted_input)
index f49a871ac0666b10393331ed3c9b611921f1039e..5c6f541126c74bb1c70adef36520213b31d3265b 100644 (file)
@@ -707,6 +707,8 @@ static int do_recursive_merge(struct repository *r,
        o.show_rename_progress = 1;
 
        head_tree = parse_tree_indirect(head);
+       if (!head_tree)
+               return error(_("unable to read tree (%s)"), oid_to_hex(head));
        next_tree = next ? repo_get_commit_tree(r, next) : empty_tree(r);
        base_tree = base ? repo_get_commit_tree(r, base) : empty_tree(r);
 
@@ -3882,6 +3884,8 @@ static int do_reset(struct repository *r,
        }
 
        tree = parse_tree_indirect(&oid);
+       if (!tree)
+               return error(_("unable to read tree (%s)"), oid_to_hex(&oid));
        prime_cache_tree(r, r->index, tree);
 
        if (write_locked_index(r->index, &lock, COMMIT_LOCK) < 0)
@@ -3908,7 +3912,7 @@ static int do_merge(struct repository *r,
        int run_commit_flags = 0;
        struct strbuf ref_name = STRBUF_INIT;
        struct commit *head_commit, *merge_commit, *i;
-       struct commit_list *bases, *j;
+       struct commit_list *bases = NULL, *j;
        struct commit_list *to_merge = NULL, **tail = &to_merge;
        const char *strategy = !opts->xopts.nr &&
                (!opts->strategy ||
@@ -4134,7 +4138,11 @@ static int do_merge(struct repository *r,
        }
 
        merge_commit = to_merge->item;
-       bases = repo_get_merge_bases(r, head_commit, merge_commit);
+       if (repo_get_merge_bases(r, head_commit, merge_commit, &bases) < 0) {
+               ret = -1;
+               goto leave_merge;
+       }
+
        if (bases && oideq(&merge_commit->object.oid,
                           &bases->item->object.oid)) {
                ret = 0;
diff --git a/serve.c b/serve.c
index a1d71134d49cc88ead5af690315b27ae23215e56..aa651b73e9b7a3378c51f7c92533a6dd0f82ce73 100644 (file)
--- a/serve.c
+++ b/serve.c
@@ -12,6 +12,7 @@
 #include "trace2.h"
 
 static int advertise_sid = -1;
+static int advertise_object_info = -1;
 static int client_hash_algo = GIT_HASH_SHA1;
 
 static int always_advertise(struct repository *r UNUSED,
@@ -67,6 +68,17 @@ static void session_id_receive(struct repository *r UNUSED,
        trace2_data_string("transfer", NULL, "client-sid", client_sid);
 }
 
+static int object_info_advertise(struct repository *r, struct strbuf *value UNUSED)
+{
+       if (advertise_object_info == -1 &&
+           repo_config_get_bool(r, "transfer.advertiseobjectinfo",
+                                &advertise_object_info)) {
+               /* disabled by default */
+               advertise_object_info = 0;
+       }
+       return advertise_object_info;
+}
+
 struct protocol_capability {
        /*
         * The name of the capability.  The server uses this name when
@@ -135,7 +147,7 @@ static struct protocol_capability capabilities[] = {
        },
        {
                .name = "object-info",
-               .advertise = always_advertise,
+               .advertise = object_info_advertise,
                .command = cap_object_info,
        },
        {
diff --git a/setup.c b/setup.c
index b69b1cbc2adb41aa5f4df8b1aff88761ea2cba5c..e3b76e84b5b8676fdd23b7709800e3df24d8c08d 100644 (file)
--- a/setup.c
+++ b/setup.c
@@ -1889,6 +1889,13 @@ void initialize_repository_version(int hash_algo,
        char repo_version_string[10];
        int repo_version = GIT_REPO_VERSION;
 
+       /*
+        * Note that we initialize the repository version to 1 when the ref
+        * storage format is unknown. This is on purpose so that we can add the
+        * correct object format to the config during git-clone(1). The format
+        * version will get adjusted by git-clone(1) once it has learned about
+        * the remote repository's format.
+        */
        if (hash_algo != GIT_HASH_SHA1 ||
            ref_storage_format != REF_STORAGE_FORMAT_FILES)
                repo_version = GIT_REPO_VERSION_READ;
@@ -1898,7 +1905,7 @@ void initialize_repository_version(int hash_algo,
                  "%d", repo_version);
        git_config_set("core.repositoryformatversion", repo_version_string);
 
-       if (hash_algo != GIT_HASH_SHA1)
+       if (hash_algo != GIT_HASH_SHA1 && hash_algo != GIT_HASH_UNKNOWN)
                git_config_set("extensions.objectformat",
                               hash_algos[hash_algo].name);
        else if (reinit)
index 7711798127e49efaa18b6403ecf103f05e92f7e7..7ff50dd0da45e00ac3de5e03af473ff361d33b02 100644 (file)
--- a/shallow.c
+++ b/shallow.c
@@ -794,12 +794,16 @@ static void post_assign_shallow(struct shallow_info *info,
                if (!*bitmap)
                        continue;
                for (j = 0; j < bitmap_nr; j++)
-                       if (bitmap[0][j] &&
-                           /* Step 7, reachability test at commit level */
-                           !repo_in_merge_bases_many(the_repository, c, ca.nr, ca.commits)) {
-                               update_refstatus(ref_status, info->ref->nr, *bitmap);
-                               dst++;
-                               break;
+                       if (bitmap[0][j]) {
+                               /* Step 7, reachability test at commit level */
+                               int ret = repo_in_merge_bases_many(the_repository, c, ca.nr, ca.commits, 1);
+                               if (ret < 0)
+                                       exit(128);
+                               if (!ret) {
+                                       update_refstatus(ref_status, info->ref->nr, *bitmap);
+                                       dst++;
+                                       break;
+                               }
                        }
        }
        info->nr_ours = dst;
@@ -827,7 +831,10 @@ int delayed_reachability_test(struct shallow_info *si, int c)
                si->reachable[c] = repo_in_merge_bases_many(the_repository,
                                                            commit,
                                                            si->nr_commits,
-                                                           si->commits);
+                                                           si->commits,
+                                                           1);
+               if (si->reachable[c] < 0)
+                       exit(128);
                si->need_reachability_test[c] = 0;
        }
        return si->reachable[c];
index 213da79f66116f3db835036140affb98406a934a..f0ddb31e8fb535264dded9fb0a7434fda1330f39 100644 (file)
@@ -592,7 +592,12 @@ static void show_submodule_header(struct diff_options *o,
             (!is_null_oid(two) && !*right))
                message = "(commits not present)";
 
-       *merge_bases = repo_get_merge_bases(sub, *left, *right);
+       *merge_bases = NULL;
+       if (repo_get_merge_bases(sub, *left, *right, merge_bases) < 0) {
+               message = "(corrupt repository)";
+               goto output_header;
+       }
+
        if (*merge_bases) {
                if ((*merge_bases)->item == *left)
                        fast_forward = 1;
@@ -1687,8 +1692,6 @@ static int get_next_submodule(struct child_process *cp, struct strbuf *err,
                task = get_fetch_task_from_changed(spf, err);
 
        if (task) {
-               struct strbuf submodule_prefix = STRBUF_INIT;
-
                child_process_init(cp);
                cp->dir = task->repo->gitdir;
                prepare_submodule_repo_env_in_gitdir(&cp->env);
@@ -1698,15 +1701,11 @@ static int get_next_submodule(struct child_process *cp, struct strbuf *err,
                        strvec_pushv(&cp->args, task->git_args.v);
                strvec_pushv(&cp->args, spf->args.v);
                strvec_push(&cp->args, task->default_argv);
-               strvec_push(&cp->args, "--submodule-prefix");
+               strvec_pushf(&cp->args, "--submodule-prefix=%s%s/",
+                            spf->prefix, task->sub->path);
 
-               strbuf_addf(&submodule_prefix, "%s%s/",
-                                               spf->prefix,
-                                               task->sub->path);
-               strvec_push(&cp->args, submodule_prefix.buf);
                *task_cb = task;
 
-               strbuf_release(&submodule_prefix);
                string_list_insert(&spf->seen_submodule_names, task->sub->name);
                return 1;
        }
@@ -1714,12 +1713,8 @@ static int get_next_submodule(struct child_process *cp, struct strbuf *err,
        if (spf->oid_fetch_tasks_nr) {
                struct fetch_task *task =
                        spf->oid_fetch_tasks[spf->oid_fetch_tasks_nr - 1];
-               struct strbuf submodule_prefix = STRBUF_INIT;
                spf->oid_fetch_tasks_nr--;
 
-               strbuf_addf(&submodule_prefix, "%s%s/",
-                           spf->prefix, task->sub->path);
-
                child_process_init(cp);
                prepare_submodule_repo_env_in_gitdir(&cp->env);
                cp->git_cmd = 1;
@@ -1728,8 +1723,8 @@ static int get_next_submodule(struct child_process *cp, struct strbuf *err,
                strvec_init(&cp->args);
                strvec_pushv(&cp->args, spf->args.v);
                strvec_push(&cp->args, "on-demand");
-               strvec_push(&cp->args, "--submodule-prefix");
-               strvec_push(&cp->args, submodule_prefix.buf);
+               strvec_pushf(&cp->args, "--submodule-prefix=%s%s/",
+                            spf->prefix, task->sub->path);
 
                /* NEEDSWORK: have get_default_remote from submodule--helper */
                strvec_push(&cp->args, "origin");
@@ -1737,7 +1732,6 @@ static int get_next_submodule(struct child_process *cp, struct strbuf *err,
                                          append_oid_to_argv, &cp->args);
 
                *task_cb = task;
-               strbuf_release(&submodule_prefix);
                return 1;
        }
 
index 1e159a754db6db5d02eaa81072b0ab19fa863004..1e3b431e3e72116df65e825bed1916d5ff7859e6 100644 (file)
@@ -111,13 +111,16 @@ int cmd__reach(int ac, const char **av)
                       repo_in_merge_bases(the_repository, A, B));
        else if (!strcmp(av[1], "in_merge_bases_many"))
                printf("%s(A,X):%d\n", av[1],
-                      repo_in_merge_bases_many(the_repository, A, X_nr, X_array));
+                      repo_in_merge_bases_many(the_repository, A, X_nr, X_array, 0));
        else if (!strcmp(av[1], "is_descendant_of"))
                printf("%s(A,X):%d\n", av[1], repo_is_descendant_of(r, A, X));
        else if (!strcmp(av[1], "get_merge_bases_many")) {
-               struct commit_list *list = repo_get_merge_bases_many(the_repository,
-                                                                    A, X_nr,
-                                                                    X_array);
+               struct commit_list *list = NULL;
+               if (repo_get_merge_bases_many(the_repository,
+                                             A, X_nr,
+                                             X_array,
+                                             &list) < 0)
+                       exit(128);
                printf("%s(A,X):\n", av[1]);
                print_sorted_commit_ids(list);
        } else if (!strcmp(av[1], "reduce_heads")) {
index 702ec1f128ad42443bc093614dee971c7106af77..7a0f6cac53d2433ade595ce95ab2d30e214b2ede 100644 (file)
@@ -221,15 +221,21 @@ static int cmd_verify_ref(struct ref_store *refs, const char **argv)
        return ret;
 }
 
+static int each_reflog(const char *refname, void *cb_data UNUSED)
+{
+       printf("%s\n", refname);
+       return 0;
+}
+
 static int cmd_for_each_reflog(struct ref_store *refs,
                               const char **argv UNUSED)
 {
-       return refs_for_each_reflog(refs, each_ref, NULL);
+       return refs_for_each_reflog(refs, each_reflog, NULL);
 }
 
-static int each_reflog(struct object_id *old_oid, struct object_id *new_oid,
-                      const char *committer, timestamp_t timestamp,
-                      int tz, const char *msg, void *cb_data UNUSED)
+static int each_reflog_ent(struct object_id *old_oid, struct object_id *new_oid,
+                          const char *committer, timestamp_t timestamp,
+                          int tz, const char *msg, void *cb_data UNUSED)
 {
        printf("%s %s %s %" PRItime " %+05d%s%s", oid_to_hex(old_oid),
               oid_to_hex(new_oid), committer, timestamp, tz,
@@ -241,14 +247,14 @@ static int cmd_for_each_reflog_ent(struct ref_store *refs, const char **argv)
 {
        const char *refname = notnull(*argv++, "refname");
 
-       return refs_for_each_reflog_ent(refs, refname, each_reflog, refs);
+       return refs_for_each_reflog_ent(refs, refname, each_reflog_ent, refs);
 }
 
 static int cmd_for_each_reflog_ent_reverse(struct ref_store *refs, const char **argv)
 {
        const char *refname = notnull(*argv++, "refname");
 
-       return refs_for_each_reflog_ent_reverse(refs, refname, each_reflog, refs);
+       return refs_for_each_reflog_ent_reverse(refs, refname, each_reflog_ent, refs);
 }
 
 static int cmd_reflog_exists(struct ref_store *refs, const char **argv)
index 15fc9a31e2cc03689e76876693e39367626784ff..44799c0d38fdb35d1c56a3e9ac8d91a963d2ee1c 100644 (file)
@@ -50,6 +50,7 @@ helper_test_clean() {
        reject $1 https example.com user-overwrite
        reject $1 https example.com user-erase1
        reject $1 https example.com user-erase2
+       reject $1 https victim.example.com user
        reject $1 http path.tld user
        reject $1 https timeout.tld user
        reject $1 https sso.tld
index 095574bfc6edf2aaf835b2ff43bb8cd35f792591..72ae405c3ed979edee54cbe83e73ec566d23f6d1 100755 (executable)
@@ -32,9 +32,24 @@ commands.
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-credential.sh
 
+# If we're not given a specific external helper to run against,
+# there isn't much to test. But we can still run through our
+# battery of tests with a fake helper and check that the
+# test themselves are self-consistent and clean up after
+# themselves.
+#
+# We'll use the "store" helper, since we can easily inspect
+# its state by looking at the on-disk file. But since it doesn't
+# implement any caching or expiry logic, we'll cheat and override
+# the "check" function to just report all results as OK.
 if test -z "$GIT_TEST_CREDENTIAL_HELPER"; then
-       skip_all="used to test external credential helpers"
-       test_done
+       GIT_TEST_CREDENTIAL_HELPER=store
+       GIT_TEST_CREDENTIAL_HELPER_TIMEOUT=store
+       check () {
+               test "$1" = "approve" || return 0
+               git -c credential.helper=store credential approve
+       }
+       check_cleanup=t
 fi
 
 test -z "$GIT_TEST_CREDENTIAL_HELPER_SETUP" ||
@@ -59,4 +74,11 @@ fi
 # might be long-term system storage
 helper_test_clean "$GIT_TEST_CREDENTIAL_HELPER"
 
+if test "$check_cleanup" = "t"
+then
+       test_expect_success 'test cleanup removes everything' '
+               test_must_be_empty "$HOME/.git-credentials"
+       '
+fi
+
 test_done
index 0f98b21be8d132d9595d54b364c2e5d4aa81dca2..88a66f09040ce0aa2a3a653579d6c3685e750ba1 100755 (executable)
@@ -665,6 +665,21 @@ test_expect_success 'lazy-fetch when accessing object not in the_repository' '
        git -C partial.git rev-list --objects --missing=print HEAD >out &&
        grep "[?]$FILE_HASH" out &&
 
+       # The no-lazy-fetch mechanism prevents Git from fetching
+       test_must_fail env GIT_NO_LAZY_FETCH=1 \
+               git -C partial.git cat-file -e "$FILE_HASH" &&
+
+       # The same with command line option to "git"
+       test_must_fail git --no-lazy-fetch -C partial.git cat-file -e "$FILE_HASH" &&
+
+       # The same, forcing a subprocess via an alias
+       test_must_fail git --no-lazy-fetch -C partial.git \
+               -c alias.foo="!git cat-file" foo -e "$FILE_HASH" &&
+
+       # Sanity check that the file is still missing
+       git -C partial.git rev-list --objects --missing=print HEAD >out &&
+       grep "[?]$FILE_HASH" out &&
+
        git -C full cat-file -s "$FILE_HASH" >expect &&
        test-tool partial-clone object-info partial.git "$FILE_HASH" >actual &&
        test_cmp expect actual &&
index 485481d6b40828b4ba83819b85af796651ccbae2..64214340e75f9ecbb20380c4cfe8e6f5813a5495 100755 (executable)
@@ -279,31 +279,31 @@ test_expect_success 'setup worktree' '
 # direct FS access for creating the reflogs. 3) PSEUDO-WT and refs/bisect/random
 # do not create reflogs by default, so it is not testing a realistic scenario.
 test_expect_success 'for_each_reflog()' '
-       echo $ZERO_OID > .git/logs/PSEUDO-MAIN &&
+       echo $ZERO_OID >.git/logs/PSEUDO_MAIN_HEAD &&
        mkdir -p     .git/logs/refs/bisect &&
-       echo $ZERO_OID > .git/logs/refs/bisect/random &&
+       echo $ZERO_OID >.git/logs/refs/bisect/random &&
 
-       echo $ZERO_OID > .git/worktrees/wt/logs/PSEUDO-WT &&
+       echo $ZERO_OID >.git/worktrees/wt/logs/PSEUDO_WT_HEAD &&
        mkdir -p     .git/worktrees/wt/logs/refs/bisect &&
-       echo $ZERO_OID > .git/worktrees/wt/logs/refs/bisect/wt-random &&
+       echo $ZERO_OID >.git/worktrees/wt/logs/refs/bisect/wt-random &&
 
-       $RWT for-each-reflog | cut -d" " -f 2- | sort >actual &&
+       $RWT for-each-reflog >actual &&
        cat >expected <<-\EOF &&
-       HEAD 0x1
-       PSEUDO-WT 0x0
-       refs/bisect/wt-random 0x0
-       refs/heads/main 0x0
-       refs/heads/wt-main 0x0
+       HEAD
+       PSEUDO_WT_HEAD
+       refs/bisect/wt-random
+       refs/heads/main
+       refs/heads/wt-main
        EOF
        test_cmp expected actual &&
 
-       $RMAIN for-each-reflog | cut -d" " -f 2- | sort >actual &&
+       $RMAIN for-each-reflog >actual &&
        cat >expected <<-\EOF &&
-       HEAD 0x1
-       PSEUDO-MAIN 0x0
-       refs/bisect/random 0x0
-       refs/heads/main 0x0
-       refs/heads/wt-main 0x0
+       HEAD
+       PSEUDO_MAIN_HEAD
+       refs/bisect/random
+       refs/heads/main
+       refs/heads/wt-main
        EOF
        test_cmp expected actual
 '
index 6a131e40b8157dc3fefea53704ad2377b7ce5c3a..686781192eb7c2d820e28393b6d0352db3f4822e 100755 (executable)
@@ -328,6 +328,18 @@ test_expect_success 'ref transaction: writes are synced' '
        EOF
 '
 
+test_expect_success 'ref transaction: empty transaction in empty repo' '
+       test_when_finished "rm -rf repo" &&
+       git init repo &&
+       test_commit -C repo --no-tag A &&
+       git -C repo update-ref -d refs/heads/main &&
+       test-tool -C repo ref-store main delete-refs REF_NO_DEREF msg HEAD &&
+       git -C repo update-ref --stdin <<-EOF
+       prepare
+       commit
+       EOF
+'
+
 test_expect_success 'pack-refs: compacts tables' '
        test_when_finished "rm -rf repo" &&
        git init repo &&
index 1183232a7296bcf265f7d8e274c8b990c3df19dc..a6bcd62ab658eefcd11ae1778692ad6d7f4c3008 100755 (executable)
@@ -68,11 +68,11 @@ test_expect_success 'verify_ref(new-main)' '
 '
 
 test_expect_success 'for_each_reflog()' '
-       $RUN for-each-reflog | sort -k2 | cut -d" " -f 2- >actual &&
+       $RUN for-each-reflog >actual &&
        cat >expected <<-\EOF &&
-       HEAD 0x1
-       refs/heads/main 0x0
-       refs/heads/new-main 0x0
+       HEAD
+       refs/heads/main
+       refs/heads/new-main
        EOF
        test_cmp expected actual
 '
index e6a7f7334b6a96e4aa310532c8dc7d6c727fdd16..c01f0f14a1266f93f9066c738693d7c8a9a526dc 100755 (executable)
@@ -63,11 +63,11 @@ test_expect_success 'verify_ref(new-main)' '
 '
 
 test_expect_success 'for_each_reflog()' '
-       $RUN for-each-reflog | sort | cut -d" " -f 2- >actual &&
+       $RUN for-each-reflog >actual &&
        cat >expected <<-\EOF &&
-       HEAD 0x1
-       refs/heads/main 0x0
-       refs/heads/new-main 0x0
+       HEAD
+       refs/heads/main
+       refs/heads/new-main
        EOF
        test_cmp expected actual
 '
index d2f5f42e67440d856fd8f1752c75b20a16278e13..5bf883f1e363306a74b15df1ab02988564aa3208 100755 (executable)
@@ -436,4 +436,112 @@ test_expect_success 'empty reflog' '
        test_must_be_empty err
 '
 
+test_expect_success 'list reflogs' '
+       test_when_finished "rm -rf repo" &&
+       git init repo &&
+       (
+               cd repo &&
+               git reflog list >actual &&
+               test_must_be_empty actual &&
+
+               test_commit A &&
+               cat >expect <<-EOF &&
+               HEAD
+               refs/heads/main
+               EOF
+               git reflog list >actual &&
+               test_cmp expect actual &&
+
+               git branch b &&
+               cat >expect <<-EOF &&
+               HEAD
+               refs/heads/b
+               refs/heads/main
+               EOF
+               git reflog list >actual &&
+               test_cmp expect actual
+       )
+'
+
+test_expect_success 'list reflogs with worktree' '
+       test_when_finished "rm -rf repo" &&
+       git init repo &&
+       (
+               cd repo &&
+
+               test_commit A &&
+               git worktree add wt &&
+               git -c core.logAllRefUpdates=always \
+                       update-ref refs/worktree/main HEAD &&
+               git -c core.logAllRefUpdates=always \
+                       update-ref refs/worktree/per-worktree HEAD &&
+               git -c core.logAllRefUpdates=always -C wt \
+                       update-ref refs/worktree/per-worktree HEAD &&
+               git -c core.logAllRefUpdates=always -C wt \
+                       update-ref refs/worktree/worktree HEAD &&
+
+               cat >expect <<-EOF &&
+               HEAD
+               refs/heads/main
+               refs/heads/wt
+               refs/worktree/main
+               refs/worktree/per-worktree
+               EOF
+               git reflog list >actual &&
+               test_cmp expect actual &&
+
+               cat >expect <<-EOF &&
+               HEAD
+               refs/heads/main
+               refs/heads/wt
+               refs/worktree/per-worktree
+               refs/worktree/worktree
+               EOF
+               git -C wt reflog list >actual &&
+               test_cmp expect actual
+       )
+'
+
+test_expect_success 'reflog list returns error with additional args' '
+       cat >expect <<-EOF &&
+       error: list does not accept arguments: ${SQ}bogus${SQ}
+       EOF
+       test_must_fail git reflog list bogus 2>err &&
+       test_cmp expect err
+'
+
+test_expect_success 'reflog for symref with unborn target can be listed' '
+       test_when_finished "rm -rf repo" &&
+       git init repo &&
+       (
+               cd repo &&
+               test_commit A &&
+               git symbolic-ref HEAD refs/heads/unborn &&
+               cat >expect <<-EOF &&
+               HEAD
+               refs/heads/main
+               EOF
+               git reflog list >actual &&
+               test_cmp expect actual
+       )
+'
+
+test_expect_success 'reflog with invalid object ID can be listed' '
+       test_when_finished "rm -rf repo" &&
+       git init repo &&
+       (
+               cd repo &&
+               test_commit A &&
+               test-tool ref-store main update-ref msg refs/heads/missing \
+                       $(test_oid deadbeef) "$ZERO_OID" REF_SKIP_OID_VERIFICATION &&
+               cat >expect <<-EOF &&
+               HEAD
+               refs/heads/main
+               refs/heads/missing
+               EOF
+               git reflog list >actual &&
+               test_cmp expect actual
+       )
+'
+
 test_done
index 6a98b2df7611ed741be197640f20e1147794aa8d..a1139f79e2ccfdff2b562571bdd8bdf8aa974883 100755 (executable)
@@ -4,9 +4,6 @@ test_description='test show-branch'
 
 . ./test-lib.sh
 
-# arbitrary reference time: 2009-08-30 19:20:00
-GIT_TEST_DATE_NOW=1251660000; export GIT_TEST_DATE_NOW
-
 test_expect_success 'error descriptions on empty repository' '
        current=$(git branch --show-current) &&
        cat >expect <<-EOF &&
@@ -187,18 +184,6 @@ test_expect_success 'show branch --merge-base with N arguments' '
        test_cmp expect actual
 '
 
-test_expect_success 'show branch --reflog=2' '
-       sed "s/^>       //" >expect <<-\EOF &&
-       >       ! [refs/heads/branch10@{0}] (4 years, 5 months ago) commit: branch10
-       >        ! [refs/heads/branch10@{1}] (4 years, 5 months ago) commit: branch10
-       >       --
-       >       +  [refs/heads/branch10@{0}] branch10
-       >       ++ [refs/heads/branch10@{1}] initial
-       EOF
-       git show-branch --reflog=2 >actual &&
-       test_cmp actual expect
-'
-
 # incompatible options
 while read combo
 do
@@ -264,4 +249,38 @@ test_expect_success 'error descriptions on orphan branch' '
        test_branch_op_in_wt -c new-branch
 '
 
+test_expect_success 'setup reflogs' '
+       test_commit base &&
+       git checkout -b branch &&
+       test_commit one &&
+       git reset --hard HEAD^ &&
+       test_commit two &&
+       test_commit three
+'
+
+test_expect_success '--reflog shows reflog entries' '
+       cat >expect <<-\EOF &&
+       ! [branch@{0}] (0 seconds ago) commit: three
+        ! [branch@{1}] (60 seconds ago) commit: two
+         ! [branch@{2}] (2 minutes ago) reset: moving to HEAD^
+          ! [branch@{3}] (2 minutes ago) commit: one
+       ----
+       +    [branch@{0}] three
+       ++   [branch@{1}] two
+          + [branch@{3}] one
+       ++++ [branch@{2}] base
+       EOF
+       # the output always contains relative timestamps; use
+       # a known time to get deterministic results
+       GIT_TEST_DATE_NOW=$test_tick \
+       git show-branch --reflog branch >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success '--reflog handles missing reflog' '
+       git reflog expire --expire=now branch &&
+       git show-branch --reflog branch >actual &&
+       test_must_be_empty actual
+'
+
 test_done
index bf33aedf4b22868fd820330a460965d27e66cb91..8ebfa3c1be2fec46558c739fd3ccda37a952c225 100755 (executable)
@@ -118,4 +118,26 @@ test_expect_success 'log notes cache and still use cache for -p' '
        git log --no-walk -p refs/notes/textconv/magic HEAD
 '
 
+test_expect_success 'caching is silently ignored outside repo' '
+       mkdir -p non-repo &&
+       echo one >non-repo/one &&
+       echo two >non-repo/two &&
+       echo "* diff=test" >attr &&
+       test_expect_code 1 \
+       nongit git -c core.attributesFile="$PWD/attr" \
+                  -c diff.test.textconv="tr a-z A-Z <" \
+                  -c diff.test.cachetextconv=true \
+                  diff --no-index one two >actual &&
+       cat >expect <<-\EOF &&
+       diff --git a/one b/two
+       index 5626abf..f719efd 100644
+       --- a/one
+       +++ b/two
+       @@ -1 +1 @@
+       -ONE
+       +TWO
+       EOF
+       test_cmp expect actual
+'
+
 test_done
index 12ac43687366d72f2fa2870c72a9d5563a05fc66..29e9974cdfd421f6fb7dd17fa2bb12db8d5d6e04 100755 (executable)
@@ -945,4 +945,49 @@ test_expect_success 'check the input format when --stdin is passed' '
        test_cmp expect actual
 '
 
+test_expect_success '--merge-base with tree OIDs' '
+       git merge-tree --merge-base=side1^ side1 side3 >with-commits &&
+       git merge-tree --merge-base=side1^^{tree} side1^{tree} side3^{tree} >with-trees &&
+       test_cmp with-commits with-trees
+'
+
+test_expect_success 'error out on missing tree objects' '
+       git init --bare missing-tree.git &&
+       git rev-list side3 >list &&
+       git rev-parse side3^: >>list &&
+       git pack-objects missing-tree.git/objects/pack/side3-tree-is-missing <list &&
+       side3=$(git rev-parse side3) &&
+       test_must_fail git --git-dir=missing-tree.git merge-tree $side3^ $side3 >actual 2>err &&
+       test_grep "Could not read $(git rev-parse $side3:)" err &&
+       test_must_be_empty actual
+'
+
+test_expect_success 'error out on missing blob objects' '
+       echo 1 | git hash-object -w --stdin >blob1 &&
+       echo 2 | git hash-object -w --stdin >blob2 &&
+       echo 3 | git hash-object -w --stdin >blob3 &&
+       printf "100644 blob $(cat blob1)\tblob\n" | git mktree >tree1 &&
+       printf "100644 blob $(cat blob2)\tblob\n" | git mktree >tree2 &&
+       printf "100644 blob $(cat blob3)\tblob\n" | git mktree >tree3 &&
+       git init --bare missing-blob.git &&
+       cat blob1 blob3 tree1 tree2 tree3 |
+       git pack-objects missing-blob.git/objects/pack/side1-whatever-is-missing &&
+       test_must_fail git --git-dir=missing-blob.git >actual 2>err \
+               merge-tree --merge-base=$(cat tree1) $(cat tree2) $(cat tree3) &&
+       test_grep "unable to read blob object $(cat blob2)" err &&
+       test_must_be_empty actual
+'
+
+test_expect_success 'error out on missing commits as well' '
+       git init --bare missing-commit.git &&
+       git rev-list --objects side1 side3 >list-including-initial &&
+       grep -v ^$(git rev-parse side1^) <list-including-initial >list &&
+       git pack-objects missing-commit.git/objects/pack/missing-initial <list &&
+       side1=$(git rev-parse side1) &&
+       side3=$(git rev-parse side3) &&
+       test_must_fail git --git-dir=missing-commit.git \
+               merge-tree --allow-unrelated-histories $side1 $side3 >actual &&
+       test_must_be_empty actual
+'
+
 test_done
index b1cfe8b7dba816ddcaee85c0a3e19d0c958e6cd5..3dcb3340a36bb0e0efca1c1dfad6373dd5aeedc5 100755 (executable)
@@ -131,7 +131,6 @@ test_expect_success 'git upload-pack --advertise-refs: v2' '
        fetch=shallow wait-for-done
        server-option
        object-format=$(test_oid algo)
-       object-info
        0000
        EOF
 
index 3591bc2417119c75181cc1884ea9e48a7a87646a..c48830de8fe20448116f7988b450fe2d52d786e3 100755 (executable)
@@ -20,7 +20,6 @@ test_expect_success 'test capability advertisement' '
        fetch=shallow wait-for-done
        server-option
        object-format=$(test_oid algo)
-       object-info
        EOF
        cat >expect.trailer <<-EOF &&
        0000
@@ -323,6 +322,8 @@ test_expect_success 'unexpected lines are not allowed in fetch request' '
 # Test the basics of object-info
 #
 test_expect_success 'basics of object-info' '
+       test_config transfer.advertiseObjectInfo true &&
+
        test-tool pkt-line pack >in <<-EOF &&
        command=object-info
        object-format=$(test_oid algo)
@@ -380,4 +381,25 @@ test_expect_success 'basics of bundle-uri: dies if not enabled' '
        test_must_be_empty out
 '
 
+test_expect_success 'object-info missing from capabilities when disabled' '
+       test_config transfer.advertiseObjectInfo false &&
+
+       GIT_TEST_SIDEBAND_ALL=0 test-tool serve-v2 \
+               --advertise-capabilities >out &&
+       test-tool pkt-line unpack <out >actual &&
+
+       ! grep object.info actual
+'
+
+test_expect_success 'object-info commands rejected when disabled' '
+       test_config transfer.advertiseObjectInfo false &&
+
+       test-tool pkt-line pack >in <<-EOF &&
+       command=object-info
+       EOF
+
+       test_must_fail test-tool serve-v2 --stateless-rpc <in 2>err &&
+       grep invalid.command err
+'
+
 test_done
index 6ef4971845fb4407977e98cb62b3468abfca5838..1ef540f73d34756673423ff77fa46567bd34b2ab 100755 (executable)
@@ -778,6 +778,25 @@ test_expect_success 'archive with custom path does not request v2' '
        ! grep ^GIT_PROTOCOL env.trace
 '
 
+test_expect_success 'reject client packfile-uris if not advertised' '
+       {
+               packetize command=fetch &&
+               packetize object-format=$(test_oid algo) &&
+               printf 0001 &&
+               packetize packfile-uris https &&
+               packetize done &&
+               printf 0000
+       } >input &&
+       test_must_fail env GIT_PROTOCOL=version=2 \
+               git upload-pack client <input &&
+       test_must_fail env GIT_PROTOCOL=version=2 \
+               git -c uploadpack.blobpackfileuri \
+               upload-pack client <input &&
+       GIT_PROTOCOL=version=2 \
+               git -c uploadpack.blobpackfileuri=anything \
+               upload-pack client <input
+'
+
 # Test protocol v2 with 'http://' transport
 #
 . "$TEST_DIRECTORY"/lib-httpd.sh
index 1544d6dc6ba19170a1dfdb8ded299530f70be1de..bcfb358c51cc087efd75aa04488f9b16a75e7293 100755 (executable)
@@ -12,6 +12,11 @@ url=$2
 
 dir="$GIT_DIR/testgit/$alias"
 
+if ! git rev-parse --is-inside-git-dir
+then
+       exit 1
+fi
+
 h_refspec="refs/heads/*:refs/testgit/$alias/heads/*"
 t_refspec="refs/tags/*:refs/testgit/$alias/tags/*"
 
index 211672759a2260e5a1bb572f96aad93b3d3f91e5..127180e1c9a246769fb5bb7ede57f814abb4fc79 100755 (executable)
@@ -10,7 +10,10 @@ TEST_PASSES_SANITIZE_LEAK=true
 test_expect_success 'create repository and alternate directory' '
        test_commit 1 &&
        test_commit 2 &&
-       test_commit 3
+       test_commit 3 &&
+       git tag -m "tag message" annot_tag HEAD~1 &&
+       git tag regul_tag HEAD~1 &&
+       git branch a_branch HEAD~1
 '
 
 # We manually corrupt the repository, which means that the commit-graph may
@@ -46,9 +49,10 @@ do
                        git rev-list --objects --no-object-names \
                                HEAD ^$obj >expect.raw &&
 
-                       # Blobs are shared by all commits, so evethough a commit/tree
+                       # Blobs are shared by all commits, so evethough a commit/tree
                        # might be skipped, its blob must be accounted for.
-                       if [ $obj != "HEAD:1.t" ]; then
+                       if test $obj != "HEAD:1.t"
+                       then
                                echo $(git rev-parse HEAD:1.t) >>expect.raw &&
                                echo $(git rev-parse HEAD:2.t) >>expect.raw
                        fi &&
@@ -77,4 +81,69 @@ do
        done
 done
 
+for missing_tip in "annot_tag" "regul_tag" "a_branch" "HEAD~1" "HEAD~1^{tree}" "HEAD:1.t"
+do
+       # We want to check that things work when both
+       #   - all the tips passed are missing (case existing_tip = ""), and
+       #   - there is one missing tip and one existing tip (case existing_tip = "HEAD")
+       for existing_tip in "" "HEAD"
+       do
+               for action in "allow-any" "print"
+               do
+                       test_expect_success "--missing=$action with tip '$missing_tip' missing and tip '$existing_tip'" '
+                               # Before the object is made missing, we use rev-list to
+                               # get the expected oids.
+                               if test "$existing_tip" = "HEAD"
+                               then
+                                       git rev-list --objects --no-object-names \
+                                               HEAD ^$missing_tip >expect.raw
+                               else
+                                       >expect.raw
+                               fi &&
+
+                               # Blobs are shared by all commits, so even though a commit/tree
+                               # might be skipped, its blob must be accounted for.
+                               if test "$existing_tip" = "HEAD" && test $missing_tip != "HEAD:1.t"
+                               then
+                                       echo $(git rev-parse HEAD:1.t) >>expect.raw &&
+                                       echo $(git rev-parse HEAD:2.t) >>expect.raw
+                               fi &&
+
+                               missing_oid="$(git rev-parse $missing_tip)" &&
+
+                               if test "$missing_tip" = "annot_tag"
+                               then
+                                       oid="$(git rev-parse $missing_tip^{commit})" &&
+                                       echo "$missing_oid" >>expect.raw
+                               else
+                                       oid="$missing_oid"
+                               fi &&
+
+                               path=".git/objects/$(test_oid_to_path $oid)" &&
+
+                               mv "$path" "$path.hidden" &&
+                               test_when_finished "mv $path.hidden $path" &&
+
+                               git rev-list --missing=$action --objects --no-object-names \
+                                    $missing_oid $existing_tip >actual.raw &&
+
+                               # When the action is to print, we should also add the missing
+                               # oid to the expect list.
+                               case $action in
+                               allow-any)
+                                       ;;
+                               print)
+                                       grep ?$oid actual.raw &&
+                                       echo ?$oid >>expect.raw
+                                       ;;
+                               esac &&
+
+                               sort actual.raw >actual &&
+                               sort expect.raw >expect &&
+                               test_cmp expect actual
+                       '
+               done
+       done
+done
+
 test_done
index 561080bf240d6d5de7b557c6332995846f60e56e..cdc02706404b34b17b29692d72d97fab7eba58b1 100755 (executable)
@@ -878,7 +878,7 @@ test_expect_success 'broken branch creation' '
 
 echo "" > expected.ok
 cat > expected.missing-tree.default <<EOF
-fatal: unable to read tree $deleted
+fatal: unable to read tree ($deleted)
 EOF
 
 test_expect_success 'bisect fails if tree is broken on start commit' '
index 82f3d1ea0f25ed60900b09a454e3c98965db574f..948f1bb5f44e66b80004cce1b1ac2b9ee3bd4ac9 100755 (executable)
@@ -31,6 +31,37 @@ test_expect_success 'setup some history and refs' '
        git update-ref refs/odd/spot main
 '
 
+test_expect_success '--include-root-refs pattern prints pseudorefs' '
+       cat >expect <<-\EOF &&
+       HEAD
+       ORIG_HEAD
+       refs/heads/main
+       refs/heads/side
+       refs/odd/spot
+       refs/tags/annotated-tag
+       refs/tags/doubly-annotated-tag
+       refs/tags/doubly-signed-tag
+       refs/tags/four
+       refs/tags/one
+       refs/tags/signed-tag
+       refs/tags/three
+       refs/tags/two
+       EOF
+       git update-ref ORIG_HEAD main &&
+       git for-each-ref --format="%(refname)" --include-root-refs >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success '--include-root-refs with other patterns' '
+       cat >expect <<-\EOF &&
+       HEAD
+       ORIG_HEAD
+       EOF
+       git update-ref ORIG_HEAD main &&
+       git for-each-ref --format="%(refname)" --include-root-refs "*HEAD" >actual &&
+       test_cmp expect actual
+'
+
 test_expect_success 'filtering with --points-at' '
        cat >expect <<-\EOF &&
        refs/heads/main
index 70650521b042b29b41ddf7b0ee794095063e457f..7a3f1cb27c12b468cb8a97e80c75a86070c7c4a8 100755 (executable)
@@ -113,7 +113,7 @@ test_expect_success 'merging should conflict for non fast-forward' '
         git checkout -b test-nonforward-a b &&
          if test "$GIT_TEST_MERGE_ALGORITHM" = ort
          then
-               test_must_fail git merge c >actual &&
+               test_must_fail git merge c 2>actual &&
                sub_expect="go to submodule (sub), and either merge commit $(git -C sub rev-parse --short sub-c)" &&
                grep "$sub_expect" actual
          else
@@ -154,9 +154,9 @@ test_expect_success 'merging should conflict for non fast-forward (resolution ex
          git rev-parse --short sub-d > ../expect) &&
          if test "$GIT_TEST_MERGE_ALGORITHM" = ort
          then
-               test_must_fail git merge c >actual &&
+               test_must_fail git merge c >actual 2>sub-actual &&
                sub_expect="go to submodule (sub), and either merge commit $(git -C sub rev-parse --short sub-c)" &&
-               grep "$sub_expect" actual
+               grep "$sub_expect" sub-actual
          else
                test_must_fail git merge c 2> actual
          fi &&
@@ -181,9 +181,9 @@ test_expect_success 'merging should fail for ambiguous common parent' '
         ) &&
         if test "$GIT_TEST_MERGE_ALGORITHM" = ort
         then
-               test_must_fail git merge c >actual &&
+               test_must_fail git merge c >actual 2>sub-actual &&
                sub_expect="go to submodule (sub), and either merge commit $(git -C sub rev-parse --short sub-c)" &&
-               grep "$sub_expect" actual
+               grep "$sub_expect" sub-actual
         else
                test_must_fail git merge c 2> actual
         fi &&
@@ -227,7 +227,7 @@ test_expect_success 'merging should fail for changes that are backwards' '
        git commit -a -m "f" &&
 
        git checkout -b test-backward e &&
-       test_must_fail git merge f >actual &&
+       test_must_fail git merge f 2>actual &&
        if test "$GIT_TEST_MERGE_ALGORITHM" = ort
     then
                sub_expect="go to submodule (sub), and either merge commit $(git -C sub rev-parse --short sub-d)" &&
@@ -535,7 +535,7 @@ test_expect_success 'merging should fail with no merge base' '
        git checkout -b b init &&
        git add sub &&
        git commit -m "b" &&
-       test_must_fail git merge a >actual &&
+       test_must_fail git merge a 2>actual &&
        if test "$GIT_TEST_MERGE_ALGORITHM" = ort
     then
                sub_expect="go to submodule (sub), and either merge commit $(git -C sub rev-parse --short HEAD^1)" &&
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 2b3c363078bc06219c8b5c16b02f331b447f5102..aa2fdc31d1a672cb229457b05adcce90bd204aa6 100755 (executable)
@@ -116,7 +116,7 @@ test_expect_success 'rebasing submodule that should conflict' '
        test_tick &&
        git commit -m fourth &&
 
-       test_must_fail git rebase --onto HEAD^^ HEAD^ HEAD^0 >actual_output &&
+       test_must_fail git rebase --onto HEAD^^ HEAD^ HEAD^0 2>actual_output &&
        git ls-files -s submodule >actual &&
        (
                cd submodule &&
index a87c211d0b16d0ffc4f8c7c2d6366209af1198f5..b37e2018a74a7b28725fa277a95cca516daf5cbe 100755 (executable)
@@ -736,6 +736,11 @@ test_expect_success 'message shows date when it is explicitly set' '
          .git/COMMIT_EDITMSG
 '
 
+test_expect_success 'message does not have multiple scissors lines' '
+       git commit --cleanup=scissors -v --allow-empty -e -m foo &&
+       test $(grep -c -e "--- >8 ---" .git/COMMIT_EDITMSG) -eq 1
+'
+
 test_expect_success AUTOIDENT 'message shows committer when it is automatic' '
 
        echo >>negative &&
index 6a36be1e63c2bb84871e521349917be30ec6b3e3..96ae5d5880d6a597d0c553c4535081c2e51356df 100755 (executable)
@@ -91,58 +91,67 @@ test_expect_success 'difftool forwards arguments to diff' '
        rm for-diff
 '
 
-test_expect_success 'difftool ignores exit code' '
-       test_config difftool.error.cmd false &&
-       git difftool -y -t error branch
-'
+for opt in '' '--dir-diff'
+do
+       test_expect_success "difftool ${opt} ignores exit code" "
+               test_config difftool.error.cmd false &&
+               git difftool ${opt} -y -t error branch
+       "
 
-test_expect_success 'difftool forwards exit code with --trust-exit-code' '
-       test_config difftool.error.cmd false &&
-       test_must_fail git difftool -y --trust-exit-code -t error branch
-'
+       test_expect_success "difftool ${opt} forwards exit code with --trust-exit-code" "
+               test_config difftool.error.cmd false &&
+               test_must_fail git difftool ${opt} -y --trust-exit-code -t error branch
+       "
 
-test_expect_success 'difftool forwards exit code with --trust-exit-code for built-ins' '
-       test_config difftool.vimdiff.path false &&
-       test_must_fail git difftool -y --trust-exit-code -t vimdiff branch
-'
+       test_expect_success "difftool ${opt} forwards exit code with --trust-exit-code for built-ins" "
+               test_config difftool.vimdiff.path false &&
+               test_must_fail git difftool ${opt} -y --trust-exit-code -t vimdiff branch
+       "
 
-test_expect_success 'difftool honors difftool.trustExitCode = true' '
-       test_config difftool.error.cmd false &&
-       test_config difftool.trustExitCode true &&
-       test_must_fail git difftool -y -t error branch
-'
+       test_expect_success "difftool ${opt} honors difftool.trustExitCode = true" "
+               test_config difftool.error.cmd false &&
+               test_config difftool.trustExitCode true &&
+               test_must_fail git difftool ${opt} -y -t error branch
+       "
 
-test_expect_success 'difftool honors difftool.trustExitCode = false' '
-       test_config difftool.error.cmd false &&
-       test_config difftool.trustExitCode false &&
-       git difftool -y -t error branch
-'
+       test_expect_success "difftool ${opt} honors difftool.trustExitCode = false" "
+               test_config difftool.error.cmd false &&
+               test_config difftool.trustExitCode false &&
+               git difftool ${opt} -y -t error branch
+       "
 
-test_expect_success 'difftool ignores exit code with --no-trust-exit-code' '
-       test_config difftool.error.cmd false &&
-       test_config difftool.trustExitCode true &&
-       git difftool -y --no-trust-exit-code -t error branch
-'
+       test_expect_success "difftool ${opt} ignores exit code with --no-trust-exit-code" "
+               test_config difftool.error.cmd false &&
+               test_config difftool.trustExitCode true &&
+               git difftool ${opt} -y --no-trust-exit-code -t error branch
+       "
 
-test_expect_success 'difftool stops on error with --trust-exit-code' '
-       test_when_finished "rm -f for-diff .git/fail-right-file" &&
-       test_when_finished "git reset -- for-diff" &&
-       write_script .git/fail-right-file <<-\EOF &&
-       echo failed
-       exit 1
-       EOF
-       >for-diff &&
-       git add for-diff &&
-       test_must_fail git difftool -y --trust-exit-code \
-               --extcmd .git/fail-right-file branch >actual &&
-       test_line_count = 1 actual
-'
+       test_expect_success "difftool ${opt} stops on error with --trust-exit-code" "
+               test_when_finished 'rm -f for-diff .git/fail-right-file' &&
+               test_when_finished 'git reset -- for-diff' &&
+               write_script .git/fail-right-file <<-\EOF &&
+               echo failed
+               exit 1
+               EOF
+               >for-diff &&
+               git add for-diff &&
+               test_must_fail git difftool ${opt} -y --trust-exit-code \
+                       --extcmd .git/fail-right-file branch >actual &&
+               test_line_count = 1 actual
+       "
 
-test_expect_success 'difftool honors exit status if command not found' '
-       test_config difftool.nonexistent.cmd i-dont-exist &&
-       test_config difftool.trustExitCode false &&
-       test_must_fail git difftool -y -t nonexistent branch
-'
+       test_expect_success "difftool ${opt} honors exit status if command not found" "
+               test_config difftool.nonexistent.cmd i-dont-exist &&
+               test_config difftool.trustExitCode false &&
+               if test "${opt}" = '--dir-diff'
+               then
+                       expected_code=127
+               else
+                       expected_code=128
+               fi &&
+               test_expect_code \${expected_code} git difftool ${opt} -y -t nonexistent branch
+       "
+done
 
 test_expect_success 'difftool honors --gui' '
        difftool_test_setup &&
index 62de819a44e40c8fae821789e1d11753054a2258..3b038c338f2154e4470d3f472a448d5881adf54d 100755 (executable)
@@ -17,32 +17,32 @@ test_expect_success 'setup svnrepo' '
 test_expect_success 'basic clone' '
        test ! -d trunk &&
        git svn clone "$svnrepo"/project/trunk &&
-       test -d trunk/.git/svn &&
-       test -e trunk/foo &&
+       test_path_is_dir trunk/.git/svn &&
+       test_path_exists trunk/foo &&
        rm -rf trunk
        '
 
 test_expect_success 'clone to target directory' '
        test ! -d target &&
        git svn clone "$svnrepo"/project/trunk target &&
-       test -d target/.git/svn &&
-       test -e target/foo &&
+       test_path_is_dir target/.git/svn &&
+       test_path_exists target/foo &&
        rm -rf target
        '
 
 test_expect_success 'clone with --stdlayout' '
        test ! -d project &&
        git svn clone -s "$svnrepo"/project &&
-       test -d project/.git/svn &&
-       test -e project/foo &&
+       test_path_is_dir project/.git/svn &&
+       test_path_exists project/foo &&
        rm -rf project
        '
 
 test_expect_success 'clone to target directory with --stdlayout' '
        test ! -d target &&
        git svn clone -s "$svnrepo"/project target &&
-       test -d target/.git/svn &&
-       test -e target/foo &&
+       test_path_is_dir target/.git/svn &&
+       test_path_exists target/foo &&
        rm -rf target
        '
 
index 09606f1b3cfeb4244de4fbc12e4e96ddb7fd7de5..926ac8143943c91493eb9be0e42ca365fae56971 100755 (executable)
@@ -20,11 +20,7 @@ test_expect_success 'empty directories exist' '
                cd cloned &&
                for i in a b c d d/e d/e/f "weird file name"
                do
-                       if ! test -d "$i"
-                       then
-                               echo >&2 "$i does not exist" &&
-                               exit 1
-                       fi
+                       test_path_is_dir "$i" || exit 1
                done
        )
 '
@@ -37,11 +33,7 @@ test_expect_success 'option automkdirs set to false' '
                git svn fetch &&
                for i in a b c d d/e d/e/f "weird file name"
                do
-                       if test -d "$i"
-                       then
-                               echo >&2 "$i exists" &&
-                               exit 1
-                       fi
+                       test_path_is_missing "$i" || exit 1
                done
        )
 '
@@ -52,7 +44,7 @@ test_expect_success 'more emptiness' '
 
 test_expect_success 'git svn rebase creates empty directory' '
        ( cd cloned && git svn rebase ) &&
-       test -d cloned/"! !"
+       test_path_is_dir cloned/"! !"
 '
 
 test_expect_success 'git svn mkdirs recreates empty directories' '
@@ -62,11 +54,7 @@ test_expect_success 'git svn mkdirs recreates empty directories' '
                git svn mkdirs &&
                for i in a b c d d/e d/e/f "weird file name" "! !"
                do
-                       if ! test -d "$i"
-                       then
-                               echo >&2 "$i does not exist" &&
-                               exit 1
-                       fi
+                       test_path_is_dir "$i" || exit 1
                done
        )
 '
@@ -78,25 +66,13 @@ test_expect_success 'git svn mkdirs -r works' '
                git svn mkdirs -r7 &&
                for i in a b c d d/e d/e/f "weird file name"
                do
-                       if ! test -d "$i"
-                       then
-                               echo >&2 "$i does not exist" &&
-                               exit 1
-                       fi
+                       test_path_is_dir "$i" || exit 1
                done &&
 
-               if test -d "! !"
-               then
-                       echo >&2 "$i should not exist" &&
-                       exit 1
-               fi &&
+               test_path_is_missing "! !" || exit 1 &&
 
                git svn mkdirs -r8 &&
-               if ! test -d "! !"
-               then
-                       echo >&2 "$i not exist" &&
-                       exit 1
-               fi
+               test_path_is_dir "! !" || exit 1
        )
 '
 
@@ -114,11 +90,7 @@ test_expect_success 'empty directories in trunk exist' '
                cd trunk &&
                for i in a "weird file name"
                do
-                       if ! test -d "$i"
-                       then
-                               echo >&2 "$i does not exist" &&
-                               exit 1
-                       fi
+                       test_path_is_dir "$i" || exit 1
                done
        )
 '
@@ -129,7 +101,7 @@ test_expect_success 'remove a top-level directory from svn' '
 
 test_expect_success 'removed top-level directory does not exist' '
        git svn clone "$svnrepo" removed &&
-       test ! -e removed/d
+       test_path_is_missing removed/d
 
 '
 unhandled=.git/svn/refs/remotes/git-svn/unhandled.log
@@ -143,15 +115,11 @@ test_expect_success 'git svn gc-ed files work' '
                        svn_cmd mkdir -m gz "$svnrepo"/gz &&
                        git reset --hard $(git rev-list HEAD | tail -1) &&
                        git svn rebase &&
-                       test -f "$unhandled".gz &&
-                       test -f "$unhandled" &&
+                       test_path_is_file "$unhandled".gz &&
+                       test_path_is_file "$unhandled" &&
                        for i in a b c "weird file name" gz "! !"
                        do
-                               if ! test -d "$i"
-                               then
-                                       echo >&2 "$i does not exist" &&
-                                       exit 1
-                               fi
+                               test_path_is_dir "$i" || exit 1
                        done
                fi
        )
index f3154899843708308a6d43074d200b8d145424be..d6ac1fe678dc73c6e126c6d1357c21d0af267a56 100644 (file)
@@ -1,30 +1,19 @@
 #include "test-lib.h"
 
-static int is_in(const char *s, int ch)
-{
-       /*
-        * We can't find NUL using strchr. Accept it as the first
-        * character in the spec -- there are no empty classes.
-        */
-       if (ch == '\0')
-               return ch == *s;
-       if (*s == '\0')
-               s++;
-       return !!strchr(s, ch);
-}
-
-/* Macro to test a character type */
-#define TEST_CTYPE_FUNC(func, string) \
-static void test_ctype_##func(void) { \
-       for (int i = 0; i < 256; i++) { \
-               if (!check_int(func(i), ==, is_in(string, i))) \
-                       test_msg("       i: 0x%02x", i); \
+#define TEST_CHAR_CLASS(class, string) do { \
+       size_t len = ARRAY_SIZE(string) - 1 + \
+               BUILD_ASSERT_OR_ZERO(ARRAY_SIZE(string) > 0) + \
+               BUILD_ASSERT_OR_ZERO(sizeof(string[0]) == sizeof(char)); \
+       int skip = test__run_begin(); \
+       if (!skip) { \
+               for (int i = 0; i < 256; i++) { \
+                       if (!check_int(class(i), ==, !!memchr(string, i, len)))\
+                               test_msg("      i: 0x%02x", i); \
+               } \
+               check(!class(EOF)); \
        } \
-       if (!check(!func(EOF))) \
-                       test_msg("      i: 0x%02x (EOF)", EOF); \
-}
-
-#define TEST_CHAR_CLASS(class) TEST(test_ctype_##class(), #class " works")
+       test__run_end(!skip, TEST_LOCATION(), #class " works"); \
+} while (0)
 
 #define DIGIT "0123456789"
 #define LOWER "abcdefghijklmnopqrstuvwxyz"
@@ -44,37 +33,21 @@ static void test_ctype_##func(void) { \
        "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" \
        "\x7f"
 
-TEST_CTYPE_FUNC(isdigit, DIGIT)
-TEST_CTYPE_FUNC(isspace, " \n\r\t")
-TEST_CTYPE_FUNC(isalpha, LOWER UPPER)
-TEST_CTYPE_FUNC(isalnum, LOWER UPPER DIGIT)
-TEST_CTYPE_FUNC(is_glob_special, "*?[\\")
-TEST_CTYPE_FUNC(is_regex_special, "$()*+.?[\\^{|")
-TEST_CTYPE_FUNC(is_pathspec_magic, "!\"#%&',-/:;<=>@_`~")
-TEST_CTYPE_FUNC(isascii, ASCII)
-TEST_CTYPE_FUNC(islower, LOWER)
-TEST_CTYPE_FUNC(isupper, UPPER)
-TEST_CTYPE_FUNC(iscntrl, CNTRL)
-TEST_CTYPE_FUNC(ispunct, PUNCT)
-TEST_CTYPE_FUNC(isxdigit, DIGIT "abcdefABCDEF")
-TEST_CTYPE_FUNC(isprint, LOWER UPPER DIGIT PUNCT " ")
-
 int cmd_main(int argc, const char **argv) {
-       /* Run all character type tests */
-       TEST_CHAR_CLASS(isspace);
-       TEST_CHAR_CLASS(isdigit);
-       TEST_CHAR_CLASS(isalpha);
-       TEST_CHAR_CLASS(isalnum);
-       TEST_CHAR_CLASS(is_glob_special);
-       TEST_CHAR_CLASS(is_regex_special);
-       TEST_CHAR_CLASS(is_pathspec_magic);
-       TEST_CHAR_CLASS(isascii);
-       TEST_CHAR_CLASS(islower);
-       TEST_CHAR_CLASS(isupper);
-       TEST_CHAR_CLASS(iscntrl);
-       TEST_CHAR_CLASS(ispunct);
-       TEST_CHAR_CLASS(isxdigit);
-       TEST_CHAR_CLASS(isprint);
+       TEST_CHAR_CLASS(isspace, " \n\r\t");
+       TEST_CHAR_CLASS(isdigit, DIGIT);
+       TEST_CHAR_CLASS(isalpha, LOWER UPPER);
+       TEST_CHAR_CLASS(isalnum, LOWER UPPER DIGIT);
+       TEST_CHAR_CLASS(is_glob_special, "*?[\\");
+       TEST_CHAR_CLASS(is_regex_special, "$()*+.?[\\^{|");
+       TEST_CHAR_CLASS(is_pathspec_magic, "!\"#%&',-/:;<=>@_`~");
+       TEST_CHAR_CLASS(isascii, ASCII);
+       TEST_CHAR_CLASS(islower, LOWER);
+       TEST_CHAR_CLASS(isupper, UPPER);
+       TEST_CHAR_CLASS(iscntrl, CNTRL);
+       TEST_CHAR_CLASS(ispunct, PUNCT);
+       TEST_CHAR_CLASS(isxdigit, DIGIT "abcdefABCDEF");
+       TEST_CHAR_CLASS(isprint, LOWER UPPER DIGIT PUNCT " ");
 
        return test_done();
 }
index b517792ba23a2104f5f8207519e64cbc5d2daaa4..690fa6569bd7fe03ca104e3e789fd67b58e41270 100644 (file)
@@ -100,7 +100,7 @@ void *fill_tree_descriptor(struct repository *r,
        if (oid) {
                buf = read_object_with_reference(r, oid, OBJ_TREE, &size, NULL);
                if (!buf)
-                       die("unable to read tree %s", oid_to_hex(oid));
+                       die(_("unable to read tree (%s)"), oid_to_hex(oid));
        }
        init_tree_desc(desc, buf, size);
        return buf;
index 2537affa90762228a19d305be749f410d5afb5dc..902144b9d3470b9a603a584f7f4207511b15dc2b 100644 (file)
@@ -28,6 +28,7 @@
 #include "shallow.h"
 #include "write-or-die.h"
 #include "json-writer.h"
+#include "strmap.h"
 
 /* Remember to update object flag allocation in object.h */
 #define THEY_HAVE      (1u << 11)
@@ -61,12 +62,11 @@ struct upload_pack_data {
        struct string_list symref;                              /* v0 only */
        struct object_array want_obj;
        struct object_array have_obj;
-       struct oid_array haves;                                 /* v2 only */
-       struct string_list wanted_refs;                         /* v2 only */
+       struct strmap wanted_refs;                              /* v2 only */
        struct strvec hidden_refs;
 
        struct object_array shallows;
-       struct string_list deepen_not;
+       struct oidset deepen_not;
        struct object_array extra_edge_obj;
        int depth;
        timestamp_t deepen_since;
@@ -113,6 +113,8 @@ struct upload_pack_data {
        unsigned done : 1;                                      /* v2 only */
        unsigned allow_ref_in_want : 1;                         /* v2 only */
        unsigned allow_sideband_all : 1;                        /* v2 only */
+       unsigned seen_haves : 1;                                /* v2 only */
+       unsigned allow_packfile_uris : 1;                       /* v2 only */
        unsigned advertise_sid : 1;
        unsigned sent_capabilities : 1;
 };
@@ -120,13 +122,12 @@ struct upload_pack_data {
 static void upload_pack_data_init(struct upload_pack_data *data)
 {
        struct string_list symref = STRING_LIST_INIT_DUP;
-       struct string_list wanted_refs = STRING_LIST_INIT_DUP;
+       struct strmap wanted_refs = STRMAP_INIT;
        struct strvec hidden_refs = STRVEC_INIT;
        struct object_array want_obj = OBJECT_ARRAY_INIT;
        struct object_array have_obj = OBJECT_ARRAY_INIT;
-       struct oid_array haves = OID_ARRAY_INIT;
        struct object_array shallows = OBJECT_ARRAY_INIT;
-       struct string_list deepen_not = STRING_LIST_INIT_DUP;
+       struct oidset deepen_not = OID_ARRAY_INIT;
        struct string_list uri_protocols = STRING_LIST_INIT_DUP;
        struct object_array extra_edge_obj = OBJECT_ARRAY_INIT;
        struct string_list allowed_filters = STRING_LIST_INIT_DUP;
@@ -137,7 +138,6 @@ static void upload_pack_data_init(struct upload_pack_data *data)
        data->hidden_refs = hidden_refs;
        data->want_obj = want_obj;
        data->have_obj = have_obj;
-       data->haves = haves;
        data->shallows = shallows;
        data->deepen_not = deepen_not;
        data->uri_protocols = uri_protocols;
@@ -155,13 +155,12 @@ static void upload_pack_data_init(struct upload_pack_data *data)
 static void upload_pack_data_clear(struct upload_pack_data *data)
 {
        string_list_clear(&data->symref, 1);
-       string_list_clear(&data->wanted_refs, 1);
+       strmap_clear(&data->wanted_refs, 1);
        strvec_clear(&data->hidden_refs);
        object_array_clear(&data->want_obj);
        object_array_clear(&data->have_obj);
-       oid_array_clear(&data->haves);
        object_array_clear(&data->shallows);
-       string_list_clear(&data->deepen_not, 0);
+       oidset_clear(&data->deepen_not);
        object_array_clear(&data->extra_edge_obj);
        list_objects_filter_release(&data->filter_options);
        string_list_clear(&data->allowed_filters, 0);
@@ -463,7 +462,7 @@ static void create_pack_file(struct upload_pack_data *pack_data,
 
  fail:
        free(output_state);
-       send_client_data(3, abort_msg, sizeof(abort_msg),
+       send_client_data(3, abort_msg, strlen(abort_msg),
                         pack_data->use_sideband);
        die("git upload-pack: %s", abort_msg);
 }
@@ -471,7 +470,9 @@ static void create_pack_file(struct upload_pack_data *pack_data,
 static int do_got_oid(struct upload_pack_data *data, const struct object_id *oid)
 {
        int we_knew_they_have = 0;
-       struct object *o = parse_object(the_repository, oid);
+       struct object *o = parse_object_with_flags(the_repository, oid,
+                                                  PARSE_OBJECT_SKIP_HASH_CHECK |
+                                                  PARSE_OBJECT_DISCARD_TREE);
 
        if (!o)
                die("oops (%s)", oid_to_hex(oid));
@@ -528,8 +529,6 @@ static int get_common_commits(struct upload_pack_data *data,
        int got_other = 0;
        int sent_ready = 0;
 
-       save_commit_buffer = 0;
-
        for (;;) {
                const char *arg;
 
@@ -926,12 +925,13 @@ static int send_shallow_list(struct upload_pack_data *data)
                strvec_push(&av, "rev-list");
                if (data->deepen_since)
                        strvec_pushf(&av, "--max-age=%"PRItime, data->deepen_since);
-               if (data->deepen_not.nr) {
+               if (oidset_size(&data->deepen_not)) {
+                       const struct object_id *oid;
+                       struct oidset_iter iter;
                        strvec_push(&av, "--not");
-                       for (i = 0; i < data->deepen_not.nr; i++) {
-                               struct string_list_item *s = data->deepen_not.items + i;
-                               strvec_push(&av, s->string);
-                       }
+                       oidset_iter_init(&data->deepen_not, &iter);
+                       while ((oid = oidset_iter_next(&iter)))
+                               strvec_push(&av, oid_to_hex(oid));
                        strvec_push(&av, "--not");
                }
                for (i = 0; i < data->want_obj.nr; i++) {
@@ -1007,7 +1007,7 @@ static int process_deepen_since(const char *line, timestamp_t *deepen_since, int
        return 0;
 }
 
-static int process_deepen_not(const char *line, struct string_list *deepen_not, int *deepen_rev_list)
+static int process_deepen_not(const char *line, struct oidset *deepen_not, int *deepen_rev_list)
 {
        const char *arg;
        if (skip_prefix(line, "deepen-not ", &arg)) {
@@ -1015,7 +1015,7 @@ static int process_deepen_not(const char *line, struct string_list *deepen_not,
                struct object_id oid;
                if (expand_ref(the_repository, arg, strlen(arg), &oid, &ref) != 1)
                        die("git upload-pack: ambiguous deepen-not: %s", line);
-               string_list_append(deepen_not, ref);
+               oidset_insert(deepen_not, &oid);
                free(ref);
                *deepen_rev_list = 1;
                return 1;
@@ -1151,7 +1151,9 @@ static void receive_needs(struct upload_pack_data *data,
                        free(client_sid);
                }
 
-               o = parse_object(the_repository, &oid_buf);
+               o = parse_object_with_flags(the_repository, &oid_buf,
+                                           PARSE_OBJECT_SKIP_HASH_CHECK |
+                                           PARSE_OBJECT_DISCARD_TREE);
                if (!o) {
                        packet_writer_error(&data->writer,
                                            "upload-pack: not our ref %s",
@@ -1362,6 +1364,9 @@ static int upload_pack_config(const char *var, const char *value,
                data->allow_ref_in_want = git_config_bool(var, value);
        } else if (!strcmp("uploadpack.allowsidebandall", var)) {
                data->allow_sideband_all = git_config_bool(var, value);
+       } else if (!strcmp("uploadpack.blobpackfileuri", var)) {
+               if (value)
+                       data->allow_packfile_uris = 1;
        } else if (!strcmp("core.precomposeunicode", var)) {
                precomposed_unicode = git_config_bool(var, value);
        } else if (!strcmp("transfer.advertisesid", var)) {
@@ -1385,10 +1390,13 @@ static int upload_pack_protected_config(const char *var, const char *value,
        return 0;
 }
 
-static void get_upload_pack_config(struct upload_pack_data *data)
+static void get_upload_pack_config(struct repository *r,
+                                  struct upload_pack_data *data)
 {
-       git_config(upload_pack_config, data);
+       repo_config(r, upload_pack_config, data);
        git_protected_config(upload_pack_protected_config, data);
+
+       data->allow_sideband_all |= git_env_bool("GIT_TEST_SIDEBAND_ALL", 0);
 }
 
 void upload_pack(const int advertise_refs, const int stateless_rpc,
@@ -1398,7 +1406,7 @@ void upload_pack(const int advertise_refs, const int stateless_rpc,
        struct upload_pack_data data;
 
        upload_pack_data_init(&data);
-       get_upload_pack_config(&data);
+       get_upload_pack_config(the_repository, &data);
 
        data.stateless_rpc = stateless_rpc;
        data.timeout = timeout;
@@ -1468,7 +1476,8 @@ static int parse_want(struct packet_writer *writer, const char *line,
                            "expected to get oid, not '%s'", line);
 
                o = parse_object_with_flags(the_repository, &oid,
-                                           PARSE_OBJECT_SKIP_HASH_CHECK);
+                                           PARSE_OBJECT_SKIP_HASH_CHECK |
+                                           PARSE_OBJECT_DISCARD_TREE);
 
                if (!o) {
                        packet_writer_error(writer,
@@ -1490,14 +1499,13 @@ static int parse_want(struct packet_writer *writer, const char *line,
 }
 
 static int parse_want_ref(struct packet_writer *writer, const char *line,
-                         struct string_list *wanted_refs,
+                         struct strmap *wanted_refs,
                          struct strvec *hidden_refs,
                          struct object_array *want_obj)
 {
        const char *refname_nons;
        if (skip_prefix(line, "want-ref ", &refname_nons)) {
                struct object_id oid;
-               struct string_list_item *item;
                struct object *o = NULL;
                struct strbuf refname = STRBUF_INIT;
 
@@ -1509,8 +1517,11 @@ static int parse_want_ref(struct packet_writer *writer, const char *line,
                }
                strbuf_release(&refname);
 
-               item = string_list_append(wanted_refs, refname_nons);
-               item->util = oiddup(&oid);
+               if (strmap_put(wanted_refs, refname_nons, oiddup(&oid))) {
+                       packet_writer_error(writer, "duplicate want-ref %s",
+                                           refname_nons);
+                       die("duplicate want-ref %s", refname_nons);
+               }
 
                if (!starts_with(refname_nons, "refs/tags/")) {
                        struct commit *commit = lookup_commit_in_graph(the_repository, &oid);
@@ -1532,15 +1543,14 @@ static int parse_want_ref(struct packet_writer *writer, const char *line,
        return 0;
 }
 
-static int parse_have(const char *line, struct oid_array *haves)
+static int parse_have(const char *line, struct upload_pack_data *data)
 {
        const char *arg;
        if (skip_prefix(line, "have ", &arg)) {
                struct object_id oid;
 
-               if (get_oid_hex(arg, &oid))
-                       die("git upload-pack: expected SHA1 object, got '%s'", arg);
-               oid_array_append(haves, &oid);
+               got_oid(data, arg, &oid);
+               data->seen_haves = 1;
                return 1;
        }
 
@@ -1552,13 +1562,13 @@ static void trace2_fetch_info(struct upload_pack_data *data)
        struct json_writer jw = JSON_WRITER_INIT;
 
        jw_object_begin(&jw, 0);
-       jw_object_intmax(&jw, "haves", data->haves.nr);
+       jw_object_intmax(&jw, "haves", data->have_obj.nr);
        jw_object_intmax(&jw, "wants", data->want_obj.nr);
-       jw_object_intmax(&jw, "want-refs", data->wanted_refs.nr);
+       jw_object_intmax(&jw, "want-refs", strmap_get_size(&data->wanted_refs));
        jw_object_intmax(&jw, "depth", data->depth);
        jw_object_intmax(&jw, "shallows", data->shallows.nr);
        jw_object_bool(&jw, "deepen-since", data->deepen_since);
-       jw_object_intmax(&jw, "deepen-not", data->deepen_not.nr);
+       jw_object_intmax(&jw, "deepen-not", oidset_size(&data->deepen_not));
        jw_object_bool(&jw, "deepen-relative", data->deepen_relative);
        if (data->filter_options.choice)
                jw_object_string(&jw, "filter", list_object_filter_config_name(data->filter_options.choice));
@@ -1586,7 +1596,7 @@ static void process_args(struct packet_reader *request,
                                   &data->hidden_refs, &data->want_obj))
                        continue;
                /* process have line */
-               if (parse_have(arg, &data->haves))
+               if (parse_have(arg, data))
                        continue;
 
                /* process args like thin-pack */
@@ -1638,14 +1648,17 @@ static void process_args(struct packet_reader *request,
                        continue;
                }
 
-               if ((git_env_bool("GIT_TEST_SIDEBAND_ALL", 0) ||
-                    data->allow_sideband_all) &&
+               if (data->allow_sideband_all &&
                    !strcmp(arg, "sideband-all")) {
                        data->writer.use_sideband = 1;
                        continue;
                }
 
-               if (skip_prefix(arg, "packfile-uris ", &p)) {
+               if (data->allow_packfile_uris &&
+                   skip_prefix(arg, "packfile-uris ", &p)) {
+                       if (data->uri_protocols.nr)
+                               send_err_and_die(data,
+                                                "multiple packfile-uris lines forbidden");
                        string_list_split(&data->uri_protocols, p, ',', -1);
                        continue;
                }
@@ -1664,27 +1677,7 @@ static void process_args(struct packet_reader *request,
                trace2_fetch_info(data);
 }
 
-static int process_haves(struct upload_pack_data *data, struct oid_array *common)
-{
-       int i;
-
-       /* Process haves */
-       for (i = 0; i < data->haves.nr; i++) {
-               const struct object_id *oid = &data->haves.oid[i];
-
-               if (!repo_has_object_file_with_flags(the_repository, oid,
-                                                    OBJECT_INFO_QUICK | OBJECT_INFO_SKIP_FETCH_OBJECT))
-                       continue;
-
-               oid_array_append(common, oid);
-
-               do_got_oid(data, oid);
-       }
-
-       return 0;
-}
-
-static int send_acks(struct upload_pack_data *data, struct oid_array *acks)
+static int send_acks(struct upload_pack_data *data, struct object_array *acks)
 {
        int i;
 
@@ -1696,7 +1689,7 @@ static int send_acks(struct upload_pack_data *data, struct oid_array *acks)
 
        for (i = 0; i < acks->nr; i++) {
                packet_writer_write(&data->writer, "ACK %s\n",
-                                   oid_to_hex(&acks->oid[i]));
+                                   oid_to_hex(&acks->objects[i].item->oid));
        }
 
        if (!data->wait_for_done && ok_to_give_up(data)) {
@@ -1710,13 +1703,11 @@ static int send_acks(struct upload_pack_data *data, struct oid_array *acks)
 
 static int process_haves_and_send_acks(struct upload_pack_data *data)
 {
-       struct oid_array common = OID_ARRAY_INIT;
        int ret = 0;
 
-       process_haves(data, &common);
        if (data->done) {
                ret = 1;
-       } else if (send_acks(data, &common)) {
+       } else if (send_acks(data, &data->have_obj)) {
                packet_writer_delim(&data->writer);
                ret = 1;
        } else {
@@ -1725,24 +1716,23 @@ static int process_haves_and_send_acks(struct upload_pack_data *data)
                ret = 0;
        }
 
-       oid_array_clear(&data->haves);
-       oid_array_clear(&common);
        return ret;
 }
 
 static void send_wanted_ref_info(struct upload_pack_data *data)
 {
-       const struct string_list_item *item;
+       struct hashmap_iter iter;
+       const struct strmap_entry *e;
 
-       if (!data->wanted_refs.nr)
+       if (strmap_empty(&data->wanted_refs))
                return;
 
        packet_writer_write(&data->writer, "wanted-refs\n");
 
-       for_each_string_list_item(item, &data->wanted_refs) {
+       strmap_for_each_entry(&data->wanted_refs, &iter, e) {
                packet_writer_write(&data->writer, "%s %s\n",
-                                   oid_to_hex(item->util),
-                                   item->string);
+                                   oid_to_hex(e->value),
+                                   e->key);
        }
 
        packet_writer_delim(&data->writer);
@@ -1771,7 +1761,7 @@ enum fetch_state {
        FETCH_DONE,
 };
 
-int upload_pack_v2(struct repository *r UNUSED, struct packet_reader *request)
+int upload_pack_v2(struct repository *r, struct packet_reader *request)
 {
        enum fetch_state state = FETCH_PROCESS_ARGS;
        struct upload_pack_data data;
@@ -1780,7 +1770,7 @@ int upload_pack_v2(struct repository *r UNUSED, struct packet_reader *request)
 
        upload_pack_data_init(&data);
        data.use_sideband = LARGE_PACKET_MAX;
-       get_upload_pack_config(&data);
+       get_upload_pack_config(r, &data);
 
        while (state != FETCH_DONE) {
                switch (state) {
@@ -1796,7 +1786,7 @@ int upload_pack_v2(struct repository *r UNUSED, struct packet_reader *request)
                                 * they didn't want anything.
                                 */
                                state = FETCH_DONE;
-                       } else if (data.haves.nr) {
+                       } else if (data.seen_haves) {
                                /*
                                 * Request had 'have' lines, so lets ACK them.
                                 */
@@ -1839,41 +1829,28 @@ int upload_pack_v2(struct repository *r UNUSED, struct packet_reader *request)
 int upload_pack_advertise(struct repository *r,
                          struct strbuf *value)
 {
-       if (value) {
-               int allow_filter_value;
-               int allow_ref_in_want;
-               int allow_sideband_all_value;
-               char *str = NULL;
+       struct upload_pack_data data;
 
+       upload_pack_data_init(&data);
+       get_upload_pack_config(r, &data);
+
+       if (value) {
                strbuf_addstr(value, "shallow wait-for-done");
 
-               if (!repo_config_get_bool(r,
-                                        "uploadpack.allowfilter",
-                                        &allow_filter_value) &&
-                   allow_filter_value)
+               if (data.allow_filter)
                        strbuf_addstr(value, " filter");
 
-               if (!repo_config_get_bool(r,
-                                        "uploadpack.allowrefinwant",
-                                        &allow_ref_in_want) &&
-                   allow_ref_in_want)
+               if (data.allow_ref_in_want)
                        strbuf_addstr(value, " ref-in-want");
 
-               if (git_env_bool("GIT_TEST_SIDEBAND_ALL", 0) ||
-                   (!repo_config_get_bool(r,
-                                          "uploadpack.allowsidebandall",
-                                          &allow_sideband_all_value) &&
-                    allow_sideband_all_value))
+               if (data.allow_sideband_all)
                        strbuf_addstr(value, " sideband-all");
 
-               if (!repo_config_get_string(r,
-                                           "uploadpack.blobpackfileuri",
-                                           &str) &&
-                   str) {
+               if (data.allow_packfile_uris)
                        strbuf_addstr(value, " packfile-uris");
-                       free(str);
-               }
        }
 
+       upload_pack_data_clear(&data);
+
        return 1;
 }
index 2b1dab2649ac50b64abf99846e5b78c517e78ef4..92ef649c99ef49d1b21688584ad7d3bbbff6b737 100644 (file)
@@ -3,6 +3,7 @@
 #include "userdiff.h"
 #include "attr.h"
 #include "strbuf.h"
+#include "environment.h"
 
 static struct userdiff_driver *drivers;
 static int ndrivers;
@@ -459,7 +460,8 @@ struct userdiff_driver *userdiff_get_textconv(struct repository *r,
        if (!driver->textconv)
                return NULL;
 
-       if (driver->textconv_want_cache && !driver->textconv_cache) {
+       if (driver->textconv_want_cache && !driver->textconv_cache &&
+           have_git_dir()) {
                struct notes_cache *c = xmalloc(sizeof(*c));
                struct strbuf name = STRBUF_INIT;
 
index b5a29083df503e3adfb6f2e4a74e55d4f44514b6..7108a92b52ce06e5674aadfc5c89d05157a9db93 100644 (file)
@@ -1107,12 +1107,15 @@ void wt_status_append_cut_line(struct strbuf *buf)
        strbuf_add_commented_lines(buf, explanation, strlen(explanation), comment_line_char);
 }
 
-void wt_status_add_cut_line(FILE *fp)
+void wt_status_add_cut_line(struct wt_status *s)
 {
        struct strbuf buf = STRBUF_INIT;
 
+       if (s->added_cut_line)
+               return;
+       s->added_cut_line = 1;
        wt_status_append_cut_line(&buf);
-       fputs(buf.buf, fp);
+       fputs(buf.buf, s->fp);
        strbuf_release(&buf);
 }
 
@@ -1143,11 +1146,12 @@ static void wt_longstatus_print_verbose(struct wt_status *s)
         * file (and even the "auto" setting won't work, since it
         * will have checked isatty on stdout). But we then do want
         * to insert the scissor line here to reliably remove the
-        * diff before committing.
+        * diff before committing, if we didn't already include one
+        * before.
         */
        if (s->fp != stdout) {
                rev.diffopt.use_color = 0;
-               wt_status_add_cut_line(s->fp);
+               wt_status_add_cut_line(s);
        }
        if (s->verbose > 1 && s->committable) {
                /* print_updated() printed a header, so do we */
index 819dcad72300c56900ac05f3943d27dd2b9ed7a4..5e99ba47073493aa4ee80a9501dd4375ec903d8f 100644 (file)
@@ -130,6 +130,7 @@ struct wt_status {
        int rename_score;
        int rename_limit;
        enum wt_status_format status_format;
+       unsigned char added_cut_line; /* boolean */
        struct wt_status_state state;
        struct object_id oid_commit; /* when not Initial */
 
@@ -147,7 +148,7 @@ struct wt_status {
 
 size_t wt_status_locate_end(const char *s, size_t len);
 void wt_status_append_cut_line(struct strbuf *buf);
-void wt_status_add_cut_line(FILE *fp);
+void wt_status_add_cut_line(struct wt_status *s);
 void wt_status_prepare(struct repository *r, struct wt_status *s);
 void wt_status_print(struct wt_status *s);
 void wt_status_collect(struct wt_status *s);