]> git.ipfire.org Git - thirdparty/git.git/commitdiff
Merge branch 'ps/reftable-binsearch-updates'
authorJunio C Hamano <gitster@pobox.com>
Fri, 12 Apr 2024 18:31:39 +0000 (11:31 -0700)
committerJunio C Hamano <gitster@pobox.com>
Fri, 12 Apr 2024 18:31:39 +0000 (11:31 -0700)
Reftable code clean-up and some bugfixes.

* ps/reftable-binsearch-updates:
  reftable/block: avoid decoding keys when searching restart points
  reftable/record: extract function to decode key lengths
  reftable/block: fix error handling when searching restart points
  reftable/block: refactor binary search over restart points
  reftable/refname: refactor binary search over refnames
  reftable/basics: improve `binsearch()` test
  reftable/basics: fix return type of `binsearch()` to be `size_t`

242 files changed:
.editorconfig
Documentation/CodingGuidelines
Documentation/MyFirstObjectWalk.txt
Documentation/RelNotes/2.45.0.txt
Documentation/SubmittingPatches
Documentation/config.txt
Documentation/config/advice.txt
Documentation/config/clone.txt
Documentation/config/core.txt
Documentation/config/diff.txt
Documentation/config/extensions.txt
Documentation/config/grep.txt
Documentation/config/init.txt
Documentation/config/status.txt
Documentation/diff-options.txt
Documentation/fetch-options.txt
Documentation/git-add.txt
Documentation/git-am.txt
Documentation/git-bugreport.txt
Documentation/git-cherry-pick.txt
Documentation/git-clone.txt
Documentation/git-commit.txt
Documentation/git-config.txt
Documentation/git-grep.txt
Documentation/git-init.txt
Documentation/git-interpret-trailers.txt
Documentation/git-pack-refs.txt
Documentation/git-pull.txt
Documentation/git-rebase.txt
Documentation/git-rev-parse.txt
Documentation/git-send-email.txt
Documentation/git-status.txt
Documentation/git-update-ref.txt
Documentation/git.txt
Documentation/githooks.txt
Documentation/gitremote-helpers.txt
Documentation/howto/update-hook-example.txt
Documentation/pretty-formats.txt
Documentation/rev-list-options.txt
Documentation/urls.txt
Makefile
add-patch.c
advice.c
advice.h
apply.c
archive.c
builtin/add.c
builtin/am.c
builtin/branch.c
builtin/bugreport.c
builtin/cat-file.c
builtin/checkout.c
builtin/clone.c
builtin/commit.c
builtin/config.c
builtin/fast-import.c
builtin/gc.c
builtin/grep.c
builtin/interpret-trailers.c
builtin/log.c
builtin/ls-files.c
builtin/ls-tree.c
builtin/merge-tree.c
builtin/merge.c
builtin/notes.c
builtin/pack-objects.c
builtin/pack-refs.c
builtin/read-tree.c
builtin/rebase.c
builtin/rev-list.c
builtin/rev-parse.c
builtin/revert.c
builtin/shortlog.c
builtin/stash.c
builtin/stripspace.c
builtin/submodule--helper.c
builtin/tag.c
builtin/update-ref.c
builtin/worktree.c
cache-tree.c
ci/run-build-and-minimal-fuzzers.sh
commit.c
commit.h
config.c
config.h
contrib/completion/git-prompt.sh
contrib/coverage-diff.sh
contrib/hg-to-git/hg-to-git.py [deleted file]
contrib/hg-to-git/hg-to-git.txt [deleted file]
contrib/subtree/t/t7900-subtree.sh
delta-islands.c
diff-lib.c
diff.c
environment.c
environment.h
fmt-merge-msg.c
fsck.c
git-quiltimport.sh
gpg-interface.c
grep.c
hash-ll.h
hash.h
http-push.c
imap-send.c
list-objects.c
log-tree.c
log-tree.h
loose.c [new file with mode: 0644]
loose.h [new file with mode: 0644]
match-trees.c
mem-pool.c
merge-ll.c
merge-ll.h
merge-ort.c
merge-recursive.c
merge-recursive.h
merge.c
midx.c
object-file-convert.c [new file with mode: 0644]
object-file-convert.h [new file with mode: 0644]
object-file.c
object-name.c
object-name.h
object-store-ll.h
object.c
object.h
oid-array.c
oss-fuzz/.gitignore
oss-fuzz/fuzz-config.c [new file with mode: 0644]
pack-bitmap-write.c
packfile.c
pretty.c
pretty.h
rebase-interactive.c
reflog.c
refs.h
refs/reftable-backend.c
reftable/error.c
reftable/reftable-error.h
reftable/stack.c
reftable/stack_test.c
remote-curl.c
repository.c
repository.h
revision.c
sequencer.c
setup.c
setup.h
strbuf.c
strbuf.h
submodule-config.c
submodule.c
t/README
t/annotate-tests.sh
t/helper/test-delete-gpgsig.c [new file with mode: 0644]
t/helper/test-ref-store.c
t/helper/test-tool.c
t/helper/test-tool.h
t/lib-cvs.sh
t/oid-info/hash-info
t/perf/repos/inflate-repo.sh
t/t0002-gitfile.sh
t/t0011-hashmap.sh
t/t0028-working-tree-encoding.sh
t/t0030-stripspace.sh
t/t0204-gettext-reencode-sanity.sh
t/t0450-txt-doc-vs-help.sh
t/t0601-reffiles-pack-refs.sh
t/t0610-reftable-basics.sh
t/t1006-cat-file.sh
t/t1007-hash-object.sh
t/t1016-compatObjectFormat.sh [new file with mode: 0755]
t/t1016/gpg [new file with mode: 0755]
t/t1091-sparse-checkout-builtin.sh
t/t1300-config.sh
t/t1400-update-ref.sh
t/t1509/prepare-chroot.sh
t/t2020-checkout-detach.sh
t/t2070-restore.sh
t/t2071-restore-patch.sh
t/t2072-restore-pathspec-file.sh
t/t2104-update-index-skip-worktree.sh
t/t3200-branch.sh
t/t3321-notes-stripspace.sh
t/t3424-rebase-empty.sh
t/t3438-rebase-broken-files.sh
t/t3501-revert-cherry-pick.sh
t/t3505-cherry-pick-empty.sh
t/t3507-cherry-pick-conflict.sh
t/t3510-cherry-pick-sequence.sh
t/t3700-add.sh
t/t3701-add-interactive.sh
t/t3920-crlf-messages.sh
t/t4002-diff-basic.sh
t/t4013-diff-various.sh
t/t4020-diff-external.sh
t/t4126-apply-empty.sh
t/t4150-am.sh
t/t4205-log-pretty-formats.sh
t/t4254-am-corrupt.sh
t/t4301-merge-tree-write-tree.sh
t/t5100-mailinfo.sh
t/t5300-pack-object.sh
t/t5317-pack-objects-filter-objects.sh
t/t5401-update-hooks.sh
t/t5534-push-signed.sh
t/t5601-clone.sh
t/t5801/git-remote-testgit
t/t6112-rev-list-filters-objects.sh
t/t6413-merge-crlf.sh
t/t6418-merge-text-auto.sh
t/t6500-gc.sh
t/t7004-tag.sh
t/t7201-co.sh
t/t7400-submodule-basic.sh
t/t7507-commit-verbose.sh
t/t7508-status.sh
t/t7704-repack-cruft.sh
t/t7800-difftool.sh
t/t8010-cat-file-filters.sh
t/t8013-blame-ignore-revs.sh
t/t9118-git-svn-funky-branch-names.sh
t/t9300-fast-import.sh
t/t9350-fast-export.sh
t/t9400-git-cvsserver-server.sh
t/t9802-git-p4-filetype.sh
t/t9807-git-p4-submit.sh
t/t9824-git-p4-git-lfs.sh
t/test-lib-functions.sh
t/test-lib.sh
t/unit-tests/t-prio-queue.c
trailer.c
transport-helper.c
tree-walk.c
tree-walk.h
tree.c
walker.c
worktree.c
wt-status.c
wt-status.h
xdiff-interface.c
xdiff-interface.h

index f9d819623d832113014dd5d5366e8ee44ac9666a..15d6cbeab109efadb786b7e0d63fcfbe8eb79ee8 100644 (file)
@@ -4,7 +4,7 @@ insert_final_newline = true
 
 # The settings for C (*.c and *.h) files are mirrored in .clang-format.  Keep
 # them in sync.
-[*.{c,h,sh,perl,pl,pm,txt}]
+[{*.{c,h,sh,perl,pl,pm,txt},config.mak.*,Makefile}]
 indent_style = tab
 tab_width = 8
 
index 32e69f798ee7da4174c1f73910c899adf14ca63c..ab39509d59dd42c9d193776185649e66fa2640a6 100644 (file)
@@ -641,15 +641,15 @@ Writing Documentation:
   - Prefer succinctness and matter-of-factly describing functionality
     in the abstract.  E.g.
 
-     --short:: Emit output in the short-format.
+     `--short`:: Emit output in the short-format.
 
     and avoid something like these overly verbose alternatives:
 
-     --short:: Use this to emit output in the short-format.
-     --short:: You can use this to get output in the short-format.
-     --short:: A user who prefers shorter output could....
-     --short:: Should a person and/or program want shorter output, he
-               she/they/it can...
+     `--short`:: Use this to emit output in the short-format.
+     `--short`:: You can use this to get output in the short-format.
+     `--short`:: A user who prefers shorter output could....
+     `--short`:: Should a person and/or program want shorter output, he
+                 she/they/it can...
 
     This practice often eliminates the need to involve human actors in
     your description, but it is a good practice regardless of the
@@ -659,12 +659,12 @@ Writing Documentation:
     addressing the hypothetical user, and possibly "we" when
     discussing how the program might react to the user.  E.g.
 
-      You can use this option instead of --xyz, but we might remove
+      You can use this option instead of `--xyz`, but we might remove
       support for it in future versions.
 
     while keeping in mind that you can probably be less verbose, e.g.
 
-      Use this instead of --xyz. This option might be removed in future
+      Use this instead of `--xyz`. This option might be removed in future
       versions.
 
   - If you still need to refer to an example person that is
@@ -682,68 +682,118 @@ Writing Documentation:
  The same general rule as for code applies -- imitate the existing
  conventions.
 
- A few commented examples follow to provide reference when writing or
- modifying command usage strings and synopsis sections in the manual
- pages:
 
- Placeholders are spelled in lowercase and enclosed in angle brackets:
-   <file>
-   --sort=<key>
-   --abbrev[=<n>]
+Markup:
+
+ Literal parts (e.g. use of command-line options, command names,
+ branch names, URLs, pathnames (files and directories), configuration and
+ environment variables) must be typeset as verbatim (i.e. wrapped with
+ backticks):
+   `--pretty=oneline`
+   `git rev-list`
+   `remote.pushDefault`
+   `http://git.example.com`
+   `.git/config`
+   `GIT_DIR`
+   `HEAD`
+   `umask`(2)
+
+ An environment variable must be prefixed with "$" only when referring to its
+ value and not when referring to the variable itself, in this case there is
+ nothing to add except the backticks:
+   `GIT_DIR` is specified
+   `$GIT_DIR/hooks/pre-receive`
+
+ Word phrases enclosed in `backtick characters` are rendered literally
+ and will not be further expanded. The use of `backticks` to achieve the
+ previous rule means that literal examples should not use AsciiDoc
+ escapes.
+   Correct:
+      `--pretty=oneline`
+   Incorrect:
+      `\--pretty=oneline`
+
+ Placeholders are spelled in lowercase and enclosed in
+ angle brackets surrounded by underscores:
+   _<file>_
+   _<commit>_
 
  If a placeholder has multiple words, they are separated by dashes:
-   <new-branch-name>
-   --template=<template-directory>
+   _<new-branch-name>_
+   _<template-directory>_
+
+ A placeholder is not enclosed in backticks, as it is not a literal.
+
+ When needed, use a distinctive identifier for placeholders, usually
+ made of a qualification and a type:
+   _<git-dir>_
+   _<key-id>_
+
+ When literal and placeholders are mixed, each markup is applied for
+ each sub-entity. If they are stuck, a special markup, called
+ unconstrained formatting is required.
+ Unconstrained formating for placeholders is __<like-this>__
+ Unconstrained formatting for literal formatting is ++like this++
+   `--jobs` _<n>_
+   ++--sort=++__<key>__
+   __<directory>__++/.git++
+   ++remote.++__<name>__++.mirror++
+
+ caveat: ++ unconstrained format is not verbatim and may expand
+ content. Use Asciidoc escapes inside them.
 
- 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.
+Synopsis Syntax
+
+ Syntax grammar is formatted neither as literal nor as placeholder.
+
+ A few commented examples follow to provide reference when writing or
+ modifying command usage strings and synopsis sections in the manual
+ pages:
 
  Possibility of multiple occurrences is indicated by three dots:
-   <file>...
+   _<file>_...
    (One or more of <file>.)
 
  Optional parts are enclosed in square brackets:
-   [<file>...]
+   [_<file>_...]
    (Zero or more of <file>.)
 
-   --exec-path[=<path>]
+   ++--exec-path++[++=++__<path>__]
    (Option with an optional argument.  Note that the "=" is inside the
    brackets.)
 
-   [<patch>...]
+   [_<patch>_...]
    (Zero or more of <patch>.  Note that the dots are inside, not
    outside the brackets.)
 
  Multiple alternatives are indicated with vertical bars:
-   [-q | --quiet]
-   [--utf8 | --no-utf8]
+   [`-q` | `--quiet`]
+   [`--utf8` | `--no-utf8`]
 
  Use spacing around "|" token(s), but not immediately after opening or
  before closing a [] or () pair:
-   Do: [-q | --quiet]
-   Don't: [-q|--quiet]
+   Do: [`-q` | `--quiet`]
+   Don't: [`-q`|`--quiet`]
 
  Don't use spacing around "|" tokens when they're used to separate the
  alternate arguments of an option:
-    Do: --track[=(direct|inherit)]
-    Don't: --track[=(direct | inherit)]
+    Do: ++--track++[++=++(`direct`|`inherit`)]`
+    Don't: ++--track++[++=++(`direct` | `inherit`)]
 
  Parentheses are used for grouping:
-   [(<rev> | <range>)...]
+   [(_<rev>_ | _<range>_)...]
    (Any number of either <rev> or <range>.  Parens are needed to make
    it clear that "..." pertains to both <rev> and <range>.)
 
-   [(-p <parent>)...]
+   [(`-p` _<parent>_)...]
    (Any number of option -p, each with one <parent> argument.)
 
-   git remote set-head <name> (-a | -d | <branch>)
+   `git remote set-head` _<name>_ (`-a` | `-d` | _<branch>_)
    (One and only one of "-a", "-d" or "<branch>" _must_ (no square
    brackets) be provided.)
 
  And a somewhat more contrived example:
-   --diff-filter=[(A|C|D|M|R|T|U|X|B)...[*]]
+   `--diff-filter=[(A|C|D|M|R|T|U|X|B)...[*]]`
    Here "=" is outside the brackets, because "--diff-filter=" is a
    valid usage.  "*" has its own pair of brackets, because it can
    (optionally) be specified only when one or more of the letters is
@@ -754,39 +804,6 @@ Writing Documentation:
    the user would type into a shell and use 'Git' (uppercase first letter)
    when talking about the version control system and its properties.
 
- A few commented examples follow to provide reference when writing or
- modifying paragraphs or option/command explanations that contain options
- or commands:
-
- Literal examples (e.g. use of command-line options, command names,
- branch names, URLs, pathnames (files and directories), configuration and
- environment variables) must be typeset in monospace (i.e. wrapped with
- backticks):
-   `--pretty=oneline`
-   `git rev-list`
-   `remote.pushDefault`
-   `http://git.example.com`
-   `.git/config`
-   `GIT_DIR`
-   `HEAD`
-
- An environment variable must be prefixed with "$" only when referring to its
- value and not when referring to the variable itself, in this case there is
- nothing to add except the backticks:
-   `GIT_DIR` is specified
-   `$GIT_DIR/hooks/pre-receive`
-
- Word phrases enclosed in `backtick characters` are rendered literally
- and will not be further expanded. The use of `backticks` to achieve the
- previous rule means that literal examples should not use AsciiDoc
- escapes.
-   Correct:
-      `--pretty=oneline`
-   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 c68cdb11b9d5a53ddc11361d0f1c889edeb24536..dec8afe5b10533aba5548699b5414b6d459be371 100644 (file)
@@ -210,13 +210,14 @@ We'll also need to include the `config.h` header:
 
 ...
 
-static int git_walken_config(const char *var, const char *value, void *cb)
+static int git_walken_config(const char *var, const char *value,
+                            const struct config_context *ctx, void *cb)
 {
        /*
         * For now, we don't have any custom configuration, so fall back to
         * the default config.
         */
-       return git_default_config(var, value, cb);
+       return git_default_config(var, value, ctx, cb);
 }
 ----
 
@@ -389,10 +390,11 @@ modifying `rev_info.grep_filter`, which is a `struct grep_opt`.
 First some setup. Add `grep_config()` to `git_walken_config()`:
 
 ----
-static int git_walken_config(const char *var, const char *value, void *cb)
+static int git_walken_config(const char *var, const char *value,
+                            const struct config_context *ctx, void *cb)
 {
-       grep_config(var, value, cb);
-       return git_default_config(var, value, cb);
+       grep_config(var, value, ctx, cb);
+       return git_default_config(var, value, ctx, cb);
 }
 ----
 
@@ -523,7 +525,7 @@ about each one.
 
 We can base our work on an example. `git pack-objects` prepares all kinds of
 objects for packing into a bitmap or packfile. The work we are interested in
-resides in `builtins/pack-objects.c:get_object_list()`; examination of that
+resides in `builtin/pack-objects.c:get_object_list()`; examination of that
 function shows that the all-object walk is being performed by
 `traverse_commit_list()` or `traverse_commit_list_filtered()`. Those two
 functions reside in `list-objects.c`; examining the source shows that, despite
@@ -732,8 +734,8 @@ walk we've just performed:
        } else {
                trace_printf(
                        _("Filtered object walk with filterspec 'tree:1'.\n"));
-               CALLOC_ARRAY(rev->filter, 1);
-               parse_list_objects_filter(rev->filter, "tree:1");
+
+               parse_list_objects_filter(&rev->filter, "tree:1");
        }
        traverse_commit_list(rev, walken_show_commit,
                             walken_show_object, NULL);
@@ -752,10 +754,12 @@ points to the same tree object as its grandparent.)
 === Counting Omitted Objects
 
 We also have the capability to enumerate all objects which were omitted by a
-filter, like with `git log --filter=<spec> --filter-print-omitted`. Asking
-`traverse_commit_list_filtered()` to populate the `omitted` list means that our
-object walk does not perform any better than an unfiltered object walk; all
-reachable objects are walked in order to populate the list.
+filter, like with `git log --filter=<spec> --filter-print-omitted`. To do this,
+change `traverse_commit_list()` to `traverse_commit_list_filtered()`, which is
+able to populate an `omitted` list.  Asking for this list of filtered objects
+may cause performance degradations, however, because in this case, despite
+filtering objects, the possibly much larger set of all reachable objects must
+be processed in order to populate that list.
 
 First, add the `struct oidset` and related items we will use to iterate it:
 
@@ -776,8 +780,9 @@ static void walken_object_walk(
        ...
 ----
 
-Modify the call to `traverse_commit_list_filtered()` to include your `omitted`
-object:
+Replace the call to `traverse_commit_list()` with
+`traverse_commit_list_filtered()` and pass a pointer to the `omitted` oidset
+defined and initialized above:
 
 ----
        ...
@@ -843,7 +848,7 @@ those lines without having to recompile.
 With only that change, run again (but save yourself some scrollback):
 
 ----
-$ GIT_TRACE=1 ./bin-wrappers/git walken | head -n 10
+$ GIT_TRACE=1 ./bin-wrappers/git walken 2>&1 | head -n 10
 ----
 
 Take a look at the top commit with `git show` and the object ID you printed; it
@@ -871,7 +876,7 @@ of the first handful:
 
 ----
 $ make
-$ GIT_TRACE=1 ./bin-wrappers git walken | tail -n 10
+$ GIT_TRACE=1 ./bin-wrappers/git walken 2>&1 | tail -n 10
 ----
 
 The last commit object given should have the same OID as the one we saw at the
index b780bb89c627ef2bdcb16ee29114252c50ce860b..3b6eba51ab7f3fc0eedfbab133c705ef62ce8f71 100644 (file)
@@ -45,6 +45,40 @@ UI, Workflows & Features
  * The output format for dates "iso-strict" has been tweaked to show
    a time in the Zulu timezone with "Z" suffix, instead of "+00:00".
 
+ * "git diff" and friends learned two extra configuration variables,
+   diff.srcPrefix and diff.dstPrefix.
+
+ * The status.showUntrackedFiles configuration variable had a name
+   that tempts users to set a Boolean value expressed in our usual
+   "false", "off", and "0", but it only took "no".  This has been
+   corrected so "true" and its synonyms are taken as "normal", while
+   "false" and its synonyms are taken as "no".
+
+ * Remove an ancient and not well maintained Hg-to-git migration
+   script from contrib/.
+
+ * Hints that suggest what to do after resolving conflicts can now be
+   squelched by disabling advice.mergeConflict.
+
+ * Allow git-cherry-pick(1) to automatically drop redundant commits via
+   a new `--empty` option, similar to the `--empty` options for
+   git-rebase(1) and git-am(1). Includes a soft deprecation of
+   `--keep-redundant-commits` as well as some related docs changes and
+   sequencer code cleanup.
+
+ * "git config" learned "--comment=<message>" option to leave a
+   comment immediately after the "variable = value" on the same line
+   in the configuration file.
+
+ * core.commentChar used to be limited to a single byte, but has been
+   updated to allow an arbitrary multi-byte sequence.
+
+ * "git add -p" and other "interactive hunk selection" UI has learned to
+   skip showing the hunk immediately after it has already been shown, and
+   an additional action to explicitly ask to reshow the current hunk.
+
+ * "git pack-refs" learned the "--auto" option, which is a useful
+   addition to be triggered from "git gc --auto".
 
 Performance, Internal Implementation, Development Support etc.
 
@@ -93,6 +127,35 @@ Performance, Internal Implementation, Development Support etc.
  * The code to iterate over reflogs in the reftable has been optimized
    to reduce memory allocation and deallocation.
 
+ * Work to support a repository that work with both SHA-1 and SHA-256
+   hash algorithms has started.
+
+ * A new fuzz target that exercises config parsing code has been
+   added.
+
+ * Fix the way recently added tests interpolate variables defined
+   outside them, and document the best practice to help future
+   developers.
+
+ * Introduce an experimental protocol for contributors to propose the
+   topic description to be used in the "What's cooking" report, the
+   merge commit message for the topic, and in the release notes and
+   document it in the SubmittingPatches document.
+
+ * The t/README file now gives a hint on running individual tests in
+   the "t/" directory with "make t<num>-*.sh t<num>-*.sh".
+   (merge 8d383806fc pb/test-scripts-are-build-targets later to maint).
+
+ * The "hint:" messages given by the advice mechanism, when given a
+   message with a blank line, left a line with trailing whitespace,
+   which has been cleansed.
+
+ * Documentation rules has been explicitly described how to mark-up
+   literal parts and a few manual pages have been updated as examples.
+
+ * The .editorconfig file has been taught that a Makefile uses HT
+   indentation.
+
 
 Fixes since v2.44
 -----------------
@@ -214,6 +277,67 @@ Fixes since v2.44
  * The status.showUntrackedFiles configuration variable was
    incorrectly documented to accept "false", which has been corrected.
 
+ * Leaks from "git restore" have been plugged.
+   (merge 2f64da0790 rj/restore-plug-leaks later to maint).
+
+ * "git bugreport --no-suffix" was not supported and instead
+   segfaulted, which has been corrected.
+   (merge b3b57c69da js/bugreport-no-suffix-fix later to maint).
+
+ * The documentation for "%(trailers[:options])" placeholder in the
+   "--pretty" option of commands in the "git log" family has been
+   updated.
+   (merge bff85a338c bl/doc-key-val-sep-fix later to maint).
+
+ * "git checkout --conflict=bad" reported a bad conflictStyle as if it
+   were given to a configuration variable; it has been corrected to
+   report that the command line option is bad.
+   (merge 5a99c1ac1a pw/checkout-conflict-errorfix later to maint).
+
+ * Code clean-up in the "git log" machinery that implements custom log
+   message formatting.
+   (merge 1c10b8e5b0 jk/pretty-subject-cleanup later to maint).
+
+ * "git config" corrupted literal HT characters written in the
+   configuration file as part of a value, which has been corrected.
+   (merge e6895c3f97 ds/config-internal-whitespace-fix later to maint).
+
+ * A unit test for reftable code tried to enumerate all files in a
+   directory after reftable operations and expected to see nothing but
+   the files it wanted to leave there, but was fooled by .nfs* cruft
+   files left, which has been corrected.
+   (merge 0068aa7946 ps/reftable-unit-test-nfs-workaround later to maint).
+
+ * The implementation and documentation of "object-format" option
+   exchange between the Git itself and its remote helpers did not
+   quite match, which has been corrected.
+
+ * The "--pretty=<shortHand>" option of the commands in the "git log"
+   family, defined as "[pretty] shortHand = <expansion>" should have
+   been looked up case insensitively, but was not, which has been
+   corrected.
+   (merge f999d5188b bl/pretty-shorthand-config-fix later to maint).
+
+ * "git apply" failed to extract the filename the patch applied to,
+   when the change was about an empty file created in or deleted from
+   a directory whose name ends with a SP, which has been corrected.
+   (merge 776ffd1a30 jc/apply-parse-diff-git-header-names-fix later to maint).
+
+ * Update a more recent tutorial doc.
+   (merge 95ab557b4b dg/myfirstobjectwalk-updates later to maint).
+
+ * The test script had an incomplete and ineffective attempt to avoid
+   clobbering the testing user's real crontab (and its equivalents),
+   which has been completed.
+   (merge 73cb87773b es/test-cron-safety later to maint).
+
+ * Use advice_if_enabled() API to rewrite a simple pattern to
+   call advise() after checking advice_enabled().
+   (merge 6412d01527 rj/use-adv-if-enabled later to maint).
+
+ * Another "set -u" fix for the bash prompt (in contrib/) script.
+   (merge d7805bc743 vs/complete-with-set-u-fix later to maint).
+
  * Other code cleanup, docfix, build fix, etc.
    (merge f0e578c69c rs/use-xstrncmpz later to maint).
    (merge 83e6eb7d7a ba/credential-test-clean-fix later to maint).
@@ -233,3 +357,9 @@ Fixes since v2.44
    (merge 781fb7b4c2 as/option-names-in-messages later to maint).
    (merge 51d41dc243 jk/doc-remote-helpers-markup-fix later to maint).
    (merge e1aaf309db pb/ci-win-artifact-names-fix later to maint).
+   (merge ad538c61da jc/index-pack-fsck-levels later to maint).
+   (merge 67471bc704 ja/doc-formatting-fix later to maint).
+   (merge 86f9ce7dd6 bl/doc-config-fixes later to maint).
+   (merge 0d527842b7 az/grep-group-error-message-update later to maint).
+   (merge 7c43bdf07b rs/strbuf-expand-bad-format later to maint).
+   (merge 8b68b48d5c ds/typofix-core-config-doc later to maint).
index e734a3f0f175795f0633e93c07afd13f9183efc8..c647c7e1b4c3eeb555831989f083138bf4f51459 100644 (file)
@@ -459,6 +459,18 @@ an explanation of changes between each iteration can be kept in
 Git-notes and inserted automatically following the three-dash
 line via `git format-patch --notes`.
 
+[[the-topic-summary]]
+*This is EXPERIMENTAL*.
+
+When sending a topic, you can propose a one-paragraph summary that
+should appear in the "What's cooking" report when it is picked up to
+explain the topic.  If you choose to do so, please write a 2-5 line
+paragraph that will fit well in our release notes (see many bulleted
+entries in the Documentation/RelNotes/* files for examples), and make
+it the first paragraph of the cover letter.  For a single-patch
+series, use the space between the three-dash line and the diffstat, as
+described earlier.
+
 [[attachment]]
 Do not attach the patch as a MIME attachment, compressed or not.
 Do not let your e-mail client send quoted-printable.  Do not let
index 782c2bab906cf188e3cb21fa6d9fa8c4fe78663d..70b448b132628c71ee8807bfef59c1e4e60fc823 100644 (file)
@@ -22,9 +22,10 @@ multivalued.
 Syntax
 ~~~~~~
 
-The syntax is fairly flexible and permissive; whitespaces are mostly
-ignored.  The '#' and ';' characters begin comments to the end of line,
-blank lines are ignored.
+The syntax is fairly flexible and permissive.  Whitespace characters,
+which in this context are the space character (SP) and the horizontal
+tabulation (HT), are mostly ignored.  The '#' and ';' characters begin
+comments to the end of line.  Blank lines are ignored.
 
 The file consists of sections and variables.  A section begins with
 the name of the section in square brackets and continues until the next
@@ -63,16 +64,17 @@ the variable is the boolean "true").
 The variable names are case-insensitive, allow only alphanumeric characters
 and `-`, and must start with an alphabetic character.
 
-A line that defines a value can be continued to the next line by
-ending it with a `\`; the backslash and the end-of-line are
-stripped.  Leading whitespaces after 'name =', the remainder of the
-line after the first comment character '#' or ';', and trailing
-whitespaces of the line are discarded unless they are enclosed in
-double quotes.  Internal whitespaces within the value are retained
-verbatim.
-
-Inside double quotes, double quote `"` and backslash `\` characters
-must be escaped: use `\"` for `"` and `\\` for `\`.
+Whitespace characters surrounding `name`, `=` and `value` are discarded.
+Internal whitespace characters within 'value' are retained verbatim.
+Comments starting with either `#` or `;` and extending to the end of line
+are discarded.  A line that defines a value can be continued to the next
+line by ending it with a backslash (`\`);  the backslash and the end-of-line
+characters are discarded.
+
+If `value` needs to contain leading or trailing whitespace characters,
+it must be enclosed in double quotation marks (`"`).  Inside double quotation
+marks, double quote (`"`) and backslash (`\`) characters must be escaped:
+use `\"` for `"` and `\\` for `\`.
 
 The following escape sequences (beside `\"` and `\\`) are recognized:
 `\n` for newline character (NL), `\t` for horizontal tabulation (HT, TAB)
index f83341165367baac4f02779eedf432822644e615..0e35ae5240fa523039524b52aaeccd41e62820ac 100644 (file)
@@ -56,6 +56,8 @@ advice.*::
                Shown when the user's information is guessed from the
                system username and domain name, to tell the user how to
                set their identity configuration.
+       mergeConflict::
+               Shown when various commands stop because of conflicts.
        nestedTag::
                Shown when a user attempts to recursively tag a tag object.
        pushAlreadyExists::
index d037b57f729e5e10549235b6278c123754d0aee8..0a10efd174ea4bdfe84c4749fd87e3b0dcdae211 100644 (file)
@@ -1,13 +1,23 @@
-clone.defaultRemoteName::
+`clone.defaultRemoteName`::
        The name of the remote to create when cloning a repository.  Defaults to
-       `origin`, and can be overridden by passing the `--origin` command-line
+       `origin`.
+ifdef::git-clone[]
+       It can be overridden by passing the `--origin` command-line
+       option.
+endif::[]
+ifndef::git-clone[]
+       It can be overridden by passing the `--origin` command-line
        option to linkgit:git-clone[1].
+endif::[]
 
-clone.rejectShallow::
+`clone.rejectShallow`::
        Reject cloning a repository if it is a shallow one; this can be overridden by
-       passing the `--reject-shallow` option on the command line. See linkgit:git-clone[1]
+       passing the `--reject-shallow` option on the command line.
+ifndef::git-clone[]
+       See linkgit:git-clone[1].
+endif::[]
 
-clone.filterSubmodules::
+`clone.filterSubmodules`::
        If a partial clone filter is provided (see `--filter` in
        linkgit:git-rev-list[1]) and `--recurse-submodules` is used, also apply
        the filter to submodules.
index 2d4bbdb25fa3104bf5151ca98be42f0b4c438b2a..93d65e1dfd24f715e08d8a4d0bb71ca77fa6fde6 100644 (file)
@@ -520,13 +520,28 @@ core.editor::
        `GIT_EDITOR` is not set.  See linkgit:git-var[1].
 
 core.commentChar::
+core.commentString::
        Commands such as `commit` and `tag` that let you edit
-       messages consider a line that begins with this ASCII character
+       messages consider a line that begins with this character
        commented, and removes them after the editor returns
        (default '#').
 +
 If set to "auto", `git-commit` would select a character that is not
 the beginning character of any line in existing commit messages.
++
+Note that these two variables are aliases of each other, and in modern
+versions of Git you are free to use a string (e.g., `//` or `⁑⁕⁑`) with
+`commentChar`. Versions of Git prior to v2.45.0 will ignore
+`commentString` but will reject a value of `commentChar` that consists
+of more than a single ASCII byte. If you plan to use your config with
+older and newer versions of Git, you may want to specify both:
++
+    [core]
+    # single character for older versions
+    commentChar = "#"
+    # string for newer versions (which will override commentChar
+    # because it comes later in the file)
+    commentString = "//"
 
 core.filesRefLockTimeout::
        The length of time, in milliseconds, to retry when trying to
@@ -688,7 +703,7 @@ core.createObject::
        will not overwrite existing objects.
 +
 On some file system/operating system combinations, this is unreliable.
-Set this config setting to 'rename' there; However, This will remove the
+Set this config setting to 'rename' there; however, this will remove the
 check that makes sure that existing object files will not get overwritten.
 
 core.notesRef::
index 6c7e09a1ef5eb481b2abb26abd93edf38806cf76..5ce7b91f1d0d2356928ccc5e59195a6db205d81b 100644 (file)
@@ -108,9 +108,15 @@ diff.mnemonicPrefix::
 `git diff --no-index a b`;;
        compares two non-git things (1) and (2).
 
-diff.noprefix::
+diff.noPrefix::
        If set, 'git diff' does not show any source or destination prefix.
 
+diff.srcPrefix::
+       If set, 'git diff' uses this source prefix. Defaults to "a/".
+
+diff.dstPrefix::
+       If set, 'git diff' uses this destination prefix. Defaults to "b/".
+
 diff.relative::
        If set to 'true', 'git diff' does not show changes outside of the directory
        and show pathnames relative to the current directory.
index 66db0e15da7db82819de941626b7abc5983862c5..38dce3df359761b54dca8afcb13f77e1919de098 100644 (file)
@@ -7,6 +7,18 @@ Note that this setting should only be set by linkgit:git-init[1] or
 linkgit:git-clone[1].  Trying to change it after initialization will not
 work and will produce hard-to-diagnose issues.
 
+extensions.compatObjectFormat::
+
+       Specify a compatitbility hash algorithm to use.  The acceptable values
+       are `sha1` and `sha256`.  The value specified must be different from the
+       value of extensions.objectFormat.  This allows client level
+       interoperability between git repositories whose objectFormat matches
+       this compatObjectFormat.  In particular when fully implemented the
+       pushes and pulls from a repository in whose objectFormat matches
+       compatObjectFormat.  As well as being able to use oids encoded in
+       compatObjectFormat in addition to oids encoded with objectFormat to
+       locally specify objects.
+
 extensions.refStorage::
        Specify the ref storage format to use. The acceptable values are:
 +
index e521f20390ceaeab8701dd424f13962c3a439ffa..10041f27b0c8e2d004c388a3fc7956b49c5614e9 100644 (file)
@@ -24,5 +24,5 @@ grep.fullName::
        If set to true, enable `--full-name` option by default.
 
 grep.fallbackToNoIndex::
-       If set to true, fall back to git grep --no-index if git grep
+       If set to true, fall back to `git grep --no-index` if `git grep`
        is executed outside of a git repository.  Defaults to false.
index dd1d8332737fe89a2feca44ce59adad4f64bce5a..af03acdbcbbeee913429d4828b976dd166242938 100644 (file)
@@ -3,8 +3,8 @@ ifndef::git-init[]
 :see-git-init: (See the "TEMPLATE DIRECTORY" section of linkgit:git-init[1].)
 endif::[]
 
-init.templateDir::
+`init.templateDir`::
        Specify the directory from which templates will be copied. {see-git-init}
-init.defaultBranch::
+`init.defaultBranch`::
        Allows overriding the default branch name e.g. when initializing
        a new repository.
index 2ff8237f8fc4585e7a2a4c9e7a27f121bbd9d7e2..8caf90f51c19a3c64b777310e69691d4e2b6dbc2 100644 (file)
@@ -57,6 +57,8 @@ status.showUntrackedFiles::
 --
 +
 If this variable is not specified, it defaults to 'normal'.
+All usual spellings for Boolean value `true` are taken as `normal`
+and `false` as `no`.
 This variable can be overridden with the -u|--untracked-files option
 of linkgit:git-status[1] and linkgit:git-commit[1].
 
index aaaff0d46f0c6f04ca4e3182872b47d2bd2f07b6..0e9456957e37847c8d7fe342d0d8d4186e3095d7 100644 (file)
@@ -865,8 +865,9 @@ endif::git-format-patch[]
 
 --default-prefix::
        Use the default source and destination prefixes ("a/" and "b/").
-       This is usually the default already, but may be used to override
-       config such as `diff.noprefix`.
+       This overrides configuration variables such as `diff.noprefix`,
+       `diff.srcPrefix`, `diff.dstPrefix`, and `diff.mnemonicPrefix`
+       (see `git-config`(1)).
 
 --line-prefix=<prefix>::
        Prepend an additional prefix to every line of output.
index 54ebb4452e997f8c62c9dd2421948086249102bd..e22b217fba9e2c6c30ad1f7becea238d0f2a9916 100644 (file)
@@ -202,7 +202,7 @@ endif::git-pull[]
        destination of an explicit refspec; see `--prune`).
 
 ifndef::git-pull[]
---recurse-submodules[=yes|on-demand|no]::
+--recurse-submodules[=(yes|on-demand|no)]::
        This option controls if and under what conditions new commits of
        submodules should be fetched too. When recursing through submodules,
        `git fetch` always attempts to fetch "changed" submodules, that is, a
index 14a371fff3569eac4fd633ebc945b1d8793b1237..aceaa025e3020adbb6f958f2314eccc99b67d470 100644 (file)
@@ -348,6 +348,7 @@ patch::
        K - leave this hunk undecided, see previous hunk
        s - split the current hunk into smaller hunks
        e - manually edit the current hunk
+       p - print the current hunk
        ? - print help
 +
 After deciding the fate for all hunks, if there is any hunk
index 463a3c660024e08b494caa3e22b4d4c417f46e0c..624a6e6fe4f9752fb518223d45cc524b8d3c8bd7 100644 (file)
@@ -66,13 +66,19 @@ OPTIONS
 --quoted-cr=<action>::
        This flag will be passed down to 'git mailinfo' (see linkgit:git-mailinfo[1]).
 
---empty=(stop|drop|keep)::
-       By default, or when the option is set to 'stop', the command
-       errors out on an input e-mail message lacking a patch
-       and stops in the middle of the current am session. When this
-       option is set to 'drop', skip such an e-mail message instead.
-       When this option is set to 'keep', create an empty commit,
-       recording the contents of the e-mail message as its log.
+--empty=(drop|keep|stop)::
+       How to handle an e-mail message lacking a patch:
++
+--
+`drop`;;
+       The e-mail message will be skipped.
+`keep`;;
+       An empty commit will be created, with the contents of the e-mail
+       message as its log.
+`stop`;;
+       The command will fail, stopping in the middle of the current `am`
+       session. This is the default behavior.
+--
 
 -m::
 --message-id::
index ca626f7fc68611de05319d78ddcc16e94b3375e1..112658b3c3bb722d91715a32590a3789302feda7 100644 (file)
@@ -8,7 +8,8 @@ git-bugreport - Collect information for user to file a bug report
 SYNOPSIS
 --------
 [verse]
-'git bugreport' [(-o | --output-directory) <path>] [(-s | --suffix) <format>]
+'git bugreport' [(-o | --output-directory) <path>]
+               [(-s | --suffix) <format> | --no-suffix]
                [--diagnose[=<mode>]]
 
 DESCRIPTION
@@ -51,9 +52,12 @@ OPTIONS
 
 -s <format>::
 --suffix <format>::
+--no-suffix::
        Specify an alternate suffix for the bugreport name, to create a file
        named 'git-bugreport-<formatted-suffix>'. This should take the form of a
        strftime(3) format string; the current local time will be used.
+       `--no-suffix` disables the suffix and the file is just named
+       `git-bugreport` without any disambiguation measure.
 
 --no-diagnose::
 --diagnose[=<mode>]::
index fdcad3d2006c8ad59333f38a3161e2060e75a8ee..81ace900fc59652cdb663cadd2279ed63c9cfd44 100644 (file)
@@ -131,20 +131,36 @@ effect to your index in a row.
        even without this option.  Note also, that use of this option only
        keeps commits that were initially empty (i.e. the commit recorded the
        same tree as its parent).  Commits which are made empty due to a
-       previous commit are dropped.  To force the inclusion of those commits
-       use `--keep-redundant-commits`.
+       previous commit will cause the cherry-pick to fail.  To force the
+       inclusion of those commits, use `--empty=keep`.
 
 --allow-empty-message::
        By default, cherry-picking a commit with an empty message will fail.
        This option overrides that behavior, allowing commits with empty
        messages to be cherry picked.
 
+--empty=(drop|keep|stop)::
+       How to handle commits being cherry-picked that are redundant with
+       changes already in the current history.
++
+--
+`drop`;;
+       The commit will be dropped.
+`keep`;;
+       The commit will be kept. Implies `--allow-empty`.
+`stop`;;
+       The cherry-pick will stop when the commit is applied, allowing
+       you to examine the commit. This is the default behavior.
+--
++
+Note that `--empty=drop` and `--empty=stop` only specify how to handle a
+commit that was not initially empty, but rather became empty due to a previous
+commit. Commits that were initially empty will still cause the cherry-pick to
+fail unless one of `--empty=keep` or `--allow-empty` are specified.
++
+
 --keep-redundant-commits::
-       If a commit being cherry picked duplicates a commit already in the
-       current history, it will become empty.  By default these
-       redundant commits cause `cherry-pick` to stop so the user can
-       examine the commit. This option overrides that behavior and
-       creates an empty commit object.  Implies `--allow-empty`.
+       Deprecated synonym for `--empty=keep`.
 
 --strategy=<strategy>::
        Use the given merge strategy.  Should only be used once.
index f90977a8519b4c9c057438afa042b4bd25855567..5de18de2ab83fef4fffdb95348adddec02d1fcfa 100644 (file)
@@ -9,15 +9,15 @@ git-clone - Clone a repository into a new directory
 SYNOPSIS
 --------
 [verse]
-'git clone' [--template=<template-directory>]
-         [-l] [-s] [--no-hardlinks] [-q] [-n] [--bare] [--mirror]
-         [-o <name>] [-b <name>] [-u <upload-pack>] [--reference <repository>]
-         [--dissociate] [--separate-git-dir <git-dir>]
-         [--depth <depth>] [--[no-]single-branch] [--no-tags]
-         [--recurse-submodules[=<pathspec>]] [--[no-]shallow-submodules]
-         [--[no-]remote-submodules] [--jobs <n>] [--sparse] [--[no-]reject-shallow]
-         [--filter=<filter> [--also-filter-submodules]] [--] <repository>
-         [<directory>]
+`git clone` [++--template=++__<template-directory>__]
+         [`-l`] [`-s`] [`--no-hardlinks`] [`-q`] [`-n`] [`--bare`] [`--mirror`]
+         [`-o` _<name>_] [`-b` _<name>_] [`-u` _<upload-pack>_] [`--reference` _<repository>_]
+         [`--dissociate`] [`--separate-git-dir` _<git-dir>_]
+         [`--depth` _<depth>_] [`--`[`no-`]`single-branch`] [`--no-tags`]
+         [++--recurse-submodules++[++=++__<pathspec>__]] [`--`[`no-`]`shallow-submodules`]
+         [`--`[`no-`]`remote-submodules`] [`--jobs` _<n>_] [`--sparse`] [`--`[`no-`]`reject-shallow`]
+         [++--filter=++__<filter-spec>__] [`--also-filter-submodules`]] [`--`] _<repository>_
+         [_<directory>_]
 
 DESCRIPTION
 -----------
@@ -31,7 +31,7 @@ currently active branch.
 After the clone, a plain `git fetch` without arguments will update
 all the remote-tracking branches, and a `git pull` without
 arguments will in addition merge the remote master branch into the
-current master branch, if any (this is untrue when "--single-branch"
+current master branch, if any (this is untrue when `--single-branch`
 is given; see below).
 
 This default configuration is achieved by creating references to
@@ -42,12 +42,12 @@ configuration variables.
 
 OPTIONS
 -------
--l::
---local::
+`-l`::
+`--local`::
        When the repository to clone from is on a local machine,
        this flag bypasses the normal "Git aware" transport
        mechanism and clones the repository by making a copy of
-       HEAD and everything under objects and refs directories.
+       `HEAD` and everything under objects and refs directories.
        The files under `.git/objects/` directory are hardlinked
        to save space when possible.
 +
@@ -67,14 +67,14 @@ links.
 source repository, similar to running `cp -r src dst` while modifying
 `src`.
 
---no-hardlinks::
+`--no-hardlinks`::
        Force the cloning process from a repository on a local
        filesystem to copy the files under the `.git/objects`
        directory instead of using hardlinks. This may be desirable
        if you are trying to make a back-up of your repository.
 
--s::
---shared::
+`-s`::
+`--shared`::
        When the repository to clone is on the local machine,
        instead of using hard links, automatically setup
        `.git/objects/info/alternates` to share the objects
@@ -101,7 +101,7 @@ If you want to break the dependency of a repository cloned with `--shared` on
 its source repository, you can simply run `git repack -a` to copy all
 objects from the source repository into a pack in the cloned repository.
 
---reference[-if-able] <repository>::
+`--reference`[`-if-able`] _<repository>_::
        If the reference _<repository>_ is on the local machine,
        automatically setup `.git/objects/info/alternates` to
        obtain objects from the reference _<repository>_.  Using
@@ -115,7 +115,7 @@ objects from the source repository into a pack in the cloned repository.
 *NOTE*: see the NOTE for the `--shared` option, and also the
 `--dissociate` option.
 
---dissociate::
+`--dissociate`::
        Borrow the objects from reference repositories specified
        with the `--reference` options only to reduce network
        transfer, and stop borrowing from them after a clone is made
@@ -126,43 +126,43 @@ objects from the source repository into a pack in the cloned repository.
        same repository, and this option can be used to stop the
        borrowing.
 
--q::
---quiet::
+`-q`::
+`--quiet`::
        Operate quietly.  Progress is not reported to the standard
        error stream.
 
--v::
---verbose::
+`-v`::
+`--verbose`::
        Run verbosely. Does not affect the reporting of progress status
        to the standard error stream.
 
---progress::
+`--progress`::
        Progress status is reported on the standard error stream
        by default when it is attached to a terminal, unless `--quiet`
        is specified. This flag forces progress status even if the
        standard error stream is not directed to a terminal.
 
---server-option=<option>::
+++--server-option=++__<option>__::
        Transmit the given string to the server when communicating using
        protocol version 2.  The given string must not contain a NUL or LF
        character.  The server's handling of server options, including
        unknown ones, is server-specific.
-       When multiple `--server-option=<option>` are given, they are all
+       When multiple ++--server-option=++__<option>__ are given, they are all
        sent to the other side in the order listed on the command line.
 
--n::
---no-checkout::
+`-n`::
+`--no-checkout`::
        No checkout of HEAD is performed after the clone is complete.
 
---[no-]reject-shallow::
+`--`[`no-`]`reject-shallow`::
        Fail if the source repository is a shallow repository.
        The `clone.rejectShallow` configuration variable can be used to
        specify the default.
 
---bare::
+`--bare`::
        Make a 'bare' Git repository.  That is, instead of
        creating _<directory>_ and placing the administrative
-       files in `<directory>/.git`, make the _<directory>_
+       files in _<directory>_`/.git`, make the _<directory>_
        itself the `$GIT_DIR`. This obviously implies the `--no-checkout`
        because there is nowhere to check out the working tree.
        Also the branch heads at the remote are copied directly
@@ -171,28 +171,28 @@ objects from the source repository into a pack in the cloned repository.
        used, neither remote-tracking branches nor the related
        configuration variables are created.
 
---sparse::
+`--sparse`::
        Employ a sparse-checkout, with only files in the toplevel
        directory initially being present.  The
        linkgit:git-sparse-checkout[1] command can be used to grow the
        working directory as needed.
 
---filter=<filter-spec>::
+++--filter=++__<filter-spec>__::
        Use the partial clone feature and request that the server sends
        a subset of reachable objects according to a given object filter.
        When using `--filter`, the supplied _<filter-spec>_ is used for
        the partial clone filter. For example, `--filter=blob:none` will
        filter out all blobs (file contents) until needed by Git. Also,
-       `--filter=blob:limit=<size>` will filter out all blobs of size
+       ++--filter=blob:limit=++__<size>__ will filter out all blobs of size
        at least _<size>_. For more details on filter specifications, see
        the `--filter` option in linkgit:git-rev-list[1].
 
---also-filter-submodules::
+`--also-filter-submodules`::
        Also apply the partial clone filter to any submodules in the repository.
        Requires `--filter` and `--recurse-submodules`. This can be turned on by
        default by setting the `clone.filterSubmodules` config option.
 
---mirror::
+`--mirror`::
        Set up a mirror of the source repository.  This implies `--bare`.
        Compared to `--bare`, `--mirror` not only maps local branches of the
        source to local branches of the target, it maps all refs (including
@@ -200,14 +200,14 @@ objects from the source repository into a pack in the cloned repository.
        that all these refs are overwritten by a `git remote update` in the
        target repository.
 
--o <name>::
---origin <name>::
+`-o` _<name>_::
+`--origin` _<name>_::
        Instead of using the remote name `origin` to keep track of the upstream
        repository, use _<name>_.  Overrides `clone.defaultRemoteName` from the
        config.
 
--b <name>::
---branch <name>::
+`-b` _<name>_::
+`--branch` _<name>_::
        Instead of pointing the newly created HEAD to the branch pointed
        to by the cloned repository's HEAD, point to _<name>_ branch
        instead. In a non-bare repository, this is the branch that will
@@ -215,18 +215,18 @@ objects from the source repository into a pack in the cloned repository.
        `--branch` can also take tags and detaches the HEAD at that commit
        in the resulting repository.
 
--u <upload-pack>::
---upload-pack <upload-pack>::
+`-u` _<upload-pack>_::
+`--upload-pack` _<upload-pack>_::
        When given, and the repository to clone from is accessed
        via ssh, this specifies a non-default path for the command
        run on the other end.
 
---template=<template-directory>::
+++--template=++__<template-directory>__::
        Specify the directory from which templates will be used;
        (See the "TEMPLATE DIRECTORY" section of linkgit:git-init[1].)
 
--c <key>=<value>::
---config <key>=<value>::
+`-c` __<key>__++=++__<value>__::
+`--config` __<key>__++=++__<value>__::
        Set a configuration variable in the newly-created repository;
        this takes effect immediately after the repository is
        initialized, but before the remote history is fetched or any
@@ -239,25 +239,25 @@ objects from the source repository into a pack in the cloned repository.
 Due to limitations of the current implementation, some configuration
 variables do not take effect until after the initial fetch and checkout.
 Configuration variables known to not take effect are:
-`remote.<name>.mirror` and `remote.<name>.tagOpt`.  Use the
+++remote.++__<name>__++.mirror++ and ++remote.++__<name>__++.tagOpt++.  Use the
 corresponding `--mirror` and `--no-tags` options instead.
 
---depth <depth>::
+`--depth` _<depth>_::
        Create a 'shallow' clone with a history truncated to the
        specified number of commits. Implies `--single-branch` unless
        `--no-single-branch` is given to fetch the histories near the
        tips of all branches. If you want to clone submodules shallowly,
        also pass `--shallow-submodules`.
 
---shallow-since=<date>::
+++--shallow-since=++__<date>__::
        Create a shallow clone with a history after the specified time.
 
---shallow-exclude=<revision>::
+++--shallow-exclude=++__<revision>__::
        Create a shallow clone with a history, excluding commits
        reachable from a specified remote branch or tag.  This option
        can be specified multiple times.
 
---[no-]single-branch::
+`--`[`no-`]`single-branch`::
        Clone only the history leading to the tip of a single branch,
        either specified by the `--branch` option or the primary
        branch remote's `HEAD` points at.
@@ -267,7 +267,7 @@ corresponding `--mirror` and `--no-tags` options instead.
        branch when `--single-branch` clone was made, no remote-tracking
        branch is created.
 
---no-tags::
+`--no-tags`::
        Don't clone any tags, and set
        `remote.<remote>.tagOpt=--no-tags` in the config, ensuring
        that future `git pull` and `git fetch` operations won't follow
@@ -279,7 +279,7 @@ maintain a branch with no references other than a single cloned
 branch. This is useful e.g. to maintain minimal clones of the default
 branch of some repository for search indexing.
 
---recurse-submodules[=<pathspec>]::
+`--recurse-submodules`[`=`{empty}__<pathspec>__]::
        After the clone is created, initialize and clone submodules
        within based on the provided _<pathspec>_.  If no _=<pathspec>_ is
        provided, all submodules are initialized and cloned.
@@ -295,46 +295,46 @@ the clone is finished. This option is ignored if the cloned repository does
 not have a worktree/checkout (i.e. if any of `--no-checkout`/`-n`, `--bare`,
 or `--mirror` is given)
 
---[no-]shallow-submodules::
+`--`[`no-`]`shallow-submodules`::
        All submodules which are cloned will be shallow with a depth of 1.
 
---[no-]remote-submodules::
+`--`[`no-`]`remote-submodules`::
        All submodules which are cloned will use the status of the submodule's
        remote-tracking branch to update the submodule, rather than the
        superproject's recorded SHA-1. Equivalent to passing `--remote` to
        `git submodule update`.
 
---separate-git-dir=<git-dir>::
+`--separate-git-dir=`{empty}__<git-dir>__::
        Instead of placing the cloned repository where it is supposed
        to be, place the cloned repository at the specified directory,
        then make a filesystem-agnostic Git symbolic link to there.
        The result is Git repository can be separated from working
        tree.
 
---ref-format=<ref-format>::
+`--ref-format=`{empty}__<ref-format>__::
 
 Specify the given ref storage format for the repository. The valid values are:
 +
 include::ref-storage-format.txt[]
 
--j <n>::
---jobs <n>::
+`-j` _<n>_::
+`--jobs` _<n>_::
        The number of submodules fetched at the same time.
        Defaults to the `submodule.fetchJobs` option.
 
-<repository>::
+_<repository>_::
        The (possibly remote) _<repository>_ to clone from.  See the
        <<URLS,GIT URLS>> section below for more information on specifying
        repositories.
 
-<directory>::
+_<directory>_::
        The name of a new directory to clone into.  The "humanish"
        part of the source repository is used if no _<directory>_ is
        explicitly given (`repo` for `/path/to/repo.git` and `foo`
        for `host.xz:foo/.git`).  Cloning into an existing directory
        is only allowed if the directory is empty.
 
---bundle-uri=<uri>::
+`--bundle-uri=`{empty}__<uri>__::
        Before fetching from the remote, fetch a bundle from the given
        _<uri>_ and unbundle the data into the local repository. The refs
        in the bundle will be stored under the hidden `refs/bundle/*`
index a6cef5d82038771c5f7e91f788f14de6394de158..89ecfc63a8d07f655137b753b306cf22586334cd 100644 (file)
@@ -347,6 +347,8 @@ The possible options are:
        - 'normal' - Shows untracked files and directories
        - 'all'    - Also shows individual files in untracked directories.
 
+All usual spellings for Boolean value `true` are taken as `normal`
+and `false` as `no`.
 The default can be changed using the status.showUntrackedFiles
 configuration variable documented in linkgit:git-config[1].
 --
index dff39093b5ef322ce115afc8f4bee0c728b6173a..ac61113fcc6107ecba82926279537292a0429141 100644 (file)
@@ -9,9 +9,9 @@ git-config - Get and set repository or global options
 SYNOPSIS
 --------
 [verse]
-'git config' [<file-option>] [--type=<type>] [--fixed-value] [--show-origin] [--show-scope] [-z|--null] <name> [<value> [<value-pattern>]]
-'git config' [<file-option>] [--type=<type>] --add <name> <value>
-'git config' [<file-option>] [--type=<type>] [--fixed-value] --replace-all <name> <value> [<value-pattern>]
+'git config' [<file-option>] [--type=<type>] [--comment=<message>] [--fixed-value] [--show-origin] [--show-scope] [-z|--null] <name> [<value> [<value-pattern>]]
+'git config' [<file-option>] [--type=<type>] [--comment=<message>] --add <name> <value>
+'git config' [<file-option>] [--type=<type>] [--comment=<message>] [--fixed-value] --replace-all <name> <value> [<value-pattern>]
 'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] [--fixed-value] --get <name> [<value-pattern>]
 'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] [--fixed-value] --get-all <name> [<value-pattern>]
 'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] [--fixed-value] [--name-only] --get-regexp <name-regex> [<value-pattern>]
@@ -87,6 +87,18 @@ OPTIONS
        values.  This is the same as providing '^$' as the `value-pattern`
        in `--replace-all`.
 
+--comment <message>::
+       Append a comment at the end of new or modified lines.
+
+       If _<message>_ begins with one or more whitespaces followed
+       by "#", it is used as-is.  If it begins with "#", a space is
+       prepended before it is used.  Otherwise, a string " # " (a
+       space followed by a hash followed by a space) is prepended
+       to it.  And the resulting string is placed immediately after
+       the value defined for the variable.  The _<message>_ must
+       not contain linefeed characters (no multi-line comments are
+       permitted).
+
 --get::
        Get the value for a given key (optionally filtered by a regex
        matching the value). Returns error code 1 if the key was not
@@ -275,7 +287,8 @@ Valid `<type>`'s include:
 -e::
 --edit::
        Opens an editor to modify the specified config file; either
-       `--system`, `--global`, or repository (default).
+       `--system`, `--global`, `--local` (default), `--worktree`, or
+       `--file <config-file>`.
 
 --[no-]includes::
        Respect `include.*` directives in config files when looking up
@@ -285,7 +298,7 @@ Valid `<type>`'s include:
 
 --default <value>::
   When using `--get`, and the requested variable is not found, behave as if
-  <value> were the value assigned to the that variable.
+  <value> were the value assigned to that variable.
 
 CONFIGURATION
 -------------
index 0d0103c780af8c454db02a33c909343fb7bd51be..1e6d7b65c84e0f7ba4a032584e08b11dcf26fcbd 100644 (file)
@@ -28,7 +28,7 @@ SYNOPSIS
           [-f <file>] [-e] <pattern>
           [--and|--or|--not|(|)|-e <pattern>...]
           [--recurse-submodules] [--parent-basename <basename>]
-          [ [--[no-]exclude-standard] [--cached | --no-index | --untracked] | <tree>...]
+          [ [--[no-]exclude-standard] [--cached | --untracked | --no-index] | <tree>...]
           [--] [<pathspec>...]
 
 DESCRIPTION
@@ -45,13 +45,21 @@ OPTIONS
        Instead of searching tracked files in the working tree, search
        blobs registered in the index file.
 
---no-index::
-       Search files in the current directory that is not managed by Git.
-
 --untracked::
        In addition to searching in the tracked files in the working
        tree, search also in untracked files.
 
+--no-index::
+       Search files in the current directory that is not managed by Git,
+       or by ignoring that the current directory is managed by Git.  This
+       is rather similar to running the regular `grep(1)` utility with its
+       `-r` option specified, but with some additional benefits, such as
+       using pathspec patterns to limit paths;  see the 'pathspec' entry
+       in linkgit:gitglossary[7] for more information.
++
+This option cannot be used together with `--cached` or `--untracked`.
+See also `grep.fallbackToNoIndex` in 'CONFIGURATION' below.
+
 --no-exclude-standard::
        Also search in ignored files by not honoring the `.gitignore`
        mechanism. Only useful with `--untracked`.
@@ -64,9 +72,9 @@ OPTIONS
 --recurse-submodules::
        Recursively search in each submodule that is active and
        checked out in the repository.  When used in combination with the
-       <tree> option the prefix of all submodule output will be the name of
-       the parent project's <tree> object. This option has no effect
-       if `--no-index` is given.
+       _<tree>_ option the prefix of all submodule output will be the name of
+       the parent project's _<tree>_ object.  This option cannot be used together
+       with `--untracked`, and it has no effect if `--no-index` is specified.
 
 -a::
 --text::
@@ -178,7 +186,7 @@ providing this option will cause it to die.
        Use \0 as the delimiter for pathnames in the output, and print
        them verbatim. Without this option, pathnames with "unusual"
        characters are quoted as explained for the configuration
-       variable core.quotePath (see linkgit:git-config[1]).
+       variable `core.quotePath` (see linkgit:git-config[1]).
 
 -o::
 --only-matching::
@@ -248,8 +256,8 @@ providing this option will cause it to die.
        a non-zero status.
 
 --threads <num>::
-       Number of grep worker threads to use.
-       See `grep.threads` in 'CONFIGURATION' for more information.
+       Number of `grep` worker threads to use.  See 'NOTES ON THREADS'
+       and `grep.threads` in 'CONFIGURATION' for more information.
 
 -f <file>::
        Read patterns from <file>, one per line.
@@ -332,13 +340,13 @@ EXAMPLES
 NOTES ON THREADS
 ----------------
 
-The `--threads` option (and the grep.threads configuration) will be ignored when
+The `--threads` option (and the `grep.threads` configuration) will be ignored when
 `--open-files-in-pager` is used, forcing a single-threaded execution.
 
 When grepping the object store (with `--cached` or giving tree objects), running
-with multiple threads might perform slower than single threaded if `--textconv`
-is given and there are too many text conversions. So if you experience low
-performance in this case, it might be desirable to use `--threads=1`.
+with multiple threads might perform slower than single-threaded if `--textconv`
+is given and there are too many text conversions.  Thus, if low performance is
+experienced in this case, it might be desirable to use `--threads=1`.
 
 CONFIGURATION
 -------------
index 2f864e11ed9719a2443ece695534636fcb6e2631..daff93bd164b7c0bba6d977276a5d0a81f81d1fa 100644 (file)
@@ -9,11 +9,11 @@ git-init - Create an empty Git repository or reinitialize an existing one
 SYNOPSIS
 --------
 [verse]
-'git init' [-q | --quiet] [--bare] [--template=<template-directory>]
-         [--separate-git-dir <git-dir>] [--object-format=<format>]
-         [--ref-format=<format>]
-         [-b <branch-name> | --initial-branch=<branch-name>]
-         [--shared[=<permissions>]] [<directory>]
+`git init` [`-q` | `--quiet`] [`--bare`] [++--template=++__<template-directory>__]
+         [`--separate-git-dir` _<git-dir>_] [++--object-format=++__<format>__]
+         [++--ref-format=++__<format>__]
+         [`-b` _<branch-name>_ | ++--initial-branch=++__<branch-name>__]
+         [++--shared++[++=++__<permissions>__]] [_<directory>_]
 
 
 DESCRIPTION
@@ -41,35 +41,35 @@ the repository to another place if `--separate-git-dir` is given).
 OPTIONS
 -------
 
--q::
---quiet::
+`-q`::
+`--quiet`::
 
 Only print error and warning messages; all other output will be suppressed.
 
---bare::
+`--bare`::
 
 Create a bare repository. If `GIT_DIR` environment is not set, it is set to the
 current working directory.
 
---object-format=<format>::
+++--object-format=++__<format>__::
 
 Specify the given object _<format>_ (hash algorithm) for the repository.  The valid
 values are `sha1` and (if enabled) `sha256`.  `sha1` is the default.
 +
 include::object-format-disclaimer.txt[]
 
---ref-format=<format>::
+++--ref-format=++__<format>__::
 
 Specify the given ref storage _<format>_ for the repository. The valid values are:
 +
 include::ref-storage-format.txt[]
 
---template=<template-directory>::
+++--template=++__<template-directory>__::
 
 Specify the directory from which templates will be used.  (See the "TEMPLATE
 DIRECTORY" section below.)
 
---separate-git-dir=<git-dir>::
+++--separate-git-dir=++__<git-dir>__::
 
 Instead of initializing the repository as a directory to either `$GIT_DIR` or
 `./.git/`, create a text file there containing the path to the actual
@@ -78,53 +78,53 @@ repository.
 +
 If this is a reinitialization, the repository will be moved to the specified path.
 
--b <branch-name>::
---initial-branch=<branch-name>::
+`-b` _<branch-name>_::
+++--initial-branch=++__<branch-name>__::
 
 Use _<branch-name>_ for the initial branch in the newly created
 repository.  If not specified, fall back to the default name (currently
 `master`, but this is subject to change in the future; the name can be
 customized via the `init.defaultBranch` configuration variable).
 
---shared[=(false|true|umask|group|all|world|everybody|<perm>)]::
+++--shared++[++=++(`false`|`true`|`umask`|`group`|`all`|`world`|`everybody`|_<perm>_)]::
 
 Specify that the Git repository is to be shared amongst several users.  This
 allows users belonging to the same group to push into that
 repository.  When specified, the config variable `core.sharedRepository` is
 set so that files and directories under `$GIT_DIR` are created with the
 requested permissions.  When not specified, Git will use permissions reported
-by `umask(2)`.
+by `umask`(2).
 +
 The option can have the following values, defaulting to `group` if no value
 is given:
 +
 --
-umask::
-false::
+`umask`::
+`false`::
 
-Use permissions reported by umask(2). The default, when `--shared` is not
+Use permissions reported by `umask`(2). The default, when `--shared` is not
 specified.
 
-group::
-true::
+`group`::
+`true`::
 
-Make the repository group-writable, (and g+sx, since the git group may not be
+Make the repository group-writable, (and `g+sx`, since the git group may not be
 the primary group of all users). This is used to loosen the permissions of an
-otherwise safe umask(2) value. Note that the umask still applies to the other
+otherwise safe `umask`(2) value. Note that the umask still applies to the other
 permission bits (e.g. if umask is `0022`, using `group` will not remove read
 privileges from other (non-group) users). See `0xxx` for how to exactly specify
 the repository permissions.
 
-all::
-world::
-everybody::
+`all`::
+`world`::
+`everybody`::
 
 Same as `group`, but make the repository readable by all users.
 
-<perm>::
+_<perm>_::
 
 _<perm>_ is a 3-digit octal number prefixed with `0` and each file
-will have mode _<perm>_. _<perm>_ will override users'`umask(2)`
+will have mode _<perm>_. _<perm>_ will override users' `umask`(2)
 value (and not only loosen permissions as `group` and `all`
 do). `0640` will create a repository which is group-readable, but
 not group-writable or accessible to others. `0660` will create a repo
index 418265f044d65000e9ae22235b9801644f9c2170..d9dfb75fef525f4a8379e0c52a78e2642e843858 100644 (file)
@@ -9,7 +9,7 @@ SYNOPSIS
 --------
 [verse]
 'git interpret-trailers' [--in-place] [--trim-empty]
-                       [(--trailer (<key>|<keyAlias>)[(=|:)<value>])...]
+                       [(--trailer (<key>|<key-alias>)[(=|:)<value>])...]
                        [--parse] [<file>...]
 
 DESCRIPTION
@@ -67,9 +67,9 @@ key: value
 This means that the trimmed <key> and <value> will be separated by
 `': '` (one colon followed by one space).
 
-For convenience, a <keyAlias> can be configured to make using `--trailer`
+For convenience, a <key-alias> can be configured to make using `--trailer`
 shorter to type on the command line. This can be configured using the
-'trailer.<keyAlias>.key' configuration variable. The <keyAlias> must be a prefix
+'trailer.<key-alias>.key' configuration variable. The <keyAlias> must be a prefix
 of the full <key> string, although case sensitivity does not matter. For
 example, if you have
 
index 284956acb3c5e8bb168626c9729a17a799c0afd7..2dcabaf74cefb4111e94e92b8c7e47e8346a1d42 100644 (file)
@@ -8,7 +8,7 @@ git-pack-refs - Pack heads and tags for efficient repository access
 SYNOPSIS
 --------
 [verse]
-'git pack-refs' [--all] [--no-prune] [--include <pattern>] [--exclude <pattern>]
+'git pack-refs' [--all] [--no-prune] [--auto] [--include <pattern>] [--exclude <pattern>]
 
 DESCRIPTION
 -----------
@@ -60,6 +60,19 @@ with many branches of historical interests.
 The command usually removes loose refs under `$GIT_DIR/refs`
 hierarchy after packing them.  This option tells it not to.
 
+--auto::
+
+Pack refs as needed depending on the current state of the ref database. The
+behavior depends on the ref format used by the repository and may change in the
+future.
++
+       - "files": No special handling for `--auto` has been implemented.
++
+       - "reftable": Tables are compacted such that they form a geometric
+         sequence. For two tables N and N+1, where N+1 is newer, this
+         maintains the property that N is at least twice as big as N+1. Only
+         tables that violate this property are compacted.
+
 --include <pattern>::
 
 Pack refs based on a `glob(7)` pattern. Repetitions of this option
index 0e14f8b5b25924c98c4bda92729787f2361ada9c..b2ae496e488c1ebf3aaf79609987ab0bdbc5f34a 100644 (file)
@@ -87,7 +87,7 @@ OPTIONS
 --verbose::
        Pass --verbose to git-fetch and git-merge.
 
---[no-]recurse-submodules[=yes|on-demand|no]::
+--[no-]recurse-submodules[=(yes|on-demand|no)]::
        This option controls if new commits of populated submodules should
        be fetched, and if the working trees of active submodules should be
        updated, too (see linkgit:git-fetch[1], linkgit:git-config[1] and
@@ -105,7 +105,7 @@ Options related to merging
 include::merge-options.txt[]
 
 -r::
---rebase[=false|true|merges|interactive]::
+--rebase[=(false|true|merges|interactive)]::
        When true, rebase the current branch on top of the upstream
        branch after fetching. If there is a remote-tracking branch
        corresponding to the upstream branch and the upstream branch
index e7e725044db418845cd8ee53aed60fba2374634f..74df345f9e8688365997e968d86b3d4bc87a0b5c 100644 (file)
@@ -12,7 +12,7 @@ SYNOPSIS
        [--onto <newbase> | --keep-base] [<upstream> [<branch>]]
 'git rebase' [-i | --interactive] [<options>] [--exec <cmd>] [--onto <newbase>]
        --root [<branch>]
-'git rebase' (--continue | --skip | --abort | --quit | --edit-todo | --show-current-patch)
+'git rebase' (--continue|--skip|--abort|--quit|--edit-todo|--show-current-patch)
 
 DESCRIPTION
 -----------
@@ -289,17 +289,25 @@ See also INCOMPATIBLE OPTIONS below.
 +
 See also INCOMPATIBLE OPTIONS below.
 
---empty=(drop|keep|ask)::
+--empty=(drop|keep|stop)::
        How to handle commits that are not empty to start and are not
        clean cherry-picks of any upstream commit, but which become
        empty after rebasing (because they contain a subset of already
-       upstream changes).  With drop (the default), commits that
-       become empty are dropped.  With keep, such commits are kept.
-       With ask (implied by `--interactive`), the rebase will halt when
-       an empty commit is applied allowing you to choose whether to
-       drop it, edit files more, or just commit the empty changes.
-       Other options, like `--exec`, will use the default of drop unless
-       `-i`/`--interactive` is explicitly specified.
+       upstream changes):
++
+--
+`drop`;;
+       The commit will be dropped. This is the default behavior.
+`keep`;;
+       The commit will be kept. This option is implied when `--exec` is
+       specified unless `-i`/`--interactive` is also specified.
+`stop`;;
+`ask`;;
+       The rebase will halt when the commit is applied, allowing you to
+       choose whether to drop it, edit files more, or just commit the empty
+       changes. This option is implied when `-i`/`--interactive` is
+       specified. `ask` is a deprecated synonym of `stop`.
+--
 +
 Note that commits which start empty are kept (unless `--no-keep-empty`
 is specified), and commits which are clean cherry-picks (as determined
@@ -704,7 +712,7 @@ be dropped automatically with `--no-keep-empty`).
 Similar to the apply backend, by default the merge backend drops
 commits that become empty unless `-i`/`--interactive` is specified (in
 which case it stops and asks the user what to do).  The merge backend
-also has an `--empty=(drop|keep|ask)` option for changing the behavior
+also has an `--empty=(drop|keep|stop)` option for changing the behavior
 of handling commits that become empty.
 
 Directory rename detection
index 5d83dd36da11f3a7e5294bc5825e131c81d17633..f9d5a35fa00d7b779e43e884ca228e5a8a23425e 100644 (file)
@@ -159,6 +159,18 @@ for another option.
        unfortunately named tag "master"), and shows them as full
        refnames (e.g. "refs/heads/master").
 
+--output-object-format=(sha1|sha256|storage)::
+
+       Allow oids to be input from any object format that the current
+       repository supports.
+
+       Specifying "sha1" translates if necessary and returns a sha1 oid.
+
+       Specifying "sha256" translates if necessary and returns a sha256 oid.
+
+       Specifying "storage" translates if necessary and returns an oid in
+       encoded in the storage hash algorithm.
+
 Options for Objects
 ~~~~~~~~~~~~~~~~~~~
 
index 8264f8738093cf64b7e32201826fb170dd512c73..c5d664f4519ba61824c212a8fb2510df17c8bf03 100644 (file)
@@ -9,7 +9,7 @@ git-send-email - Send a collection of patches as emails
 SYNOPSIS
 --------
 [verse]
-'git send-email' [<options>] <file|directory>...
+'git send-email' [<options>] (<file>|<directory>)...
 'git send-email' [<options>] <format-patch-options>
 'git send-email' --dump-aliases
 
@@ -278,7 +278,7 @@ must be used for each option.
        if a username is not specified (with `--smtp-user` or `sendemail.smtpUser`),
        then authentication is not attempted.
 
---smtp-debug=0|1::
+--smtp-debug=(0|1)::
        Enable (1) or disable (0) debug output. If enabled, SMTP
        commands and replies will be printed. Useful to debug TLS
        connection and authentication problems.
@@ -301,7 +301,9 @@ must be used for each option.
 Automating
 ~~~~~~~~~~
 
---no-[to|cc|bcc]::
+--no-to::
+--no-cc::
+--no-bcc::
        Clears any list of "To:", "Cc:", "Bcc:" addresses previously
        set via config.
 
index b0f36fabfb391c10582a6f0feb63fa596e693ddc..9a376886a5867a63d173d85162c57027f336f915 100644 (file)
@@ -79,6 +79,8 @@ Consider enabling untracked cache and split index if supported (see
 `git update-index --untracked-cache` and `git update-index
 --split-index`), Otherwise you can use `no` to have `git status`
 return more quickly without showing untracked files.
+All usual spellings for Boolean value `true` are taken as `normal`
+and `false` as `no`.
 
 The default can be changed using the status.showUntrackedFiles
 configuration variable documented in linkgit:git-config[1].
index 0561808cca04a6873909ad18a6fd5f6e9af8b8de..374a2ebd2b0bdaa1e0a8cb09e172f27d1fb5fada 100644 (file)
@@ -8,21 +8,21 @@ git-update-ref - Update the object name stored in a ref safely
 SYNOPSIS
 --------
 [verse]
-'git update-ref' [-m <reason>] [--no-deref] (-d <ref> [<oldvalue>] | [--create-reflog] <ref> <newvalue> [<oldvalue>] | --stdin [-z])
+'git update-ref' [-m <reason>] [--no-deref] (-d <ref> [<old-oid>] | [--create-reflog] <ref> <new-oid> [<old-oid>] | --stdin [-z])
 
 DESCRIPTION
 -----------
-Given two arguments, stores the <newvalue> in the <ref>, possibly
+Given two arguments, stores the <new-oid> in the <ref>, possibly
 dereferencing the symbolic refs.  E.g. `git update-ref HEAD
-<newvalue>` updates the current branch head to the new object.
+<new-oid>` updates the current branch head to the new object.
 
-Given three arguments, stores the <newvalue> in the <ref>,
+Given three arguments, stores the <new-oid> in the <ref>,
 possibly dereferencing the symbolic refs, after verifying that
-the current value of the <ref> matches <oldvalue>.
-E.g. `git update-ref refs/heads/master <newvalue> <oldvalue>`
-updates the master branch head to <newvalue> only if its current
-value is <oldvalue>.  You can specify 40 "0" or an empty string
-as <oldvalue> to make sure that the ref you are creating does
+the current value of the <ref> matches <old-oid>.
+E.g. `git update-ref refs/heads/master <new-oid> <old-oid>`
+updates the master branch head to <new-oid> only if its current
+value is <old-oid>.  You can specify 40 "0" or an empty string
+as <old-oid> to make sure that the ref you are creating does
 not exist.
 
 It also allows a "ref" file to be a symbolic pointer to another
@@ -56,15 +56,15 @@ ref symlink to some other tree, if you have copied a whole
 archive by creating a symlink tree).
 
 With `-d` flag, it deletes the named <ref> after verifying it
-still contains <oldvalue>.
+still contains <old-oid>.
 
 With `--stdin`, update-ref reads instructions from standard input and
 performs all modifications together.  Specify commands of the form:
 
-       update SP <ref> SP <newvalue> [SP <oldvalue>] LF
-       create SP <ref> SP <newvalue> LF
-       delete SP <ref> [SP <oldvalue>] LF
-       verify SP <ref> [SP <oldvalue>] LF
+       update SP <ref> SP <new-oid> [SP <old-oid>] LF
+       create SP <ref> SP <new-oid> LF
+       delete SP <ref> [SP <old-oid>] LF
+       verify SP <ref> [SP <old-oid>] LF
        option SP <opt> LF
        start LF
        prepare LF
@@ -82,10 +82,10 @@ specify a missing value, omit the value and its preceding SP entirely.
 Alternatively, use `-z` to specify in NUL-terminated format, without
 quoting:
 
-       update SP <ref> NUL <newvalue> NUL [<oldvalue>] NUL
-       create SP <ref> NUL <newvalue> NUL
-       delete SP <ref> NUL [<oldvalue>] NUL
-       verify SP <ref> NUL [<oldvalue>] NUL
+       update SP <ref> NUL <new-oid> NUL [<old-oid>] NUL
+       create SP <ref> NUL <new-oid> NUL
+       delete SP <ref> NUL [<old-oid>] NUL
+       verify SP <ref> NUL [<old-oid>] NUL
        option SP <opt> NUL
        start NUL
        prepare NUL
@@ -100,22 +100,22 @@ recognizes as an object name.  Commands in any other format or a
 repeated <ref> produce an error.  Command meanings are:
 
 update::
-       Set <ref> to <newvalue> after verifying <oldvalue>, if given.
-       Specify a zero <newvalue> to ensure the ref does not exist
-       after the update and/or a zero <oldvalue> to make sure the
+       Set <ref> to <new-oid> after verifying <old-oid>, if given.
+       Specify a zero <new-oid> to ensure the ref does not exist
+       after the update and/or a zero <old-oid> to make sure the
        ref does not exist before the update.
 
 create::
-       Create <ref> with <newvalue> after verifying it does not
-       exist.  The given <newvalue> may not be zero.
+       Create <ref> with <new-oid> after verifying it does not
+       exist.  The given <new-oid> may not be zero.
 
 delete::
-       Delete <ref> after verifying it exists with <oldvalue>, if
-       given.  If given, <oldvalue> may not be zero.
+       Delete <ref> after verifying it exists with <old-oid>, if
+       given.  If given, <old-oid> may not be zero.
 
 verify::
-       Verify <ref> against <oldvalue> but do not change it.  If
-       <oldvalue> is zero or missing, the ref must not exist.
+       Verify <ref> against <old-oid> but do not change it.  If
+       <old-oid> is zero or missing, the ref must not exist.
 
 option::
        Modify the behavior of the next command naming a <ref>.
@@ -141,7 +141,7 @@ abort::
        Abort the transaction, releasing all locks if the transaction is in
        prepared state.
 
-If all <ref>s can be locked with matching <oldvalue>s
+If all <ref>s can be locked with matching <old-oid>s
 simultaneously, all modifications are performed.  Otherwise, no
 modifications are performed.  Note that while each individual
 <ref> is updated or deleted atomically, a concurrent reader may
@@ -161,7 +161,7 @@ formatted as:
 
 Where "oldsha1" is the 40 character hexadecimal value previously
 stored in <ref>, "newsha1" is the 40 character hexadecimal value of
-<newvalue> and "committer" is the committer's name, email address
+<new-oid> and "committer" is the committer's name, email address
 and date in the standard Git committer ident format.
 
 Optionally with -m:
index e6b766d5c3ab54e6fc97d3cf31f9093a6eac5a41..7a1b112a3e70203a8294952333c05845bfcb4da6 100644 (file)
@@ -960,7 +960,7 @@ will never be returned from the commit-graph at the cost of performance.
 `GIT_PROTOCOL`::
        For internal use only.  Used in handshaking the wire protocol.
        Contains a colon ':' separated list of keys with optional values
-       'key[=value]'.  Presence of unknown keys and values must be
+       '<key>[=<value>]'.  Presence of unknown keys and values must be
        ignored.
 +
 Note that servers may need to be configured to allow this variable to
index 37f91d5b50ca3ac9053bd687ee540ca87de21083..ee9b92c90da99df3dc77e2122ee9feead2d1de8e 100644 (file)
@@ -275,12 +275,12 @@ This hook executes once for the receive operation. It takes no
 arguments, but for each ref to be updated it receives on standard
 input a line of the format:
 
-  <old-value> SP <new-value> SP <ref-name> LF
+  <old-oid> SP <new-oid> SP <ref-name> LF
 
-where `<old-value>` is the old object name stored in the ref,
-`<new-value>` is the new object name to be stored in the ref and
+where `<old-oid>` is the old object name stored in the ref,
+`<new-oid>` is the new object name to be stored in the ref and
 `<ref-name>` is the full name of the ref.
-When creating a new ref, `<old-value>` is the all-zeroes object name.
+When creating a new ref, `<old-oid>` is the all-zeroes object name.
 
 If the hook exits with non-zero status, none of the refs will be
 updated. If the hook exits with zero, updating of individual refs can
@@ -503,13 +503,13 @@ given reference transaction is in:
 For each reference update that was added to the transaction, the hook
 receives on standard input a line of the format:
 
-  <old-value> SP <new-value> SP <ref-name> LF
+  <old-oid> SP <new-oid> SP <ref-name> LF
 
-where `<old-value>` is the old object name passed into the reference
-transaction, `<new-value>` is the new object name to be stored in the
+where `<old-oid>` is the old object name passed into the reference
+transaction, `<new-oid>` is the new object name to be stored in the
 ref and `<ref-name>` is the full name of the ref. When force updating
 the reference regardless of its current value or when the reference is
-to be created anew, `<old-value>` is the all-zeroes object name. To
+to be created anew, `<old-oid>` is the all-zeroes object name. To
 distinguish these cases, you can inspect the current value of
 `<ref-name>` via `git rev-parse`.
 
index 07c8439a6f784bc818108fad6fdd82891509dd84..d0be008e5e3576012768ab0ed28ebc55a7f135c7 100644 (file)
@@ -479,14 +479,14 @@ set by Git if the remote helper has the 'option' capability.
 'option depth' <depth>::
        Deepens the history of a shallow repository.
 
-'option deepen-since <timestamp>::
+'option deepen-since' <timestamp>::
        Deepens the history of a shallow repository based on time.
 
-'option deepen-not <ref>::
+'option deepen-not' <ref>::
        Deepens the history of a shallow repository excluding ref.
        Multiple options add up.
 
-'option deepen-relative {'true'|'false'}::
+'option deepen-relative' {'true'|'false'}::
        Deepens the history of a shallow repository relative to
        current boundary. Only valid when used with "option depth".
 
@@ -542,13 +542,10 @@ set by Git if the remote helper has the 'option' capability.
        transaction.  If successful, all refs will be updated, or none will.  If the
        remote side does not support this capability, the push will fail.
 
-'option object-format' {'true'|algorithm}::
-       If 'true', indicate that the caller wants hash algorithm information
+'option object-format true'::
+       Indicate that the caller wants hash algorithm information
        to be passed back from the remote.  This mode is used when fetching
        refs.
-+
-If set to an algorithm, indicate that the caller wants to interact with
-the remote side using that algorithm.
 
 SEE ALSO
 --------
index 151ee84cebcef3ca549f3150e69e91b29cc2f372..4e727deedd21235403c4a3b02e8c826780d39d8b 100644 (file)
@@ -100,7 +100,7 @@ info "The user is: '$username'"
 
 if test -f "$allowed_users_file"
 then
-  rc=$(cat $allowed_users_file | grep -v '^#' | grep -v '^$' |
+  rc=$(grep -Ev '^(#|$)' $allowed_users_file |
     while read heads user_patterns
     do
       # does this rule apply to us?
@@ -138,7 +138,7 @@ info "'$groups'"
 
 if test -f "$allowed_groups_file"
 then
-  rc=$(cat $allowed_groups_file | grep -v '^#' | grep -v '^$' |
+  rc=$(grep -Ev '^(#|$)' $allowed_groups_file |
     while read heads group_patterns
     do
       # does this rule apply to us?
index d38b4ab5666c35e0ae83680abca0e067c7b14792..8ee940b6a452daba6202f1b76747c7eeea8a6f48 100644 (file)
@@ -316,9 +316,8 @@ multiple times, the last occurrence wins.
    `Reviewed-by`.
 ** 'only[=<bool>]': select whether non-trailer lines from the trailer
    block should be included.
-** 'separator=<sep>': specify a separator inserted between trailer
-   lines. When this option is not given each trailer line is
-   terminated with a line feed character. The string <sep> may contain
+** 'separator=<sep>': specify the separator inserted between trailer
+   lines. Defaults to a line feed character. The string <sep> may contain
    the literal formatting codes described above. To use comma as
    separator one must use `%x2C` as it would otherwise be parsed as
    next option. E.g., `%(trailers:key=Ticket,separator=%x2C )`
@@ -329,10 +328,9 @@ multiple times, the last occurrence wins.
    `%(trailers:only,unfold=true)` unfolds and shows all trailer lines.
 ** 'keyonly[=<bool>]': only show the key part of the trailer.
 ** 'valueonly[=<bool>]': only show the value part of the trailer.
-** 'key_value_separator=<sep>': specify a separator inserted between
-   trailer lines. When this option is not given each trailer key-value
-   pair is separated by ": ". Otherwise it shares the same semantics
-   as 'separator=<sep>' above.
+** 'key_value_separator=<sep>': specify the separator inserted between
+   the key and value of each trailer. Defaults to ": ". Otherwise it
+   shares the same semantics as 'separator=<sep>' above.
 
 NOTE: Some placeholders may depend on other options given to the
 revision traversal engine. For example, the `%g*` reflog options will
index 408d9314d0887969a8cb485b59265a3c4b570ced..00ccf68744103d4dc6ffdce5c582c726c2d9ea9d 100644 (file)
@@ -316,12 +316,12 @@ list.
 With `--pretty` format other than `oneline` and `reference` (for obvious reasons),
 this causes the output to have two extra lines of information
 taken from the reflog.  The reflog designator in the output may be shown
-as `ref@{Nth}` (where `Nth` is the reverse-chronological index in the
-reflog) or as `ref@{timestamp}` (with the timestamp for that entry),
+as `ref@{<Nth>}` (where _<Nth>_ is the reverse-chronological index in the
+reflog) or as `ref@{<timestamp>}` (with the _<timestamp>_ for that entry),
 depending on a few rules:
 +
 --
-1. If the starting point is specified as `ref@{Nth}`, show the index
+1. If the starting point is specified as `ref@{<Nth>}`, show the index
    format.
 +
 2. If the starting point was specified as `ref@{now}`, show the
index 0b9e0c4302d850a7a38044d03bd1146764bc83ca..7cec85aef17f437ce71a73f0e43fa19c046a4aa8 100644 (file)
@@ -15,14 +15,14 @@ should be used with caution on unsecured networks.
 
 The following syntaxes may be used with them:
 
-- ssh://{startsb}user@{endsb}host.xz{startsb}:port{endsb}/path/to/repo.git/
-- git://host.xz{startsb}:port{endsb}/path/to/repo.git/
-- http{startsb}s{endsb}://host.xz{startsb}:port{endsb}/path/to/repo.git/
-- ftp{startsb}s{endsb}://host.xz{startsb}:port{endsb}/path/to/repo.git/
+- ++ssh://++{startsb}__<user>__++@++{endsb}__<host>__{startsb}++:++__<port>__{endsb}++/++__<path-to-git-repo>__
+- ++git://++__<host>__{startsb}:__<port>__{endsb}++/++__<path-to-git-repo>__
+- ++http++{startsb}++s++{endsb}++://++__<host>__{startsb}++:++__<port>__{endsb}++/++__<path-to-git-repo>__
+- ++ftp++{startsb}++s++{endsb}++://++__<host>__{startsb}++:++__<port>__{endsb}++/++__<path-to-git-repo>__
 
 An alternative scp-like syntax may also be used with the ssh protocol:
 
-- {startsb}user@{endsb}host.xz:path/to/repo.git/
+- {startsb}__<user>__++@++{endsb}__<host>__++:/++__<path-to-git-repo>__
 
 This syntax is only recognized if there are no slashes before the
 first colon. This helps differentiate a local path that contains a
@@ -30,17 +30,17 @@ colon. For example the local path `foo:bar` could be specified as an
 absolute path or `./foo:bar` to avoid being misinterpreted as an ssh
 url.
 
-The ssh and git protocols additionally support ~username expansion:
+The ssh and git protocols additionally support ++~++__<username>__ expansion:
 
-- ssh://{startsb}user@{endsb}host.xz{startsb}:port{endsb}/~{startsb}user{endsb}/path/to/repo.git/
-- git://host.xz{startsb}:port{endsb}/~{startsb}user{endsb}/path/to/repo.git/
-- {startsb}user@{endsb}host.xz:/~{startsb}user{endsb}/path/to/repo.git/
+- ++ssh://++{startsb}__<user>__++@++{endsb}__<host>__{startsb}++:++__<port>__{endsb}++/~++__<user>__++/++__<path-to-git-repo>__
+- ++git://++__<host>__{startsb}++:++__<port>__{endsb}++/~++__<user>__++/++__<path-to-git-repo>__
+- {startsb}__<user>__++@++{endsb}__<host>__++:~++__<user>__++/++__<path-to-git-repo>__
 
 For local repositories, also supported by Git natively, the following
 syntaxes may be used:
 
-- /path/to/repo.git/
-- \file:///path/to/repo.git/
+- `/path/to/repo.git/`
+- ++file:///path/to/repo.git/++
 
 ifndef::git-clone[]
 These two syntaxes are mostly equivalent, except when cloning, when
@@ -57,11 +57,11 @@ endif::git-clone[]
 accept a suitable bundle file. See linkgit:git-bundle[1].
 
 When Git doesn't know how to handle a certain transport protocol, it
-attempts to use the `remote-<transport>` remote helper, if one
+attempts to use the `remote-`{empty}__<transport>__ remote helper, if one
 exists. To explicitly request a remote helper, the following syntax
 may be used:
 
-- _<transport>_::_<address>_
+- _<transport>_::__<address>__
 
 where _<address>_ may be a path, a server and path, or an arbitrary
 URL-like string recognized by the specific remote helper being
@@ -72,10 +72,11 @@ you want to use a different format for them (such that the URLs you
 use will be rewritten into URLs that work), you can create a
 configuration section of the form:
 
-------------
-       [url "<actual-url-base>"]
-               insteadOf = <other-url-base>
-------------
+[verse]
+--
+       [url "__<actual-url-base>__"]
+               insteadOf = _<other-url-base>_
+--
 
 For example, with this:
 
@@ -91,10 +92,11 @@ rewritten in any context that takes a URL to be "git://git.host.xz/repo.git".
 If you want to rewrite URLs for push only, you can create a
 configuration section of the form:
 
-------------
-       [url "<actual-url-base>"]
-               pushInsteadOf = <other-url-base>
-------------
+[verse]
+--
+       [url "__<actual-url-base>__"]
+               pushInsteadOf = _<other-url-base>_
+--
 
 For example, with this:
 
index 4e255c81f22386389c7460d8f5e59426673b5a5a..c43c1bd1a05c895737307cac38e8f3caf573ffe1 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -757,6 +757,7 @@ ETAGS_TARGET = TAGS
 # runs in the future.
 FUZZ_OBJS += oss-fuzz/dummy-cmd-main.o
 FUZZ_OBJS += oss-fuzz/fuzz-commit-graph.o
+FUZZ_OBJS += oss-fuzz/fuzz-config.o
 FUZZ_OBJS += oss-fuzz/fuzz-date.o
 FUZZ_OBJS += oss-fuzz/fuzz-pack-headers.o
 FUZZ_OBJS += oss-fuzz/fuzz-pack-idx.o
@@ -797,6 +798,7 @@ TEST_BUILTINS_OBJS += test-config.o
 TEST_BUILTINS_OBJS += test-crontab.o
 TEST_BUILTINS_OBJS += test-csprng.o
 TEST_BUILTINS_OBJS += test-date.o
+TEST_BUILTINS_OBJS += test-delete-gpgsig.o
 TEST_BUILTINS_OBJS += test-delta.o
 TEST_BUILTINS_OBJS += test-dir-iterator.o
 TEST_BUILTINS_OBJS += test-drop-caches.o
@@ -1060,6 +1062,7 @@ LIB_OBJS += list-objects-filter.o
 LIB_OBJS += list-objects.o
 LIB_OBJS += lockfile.o
 LIB_OBJS += log-tree.o
+LIB_OBJS += loose.o
 LIB_OBJS += ls-refs.o
 LIB_OBJS += mailinfo.o
 LIB_OBJS += mailmap.o
@@ -1080,6 +1083,7 @@ LIB_OBJS += notes-cache.o
 LIB_OBJS += notes-merge.o
 LIB_OBJS += notes-utils.o
 LIB_OBJS += notes.o
+LIB_OBJS += object-file-convert.o
 LIB_OBJS += object-file.o
 LIB_OBJS += object-name.o
 LIB_OBJS += object.o
index 68f525b35cfe650ebf440a1d879a761d55aa02e6..a06dd189854a7a8e680ce76b8995296f2e773b98 100644 (file)
@@ -1105,26 +1105,26 @@ static int edit_hunk_manually(struct add_p_state *s, struct hunk *hunk)
        size_t i;
 
        strbuf_reset(&s->buf);
-       strbuf_commented_addf(&s->buf, comment_line_char,
+       strbuf_commented_addf(&s->buf, comment_line_str,
                              _("Manual hunk edit mode -- see bottom for "
                                "a quick guide.\n"));
        render_hunk(s, hunk, 0, 0, &s->buf);
-       strbuf_commented_addf(&s->buf, comment_line_char,
+       strbuf_commented_addf(&s->buf, comment_line_str,
                              _("---\n"
                                "To remove '%c' lines, make them ' ' lines "
                                "(context).\n"
                                "To remove '%c' lines, delete them.\n"
-                               "Lines starting with %c will be removed.\n"),
+                               "Lines starting with %s will be removed.\n"),
                              s->mode->is_reverse ? '+' : '-',
                              s->mode->is_reverse ? '-' : '+',
-                             comment_line_char);
-       strbuf_commented_addf(&s->buf, comment_line_char, "%s",
+                             comment_line_str);
+       strbuf_commented_addf(&s->buf, comment_line_str, "%s",
                              _(s->mode->edit_hunk_hint));
        /*
         * TRANSLATORS: 'it' refers to the patch mentioned in the previous
         * messages.
         */
-       strbuf_commented_addf(&s->buf, comment_line_char,
+       strbuf_commented_addf(&s->buf, comment_line_str,
                              _("If it does not apply cleanly, you will be "
                                "given an opportunity to\n"
                                "edit again.  If all lines of the hunk are "
@@ -1139,7 +1139,7 @@ static int edit_hunk_manually(struct add_p_state *s, struct hunk *hunk)
        for (i = 0; i < s->buf.len; ) {
                size_t next = find_next_line(&s->buf, i);
 
-               if (s->buf.buf[i] != comment_line_char)
+               if (!starts_with(s->buf.buf + i, comment_line_str))
                        strbuf_add(&s->plain, s->buf.buf + i, next - i);
                i = next;
        }
@@ -1388,13 +1388,14 @@ N_("j - leave this hunk undecided, see next undecided hunk\n"
    "/ - search for a hunk matching the given regex\n"
    "s - split the current hunk into smaller hunks\n"
    "e - manually edit the current hunk\n"
+   "p - print the current hunk\n"
    "? - print help\n");
 
 static int patch_update_file(struct add_p_state *s,
                             struct file_diff *file_diff)
 {
        size_t hunk_index = 0;
-       ssize_t i, undecided_previous, undecided_next;
+       ssize_t i, undecided_previous, undecided_next, rendered_hunk_index = -1;
        struct hunk *hunk;
        char ch;
        struct child_process cp = CHILD_PROCESS_INIT;
@@ -1447,8 +1448,11 @@ static int patch_update_file(struct add_p_state *s,
 
                strbuf_reset(&s->buf);
                if (file_diff->hunk_nr) {
-                       render_hunk(s, hunk, 0, colored, &s->buf);
-                       fputs(s->buf.buf, stdout);
+                       if (rendered_hunk_index != hunk_index) {
+                               render_hunk(s, hunk, 0, colored, &s->buf);
+                               fputs(s->buf.buf, stdout);
+                               rendered_hunk_index = hunk_index;
+                       }
 
                        strbuf_reset(&s->buf);
                        if (undecided_previous >= 0) {
@@ -1480,6 +1484,7 @@ static int patch_update_file(struct add_p_state *s,
                                permitted |= ALLOW_EDIT;
                                strbuf_addstr(&s->buf, ",e");
                        }
+                       strbuf_addstr(&s->buf, ",p");
                }
                if (file_diff->deleted)
                        prompt_mode_type = PROMPT_DELETION;
@@ -1644,13 +1649,15 @@ soft_increment:
                        hunk_index = i;
                } else if (s->answer.buf[0] == 's') {
                        size_t splittable_into = hunk->splittable_into;
-                       if (!(permitted & ALLOW_SPLIT))
+                       if (!(permitted & ALLOW_SPLIT)) {
                                err(s, _("Sorry, cannot split this hunk"));
-                       else if (!split_hunk(s, file_diff,
-                                            hunk - file_diff->hunk))
+                       else if (!split_hunk(s, file_diff,
+                                            hunk - file_diff->hunk)) {
                                color_fprintf_ln(stdout, s->s.header_color,
                                                 _("Split into %d hunks."),
                                                 (int)splittable_into);
+                               rendered_hunk_index = -1;
+                       }
                } else if (s->answer.buf[0] == 'e') {
                        if (!(permitted & ALLOW_EDIT))
                                err(s, _("Sorry, cannot edit this hunk"));
@@ -1658,6 +1665,8 @@ soft_increment:
                                hunk->use = USE_HUNK;
                                goto soft_increment;
                        }
+               } else if (s->answer.buf[0] == 'p') {
+                       rendered_hunk_index = -1;
                } else {
                        const char *p = _(help_patch_remainder), *eol = p;
 
index b0e05506871b9c402b75ad7ba52c47776797611d..75111191ad586d4cbae07e806f8b2bb6f7ebe83b 100644 (file)
--- a/advice.c
+++ b/advice.c
@@ -57,6 +57,7 @@ static struct {
        [ADVICE_GRAFT_FILE_DEPRECATED]                  = { "graftFileDeprecated" },
        [ADVICE_IGNORED_HOOK]                           = { "ignoredHook" },
        [ADVICE_IMPLICIT_IDENTITY]                      = { "implicitIdentity" },
+       [ADVICE_MERGE_CONFLICT]                         = { "mergeConflict" },
        [ADVICE_NESTED_TAG]                             = { "nestedTag" },
        [ADVICE_OBJECT_NAME_WARNING]                    = { "objectNameWarning" },
        [ADVICE_PUSH_ALREADY_EXISTS]                    = { "pushAlreadyExists" },
@@ -104,8 +105,9 @@ static void vadvise(const char *advice, int display_instructions,
 
        for (cp = buf.buf; *cp; cp = np) {
                np = strchrnul(cp, '\n');
-               fprintf(stderr, _("%shint: %.*s%s\n"),
+               fprintf(stderr, _("%shint:%s%.*s%s\n"),
                        advise_get_color(ADVICE_COLOR_HINT),
+                       (np == cp) ? "" : " ",
                        (int)(np - cp), cp,
                        advise_get_color(ADVICE_COLOR_RESET));
                if (*np)
index bf630ee3ac3eae23b05b9554f4cc20ce2ea7a280..c8d29f97f39bef84062d541009a27bd35a47f9dd 100644 (file)
--- a/advice.h
+++ b/advice.h
@@ -25,6 +25,7 @@ enum advice_type {
        ADVICE_GRAFT_FILE_DEPRECATED,
        ADVICE_IGNORED_HOOK,
        ADVICE_IMPLICIT_IDENTITY,
+       ADVICE_MERGE_CONFLICT,
        ADVICE_NESTED_TAG,
        ADVICE_OBJECT_NAME_WARNING,
        ADVICE_PUSH_ALREADY_EXISTS,
diff --git a/apply.c b/apply.c
index 432837a674c3cc559f762aa4b7766bd8f43177e0..e311013bc434dbec6612fa07b1d5c9e4d7979b74 100644 (file)
--- a/apply.c
+++ b/apply.c
@@ -1292,8 +1292,15 @@ static char *git_header_name(int p_value,
                                return NULL; /* no postimage name */
                        second = skip_tree_prefix(p_value, name + len + 1,
                                                  line_len - (len + 1));
+                       /*
+                        * If we are at the SP at the end of a directory,
+                        * skip_tree_prefix() may return NULL as that makes
+                        * it appears as if we have an absolute path.
+                        * Keep going to find another SP.
+                        */
                        if (!second)
-                               return NULL;
+                               continue;
+
                        /*
                         * Does len bytes starting at "name" and "second"
                         * (that are separated by one HT or SP we just
index a6730bebfa07d65d003b5594c63081e6ef7d3e88..5287fcdd8e0460063f40e4e3f7a812d248e41afd 100644 (file)
--- a/archive.c
+++ b/archive.c
@@ -339,7 +339,8 @@ int write_archive_entries(struct archiver_args *args,
                opts.src_index = args->repo->index;
                opts.dst_index = args->repo->index;
                opts.fn = oneway_merge;
-               init_tree_desc(&t, args->tree->buffer, args->tree->size);
+               init_tree_desc(&t, &args->tree->object.oid,
+                              args->tree->buffer, args->tree->size);
                if (unpack_trees(1, &t, &opts))
                        return -1;
                git_attr_set_direction(GIT_ATTR_INDEX);
index 393c10cbcf6315efb525b38db26e218bf6b1959d..e97699d6b9bad0bbce107cb1f14443ce1f62274f 100644 (file)
@@ -310,9 +310,9 @@ static void check_embedded_repo(const char *path)
        strbuf_strip_suffix(&name, "/");
 
        warning(_("adding embedded git repository: %s"), name.buf);
-       if (!adviced_on_embedded_repo &&
-           advice_enabled(ADVICE_ADD_EMBEDDED_REPO)) {
-               advise(embedded_advice, name.buf, name.buf);
+       if (!adviced_on_embedded_repo) {
+               advise_if_enabled(ADVICE_ADD_EMBEDDED_REPO,
+                                 embedded_advice, name.buf, name.buf);
                adviced_on_embedded_repo = 1;
        }
 
@@ -328,10 +328,8 @@ static int add_files(struct dir_struct *dir, int flags)
                fprintf(stderr, _(ignore_error));
                for (i = 0; i < dir->ignored_nr; i++)
                        fprintf(stderr, "%s\n", dir->ignored[i]->name);
-               if (advice_enabled(ADVICE_ADD_IGNORED_FILE))
-                       advise(_("Use -f if you really want to add them.\n"
-                               "Turn this message off by running\n"
-                               "\"git config advice.addIgnoredFile false\""));
+               advise_if_enabled(ADVICE_ADD_IGNORED_FILE,
+                                 _("Use -f if you really want to add them."));
                exit_status = 1;
        }
 
@@ -440,10 +438,8 @@ int cmd_add(int argc, const char **argv, const char *prefix)
 
        if (require_pathspec && pathspec.nr == 0) {
                fprintf(stderr, _("Nothing specified, nothing added.\n"));
-               if (advice_enabled(ADVICE_ADD_EMPTY_PATHSPEC))
-                       advise( _("Maybe you wanted to say 'git add .'?\n"
-                               "Turn this message off by running\n"
-                               "\"git config advice.addEmptyPathspec false\""));
+               advise_if_enabled(ADVICE_ADD_EMPTY_PATHSPEC,
+                                 _("Maybe you wanted to say 'git add .'?"));
                return 0;
        }
 
index d1990d7edcbe37467c59a420a9b03a5894e6da93..022cae2e8d83a20a4d80e4e22cd56fe5fc48d8d9 100644 (file)
@@ -1150,19 +1150,23 @@ static const char *msgnum(const struct am_state *state)
 static void NORETURN die_user_resolve(const struct am_state *state)
 {
        if (state->resolvemsg) {
-               printf_ln("%s", state->resolvemsg);
+               advise_if_enabled(ADVICE_MERGE_CONFLICT, "%s", state->resolvemsg);
        } else {
                const char *cmdline = state->interactive ? "git am -i" : "git am";
+               struct strbuf sb = STRBUF_INIT;
 
-               printf_ln(_("When you have resolved this problem, run \"%s --continue\"."), cmdline);
-               printf_ln(_("If you prefer to skip this patch, run \"%s --skip\" instead."), cmdline);
+               strbuf_addf(&sb, _("When you have resolved this problem, run \"%s --continue\".\n"), cmdline);
+               strbuf_addf(&sb, _("If you prefer to skip this patch, run \"%s --skip\" instead.\n"), cmdline);
 
                if (advice_enabled(ADVICE_AM_WORK_DIR) &&
                    is_empty_or_missing_file(am_path(state, "patch")) &&
                    !repo_index_has_changes(the_repository, NULL, NULL))
-                       printf_ln(_("To record the empty patch as an empty commit, run \"%s --allow-empty\"."), cmdline);
+                       strbuf_addf(&sb, _("To record the empty patch as an empty commit, run \"%s --allow-empty\".\n"), cmdline);
 
-               printf_ln(_("To restore the original branch and stop patching, run \"%s --abort\"."), cmdline);
+               strbuf_addf(&sb, _("To restore the original branch and stop patching, run \"%s --abort\"."), cmdline);
+
+               advise_if_enabled(ADVICE_MERGE_CONFLICT, "%s", sb.buf);
+               strbuf_release(&sb);
        }
 
        exit(128);
@@ -1286,7 +1290,7 @@ static int parse_mail(struct am_state *state, const char *mail)
 
        strbuf_addstr(&msg, "\n\n");
        strbuf_addbuf(&msg, &mi.log_message);
-       strbuf_stripspace(&msg, '\0');
+       strbuf_stripspace(&msg, NULL);
 
        assert(!state->author_name);
        state->author_name = strbuf_detach(&author_name, NULL);
@@ -1994,8 +1998,8 @@ static int fast_forward_to(struct tree *head, struct tree *remote, int reset)
        opts.reset = reset ? UNPACK_RESET_PROTECT_UNTRACKED : 0;
        opts.preserve_ignored = 0; /* FIXME: !overwrite_ignore */
        opts.fn = twoway_merge;
-       init_tree_desc(&t[0], head->buffer, head->size);
-       init_tree_desc(&t[1], remote->buffer, remote->size);
+       init_tree_desc(&t[0], &head->object.oid, head->buffer, head->size);
+       init_tree_desc(&t[1], &remote->object.oid, remote->buffer, remote->size);
 
        if (unpack_trees(2, t, &opts)) {
                rollback_lock_file(&lock_file);
@@ -2029,7 +2033,7 @@ static int merge_tree(struct tree *tree)
        opts.dst_index = &the_index;
        opts.merge = 1;
        opts.fn = oneway_merge;
-       init_tree_desc(&t[0], tree->buffer, tree->size);
+       init_tree_desc(&t[0], &tree->object.oid, tree->buffer, tree->size);
 
        if (unpack_trees(1, t, &opts)) {
                rollback_lock_file(&lock_file);
index 8c2305ad2c85586c960c986582f1d9e874a26a24..dd3e3a7dc09541892d4fb86f66f78f5d8038daa0 100644 (file)
@@ -677,18 +677,18 @@ static int edit_branch_description(const char *branch_name)
        exists = !read_branch_desc(&buf, branch_name);
        if (!buf.len || buf.buf[buf.len-1] != '\n')
                strbuf_addch(&buf, '\n');
-       strbuf_commented_addf(&buf, comment_line_char,
+       strbuf_commented_addf(&buf, comment_line_str,
                    _("Please edit the description for the branch\n"
                      "  %s\n"
-                     "Lines starting with '%c' will be stripped.\n"),
-                   branch_name, comment_line_char);
+                     "Lines starting with '%s' will be stripped.\n"),
+                   branch_name, comment_line_str);
        write_file_buf(edit_description(), buf.buf, buf.len);
        strbuf_reset(&buf);
        if (launch_editor(edit_description(), &buf, NULL)) {
                strbuf_release(&buf);
                return -1;
        }
-       strbuf_stripspace(&buf, comment_line_char);
+       strbuf_stripspace(&buf, comment_line_str);
 
        strbuf_addf(&name, "branch.%s.description", branch_name);
        if (buf.len || exists)
index 3106e56a130c58994fced2afd8dbd337134861ad..25f860a0d973caca44593cb28919e47afb13089d 100644 (file)
@@ -64,7 +64,8 @@ static void get_populated_hooks(struct strbuf *hook_info, int nongit)
 }
 
 static const char * const bugreport_usage[] = {
-       N_("git bugreport [(-o | --output-directory) <path>] [(-s | --suffix) <format>]\n"
+       N_("git bugreport [(-o | --output-directory) <path>]\n"
+          "              [(-s | --suffix) <format> | --no-suffix]\n"
           "              [--diagnose[=<mode>]]"),
        NULL
 };
@@ -138,8 +139,11 @@ int cmd_bugreport(int argc, const char **argv, const char *prefix)
        strbuf_complete(&report_path, '/');
        output_path_len = report_path.len;
 
-       strbuf_addstr(&report_path, "git-bugreport-");
-       strbuf_addftime(&report_path, option_suffix, localtime_r(&now, &tm), 0, 0);
+       strbuf_addstr(&report_path, "git-bugreport");
+       if (option_suffix) {
+               strbuf_addch(&report_path, '-');
+               strbuf_addftime(&report_path, option_suffix, localtime_r(&now, &tm), 0, 0);
+       }
        strbuf_addstr(&report_path, ".txt");
 
        switch (safe_create_leading_directories(report_path.buf)) {
index bbf851138ec40875367aba94bc5b5de29b8dbe77..0c948f40fb073cf15a7ec9f5bea661c7ec955124 100644 (file)
@@ -106,7 +106,10 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
        struct object_info oi = OBJECT_INFO_INIT;
        struct strbuf sb = STRBUF_INIT;
        unsigned flags = OBJECT_INFO_LOOKUP_REPLACE;
-       unsigned get_oid_flags = GET_OID_RECORD_PATH | GET_OID_ONLY_TO_DIE;
+       unsigned get_oid_flags =
+               GET_OID_RECORD_PATH |
+               GET_OID_ONLY_TO_DIE |
+               GET_OID_HASH_ANY;
        const char *path = force_path;
        const int opt_cw = (opt == 'c' || opt == 'w');
        if (!path && opt_cw)
@@ -226,7 +229,8 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
                                        die(_("unable to read %s"), oid_to_hex(&oid));
 
                                if (!skip_prefix(buffer, "object ", &target) ||
-                                   get_oid_hex(target, &blob_oid))
+                                   get_oid_hex_algop(target, &blob_oid,
+                                                     &hash_algos[oid.algo]))
                                        die("%s not a valid tag", oid_to_hex(&oid));
                                free(buffer);
                        } else
@@ -310,8 +314,8 @@ static int is_atom(const char *atom, const char *s, int slen)
        return alen == slen && !memcmp(atom, s, alen);
 }
 
-static void expand_atom(struct strbuf *sb, const char *atom, int len,
-                       struct expand_data *data)
+static int expand_atom(struct strbuf *sb, const char *atom, int len,
+                      struct expand_data *data)
 {
        if (is_atom("objectname", atom, len)) {
                if (!data->mark_query)
@@ -343,7 +347,8 @@ static void expand_atom(struct strbuf *sb, const char *atom, int len,
                        strbuf_addstr(sb,
                                      oid_to_hex(&data->delta_base_oid));
        } else
-               die("unknown format element: %.*s", len, atom);
+               return 0;
+       return 1;
 }
 
 static void expand_format(struct strbuf *sb, const char *start,
@@ -354,12 +359,11 @@ static void expand_format(struct strbuf *sb, const char *start,
 
                if (skip_prefix(start, "%", &start) || *start != '(')
                        strbuf_addch(sb, '%');
-               else if (!(end = strchr(start + 1, ')')))
-                       die("format element '%s' does not end in ')'", start);
-               else {
-                       expand_atom(sb, start + 1, end - start - 1, data);
+               else if ((end = strchr(start + 1, ')')) &&
+                        expand_atom(sb, start + 1, end - start - 1, data))
                        start = end + 1;
-               }
+               else
+                       strbuf_expand_bad_format(start, "cat-file");
        }
 }
 
@@ -517,7 +521,9 @@ static void batch_one_object(const char *obj_name,
                             struct expand_data *data)
 {
        struct object_context ctx;
-       int flags = opt->follow_symlinks ? GET_OID_FOLLOW_SYMLINKS : 0;
+       int flags =
+               GET_OID_HASH_ANY |
+               (opt->follow_symlinks ? GET_OID_FOLLOW_SYMLINKS : 0);
        enum get_oid_result result;
 
        result = get_oid_with_context(the_repository, obj_name,
index 15293a30134094f699d15035232bbc644277910c..947827de1d1abc7397ce4cb7190f7faf6e83d787 100644 (file)
@@ -91,7 +91,7 @@ struct checkout_opts {
        int new_branch_log;
        enum branch_track track;
        struct diff_options diff_options;
-       char *conflict_style;
+       int conflict_style;
 
        int branch_exists;
        const char *prefix;
@@ -100,6 +100,8 @@ struct checkout_opts {
        struct tree *source_tree;
 };
 
+#define CHECKOUT_OPTS_INIT { .conflict_style = -1, .merge = -1 }
+
 struct branch_info {
        char *name; /* The short name used */
        char *path; /* The full name of a real branch */
@@ -251,7 +253,8 @@ static int checkout_stage(int stage, const struct cache_entry *ce, int pos,
 }
 
 static int checkout_merged(int pos, const struct checkout *state,
-                          int *nr_checkouts, struct mem_pool *ce_mem_pool)
+                          int *nr_checkouts, struct mem_pool *ce_mem_pool,
+                          int conflict_style)
 {
        struct cache_entry *ce = the_index.cache[pos];
        const char *path = ce->name;
@@ -262,7 +265,7 @@ static int checkout_merged(int pos, const struct checkout *state,
        mmbuffer_t result_buf;
        struct object_id threeway[3];
        unsigned mode = 0;
-       struct ll_merge_options ll_opts;
+       struct ll_merge_options ll_opts = LL_MERGE_OPTIONS_INIT;
        int renormalize = 0;
 
        memset(threeway, 0, sizeof(threeway));
@@ -284,9 +287,9 @@ static int checkout_merged(int pos, const struct checkout *state,
        read_mmblob(&ours, &threeway[1]);
        read_mmblob(&theirs, &threeway[2]);
 
-       memset(&ll_opts, 0, sizeof(ll_opts));
        git_config_get_bool("merge.renormalize", &renormalize);
        ll_opts.renormalize = renormalize;
+       ll_opts.conflict_style = conflict_style;
        merge_status = ll_merge(&result_buf, path, &ancestor, "base",
                                &ours, "ours", &theirs, "theirs",
                                state->istate, &ll_opts);
@@ -417,7 +420,8 @@ static int checkout_worktree(const struct checkout_opts *opts,
                        else if (opts->merge)
                                errs |= checkout_merged(pos, &state,
                                                        &nr_unmerged,
-                                                       &ce_mem_pool);
+                                                       &ce_mem_pool,
+                                                       opts->conflict_style);
                        pos = skip_same_name(ce, pos) - 1;
                }
        }
@@ -706,7 +710,7 @@ static int reset_tree(struct tree *tree, const struct checkout_opts *o,
                               NULL);
        if (parse_tree(tree) < 0)
                return 128;
-       init_tree_desc(&tree_desc, tree->buffer, tree->size);
+       init_tree_desc(&tree_desc, &tree->object.oid, tree->buffer, tree->size);
        switch (unpack_trees(1, &tree_desc, &opts)) {
        case -2:
                *writeout_error = 1;
@@ -826,11 +830,13 @@ static int merge_working_tree(const struct checkout_opts *opts,
                        die(_("unable to parse commit %s"),
                                oid_to_hex(old_commit_oid));
 
-               init_tree_desc(&trees[0], tree->buffer, tree->size);
+               init_tree_desc(&trees[0], &tree->object.oid,
+                              tree->buffer, tree->size);
                if (parse_tree(new_tree) < 0)
                        exit(128);
                tree = new_tree;
-               init_tree_desc(&trees[1], tree->buffer, tree->size);
+               init_tree_desc(&trees[1], &tree->object.oid,
+                              tree->buffer, tree->size);
 
                ret = unpack_trees(2, trees, &topts);
                clear_unpack_trees_porcelain(&topts);
@@ -895,6 +901,7 @@ static int merge_working_tree(const struct checkout_opts *opts,
                        }
                        o.branch1 = new_branch_info->name;
                        o.branch2 = "local";
+                       o.conflict_style = opts->conflict_style;
                        ret = merge_trees(&o,
                                          new_tree,
                                          work,
@@ -1028,7 +1035,8 @@ static void update_refs_for_switch(const struct checkout_opts *opts,
        remove_branch_state(the_repository, !opts->quiet);
        strbuf_release(&msg);
        if (!opts->quiet &&
-           (new_branch_info->path || (!opts->force_detach && !strcmp(new_branch_info->name, "HEAD"))))
+           !opts->force_detach &&
+           (new_branch_info->path || !strcmp(new_branch_info->name, "HEAD")))
                report_tracking(new_branch_info);
 }
 
@@ -1632,6 +1640,24 @@ static int checkout_branch(struct checkout_opts *opts,
        return switch_branches(opts, new_branch_info);
 }
 
+static int parse_opt_conflict(const struct option *o, const char *arg, int unset)
+{
+       struct checkout_opts *opts = o->value;
+
+       if (unset) {
+               opts->conflict_style = -1;
+               return 0;
+       }
+       opts->conflict_style = parse_conflict_style_name(arg);
+       if (opts->conflict_style < 0)
+               return error(_("unknown conflict style '%s'"), arg);
+       /* --conflict overrides a previous --no-merge */
+       if (!opts->merge)
+               opts->merge = -1;
+
+       return 0;
+}
+
 static struct option *add_common_options(struct checkout_opts *opts,
                                         struct option *prevopts)
 {
@@ -1642,8 +1668,9 @@ static struct option *add_common_options(struct checkout_opts *opts,
                            PARSE_OPT_OPTARG, option_parse_recurse_submodules_worktree_updater),
                OPT_BOOL(0, "progress", &opts->show_progress, N_("force progress reporting")),
                OPT_BOOL('m', "merge", &opts->merge, N_("perform a 3-way merge with the new branch")),
-               OPT_STRING(0, "conflict", &opts->conflict_style, N_("style"),
-                          N_("conflict style (merge, diff3, or zdiff3)")),
+               OPT_CALLBACK(0, "conflict", opts, N_("style"),
+                            N_("conflict style (merge, diff3, or zdiff3)"),
+                            parse_opt_conflict),
                OPT_END()
        };
        struct option *newopts = parse_options_concat(prevopts, options);
@@ -1702,10 +1729,11 @@ static char cb_option = 'b';
 
 static int checkout_main(int argc, const char **argv, const char *prefix,
                         struct checkout_opts *opts, struct option *options,
-                        const char * const usagestr[],
-                        struct branch_info *new_branch_info)
+                        const char * const usagestr[])
 {
        int parseopt_flags = 0;
+       struct branch_info new_branch_info = { 0 };
+       int ret;
 
        opts->overwrite_ignore = 1;
        opts->prefix = prefix;
@@ -1734,15 +1762,10 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
                        opts->show_progress = isatty(2);
        }
 
-       if (opts->conflict_style) {
-               struct key_value_info kvi = KVI_INIT;
-               struct config_context ctx = {
-                       .kvi = &kvi,
-               };
-               opts->merge = 1; /* implied */
-               git_xmerge_config("merge.conflictstyle", opts->conflict_style,
-                                 &ctx, NULL);
-       }
+       /* --conflicts implies --merge */
+       if (opts->merge == -1)
+               opts->merge = opts->conflict_style >= 0;
+
        if (opts->force) {
                opts->discard_changes = 1;
                opts->ignore_unmerged_opt = "--force";
@@ -1821,7 +1844,7 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
                        opts->track == BRANCH_TRACK_UNSPECIFIED &&
                        !opts->new_branch;
                int n = parse_branchname_arg(argc, argv, dwim_ok,
-                                            new_branch_info, opts, &rev);
+                                            &new_branch_info, opts, &rev);
                argv += n;
                argc -= n;
        } else if (!opts->accept_ref && opts->from_treeish) {
@@ -1830,7 +1853,7 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
                if (repo_get_oid_mb(the_repository, opts->from_treeish, &rev))
                        die(_("could not resolve %s"), opts->from_treeish);
 
-               setup_new_branch_info_and_source_tree(new_branch_info,
+               setup_new_branch_info_and_source_tree(&new_branch_info,
                                                      opts, &rev,
                                                      opts->from_treeish);
 
@@ -1850,7 +1873,7 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
                 * Try to give more helpful suggestion.
                 * new_branch && argc > 1 will be caught later.
                 */
-               if (opts->new_branch && argc == 1 && !new_branch_info->commit)
+               if (opts->new_branch && argc == 1 && !new_branch_info.commit)
                        die(_("'%s' is not a commit and a branch '%s' cannot be created from it"),
                                argv[0], opts->new_branch);
 
@@ -1900,14 +1923,21 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
        }
 
        if (opts->patch_mode || opts->pathspec.nr)
-               return checkout_paths(opts, new_branch_info);
+               ret = checkout_paths(opts, &new_branch_info);
        else
-               return checkout_branch(opts, new_branch_info);
+               ret = checkout_branch(opts, &new_branch_info);
+
+       branch_info_release(&new_branch_info);
+       clear_pathspec(&opts->pathspec);
+       free(opts->pathspec_from_file);
+       free(options);
+
+       return ret;
 }
 
 int cmd_checkout(int argc, const char **argv, const char *prefix)
 {
-       struct checkout_opts opts;
+       struct checkout_opts opts = CHECKOUT_OPTS_INIT;
        struct option *options;
        struct option checkout_options[] = {
                OPT_STRING('b', NULL, &opts.new_branch, N_("branch"),
@@ -1920,10 +1950,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
                OPT_BOOL(0, "overlay", &opts.overlay_mode, N_("use overlay mode (default)")),
                OPT_END()
        };
-       int ret;
-       struct branch_info new_branch_info = { 0 };
 
-       memset(&opts, 0, sizeof(opts));
        opts.dwim_new_local_branch = 1;
        opts.switch_branch_doing_nothing_is_ok = 1;
        opts.only_merge_on_switching_branches = 0;
@@ -1951,18 +1978,13 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
        options = add_common_switch_branch_options(&opts, options);
        options = add_checkout_path_options(&opts, options);
 
-       ret = checkout_main(argc, argv, prefix, &opts,
-                           options, checkout_usage, &new_branch_info);
-       branch_info_release(&new_branch_info);
-       clear_pathspec(&opts.pathspec);
-       free(opts.pathspec_from_file);
-       FREE_AND_NULL(options);
-       return ret;
+       return checkout_main(argc, argv, prefix, &opts, options,
+                            checkout_usage);
 }
 
 int cmd_switch(int argc, const char **argv, const char *prefix)
 {
-       struct checkout_opts opts;
+       struct checkout_opts opts = CHECKOUT_OPTS_INIT;
        struct option *options = NULL;
        struct option switch_options[] = {
                OPT_STRING('c', "create", &opts.new_branch, N_("branch"),
@@ -1975,10 +1997,7 @@ int cmd_switch(int argc, const char **argv, const char *prefix)
                         N_("throw away local modifications")),
                OPT_END()
        };
-       int ret;
-       struct branch_info new_branch_info = { 0 };
 
-       memset(&opts, 0, sizeof(opts));
        opts.dwim_new_local_branch = 1;
        opts.accept_ref = 1;
        opts.accept_pathspec = 0;
@@ -1995,16 +2014,13 @@ int cmd_switch(int argc, const char **argv, const char *prefix)
 
        cb_option = 'c';
 
-       ret = checkout_main(argc, argv, prefix, &opts,
-                           options, switch_branch_usage, &new_branch_info);
-       branch_info_release(&new_branch_info);
-       FREE_AND_NULL(options);
-       return ret;
+       return checkout_main(argc, argv, prefix, &opts, options,
+                            switch_branch_usage);
 }
 
 int cmd_restore(int argc, const char **argv, const char *prefix)
 {
-       struct checkout_opts opts;
+       struct checkout_opts opts = CHECKOUT_OPTS_INIT;
        struct option *options;
        struct option restore_options[] = {
                OPT_STRING('s', "source", &opts.from_treeish, "<tree-ish>",
@@ -2018,10 +2034,7 @@ int cmd_restore(int argc, const char **argv, const char *prefix)
                OPT_BOOL(0, "overlay", &opts.overlay_mode, N_("use overlay mode")),
                OPT_END()
        };
-       int ret;
-       struct branch_info new_branch_info = { 0 };
 
-       memset(&opts, 0, sizeof(opts));
        opts.accept_ref = 0;
        opts.accept_pathspec = 1;
        opts.empty_pathspec_ok = 0;
@@ -2034,9 +2047,6 @@ int cmd_restore(int argc, const char **argv, const char *prefix)
        options = add_common_options(&opts, options);
        options = add_checkout_path_options(&opts, options);
 
-       ret = checkout_main(argc, argv, prefix, &opts,
-                           options, restore_usage, &new_branch_info);
-       branch_info_release(&new_branch_info);
-       FREE_AND_NULL(options);
-       return ret;
+       return checkout_main(argc, argv, prefix, &opts, options,
+                            restore_usage);
 }
index f3bc6edef8ce4bf15998bbf87fd2d5af4d7e3353..74ec14542e811d10f0dc88c9bfe935fc41af1491 100644 (file)
@@ -740,7 +740,7 @@ static int checkout(int submodule_progress, int filter_submodules)
                die(_("unable to parse commit %s"), oid_to_hex(&oid));
        if (parse_tree(tree) < 0)
                exit(128);
-       init_tree_desc(&t, tree->buffer, tree->size);
+       init_tree_desc(&t, &tree->object.oid, tree->buffer, tree->size);
        if (unpack_trees(1, &t, &opts) < 0)
                die(_("unable to checkout working tree"));
 
index a91197245f18ed7b73eee64d3d5d930ca164bb06..7ba7201cfb9160dc062e50f64b875e59d0405c6a 100644 (file)
@@ -333,7 +333,7 @@ static void create_base_index(const struct commit *current_head)
                die(_("failed to unpack HEAD tree object"));
        if (parse_tree(tree) < 0)
                exit(128);
-       init_tree_desc(&t, tree->buffer, tree->size);
+       init_tree_desc(&t, &tree->object.oid, tree->buffer, tree->size);
        if (unpack_trees(1, &t, &opts))
                exit(128); /* We've already reported the error, finish dying */
 }
@@ -685,9 +685,10 @@ static void adjust_comment_line_char(const struct strbuf *sb)
        char *candidate;
        const char *p;
 
-       comment_line_char = candidates[0];
-       if (!memchr(sb->buf, comment_line_char, sb->len))
+       if (!memchr(sb->buf, candidates[0], sb->len)) {
+               comment_line_str = xstrfmt("%c", candidates[0]);
                return;
+       }
 
        p = sb->buf;
        candidate = strchr(candidates, *p);
@@ -706,7 +707,7 @@ static void adjust_comment_line_char(const struct strbuf *sb)
        if (!*p)
                die(_("unable to select a comment character that is not used\n"
                      "in the current commit message"));
-       comment_line_char = *p;
+       comment_line_str = xstrfmt("%c", *p);
 }
 
 static void prepare_amend_commit(struct commit *commit, struct strbuf *sb,
@@ -889,7 +890,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
        s->hints = 0;
 
        if (clean_message_contents)
-               strbuf_stripspace(&sb, '\0');
+               strbuf_stripspace(&sb, NULL);
 
        if (signoff)
                append_signoff(&sb, ignored_log_message_bytes(sb.buf, sb.len), 0);
@@ -909,18 +910,18 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
                struct ident_split ci, ai;
                const char *hint_cleanup_all = allow_empty_message ?
                        _("Please enter the commit message for your changes."
-                         " Lines starting\nwith '%c' will be ignored.\n") :
+                         " Lines starting\nwith '%s' will be ignored.\n") :
                        _("Please enter the commit message for your changes."
-                         " Lines starting\nwith '%c' will be ignored, and an empty"
+                         " Lines starting\nwith '%s' will be ignored, and an empty"
                          " message aborts the commit.\n");
                const char *hint_cleanup_space = allow_empty_message ?
                        _("Please enter the commit message for your changes."
                          " Lines starting\n"
-                         "with '%c' will be kept; you may remove them"
+                         "with '%s' will be kept; you may remove them"
                          " yourself if you want to.\n") :
                        _("Please enter the commit message for your changes."
                          " Lines starting\n"
-                         "with '%c' will be kept; you may remove them"
+                         "with '%s' will be kept; you may remove them"
                          " yourself if you want to.\n"
                          "An empty message aborts the commit.\n");
                if (whence != FROM_COMMIT) {
@@ -943,12 +944,12 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
 
                fprintf(s->fp, "\n");
                if (cleanup_mode == COMMIT_MSG_CLEANUP_ALL)
-                       status_printf(s, GIT_COLOR_NORMAL, hint_cleanup_all, comment_line_char);
+                       status_printf(s, GIT_COLOR_NORMAL, hint_cleanup_all, comment_line_str);
                else if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS) {
                        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);
+                       status_printf(s, GIT_COLOR_NORMAL, hint_cleanup_space, comment_line_str);
 
                /*
                 * These should never fail because they come from our own
@@ -1157,22 +1158,45 @@ static void handle_ignored_arg(struct wt_status *s)
                die(_("Invalid ignored mode '%s'"), ignored_arg);
 }
 
-static void handle_untracked_files_arg(struct wt_status *s)
+static enum untracked_status_type parse_untracked_setting_name(const char *u)
 {
-       if (!untracked_files_arg)
-               ; /* default already initialized */
-       else if (!strcmp(untracked_files_arg, "no"))
-               s->show_untracked_files = SHOW_NO_UNTRACKED_FILES;
-       else if (!strcmp(untracked_files_arg, "normal"))
-               s->show_untracked_files = SHOW_NORMAL_UNTRACKED_FILES;
-       else if (!strcmp(untracked_files_arg, "all"))
-               s->show_untracked_files = SHOW_ALL_UNTRACKED_FILES;
        /*
         * Please update $__git_untracked_file_modes in
         * git-completion.bash when you add new options
         */
+       switch (git_parse_maybe_bool(u)) {
+       case 0:
+               u = "no";
+               break;
+       case 1:
+               u = "normal";
+               break;
+       default:
+               break;
+       }
+
+       if (!strcmp(u, "no"))
+               return SHOW_NO_UNTRACKED_FILES;
+       else if (!strcmp(u, "normal"))
+               return SHOW_NORMAL_UNTRACKED_FILES;
+       else if (!strcmp(u, "all"))
+               return SHOW_ALL_UNTRACKED_FILES;
        else
-               die(_("Invalid untracked files mode '%s'"), untracked_files_arg);
+               return SHOW_UNTRACKED_FILES_ERROR;
+}
+
+static void handle_untracked_files_arg(struct wt_status *s)
+{
+       enum untracked_status_type u;
+
+       if (!untracked_files_arg)
+               return; /* default already initialized */
+
+       u = parse_untracked_setting_name(untracked_files_arg);
+       if (u == SHOW_UNTRACKED_FILES_ERROR)
+               die(_("Invalid untracked files mode '%s'"),
+                   untracked_files_arg);
+       s->show_untracked_files = u;
 }
 
 static const char *read_commit_message(const char *name)
@@ -1455,16 +1479,12 @@ static int git_status_config(const char *k, const char *v,
                return 0;
        }
        if (!strcmp(k, "status.showuntrackedfiles")) {
-               if (!v)
-                       return config_error_nonbool(k);
-               else if (!strcmp(v, "no"))
-                       s->show_untracked_files = SHOW_NO_UNTRACKED_FILES;
-               else if (!strcmp(v, "normal"))
-                       s->show_untracked_files = SHOW_NORMAL_UNTRACKED_FILES;
-               else if (!strcmp(v, "all"))
-                       s->show_untracked_files = SHOW_ALL_UNTRACKED_FILES;
-               else
+               enum untracked_status_type u;
+
+               u = parse_untracked_setting_name(v);
+               if (u == SHOW_UNTRACKED_FILES_ERROR)
                        return error(_("Invalid untracked files mode '%s'"), v);
+               s->show_untracked_files = u;
                return 0;
        }
        if (!strcmp(k, "diff.renamelimit")) {
index b55bfae7d66df0cea54313f677e1a924a4a579b3..0015620ddeb2ab31a1bbb41a85eac70c7c548bfb 100644 (file)
@@ -44,6 +44,7 @@ static struct config_options config_options;
 static int show_origin;
 static int show_scope;
 static int fixed_value;
+static const char *comment;
 
 #define ACTION_GET (1<<0)
 #define ACTION_GET_ALL (1<<1)
@@ -173,6 +174,7 @@ static struct option builtin_config_options[] = {
        OPT_BOOL(0, "show-origin", &show_origin, N_("show origin of config (file, standard input, blob, command line)")),
        OPT_BOOL(0, "show-scope", &show_scope, N_("show scope of config (worktree, local, global, system, command)")),
        OPT_STRING(0, "default", &default_value, N_("value"), N_("with --get, use default value when missing entry")),
+       OPT_STRING(0, "comment", &comment, N_("value"), N_("human-readable comment string (# will be prepended as needed)")),
        OPT_END(),
 };
 
@@ -797,6 +799,12 @@ int cmd_config(int argc, const char **argv, const char *prefix)
                usage_builtin_config();
        }
 
+       if (comment &&
+           !(actions & (ACTION_ADD|ACTION_SET|ACTION_SET_ALL|ACTION_REPLACE_ALL))) {
+               error(_("--comment is only applicable to add/set/replace operations"));
+               usage_builtin_config();
+       }
+
        /* check usage of --fixed-value */
        if (fixed_value) {
                int allowed_usage = 0;
@@ -833,6 +841,8 @@ int cmd_config(int argc, const char **argv, const char *prefix)
                flags |= CONFIG_FLAGS_FIXED_VALUE;
        }
 
+       comment = git_config_prepare_comment_string(comment);
+
        if (actions & PAGING_ACTIONS)
                setup_auto_pager("config", 1);
 
@@ -880,7 +890,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)
                check_write();
                check_argc(argc, 2, 2);
                value = normalize_value(argv[0], argv[1], &default_kvi);
-               ret = git_config_set_in_file_gently(given_config_source.file, argv[0], value);
+               ret = git_config_set_in_file_gently(given_config_source.file, argv[0], comment, value);
                if (ret == CONFIG_NOTHING_SET)
                        error(_("cannot overwrite multiple values with a single value\n"
                        "       Use a regexp, --add or --replace-all to change %s."), argv[0]);
@@ -891,7 +901,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)
                value = normalize_value(argv[0], argv[1], &default_kvi);
                ret = git_config_set_multivar_in_file_gently(given_config_source.file,
                                                             argv[0], value, argv[2],
-                                                            flags);
+                                                            comment, flags);
        }
        else if (actions == ACTION_ADD) {
                check_write();
@@ -900,7 +910,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)
                ret = git_config_set_multivar_in_file_gently(given_config_source.file,
                                                             argv[0], value,
                                                             CONFIG_REGEX_NONE,
-                                                            flags);
+                                                            comment, flags);
        }
        else if (actions == ACTION_REPLACE_ALL) {
                check_write();
@@ -908,7 +918,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)
                value = normalize_value(argv[0], argv[1], &default_kvi);
                ret = git_config_set_multivar_in_file_gently(given_config_source.file,
                                                             argv[0], value, argv[2],
-                                                            flags | CONFIG_FLAGS_MULTI_REPLACE);
+                                                            comment, flags | CONFIG_FLAGS_MULTI_REPLACE);
        }
        else if (actions == ACTION_GET) {
                check_argc(argc, 1, 2);
@@ -936,17 +946,17 @@ int cmd_config(int argc, const char **argv, const char *prefix)
                if (argc == 2)
                        return git_config_set_multivar_in_file_gently(given_config_source.file,
                                                                      argv[0], NULL, argv[1],
-                                                                     flags);
+                                                                     NULL, flags);
                else
                        return git_config_set_in_file_gently(given_config_source.file,
-                                                            argv[0], NULL);
+                                                            argv[0], NULL, NULL);
        }
        else if (actions == ACTION_UNSET_ALL) {
                check_write();
                check_argc(argc, 1, 2);
                return git_config_set_multivar_in_file_gently(given_config_source.file,
                                                              argv[0], NULL, argv[1],
-                                                             flags | CONFIG_FLAGS_MULTI_REPLACE);
+                                                             NULL, flags | CONFIG_FLAGS_MULTI_REPLACE);
        }
        else if (actions == ACTION_RENAME_SECTION) {
                check_write();
index 71a195ca227315d799c03dd766d4723f7ffbec46..782bda007c29e7f23393d1d5543dc47497ee2d14 100644 (file)
@@ -1236,20 +1236,6 @@ static void *gfi_unpack_entry(
        return unpack_entry(the_repository, p, oe->idx.offset, &type, sizep);
 }
 
-static const char *get_mode(const char *str, uint16_t *modep)
-{
-       unsigned char c;
-       uint16_t mode = 0;
-
-       while ((c = *str++) != ' ') {
-               if (c < '0' || c > '7')
-                       return NULL;
-               mode = (mode << 3) + (c - '0');
-       }
-       *modep = mode;
-       return str;
-}
-
 static void load_tree(struct tree_entry *root)
 {
        struct object_id *oid = &root->versions[1].oid;
@@ -1287,7 +1273,7 @@ static void load_tree(struct tree_entry *root)
                t->entries[t->entry_count++] = e;
 
                e->tree = NULL;
-               c = get_mode(c, &e->versions[1].mode);
+               c = parse_mode(c, &e->versions[1].mode);
                if (!c)
                        die("Corrupt mode in %s", oid_to_hex(oid));
                e->versions[0].mode = e->versions[1].mode;
@@ -2280,7 +2266,7 @@ static void file_change_m(const char *p, struct branch *b)
        struct object_id oid;
        uint16_t mode, inline_data = 0;
 
-       p = get_mode(p, &mode);
+       p = parse_mode(p, &mode);
        if (!p)
                die("Corrupt mode: %s", command_buf.buf);
        switch (mode) {
index cb80ced6cb5c65d70859a3a6f57cff3d886084b3..d187cec1ea7550aa237cc2d6c86975c865457a21 100644 (file)
@@ -180,13 +180,51 @@ static void gc_config(void)
        git_config(git_default_config, NULL);
 }
 
-struct maintenance_run_opts;
+enum schedule_priority {
+       SCHEDULE_NONE = 0,
+       SCHEDULE_WEEKLY = 1,
+       SCHEDULE_DAILY = 2,
+       SCHEDULE_HOURLY = 3,
+};
+
+static enum schedule_priority parse_schedule(const char *value)
+{
+       if (!value)
+               return SCHEDULE_NONE;
+       if (!strcasecmp(value, "hourly"))
+               return SCHEDULE_HOURLY;
+       if (!strcasecmp(value, "daily"))
+               return SCHEDULE_DAILY;
+       if (!strcasecmp(value, "weekly"))
+               return SCHEDULE_WEEKLY;
+       return SCHEDULE_NONE;
+}
+
+struct maintenance_run_opts {
+       int auto_flag;
+       int quiet;
+       enum schedule_priority schedule;
+};
+
+static int pack_refs_condition(void)
+{
+       /*
+        * The auto-repacking logic for refs is handled by the ref backends and
+        * exposed via `git pack-refs --auto`. We thus always return truish
+        * here and let the backend decide for us.
+        */
+       return 1;
+}
+
 static int maintenance_task_pack_refs(MAYBE_UNUSED struct maintenance_run_opts *opts)
 {
        struct child_process cmd = CHILD_PROCESS_INIT;
 
        cmd.git_cmd = 1;
        strvec_pushl(&cmd.args, "pack-refs", "--all", "--prune", NULL);
+       if (opts->auto_flag)
+               strvec_push(&cmd.args, "--auto");
+
        return run_command(&cmd);
 }
 
@@ -547,7 +585,7 @@ done:
        return ret;
 }
 
-static void gc_before_repack(void)
+static void gc_before_repack(struct maintenance_run_opts *opts)
 {
        /*
         * We may be called twice, as both the pre- and
@@ -558,7 +596,7 @@ static void gc_before_repack(void)
        if (done++)
                return;
 
-       if (pack_refs && maintenance_task_pack_refs(NULL))
+       if (pack_refs && maintenance_task_pack_refs(opts))
                die(FAILED_RUN, "pack-refs");
 
        if (prune_reflogs) {
@@ -574,7 +612,6 @@ static void gc_before_repack(void)
 int cmd_gc(int argc, const char **argv, const char *prefix)
 {
        int aggressive = 0;
-       int auto_gc = 0;
        int quiet = 0;
        int force = 0;
        const char *name;
@@ -583,6 +620,7 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
        int keep_largest_pack = -1;
        timestamp_t dummy;
        struct child_process rerere_cmd = CHILD_PROCESS_INIT;
+       struct maintenance_run_opts opts = {0};
 
        struct option builtin_gc_options[] = {
                OPT__QUIET(&quiet, N_("suppress progress reporting")),
@@ -593,7 +631,7 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
                OPT_MAGNITUDE(0, "max-cruft-size", &max_cruft_size,
                              N_("with --cruft, limit the size of new cruft packs")),
                OPT_BOOL(0, "aggressive", &aggressive, N_("be more thorough (increased runtime)")),
-               OPT_BOOL_F(0, "auto", &auto_gc, N_("enable auto-gc mode"),
+               OPT_BOOL_F(0, "auto", &opts.auto_flag, N_("enable auto-gc mode"),
                           PARSE_OPT_NOCOMPLETE),
                OPT_BOOL_F(0, "force", &force,
                           N_("force running gc even if there may be another gc running"),
@@ -638,7 +676,7 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
        if (quiet)
                strvec_push(&repack, "-q");
 
-       if (auto_gc) {
+       if (opts.auto_flag) {
                /*
                 * Auto-gc should be least intrusive as possible.
                 */
@@ -663,7 +701,7 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
 
                        if (lock_repo_for_gc(force, &pid))
                                return 0;
-                       gc_before_repack(); /* dies on failure */
+                       gc_before_repack(&opts); /* dies on failure */
                        delete_tempfile(&pidfile);
 
                        /*
@@ -688,7 +726,7 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
 
        name = lock_repo_for_gc(force, &pid);
        if (name) {
-               if (auto_gc)
+               if (opts.auto_flag)
                        return 0; /* be quiet on --auto */
                die(_("gc is already running on machine '%s' pid %"PRIuMAX" (use --force if not)"),
                    name, (uintmax_t)pid);
@@ -703,7 +741,7 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
                atexit(process_log_file_at_exit);
        }
 
-       gc_before_repack();
+       gc_before_repack(&opts);
 
        if (!repository_format_precious_objects) {
                struct child_process repack_cmd = CHILD_PROCESS_INIT;
@@ -758,7 +796,7 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
                                             !quiet && !daemonized ? COMMIT_GRAPH_WRITE_PROGRESS : 0,
                                             NULL);
 
-       if (auto_gc && too_many_loose_objects())
+       if (opts.auto_flag && too_many_loose_objects())
                warning(_("There are too many unreachable loose objects; "
                        "run 'git prune' to remove them."));
 
@@ -773,26 +811,6 @@ static const char *const builtin_maintenance_run_usage[] = {
        NULL
 };
 
-enum schedule_priority {
-       SCHEDULE_NONE = 0,
-       SCHEDULE_WEEKLY = 1,
-       SCHEDULE_DAILY = 2,
-       SCHEDULE_HOURLY = 3,
-};
-
-static enum schedule_priority parse_schedule(const char *value)
-{
-       if (!value)
-               return SCHEDULE_NONE;
-       if (!strcasecmp(value, "hourly"))
-               return SCHEDULE_HOURLY;
-       if (!strcasecmp(value, "daily"))
-               return SCHEDULE_DAILY;
-       if (!strcasecmp(value, "weekly"))
-               return SCHEDULE_WEEKLY;
-       return SCHEDULE_NONE;
-}
-
 static int maintenance_opt_schedule(const struct option *opt, const char *arg,
                                    int unset)
 {
@@ -809,12 +827,6 @@ static int maintenance_opt_schedule(const struct option *opt, const char *arg,
        return 0;
 }
 
-struct maintenance_run_opts {
-       int auto_flag;
-       int quiet;
-       enum schedule_priority schedule;
-};
-
 /* Remember to update object flag allocation in object.h */
 #define SEEN           (1u<<0)
 
@@ -1296,7 +1308,7 @@ static struct maintenance_task tasks[] = {
        [TASK_PACK_REFS] = {
                "pack-refs",
                maintenance_task_pack_refs,
-               NULL,
+               pack_refs_condition,
        },
 };
 
@@ -1553,7 +1565,7 @@ static int maintenance_register(int argc, const char **argv, const char *prefix)
                        die(_("$HOME not set"));
                rc = git_config_set_multivar_in_file_gently(
                        config_file, "maintenance.repo", maintpath,
-                       CONFIG_REGEX_NONE, 0);
+                       CONFIG_REGEX_NONE, NULL, 0);
                free(global_config_file);
 
                if (rc)
@@ -1620,7 +1632,7 @@ static int maintenance_unregister(int argc, const char **argv, const char *prefi
                if (!config_file)
                        die(_("$HOME not set"));
                rc = git_config_set_multivar_in_file_gently(
-                       config_file, key, NULL, maintpath,
+                       config_file, key, NULL, maintpath, NULL,
                        CONFIG_FLAGS_MULTI_REPLACE | CONFIG_FLAGS_FIXED_VALUE);
                free(global_config_file);
 
index 982bcfc4b1dfb81d35c2af2bf6118f289ec376ef..5777ba82a988e8a26dcae2e77ab8fb3423b70a6f 100644 (file)
@@ -527,7 +527,7 @@ static int grep_submodule(struct grep_opt *opt,
                strbuf_addstr(&base, filename);
                strbuf_addch(&base, '/');
 
-               init_tree_desc(&tree, data, size);
+               init_tree_desc(&tree, oid, data, size);
                hit = grep_tree(&subopt, pathspec, &tree, &base, base.len,
                                object_type == OBJ_COMMIT);
                strbuf_release(&base);
@@ -573,7 +573,7 @@ static int grep_cache(struct grep_opt *opt,
                                                     &type, &size);
                        if (!data)
                                die(_("unable to read tree %s"), oid_to_hex(&ce->oid));
-                       init_tree_desc(&tree, data, size);
+                       init_tree_desc(&tree, &ce->oid, data, size);
 
                        hit |= grep_tree(opt, pathspec, &tree, &name, 0, 0);
                        strbuf_setlen(&name, name_base_len);
@@ -669,7 +669,7 @@ static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec,
                                    oid_to_hex(&entry.oid));
 
                        strbuf_addch(base, '/');
-                       init_tree_desc(&sub, data, size);
+                       init_tree_desc(&sub, &entry.oid, data, size);
                        hit |= grep_tree(opt, pathspec, &sub, base, tn_len,
                                         check_attr);
                        free(data);
@@ -713,7 +713,7 @@ static int grep_object(struct grep_opt *opt, const struct pathspec *pathspec,
                        strbuf_add(&base, name, len);
                        strbuf_addch(&base, ':');
                }
-               init_tree_desc(&tree, data, size);
+               init_tree_desc(&tree, &obj->oid, data, size);
                hit = grep_tree(opt, pathspec, &tree, &base, base.len,
                                obj->type == OBJ_COMMIT);
                strbuf_release(&base);
index 11f4ce9e4a274c666fe4127a904872653d35010d..8768bfea3c43fe43d8a41c2dd148912b1546d5b7 100644 (file)
@@ -15,7 +15,7 @@
 
 static const char * const git_interpret_trailers_usage[] = {
        N_("git interpret-trailers [--in-place] [--trim-empty]\n"
-          "                       [(--trailer (<key>|<keyAlias>)[(=|:)<value>])...]\n"
+          "                       [(--trailer (<key>|<key-alias>)[(=|:)<value>])...]\n"
           "                       [--parse] [<file>...]"),
        NULL
 };
index e5da0d10434dddc146ee00d9b035e76c28e3b214..c0a8bb95e9830b655c8de71521764c59b73c6ea0 100644 (file)
@@ -1297,7 +1297,7 @@ static void prepare_cover_text(struct pretty_print_context *pp,
                subject = subject_sb.buf;
 
 do_pp:
-       pp_title_line(pp, &subject, sb, encoding, need_8bit_cte);
+       pp_email_subject(pp, &subject, sb, encoding, need_8bit_cte);
        pp_remainder(pp, &body, sb, 0);
 
        strbuf_release(&description_sb);
@@ -1364,13 +1364,13 @@ static void make_cover_letter(struct rev_info *rev, int use_separate_file,
        pp.fmt = CMIT_FMT_EMAIL;
        pp.date_mode.type = DATE_RFC2822;
        pp.rev = rev;
-       pp.print_email_subject = 1;
        pp.encode_email_headers = rev->encode_email_headers;
        pp_user_info(&pp, NULL, &sb, committer, encoding);
        prepare_cover_text(&pp, description_file, branch_name, &sb,
                           encoding, need_8bit_cte);
        fprintf(rev->diffopt.file, "%s\n", sb.buf);
 
+       free(pp.after_subject);
        strbuf_release(&sb);
 
        shortlog_init(&log);
index 92f94e65bf065e900e59b9fcf2bc18f5af045975..6eeb5cba783d8dd55a9a0dce77818293bbd6699d 100644 (file)
@@ -266,7 +266,6 @@ static void show_ce_fmt(struct repository *repo, const struct cache_entry *ce,
        struct strbuf sb = STRBUF_INIT;
 
        while (strbuf_expand_step(&sb, &format)) {
-               const char *end;
                size_t len;
                struct stat st;
 
@@ -274,12 +273,6 @@ static void show_ce_fmt(struct repository *repo, const struct cache_entry *ce,
                        strbuf_addch(&sb, '%');
                else if ((len = strbuf_expand_literal(&sb, format)))
                        format += len;
-               else if (*format != '(')
-                       die(_("bad ls-files format: element '%s' "
-                             "does not start with '('"), format);
-               else if (!(end = strchr(format + 1, ')')))
-                       die(_("bad ls-files format: element '%s' "
-                             "does not end in ')'"), format);
                else if (skip_prefix(format, "(objectmode)", &format))
                        strbuf_addf(&sb, "%06o", ce->ce_mode);
                else if (skip_prefix(format, "(objectname)", &format))
@@ -308,8 +301,7 @@ static void show_ce_fmt(struct repository *repo, const struct cache_entry *ce,
                else if (skip_prefix(format, "(path)", &format))
                        write_name_to_buf(&sb, fullname);
                else
-                       die(_("bad ls-files format: %%%.*s"),
-                           (int)(end - format + 1), format);
+                       strbuf_expand_bad_format(format, "ls-files");
        }
        strbuf_addch(&sb, line_terminator);
        fwrite(sb.buf, sb.len, 1, stdout);
index e4a891337c3c619112af77bac014139b520e463f..7bf84b235ce69e63aaf0f11f6dfb82bce0a42eeb 100644 (file)
@@ -100,19 +100,12 @@ static int show_tree_fmt(const struct object_id *oid, struct strbuf *base,
                return 0;
 
        while (strbuf_expand_step(&sb, &format)) {
-               const char *end;
                size_t len;
 
                if (skip_prefix(format, "%", &format))
                        strbuf_addch(&sb, '%');
                else if ((len = strbuf_expand_literal(&sb, format)))
                        format += len;
-               else if (*format != '(')
-                       die(_("bad ls-tree format: element '%s' "
-                             "does not start with '('"), format);
-               else if (!(end = strchr(format + 1, ')')))
-                       die(_("bad ls-tree format: element '%s' "
-                             "does not end in ')'"), format);
                else if (skip_prefix(format, "(objectmode)", &format))
                        strbuf_addf(&sb, "%06o", mode);
                else if (skip_prefix(format, "(objecttype)", &format))
@@ -135,8 +128,7 @@ static int show_tree_fmt(const struct object_id *oid, struct strbuf *base,
                        strbuf_setlen(base, baselen);
                        strbuf_release(&sbuf);
                } else
-                       die(_("bad ls-tree format: %%%.*s"),
-                           (int)(end - format + 1), format);
+                       strbuf_expand_bad_format(format, "ls-tree");
        }
        strbuf_addch(&sb, options->null_termination ? '\0' : '\n');
        fwrite(sb.buf, sb.len, 1, stdout);
@@ -375,6 +367,7 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)
                OPT_END()
        };
        struct ls_tree_cmdmode_to_fmt *m2f = ls_tree_cmdmode_format;
+       struct object_context obj_context;
        int ret;
 
        git_config(git_default_config, NULL);
@@ -406,7 +399,9 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)
                        ls_tree_usage, ls_tree_options);
        if (argc < 1)
                usage_with_options(ls_tree_usage, ls_tree_options);
-       if (repo_get_oid(the_repository, argv[0], &oid))
+       if (get_oid_with_context(the_repository, argv[0],
+                                GET_OID_HASH_ANY, &oid,
+                                &obj_context))
                die("Not a valid object name %s", argv[0]);
 
        /*
index 05d0cad55438a90b72c8e001dfe71b05ad8203d3..8bdb439131499a57751ee87569028389dec82179 100644 (file)
@@ -563,7 +563,7 @@ int cmd_merge_tree(int argc, const char **argv, const char *prefix)
                           PARSE_OPT_NONEG),
                OPT_STRING(0, "merge-base",
                           &merge_base,
-                          N_("commit"),
+                          N_("tree-ish"),
                           N_("specify a merge-base for the merge")),
                OPT_STRVEC('X', "strategy-option", &xopts, N_("option=value"),
                        N_("option for selected merge strategy")),
index a0ba1f9815d9f45a1525bab8932810c9f5919aff..6f4fec87fcd0e64af4017c0a4adf32ec655cc224 100644 (file)
@@ -677,7 +677,8 @@ static int read_tree_trivial(struct object_id *common, struct object_id *head,
        cache_tree_free(&the_index.cache_tree);
        for (i = 0; i < nr_trees; i++) {
                parse_tree(trees[i]);
-               init_tree_desc(t+i, trees[i]->buffer, trees[i]->size);
+               init_tree_desc(t+i, &trees[i]->object.oid,
+                              trees[i]->buffer, trees[i]->size);
        }
        if (unpack_trees(nr_trees, t, &opts))
                return -1;
@@ -821,7 +822,7 @@ static const char scissors_editor_comment[] =
 N_("An empty message aborts the commit.\n");
 
 static const char no_scissors_editor_comment[] =
-N_("Lines starting with '%c' will be ignored, and an empty message aborts\n"
+N_("Lines starting with '%s' will be ignored, and an empty message aborts\n"
    "the commit.\n");
 
 static void write_merge_heads(struct commit_list *);
@@ -852,16 +853,16 @@ static void prepare_to_commit(struct commit_list *remoteheads)
                strbuf_addch(&msg, '\n');
                if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS) {
                        wt_status_append_cut_line(&msg);
-                       strbuf_commented_addf(&msg, comment_line_char, "\n");
+                       strbuf_commented_addf(&msg, comment_line_str, "\n");
                }
-               strbuf_commented_addf(&msg, comment_line_char,
+               strbuf_commented_addf(&msg, comment_line_str,
                                      _(merge_editor_comment));
                if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS)
-                       strbuf_commented_addf(&msg, comment_line_char,
+                       strbuf_commented_addf(&msg, comment_line_str,
                                              _(scissors_editor_comment));
                else
-                       strbuf_commented_addf(&msg, comment_line_char,
-                               _(no_scissors_editor_comment), comment_line_char);
+                       strbuf_commented_addf(&msg, comment_line_str,
+                               _(no_scissors_editor_comment), comment_line_str);
        }
        if (signoff)
                append_signoff(&msg, ignored_log_message_bytes(msg.buf, msg.len), 0);
index caf20fd5bdd4b7c1cb44191d58ecbb389643b748..cb011303e68d7725b8ed7bed0f2069e81859d692 100644 (file)
@@ -179,7 +179,7 @@ static void write_commented_object(int fd, const struct object_id *object)
 
        if (strbuf_read(&buf, show.out, 0) < 0)
                die_errno(_("could not read 'show' output"));
-       strbuf_add_commented_lines(&cbuf, buf.buf, buf.len, comment_line_char);
+       strbuf_add_commented_lines(&cbuf, buf.buf, buf.len, comment_line_str);
        write_or_die(fd, cbuf.buf, cbuf.len);
 
        strbuf_release(&cbuf);
@@ -207,10 +207,10 @@ static void prepare_note_data(const struct object_id *object, struct note_data *
                        copy_obj_to_fd(fd, old_note);
 
                strbuf_addch(&buf, '\n');
-               strbuf_add_commented_lines(&buf, "\n", strlen("\n"), comment_line_char);
+               strbuf_add_commented_lines(&buf, "\n", strlen("\n"), comment_line_str);
                strbuf_add_commented_lines(&buf, _(note_template), strlen(_(note_template)),
-                                          comment_line_char);
-               strbuf_add_commented_lines(&buf, "\n", strlen("\n"), comment_line_char);
+                                          comment_line_str);
+               strbuf_add_commented_lines(&buf, "\n", strlen("\n"), comment_line_str);
                write_or_die(fd, buf.buf, buf.len);
 
                write_commented_object(fd, object);
@@ -223,7 +223,7 @@ static void prepare_note_data(const struct object_id *object, struct note_data *
                        die(_("please supply the note contents using either -m or -F option"));
                }
                if (d->stripspace)
-                       strbuf_stripspace(&d->buf, comment_line_char);
+                       strbuf_stripspace(&d->buf, comment_line_str);
        }
 }
 
@@ -264,7 +264,7 @@ static void concat_messages(struct note_data *d)
                if ((d->stripspace == UNSPECIFIED &&
                     d->messages[i]->stripspace == STRIPSPACE) ||
                    d->stripspace == STRIPSPACE)
-                       strbuf_stripspace(&d->buf, 0);
+                       strbuf_stripspace(&d->buf, NULL);
                strbuf_reset(&msg);
        }
        strbuf_release(&msg);
index 329aeac80437502a49b48fe5383aa40e90cd16c3..baf0090fc8dac79201a682f1d2c56257d8587283 100644 (file)
@@ -1826,7 +1826,8 @@ static void add_pbase_object(struct tree_desc *tree,
                        tree = pbase_tree_get(&entry.oid);
                        if (!tree)
                                return;
-                       init_tree_desc(&sub, tree->tree_data, tree->tree_size);
+                       init_tree_desc(&sub, &tree->oid,
+                                      tree->tree_data, tree->tree_size);
 
                        add_pbase_object(&sub, down, downlen, fullname);
                        pbase_tree_put(tree);
@@ -1886,7 +1887,8 @@ static void add_preferred_base_object(const char *name)
                }
                else {
                        struct tree_desc tree;
-                       init_tree_desc(&tree, it->pcache.tree_data, it->pcache.tree_size);
+                       init_tree_desc(&tree, &it->pcache.oid,
+                                      it->pcache.tree_data, it->pcache.tree_size);
                        add_pbase_object(&tree, name, cmplen, name);
                }
        }
index bcf383cac9dd875354d3c91152f7b8d635f82ff4..db4082566634cbf5bdbb081d0807b0a82be7645e 100644 (file)
@@ -7,24 +7,28 @@
 #include "revision.h"
 
 static char const * const pack_refs_usage[] = {
-       N_("git pack-refs [--all] [--no-prune] [--include <pattern>] [--exclude <pattern>]"),
+       N_("git pack-refs [--all] [--no-prune] [--auto] [--include <pattern>] [--exclude <pattern>]"),
        NULL
 };
 
 int cmd_pack_refs(int argc, const char **argv, const char *prefix)
 {
-       unsigned int flags = PACK_REFS_PRUNE;
-       static struct ref_exclusions excludes = REF_EXCLUSIONS_INIT;
-       static struct string_list included_refs = STRING_LIST_INIT_NODUP;
-       struct pack_refs_opts pack_refs_opts = { .exclusions = &excludes,
-                                                .includes = &included_refs,
-                                                .flags = flags };
-       static struct string_list option_excluded_refs = STRING_LIST_INIT_NODUP;
+       struct ref_exclusions excludes = REF_EXCLUSIONS_INIT;
+       struct string_list included_refs = STRING_LIST_INIT_NODUP;
+       struct pack_refs_opts pack_refs_opts = {
+               .exclusions = &excludes,
+               .includes = &included_refs,
+               .flags = PACK_REFS_PRUNE,
+       };
+       struct string_list option_excluded_refs = STRING_LIST_INIT_NODUP;
        struct string_list_item *item;
+       int pack_all = 0;
+       int ret;
 
        struct option opts[] = {
-               OPT_BIT(0, "all",   &pack_refs_opts.flags, N_("pack everything"), PACK_REFS_ALL),
+               OPT_BOOL(0, "all",   &pack_all, N_("pack everything")),
                OPT_BIT(0, "prune", &pack_refs_opts.flags, N_("prune loose refs (default)"), PACK_REFS_PRUNE),
+               OPT_BIT(0, "auto", &pack_refs_opts.flags, N_("auto-pack refs as needed"), PACK_REFS_AUTO),
                OPT_STRING_LIST(0, "include", pack_refs_opts.includes, N_("pattern"),
                        N_("references to include")),
                OPT_STRING_LIST(0, "exclude", &option_excluded_refs, N_("pattern"),
@@ -38,11 +42,16 @@ int cmd_pack_refs(int argc, const char **argv, const char *prefix)
        for_each_string_list_item(item, &option_excluded_refs)
                add_ref_exclusion(pack_refs_opts.exclusions, item->string);
 
-       if (pack_refs_opts.flags & PACK_REFS_ALL)
+       if (pack_all)
                string_list_append(pack_refs_opts.includes, "*");
 
        if (!pack_refs_opts.includes->nr)
                string_list_append(pack_refs_opts.includes, "refs/tags/*");
 
-       return refs_pack_refs(get_main_ref_store(the_repository), &pack_refs_opts);
+       ret = refs_pack_refs(get_main_ref_store(the_repository), &pack_refs_opts);
+
+       clear_ref_exclusions(&excludes);
+       string_list_clear(&included_refs, 0);
+       string_list_clear(&option_excluded_refs, 0);
+       return ret;
 }
index 1ffd863cff6701af8013130d2e2801366f4e7228..6f89cec0fbb6b181b8feae32d7d3da6ff45b7ef3 100644 (file)
@@ -263,7 +263,7 @@ int cmd_read_tree(int argc, const char **argv, const char *cmd_prefix)
                struct tree *tree = trees[i];
                if (parse_tree(tree) < 0)
                        return 128;
-               init_tree_desc(t+i, tree->buffer, tree->size);
+               init_tree_desc(t+i, &tree->object.oid, tree->buffer, tree->size);
        }
        if (unpack_trees(nr_trees, t, &opts))
                return 128;
index e444ab102dfe5c99dae8b004960b27a8a600a16b..891f28468e8b04c4c4d6a235025b5652905d0636 100644 (file)
@@ -58,7 +58,7 @@ enum empty_type {
        EMPTY_UNSPECIFIED = -1,
        EMPTY_DROP,
        EMPTY_KEEP,
-       EMPTY_ASK
+       EMPTY_STOP
 };
 
 enum action {
@@ -204,7 +204,7 @@ static int edit_todo_file(unsigned flags)
        if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)
                return error_errno(_("could not read '%s'."), todo_file);
 
-       strbuf_stripspace(&todo_list.buf, comment_line_char);
+       strbuf_stripspace(&todo_list.buf, comment_line_str);
        res = edit_todo_list(the_repository, &todo_list, &new_todo, NULL, NULL, flags);
        if (!res && todo_list_write_to_file(the_repository, &new_todo, todo_file,
                                            NULL, NULL, -1, flags & ~(TODO_LIST_SHORTEN_IDS)))
@@ -610,7 +610,7 @@ static int run_am(struct rebase_options *opts)
                status = error_errno(_("could not open '%s' for writing"),
                                     rebased_patches);
                free(rebased_patches);
-               strvec_clear(&am.args);
+               child_process_clear(&am);
                return status;
        }
 
@@ -638,7 +638,7 @@ static int run_am(struct rebase_options *opts)
                struct reset_head_opts ropts = { 0 };
                unlink(rebased_patches);
                free(rebased_patches);
-               strvec_clear(&am.args);
+               child_process_clear(&am);
 
                ropts.oid = &opts->orig_head->object.oid;
                ropts.branch = opts->head_name;
@@ -659,7 +659,7 @@ static int run_am(struct rebase_options *opts)
                status = error_errno(_("could not open '%s' for reading"),
                                     rebased_patches);
                free(rebased_patches);
-               strvec_clear(&am.args);
+               child_process_clear(&am);
                return status;
        }
 
@@ -945,10 +945,14 @@ static enum empty_type parse_empty_value(const char *value)
                return EMPTY_DROP;
        else if (!strcasecmp(value, "keep"))
                return EMPTY_KEEP;
-       else if (!strcasecmp(value, "ask"))
-               return EMPTY_ASK;
+       else if (!strcasecmp(value, "stop"))
+               return EMPTY_STOP;
+       else if (!strcasecmp(value, "ask")) {
+               warning(_("--empty=ask is deprecated; use '--empty=stop' instead."));
+               return EMPTY_STOP;
+       }
 
-       die(_("unrecognized empty type '%s'; valid values are \"drop\", \"keep\", and \"ask\"."), value);
+       die(_("unrecognized empty type '%s'; valid values are \"drop\", \"keep\", and \"stop\"."), value);
 }
 
 static int parse_opt_keep_empty(const struct option *opt, const char *arg,
@@ -1127,7 +1131,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
                                 "instead of ignoring them"),
                              1, PARSE_OPT_HIDDEN),
                OPT_RERERE_AUTOUPDATE(&options.allow_rerere_autoupdate),
-               OPT_CALLBACK_F(0, "empty", &options, "(drop|keep|ask)",
+               OPT_CALLBACK_F(0, "empty", &options, "(drop|keep|stop)",
                               N_("how to handle commits that become empty"),
                               PARSE_OPT_NONEG, parse_opt_empty),
                OPT_CALLBACK_F('k', "keep-empty", &options, NULL,
@@ -1544,7 +1548,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 
        if (options.empty == EMPTY_UNSPECIFIED) {
                if (options.flags & REBASE_INTERACTIVE_EXPLICIT)
-                       options.empty = EMPTY_ASK;
+                       options.empty = EMPTY_STOP;
                else if (options.exec.nr > 0)
                        options.empty = EMPTY_KEEP;
                else
index ec455aa97277dd58d106c10623ad36aa923bcd10..77803727e0765de41c04c5ff415964d30e0e1524 100644 (file)
@@ -219,6 +219,7 @@ static void show_commit(struct commit *commit, void *data)
                ctx.fmt = revs->commit_format;
                ctx.output_encoding = get_log_output_encoding();
                ctx.color = revs->diffopt.use_color;
+               ctx.rev = revs;
                pretty_print_commit(&ctx, commit, &buf);
                if (buf.len) {
                        if (revs->commit_format != CMIT_FMT_ONELINE)
index 181c703d4c00bcce1f1715f1a8913fad0892828d..624182e507e5af9ac34a67f18d7a5dcf6fe2e58c 100644 (file)
@@ -25,6 +25,7 @@
 #include "submodule.h"
 #include "commit-reach.h"
 #include "shallow.h"
+#include "object-file-convert.h"
 
 #define DO_REVS                1
 #define DO_NOREV       2
@@ -676,6 +677,8 @@ static void print_path(const char *path, const char *prefix, enum format_type fo
 int cmd_rev_parse(int argc, const char **argv, const char *prefix)
 {
        int i, as_is = 0, verify = 0, quiet = 0, revs_count = 0, type = 0;
+       const struct git_hash_algo *output_algo = NULL;
+       const struct git_hash_algo *compat = NULL;
        int did_repo_setup = 0;
        int has_dashdash = 0;
        int output_prefix = 0;
@@ -747,6 +750,7 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
 
                        prepare_repo_settings(the_repository);
                        the_repository->settings.command_requires_full_index = 0;
+                       compat = the_repository->compat_hash_algo;
                }
 
                if (!strcmp(arg, "--")) {
@@ -834,6 +838,22 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
                                flags |= GET_OID_QUIETLY;
                                continue;
                        }
+                       if (opt_with_value(arg, "--output-object-format", &arg)) {
+                               if (!arg)
+                                       die(_("no object format specified"));
+                               if (!strcmp(arg, the_hash_algo->name) ||
+                                   !strcmp(arg, "storage")) {
+                                       flags |= GET_OID_HASH_ANY;
+                                       output_algo = the_hash_algo;
+                                       continue;
+                               }
+                               else if (compat && !strcmp(arg, compat->name)) {
+                                       flags |= GET_OID_HASH_ANY;
+                                       output_algo = compat;
+                                       continue;
+                               }
+                               else die(_("unsupported object format: %s"), arg);
+                       }
                        if (opt_with_value(arg, "--short", &arg)) {
                                filter &= ~(DO_FLAGS|DO_NOREV);
                                verify = 1;
@@ -883,7 +903,7 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
                                continue;
                        }
                        if (skip_prefix(arg, "--disambiguate=", &arg)) {
-                               repo_for_each_abbrev(the_repository, arg,
+                               repo_for_each_abbrev(the_repository, arg, the_hash_algo,
                                                     show_abbrev, NULL);
                                continue;
                        }
@@ -1091,6 +1111,9 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
                }
                if (!get_oid_with_context(the_repository, name,
                                          flags, &oid, &unused)) {
+                       if (output_algo)
+                               repo_oid_to_algop(the_repository, &oid,
+                                                 output_algo, &oid);
                        if (verify)
                                revs_count++;
                        else
index 89821bab95745a5fca294d00811804799ecd323d..53935d2c68a1653bbf17399ede26b5586ac46ba0 100644 (file)
@@ -43,6 +43,31 @@ static const char * const *revert_or_cherry_pick_usage(struct replay_opts *opts)
        return opts->action == REPLAY_REVERT ? revert_usage : cherry_pick_usage;
 }
 
+enum empty_action {
+       EMPTY_COMMIT_UNSPECIFIED = -1,
+       STOP_ON_EMPTY_COMMIT,      /* output errors and stop in the middle of a cherry-pick */
+       DROP_EMPTY_COMMIT,         /* skip with a notice message */
+       KEEP_EMPTY_COMMIT,         /* keep recording as empty commits */
+};
+
+static int parse_opt_empty(const struct option *opt, const char *arg, int unset)
+{
+       int *opt_value = opt->value;
+
+       BUG_ON_OPT_NEG(unset);
+
+       if (!strcmp(arg, "stop"))
+               *opt_value = STOP_ON_EMPTY_COMMIT;
+       else if (!strcmp(arg, "drop"))
+               *opt_value = DROP_EMPTY_COMMIT;
+       else if (!strcmp(arg, "keep"))
+               *opt_value = KEEP_EMPTY_COMMIT;
+       else
+               return error(_("invalid value for '%s': '%s'"), "--empty", arg);
+
+       return 0;
+}
+
 static int option_parse_m(const struct option *opt,
                          const char *arg, int unset)
 {
@@ -85,6 +110,7 @@ static int run_sequencer(int argc, const char **argv, const char *prefix,
        const char * const * usage_str = revert_or_cherry_pick_usage(opts);
        const char *me = action_name(opts);
        const char *cleanup_arg = NULL;
+       enum empty_action empty_opt = EMPTY_COMMIT_UNSPECIFIED;
        int cmd = 0;
        struct option base_options[] = {
                OPT_CMDMODE(0, "quit", &cmd, N_("end revert or cherry-pick sequence"), 'q'),
@@ -114,7 +140,10 @@ static int run_sequencer(int argc, const char **argv, const char *prefix,
                        OPT_BOOL(0, "ff", &opts->allow_ff, N_("allow fast-forward")),
                        OPT_BOOL(0, "allow-empty", &opts->allow_empty, N_("preserve initially empty commits")),
                        OPT_BOOL(0, "allow-empty-message", &opts->allow_empty_message, N_("allow commits with empty messages")),
-                       OPT_BOOL(0, "keep-redundant-commits", &opts->keep_redundant_commits, N_("keep redundant, empty commits")),
+                       OPT_BOOL(0, "keep-redundant-commits", &opts->keep_redundant_commits, N_("deprecated: use --empty=keep instead")),
+                       OPT_CALLBACK_F(0, "empty", &empty_opt, "(stop|drop|keep)",
+                                      N_("how to handle commits that become empty"),
+                                      PARSE_OPT_NONEG, parse_opt_empty),
                        OPT_END(),
                };
                options = parse_options_concat(options, cp_extra);
@@ -134,6 +163,11 @@ static int run_sequencer(int argc, const char **argv, const char *prefix,
        prepare_repo_settings(the_repository);
        the_repository->settings.command_requires_full_index = 0;
 
+       if (opts->action == REPLAY_PICK) {
+               opts->drop_redundant_commits = (empty_opt == DROP_EMPTY_COMMIT);
+               opts->keep_redundant_commits = opts->keep_redundant_commits || (empty_opt == KEEP_EMPTY_COMMIT);
+       }
+
        /* implies allow_empty */
        if (opts->keep_redundant_commits)
                opts->allow_empty = 1;
@@ -167,6 +201,8 @@ static int run_sequencer(int argc, const char **argv, const char *prefix,
                                "--ff", opts->allow_ff,
                                "--rerere-autoupdate", opts->allow_rerere_auto == RERERE_AUTOUPDATE,
                                "--no-rerere-autoupdate", opts->allow_rerere_auto == RERERE_NOAUTOUPDATE,
+                               "--keep-redundant-commits", opts->keep_redundant_commits,
+                               "--empty", empty_opt != EMPTY_COMMIT_UNSPECIFIED,
                                NULL);
        }
 
index 1307ed2b88a77dbb93d38528b8d39e2b87452d07..3c7cd2d6ef83f9e43ec6645d5adbbd02000fbdd9 100644 (file)
@@ -245,7 +245,6 @@ void shortlog_add_commit(struct shortlog *log, struct commit *commit)
 
        ctx.fmt = CMIT_FMT_USERFORMAT;
        ctx.abbrev = log->abbrev;
-       ctx.print_email_subject = 1;
        ctx.date_mode = log->date_mode;
        ctx.output_encoding = get_log_output_encoding();
 
index 7fb355bff0a628d1d7a1a08646175ac33d8fe9d7..062be1fbc07b0c2ecfcc6f41f5d995ae13c0aecb 100644 (file)
@@ -284,7 +284,7 @@ static int reset_tree(struct object_id *i_tree, int update, int reset)
        if (parse_tree(tree))
                return -1;
 
-       init_tree_desc(t, tree->buffer, tree->size);
+       init_tree_desc(t, &tree->object.oid, tree->buffer, tree->size);
 
        opts.head_idx = 1;
        opts.src_index = &the_index;
@@ -870,7 +870,8 @@ static void diff_include_untracked(const struct stash_info *info, struct diff_op
                tree[i] = parse_tree_indirect(oid[i]);
                if (parse_tree(tree[i]) < 0)
                        die(_("failed to parse tree"));
-               init_tree_desc(&tree_desc[i], tree[i]->buffer, tree[i]->size);
+               init_tree_desc(&tree_desc[i], &tree[i]->object.oid,
+                              tree[i]->buffer, tree[i]->size);
        }
 
        unpack_tree_opt.head_idx = -1;
index 7b700a9fb1c2a355af6ee6935edbeec49f3f4135..e5626e5126695b43aea014e3852ea325f055e62b 100644 (file)
@@ -13,7 +13,7 @@ static void comment_lines(struct strbuf *buf)
        size_t len;
 
        msg = strbuf_detach(buf, &len);
-       strbuf_add_commented_lines(buf, msg, len, comment_line_char);
+       strbuf_add_commented_lines(buf, msg, len, comment_line_str);
        free(msg);
 }
 
@@ -59,7 +59,7 @@ int cmd_stripspace(int argc, const char **argv, const char *prefix)
 
        if (mode == STRIP_DEFAULT || mode == STRIP_COMMENTS)
                strbuf_stripspace(&buf,
-                         mode == STRIP_COMMENTS ? comment_line_char : '\0');
+                         mode == STRIP_COMMENTS ? comment_line_str : NULL);
        else
                comment_lines(&buf);
 
index fda50f2af1e33277161f819f560b4d8164d284a0..e4e18adb575ca2b5d180aa4ec420066e7455f4ce 100644 (file)
@@ -1283,7 +1283,7 @@ static void sync_submodule(const char *path, const char *prefix,
        submodule_to_gitdir(&sb, path);
        strbuf_addstr(&sb, "/config");
 
-       if (git_config_set_in_file_gently(sb.buf, remote_key, sub_origin_url))
+       if (git_config_set_in_file_gently(sb.buf, remote_key, NULL, sub_origin_url))
                die(_("failed to update remote for submodule '%s'"),
                      path);
 
index 19a7e06bf414ccf811c93f0ac941a142a7fabef2..9a33cb50b4557317bfad8904e189a6909a4490c0 100644 (file)
@@ -27,6 +27,7 @@
 #include "ref-filter.h"
 #include "date.h"
 #include "write-or-die.h"
+#include "object-file-convert.h"
 
 static const char * const git_tag_usage[] = {
        N_("git tag [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>] [-e]\n"
@@ -151,18 +152,52 @@ static int verify_tag(const char *name, const char *ref UNUSED,
        return 0;
 }
 
-static int do_sign(struct strbuf *buffer)
+static int do_sign(struct strbuf *buffer, struct object_id **compat_oid,
+                  struct object_id *compat_oid_buf)
 {
-       return sign_buffer(buffer, buffer, get_signing_key()) ? -1 : 0;
+       const struct git_hash_algo *compat = the_repository->compat_hash_algo;
+       struct strbuf sig = STRBUF_INIT, compat_sig = STRBUF_INIT;
+       struct strbuf compat_buf = STRBUF_INIT;
+       const char *keyid = get_signing_key();
+       int ret = -1;
+
+       if (sign_buffer(buffer, &sig, keyid))
+               return -1;
+
+       if (compat) {
+               const struct git_hash_algo *algo = the_repository->hash_algo;
+
+               if (convert_object_file(&compat_buf, algo, compat,
+                                       buffer->buf, buffer->len, OBJ_TAG, 1))
+                       goto out;
+               if (sign_buffer(&compat_buf, &compat_sig, keyid))
+                       goto out;
+               add_header_signature(&compat_buf, &sig, algo);
+               strbuf_addbuf(&compat_buf, &compat_sig);
+               hash_object_file(compat, compat_buf.buf, compat_buf.len,
+                                OBJ_TAG, compat_oid_buf);
+               *compat_oid = compat_oid_buf;
+       }
+
+       if (compat_sig.len)
+               add_header_signature(buffer, &compat_sig, compat);
+
+       strbuf_addbuf(buffer, &sig);
+       ret = 0;
+out:
+       strbuf_release(&sig);
+       strbuf_release(&compat_sig);
+       strbuf_release(&compat_buf);
+       return ret;
 }
 
 static const char tag_template[] =
        N_("\nWrite a message for tag:\n  %s\n"
-       "Lines starting with '%c' will be ignored.\n");
+       "Lines starting with '%s' will be ignored.\n");
 
 static const char tag_template_nocleanup[] =
        N_("\nWrite a message for tag:\n  %s\n"
-       "Lines starting with '%c' will be kept; you may remove them"
+       "Lines starting with '%s' will be kept; you may remove them"
        " yourself if you want to.\n");
 
 static int git_tag_config(const char *var, const char *value,
@@ -226,9 +261,11 @@ static void write_tag_body(int fd, const struct object_id *oid)
 
 static int build_tag_object(struct strbuf *buf, int sign, struct object_id *result)
 {
-       if (sign && do_sign(buf) < 0)
+       struct object_id *compat_oid = NULL, compat_oid_buf;
+       if (sign && do_sign(buf, &compat_oid, &compat_oid_buf) < 0)
                return error(_("unable to sign the tag"));
-       if (write_object_file(buf->buf, buf->len, OBJ_TAG, result) < 0)
+       if (write_object_file_flags(buf->buf, buf->len, OBJ_TAG, result,
+                                   compat_oid, 0) < 0)
                return error(_("unable to write tag file"));
        return 0;
 }
@@ -291,11 +328,11 @@ static void create_tag(const struct object_id *object, const char *object_ref,
                        struct strbuf buf = STRBUF_INIT;
                        strbuf_addch(&buf, '\n');
                        if (opt->cleanup_mode == CLEANUP_ALL)
-                               strbuf_commented_addf(&buf, comment_line_char,
-                                     _(tag_template), tag, comment_line_char);
+                               strbuf_commented_addf(&buf, comment_line_str,
+                                     _(tag_template), tag, comment_line_str);
                        else
-                               strbuf_commented_addf(&buf, comment_line_char,
-                                     _(tag_template_nocleanup), tag, comment_line_char);
+                               strbuf_commented_addf(&buf, comment_line_str,
+                                     _(tag_template_nocleanup), tag, comment_line_str);
                        write_or_die(fd, buf.buf, buf.len);
                        strbuf_release(&buf);
                }
@@ -310,7 +347,7 @@ static void create_tag(const struct object_id *object, const char *object_ref,
 
        if (opt->cleanup_mode != CLEANUP_NONE)
                strbuf_stripspace(buf,
-                 opt->cleanup_mode == CLEANUP_ALL ? comment_line_char : '\0');
+                 opt->cleanup_mode == CLEANUP_ALL ? comment_line_str : NULL);
 
        if (!opt->message_given && !buf->len)
                die(_("no tag message?"));
index 61338a01ecfc0ee4f029215149651bb9ac03da1e..e46afbc46d950df6f98dccc1908602470976618c 100644 (file)
@@ -9,8 +9,8 @@
 #include "repository.h"
 
 static const char * const git_update_ref_usage[] = {
-       N_("git update-ref [<options>] -d <refname> [<old-val>]"),
-       N_("git update-ref [<options>]    <refname> <new-val> [<old-val>]"),
+       N_("git update-ref [<options>] -d <refname> [<old-oid>]"),
+       N_("git update-ref [<options>]    <refname> <new-oid> [<old-oid>]"),
        N_("git update-ref [<options>] --stdin [-z]"),
        NULL
 };
@@ -77,14 +77,14 @@ static char *parse_refname(const char **next)
 }
 
 /*
- * The value being parsed is <oldvalue> (as opposed to <newvalue>; the
+ * The value being parsed is <old-oid> (as opposed to <new-oid>; the
  * difference affects which error messages are generated):
  */
 #define PARSE_SHA1_OLD 0x01
 
 /*
  * For backwards compatibility, accept an empty string for update's
- * <newvalue> in binary mode to be equivalent to specifying zeros.
+ * <new-oid> in binary mode to be equivalent to specifying zeros.
  */
 #define PARSE_SHA1_ALLOW_EMPTY 0x02
 
@@ -140,7 +140,7 @@ static int parse_next_oid(const char **next, const char *end,
                                goto invalid;
                } else if (flags & PARSE_SHA1_ALLOW_EMPTY) {
                        /* With -z, treat an empty value as all zeros: */
-                       warning("%s %s: missing <newvalue>, treating as zero",
+                       warning("%s %s: missing <new-oid>, treating as zero",
                                command, refname);
                        oidclr(oid);
                } else {
@@ -158,14 +158,14 @@ static int parse_next_oid(const char **next, const char *end,
 
  invalid:
        die(flags & PARSE_SHA1_OLD ?
-           "%s %s: invalid <oldvalue>: %s" :
-           "%s %s: invalid <newvalue>: %s",
+           "%s %s: invalid <old-oid>: %s" :
+           "%s %s: invalid <new-oid>: %s",
            command, refname, arg.buf);
 
  eof:
        die(flags & PARSE_SHA1_OLD ?
-           "%s %s: unexpected end of input when reading <oldvalue>" :
-           "%s %s: unexpected end of input when reading <newvalue>",
+           "%s %s: unexpected end of input when reading <old-oid>" :
+           "%s %s: unexpected end of input when reading <new-oid>",
            command, refname);
 }
 
@@ -194,7 +194,7 @@ static void parse_cmd_update(struct ref_transaction *transaction,
 
        if (parse_next_oid(&next, end, &new_oid, "update", refname,
                           PARSE_SHA1_ALLOW_EMPTY))
-               die("update %s: missing <newvalue>", refname);
+               die("update %s: missing <new-oid>", refname);
 
        have_old = !parse_next_oid(&next, end, &old_oid, "update", refname,
                                   PARSE_SHA1_OLD);
@@ -225,10 +225,10 @@ static void parse_cmd_create(struct ref_transaction *transaction,
                die("create: missing <ref>");
 
        if (parse_next_oid(&next, end, &new_oid, "create", refname, 0))
-               die("create %s: missing <newvalue>", refname);
+               die("create %s: missing <new-oid>", refname);
 
        if (is_null_oid(&new_oid))
-               die("create %s: zero <newvalue>", refname);
+               die("create %s: zero <new-oid>", refname);
 
        if (*next != line_termination)
                die("create %s: extra input: %s", refname, next);
@@ -260,7 +260,7 @@ static void parse_cmd_delete(struct ref_transaction *transaction,
                have_old = 0;
        } else {
                if (is_null_oid(&old_oid))
-                       die("delete %s: zero <oldvalue>", refname);
+                       die("delete %s: zero <old-oid>", refname);
                have_old = 1;
        }
 
index 9c76b62b02da037db84ef633100cc6d9136eb0cf..7c6c72536bdb22830d729edf5fa4ac127b4263fb 100644 (file)
@@ -365,12 +365,12 @@ static void copy_filtered_worktree_config(const char *worktree_git_dir)
                if (!git_configset_get_bool(&cs, "core.bare", &bare) &&
                        bare &&
                        git_config_set_multivar_in_file_gently(
-                               to_file, "core.bare", NULL, "true", 0))
+                               to_file, "core.bare", NULL, "true", NULL, 0))
                        error(_("failed to unset '%s' in '%s'"),
                                "core.bare", to_file);
                if (!git_configset_get(&cs, "core.worktree") &&
                        git_config_set_in_file_gently(to_file,
-                                                       "core.worktree", NULL))
+                                                       "core.worktree", NULL, NULL))
                        error(_("failed to unset '%s' in '%s'"),
                                "core.worktree", to_file);
 
@@ -657,7 +657,7 @@ static int can_use_local_refs(const struct add_opts *opts)
                        strbuf_add_real_path(&path, get_worktree_git_dir(NULL));
                        strbuf_addstr(&path, "/HEAD");
                        strbuf_read_file(&contents, path.buf, 64);
-                       strbuf_stripspace(&contents, 0);
+                       strbuf_stripspace(&contents, NULL);
                        strbuf_strip_suffix(&contents, "\n");
 
                        warning(_("HEAD points to an invalid (or orphaned) reference.\n"
index 80ca8324773d2dc352bf4d47cfc4d0036316180e..387c0a3e5b7b8bfc895f380e8c3976fcf9b777d8 100644 (file)
@@ -447,7 +447,7 @@ static int update_one(struct cache_tree *it,
                hash_object_file(the_hash_algo, buffer.buf, buffer.len,
                                 OBJ_TREE, &it->oid);
        } else if (write_object_file_flags(buffer.buf, buffer.len, OBJ_TREE,
-                                          &it->oid, flags & WRITE_TREE_SILENT
+                                          &it->oid, NULL, flags & WRITE_TREE_SILENT
                                           ? HASH_SILENT : 0)) {
                strbuf_release(&buffer);
                return -1;
@@ -769,7 +769,7 @@ static void prime_cache_tree_rec(struct repository *r,
 
        oidcpy(&it->oid, &tree->object.oid);
 
-       init_tree_desc(&desc, tree->buffer, tree->size);
+       init_tree_desc(&desc, &tree->object.oid, tree->buffer, tree->size);
        cnt = 0;
        while (tree_entry(&desc, &entry)) {
                if (!S_ISDIR(entry.mode))
index 8ba486f6598880f0aeb69c1a36f410d08de8892d..a51076d18df1785c82ec71f8dd2a071056b9d6f4 100755 (executable)
@@ -12,7 +12,7 @@ group "Build fuzzers" make \
        LIB_FUZZING_ENGINE="-fsanitize=fuzzer,address" \
        fuzz-all
 
-for fuzzer in commit-graph date pack-headers pack-idx ; do
+for fuzzer in commit-graph config date pack-headers pack-idx ; do
        begin_group "fuzz-$fuzzer"
        ./oss-fuzz/fuzz-$fuzzer -verbosity=0 -runs=1 || exit 1
        end_group "fuzz-$fuzzer"
index 467be9f7f99408edbe1a34e2f21c491dd50e2813..1a479a997c4e470318be6098e03dac11d1fda606 100644 (file)
--- a/commit.c
+++ b/commit.c
@@ -27,6 +27,7 @@
 #include "tree.h"
 #include "hook.h"
 #include "parse.h"
+#include "object-file-convert.h"
 
 static struct commit_extra_header *read_commit_extra_header_lines(const char *buf, size_t len, const char **);
 
@@ -1113,12 +1114,11 @@ static const char *gpg_sig_headers[] = {
        "gpgsig-sha256",
 };
 
-int sign_with_header(struct strbuf *buf, const char *keyid)
+int add_header_signature(struct strbuf *buf, struct strbuf *sig, const struct git_hash_algo *algo)
 {
-       struct strbuf sig = STRBUF_INIT;
        int inspos, copypos;
        const char *eoh;
-       const char *gpg_sig_header = gpg_sig_headers[hash_algo_by_ptr(the_hash_algo)];
+       const char *gpg_sig_header = gpg_sig_headers[hash_algo_by_ptr(algo)];
        int gpg_sig_header_len = strlen(gpg_sig_header);
 
        /* find the end of the header */
@@ -1128,15 +1128,8 @@ int sign_with_header(struct strbuf *buf, const char *keyid)
        else
                inspos = eoh - buf->buf + 1;
 
-       if (!keyid || !*keyid)
-               keyid = get_signing_key();
-       if (sign_buffer(buf, &sig, keyid)) {
-               strbuf_release(&sig);
-               return -1;
-       }
-
-       for (copypos = 0; sig.buf[copypos]; ) {
-               const char *bol = sig.buf + copypos;
+       for (copypos = 0; sig->buf[copypos]; ) {
+               const char *bol = sig->buf + copypos;
                const char *eol = strchrnul(bol, '\n');
                int len = (eol - bol) + !!*eol;
 
@@ -1149,11 +1142,17 @@ int sign_with_header(struct strbuf *buf, const char *keyid)
                inspos += len;
                copypos += len;
        }
-       strbuf_release(&sig);
        return 0;
 }
 
-
+static int sign_commit_to_strbuf(struct strbuf *sig, struct strbuf *buf, const char *keyid)
+{
+       if (!keyid || !*keyid)
+               keyid = get_signing_key();
+       if (sign_buffer(buf, sig, keyid))
+               return -1;
+       return 0;
+}
 
 int parse_signed_commit(const struct commit *commit,
                        struct strbuf *payload, struct strbuf *signature,
@@ -1369,6 +1368,39 @@ void append_merge_tag_headers(struct commit_list *parents,
        }
 }
 
+static int convert_commit_extra_headers(struct commit_extra_header *orig,
+                                       struct commit_extra_header **result)
+{
+       const struct git_hash_algo *compat = the_repository->compat_hash_algo;
+       const struct git_hash_algo *algo = the_repository->hash_algo;
+       struct commit_extra_header *extra = NULL, **tail = &extra;
+       struct strbuf out = STRBUF_INIT;
+       while (orig) {
+               struct commit_extra_header *new;
+               CALLOC_ARRAY(new, 1);
+               if (!strcmp(orig->key, "mergetag")) {
+                       if (convert_object_file(&out, algo, compat,
+                                               orig->value, orig->len,
+                                               OBJ_TAG, 1)) {
+                               free(new);
+                               free_commit_extra_headers(extra);
+                               return -1;
+                       }
+                       new->key = xstrdup("mergetag");
+                       new->value = strbuf_detach(&out, &new->len);
+               } else {
+                       new->key = xstrdup(orig->key);
+                       new->len = orig->len;
+                       new->value = xmemdupz(orig->value, orig->len);
+               }
+               *tail = new;
+               tail = &new->next;
+               orig = orig->next;
+       }
+       *result = extra;
+       return 0;
+}
+
 static void add_extra_header(struct strbuf *buffer,
                             struct commit_extra_header *extra)
 {
@@ -1612,70 +1644,169 @@ N_("Warning: commit message did not conform to UTF-8.\n"
    "You may want to amend it after fixing the message, or set the config\n"
    "variable i18n.commitEncoding to the encoding your project uses.\n");
 
-int commit_tree_extended(const char *msg, size_t msg_len,
-                        const struct object_id *tree,
-                        struct commit_list *parents, struct object_id *ret,
-                        const char *author, const char *committer,
-                        const char *sign_commit,
-                        struct commit_extra_header *extra)
+static void write_commit_tree(struct strbuf *buffer, const char *msg, size_t msg_len,
+                             const struct object_id *tree,
+                             const struct object_id *parents, size_t parents_len,
+                             const char *author, const char *committer,
+                             struct commit_extra_header *extra)
 {
-       int result;
        int encoding_is_utf8;
-       struct strbuf buffer;
-
-       assert_oid_type(tree, OBJ_TREE);
-
-       if (memchr(msg, '\0', msg_len))
-               return error("a NUL byte in commit log message not allowed.");
+       size_t i;
 
        /* Not having i18n.commitencoding is the same as having utf-8 */
        encoding_is_utf8 = is_encoding_utf8(git_commit_encoding);
 
-       strbuf_init(&buffer, 8192); /* should avoid reallocs for the headers */
-       strbuf_addf(&buffer, "tree %s\n", oid_to_hex(tree));
+       strbuf_grow(buffer, 8192); /* should avoid reallocs for the headers */
+       strbuf_addf(buffer, "tree %s\n", oid_to_hex(tree));
 
        /*
         * NOTE! This ordering means that the same exact tree merged with a
         * different order of parents will be a _different_ changeset even
         * if everything else stays the same.
         */
-       while (parents) {
-               struct commit *parent = pop_commit(&parents);
-               strbuf_addf(&buffer, "parent %s\n",
-                           oid_to_hex(&parent->object.oid));
-       }
+       for (i = 0; i < parents_len; i++)
+               strbuf_addf(buffer, "parent %s\n", oid_to_hex(&parents[i]));
 
        /* Person/date information */
        if (!author)
                author = git_author_info(IDENT_STRICT);
-       strbuf_addf(&buffer, "author %s\n", author);
+       strbuf_addf(buffer, "author %s\n", author);
        if (!committer)
                committer = git_committer_info(IDENT_STRICT);
-       strbuf_addf(&buffer, "committer %s\n", committer);
+       strbuf_addf(buffer, "committer %s\n", committer);
        if (!encoding_is_utf8)
-               strbuf_addf(&buffer, "encoding %s\n", git_commit_encoding);
+               strbuf_addf(buffer, "encoding %s\n", git_commit_encoding);
 
        while (extra) {
-               add_extra_header(&buffer, extra);
+               add_extra_header(buffer, extra);
                extra = extra->next;
        }
-       strbuf_addch(&buffer, '\n');
+       strbuf_addch(buffer, '\n');
 
        /* And add the comment */
-       strbuf_add(&buffer, msg, msg_len);
+       strbuf_add(buffer, msg, msg_len);
+}
 
-       /* And check the encoding */
-       if (encoding_is_utf8 && !verify_utf8(&buffer))
-               fprintf(stderr, _(commit_utf8_warn));
+int commit_tree_extended(const char *msg, size_t msg_len,
+                        const struct object_id *tree,
+                        struct commit_list *parents, struct object_id *ret,
+                        const char *author, const char *committer,
+                        const char *sign_commit,
+                        struct commit_extra_header *extra)
+{
+       struct repository *r = the_repository;
+       int result = 0;
+       int encoding_is_utf8;
+       struct strbuf buffer = STRBUF_INIT, compat_buffer = STRBUF_INIT;
+       struct strbuf sig = STRBUF_INIT, compat_sig = STRBUF_INIT;
+       struct object_id *parent_buf = NULL, *compat_oid = NULL;
+       struct object_id compat_oid_buf;
+       size_t i, nparents;
+
+       /* Not having i18n.commitencoding is the same as having utf-8 */
+       encoding_is_utf8 = is_encoding_utf8(git_commit_encoding);
+
+       assert_oid_type(tree, OBJ_TREE);
 
-       if (sign_commit && sign_with_header(&buffer, sign_commit)) {
+       if (memchr(msg, '\0', msg_len))
+               return error("a NUL byte in commit log message not allowed.");
+
+       nparents = commit_list_count(parents);
+       CALLOC_ARRAY(parent_buf, nparents);
+       i = 0;
+       while (parents) {
+               struct commit *parent = pop_commit(&parents);
+               oidcpy(&parent_buf[i++], &parent->object.oid);
+       }
+
+       write_commit_tree(&buffer, msg, msg_len, tree, parent_buf, nparents, author, committer, extra);
+       if (sign_commit && sign_commit_to_strbuf(&sig, &buffer, sign_commit)) {
                result = -1;
                goto out;
        }
+       if (r->compat_hash_algo) {
+               struct commit_extra_header *compat_extra = NULL;
+               struct object_id mapped_tree;
+               struct object_id *mapped_parents;
+
+               CALLOC_ARRAY(mapped_parents, nparents);
 
-       result = write_object_file(buffer.buf, buffer.len, OBJ_COMMIT, ret);
+               if (repo_oid_to_algop(r, tree, r->compat_hash_algo, &mapped_tree)) {
+                       result = -1;
+                       free(mapped_parents);
+                       goto out;
+               }
+               for (i = 0; i < nparents; i++)
+                       if (repo_oid_to_algop(r, &parent_buf[i], r->compat_hash_algo, &mapped_parents[i])) {
+                               result = -1;
+                               free(mapped_parents);
+                               goto out;
+                       }
+               if (convert_commit_extra_headers(extra, &compat_extra)) {
+                       result = -1;
+                       free(mapped_parents);
+                       goto out;
+               }
+               write_commit_tree(&compat_buffer, msg, msg_len, &mapped_tree,
+                                 mapped_parents, nparents, author, committer, compat_extra);
+               free_commit_extra_headers(compat_extra);
+               free(mapped_parents);
+
+               if (sign_commit && sign_commit_to_strbuf(&compat_sig, &compat_buffer, sign_commit)) {
+                       result = -1;
+                       goto out;
+               }
+       }
+
+       if (sign_commit) {
+               struct sig_pairs {
+                       struct strbuf *sig;
+                       const struct git_hash_algo *algo;
+               } bufs [2] = {
+                       { &compat_sig, r->compat_hash_algo },
+                       { &sig, r->hash_algo },
+               };
+               int i;
+
+               /*
+                * We write algorithms in the order they were implemented in
+                * Git to produce a stable hash when multiple algorithms are
+                * used.
+                */
+               if (r->compat_hash_algo && hash_algo_by_ptr(bufs[0].algo) > hash_algo_by_ptr(bufs[1].algo))
+                       SWAP(bufs[0], bufs[1]);
+
+               /*
+                * We traverse each algorithm in order, and apply the signature
+                * to each buffer.
+                */
+               for (i = 0; i < ARRAY_SIZE(bufs); i++) {
+                       if (!bufs[i].algo)
+                               continue;
+                       add_header_signature(&buffer, bufs[i].sig, bufs[i].algo);
+                       if (r->compat_hash_algo)
+                               add_header_signature(&compat_buffer, bufs[i].sig, bufs[i].algo);
+               }
+       }
+
+       /* And check the encoding. */
+       if (encoding_is_utf8 && (!verify_utf8(&buffer) || !verify_utf8(&compat_buffer)))
+               fprintf(stderr, _(commit_utf8_warn));
+
+       if (r->compat_hash_algo) {
+               hash_object_file(r->compat_hash_algo, compat_buffer.buf, compat_buffer.len,
+                       OBJ_COMMIT, &compat_oid_buf);
+               compat_oid = &compat_oid_buf;
+       }
+
+       result = write_object_file_flags(buffer.buf, buffer.len, OBJ_COMMIT,
+                                        ret, compat_oid, 0);
 out:
+       free(parent_buf);
        strbuf_release(&buffer);
+       strbuf_release(&compat_buffer);
+       strbuf_release(&sig);
+       strbuf_release(&compat_sig);
        return result;
 }
 
@@ -1797,7 +1928,8 @@ size_t ignored_log_message_bytes(const char *buf, size_t len)
                else
                        next_line++;
 
-               if (buf[bol] == comment_line_char || buf[bol] == '\n') {
+               if (starts_with_mem(buf + bol, cutoff - bol, comment_line_str) ||
+                   buf[bol] == '\n') {
                        /* is this the first of the run of comments? */
                        if (!boc)
                                boc = bol;
index 1cc872f225f438be7dc03a9b4dc8f207da0deb5b..62fe0d77a70738048858e4087533b045fcc40613 100644 (file)
--- a/commit.h
+++ b/commit.h
@@ -370,5 +370,6 @@ int parse_buffer_signed_by_header(const char *buffer,
                                  struct strbuf *payload,
                                  struct strbuf *signature,
                                  const struct git_hash_algo *algop);
+int add_header_signature(struct strbuf *buf, struct strbuf *sig, const struct git_hash_algo *algo);
 
 #endif /* COMMIT_H */
index 3cfeb3d8bd99f4ca15d0f3a06cd4b1fe932f7f47..eebce8c7e09393fa8756230e483965674b9afbdf 100644 (file)
--- a/config.c
+++ b/config.c
@@ -817,7 +817,8 @@ static int get_next_char(struct config_source *cs)
 
 static char *parse_value(struct config_source *cs)
 {
-       int quote = 0, comment = 0, space = 0;
+       int quote = 0, comment = 0;
+       size_t trim_len = 0;
 
        strbuf_reset(&cs->value);
        for (;;) {
@@ -827,13 +828,17 @@ static char *parse_value(struct config_source *cs)
                                cs->linenr--;
                                return NULL;
                        }
+                       if (trim_len)
+                               strbuf_setlen(&cs->value, trim_len);
                        return cs->value.buf;
                }
                if (comment)
                        continue;
                if (isspace(c) && !quote) {
+                       if (!trim_len)
+                               trim_len = cs->value.len;
                        if (cs->value.len)
-                               space++;
+                               strbuf_addch(&cs->value, c);
                        continue;
                }
                if (!quote) {
@@ -842,8 +847,8 @@ static char *parse_value(struct config_source *cs)
                                continue;
                        }
                }
-               for (; space; space--)
-                       strbuf_addch(&cs->value, ' ');
+               if (trim_len)
+                       trim_len = 0;
                if (c == '\\') {
                        c = get_next_char(cs);
                        switch (c) {
@@ -869,7 +874,7 @@ static char *parse_value(struct config_source *cs)
                        continue;
                }
                if (c == '"') {
-                       quote = 1-quote;
+                       quote = 1 - quote;
                        continue;
                }
                strbuf_addch(&cs->value, c);
@@ -1560,16 +1565,19 @@ static int git_default_core_config(const char *var, const char *value,
        if (!strcmp(var, "core.editor"))
                return git_config_string(&editor_program, var, value);
 
-       if (!strcmp(var, "core.commentchar")) {
+       if (!strcmp(var, "core.commentchar") ||
+           !strcmp(var, "core.commentstring")) {
                if (!value)
                        return config_error_nonbool(var);
                else if (!strcasecmp(value, "auto"))
                        auto_comment_line_char = 1;
-               else if (value[0] && !value[1]) {
-                       comment_line_char = value[0];
+               else if (value[0]) {
+                       if (strchr(value, '\n'))
+                               return error(_("%s cannot contain newline"), var);
+                       comment_line_str = xstrdup(value);
                        auto_comment_line_char = 0;
                } else
-                       return error(_("core.commentChar should only be one ASCII character"));
+                       return error(_("%s must have at least one character"), var);
                return 0;
        }
 
@@ -3001,6 +3009,7 @@ static ssize_t write_section(int fd, const char *key,
 }
 
 static ssize_t write_pair(int fd, const char *key, const char *value,
+                         const char *comment,
                          const struct config_store_data *store)
 {
        int i;
@@ -3041,7 +3050,11 @@ static ssize_t write_pair(int fd, const char *key, const char *value,
                        strbuf_addch(&sb, value[i]);
                        break;
                }
-       strbuf_addf(&sb, "%s\n", quote);
+
+       if (comment)
+               strbuf_addf(&sb, "%s%s\n", quote, comment);
+       else
+               strbuf_addf(&sb, "%s\n", quote);
 
        ret = write_in_full(fd, sb.buf, sb.len);
        strbuf_release(&sb);
@@ -3130,9 +3143,9 @@ static void maybe_remove_section(struct config_store_data *store,
 }
 
 int git_config_set_in_file_gently(const char *config_filename,
-                                 const char *key, const char *value)
+                                 const char *key, const char *comment, const char *value)
 {
-       return git_config_set_multivar_in_file_gently(config_filename, key, value, NULL, 0);
+       return git_config_set_multivar_in_file_gently(config_filename, key, value, NULL, comment, 0);
 }
 
 void git_config_set_in_file(const char *config_filename,
@@ -3153,7 +3166,7 @@ int repo_config_set_worktree_gently(struct repository *r,
        if (r->repository_format_worktree_config) {
                char *file = repo_git_path(r, "config.worktree");
                int ret = git_config_set_multivar_in_file_gently(
-                                       file, key, value, NULL, 0);
+                                       file, key, value, NULL, NULL, 0);
                free(file);
                return ret;
        }
@@ -3167,6 +3180,62 @@ void git_config_set(const char *key, const char *value)
        trace2_cmd_set_config(key, value);
 }
 
+/*
+ * The ownership rule is that the caller will own the string
+ * if it receives a piece of memory different from what it passed
+ * as the parameter.
+ */
+const char *git_config_prepare_comment_string(const char *comment)
+{
+       size_t leading_blanks;
+
+       if (!comment)
+               return NULL;
+
+       if (strchr(comment, '\n'))
+               die(_("no multi-line comment allowed: '%s'"), comment);
+
+       /*
+        * If it begins with one or more leading whitespace characters
+        * followed by '#", the comment string is used as-is.
+        *
+        * If it begins with '#', a SP is inserted between the comment
+        * and the value the comment is about.
+        *
+        * Otherwise, the value is followed by a SP followed by '#'
+        * followed by SP and then the comment string comes.
+        */
+
+       leading_blanks = strspn(comment, " \t");
+       if (leading_blanks && comment[leading_blanks] == '#')
+               ; /* use it as-is */
+       else if (comment[0] == '#')
+               comment = xstrfmt(" %s", comment);
+       else
+               comment = xstrfmt(" # %s", comment);
+
+       return comment;
+}
+
+static void validate_comment_string(const char *comment)
+{
+       size_t leading_blanks;
+
+       if (!comment)
+               return;
+       /*
+        * The front-end must have massaged the comment string
+        * properly before calling us.
+        */
+       if (strchr(comment, '\n'))
+               BUG("multi-line comments are not permitted: '%s'", comment);
+
+       leading_blanks = strspn(comment, " \t");
+       if (!leading_blanks || comment[leading_blanks] != '#')
+               BUG("comment must begin with one or more SP followed by '#': '%s'",
+                   comment);
+}
+
 /*
  * If value==NULL, unset in (remove from) config,
  * if value_pattern!=NULL, disregard key/value pairs where value does not match.
@@ -3195,6 +3264,7 @@ void git_config_set(const char *key, const char *value)
 int git_config_set_multivar_in_file_gently(const char *config_filename,
                                           const char *key, const char *value,
                                           const char *value_pattern,
+                                          const char *comment,
                                           unsigned flags)
 {
        int fd = -1, in_fd = -1;
@@ -3205,6 +3275,8 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,
        size_t contents_sz;
        struct config_store_data store = CONFIG_STORE_INIT;
 
+       validate_comment_string(comment);
+
        /* parse-key returns negative; flip the sign to feed exit(3) */
        ret = 0 - git_config_parse_key(key, &store.key, &store.baselen);
        if (ret)
@@ -3245,7 +3317,7 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,
                free(store.key);
                store.key = xstrdup(key);
                if (write_section(fd, key, &store) < 0 ||
-                   write_pair(fd, key, value, &store) < 0)
+                   write_pair(fd, key, value, comment, &store) < 0)
                        goto write_err_out;
        } else {
                struct stat st;
@@ -3399,7 +3471,7 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,
                                if (write_section(fd, key, &store) < 0)
                                        goto write_err_out;
                        }
-                       if (write_pair(fd, key, value, &store) < 0)
+                       if (write_pair(fd, key, value, comment, &store) < 0)
                                goto write_err_out;
                }
 
@@ -3444,7 +3516,7 @@ void git_config_set_multivar_in_file(const char *config_filename,
                                     const char *value_pattern, unsigned flags)
 {
        if (!git_config_set_multivar_in_file_gently(config_filename, key, value,
-                                                   value_pattern, flags))
+                                                   value_pattern, NULL, flags))
                return;
        if (value)
                die(_("could not set '%s' to '%s'"), key, value);
@@ -3467,7 +3539,7 @@ int repo_config_set_multivar_gently(struct repository *r, const char *key,
        int res = git_config_set_multivar_in_file_gently(file,
                                                         key, value,
                                                         value_pattern,
-                                                        flags);
+                                                        NULL, flags);
        free(file);
        return res;
 }
index 5dba984f770e4e96d322874351a70d0f7d0ee8ba..f4966e374948a59cbe95af6f999f0e70d44aa1e9 100644 (file)
--- a/config.h
+++ b/config.h
@@ -290,7 +290,7 @@ int git_config_pathname(const char **, const char *, const char *);
 
 int git_config_expiry_date(timestamp_t *, const char *, const char *);
 int git_config_color(char *, const char *, const char *);
-int git_config_set_in_file_gently(const char *, const char *, const char *);
+int git_config_set_in_file_gently(const char *, const char *, const char *, const char *);
 
 /**
  * write config values to a specific config file, takes a key/value pair as
@@ -336,7 +336,9 @@ int git_config_parse_key(const char *, char **, size_t *);
 int git_config_set_multivar_gently(const char *, const char *, const char *, unsigned);
 void git_config_set_multivar(const char *, const char *, const char *, unsigned);
 int repo_config_set_multivar_gently(struct repository *, const char *, const char *, const char *, unsigned);
-int git_config_set_multivar_in_file_gently(const char *, const char *, const char *, const char *, unsigned);
+int git_config_set_multivar_in_file_gently(const char *, const char *, const char *, const char *, const char *, unsigned);
+
+const char *git_config_prepare_comment_string(const char *);
 
 /**
  * takes four parameters:
index 71f179cba3fbda3bc93de461649cf75bb08c6653..5330e769a72a86df6c4d2e47d796bc4e0cb90773 100644 (file)
@@ -141,7 +141,7 @@ __git_ps1_show_upstream ()
 
        # parse configuration values
        local option
-       for option in ${GIT_PS1_SHOWUPSTREAM}; do
+       for option in ${GIT_PS1_SHOWUPSTREAM-}; do
                case "$option" in
                git|svn) upstream_type="$option" ;;
                verbose) verbose=1 ;;
@@ -528,7 +528,7 @@ __git_ps1 ()
        fi
 
        local conflict="" # state indicator for unresolved conflicts
-       if [[ "${GIT_PS1_SHOWCONFLICTSTATE}" == "yes" ]] &&
+       if [[ "${GIT_PS1_SHOWCONFLICTSTATE-}" == "yes" ]] &&
           [[ $(git ls-files --unmerged 2>/dev/null) ]]; then
                conflict="|CONFLICT"
        fi
index 4ec419f90048babf0b18a0f8ca0fce82fc2b3f30..6ce9603568ef7d4b41d7eed9247e8512a2f45a74 100755 (executable)
@@ -74,8 +74,7 @@ do
        sort >uncovered_lines.txt
 
        comm -12 uncovered_lines.txt new_lines.txt |
-       sed -e 's/$/\)/' |
-       sed -e 's/^/ /' >uncovered_new_lines.txt
+       sed -e 's/$/\)/' -e 's/^/ /' >uncovered_new_lines.txt
 
        grep -q '[^[:space:]]' <uncovered_new_lines.txt &&
        echo $file >>coverage-data.txt &&
@@ -91,11 +90,7 @@ cat coverage-data.txt
 
 echo "Commits introducing uncovered code:"
 
-commit_list=$(cat coverage-data.txt |
-       grep -E '^[0-9a-f]{7,} ' |
-       awk '{print $1;}' |
-       sort |
-       uniq)
+commit_list=$(awk '/^[0-9a-f]{7,}/ { print $1 }' coverage-data.txt | sort -u)
 
 (
        for commit in $commit_list
diff --git a/contrib/hg-to-git/hg-to-git.py b/contrib/hg-to-git/hg-to-git.py
deleted file mode 100755 (executable)
index 7eb1b24..0000000
+++ /dev/null
@@ -1,254 +0,0 @@
-#!/usr/bin/env python
-
-""" hg-to-git.py - A Mercurial to GIT converter
-
-    Copyright (C)2007 Stelian Pop <stelian@popies.net>
-
-    This program is free software; you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation; either version 2, or (at your option)
-    any later version.
-
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with this program; if not, see <http://www.gnu.org/licenses/>.
-"""
-
-import os, os.path, sys
-import tempfile, pickle, getopt
-import re
-
-if sys.hexversion < 0x02030000:
-   # The behavior of the pickle module changed significantly in 2.3
-   sys.stderr.write("hg-to-git.py: requires Python 2.3 or later.\n")
-   sys.exit(1)
-
-# Maps hg version -> git version
-hgvers = {}
-# List of children for each hg revision
-hgchildren = {}
-# List of parents for each hg revision
-hgparents = {}
-# Current branch for each hg revision
-hgbranch = {}
-# Number of new changesets converted from hg
-hgnewcsets = 0
-
-#------------------------------------------------------------------------------
-
-def usage():
-
-        print("""\
-%s: [OPTIONS] <hgprj>
-
-options:
-    -s, --gitstate=FILE: name of the state to be saved/read
-                         for incrementals
-    -n, --nrepack=INT:   number of changesets that will trigger
-                         a repack (default=0, -1 to deactivate)
-    -v, --verbose:       be verbose
-
-required:
-    hgprj:  name of the HG project to import (directory)
-""" % sys.argv[0])
-
-#------------------------------------------------------------------------------
-
-def getgitenv(user, date):
-    env = ''
-    elems = re.compile('(.*?)\s+<(.*)>').match(user)
-    if elems:
-        env += 'export GIT_AUTHOR_NAME="%s" ;' % elems.group(1)
-        env += 'export GIT_COMMITTER_NAME="%s" ;' % elems.group(1)
-        env += 'export GIT_AUTHOR_EMAIL="%s" ;' % elems.group(2)
-        env += 'export GIT_COMMITTER_EMAIL="%s" ;' % elems.group(2)
-    else:
-        env += 'export GIT_AUTHOR_NAME="%s" ;' % user
-        env += 'export GIT_COMMITTER_NAME="%s" ;' % user
-        env += 'export GIT_AUTHOR_EMAIL= ;'
-        env += 'export GIT_COMMITTER_EMAIL= ;'
-
-    env += 'export GIT_AUTHOR_DATE="%s" ;' % date
-    env += 'export GIT_COMMITTER_DATE="%s" ;' % date
-    return env
-
-#------------------------------------------------------------------------------
-
-state = ''
-opt_nrepack = 0
-verbose = False
-
-try:
-    opts, args = getopt.getopt(sys.argv[1:], 's:t:n:v', ['gitstate=', 'tempdir=', 'nrepack=', 'verbose'])
-    for o, a in opts:
-        if o in ('-s', '--gitstate'):
-            state = a
-            state = os.path.abspath(state)
-        if o in ('-n', '--nrepack'):
-            opt_nrepack = int(a)
-        if o in ('-v', '--verbose'):
-            verbose = True
-    if len(args) != 1:
-        raise Exception('params')
-except:
-    usage()
-    sys.exit(1)
-
-hgprj = args[0]
-os.chdir(hgprj)
-
-if state:
-    if os.path.exists(state):
-        if verbose:
-            print('State does exist, reading')
-        f = open(state, 'r')
-        hgvers = pickle.load(f)
-    else:
-        print('State does not exist, first run')
-
-sock = os.popen('hg tip --template "{rev}"')
-tip = sock.read()
-if sock.close():
-    sys.exit(1)
-if verbose:
-    print('tip is', tip)
-
-# Calculate the branches
-if verbose:
-    print('analysing the branches...')
-hgchildren["0"] = ()
-hgparents["0"] = (None, None)
-hgbranch["0"] = "master"
-for cset in range(1, int(tip) + 1):
-    hgchildren[str(cset)] = ()
-    prnts = os.popen('hg log -r %d --template "{parents}"' % cset).read().strip().split(' ')
-    prnts = map(lambda x: x[:x.find(':')], prnts)
-    if prnts[0] != '':
-        parent = prnts[0].strip()
-    else:
-        parent = str(cset - 1)
-    hgchildren[parent] += ( str(cset), )
-    if len(prnts) > 1:
-        mparent = prnts[1].strip()
-        hgchildren[mparent] += ( str(cset), )
-    else:
-        mparent = None
-
-    hgparents[str(cset)] = (parent, mparent)
-
-    if mparent:
-        # For merge changesets, take either one, preferably the 'master' branch
-        if hgbranch[mparent] == 'master':
-            hgbranch[str(cset)] = 'master'
-        else:
-            hgbranch[str(cset)] = hgbranch[parent]
-    else:
-        # Normal changesets
-        # For first children, take the parent branch, for the others create a new branch
-        if hgchildren[parent][0] == str(cset):
-            hgbranch[str(cset)] = hgbranch[parent]
-        else:
-            hgbranch[str(cset)] = "branch-" + str(cset)
-
-if "0" not in hgvers:
-    print('creating repository')
-    os.system('git init')
-
-# loop through every hg changeset
-for cset in range(int(tip) + 1):
-
-    # incremental, already seen
-    if str(cset) in hgvers:
-        continue
-    hgnewcsets += 1
-
-    # get info
-    log_data = os.popen('hg log -r %d --template "{tags}\n{date|date}\n{author}\n"' % cset).readlines()
-    tag = log_data[0].strip()
-    date = log_data[1].strip()
-    user = log_data[2].strip()
-    parent = hgparents[str(cset)][0]
-    mparent = hgparents[str(cset)][1]
-
-    #get comment
-    (fdcomment, filecomment) = tempfile.mkstemp()
-    csetcomment = os.popen('hg log -r %d --template "{desc}"' % cset).read().strip()
-    os.write(fdcomment, csetcomment)
-    os.close(fdcomment)
-
-    print('-----------------------------------------')
-    print('cset:', cset)
-    print('branch:', hgbranch[str(cset)])
-    print('user:', user)
-    print('date:', date)
-    print('comment:', csetcomment)
-    if parent:
-        print('parent:', parent)
-    if mparent:
-        print('mparent:', mparent)
-    if tag:
-        print('tag:', tag)
-    print('-----------------------------------------')
-
-    # checkout the parent if necessary
-    if cset != 0:
-        if hgbranch[str(cset)] == "branch-" + str(cset):
-            print('creating new branch', hgbranch[str(cset)])
-            os.system('git checkout -b %s %s' % (hgbranch[str(cset)], hgvers[parent]))
-        else:
-            print('checking out branch', hgbranch[str(cset)])
-            os.system('git checkout %s' % hgbranch[str(cset)])
-
-    # merge
-    if mparent:
-        if hgbranch[parent] == hgbranch[str(cset)]:
-            otherbranch = hgbranch[mparent]
-        else:
-            otherbranch = hgbranch[parent]
-        print('merging', otherbranch, 'into', hgbranch[str(cset)])
-        os.system(getgitenv(user, date) + 'git merge --no-commit -s ours "" %s %s' % (hgbranch[str(cset)], otherbranch))
-
-    # remove everything except .git and .hg directories
-    os.system('find . \( -path "./.hg" -o -path "./.git" \) -prune -o ! -name "." -print | xargs rm -rf')
-
-    # repopulate with checkouted files
-    os.system('hg update -C %d' % cset)
-
-    # add new files
-    os.system('git ls-files -x .hg --others | git update-index --add --stdin')
-    # delete removed files
-    os.system('git ls-files -x .hg --deleted | git update-index --remove --stdin')
-
-    # commit
-    os.system(getgitenv(user, date) + 'git commit --allow-empty --allow-empty-message -a -F %s' % filecomment)
-    os.unlink(filecomment)
-
-    # tag
-    if tag and tag != 'tip':
-        os.system(getgitenv(user, date) + 'git tag %s' % tag)
-
-    # delete branch if not used anymore...
-    if mparent and len(hgchildren[str(cset)]):
-        print("Deleting unused branch:", otherbranch)
-        os.system('git branch -d %s' % otherbranch)
-
-    # retrieve and record the version
-    vvv = os.popen('git show --quiet --pretty=format:%H').read()
-    print('record', cset, '->', vvv)
-    hgvers[str(cset)] = vvv
-
-if hgnewcsets >= opt_nrepack and opt_nrepack != -1:
-    os.system('git repack -a -d')
-
-# write the state for incrementals
-if state:
-    if verbose:
-        print('Writing state')
-    f = open(state, 'w')
-    pickle.dump(hgvers, f)
-
-# vim: et ts=8 sw=4 sts=4
diff --git a/contrib/hg-to-git/hg-to-git.txt b/contrib/hg-to-git/hg-to-git.txt
deleted file mode 100644 (file)
index 91f8fe6..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-hg-to-git.py is able to convert a Mercurial repository into a git one,
-and preserves the branches in the process (unlike tailor)
-
-hg-to-git.py can probably be greatly improved (it's a rather crude
-combination of shell and python) but it does already work quite well for
-me. Features:
-       - supports incremental conversion
-         (for keeping a git repo in sync with a hg one)
-        - supports hg branches
-        - converts hg tags
-
-Note that the git repository will be created 'in place' (at the same
-location as the source hg repo). You will have to manually remove the
-'.hg' directory after the conversion.
-
-Also note that the incremental conversion uses 'simple' hg changesets
-identifiers (ordinals, as opposed to SHA-1 ids), and since these ids
-are not stable across different repositories the hg-to-git.py state file
-is forever tied to one hg repository.
-
-Stelian Pop <stelian@popies.net>
index ca4df5be83245fe9a37a0b46737b61071f3b78b0..c3bd2a58b941f0bda8673a707fb0da71e4cbca88 100755 (executable)
@@ -63,7 +63,7 @@ test_create_pre2_32_repo () {
        git -C "$1" log -1 --format=%B HEAD^2 >msg &&
        test_commit -C "$1-sub" --annotate sub2 &&
        git clone --no-local "$1" "$1-clone" &&
-       new_commit=$(cat msg | sed -e "s/$commit/$tag/" | git -C "$1-clone" commit-tree HEAD^2^{tree}) &&
+       new_commit=$(sed -e "s/$commit/$tag/" msg | git -C "$1-clone" commit-tree HEAD^2^{tree}) &&
        git -C "$1-clone" replace HEAD^2 $new_commit
 }
 
index ee2318d45a1c7898b39999b1f5fc81be8ec49f00..f7e079425fe45d70ead6ea191d2ecc37ee7a339b 100644 (file)
@@ -284,7 +284,7 @@ void resolve_tree_islands(struct repository *r,
                if (!tree || parse_tree(tree) < 0)
                        die(_("bad tree object %s"), oid_to_hex(&ent->idx.oid));
 
-               init_tree_desc(&desc, tree->buffer, tree->size);
+               init_tree_desc(&desc, &tree->object.oid, tree->buffer, tree->size);
                while (tree_entry(&desc, &entry)) {
                        struct object *obj;
 
index 5e8717c774eff4bb2364344c440a4efb4686e4f4..1cd790a4d2bef6a3df8a6eb080622ccaa6749fa3 100644 (file)
@@ -562,7 +562,7 @@ static int diff_cache(struct rev_info *revs,
        opts.pathspec = &revs->diffopt.pathspec;
        opts.pathspec->recursive = 1;
 
-       init_tree_desc(&t, tree->buffer, tree->size);
+       init_tree_desc(&t, &tree->object.oid, tree->buffer, tree->size);
        return unpack_trees(1, &t, &opts);
 }
 
diff --git a/diff.c b/diff.c
index e50def45383eba4af74300802a161bdeeb6c4e8f..108c1875775df223c4ca4f9dec27194033a7cfaa 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -62,6 +62,8 @@ static const char *diff_order_file_cfg;
 int diff_auto_refresh_index = 1;
 static int diff_mnemonic_prefix;
 static int diff_no_prefix;
+static const char *diff_src_prefix = "a/";
+static const char *diff_dst_prefix = "b/";
 static int diff_relative;
 static int diff_stat_name_width;
 static int diff_stat_graph_width;
@@ -408,6 +410,12 @@ int git_diff_ui_config(const char *var, const char *value,
                diff_no_prefix = git_config_bool(var, value);
                return 0;
        }
+       if (!strcmp(var, "diff.srcprefix")) {
+               return git_config_string(&diff_src_prefix, var, value);
+       }
+       if (!strcmp(var, "diff.dstprefix")) {
+               return git_config_string(&diff_dst_prefix, var, value);
+       }
        if (!strcmp(var, "diff.relative")) {
                diff_relative = git_config_bool(var, value);
                return 0;
@@ -3425,8 +3433,8 @@ void diff_set_noprefix(struct diff_options *options)
 
 void diff_set_default_prefix(struct diff_options *options)
 {
-       options->a_prefix = "a/";
-       options->b_prefix = "b/";
+       options->a_prefix = diff_src_prefix;
+       options->b_prefix = diff_dst_prefix;
 }
 
 struct userdiff_driver *get_textconv(struct repository *r,
@@ -5362,6 +5370,8 @@ static int diff_opt_default_prefix(const struct option *opt,
 
        BUG_ON_OPT_NEG(unset);
        BUG_ON_OPT_ARG(optarg);
+       diff_src_prefix = "a/";
+       diff_dst_prefix = "b/";
        diff_set_default_prefix(options);
        return 0;
 }
index 60706ea3987f2c4d11e5e2e2d21aa5cd5e9196e1..a73ba9c12caed24dd724144890fffd0f116b1c38 100644 (file)
@@ -110,7 +110,7 @@ int protect_ntfs = PROTECT_NTFS_DEFAULT;
  * The character that begins a commented line in user-editable file
  * that is subject to stripspace.
  */
-char comment_line_char = '#';
+const char *comment_line_str = "#";
 int auto_comment_line_char;
 
 /* Parallel index stat data preload? */
index 5cec19cecc1a5a2c057f807f3b96dd2b29f0b355..05fd94d7be87492f9f500e5848ffe607cf3b6fda 100644 (file)
@@ -8,7 +8,7 @@ struct strvec;
  * The character that begins a commented line in user-editable file
  * that is subject to stripspace.
  */
-extern char comment_line_char;
+extern const char *comment_line_str;
 extern int auto_comment_line_char;
 
 /*
index 66e47449a092d67b66a172a0e325593d5e494efa..ae201e21db333f89080762a9003cfa4a6f78933f 100644 (file)
@@ -321,7 +321,7 @@ static void credit_people(struct strbuf *out,
             skip_prefix(me, them->items->string, &me) &&
             starts_with(me, " <")))
                return;
-       strbuf_addf(out, "\n%c %s ", comment_line_char, label);
+       strbuf_addf(out, "\n%s %s ", comment_line_str, label);
        add_people_count(out, them);
 }
 
@@ -510,7 +510,7 @@ static void fmt_tag_signature(struct strbuf *tagbuf,
        if (sig->len) {
                strbuf_addch(tagbuf, '\n');
                strbuf_add_commented_lines(tagbuf, sig->buf, sig->len,
-                                          comment_line_char);
+                                          comment_line_str);
        }
 }
 
@@ -557,7 +557,7 @@ static void fmt_merge_msg_sigs(struct strbuf *out)
                                strbuf_add_commented_lines(&tagline,
                                                origins.items[first_tag].string,
                                                strlen(origins.items[first_tag].string),
-                                               comment_line_char);
+                                               comment_line_str);
                                strbuf_insert(&tagbuf, 0, tagline.buf,
                                              tagline.len);
                                strbuf_release(&tagline);
@@ -566,7 +566,7 @@ static void fmt_merge_msg_sigs(struct strbuf *out)
                        strbuf_add_commented_lines(&tagbuf,
                                        origins.items[i].string,
                                        strlen(origins.items[i].string),
-                                       comment_line_char);
+                                       comment_line_str);
                        fmt_tag_signature(&tagbuf, &sig, buf, len);
                }
                strbuf_release(&payload);
diff --git a/fsck.c b/fsck.c
index 8ded0a473a47fb45c223d8736f58c599b66cbb93..78af29d26459e1d392d8a64ff72dd5fed575daff 100644 (file)
--- a/fsck.c
+++ b/fsck.c
@@ -327,7 +327,8 @@ static int fsck_walk_tree(struct tree *tree, void *data, struct fsck_options *op
                return -1;
 
        name = fsck_get_object_name(options, &tree->object.oid);
-       if (init_tree_desc_gently(&desc, tree->buffer, tree->size, 0))
+       if (init_tree_desc_gently(&desc, &tree->object.oid,
+                                 tree->buffer, tree->size, 0))
                return -1;
        while (tree_entry_gently(&desc, &entry)) {
                struct object *obj;
@@ -598,7 +599,8 @@ static int fsck_tree(const struct object_id *tree_oid,
        const char *o_name;
        struct name_stack df_dup_candidates = { NULL };
 
-       if (init_tree_desc_gently(&desc, buffer, size, TREE_DESC_RAW_MODES)) {
+       if (init_tree_desc_gently(&desc, tree_oid, buffer, size,
+                                 TREE_DESC_RAW_MODES)) {
                retval += report(options, tree_oid, OBJ_TREE,
                                 FSCK_MSG_BAD_TREE,
                                 "cannot be parsed as a tree");
index e3d390974331e83261ba32e757724723b9bea724..eb34cda4092aaf0a396893cd424ca358d93b36aa 100755 (executable)
@@ -148,7 +148,7 @@ do
        if [ -z "$dry_run" ] ; then
                git apply --index -C1 ${level:+"$level"} "$tmp_patch" &&
                tree=$(git write-tree) &&
-               commit=$( (echo "$SUBJECT"; echo; cat "$tmp_msg") | git commit-tree $tree -p $commit) &&
+               commit=$( { echo "$SUBJECT"; echo; cat "$tmp_msg"; } | git commit-tree $tree -p $commit) &&
                git update-ref -m "quiltimport: $patch_name" HEAD $commit || exit 4
        fi
 done 3<"$QUILT_SERIES"
index 95e764acb14b3e069a255a8261a7d724c5f8d1ad..b5993385ff521da302eea04148d1055ff8d6b5ff 100644 (file)
@@ -586,8 +586,8 @@ static int verify_ssh_signed_buffer(struct signature_check *sigc,
                }
        }
 
-       strbuf_stripspace(&ssh_keygen_out, '\0');
-       strbuf_stripspace(&ssh_keygen_err, '\0');
+       strbuf_stripspace(&ssh_keygen_out, NULL);
+       strbuf_stripspace(&ssh_keygen_err, NULL);
        /* Add stderr outputs to show the user actual ssh-keygen errors */
        strbuf_add(&ssh_keygen_out, ssh_principals_err.buf, ssh_principals_err.len);
        strbuf_add(&ssh_keygen_out, ssh_keygen_err.buf, ssh_keygen_err.len);
diff --git a/grep.c b/grep.c
index 5f23d1a50cabb35732f9515355fe0a85a33d2ff8..ac34bfeafb3c911aa6918d4f90deeca5f8da3bee 100644 (file)
--- a/grep.c
+++ b/grep.c
@@ -621,7 +621,7 @@ static struct grep_expr *compile_pattern_atom(struct grep_pat **list)
                *list = p->next;
                x = compile_pattern_or(list);
                if (!*list || (*list)->token != GREP_CLOSE_PAREN)
-                       die("unmatched parenthesis");
+                       die("unmatched ( for expression group");
                *list = (*list)->next;
                return x;
        default:
@@ -792,7 +792,7 @@ void compile_grep_patterns(struct grep_opt *opt)
        if (p)
                opt->pattern_expression = compile_pattern_expr(&p);
        if (p)
-               die("incomplete pattern expression: %s", p->pattern);
+               die("incomplete pattern expression group: %s", p->pattern);
 
        if (opt->no_body_match && opt->pattern_expression)
                opt->pattern_expression = grep_not_expr(opt->pattern_expression);
index 10d84cc208886187d044b0b69fbfc397f4d28864..2cfde63ae1cf029e9700185247fc833c82d54407 100644 (file)
--- a/hash-ll.h
+++ b/hash-ll.h
@@ -145,6 +145,7 @@ struct object_id {
 #define GET_OID_RECORD_PATH     0200
 #define GET_OID_ONLY_TO_DIE    04000
 #define GET_OID_REQUIRE_PATH  010000
+#define GET_OID_HASH_ANY      020000
 
 #define GET_OID_DISAMBIGUATORS \
        (GET_OID_COMMIT | GET_OID_COMMITTISH | \
diff --git a/hash.h b/hash.h
index 615ae0691d070b4bdc110fecad3a0c484d2b234c..e064807c17333ab1ab5572adb4f331305bcad954 100644 (file)
--- a/hash.h
+++ b/hash.h
@@ -73,10 +73,15 @@ static inline void oidclr(struct object_id *oid)
        oid->algo = hash_algo_by_ptr(the_hash_algo);
 }
 
+static inline void oidread_algop(struct object_id *oid, const unsigned char *hash, const struct git_hash_algo *algop)
+{
+       memcpy(oid->hash, hash, algop->rawsz);
+       oid->algo = hash_algo_by_ptr(algop);
+}
+
 static inline void oidread(struct object_id *oid, const unsigned char *hash)
 {
-       memcpy(oid->hash, hash, the_hash_algo->rawsz);
-       oid->algo = hash_algo_by_ptr(the_hash_algo);
+       oidread_algop(oid, hash, the_hash_algo);
 }
 
 static inline int is_empty_blob_sha1(const unsigned char *sha1)
index 33db41bfac5f977747ce7af864207df3f255e491..1fe51226fd28a84fefe621cd2cf0a593afa1105f 100644 (file)
@@ -1307,7 +1307,7 @@ static struct object_list **process_tree(struct tree *tree,
        obj->flags |= SEEN;
        p = add_one_object(obj, p);
 
-       init_tree_desc(&desc, tree->buffer, tree->size);
+       init_tree_desc(&desc, &tree->object.oid, tree->buffer, tree->size);
 
        while (tree_entry(&desc, &entry))
                switch (object_type(entry.mode)) {
index f2e1947e63815c80e45f648f0ce37cd6341f825b..4caa8668e6ccb410ebf7eabd4245c00ac7fff0aa 100644 (file)
@@ -68,9 +68,6 @@ static void imap_warn(const char *, ...);
 
 static char *next_arg(char **);
 
-__attribute__((format (printf, 3, 4)))
-static int nfsnprintf(char *buf, int blen, const char *fmt, ...);
-
 static int nfvasprintf(char **strp, const char *fmt, va_list ap)
 {
        int len;
@@ -500,19 +497,6 @@ static char *next_arg(char **s)
        return ret;
 }
 
-__attribute__((format (printf, 3, 4)))
-static int nfsnprintf(char *buf, int blen, const char *fmt, ...)
-{
-       int ret;
-       va_list va;
-
-       va_start(va, fmt);
-       if (blen <= 0 || (unsigned)(ret = vsnprintf(buf, blen, fmt, va)) >= (unsigned)blen)
-               BUG("buffer too small. Please report a bug.");
-       va_end(va);
-       return ret;
-}
-
 static struct imap_cmd *issue_imap_cmd(struct imap_store *ctx,
                                       struct imap_cmd_cb *cb,
                                       const char *fmt, va_list ap)
@@ -535,11 +519,11 @@ static struct imap_cmd *issue_imap_cmd(struct imap_store *ctx,
                get_cmd_result(ctx, NULL);
 
        if (!cmd->cb.data)
-               bufl = nfsnprintf(buf, sizeof(buf), "%d %s\r\n", cmd->tag, cmd->cmd);
+               bufl = xsnprintf(buf, sizeof(buf), "%d %s\r\n", cmd->tag, cmd->cmd);
        else
-               bufl = nfsnprintf(buf, sizeof(buf), "%d %s{%d%s}\r\n",
-                                 cmd->tag, cmd->cmd, cmd->cb.dlen,
-                                 CAP(LITERALPLUS) ? "+" : "");
+               bufl = xsnprintf(buf, sizeof(buf), "%d %s{%d%s}\r\n",
+                                cmd->tag, cmd->cmd, cmd->cb.dlen,
+                                CAP(LITERALPLUS) ? "+" : "");
 
        if (0 < verbosity) {
                if (imap->num_in_progress)
index f39b68faf54889144abd81e2c1f9c6f19edd8d4f..11ad8be411eeb29b3f1bb0fd798ab71e71791014 100644 (file)
@@ -102,7 +102,7 @@ static void process_tree_contents(struct traversal_context *ctx,
        enum interesting match = ctx->revs->diffopt.pathspec.nr == 0 ?
                all_entries_interesting : entry_not_interesting;
 
-       init_tree_desc(&desc, tree->buffer, tree->size);
+       init_tree_desc(&desc, &tree->object.oid, tree->buffer, tree->size);
 
        while (tree_entry(&desc, &entry)) {
                if (match != all_entries_interesting) {
index e5438b029d90f352f0c434ebc8088c01fce11f3a..59eeaef1f745e40eff51681460e40a2026e834d4 100644 (file)
@@ -470,16 +470,19 @@ void fmt_output_email_subject(struct strbuf *sb, struct rev_info *opt)
 }
 
 void log_write_email_headers(struct rev_info *opt, struct commit *commit,
-                            const char **extra_headers_p,
+                            char **extra_headers_p,
                             int *need_8bit_cte_p,
                             int maybe_multipart)
 {
-       const char *extra_headers = opt->extra_headers;
+       struct strbuf headers = STRBUF_INIT;
        const char *name = oid_to_hex(opt->zero_commit ?
                                      null_oid() : &commit->object.oid);
 
        *need_8bit_cte_p = 0; /* unknown */
 
+       if (opt->extra_headers && *opt->extra_headers)
+               strbuf_addstr(&headers, opt->extra_headers);
+
        fprintf(opt->diffopt.file, "From %s Mon Sep 17 00:00:00 2001\n", name);
        graph_show_oneline(opt->graph);
        if (opt->message_id) {
@@ -496,16 +499,13 @@ void log_write_email_headers(struct rev_info *opt, struct commit *commit,
                graph_show_oneline(opt->graph);
        }
        if (opt->mime_boundary && maybe_multipart) {
-               static struct strbuf subject_buffer = STRBUF_INIT;
                static struct strbuf buffer = STRBUF_INIT;
                struct strbuf filename =  STRBUF_INIT;
                *need_8bit_cte_p = -1; /* NEVER */
 
-               strbuf_reset(&subject_buffer);
                strbuf_reset(&buffer);
 
-               strbuf_addf(&subject_buffer,
-                        "%s"
+               strbuf_addf(&headers,
                         "MIME-Version: 1.0\n"
                         "Content-Type: multipart/mixed;"
                         " boundary=\"%s%s\"\n"
@@ -516,10 +516,8 @@ void log_write_email_headers(struct rev_info *opt, struct commit *commit,
                         "Content-Type: text/plain; "
                         "charset=UTF-8; format=fixed\n"
                         "Content-Transfer-Encoding: 8bit\n\n",
-                        extra_headers ? extra_headers : "",
                         mime_boundary_leader, opt->mime_boundary,
                         mime_boundary_leader, opt->mime_boundary);
-               extra_headers = subject_buffer.buf;
 
                if (opt->numbered_files)
                        strbuf_addf(&filename, "%d", opt->nr);
@@ -539,7 +537,7 @@ void log_write_email_headers(struct rev_info *opt, struct commit *commit,
                opt->diffopt.stat_sep = buffer.buf;
                strbuf_release(&filename);
        }
-       *extra_headers_p = extra_headers;
+       *extra_headers_p = headers.len ? strbuf_detach(&headers, NULL) : NULL;
 }
 
 static void show_sig_lines(struct rev_info *opt, int status, const char *bol)
@@ -678,7 +676,6 @@ void show_log(struct rev_info *opt)
        struct log_info *log = opt->loginfo;
        struct commit *commit = log->commit, *parent = log->parent;
        int abbrev_commit = opt->abbrev_commit ? opt->abbrev : the_hash_algo->hexsz;
-       const char *extra_headers = opt->extra_headers;
        struct pretty_print_context ctx = {0};
 
        opt->loginfo = NULL;
@@ -739,10 +736,9 @@ void show_log(struct rev_info *opt)
         */
 
        if (cmit_fmt_is_mail(opt->commit_format)) {
-               log_write_email_headers(opt, commit, &extra_headers,
+               log_write_email_headers(opt, commit, &ctx.after_subject,
                                        &ctx.need_8bit_cte, 1);
                ctx.rev = opt;
-               ctx.print_email_subject = 1;
        } else if (opt->commit_format != CMIT_FMT_USERFORMAT) {
                fputs(diff_get_color_opt(&opt->diffopt, DIFF_COMMIT), opt->diffopt.file);
                if (opt->commit_format != CMIT_FMT_ONELINE)
@@ -808,7 +804,6 @@ void show_log(struct rev_info *opt)
        ctx.date_mode = opt->date_mode;
        ctx.date_mode_explicit = opt->date_mode_explicit;
        ctx.abbrev = opt->diffopt.abbrev;
-       ctx.after_subject = extra_headers;
        ctx.preserve_subject = opt->preserve_subject;
        ctx.encode_email_headers = opt->encode_email_headers;
        ctx.reflog_info = opt->reflog_info;
@@ -857,6 +852,7 @@ void show_log(struct rev_info *opt)
 
        strbuf_release(&msgbuf);
        free(ctx.notes_message);
+       free(ctx.after_subject);
 
        if (cmit_fmt_is_mail(ctx.fmt) && opt->idiff_oid1) {
                struct diff_queue_struct dq;
index 41c776fea52e6867800caf2eb919379fc6e54218..94978e2c838ce98bdd442006b942cce38e2ef6c8 100644 (file)
@@ -29,7 +29,7 @@ void format_decorations(struct strbuf *sb, const struct commit *commit,
                        int use_color, const struct decoration_options *opts);
 void show_decorations(struct rev_info *opt, struct commit *commit);
 void log_write_email_headers(struct rev_info *opt, struct commit *commit,
-                            const char **extra_headers_p,
+                            char **extra_headers_p,
                             int *need_8bit_cte_p,
                             int maybe_multipart);
 void load_ref_decorations(struct decoration_filter *filter, int flags);
diff --git a/loose.c b/loose.c
new file mode 100644 (file)
index 0000000..f6faa62
--- /dev/null
+++ b/loose.c
@@ -0,0 +1,259 @@
+#include "git-compat-util.h"
+#include "hash.h"
+#include "path.h"
+#include "object-store.h"
+#include "hex.h"
+#include "wrapper.h"
+#include "gettext.h"
+#include "loose.h"
+#include "lockfile.h"
+#include "oidtree.h"
+
+static const char *loose_object_header = "# loose-object-idx\n";
+
+static inline int should_use_loose_object_map(struct repository *repo)
+{
+       return repo->compat_hash_algo && repo->gitdir;
+}
+
+void loose_object_map_init(struct loose_object_map **map)
+{
+       struct loose_object_map *m;
+       m = xmalloc(sizeof(**map));
+       m->to_compat = kh_init_oid_map();
+       m->to_storage = kh_init_oid_map();
+       *map = m;
+}
+
+static int insert_oid_pair(kh_oid_map_t *map, const struct object_id *key, const struct object_id *value)
+{
+       khiter_t pos;
+       int ret;
+       struct object_id *stored;
+
+       pos = kh_put_oid_map(map, *key, &ret);
+
+       /* This item already exists in the map. */
+       if (ret == 0)
+               return 0;
+
+       stored = xmalloc(sizeof(*stored));
+       oidcpy(stored, value);
+       kh_value(map, pos) = stored;
+       return 1;
+}
+
+static int insert_loose_map(struct object_directory *odb,
+                           const struct object_id *oid,
+                           const struct object_id *compat_oid)
+{
+       struct loose_object_map *map = odb->loose_map;
+       int inserted = 0;
+
+       inserted |= insert_oid_pair(map->to_compat, oid, compat_oid);
+       inserted |= insert_oid_pair(map->to_storage, compat_oid, oid);
+       if (inserted)
+               oidtree_insert(odb->loose_objects_cache, compat_oid);
+
+       return inserted;
+}
+
+static int load_one_loose_object_map(struct repository *repo, struct object_directory *dir)
+{
+       struct strbuf buf = STRBUF_INIT, path = STRBUF_INIT;
+       FILE *fp;
+
+       if (!dir->loose_map)
+               loose_object_map_init(&dir->loose_map);
+       if (!dir->loose_objects_cache) {
+               ALLOC_ARRAY(dir->loose_objects_cache, 1);
+               oidtree_init(dir->loose_objects_cache);
+       }
+
+       insert_loose_map(dir, repo->hash_algo->empty_tree, repo->compat_hash_algo->empty_tree);
+       insert_loose_map(dir, repo->hash_algo->empty_blob, repo->compat_hash_algo->empty_blob);
+       insert_loose_map(dir, repo->hash_algo->null_oid, repo->compat_hash_algo->null_oid);
+
+       strbuf_git_common_path(&path, repo, "objects/loose-object-idx");
+       fp = fopen(path.buf, "rb");
+       if (!fp) {
+               strbuf_release(&path);
+               return 0;
+       }
+
+       errno = 0;
+       if (strbuf_getwholeline(&buf, fp, '\n') || strcmp(buf.buf, loose_object_header))
+               goto err;
+       while (!strbuf_getline_lf(&buf, fp)) {
+               const char *p;
+               struct object_id oid, compat_oid;
+               if (parse_oid_hex_algop(buf.buf, &oid, &p, repo->hash_algo) ||
+                   *p++ != ' ' ||
+                   parse_oid_hex_algop(p, &compat_oid, &p, repo->compat_hash_algo) ||
+                   p != buf.buf + buf.len)
+                       goto err;
+               insert_loose_map(dir, &oid, &compat_oid);
+       }
+
+       strbuf_release(&buf);
+       strbuf_release(&path);
+       return errno ? -1 : 0;
+err:
+       strbuf_release(&buf);
+       strbuf_release(&path);
+       return -1;
+}
+
+int repo_read_loose_object_map(struct repository *repo)
+{
+       struct object_directory *dir;
+
+       if (!should_use_loose_object_map(repo))
+               return 0;
+
+       prepare_alt_odb(repo);
+
+       for (dir = repo->objects->odb; dir; dir = dir->next) {
+               if (load_one_loose_object_map(repo, dir) < 0) {
+                       return -1;
+               }
+       }
+       return 0;
+}
+
+int repo_write_loose_object_map(struct repository *repo)
+{
+       kh_oid_map_t *map = repo->objects->odb->loose_map->to_compat;
+       struct lock_file lock;
+       int fd;
+       khiter_t iter;
+       struct strbuf buf = STRBUF_INIT, path = STRBUF_INIT;
+
+       if (!should_use_loose_object_map(repo))
+               return 0;
+
+       strbuf_git_common_path(&path, repo, "objects/loose-object-idx");
+       fd = hold_lock_file_for_update_timeout(&lock, path.buf, LOCK_DIE_ON_ERROR, -1);
+       iter = kh_begin(map);
+       if (write_in_full(fd, loose_object_header, strlen(loose_object_header)) < 0)
+               goto errout;
+
+       for (; iter != kh_end(map); iter++) {
+               if (kh_exist(map, iter)) {
+                       if (oideq(&kh_key(map, iter), the_hash_algo->empty_tree) ||
+                           oideq(&kh_key(map, iter), the_hash_algo->empty_blob))
+                               continue;
+                       strbuf_addf(&buf, "%s %s\n", oid_to_hex(&kh_key(map, iter)), oid_to_hex(kh_value(map, iter)));
+                       if (write_in_full(fd, buf.buf, buf.len) < 0)
+                               goto errout;
+                       strbuf_reset(&buf);
+               }
+       }
+       strbuf_release(&buf);
+       if (commit_lock_file(&lock) < 0) {
+               error_errno(_("could not write loose object index %s"), path.buf);
+               strbuf_release(&path);
+               return -1;
+       }
+       strbuf_release(&path);
+       return 0;
+errout:
+       rollback_lock_file(&lock);
+       strbuf_release(&buf);
+       error_errno(_("failed to write loose object index %s\n"), path.buf);
+       strbuf_release(&path);
+       return -1;
+}
+
+static int write_one_object(struct repository *repo, const struct object_id *oid,
+                           const struct object_id *compat_oid)
+{
+       struct lock_file lock;
+       int fd;
+       struct stat st;
+       struct strbuf buf = STRBUF_INIT, path = STRBUF_INIT;
+
+       strbuf_git_common_path(&path, repo, "objects/loose-object-idx");
+       hold_lock_file_for_update_timeout(&lock, path.buf, LOCK_DIE_ON_ERROR, -1);
+
+       fd = open(path.buf, O_WRONLY | O_CREAT | O_APPEND, 0666);
+       if (fd < 0)
+               goto errout;
+       if (fstat(fd, &st) < 0)
+               goto errout;
+       if (!st.st_size && write_in_full(fd, loose_object_header, strlen(loose_object_header)) < 0)
+               goto errout;
+
+       strbuf_addf(&buf, "%s %s\n", oid_to_hex(oid), oid_to_hex(compat_oid));
+       if (write_in_full(fd, buf.buf, buf.len) < 0)
+               goto errout;
+       if (close(fd))
+               goto errout;
+       adjust_shared_perm(path.buf);
+       rollback_lock_file(&lock);
+       strbuf_release(&buf);
+       strbuf_release(&path);
+       return 0;
+errout:
+       error_errno(_("failed to write loose object index %s\n"), path.buf);
+       close(fd);
+       rollback_lock_file(&lock);
+       strbuf_release(&buf);
+       strbuf_release(&path);
+       return -1;
+}
+
+int repo_add_loose_object_map(struct repository *repo, const struct object_id *oid,
+                             const struct object_id *compat_oid)
+{
+       int inserted = 0;
+
+       if (!should_use_loose_object_map(repo))
+               return 0;
+
+       inserted = insert_loose_map(repo->objects->odb, oid, compat_oid);
+       if (inserted)
+               return write_one_object(repo, oid, compat_oid);
+       return 0;
+}
+
+int repo_loose_object_map_oid(struct repository *repo,
+                             const struct object_id *src,
+                             const struct git_hash_algo *to,
+                             struct object_id *dest)
+{
+       struct object_directory *dir;
+       kh_oid_map_t *map;
+       khiter_t pos;
+
+       for (dir = repo->objects->odb; dir; dir = dir->next) {
+               struct loose_object_map *loose_map = dir->loose_map;
+               if (!loose_map)
+                       continue;
+               map = (to == repo->compat_hash_algo) ?
+                       loose_map->to_compat :
+                       loose_map->to_storage;
+               pos = kh_get_oid_map(map, *src);
+               if (pos < kh_end(map)) {
+                       oidcpy(dest, kh_value(map, pos));
+                       return 0;
+               }
+       }
+       return -1;
+}
+
+void loose_object_map_clear(struct loose_object_map **map)
+{
+       struct loose_object_map *m = *map;
+       struct object_id *oid;
+
+       if (!m)
+               return;
+
+       kh_foreach_value(m->to_compat, oid, free(oid));
+       kh_foreach_value(m->to_storage, oid, free(oid));
+       kh_destroy_oid_map(m->to_compat);
+       kh_destroy_oid_map(m->to_storage);
+       free(m);
+       *map = NULL;
+}
diff --git a/loose.h b/loose.h
new file mode 100644 (file)
index 0000000..2c29570
--- /dev/null
+++ b/loose.h
@@ -0,0 +1,22 @@
+#ifndef LOOSE_H
+#define LOOSE_H
+
+#include "khash.h"
+
+struct loose_object_map {
+       kh_oid_map_t *to_compat;
+       kh_oid_map_t *to_storage;
+};
+
+void loose_object_map_init(struct loose_object_map **map);
+void loose_object_map_clear(struct loose_object_map **map);
+int repo_loose_object_map_oid(struct repository *repo,
+                             const struct object_id *src,
+                             const struct git_hash_algo *dest_algo,
+                             struct object_id *dest);
+int repo_add_loose_object_map(struct repository *repo, const struct object_id *oid,
+                             const struct object_id *compat_oid);
+int repo_read_loose_object_map(struct repository *repo);
+int repo_write_loose_object_map(struct repository *repo);
+
+#endif
index 0885ac681cd5055f80fb5088b04c92eed8e7f291..3412b6a1401df13f25069999e7384396558fbd34 100644 (file)
@@ -63,7 +63,7 @@ static void *fill_tree_desc_strict(struct tree_desc *desc,
                die("unable to read tree (%s)", oid_to_hex(hash));
        if (type != OBJ_TREE)
                die("%s is not a tree", oid_to_hex(hash));
-       init_tree_desc(desc, buffer, size);
+       init_tree_desc(desc, hash, buffer, size);
        return buffer;
 }
 
@@ -194,7 +194,7 @@ static int splice_tree(const struct object_id *oid1, const char *prefix,
        buf = repo_read_object_file(the_repository, oid1, &type, &sz);
        if (!buf)
                die("cannot read tree %s", oid_to_hex(oid1));
-       init_tree_desc(&desc, buf, sz);
+       init_tree_desc(&desc, oid1, buf, sz);
 
        rewrite_here = NULL;
        while (desc.size) {
index 2078c22b097a61c97faddb6689a75e6c10ccb9ee..3065b12b237a9d9e2314e08306222d33c6f137dd 100644 (file)
@@ -115,6 +115,7 @@ static char *mem_pool_strvfmt(struct mem_pool *pool, const char *fmt,
        size_t available = block ? block->end - block->next_free : 0;
        va_list cp;
        int len, len2;
+       size_t size;
        char *ret;
 
        va_copy(cp, ap);
@@ -123,13 +124,14 @@ static char *mem_pool_strvfmt(struct mem_pool *pool, const char *fmt,
        if (len < 0)
                BUG("your vsnprintf is broken (returned %d)", len);
 
-       ret = mem_pool_alloc(pool, len + 1);  /* 1 for NUL */
+       size = st_add(len, 1); /* 1 for NUL */
+       ret = mem_pool_alloc(pool, size);
 
        /* Shortcut; relies on mem_pool_alloc() not touching buffer contents. */
        if (ret == next_free)
                return ret;
 
-       len2 = vsnprintf(ret, len + 1, fmt, ap);
+       len2 = vsnprintf(ret, size, fmt, ap);
        if (len2 != len)
                BUG("your vsnprintf is broken (returns inconsistent lengths)");
        return ret;
index 61e0ae53981dbc786061a1b8ed7689ca5fee3912..bf1077ae0928e99cb0c5dc0814fecf9e141447b7 100644 (file)
@@ -128,7 +128,9 @@ static enum ll_merge_result ll_xdl_merge(const struct ll_merge_driver *drv_unuse
        xmp.level = XDL_MERGE_ZEALOUS;
        xmp.favor = opts->variant;
        xmp.xpp.flags = opts->xdl_opts;
-       if (git_xmerge_style >= 0)
+       if (opts->conflict_style >= 0)
+               xmp.style = opts->conflict_style;
+       else if (git_xmerge_style >= 0)
                xmp.style = git_xmerge_style;
        if (marker_size > 0)
                xmp.marker_size = marker_size;
@@ -401,7 +403,7 @@ enum ll_merge_result ll_merge(mmbuffer_t *result_buf,
             const struct ll_merge_options *opts)
 {
        struct attr_check *check = load_merge_attributes();
-       static const struct ll_merge_options default_opts;
+       static const struct ll_merge_options default_opts = LL_MERGE_OPTIONS_INIT;
        const char *ll_driver_name = NULL;
        int marker_size = DEFAULT_CONFLICT_MARKER_SIZE;
        const struct ll_merge_driver *driver;
index e4a20e81a3aea90b64e65b1f33a38efa595d9a2a..d038ee0c1e81f71f75a2655be149b481e5afa19f 100644 (file)
@@ -78,10 +78,15 @@ struct ll_merge_options {
         */
        unsigned extra_marker_size;
 
+       /* Override the global conflict style. */
+       int conflict_style;
+
        /* Extra xpparam_t flags as defined in xdiff/xdiff.h. */
        long xdl_opts;
 };
 
+#define LL_MERGE_OPTIONS_INIT { .conflict_style = -1 }
+
 enum ll_merge_result {
        LL_MERGE_ERROR = -1,
        LL_MERGE_OK = 0,
index 201f8f77755b439ad1b91b5b692c16aaeba5d032..eaede6cead9442995ef2f0ea2b449bceeaf0bcb7 100644 (file)
@@ -1665,9 +1665,10 @@ static int collect_merge_info(struct merge_options *opt,
            parse_tree(side1) < 0 ||
            parse_tree(side2) < 0)
                return -1;
-       init_tree_desc(t + 0, merge_base->buffer, merge_base->size);
-       init_tree_desc(t + 1, side1->buffer, side1->size);
-       init_tree_desc(t + 2, side2->buffer, side2->size);
+       init_tree_desc(t + 0, &merge_base->object.oid,
+                      merge_base->buffer, merge_base->size);
+       init_tree_desc(t + 1, &side1->object.oid, side1->buffer, side1->size);
+       init_tree_desc(t + 2, &side2->object.oid, side2->buffer, side2->size);
 
        trace2_region_enter("merge", "traverse_trees", opt->repo);
        ret = traverse_trees(NULL, 3, t, &info);
@@ -2024,7 +2025,7 @@ static int merge_3way(struct merge_options *opt,
                      mmbuffer_t *result_buf)
 {
        mmfile_t orig, src1, src2;
-       struct ll_merge_options ll_opts = {0};
+       struct ll_merge_options ll_opts = LL_MERGE_OPTIONS_INIT;
        char *base, *name1, *name2;
        enum ll_merge_result merge_status;
 
@@ -2034,6 +2035,7 @@ static int merge_3way(struct merge_options *opt,
        ll_opts.renormalize = opt->renormalize;
        ll_opts.extra_marker_size = extra_marker_size;
        ll_opts.xdl_opts = opt->xdl_opts;
+       ll_opts.conflict_style = opt->conflict_style;
 
        if (opt->priv->call_depth) {
                ll_opts.virtual_ancestor = 1;
@@ -4446,10 +4448,10 @@ static int checkout(struct merge_options *opt,
        unpack_opts.preserve_ignored = 0; /* FIXME: !opts->overwrite_ignore */
        if (parse_tree(prev) < 0)
                return -1;
-       init_tree_desc(&trees[0], prev->buffer, prev->size);
+       init_tree_desc(&trees[0], &prev->object.oid, prev->buffer, prev->size);
        if (parse_tree(next) < 0)
                return -1;
-       init_tree_desc(&trees[1], next->buffer, next->size);
+       init_tree_desc(&trees[1], &next->object.oid, next->buffer, next->size);
 
        ret = unpack_trees(2, trees, &unpack_opts);
        clear_unpack_trees_porcelain(&unpack_opts);
index 103ee321aeb8cd8c169d35316bb20cfd9eea1693..8ff29ed09efb3303adf82d39c5fb54a0d11fc897 100644 (file)
@@ -407,7 +407,7 @@ static void init_tree_desc_from_tree(struct tree_desc *desc, struct tree *tree)
 {
        if (parse_tree(tree) < 0)
                exit(128);
-       init_tree_desc(desc, tree->buffer, tree->size);
+       init_tree_desc(desc, &tree->object.oid, tree->buffer, tree->size);
 }
 
 static int unpack_trees_start(struct merge_options *opt,
@@ -1048,13 +1048,14 @@ static int merge_3way(struct merge_options *opt,
                      const int extra_marker_size)
 {
        mmfile_t orig, src1, src2;
-       struct ll_merge_options ll_opts = {0};
+       struct ll_merge_options ll_opts = LL_MERGE_OPTIONS_INIT;
        char *base, *name1, *name2;
        enum ll_merge_result merge_status;
 
        ll_opts.renormalize = opt->renormalize;
        ll_opts.extra_marker_size = extra_marker_size;
        ll_opts.xdl_opts = opt->xdl_opts;
+       ll_opts.conflict_style = opt->conflict_style;
 
        if (opt->priv->call_depth) {
                ll_opts.virtual_ancestor = 1;
@@ -3947,6 +3948,8 @@ void init_merge_options(struct merge_options *opt,
 
        opt->renormalize = 0;
 
+       opt->conflict_style = -1;
+
        merge_recursive_config(opt);
        merge_verbosity = getenv("GIT_MERGE_VERBOSITY");
        if (merge_verbosity)
index 3d3b3e3c295deb0dc8470958d01f8ed1e6ef0611..e67d38c30305de9fc34b37dcdcb3f345b1cf97b4 100644 (file)
@@ -31,6 +31,7 @@ struct merge_options {
 
        /* xdiff-related options (patience, ignore whitespace, ours/theirs) */
        long xdl_opts;
+       int conflict_style;
        enum {
                MERGE_VARIANT_NORMAL = 0,
                MERGE_VARIANT_OURS,
diff --git a/merge.c b/merge.c
index 563281b10f1d04bd6b51a79f2852a61aa49552fd..752a937fa93dd3bf8703400c6311afe7a5265e02 100644 (file)
--- a/merge.c
+++ b/merge.c
@@ -81,7 +81,8 @@ int checkout_fast_forward(struct repository *r,
                        rollback_lock_file(&lock_file);
                        return -1;
                }
-               init_tree_desc(t+i, trees[i]->buffer, trees[i]->size);
+               init_tree_desc(t+i, &trees[i]->object.oid,
+                              trees[i]->buffer, trees[i]->size);
        }
 
        memset(&opts, 0, sizeof(opts));
diff --git a/midx.c b/midx.c
index 85e1c2cd1287b34e91d9dcccc74d330c93ff809c..41521e019c66d25ba538f71262d8dd73ff5cfa1d 100644 (file)
--- a/midx.c
+++ b/midx.c
@@ -2163,7 +2163,6 @@ int midx_repack(struct repository *r, const char *object_dir, size_t batch_size,
        unsigned char *include_pack;
        struct child_process cmd = CHILD_PROCESS_INIT;
        FILE *cmd_in;
-       struct strbuf base_name = STRBUF_INIT;
        struct multi_pack_index *m = lookup_multi_pack_index(r, object_dir);
 
        /*
@@ -2190,9 +2189,7 @@ int midx_repack(struct repository *r, const char *object_dir, size_t batch_size,
 
        strvec_push(&cmd.args, "pack-objects");
 
-       strbuf_addstr(&base_name, object_dir);
-       strbuf_addstr(&base_name, "/pack/pack");
-       strvec_push(&cmd.args, base_name.buf);
+       strvec_pushf(&cmd.args, "%s/pack/pack", object_dir);
 
        if (delta_base_offset)
                strvec_push(&cmd.args, "--delta-base-offset");
@@ -2204,8 +2201,6 @@ int midx_repack(struct repository *r, const char *object_dir, size_t batch_size,
        else
                strvec_push(&cmd.args, "-q");
 
-       strbuf_release(&base_name);
-
        cmd.git_cmd = 1;
        cmd.in = cmd.out = -1;
 
diff --git a/object-file-convert.c b/object-file-convert.c
new file mode 100644 (file)
index 0000000..4f61890
--- /dev/null
@@ -0,0 +1,277 @@
+#include "git-compat-util.h"
+#include "gettext.h"
+#include "strbuf.h"
+#include "hex.h"
+#include "repository.h"
+#include "hash-ll.h"
+#include "hash.h"
+#include "object.h"
+#include "loose.h"
+#include "commit.h"
+#include "gpg-interface.h"
+#include "object-file-convert.h"
+
+int repo_oid_to_algop(struct repository *repo, const struct object_id *src,
+                     const struct git_hash_algo *to, struct object_id *dest)
+{
+       /*
+        * If the source algorithm is not set, then we're using the
+        * default hash algorithm for that object.
+        */
+       const struct git_hash_algo *from =
+               src->algo ? &hash_algos[src->algo] : repo->hash_algo;
+
+       if (from == to) {
+               if (src != dest)
+                       oidcpy(dest, src);
+               return 0;
+       }
+       if (repo_loose_object_map_oid(repo, src, to, dest)) {
+               /*
+                * We may have loaded the object map at repo initialization but
+                * another process (perhaps upstream of a pipe from us) may have
+                * written a new object into the map.  If the object is missing,
+                * let's reload the map to see if the object has appeared.
+                */
+               repo_read_loose_object_map(repo);
+               if (repo_loose_object_map_oid(repo, src, to, dest))
+                       return -1;
+       }
+       return 0;
+}
+
+static int decode_tree_entry_raw(struct object_id *oid, const char **path,
+                                size_t *len, const struct git_hash_algo *algo,
+                                const char *buf, unsigned long size)
+{
+       uint16_t mode;
+       const unsigned hashsz = algo->rawsz;
+
+       if (size < hashsz + 3 || buf[size - (hashsz + 1)]) {
+               return -1;
+       }
+
+       *path = parse_mode(buf, &mode);
+       if (!*path || !**path)
+               return -1;
+       *len = strlen(*path) + 1;
+
+       oidread_algop(oid, (const unsigned char *)*path + *len, algo);
+       return 0;
+}
+
+static int convert_tree_object(struct strbuf *out,
+                              const struct git_hash_algo *from,
+                              const struct git_hash_algo *to,
+                              const char *buffer, size_t size)
+{
+       const char *p = buffer, *end = buffer + size;
+
+       while (p < end) {
+               struct object_id entry_oid, mapped_oid;
+               const char *path = NULL;
+               size_t pathlen;
+
+               if (decode_tree_entry_raw(&entry_oid, &path, &pathlen, from, p,
+                                         end - p))
+                       return error(_("failed to decode tree entry"));
+               if (repo_oid_to_algop(the_repository, &entry_oid, to, &mapped_oid))
+                       return error(_("failed to map tree entry for %s"), oid_to_hex(&entry_oid));
+               strbuf_add(out, p, path - p);
+               strbuf_add(out, path, pathlen);
+               strbuf_add(out, mapped_oid.hash, to->rawsz);
+               p = path + pathlen + from->rawsz;
+       }
+       return 0;
+}
+
+static int convert_tag_object(struct strbuf *out,
+                             const struct git_hash_algo *from,
+                             const struct git_hash_algo *to,
+                             const char *buffer, size_t size)
+{
+       struct strbuf payload = STRBUF_INIT, oursig = STRBUF_INIT, othersig = STRBUF_INIT;
+       const int entry_len = from->hexsz + 7;
+       size_t payload_size;
+       struct object_id oid, mapped_oid;
+       const char *p;
+
+       /* Consume the object line */
+       if ((entry_len >= size) ||
+           memcmp(buffer, "object ", 7) || buffer[entry_len] != '\n')
+               return error("bogus tag object");
+       if (parse_oid_hex_algop(buffer + 7, &oid, &p, from) < 0)
+               return error("bad tag object ID");
+       if (repo_oid_to_algop(the_repository, &oid, to, &mapped_oid))
+               return error("unable to map tree %s in tag object",
+                            oid_to_hex(&oid));
+       size -= ((p + 1) - buffer);
+       buffer = p + 1;
+
+       /* Is there a signature for our algorithm? */
+       payload_size = parse_signed_buffer(buffer, size);
+       if (payload_size != size) {
+               /* Yes, there is. */
+               strbuf_add(&oursig, buffer + payload_size, size - payload_size);
+       }
+
+       /* Now, is there a signature for the other algorithm? */
+       parse_buffer_signed_by_header(buffer, payload_size, &payload, &othersig, to);
+       /*
+        * Our payload is now in payload and we may have up to two signatrures
+        * in oursig and othersig.
+        */
+
+       /* Add some slop for longer signature header in the new algorithm. */
+       strbuf_grow(out, (7 + to->hexsz + 1) + size + 7);
+       strbuf_addf(out, "object %s\n", oid_to_hex(&mapped_oid));
+       strbuf_addbuf(out, &payload);
+       if (oursig.len)
+               add_header_signature(out, &oursig, from);
+       strbuf_addbuf(out, &othersig);
+
+       strbuf_release(&payload);
+       strbuf_release(&othersig);
+       strbuf_release(&oursig);
+       return 0;
+}
+
+static int convert_commit_object(struct strbuf *out,
+                                const struct git_hash_algo *from,
+                                const struct git_hash_algo *to,
+                                const char *buffer, size_t size)
+{
+       const char *tail = buffer;
+       const char *bufptr = buffer;
+       const int tree_entry_len = from->hexsz + 5;
+       const int parent_entry_len = from->hexsz + 7;
+       struct object_id oid, mapped_oid;
+       const char *p, *eol;
+
+       tail += size;
+
+       while ((bufptr < tail) && (*bufptr != '\n')) {
+               eol = memchr(bufptr, '\n', tail - bufptr);
+               if (!eol)
+                       return error(_("bad %s in commit"), "line");
+
+               if (((bufptr + 5) < eol) && !memcmp(bufptr, "tree ", 5))
+               {
+                       if (((bufptr + tree_entry_len) != eol) ||
+                           parse_oid_hex_algop(bufptr + 5, &oid, &p, from) ||
+                           (p != eol))
+                               return error(_("bad %s in commit"), "tree");
+
+                       if (repo_oid_to_algop(the_repository, &oid, to, &mapped_oid))
+                               return error(_("unable to map %s %s in commit object"),
+                                            "tree", oid_to_hex(&oid));
+                       strbuf_addf(out, "tree %s\n", oid_to_hex(&mapped_oid));
+               }
+               else if (((bufptr + 7) < eol) && !memcmp(bufptr, "parent ", 7))
+               {
+                       if (((bufptr + parent_entry_len) != eol) ||
+                           parse_oid_hex_algop(bufptr + 7, &oid, &p, from) ||
+                           (p != eol))
+                               return error(_("bad %s in commit"), "parent");
+
+                       if (repo_oid_to_algop(the_repository, &oid, to, &mapped_oid))
+                               return error(_("unable to map %s %s in commit object"),
+                                            "parent", oid_to_hex(&oid));
+
+                       strbuf_addf(out, "parent %s\n", oid_to_hex(&mapped_oid));
+               }
+               else if (((bufptr + 9) < eol) && !memcmp(bufptr, "mergetag ", 9))
+               {
+                       struct strbuf tag = STRBUF_INIT, new_tag = STRBUF_INIT;
+
+                       /* Recover the tag object from the mergetag */
+                       strbuf_add(&tag, bufptr + 9, (eol - (bufptr + 9)) + 1);
+
+                       bufptr = eol + 1;
+                       while ((bufptr < tail) && (*bufptr == ' ')) {
+                               eol = memchr(bufptr, '\n', tail - bufptr);
+                               if (!eol) {
+                                       strbuf_release(&tag);
+                                       return error(_("bad %s in commit"), "mergetag continuation");
+                               }
+                               strbuf_add(&tag, bufptr + 1, (eol - (bufptr + 1)) + 1);
+                               bufptr = eol + 1;
+                       }
+
+                       /* Compute the new tag object */
+                       if (convert_tag_object(&new_tag, from, to, tag.buf, tag.len)) {
+                               strbuf_release(&tag);
+                               strbuf_release(&new_tag);
+                               return -1;
+                       }
+
+                       /* Write the new mergetag */
+                       strbuf_addstr(out, "mergetag");
+                       strbuf_add_lines(out, " ", new_tag.buf, new_tag.len);
+                       strbuf_release(&tag);
+                       strbuf_release(&new_tag);
+               }
+               else if (((bufptr + 7) < tail) && !memcmp(bufptr, "author ", 7))
+                       strbuf_add(out, bufptr, (eol - bufptr) + 1);
+               else if (((bufptr + 10) < tail) && !memcmp(bufptr, "committer ", 10))
+                       strbuf_add(out, bufptr, (eol - bufptr) + 1);
+               else if (((bufptr + 9) < tail) && !memcmp(bufptr, "encoding ", 9))
+                       strbuf_add(out, bufptr, (eol - bufptr) + 1);
+               else if (((bufptr + 6) < tail) && !memcmp(bufptr, "gpgsig", 6))
+                       strbuf_add(out, bufptr, (eol - bufptr) + 1);
+               else {
+                       /* Unknown line fail it might embed an oid */
+                       return -1;
+               }
+               /* Consume any trailing continuation lines */
+               bufptr = eol + 1;
+               while ((bufptr < tail) && (*bufptr == ' ')) {
+                       eol = memchr(bufptr, '\n', tail - bufptr);
+                       if (!eol)
+                               return error(_("bad %s in commit"), "continuation");
+                       strbuf_add(out, bufptr, (eol - bufptr) + 1);
+                       bufptr = eol + 1;
+               }
+       }
+       if (bufptr < tail)
+               strbuf_add(out, bufptr, tail - bufptr);
+       return 0;
+}
+
+int convert_object_file(struct strbuf *outbuf,
+                       const struct git_hash_algo *from,
+                       const struct git_hash_algo *to,
+                       const void *buf, size_t len,
+                       enum object_type type,
+                       int gentle)
+{
+       int ret;
+
+       /* Don't call this function when no conversion is necessary */
+       if ((from == to) || (type == OBJ_BLOB))
+               BUG("Refusing noop object file conversion");
+
+       switch (type) {
+       case OBJ_COMMIT:
+               ret = convert_commit_object(outbuf, from, to, buf, len);
+               break;
+       case OBJ_TREE:
+               ret = convert_tree_object(outbuf, from, to, buf, len);
+               break;
+       case OBJ_TAG:
+               ret = convert_tag_object(outbuf, from, to, buf, len);
+               break;
+       default:
+               /* Not implemented yet, so fail. */
+               ret = -1;
+               break;
+       }
+       if (!ret)
+               return 0;
+       if (gentle) {
+               strbuf_release(outbuf);
+               return ret;
+       }
+       die(_("Failed to convert object from %s to %s"),
+               from->name, to->name);
+}
diff --git a/object-file-convert.h b/object-file-convert.h
new file mode 100644 (file)
index 0000000..a4f802a
--- /dev/null
@@ -0,0 +1,24 @@
+#ifndef OBJECT_CONVERT_H
+#define OBJECT_CONVERT_H
+
+struct repository;
+struct object_id;
+struct git_hash_algo;
+struct strbuf;
+#include "object.h"
+
+int repo_oid_to_algop(struct repository *repo, const struct object_id *src,
+                     const struct git_hash_algo *to, struct object_id *dest);
+
+/*
+ * Convert an object file from one hash algorithm to another algorithm.
+ * Return -1 on failure, 0 on success.
+ */
+int convert_object_file(struct strbuf *outbuf,
+                       const struct git_hash_algo *from,
+                       const struct git_hash_algo *to,
+                       const void *buf, size_t len,
+                       enum object_type type,
+                       int gentle);
+
+#endif /* OBJECT_CONVERT_H */
index 619f039ebc7ceb52467e4810c5c4cf3afabb907a..610b1f465c4248e8c0520687049a707a8497195e 100644 (file)
@@ -35,6 +35,8 @@
 #include "setup.h"
 #include "submodule.h"
 #include "fsck.h"
+#include "loose.h"
+#include "object-file-convert.h"
 
 /* The maximum size for an object header. */
 #define MAX_HEADER_LEN 32
@@ -1084,9 +1086,11 @@ int check_object_signature(struct repository *r, const struct object_id *oid,
                           void *buf, unsigned long size,
                           enum object_type type)
 {
+       const struct git_hash_algo *algo =
+               oid->algo ? &hash_algos[oid->algo] : r->hash_algo;
        struct object_id real_oid;
 
-       hash_object_file(r->hash_algo, buf, size, type, &real_oid);
+       hash_object_file(algo, buf, size, type, &real_oid);
 
        return !oideq(oid, &real_oid) ? -1 : 0;
 }
@@ -1652,10 +1656,101 @@ static int do_oid_object_info_extended(struct repository *r,
        return 0;
 }
 
+static int oid_object_info_convert(struct repository *r,
+                                  const struct object_id *input_oid,
+                                  struct object_info *input_oi, unsigned flags)
+{
+       const struct git_hash_algo *input_algo = &hash_algos[input_oid->algo];
+       int do_die = flags & OBJECT_INFO_DIE_IF_CORRUPT;
+       struct strbuf type_name = STRBUF_INIT;
+       struct object_id oid, delta_base_oid;
+       struct object_info new_oi, *oi;
+       unsigned long size;
+       void *content;
+       int ret;
+
+       if (repo_oid_to_algop(r, input_oid, the_hash_algo, &oid)) {
+               if (do_die)
+                       die(_("missing mapping of %s to %s"),
+                           oid_to_hex(input_oid), the_hash_algo->name);
+               return -1;
+       }
+
+       /* Is new_oi needed? */
+       oi = input_oi;
+       if (input_oi && (input_oi->delta_base_oid || input_oi->sizep ||
+                        input_oi->contentp)) {
+               new_oi = *input_oi;
+               /* Does delta_base_oid need to be converted? */
+               if (input_oi->delta_base_oid)
+                       new_oi.delta_base_oid = &delta_base_oid;
+               /* Will the attributes differ when converted? */
+               if (input_oi->sizep || input_oi->contentp) {
+                       new_oi.contentp = &content;
+                       new_oi.sizep = &size;
+                       new_oi.type_name = &type_name;
+               }
+               oi = &new_oi;
+       }
+
+       ret = oid_object_info_extended(r, &oid, oi, flags);
+       if (ret)
+               return -1;
+       if (oi == input_oi)
+               return ret;
+
+       if (new_oi.contentp) {
+               struct strbuf outbuf = STRBUF_INIT;
+               enum object_type type;
+
+               type = type_from_string_gently(type_name.buf, type_name.len,
+                                              !do_die);
+               if (type == -1)
+                       return -1;
+               if (type != OBJ_BLOB) {
+                       ret = convert_object_file(&outbuf,
+                                                 the_hash_algo, input_algo,
+                                                 content, size, type, !do_die);
+                       if (ret == -1)
+                               return -1;
+                       free(content);
+                       size = outbuf.len;
+                       content = strbuf_detach(&outbuf, NULL);
+               }
+               if (input_oi->sizep)
+                       *input_oi->sizep = size;
+               if (input_oi->contentp)
+                       *input_oi->contentp = content;
+               else
+                       free(content);
+               if (input_oi->type_name)
+                       *input_oi->type_name = type_name;
+               else
+                       strbuf_release(&type_name);
+       }
+       if (new_oi.delta_base_oid == &delta_base_oid) {
+               if (repo_oid_to_algop(r, &delta_base_oid, input_algo,
+                                input_oi->delta_base_oid)) {
+                       if (do_die)
+                               die(_("missing mapping of %s to %s"),
+                                   oid_to_hex(&delta_base_oid),
+                                   input_algo->name);
+                       return -1;
+               }
+       }
+       input_oi->whence = new_oi.whence;
+       input_oi->u = new_oi.u;
+       return ret;
+}
+
 int oid_object_info_extended(struct repository *r, const struct object_id *oid,
                             struct object_info *oi, unsigned flags)
 {
        int ret;
+
+       if (oid->algo && (hash_algo_by_ptr(r->hash_algo) != oid->algo))
+               return oid_object_info_convert(r, oid, oi, flags);
+
        obj_read_lock();
        ret = do_oid_object_info_extended(r, oid, oi, flags);
        obj_read_unlock();
@@ -1944,9 +2039,12 @@ static int start_loose_object_common(struct strbuf *tmp_file,
                                     const char *filename, unsigned flags,
                                     git_zstream *stream,
                                     unsigned char *buf, size_t buflen,
-                                    git_hash_ctx *c,
+                                    git_hash_ctx *c, git_hash_ctx *compat_c,
                                     char *hdr, int hdrlen)
 {
+       struct repository *repo = the_repository;
+       const struct git_hash_algo *algo = repo->hash_algo;
+       const struct git_hash_algo *compat = repo->compat_hash_algo;
        int fd;
 
        fd = create_tmpfile(tmp_file, filename);
@@ -1966,14 +2064,18 @@ static int start_loose_object_common(struct strbuf *tmp_file,
        git_deflate_init(stream, zlib_compression_level);
        stream->next_out = buf;
        stream->avail_out = buflen;
-       the_hash_algo->init_fn(c);
+       algo->init_fn(c);
+       if (compat && compat_c)
+               compat->init_fn(compat_c);
 
        /*  Start to feed header to zlib stream */
        stream->next_in = (unsigned char *)hdr;
        stream->avail_in = hdrlen;
        while (git_deflate(stream, 0) == Z_OK)
                ; /* nothing */
-       the_hash_algo->update_fn(c, hdr, hdrlen);
+       algo->update_fn(c, hdr, hdrlen);
+       if (compat && compat_c)
+               compat->update_fn(compat_c, hdr, hdrlen);
 
        return fd;
 }
@@ -1982,16 +2084,21 @@ static int start_loose_object_common(struct strbuf *tmp_file,
  * Common steps for the inner git_deflate() loop for writing loose
  * objects. Returns what git_deflate() returns.
  */
-static int write_loose_object_common(git_hash_ctx *c,
+static int write_loose_object_common(git_hash_ctx *c, git_hash_ctx *compat_c,
                                     git_zstream *stream, const int flush,
                                     unsigned char *in0, const int fd,
                                     unsigned char *compressed,
                                     const size_t compressed_len)
 {
+       struct repository *repo = the_repository;
+       const struct git_hash_algo *algo = repo->hash_algo;
+       const struct git_hash_algo *compat = repo->compat_hash_algo;
        int ret;
 
        ret = git_deflate(stream, flush ? Z_FINISH : 0);
-       the_hash_algo->update_fn(c, in0, stream->next_in - in0);
+       algo->update_fn(c, in0, stream->next_in - in0);
+       if (compat && compat_c)
+               compat->update_fn(compat_c, in0, stream->next_in - in0);
        if (write_in_full(fd, compressed, stream->next_out - compressed) < 0)
                die_errno(_("unable to write loose object file"));
        stream->next_out = compressed;
@@ -2006,15 +2113,21 @@ static int write_loose_object_common(git_hash_ctx *c,
  * - End the compression of zlib stream.
  * - Get the calculated oid to "oid".
  */
-static int end_loose_object_common(git_hash_ctx *c, git_zstream *stream,
-                                  struct object_id *oid)
+static int end_loose_object_common(git_hash_ctx *c, git_hash_ctx *compat_c,
+                                  git_zstream *stream, struct object_id *oid,
+                                  struct object_id *compat_oid)
 {
+       struct repository *repo = the_repository;
+       const struct git_hash_algo *algo = repo->hash_algo;
+       const struct git_hash_algo *compat = repo->compat_hash_algo;
        int ret;
 
        ret = git_deflate_end_gently(stream);
        if (ret != Z_OK)
                return ret;
-       the_hash_algo->final_oid_fn(oid, c);
+       algo->final_oid_fn(oid, c);
+       if (compat && compat_c)
+               compat->final_oid_fn(compat_oid, compat_c);
 
        return Z_OK;
 }
@@ -2038,7 +2151,7 @@ static int write_loose_object(const struct object_id *oid, char *hdr,
 
        fd = start_loose_object_common(&tmp_file, filename.buf, flags,
                                       &stream, compressed, sizeof(compressed),
-                                      &c, hdr, hdrlen);
+                                      &c, NULL, hdr, hdrlen);
        if (fd < 0)
                return -1;
 
@@ -2048,14 +2161,14 @@ static int write_loose_object(const struct object_id *oid, char *hdr,
        do {
                unsigned char *in0 = stream.next_in;
 
-               ret = write_loose_object_common(&c, &stream, 1, in0, fd,
+               ret = write_loose_object_common(&c, NULL, &stream, 1, in0, fd,
                                                compressed, sizeof(compressed));
        } while (ret == Z_OK);
 
        if (ret != Z_STREAM_END)
                die(_("unable to deflate new object %s (%d)"), oid_to_hex(oid),
                    ret);
-       ret = end_loose_object_common(&c, &stream, &parano_oid);
+       ret = end_loose_object_common(&c, NULL, &stream, &parano_oid, NULL);
        if (ret != Z_OK)
                die(_("deflateEnd on object %s failed (%d)"), oid_to_hex(oid),
                    ret);
@@ -2100,10 +2213,12 @@ static int freshen_packed_object(const struct object_id *oid)
 int stream_loose_object(struct input_stream *in_stream, size_t len,
                        struct object_id *oid)
 {
+       const struct git_hash_algo *compat = the_repository->compat_hash_algo;
+       struct object_id compat_oid;
        int fd, ret, err = 0, flush = 0;
        unsigned char compressed[4096];
        git_zstream stream;
-       git_hash_ctx c;
+       git_hash_ctx c, compat_c;
        struct strbuf tmp_file = STRBUF_INIT;
        struct strbuf filename = STRBUF_INIT;
        int dirlen;
@@ -2127,7 +2242,7 @@ int stream_loose_object(struct input_stream *in_stream, size_t len,
         */
        fd = start_loose_object_common(&tmp_file, filename.buf, 0,
                                       &stream, compressed, sizeof(compressed),
-                                      &c, hdr, hdrlen);
+                                      &c, &compat_c, hdr, hdrlen);
        if (fd < 0) {
                err = -1;
                goto cleanup;
@@ -2145,7 +2260,7 @@ int stream_loose_object(struct input_stream *in_stream, size_t len,
                        if (in_stream->is_finished)
                                flush = 1;
                }
-               ret = write_loose_object_common(&c, &stream, flush, in0, fd,
+               ret = write_loose_object_common(&c, &compat_c, &stream, flush, in0, fd,
                                                compressed, sizeof(compressed));
                /*
                 * Unlike write_loose_object(), we do not have the entire
@@ -2168,7 +2283,7 @@ int stream_loose_object(struct input_stream *in_stream, size_t len,
         */
        if (ret != Z_STREAM_END)
                die(_("unable to stream deflate new object (%d)"), ret);
-       ret = end_loose_object_common(&c, &stream, oid);
+       ret = end_loose_object_common(&c, &compat_c, &stream, oid, &compat_oid);
        if (ret != Z_OK)
                die(_("deflateEnd on stream object failed (%d)"), ret);
        close_loose_object(fd, tmp_file.buf);
@@ -2195,6 +2310,8 @@ int stream_loose_object(struct input_stream *in_stream, size_t len,
        }
 
        err = finalize_object_file(tmp_file.buf, filename.buf);
+       if (!err && compat)
+               err = repo_add_loose_object_map(the_repository, oid, &compat_oid);
 cleanup:
        strbuf_release(&tmp_file);
        strbuf_release(&filename);
@@ -2203,19 +2320,42 @@ cleanup:
 
 int write_object_file_flags(const void *buf, unsigned long len,
                            enum object_type type, struct object_id *oid,
-                           unsigned flags)
+                           struct object_id *compat_oid_in, unsigned flags)
 {
+       struct repository *repo = the_repository;
+       const struct git_hash_algo *algo = repo->hash_algo;
+       const struct git_hash_algo *compat = repo->compat_hash_algo;
+       struct object_id compat_oid;
        char hdr[MAX_HEADER_LEN];
        int hdrlen = sizeof(hdr);
 
+       /* Generate compat_oid */
+       if (compat) {
+               if (compat_oid_in)
+                       oidcpy(&compat_oid, compat_oid_in);
+               else if (type == OBJ_BLOB)
+                       hash_object_file(compat, buf, len, type, &compat_oid);
+               else {
+                       struct strbuf converted = STRBUF_INIT;
+                       convert_object_file(&converted, algo, compat,
+                                           buf, len, type, 0);
+                       hash_object_file(compat, converted.buf, converted.len,
+                                        type, &compat_oid);
+                       strbuf_release(&converted);
+               }
+       }
+
        /* Normally if we have it in the pack then we do not bother writing
         * it out into .git/objects/??/?{38} file.
         */
-       write_object_file_prepare(the_hash_algo, buf, len, type, oid, hdr,
-                                 &hdrlen);
+       write_object_file_prepare(algo, buf, len, type, oid, hdr, &hdrlen);
        if (freshen_packed_object(oid) || freshen_loose_object(oid))
                return 0;
-       return write_loose_object(oid, hdr, hdrlen, buf, len, 0, flags);
+       if (write_loose_object(oid, hdr, hdrlen, buf, len, 0, flags))
+               return -1;
+       if (compat)
+               return repo_add_loose_object_map(repo, oid, &compat_oid);
+       return 0;
 }
 
 int write_object_file_literally(const void *buf, unsigned long len,
@@ -2223,7 +2363,27 @@ int write_object_file_literally(const void *buf, unsigned long len,
                                unsigned flags)
 {
        char *header;
+       struct repository *repo = the_repository;
+       const struct git_hash_algo *algo = repo->hash_algo;
+       const struct git_hash_algo *compat = repo->compat_hash_algo;
+       struct object_id compat_oid;
        int hdrlen, status = 0;
+       int compat_type = -1;
+
+       if (compat) {
+               compat_type = type_from_string_gently(type, -1, 1);
+               if (compat_type == OBJ_BLOB)
+                       hash_object_file(compat, buf, len, compat_type,
+                                        &compat_oid);
+               else if (compat_type != -1) {
+                       struct strbuf converted = STRBUF_INIT;
+                       convert_object_file(&converted, algo, compat,
+                                           buf, len, compat_type, 0);
+                       hash_object_file(compat, converted.buf, converted.len,
+                                        compat_type, &compat_oid);
+                       strbuf_release(&converted);
+               }
+       }
 
        /* type string, SP, %lu of the length plus NUL must fit this */
        hdrlen = strlen(type) + MAX_HEADER_LEN;
@@ -2236,6 +2396,8 @@ int write_object_file_literally(const void *buf, unsigned long len,
        if (freshen_packed_object(oid) || freshen_loose_object(oid))
                goto cleanup;
        status = write_loose_object(oid, header, hdrlen, buf, len, 0, 0);
+       if (compat_type != -1)
+               return repo_add_loose_object_map(repo, oid, &compat_oid);
 
 cleanup:
        free(header);
@@ -2244,9 +2406,12 @@ cleanup:
 
 int force_object_loose(const struct object_id *oid, time_t mtime)
 {
+       struct repository *repo = the_repository;
+       const struct git_hash_algo *compat = repo->compat_hash_algo;
        void *buf;
        unsigned long len;
        struct object_info oi = OBJECT_INFO_INIT;
+       struct object_id compat_oid;
        enum object_type type;
        char hdr[MAX_HEADER_LEN];
        int hdrlen;
@@ -2259,8 +2424,15 @@ int force_object_loose(const struct object_id *oid, time_t mtime)
        oi.contentp = &buf;
        if (oid_object_info_extended(the_repository, oid, &oi, 0))
                return error(_("cannot read object for %s"), oid_to_hex(oid));
+       if (compat) {
+               if (repo_oid_to_algop(repo, oid, compat, &compat_oid))
+                       return error(_("cannot map object %s to %s"),
+                                    oid_to_hex(oid), compat->name);
+       }
        hdrlen = format_object_header(hdr, sizeof(hdr), type, len);
        ret = write_loose_object(oid, hdr, hdrlen, buf, len, mtime, 0);
+       if (!ret && compat)
+               ret = repo_add_loose_object_map(the_repository, oid, &compat_oid);
        free(buf);
 
        return ret;
index bd77695d7eac539ff114c4b6a031cdd81931d7d8..523af6f64f33512d1d7587bf0ce96cbe23b63b94 100644 (file)
@@ -23,6 +23,7 @@
 #include "midx.h"
 #include "commit-reach.h"
 #include "date.h"
+#include "object-file-convert.h"
 
 static int get_oid_oneline(struct repository *r, const char *, struct object_id *, struct commit_list *);
 
@@ -47,6 +48,7 @@ struct disambiguate_state {
 
 static void update_candidates(struct disambiguate_state *ds, const struct object_id *current)
 {
+       /* The hash algorithm of current has already been filtered */
        if (ds->always_call_fn) {
                ds->ambiguous = ds->fn(ds->repo, current, ds->cb_data) ? 1 : 0;
                return;
@@ -132,6 +134,8 @@ static void unique_in_midx(struct multi_pack_index *m,
 {
        uint32_t num, i, first = 0;
        const struct object_id *current = NULL;
+       int len = ds->len > ds->repo->hash_algo->hexsz ?
+               ds->repo->hash_algo->hexsz : ds->len;
        num = m->num_objects;
 
        if (!num)
@@ -147,7 +151,7 @@ static void unique_in_midx(struct multi_pack_index *m,
        for (i = first; i < num && !ds->ambiguous; i++) {
                struct object_id oid;
                current = nth_midxed_object_oid(&oid, m, i);
-               if (!match_hash(ds->len, ds->bin_pfx.hash, current->hash))
+               if (!match_hash(len, ds->bin_pfx.hash, current->hash))
                        break;
                update_candidates(ds, current);
        }
@@ -157,6 +161,8 @@ static void unique_in_pack(struct packed_git *p,
                           struct disambiguate_state *ds)
 {
        uint32_t num, i, first = 0;
+       int len = ds->len > ds->repo->hash_algo->hexsz ?
+               ds->repo->hash_algo->hexsz : ds->len;
 
        if (p->multi_pack_index)
                return;
@@ -175,7 +181,7 @@ static void unique_in_pack(struct packed_git *p,
        for (i = first; i < num && !ds->ambiguous; i++) {
                struct object_id oid;
                nth_packed_object_id(&oid, p, i);
-               if (!match_hash(ds->len, ds->bin_pfx.hash, oid.hash))
+               if (!match_hash(len, ds->bin_pfx.hash, oid.hash))
                        break;
                update_candidates(ds, &oid);
        }
@@ -186,6 +192,10 @@ static void find_short_packed_object(struct disambiguate_state *ds)
        struct multi_pack_index *m;
        struct packed_git *p;
 
+       /* Skip, unless oids from the storage hash algorithm are wanted */
+       if (ds->bin_pfx.algo && (&hash_algos[ds->bin_pfx.algo] != ds->repo->hash_algo))
+               return;
+
        for (m = get_multi_pack_index(ds->repo); m && !ds->ambiguous;
             m = m->next)
                unique_in_midx(m, ds);
@@ -324,11 +334,12 @@ int set_disambiguate_hint_config(const char *var, const char *value)
 
 static int init_object_disambiguation(struct repository *r,
                                      const char *name, int len,
+                                     const struct git_hash_algo *algo,
                                      struct disambiguate_state *ds)
 {
        int i;
 
-       if (len < MINIMUM_ABBREV || len > the_hash_algo->hexsz)
+       if (len < MINIMUM_ABBREV || len > GIT_MAX_HEXSZ)
                return -1;
 
        memset(ds, 0, sizeof(*ds));
@@ -355,6 +366,7 @@ static int init_object_disambiguation(struct repository *r,
        ds->len = len;
        ds->hex_pfx[len] = '\0';
        ds->repo = r;
+       ds->bin_pfx.algo = algo ? hash_algo_by_ptr(algo) : GIT_HASH_UNKNOWN;
        prepare_alt_odb(r);
        return 0;
 }
@@ -489,9 +501,10 @@ static int repo_collect_ambiguous(struct repository *r UNUSED,
        return collect_ambiguous(oid, data);
 }
 
-static int sort_ambiguous(const void *a, const void *b, void *ctx)
+static int sort_ambiguous(const void *va, const void *vb, void *ctx)
 {
        struct repository *sort_ambiguous_repo = ctx;
+       const struct object_id *a = va, *b = vb;
        int a_type = oid_object_info(sort_ambiguous_repo, a, NULL);
        int b_type = oid_object_info(sort_ambiguous_repo, b, NULL);
        int a_type_sort;
@@ -501,8 +514,12 @@ static int sort_ambiguous(const void *a, const void *b, void *ctx)
         * Sorts by hash within the same object type, just as
         * oid_array_for_each_unique() would do.
         */
-       if (a_type == b_type)
-               return oidcmp(a, b);
+       if (a_type == b_type) {
+               if (a->algo == b->algo)
+                       return oidcmp(a, b);
+               else
+                       return a->algo > b->algo ? 1 : -1;
+       }
 
        /*
         * Between object types show tags, then commits, and finally
@@ -531,8 +548,12 @@ static enum get_oid_result get_short_oid(struct repository *r,
        int status;
        struct disambiguate_state ds;
        int quietly = !!(flags & GET_OID_QUIETLY);
+       const struct git_hash_algo *algo = r->hash_algo;
+
+       if (flags & GET_OID_HASH_ANY)
+               algo = NULL;
 
-       if (init_object_disambiguation(r, name, len, &ds) < 0)
+       if (init_object_disambiguation(r, name, len, algo, &ds) < 0)
                return -1;
 
        if (HAS_MULTI_BITS(flags & GET_OID_DISAMBIGUATORS))
@@ -586,7 +607,7 @@ static enum get_oid_result get_short_oid(struct repository *r,
                if (!ds.ambiguous)
                        ds.fn = NULL;
 
-               repo_for_each_abbrev(r, ds.hex_pfx, collect_ambiguous, &collect);
+               repo_for_each_abbrev(r, ds.hex_pfx, algo, collect_ambiguous, &collect);
                sort_ambiguous_oid_array(r, &collect);
 
                if (oid_array_for_each(&collect, show_ambiguous_object, &out))
@@ -608,13 +629,14 @@ static enum get_oid_result get_short_oid(struct repository *r,
 }
 
 int repo_for_each_abbrev(struct repository *r, const char *prefix,
+                        const struct git_hash_algo *algo,
                         each_abbrev_fn fn, void *cb_data)
 {
        struct oid_array collect = OID_ARRAY_INIT;
        struct disambiguate_state ds;
        int ret;
 
-       if (init_object_disambiguation(r, prefix, strlen(prefix), &ds) < 0)
+       if (init_object_disambiguation(r, prefix, strlen(prefix), algo, &ds) < 0)
                return -1;
 
        ds.always_call_fn = 1;
@@ -785,10 +807,12 @@ void strbuf_add_unique_abbrev(struct strbuf *sb, const struct object_id *oid,
 int repo_find_unique_abbrev_r(struct repository *r, char *hex,
                              const struct object_id *oid, int len)
 {
+       const struct git_hash_algo *algo =
+               oid->algo ? &hash_algos[oid->algo] : r->hash_algo;
        struct disambiguate_state ds;
        struct min_abbrev_data mad;
        struct object_id oid_ret;
-       const unsigned hexsz = r->hash_algo->hexsz;
+       const unsigned hexsz = algo->hexsz;
 
        if (len < 0) {
                unsigned long count = repo_approximate_object_count(r);
@@ -824,7 +848,7 @@ int repo_find_unique_abbrev_r(struct repository *r, char *hex,
 
        find_abbrev_len_packed(&mad);
 
-       if (init_object_disambiguation(r, hex, mad.cur_len, &ds) < 0)
+       if (init_object_disambiguation(r, hex, mad.cur_len, algo, &ds) < 0)
                return -1;
 
        ds.fn = repo_extend_abbrev_len;
index 9ae52230714876b719dd3d48755ad3885030d07a..064ddc97d1fe991c98272802c9eff65c8445a49b 100644 (file)
@@ -67,7 +67,8 @@ enum get_oid_result get_oid_with_context(struct repository *repo, const char *st
 
 
 typedef int each_abbrev_fn(const struct object_id *oid, void *);
-int repo_for_each_abbrev(struct repository *r, const char *prefix, each_abbrev_fn, void *);
+int repo_for_each_abbrev(struct repository *r, const char *prefix,
+                        const struct git_hash_algo *algo, each_abbrev_fn, void *);
 
 int set_disambiguate_hint_config(const char *var, const char *value);
 
index 26a3895c821c61620d5988b2e62a77dc9513d025..c5f2bb2fc2fe6eb36cafa559b038dcd3095395da 100644 (file)
@@ -26,6 +26,9 @@ struct object_directory {
        uint32_t loose_objects_subdir_seen[8]; /* 256 bits */
        struct oidtree *loose_objects_cache;
 
+       /* Map between object IDs for loose objects. */
+       struct loose_object_map *loose_map;
+
        /*
         * This is a temporary object store created by the tmp_objdir
         * facility. Disable ref updates since the objects in the store
@@ -252,11 +255,11 @@ void hash_object_file(const struct git_hash_algo *algo, const void *buf,
 
 int write_object_file_flags(const void *buf, unsigned long len,
                            enum object_type type, struct object_id *oid,
-                           unsigned flags);
+                           struct object_id *comapt_oid_in, unsigned flags);
 static inline int write_object_file(const void *buf, unsigned long len,
                                    enum object_type type, struct object_id *oid)
 {
-       return write_object_file_flags(buf, len, type, oid, 0);
+       return write_object_file_flags(buf, len, type, oid, NULL, 0);
 }
 
 int write_object_file_literally(const void *buf, unsigned long len,
index f11c59ac0cf4d41e3c19f48e04af47237797db03..51e384828e96efa9b3a9cbf96f5cf8b1b719856e 100644 (file)
--- a/object.c
+++ b/object.c
@@ -13,6 +13,7 @@
 #include "alloc.h"
 #include "packfile.h"
 #include "commit-graph.h"
+#include "loose.h"
 
 unsigned int get_max_object_index(void)
 {
@@ -553,6 +554,7 @@ void free_object_directory(struct object_directory *odb)
 {
        free(odb->path);
        odb_clear_loose_cache(odb);
+       loose_object_map_clear(&odb->loose_map);
        free(odb);
 }
 
index c7123cade622cd2a5b6aafc99ad924aafeaaa301..9293e703cccc6acf6205be9c5fae440fff441d5a 100644 (file)
--- a/object.h
+++ b/object.h
@@ -190,6 +190,24 @@ void *create_object(struct repository *r, const struct object_id *oid, void *obj
 
 void *object_as_type(struct object *obj, enum object_type type, int quiet);
 
+
+static inline const char *parse_mode(const char *str, uint16_t *modep)
+{
+       unsigned char c;
+       unsigned int mode = 0;
+
+       if (*str == ' ')
+               return NULL;
+
+       while ((c = *str++) != ' ') {
+               if (c < '0' || c > '7')
+                       return NULL;
+               mode = (mode << 3) + (c - '0');
+       }
+       *modep = mode;
+       return str;
+}
+
 /*
  * Returns the object, having parsed it to find out what it is.
  *
index 8e4717746c3183bd47c3c746c747efd83c61168c..1f36651754edc47f733bc51bbecd716deee31ed4 100644 (file)
@@ -6,12 +6,20 @@ void oid_array_append(struct oid_array *array, const struct object_id *oid)
 {
        ALLOC_GROW(array->oid, array->nr + 1, array->alloc);
        oidcpy(&array->oid[array->nr++], oid);
+       if (!oid->algo)
+               oid_set_algo(&array->oid[array->nr - 1], the_hash_algo);
        array->sorted = 0;
 }
 
-static int void_hashcmp(const void *a, const void *b)
+static int void_hashcmp(const void *va, const void *vb)
 {
-       return oidcmp(a, b);
+       const struct object_id *a = va, *b = vb;
+       int ret;
+       if (a->algo == b->algo)
+               ret = oidcmp(a, b);
+       else
+               ret = a->algo > b->algo ? 1 : -1;
+       return ret;
 }
 
 void oid_array_sort(struct oid_array *array)
index 5b954088254b21a6cbc90b41d0dac415a552d6b7..a877c11f42b2d25550457b55c2c021985adacd1f 100644 (file)
@@ -1,4 +1,5 @@
 fuzz-commit-graph
+fuzz-config
 fuzz-date
 fuzz-pack-headers
 fuzz-pack-idx
diff --git a/oss-fuzz/fuzz-config.c b/oss-fuzz/fuzz-config.c
new file mode 100644 (file)
index 0000000..94027f5
--- /dev/null
@@ -0,0 +1,33 @@
+#include "git-compat-util.h"
+#include "config.h"
+
+int LLVMFuzzerTestOneInput(const uint8_t *, size_t);
+static int config_parser_callback(const char *, const char *,
+                                       const struct config_context *, void *);
+
+static int config_parser_callback(const char *key, const char *value,
+                                       const struct config_context *ctx UNUSED,
+                                       void *data UNUSED)
+{
+       /*
+        * Visit every byte of memory we are given to make sure the parser
+        * gave it to us appropriately. We need to unconditionally return 0,
+        * but we also want to prevent the strlen from being optimized away.
+        */
+       size_t c = strlen(key);
+
+       if (value)
+               c += strlen(value);
+       return c == SIZE_MAX;
+}
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, const size_t size)
+{
+       struct config_options config_opts = { 0 };
+
+       config_opts.error_action = CONFIG_ERROR_SILENT;
+       git_config_from_mem(config_parser_callback, CONFIG_ORIGIN_BLOB,
+                               "fuzztest-config", (const char *)data, size, NULL,
+                               CONFIG_SCOPE_UNKNOWN, &config_opts);
+       return 0;
+}
index 990a9498d731942bf2b01580a8ac77d08ba04d5f..c6c8f94cc514476a5a6f543cbb86a260020e9753 100644 (file)
@@ -370,7 +370,7 @@ static int fill_bitmap_tree(struct bitmap *bitmap,
        if (parse_tree(tree) < 0)
                die("unable to load tree object %s",
                    oid_to_hex(&tree->object.oid));
-       init_tree_desc(&desc, tree->buffer, tree->size);
+       init_tree_desc(&desc, &tree->object.oid, tree->buffer, tree->size);
 
        while (tree_entry(&desc, &entry)) {
                switch (object_type(entry.mode)) {
index 84a005674d8749407afffc1f6fd670501ce764d4..d4df7fdeea56ffe63fd1738d227b509bca2d553a 100644 (file)
@@ -2249,7 +2249,8 @@ static int add_promisor_object(const struct object_id *oid,
                struct tree *tree = (struct tree *)obj;
                struct tree_desc desc;
                struct name_entry entry;
-               if (init_tree_desc_gently(&desc, tree->buffer, tree->size, 0))
+               if (init_tree_desc_gently(&desc, &tree->object.oid,
+                                         tree->buffer, tree->size, 0))
                        /*
                         * Error messages are given when packs are
                         * verified, so do not print any here.
index bdbed4295aab2f0309eb872170251e2e9b185bbf..2faf651d3ef2afc237bb755c9c535d9bd9b60295 100644 (file)
--- a/pretty.c
+++ b/pretty.c
@@ -147,7 +147,7 @@ static struct cmt_fmt_map *find_commit_format_recursive(const char *sought,
        for (i = 0; i < commit_formats_len; i++) {
                size_t match_len;
 
-               if (!starts_with(commit_formats[i].name, sought))
+               if (!istarts_with(commit_formats[i].name, sought))
                        continue;
 
                match_len = strlen(commit_formats[i].name);
@@ -2077,11 +2077,11 @@ static void pp_header(struct pretty_print_context *pp,
        }
 }
 
-void pp_title_line(struct pretty_print_context *pp,
-                  const char **msg_p,
-                  struct strbuf *sb,
-                  const char *encoding,
-                  int need_8bit_cte)
+void pp_email_subject(struct pretty_print_context *pp,
+                     const char **msg_p,
+                     struct strbuf *sb,
+                     const char *encoding,
+                     int need_8bit_cte)
 {
        static const int max_length = 78; /* per rfc2047 */
        struct strbuf title;
@@ -2091,19 +2091,14 @@ void pp_title_line(struct pretty_print_context *pp,
                                pp->preserve_subject ? "\n" : " ");
 
        strbuf_grow(sb, title.len + 1024);
-       if (pp->print_email_subject) {
-               if (pp->rev)
-                       fmt_output_email_subject(sb, pp->rev);
-               if (pp->encode_email_headers &&
-                   needs_rfc2047_encoding(title.buf, title.len))
-                       add_rfc2047(sb, title.buf, title.len,
-                                               encoding, RFC2047_SUBJECT);
-               else
-                       strbuf_add_wrapped_bytes(sb, title.buf, title.len,
+       fmt_output_email_subject(sb, pp->rev);
+       if (pp->encode_email_headers &&
+           needs_rfc2047_encoding(title.buf, title.len))
+               add_rfc2047(sb, title.buf, title.len,
+                           encoding, RFC2047_SUBJECT);
+       else
+               strbuf_add_wrapped_bytes(sb, title.buf, title.len,
                                         -last_line_length(sb), 1, max_length);
-       } else {
-               strbuf_addbuf(sb, &title);
-       }
        strbuf_addch(sb, '\n');
 
        if (need_8bit_cte == 0) {
@@ -2126,9 +2121,8 @@ void pp_title_line(struct pretty_print_context *pp,
        if (pp->after_subject) {
                strbuf_addstr(sb, pp->after_subject);
        }
-       if (cmit_fmt_is_mail(pp->fmt)) {
-               strbuf_addch(sb, '\n');
-       }
+
+       strbuf_addch(sb, '\n');
 
        if (pp->in_body_headers.nr) {
                int i;
@@ -2320,7 +2314,7 @@ void pretty_print_commit(struct pretty_print_context *pp,
        }
 
        pp_header(pp, encoding, commit, &msg, sb);
-       if (pp->fmt != CMIT_FMT_ONELINE && !pp->print_email_subject) {
+       if (pp->fmt != CMIT_FMT_ONELINE && !cmit_fmt_is_mail(pp->fmt)) {
                strbuf_addch(sb, '\n');
        }
 
@@ -2328,8 +2322,11 @@ void pretty_print_commit(struct pretty_print_context *pp,
        msg = skip_blank_lines(msg);
 
        /* These formats treat the title line specially. */
-       if (pp->fmt == CMIT_FMT_ONELINE || cmit_fmt_is_mail(pp->fmt))
-               pp_title_line(pp, &msg, sb, encoding, need_8bit_cte);
+       if (pp->fmt == CMIT_FMT_ONELINE) {
+               msg = format_subject(sb, msg, " ");
+               strbuf_addch(sb, '\n');
+       } else if (cmit_fmt_is_mail(pp->fmt))
+               pp_email_subject(pp, &msg, sb, encoding, need_8bit_cte);
 
        beginning_of_body = sb->len;
        if (pp->fmt != CMIT_FMT_ONELINE)
index 421209e9ec2732e1a08b130a6895d2688a28f52e..9cc9e5d42b8525a40698420d8a3a6ad09742498a 100644 (file)
--- a/pretty.h
+++ b/pretty.h
@@ -35,11 +35,10 @@ struct pretty_print_context {
         */
        enum cmit_fmt fmt;
        int abbrev;
-       const char *after_subject;
+       char *after_subject;
        int preserve_subject;
        struct date_mode date_mode;
        unsigned date_mode_explicit:1;
-       int print_email_subject;
        int expand_tabs_in_log;
        int need_8bit_cte;
        char *notes_message;
@@ -96,13 +95,13 @@ void pp_user_info(struct pretty_print_context *pp, const char *what,
                        const char *encoding);
 
 /*
- * Format title line of commit message taken from "msg_p" and
+ * Format subject line of commit message taken from "msg_p" and
  * put it into "sb".
  * First line of "msg_p" is also affected.
  */
-void pp_title_line(struct pretty_print_context *pp, const char **msg_p,
-                       struct strbuf *sb, const char *encoding,
-                       int need_8bit_cte);
+void pp_email_subject(struct pretty_print_context *pp, const char **msg_p,
+                     struct strbuf *sb, const char *encoding,
+                     int need_8bit_cte);
 
 /*
  * Get current state of commit message from "msg_p" and continue formatting
index d9718409b3d7862fee0eaa7e2255e83f50379656..c343e16fcddddec8601a2278ae3b5e39dbab770c 100644 (file)
@@ -71,14 +71,14 @@ void append_todo_help(int command_count,
 
        if (!edit_todo) {
                strbuf_addch(buf, '\n');
-               strbuf_commented_addf(buf, comment_line_char,
+               strbuf_commented_addf(buf, comment_line_str,
                                      Q_("Rebase %s onto %s (%d command)",
                                         "Rebase %s onto %s (%d commands)",
                                         command_count),
                                      shortrevisions, shortonto, command_count);
        }
 
-       strbuf_add_commented_lines(buf, msg, strlen(msg), comment_line_char);
+       strbuf_add_commented_lines(buf, msg, strlen(msg), comment_line_str);
 
        if (get_missing_commit_check_level() == MISSING_COMMIT_CHECK_ERROR)
                msg = _("\nDo not remove any line. Use 'drop' "
@@ -87,7 +87,7 @@ void append_todo_help(int command_count,
                msg = _("\nIf you remove a line here "
                         "THAT COMMIT WILL BE LOST.\n");
 
-       strbuf_add_commented_lines(buf, msg, strlen(msg), comment_line_char);
+       strbuf_add_commented_lines(buf, msg, strlen(msg), comment_line_str);
 
        if (edit_todo)
                msg = _("\nYou are editing the todo file "
@@ -98,7 +98,7 @@ void append_todo_help(int command_count,
                msg = _("\nHowever, if you remove everything, "
                        "the rebase will be aborted.\n\n");
 
-       strbuf_add_commented_lines(buf, msg, strlen(msg), comment_line_char);
+       strbuf_add_commented_lines(buf, msg, strlen(msg), comment_line_str);
 }
 
 int edit_todo_list(struct repository *r, struct todo_list *todo_list,
@@ -130,7 +130,7 @@ int edit_todo_list(struct repository *r, struct todo_list *todo_list,
        if (launch_sequence_editor(todo_file, &new_todo->buf, NULL))
                return -2;
 
-       strbuf_stripspace(&new_todo->buf, comment_line_char);
+       strbuf_stripspace(&new_todo->buf, comment_line_str);
        if (initial && new_todo->buf.len == 0)
                return -3;
 
index 0a1bc35e8cd2c82771c0272dcdc08f8b99ba73e7..647f3ca398a32711eb940281fc05bad43cb303a6 100644 (file)
--- a/reflog.c
+++ b/reflog.c
@@ -39,7 +39,7 @@ static int tree_is_complete(const struct object_id *oid)
                tree->buffer = data;
                tree->size = size;
        }
-       init_tree_desc(&desc, tree->buffer, tree->size);
+       init_tree_desc(&desc, &tree->object.oid, tree->buffer, tree->size);
        complete = 1;
        while (tree_entry(&desc, &entry)) {
                if (!repo_has_object_file(the_repository, &entry.oid) ||
diff --git a/refs.h b/refs.h
index 298caf6c6184cc3a23acf78d1a0e3dc8c7d8614c..d278775e086bfa7990999c226ad1db2f488e890d 100644 (file)
--- a/refs.h
+++ b/refs.h
@@ -66,12 +66,6 @@ const char *ref_storage_format_to_name(unsigned int ref_storage_format);
 #define RESOLVE_REF_NO_RECURSE 0x02
 #define RESOLVE_REF_ALLOW_BAD_NAME 0x04
 
-struct pack_refs_opts {
-       unsigned int flags;
-       struct ref_exclusions *exclusions;
-       struct string_list *includes;
-};
-
 const char *refs_resolve_ref_unsafe(struct ref_store *refs,
                                    const char *refname,
                                    int resolve_flags,
@@ -428,10 +422,18 @@ void warn_dangling_symrefs(FILE *fp, const char *msg_fmt,
 /*
  * Flags for controlling behaviour of pack_refs()
  * PACK_REFS_PRUNE: Prune loose refs after packing
- * PACK_REFS_ALL:   Pack _all_ refs, not just tags and already packed refs
+ * PACK_REFS_AUTO: Pack refs on a best effort basis. The heuristics and end
+ *                 result are decided by the ref backend. Backends may ignore
+ *                 this flag and fall back to a normal repack.
  */
-#define PACK_REFS_PRUNE 0x0001
-#define PACK_REFS_ALL   0x0002
+#define PACK_REFS_PRUNE (1 << 0)
+#define PACK_REFS_AUTO  (1 << 1)
+
+struct pack_refs_opts {
+       unsigned int flags;
+       struct ref_exclusions *exclusions;
+       struct string_list *includes;
+};
 
 /*
  * Write a packed-refs file for the current repository.
index e206d5a073ced9c24d9020f03a674ca207abdd06..0bed6d2ab4844a1eb6b548c77577f3fc13105a81 100644 (file)
@@ -1203,9 +1203,16 @@ static int reftable_be_pack_refs(struct ref_store *ref_store,
        if (!stack)
                stack = refs->main_stack;
 
-       ret = reftable_stack_compact_all(stack, NULL);
-       if (ret)
+       if (opts->flags & PACK_REFS_AUTO)
+               ret = reftable_stack_auto_compact(stack);
+       else
+               ret = reftable_stack_compact_all(stack, NULL);
+       if (ret < 0) {
+               ret = error(_("unable to compact stack: %s"),
+                           reftable_error_str(ret));
                goto out;
+       }
+
        ret = reftable_stack_clean(stack);
        if (ret)
                goto out;
index 0d1766735e8ff0f1c5c057d1882728aa4292d1ee..cfb7a0fda4a24dc190892b22cffc713c3e41c2a3 100644 (file)
@@ -22,7 +22,7 @@ const char *reftable_error_str(int err)
        case REFTABLE_NOT_EXIST_ERROR:
                return "file does not exist";
        case REFTABLE_LOCK_ERROR:
-               return "data is outdated";
+               return "data is locked";
        case REFTABLE_API_ERROR:
                return "misuse of the reftable API";
        case REFTABLE_ZLIB_ERROR:
@@ -35,6 +35,8 @@ const char *reftable_error_str(int err)
                return "invalid refname";
        case REFTABLE_ENTRY_TOO_BIG_ERROR:
                return "entry too large";
+       case REFTABLE_OUTDATED_ERROR:
+               return "data concurrently modified";
        case -1:
                return "general error";
        default:
index 4c457aaaf8906384e65edfeb06057a86dcb594e4..e9b07c9f3623ec69db5447ced5474993f7b98d0f 100644 (file)
@@ -25,7 +25,7 @@ enum reftable_error {
         */
        REFTABLE_NOT_EXIST_ERROR = -4,
 
-       /* Trying to write out-of-date data. */
+       /* Trying to access locked data. */
        REFTABLE_LOCK_ERROR = -5,
 
        /* Misuse of the API:
@@ -57,6 +57,9 @@ enum reftable_error {
        /* Entry does not fit. This can happen when writing outsize reflog
           messages. */
        REFTABLE_ENTRY_TOO_BIG_ERROR = -11,
+
+       /* Trying to write out-of-date data. */
+       REFTABLE_OUTDATED_ERROR = -12,
 };
 
 /* convert the numeric error code to a string. The string should not be
index 1ecf1b9751ce4975580ab7deed2bbe2a9a7847bc..dde50b61d696befd29bb16452e0e2588f0823761 100644 (file)
@@ -529,9 +529,9 @@ int reftable_stack_add(struct reftable_stack *st,
 {
        int err = stack_try_add(st, write, arg);
        if (err < 0) {
-               if (err == REFTABLE_LOCK_ERROR) {
+               if (err == REFTABLE_OUTDATED_ERROR) {
                        /* Ignore error return, we want to propagate
-                          REFTABLE_LOCK_ERROR.
+                          REFTABLE_OUTDATED_ERROR.
                        */
                        reftable_stack_reload(st);
                }
@@ -590,9 +590,8 @@ static int reftable_stack_init_addition(struct reftable_addition *add,
        err = stack_uptodate(st);
        if (err < 0)
                goto done;
-
-       if (err > 1) {
-               err = REFTABLE_LOCK_ERROR;
+       if (err > 0) {
+               err = REFTABLE_OUTDATED_ERROR;
                goto done;
        }
 
@@ -681,8 +680,19 @@ int reftable_addition_commit(struct reftable_addition *add)
        if (err)
                goto done;
 
-       if (!add->stack->disable_auto_compact)
+       if (!add->stack->disable_auto_compact) {
+               /*
+                * Auto-compact the stack to keep the number of tables in
+                * control. It is possible that a concurrent writer is already
+                * trying to compact parts of the stack, which would lead to a
+                * `REFTABLE_LOCK_ERROR` because parts of the stack are locked
+                * already. This is a benign error though, so we ignore it.
+                */
                err = reftable_stack_auto_compact(add->stack);
+               if (err < 0 && err != REFTABLE_LOCK_ERROR)
+                       goto done;
+               err = 0;
+       }
 
 done:
        reftable_addition_close(add);
@@ -713,10 +723,6 @@ static int stack_try_add(struct reftable_stack *st,
        int err = reftable_stack_init_addition(&add, st);
        if (err < 0)
                goto done;
-       if (err > 0) {
-               err = REFTABLE_LOCK_ERROR;
-               goto done;
-       }
 
        err = reftable_addition_add(&add, write_table, arg);
        if (err < 0)
@@ -978,7 +984,15 @@ done:
        return err;
 }
 
-/* <  0: error. 0 == OK, > 0 attempt failed; could retry. */
+/*
+ * Compact all tables in the range `[first, last)` into a single new table.
+ *
+ * This function returns `0` on success or a code `< 0` on failure. When the
+ * stack or any of the tables in the specified range are already locked then
+ * this function returns `REFTABLE_LOCK_ERROR`. This is a benign error that
+ * callers can either ignore, or they may choose to retry compaction after some
+ * amount of time.
+ */
 static int stack_compact_range(struct reftable_stack *st,
                               size_t first, size_t last,
                               struct reftable_log_expiry_config *expiry)
@@ -1008,7 +1022,7 @@ static int stack_compact_range(struct reftable_stack *st,
                                        LOCK_NO_DEREF);
        if (err < 0) {
                if (errno == EEXIST)
-                       err = 1;
+                       err = REFTABLE_LOCK_ERROR;
                else
                        err = REFTABLE_IO_ERROR;
                goto done;
@@ -1030,7 +1044,7 @@ static int stack_compact_range(struct reftable_stack *st,
                                                table_name.buf, LOCK_NO_DEREF);
                if (err < 0) {
                        if (errno == EEXIST)
-                               err = 1;
+                               err = REFTABLE_LOCK_ERROR;
                        else
                                err = REFTABLE_IO_ERROR;
                        goto done;
@@ -1080,7 +1094,7 @@ static int stack_compact_range(struct reftable_stack *st,
                                        LOCK_NO_DEREF);
        if (err < 0) {
                if (errno == EEXIST)
-                       err = 1;
+                       err = REFTABLE_LOCK_ERROR;
                else
                        err = REFTABLE_IO_ERROR;
                goto done;
@@ -1192,7 +1206,7 @@ static int stack_compact_range_stats(struct reftable_stack *st,
                                     struct reftable_log_expiry_config *config)
 {
        int err = stack_compact_range(st, first, last, config);
-       if (err > 0)
+       if (err == REFTABLE_LOCK_ERROR)
                st->stats.failures++;
        return err;
 }
index 7336757cf534058dd6f3e8346d15874036c5b763..351e35bd86d8465ec7f741374845ae104bcf23f9 100644 (file)
@@ -38,7 +38,17 @@ static int count_dir_entries(const char *dirname)
                return 0;
 
        while ((d = readdir(dir))) {
-               if (!strcmp(d->d_name, "..") || !strcmp(d->d_name, "."))
+               /*
+                * Besides skipping over "." and "..", we also need to
+                * skip over other files that have a leading ".". This
+                * is due to behaviour of NFS, which will rename files
+                * to ".nfs*" to emulate delete-on-last-close.
+                *
+                * In any case this should be fine as the reftable
+                * library will never write files with leading dots
+                * anyway.
+                */
+               if (starts_with(d->d_name, "."))
                        continue;
                len++;
        }
@@ -232,7 +242,7 @@ static void test_reftable_stack_uptodate(void)
        EXPECT_ERR(err);
 
        err = reftable_stack_add(st2, &write_test_ref, &ref2);
-       EXPECT(err == REFTABLE_LOCK_ERROR);
+       EXPECT(err == REFTABLE_OUTDATED_ERROR);
 
        err = reftable_stack_reload(st2);
        EXPECT_ERR(err);
@@ -343,6 +353,49 @@ static void test_reftable_stack_transaction_api_performs_auto_compaction(void)
        clear_dir(dir);
 }
 
+static void test_reftable_stack_auto_compaction_fails_gracefully(void)
+{
+       struct reftable_ref_record ref = {
+               .refname = "refs/heads/master",
+               .update_index = 1,
+               .value_type = REFTABLE_REF_VAL1,
+               .value.val1 = {0x01},
+       };
+       struct reftable_write_options cfg = {0};
+       struct reftable_stack *st;
+       struct strbuf table_path = STRBUF_INIT;
+       char *dir = get_tmp_dir(__LINE__);
+       int err;
+
+       err = reftable_new_stack(&st, dir, cfg);
+       EXPECT_ERR(err);
+
+       err = reftable_stack_add(st, write_test_ref, &ref);
+       EXPECT_ERR(err);
+       EXPECT(st->merged->stack_len == 1);
+       EXPECT(st->stats.attempts == 0);
+       EXPECT(st->stats.failures == 0);
+
+       /*
+        * Lock the newly written table such that it cannot be compacted.
+        * Adding a new table to the stack should not be impacted by this, even
+        * though auto-compaction will now fail.
+        */
+       strbuf_addf(&table_path, "%s/%s.lock", dir, st->readers[0]->name);
+       write_file_buf(table_path.buf, "", 0);
+
+       ref.update_index = 2;
+       err = reftable_stack_add(st, write_test_ref, &ref);
+       EXPECT_ERR(err);
+       EXPECT(st->merged->stack_len == 2);
+       EXPECT(st->stats.attempts == 1);
+       EXPECT(st->stats.failures == 1);
+
+       reftable_stack_destroy(st);
+       strbuf_release(&table_path);
+       clear_dir(dir);
+}
+
 static void test_reftable_stack_validate_refname(void)
 {
        struct reftable_write_options cfg = { 0 };
@@ -1085,6 +1138,7 @@ int stack_test_main(int argc, const char *argv[])
        RUN_TEST(test_reftable_stack_tombstone);
        RUN_TEST(test_reftable_stack_transaction_api);
        RUN_TEST(test_reftable_stack_transaction_api_performs_auto_compaction);
+       RUN_TEST(test_reftable_stack_auto_compaction_fails_gracefully);
        RUN_TEST(test_reftable_stack_update_index_check);
        RUN_TEST(test_reftable_stack_uptodate);
        RUN_TEST(test_reftable_stack_validate_refname);
index 1161dc7fed689259141ae2eb5403d84b906027d3..31b02b8840969d17c34715fb3b902c6d2b4a4f2d 100644 (file)
@@ -211,14 +211,9 @@ static int set_option(const char *name, const char *value)
                options.filter = xstrdup(value);
                return 0;
        } else if (!strcmp(name, "object-format")) {
-               int algo;
                options.object_format = 1;
-               if (strcmp(value, "true")) {
-                       algo = hash_algo_by_name(value);
-                       if (algo == GIT_HASH_UNKNOWN)
-                               die("unknown object format '%s'", value);
-                       options.hash_algo = &hash_algos[algo];
-               }
+               if (strcmp(value, "true"))
+                       die(_("unknown value for object-format: %s"), value);
                return 0;
        } else {
                return 1 /* unsupported */;
index 7aacb51b65cca69ec6acd0c879dd0aa5b15978b3..e15b416944dfb21f752093fab777fabf475d4f31 100644 (file)
@@ -14,6 +14,7 @@
 #include "read-cache-ll.h"
 #include "remote.h"
 #include "setup.h"
+#include "loose.h"
 #include "submodule-config.h"
 #include "sparse-index.h"
 #include "trace2.h"
@@ -104,6 +105,15 @@ void repo_set_hash_algo(struct repository *repo, int hash_algo)
        repo->hash_algo = &hash_algos[hash_algo];
 }
 
+void repo_set_compat_hash_algo(struct repository *repo, int algo)
+{
+       if (hash_algo_by_ptr(repo->hash_algo) == algo)
+               BUG("hash_algo and compat_hash_algo match");
+       repo->compat_hash_algo = algo ? &hash_algos[algo] : NULL;
+       if (repo->compat_hash_algo)
+               repo_read_loose_object_map(repo);
+}
+
 void repo_set_ref_storage_format(struct repository *repo, unsigned int format)
 {
        repo->ref_storage_format = format;
@@ -189,6 +199,7 @@ int repo_init(struct repository *repo,
                goto error;
 
        repo_set_hash_algo(repo, format.hash_algo);
+       repo_set_compat_hash_algo(repo, format.compat_hash_algo);
        repo_set_ref_storage_format(repo, format.ref_storage_format);
        repo->repository_format_worktree_config = format.worktree_config;
 
@@ -199,6 +210,9 @@ int repo_init(struct repository *repo,
        if (worktree)
                repo_set_worktree(repo, worktree);
 
+       if (repo->compat_hash_algo)
+               repo_read_loose_object_map(repo);
+
        clear_repository_format(&format);
        return 0;
 
index 9bf1e33d2591bbd8d62c971dbc70790d250b021f..268436779c8f315228aef0dde9039f9ebf4f723e 100644 (file)
@@ -163,6 +163,9 @@ struct repository {
        /* Repository's current hash algorithm, as serialized on disk. */
        const struct git_hash_algo *hash_algo;
 
+       /* Repository's compatibility hash algorithm. */
+       const struct git_hash_algo *compat_hash_algo;
+
        /* Repository's reference storage format, as serialized on disk. */
        unsigned int ref_storage_format;
 
@@ -205,6 +208,7 @@ void repo_set_gitdir(struct repository *repo, const char *root,
                     const struct set_gitdir_args *extra_args);
 void repo_set_worktree(struct repository *repo, const char *path);
 void repo_set_hash_algo(struct repository *repo, int algo);
+void repo_set_compat_hash_algo(struct repository *repo, int compat_algo);
 void repo_set_ref_storage_format(struct repository *repo, unsigned int format);
 void initialize_the_repository(void);
 RESULT_MUST_BE_USED
index d6436ee66b146f63e4aabf16377491d470583328..7e45f765d9fe16c8380f457c0d40e385cbf88f55 100644 (file)
@@ -81,7 +81,7 @@ static void mark_tree_contents_uninteresting(struct repository *r,
        if (parse_tree_gently(tree, 1) < 0)
                return;
 
-       init_tree_desc(&desc, tree->buffer, tree->size);
+       init_tree_desc(&desc, &tree->object.oid, tree->buffer, tree->size);
        while (tree_entry(&desc, &entry)) {
                switch (object_type(entry.mode)) {
                case OBJ_TREE:
@@ -188,7 +188,7 @@ static void add_children_by_path(struct repository *r,
        if (parse_tree_gently(tree, 1) < 0)
                return;
 
-       init_tree_desc(&desc, tree->buffer, tree->size);
+       init_tree_desc(&desc, &tree->object.oid, tree->buffer, tree->size);
        while (tree_entry(&desc, &entry)) {
                switch (object_type(entry.mode)) {
                case OBJ_TREE:
index 4e14fa6541c7012c8549e84957c47f386efbc7ec..2c19846385baa1df3240e865752087db827ba36e 100644 (file)
@@ -479,7 +479,7 @@ static void print_advice(struct repository *r, int show_hint,
                msg = getenv("GIT_CHERRY_PICK_HELP");
 
        if (msg) {
-               advise("%s\n", msg);
+               advise_if_enabled(ADVICE_MERGE_CONFLICT, "%s", msg);
                /*
                 * A conflict has occurred but the porcelain
                 * (typically rebase --interactive) wants to take care
@@ -492,22 +492,25 @@ static void print_advice(struct repository *r, int show_hint,
 
        if (show_hint) {
                if (opts->no_commit)
-                       advise(_("after resolving the conflicts, mark the corrected paths\n"
-                                "with 'git add <paths>' or 'git rm <paths>'"));
+                       advise_if_enabled(ADVICE_MERGE_CONFLICT,
+                                         _("after resolving the conflicts, mark the corrected paths\n"
+                                           "with 'git add <paths>' or 'git rm <paths>'"));
                else if (opts->action == REPLAY_PICK)
-                       advise(_("After resolving the conflicts, mark them with\n"
-                                "\"git add/rm <pathspec>\", then run\n"
-                                "\"git cherry-pick --continue\".\n"
-                                "You can instead skip this commit with \"git cherry-pick --skip\".\n"
-                                "To abort and get back to the state before \"git cherry-pick\",\n"
-                                "run \"git cherry-pick --abort\"."));
+                       advise_if_enabled(ADVICE_MERGE_CONFLICT,
+                                         _("After resolving the conflicts, mark them with\n"
+                                           "\"git add/rm <pathspec>\", then run\n"
+                                           "\"git cherry-pick --continue\".\n"
+                                           "You can instead skip this commit with \"git cherry-pick --skip\".\n"
+                                           "To abort and get back to the state before \"git cherry-pick\",\n"
+                                           "run \"git cherry-pick --abort\"."));
                else if (opts->action == REPLAY_REVERT)
-                       advise(_("After resolving the conflicts, mark them with\n"
-                                "\"git add/rm <pathspec>\", then run\n"
-                                "\"git revert --continue\".\n"
-                                "You can instead skip this commit with \"git revert --skip\".\n"
-                                "To abort and get back to the state before \"git revert\",\n"
-                                "run \"git revert --abort\"."));
+                       advise_if_enabled(ADVICE_MERGE_CONFLICT,
+                                         _("After resolving the conflicts, mark them with\n"
+                                           "\"git add/rm <pathspec>\", then run\n"
+                                           "\"git revert --continue\".\n"
+                                           "You can instead skip this commit with \"git revert --skip\".\n"
+                                           "To abort and get back to the state before \"git revert\",\n"
+                                           "run \"git revert --abort\"."));
                else
                        BUG("unexpected pick action in print_advice()");
        }
@@ -675,15 +678,15 @@ void append_conflicts_hint(struct index_state *istate,
        if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS) {
                strbuf_addch(msgbuf, '\n');
                wt_status_append_cut_line(msgbuf);
-               strbuf_addch(msgbuf, comment_line_char);
+               strbuf_addstr(msgbuf, comment_line_str);
        }
 
        strbuf_addch(msgbuf, '\n');
-       strbuf_commented_addf(msgbuf, comment_line_char, "Conflicts:\n");
+       strbuf_commented_addf(msgbuf, comment_line_str, "Conflicts:\n");
        for (i = 0; i < istate->cache_nr;) {
                const struct cache_entry *ce = istate->cache[i++];
                if (ce_stage(ce)) {
-                       strbuf_commented_addf(msgbuf, comment_line_char,
+                       strbuf_commented_addf(msgbuf, comment_line_str,
                                              "\t%s\n", ce->name);
                        while (i < istate->cache_nr &&
                               !strcmp(ce->name, istate->cache[i]->name))
@@ -784,29 +787,42 @@ static struct object_id *get_cache_tree_oid(struct index_state *istate)
 static int is_index_unchanged(struct repository *r)
 {
        struct object_id head_oid, *cache_tree_oid;
+       const struct object_id *head_tree_oid;
        struct commit *head_commit;
        struct index_state *istate = r->index;
+       const char *head_name;
+
+       if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, &head_oid, NULL)) {
+               /* Check to see if this is an unborn branch */
+               head_name = resolve_ref_unsafe("HEAD",
+                       RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,
+                       &head_oid, NULL);
+               if (!head_name ||
+                       !starts_with(head_name, "refs/heads/") ||
+                       !is_null_oid(&head_oid))
+                       return error(_("could not resolve HEAD commit"));
+               head_tree_oid = the_hash_algo->empty_tree;
+       } else {
+               head_commit = lookup_commit(r, &head_oid);
 
-       if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, &head_oid, NULL))
-               return error(_("could not resolve HEAD commit"));
-
-       head_commit = lookup_commit(r, &head_oid);
+               /*
+                * If head_commit is NULL, check_commit, called from
+                * lookup_commit, would have indicated that head_commit is not
+                * a commit object already.  repo_parse_commit() will return failure
+                * without further complaints in such a case.  Otherwise, if
+                * the commit is invalid, repo_parse_commit() will complain.  So
+                * there is nothing for us to say here.  Just return failure.
+                */
+               if (repo_parse_commit(r, head_commit))
+                       return -1;
 
-       /*
-        * If head_commit is NULL, check_commit, called from
-        * lookup_commit, would have indicated that head_commit is not
-        * a commit object already.  repo_parse_commit() will return failure
-        * without further complaints in such a case.  Otherwise, if
-        * the commit is invalid, repo_parse_commit() will complain.  So
-        * there is nothing for us to say here.  Just return failure.
-        */
-       if (repo_parse_commit(r, head_commit))
-               return -1;
+               head_tree_oid = get_commit_tree_oid(head_commit);
+       }
 
        if (!(cache_tree_oid = get_cache_tree_oid(istate)))
                return -1;
 
-       return oideq(cache_tree_oid, get_commit_tree_oid(head_commit));
+       return oideq(cache_tree_oid, head_tree_oid);
 }
 
 static int write_author_script(const char *message)
@@ -1166,7 +1182,7 @@ void cleanup_message(struct strbuf *msgbuf,
                strbuf_setlen(msgbuf, wt_status_locate_end(msgbuf->buf, msgbuf->len));
        if (cleanup_mode != COMMIT_MSG_CLEANUP_NONE)
                strbuf_stripspace(msgbuf,
-                 cleanup_mode == COMMIT_MSG_CLEANUP_ALL ? comment_line_char : '\0');
+                 cleanup_mode == COMMIT_MSG_CLEANUP_ALL ? comment_line_str : NULL);
 }
 
 /*
@@ -1198,7 +1214,7 @@ int template_untouched(const struct strbuf *sb, const char *template_file,
                return 0;
 
        strbuf_stripspace(&tmpl,
-         cleanup_mode == COMMIT_MSG_CLEANUP_ALL ? comment_line_char : '\0');
+         cleanup_mode == COMMIT_MSG_CLEANUP_ALL ? comment_line_str : NULL);
        if (!skip_prefix(sb->buf, tmpl.buf, &start))
                start = sb->buf;
        strbuf_release(&tmpl);
@@ -1571,7 +1587,7 @@ static int try_to_commit(struct repository *r,
 
        if (cleanup != COMMIT_MSG_CLEANUP_NONE)
                strbuf_stripspace(msg,
-                 cleanup == COMMIT_MSG_CLEANUP_ALL ? comment_line_char : '\0');
+                 cleanup == COMMIT_MSG_CLEANUP_ALL ? comment_line_str : NULL);
        if ((flags & EDIT_MSG) && message_is_empty(msg, cleanup)) {
                res = 1; /* run 'git commit' to display error message */
                goto out;
@@ -1733,34 +1749,25 @@ static int allow_empty(struct repository *r,
        int index_unchanged, originally_empty;
 
        /*
-        * Four cases:
-        *
-        * (1) we do not allow empty at all and error out.
+        * For a commit that is initially empty, allow_empty determines if it
+        * should be kept or not
         *
-        * (2) we allow ones that were initially empty, and
-        *     just drop the ones that become empty
-        *
-        * (3) we allow ones that were initially empty, but
-        *     halt for the ones that become empty;
-        *
-        * (4) we allow both.
+        * For a commit that becomes empty, keep_redundant_commits and
+        * drop_redundant_commits determine whether the commit should be kept or
+        * dropped. If neither is specified, halt.
         */
-       if (!opts->allow_empty)
-               return 0; /* let "git commit" barf as necessary */
-
        index_unchanged = is_index_unchanged(r);
        if (index_unchanged < 0)
                return index_unchanged;
        if (!index_unchanged)
                return 0; /* we do not have to say --allow-empty */
 
-       if (opts->keep_redundant_commits)
-               return 1;
-
        originally_empty = is_original_commit_empty(commit);
        if (originally_empty < 0)
                return originally_empty;
        if (originally_empty)
+               return opts->allow_empty;
+       else if (opts->keep_redundant_commits)
                return 1;
        else if (opts->drop_redundant_commits)
                return 2;
@@ -1793,6 +1800,8 @@ static const char *command_to_string(const enum todo_command command)
 {
        if (command < TODO_COMMENT)
                return todo_command_info[command].str;
+       if (command == TODO_COMMENT)
+               return comment_line_str;
        die(_("unknown command: %d"), command);
 }
 
@@ -1800,7 +1809,7 @@ static char command_to_char(const enum todo_command command)
 {
        if (command < TODO_COMMENT)
                return todo_command_info[command].c;
-       return comment_line_char;
+       return 0;
 }
 
 static int is_noop(const enum todo_command command)
@@ -1854,7 +1863,7 @@ static int is_fixup_flag(enum todo_command command, unsigned flag)
 static void add_commented_lines(struct strbuf *buf, const void *str, size_t len)
 {
        const char *s = str;
-       while (len > 0 && s[0] == comment_line_char) {
+       while (starts_with_mem(s, len, comment_line_str)) {
                size_t count;
                const char *n = memchr(s, '\n', len);
                if (!n)
@@ -1865,7 +1874,7 @@ static void add_commented_lines(struct strbuf *buf, const void *str, size_t len)
                s += count;
                len -= count;
        }
-       strbuf_add_commented_lines(buf, s, len, comment_line_char);
+       strbuf_add_commented_lines(buf, s, len, comment_line_str);
 }
 
 /* Does the current fixup chain contain a squash command? */
@@ -1960,11 +1969,11 @@ static int append_squash_message(struct strbuf *buf, const char *body,
             (starts_with(body, "squash!") || starts_with(body, "fixup!"))))
                commented_len = commit_subject_length(body);
 
-       strbuf_addf(buf, "\n%c ", comment_line_char);
+       strbuf_addf(buf, "\n%s ", comment_line_str);
        strbuf_addf(buf, _(nth_commit_msg_fmt),
                    ++opts->current_fixup_count + 1);
        strbuf_addstr(buf, "\n\n");
-       strbuf_add_commented_lines(buf, body, commented_len, comment_line_char);
+       strbuf_add_commented_lines(buf, body, commented_len, comment_line_str);
        /* buf->buf may be reallocated so store an offset into the buffer */
        fixup_off = buf->len;
        strbuf_addstr(buf, body + commented_len);
@@ -2017,10 +2026,10 @@ static int update_squash_messages(struct repository *r,
                        return error(_("could not read '%s'"),
                                rebase_path_squash_msg());
 
-               eol = buf.buf[0] != comment_line_char ?
+               eol = !starts_with(buf.buf, comment_line_str) ?
                        buf.buf : strchrnul(buf.buf, '\n');
 
-               strbuf_addf(&header, "%c ", comment_line_char);
+               strbuf_addf(&header, "%s ", comment_line_str);
                strbuf_addf(&header, _(combined_commit_msg_fmt),
                            opts->current_fixup_count + 2);
                strbuf_splice(&buf, 0, eol - buf.buf, header.buf, header.len);
@@ -2046,16 +2055,16 @@ static int update_squash_messages(struct repository *r,
                        repo_unuse_commit_buffer(r, head_commit, head_message);
                        return error(_("cannot write '%s'"), rebase_path_fixup_msg());
                }
-               strbuf_addf(&buf, "%c ", comment_line_char);
+               strbuf_addf(&buf, "%s ", comment_line_str);
                strbuf_addf(&buf, _(combined_commit_msg_fmt), 2);
-               strbuf_addf(&buf, "\n%c ", comment_line_char);
+               strbuf_addf(&buf, "\n%s ", comment_line_str);
                strbuf_addstr(&buf, is_fixup_flag(command, flag) ?
                              _(skip_first_commit_msg_str) :
                              _(first_commit_msg_str));
                strbuf_addstr(&buf, "\n\n");
                if (is_fixup_flag(command, flag))
                        strbuf_add_commented_lines(&buf, body, strlen(body),
-                                                  comment_line_char);
+                                                  comment_line_str);
                else
                        strbuf_addstr(&buf, body);
 
@@ -2070,12 +2079,12 @@ static int update_squash_messages(struct repository *r,
        if (command == TODO_SQUASH || is_fixup_flag(command, flag)) {
                res = append_squash_message(&buf, body, command, opts, flag);
        } else if (command == TODO_FIXUP) {
-               strbuf_addf(&buf, "\n%c ", comment_line_char);
+               strbuf_addf(&buf, "\n%s ", comment_line_str);
                strbuf_addf(&buf, _(skip_nth_commit_msg_fmt),
                            ++opts->current_fixup_count + 1);
                strbuf_addstr(&buf, "\n\n");
                strbuf_add_commented_lines(&buf, body, strlen(body),
-                                          comment_line_char);
+                                          comment_line_str);
        } else
                return error(_("unknown command: %d"), command);
        repo_unuse_commit_buffer(r, commit, message);
@@ -2576,7 +2585,7 @@ static int parse_insn_line(struct repository *r, struct todo_item *item,
        /* left-trim */
        bol += strspn(bol, " \t");
 
-       if (bol == eol || *bol == '\r' || *bol == comment_line_char) {
+       if (bol == eol || *bol == '\r' || starts_with_mem(bol, eol - bol, comment_line_str)) {
                item->command = TODO_COMMENT;
                item->commit = NULL;
                item->arg_offset = bol - buf;
@@ -2940,6 +2949,9 @@ static int populate_opts_cb(const char *key, const char *value,
        else if (!strcmp(key, "options.allow-empty-message"))
                opts->allow_empty_message =
                        git_config_bool_or_int(key, value, ctx->kvi, &error_flag);
+       else if (!strcmp(key, "options.drop-redundant-commits"))
+               opts->drop_redundant_commits =
+                       git_config_bool_or_int(key, value, ctx->kvi, &error_flag);
        else if (!strcmp(key, "options.keep-redundant-commits"))
                opts->keep_redundant_commits =
                        git_config_bool_or_int(key, value, ctx->kvi, &error_flag);
@@ -3474,54 +3486,57 @@ static int save_opts(struct replay_opts *opts)
 
        if (opts->no_commit)
                res |= git_config_set_in_file_gently(opts_file,
-                                       "options.no-commit", "true");
+                                       "options.no-commit", NULL, "true");
        if (opts->edit >= 0)
-               res |= git_config_set_in_file_gently(opts_file, "options.edit",
+               res |= git_config_set_in_file_gently(opts_file, "options.edit", NULL,
                                                     opts->edit ? "true" : "false");
        if (opts->allow_empty)
                res |= git_config_set_in_file_gently(opts_file,
-                                       "options.allow-empty", "true");
+                                       "options.allow-empty", NULL, "true");
        if (opts->allow_empty_message)
                res |= git_config_set_in_file_gently(opts_file,
-                               "options.allow-empty-message", "true");
+                               "options.allow-empty-message", NULL, "true");
+       if (opts->drop_redundant_commits)
+               res |= git_config_set_in_file_gently(opts_file,
+                               "options.drop-redundant-commits", NULL, "true");
        if (opts->keep_redundant_commits)
                res |= git_config_set_in_file_gently(opts_file,
-                               "options.keep-redundant-commits", "true");
+                               "options.keep-redundant-commits", NULL, "true");
        if (opts->signoff)
                res |= git_config_set_in_file_gently(opts_file,
-                                       "options.signoff", "true");
+                                       "options.signoff", NULL, "true");
        if (opts->record_origin)
                res |= git_config_set_in_file_gently(opts_file,
-                                       "options.record-origin", "true");
+                                       "options.record-origin", NULL, "true");
        if (opts->allow_ff)
                res |= git_config_set_in_file_gently(opts_file,
-                                       "options.allow-ff", "true");
+                                       "options.allow-ff", NULL, "true");
        if (opts->mainline) {
                struct strbuf buf = STRBUF_INIT;
                strbuf_addf(&buf, "%d", opts->mainline);
                res |= git_config_set_in_file_gently(opts_file,
-                                       "options.mainline", buf.buf);
+                                       "options.mainline", NULL, buf.buf);
                strbuf_release(&buf);
        }
        if (opts->strategy)
                res |= git_config_set_in_file_gently(opts_file,
-                                       "options.strategy", opts->strategy);
+                                       "options.strategy", NULL, opts->strategy);
        if (opts->gpg_sign)
                res |= git_config_set_in_file_gently(opts_file,
-                                       "options.gpg-sign", opts->gpg_sign);
+                                       "options.gpg-sign", NULL, opts->gpg_sign);
        for (size_t i = 0; i < opts->xopts.nr; i++)
                res |= git_config_set_multivar_in_file_gently(opts_file,
                                "options.strategy-option",
-                               opts->xopts.v[i], "^$", 0);
+                               opts->xopts.v[i], "^$", NULL, 0);
        if (opts->allow_rerere_auto)
                res |= git_config_set_in_file_gently(opts_file,
-                               "options.allow-rerere-auto",
+                               "options.allow-rerere-auto", NULL,
                                opts->allow_rerere_auto == RERERE_AUTOUPDATE ?
                                "true" : "false");
 
        if (opts->explicit_cleanup)
                res |= git_config_set_in_file_gently(opts_file,
-                               "options.default-msg-cleanup",
+                               "options.default-msg-cleanup", NULL,
                                describe_cleanup_mode(opts->default_msg_cleanup));
        return res;
 }
@@ -5679,8 +5694,8 @@ static int make_script_with_merges(struct pretty_print_context *pp,
                                    oid_to_hex(&commit->object.oid),
                                    oneline.buf);
                        if (is_empty)
-                               strbuf_addf(&buf, " %c empty",
-                                           comment_line_char);
+                               strbuf_addf(&buf, " %s empty",
+                                           comment_line_str);
 
                        FLEX_ALLOC_STR(entry, string, buf.buf);
                        oidcpy(&entry->entry.oid, &commit->object.oid);
@@ -5770,7 +5785,7 @@ static int make_script_with_merges(struct pretty_print_context *pp,
                entry = oidmap_get(&state.commit2label, &commit->object.oid);
 
                if (entry)
-                       strbuf_addf(out, "\n%c Branch %s\n", comment_line_char, entry->string);
+                       strbuf_addf(out, "\n%s Branch %s\n", comment_line_str, entry->string);
                else
                        strbuf_addch(out, '\n');
 
@@ -5907,7 +5922,7 @@ int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,
                            oid_to_hex(&commit->object.oid));
                pretty_print_commit(&pp, commit, out);
                if (is_empty)
-                       strbuf_addf(out, " %c empty", comment_line_char);
+                       strbuf_addf(out, " %s empty", comment_line_str);
                strbuf_addch(out, '\n');
        }
        if (skipped_commit)
diff --git a/setup.c b/setup.c
index 0b798591c0c5c2b23285ce1c4f8fcfb71caddb14..f4b32f76e3d86b46dbd7713195592906b73b9571 100644 (file)
--- a/setup.c
+++ b/setup.c
@@ -591,6 +591,25 @@ static enum extension_result handle_extension(const char *var,
                                     "extensions.objectformat", value);
                data->hash_algo = format;
                return EXTENSION_OK;
+       } else if (!strcmp(ext, "compatobjectformat")) {
+               struct string_list_item *item;
+               int format;
+
+               if (!value)
+                       return config_error_nonbool(var);
+               format = hash_algo_by_name(value);
+               if (format == GIT_HASH_UNKNOWN)
+                       return error(_("invalid value for '%s': '%s'"),
+                                    "extensions.compatobjectformat", value);
+               /* For now only support compatObjectFormat being specified once. */
+               for_each_string_list_item(item, &data->v1_only_extensions) {
+                       if (!strcmp(item->string, "compatobjectformat"))
+                               return error(_("'%s' already specified as '%s'"),
+                                       "extensions.compatobjectformat",
+                                       hash_algos[data->compat_hash_algo].name);
+               }
+               data->compat_hash_algo = format;
+               return EXTENSION_OK;
        } else if (!strcmp(ext, "refstorage")) {
                unsigned int format;
 
@@ -1603,6 +1622,8 @@ const char *setup_git_directory_gently(int *nongit_ok)
                }
                if (startup_info->have_repository) {
                        repo_set_hash_algo(the_repository, repo_fmt.hash_algo);
+                       repo_set_compat_hash_algo(the_repository,
+                                                 repo_fmt.compat_hash_algo);
                        repo_set_ref_storage_format(the_repository,
                                                    repo_fmt.ref_storage_format);
                        the_repository->repository_format_worktree_config =
@@ -1698,6 +1719,7 @@ void check_repository_format(struct repository_format *fmt)
        check_repository_format_gently(get_git_dir(), fmt, NULL);
        startup_info->have_repository = 1;
        repo_set_hash_algo(the_repository, fmt->hash_algo);
+       repo_set_compat_hash_algo(the_repository, fmt->compat_hash_algo);
        repo_set_ref_storage_format(the_repository,
                                    fmt->ref_storage_format);
        the_repository->repository_format_worktree_config =
diff --git a/setup.h b/setup.h
index 3599aec93c5ac0b72aafaf3cdcb030831cc53e3b..d88bb37aafb3c59cb091e7f7b02fc421cde6c2b2 100644 (file)
--- a/setup.h
+++ b/setup.h
@@ -115,6 +115,7 @@ struct repository_format {
        int worktree_config;
        int is_bare;
        int hash_algo;
+       int compat_hash_algo;
        unsigned int ref_storage_format;
        int sparse_index;
        char *work_tree;
index 7827178d8e5e374582357f48a14a77bb15d98320..1492a0822526fe4fa8e982fa36f52cfd40b4b0ad 100644 (file)
--- a/strbuf.c
+++ b/strbuf.c
@@ -24,6 +24,17 @@ int istarts_with(const char *str, const char *prefix)
                        return 0;
 }
 
+int starts_with_mem(const char *str, size_t len, const char *prefix)
+{
+       const char *end = str + len;
+       for (; ; str++, prefix++) {
+               if (!*prefix)
+                       return 1;
+               else if (str == end || *str != *prefix)
+                       return 0;
+       }
+}
+
 int skip_to_optional_arg_default(const char *str, const char *prefix,
                                 const char **arg, const char *def)
 {
@@ -340,18 +351,17 @@ void strbuf_addf(struct strbuf *sb, const char *fmt, ...)
 }
 
 static void add_lines(struct strbuf *out,
-                       const char *prefix1,
-                       const char *prefix2,
-                       const char *buf, size_t size)
+                       const char *prefix,
+                       const char *buf, size_t size,
+                       int space_after_prefix)
 {
        while (size) {
-               const char *prefix;
                const char *next = memchr(buf, '\n', size);
                next = next ? (next + 1) : (buf + size);
 
-               prefix = ((prefix2 && (buf[0] == '\n' || buf[0] == '\t'))
-                         ? prefix2 : prefix1);
                strbuf_addstr(out, prefix);
+               if (space_after_prefix && buf[0] != '\n' && buf[0] != '\t')
+                       strbuf_addch(out, ' ');
                strbuf_add(out, buf, next - buf);
                size -= next - buf;
                buf = next;
@@ -360,19 +370,12 @@ static void add_lines(struct strbuf *out,
 }
 
 void strbuf_add_commented_lines(struct strbuf *out, const char *buf,
-                               size_t size, char comment_line_char)
+                               size_t size, const char *comment_prefix)
 {
-       static char prefix1[3];
-       static char prefix2[2];
-
-       if (prefix1[0] != comment_line_char) {
-               xsnprintf(prefix1, sizeof(prefix1), "%c ", comment_line_char);
-               xsnprintf(prefix2, sizeof(prefix2), "%c", comment_line_char);
-       }
-       add_lines(out, prefix1, prefix2, buf, size);
+       add_lines(out, comment_prefix, buf, size, 1);
 }
 
-void strbuf_commented_addf(struct strbuf *sb, char comment_line_char,
+void strbuf_commented_addf(struct strbuf *sb, const char *comment_prefix,
                           const char *fmt, ...)
 {
        va_list params;
@@ -383,7 +386,7 @@ void strbuf_commented_addf(struct strbuf *sb, char comment_line_char,
        strbuf_vaddf(&buf, fmt, params);
        va_end(params);
 
-       strbuf_add_commented_lines(sb, buf.buf, buf.len, comment_line_char);
+       strbuf_add_commented_lines(sb, buf.buf, buf.len, comment_prefix);
        if (incomplete_line)
                sb->buf[--sb->len] = '\0';
 
@@ -442,6 +445,26 @@ size_t strbuf_expand_literal(struct strbuf *sb, const char *placeholder)
        return 0;
 }
 
+void strbuf_expand_bad_format(const char *format, const char *command)
+{
+       const char *end;
+
+       if (*format != '(')
+               /* TRANSLATORS: The first %s is a command like "ls-tree". */
+               die(_("bad %s format: element '%s' does not start with '('"),
+                   command, format);
+
+       end = strchr(format + 1, ')');
+       if (!end)
+               /* TRANSLATORS: The first %s is a command like "ls-tree". */
+               die(_("bad %s format: element '%s' does not end in ')'"),
+                   command, format);
+
+       /* TRANSLATORS: %s is a command like "ls-tree". */
+       die(_("bad %s format: %%%.*s"),
+           command, (int)(end - format + 1), format);
+}
+
 void strbuf_addbuf_percentquote(struct strbuf *dst, const struct strbuf *src)
 {
        size_t i, len = src->len;
@@ -750,7 +773,7 @@ ssize_t strbuf_read_file(struct strbuf *sb, const char *path, size_t hint)
 void strbuf_add_lines(struct strbuf *out, const char *prefix,
                      const char *buf, size_t size)
 {
-       add_lines(out, prefix, NULL, buf, size);
+       add_lines(out, prefix, buf, size, 0);
 }
 
 void strbuf_addstr_xml_quoted(struct strbuf *buf, const char *s)
@@ -1005,10 +1028,10 @@ static size_t cleanup(char *line, size_t len)
  *
  * If last line does not have a newline at the end, one is added.
  *
- * Pass a non-NUL comment_line_char to skip every line starting
+ * Pass a non-NULL comment_prefix to skip every line starting
  * with it.
  */
-void strbuf_stripspace(struct strbuf *sb, char comment_line_char)
+void strbuf_stripspace(struct strbuf *sb, const char *comment_prefix)
 {
        size_t empties = 0;
        size_t i, j, len, newlen;
@@ -1021,8 +1044,8 @@ void strbuf_stripspace(struct strbuf *sb, char comment_line_char)
                eol = memchr(sb->buf + i, '\n', sb->len - i);
                len = eol ? eol - (sb->buf + i) + 1 : sb->len - i;
 
-               if (comment_line_char && len &&
-                   sb->buf[i] == comment_line_char) {
+               if (comment_prefix && len &&
+                   starts_with(sb->buf + i, comment_prefix)) {
                        newlen = 0;
                        continue;
                }
index e959caca876ac759debb0c77cd7c6d1046cbc25d..97fa4a3d01c04d1a591eec8dc479b701b962e62a 100644 (file)
--- a/strbuf.h
+++ b/strbuf.h
@@ -288,7 +288,7 @@ void strbuf_splice(struct strbuf *sb, size_t pos, size_t len,
  */
 void strbuf_add_commented_lines(struct strbuf *out,
                                const char *buf, size_t size,
-                               char comment_line_char);
+                               const char *comment_prefix);
 
 
 /**
@@ -337,6 +337,11 @@ size_t strbuf_expand_literal(struct strbuf *sb, const char *placeholder);
  */
 int strbuf_expand_step(struct strbuf *sb, const char **formatp);
 
+/**
+ * Used with `strbuf_expand_step` to report unknown placeholders.
+ */
+void strbuf_expand_bad_format(const char *format, const char *command);
+
 /**
  * Append the contents of one strbuf to another, quoting any
  * percent signs ("%") into double-percents ("%%") in the
@@ -379,7 +384,7 @@ void strbuf_addf(struct strbuf *sb, const char *fmt, ...);
  * blank to the buffer.
  */
 __attribute__((format (printf, 3, 4)))
-void strbuf_commented_addf(struct strbuf *sb, char comment_line_char, const char *fmt, ...);
+void strbuf_commented_addf(struct strbuf *sb, const char *comment_prefix, const char *fmt, ...);
 
 __attribute__((format (printf,2,0)))
 void strbuf_vaddf(struct strbuf *sb, const char *fmt, va_list ap);
@@ -513,11 +518,11 @@ int strbuf_getcwd(struct strbuf *sb);
 int strbuf_normalize_path(struct strbuf *sb);
 
 /**
- * Strip whitespace from a buffer. If comment_line_char is non-NUL,
+ * Strip whitespace from a buffer. If comment_prefix is non-NULL,
  * then lines beginning with that character are considered comments,
  * thus removed.
  */
-void strbuf_stripspace(struct strbuf *buf, char comment_line_char);
+void strbuf_stripspace(struct strbuf *buf, const char *comment_prefix);
 
 static inline int strbuf_strip_suffix(struct strbuf *sb, const char *suffix)
 {
@@ -673,6 +678,7 @@ char *xstrfmt(const char *fmt, ...);
 
 int starts_with(const char *str, const char *prefix);
 int istarts_with(const char *str, const char *prefix);
+int starts_with_mem(const char *str, size_t len, const char *prefix);
 
 /*
  * If the string "str" is the same as the string in "prefix", then the "arg"
index 54130f6a38572b613d4b7ee8ae1cf3bc6035055d..11428b4adad515a9e0ee495c16e1af5636dc6546 100644 (file)
@@ -978,7 +978,7 @@ int config_set_in_gitmodules_file_gently(const char *key, const char *value)
 {
        int ret;
 
-       ret = git_config_set_in_file_gently(GITMODULES_FILE, key, value);
+       ret = git_config_set_in_file_gently(GITMODULES_FILE, key, NULL, value);
        if (ret < 0)
                /* Maybe the user already did that, don't error out here */
                warning(_("Could not update .gitmodules entry %s"), key);
index f0ddb31e8fb535264dded9fb0a7434fda1330f39..ce2d03252157f6cb5c83ec7d8110ef71d7152830 100644 (file)
@@ -2046,7 +2046,7 @@ void submodule_unset_core_worktree(const struct submodule *sub)
        submodule_name_to_gitdir(&config_path, the_repository, sub->name);
        strbuf_addstr(&config_path, "/config");
 
-       if (git_config_set_in_file_gently(config_path.buf, "core.worktree", NULL))
+       if (git_config_set_in_file_gently(config_path.buf, "core.worktree", NULL, NULL))
                warning(_("Could not unset core.worktree setting in submodule '%s'"),
                          sub->path);
 
index 621d3b8c095441a8a8985b7f12363e26f8ab4d98..d9e0e075061f8d1d629abd02611bd1102d0269ba 100644 (file)
--- a/t/README
+++ b/t/README
@@ -32,6 +32,13 @@ the tests.
     ok 2 - plain with GIT_WORK_TREE
     ok 3 - plain bare
 
+t/Makefile defines a target for each test file, such that you can also use
+shell pattern matching to run a subset of the tests:
+
+    make *checkout*
+
+will run all tests with 'checkout' in their filename.
+
 Since the tests all output TAP (see https://testanything.org) they can
 be run with any TAP harness. Here's an example of parallel testing
 powered by a recent version of prove(1):
@@ -724,6 +731,26 @@ The "do's:"
    Note that we still &&-chain the loop to propagate failures from
    earlier commands.
 
+ - Repeat tests with slightly different arguments in a loop.
+
+   In some cases it may make sense to re-run the same set of tests with
+   different options or commands to ensure that the command behaves
+   despite the different parameters. This can be achieved by looping
+   around a specific parameter:
+
+       for arg in '' "--foo"
+       do
+               test_expect_success "test command ${arg:-without arguments}" '
+                       command $arg
+               '
+       done
+
+   Note that while the test title uses double quotes ("), the test body
+   should continue to use single quotes (') to avoid breakage in case the
+   values contain e.g. quoting characters. The loop variable will be
+   accessible regardless of the single quotes as the test body is passed
+   to `eval`.
+
 
 And here are the "don'ts:"
 
index 5e21e84f3884eb8c787ab82a5d1360d2b7cffb74..87572459e4b873cdc78f36867ba782e022eb13f4 100644 (file)
@@ -532,7 +532,7 @@ test_expect_success 'blame -L :funcname with userdiff driver' '
                "$(cat file.template)" &&
        test_commit --author "B <B@test.git>" \
                "change" "$fortran_file" \
-               "$(cat file.template | sed -e s/ChangeMe/IWasChanged/)" &&
+               "$(sed -e s/ChangeMe/IWasChanged/ file.template)" &&
        check_count -f "$fortran_file" -L:RIGHT A 3 B 1
 '
 
diff --git a/t/helper/test-delete-gpgsig.c b/t/helper/test-delete-gpgsig.c
new file mode 100644 (file)
index 0000000..e36831a
--- /dev/null
@@ -0,0 +1,62 @@
+#include "test-tool.h"
+#include "gpg-interface.h"
+#include "strbuf.h"
+
+
+int cmd__delete_gpgsig(int argc, const char **argv)
+{
+       struct strbuf buf = STRBUF_INIT;
+       const char *pattern = "gpgsig";
+       const char *bufptr, *tail, *eol;
+       int deleting = 0;
+       size_t plen;
+
+       if (argc >= 2) {
+               pattern = argv[1];
+               argv++;
+               argc--;
+       }
+
+       plen = strlen(pattern);
+       strbuf_read(&buf, 0, 0);
+
+       if (!strcmp(pattern, "trailer")) {
+               size_t payload_size = parse_signed_buffer(buf.buf, buf.len);
+               fwrite(buf.buf, 1, payload_size, stdout);
+               fflush(stdout);
+               return 0;
+       }
+
+       bufptr = buf.buf;
+       tail = bufptr + buf.len;
+
+       while (bufptr < tail) {
+               /* Find the end of the line */
+               eol = memchr(bufptr, '\n', tail - bufptr);
+               if (!eol)
+                       eol = tail;
+
+               /* Drop continuation lines */
+               if (deleting && (bufptr < eol) && (bufptr[0] == ' ')) {
+                       bufptr = eol + 1;
+                       continue;
+               }
+               deleting = 0;
+
+               /* Does the line match the prefix? */
+               if (((bufptr + plen) < eol) &&
+                   !memcmp(bufptr, pattern, plen) &&
+                   (bufptr[plen] == ' ')) {
+                       deleting = 1;
+                       bufptr = eol + 1;
+                       continue;
+               }
+
+               /* Print all other lines */
+               fwrite(bufptr, 1, (eol - bufptr) + 1, stdout);
+               bufptr = eol + 1;
+       }
+       fflush(stdout);
+
+       return 0;
+}
index 7a0f6cac53d2433ade595ce95ab2d30e214b2ede..82bbf6e2e68b9b57b8c2d159439631aa36a16d20 100644 (file)
@@ -112,25 +112,6 @@ static const char **get_store(const char **argv, struct ref_store **refs)
        return argv + 1;
 }
 
-static struct flag_definition pack_flags[] = { FLAG_DEF(PACK_REFS_PRUNE),
-                                              FLAG_DEF(PACK_REFS_ALL),
-                                              { NULL, 0 } };
-
-static int cmd_pack_refs(struct ref_store *refs, const char **argv)
-{
-       unsigned int flags = arg_flags(*argv++, "flags", pack_flags);
-       static struct ref_exclusions exclusions = REF_EXCLUSIONS_INIT;
-       static struct string_list included_refs = STRING_LIST_INIT_NODUP;
-       struct pack_refs_opts pack_opts = { .flags = flags,
-                                           .exclusions = &exclusions,
-                                           .includes = &included_refs };
-
-       if (pack_opts.flags & PACK_REFS_ALL)
-               string_list_append(pack_opts.includes, "*");
-
-       return refs_pack_refs(refs, &pack_opts);
-}
-
 static int cmd_create_symref(struct ref_store *refs, const char **argv)
 {
        const char *refname = notnull(*argv++, "refname");
@@ -326,7 +307,6 @@ struct command {
 };
 
 static struct command commands[] = {
-       { "pack-refs", cmd_pack_refs },
        { "create-symref", cmd_create_symref },
        { "delete-refs", cmd_delete_refs },
        { "rename-ref", cmd_rename_ref },
index 482a1e58a4b6ec922f1d5abd39848acda79a78bf..80a946b847e67da00d8a6fc9884ab6d7cdfe2903 100644 (file)
@@ -20,6 +20,7 @@ static struct test_cmd cmds[] = {
        { "crontab", cmd__crontab },
        { "csprng", cmd__csprng },
        { "date", cmd__date },
+       { "delete-gpgsig", cmd__delete_gpgsig },
        { "delta", cmd__delta },
        { "dir-iterator", cmd__dir_iterator },
        { "drop-caches", cmd__drop_caches },
index b1be7cfcf593d0d34ac5f851738ae2fbdf93478f..2808b924191f21ee4ac18462397efd86289eb0c0 100644 (file)
@@ -14,6 +14,7 @@ int cmd__crontab(int argc, const char **argv);
 int cmd__csprng(int argc, const char **argv);
 int cmd__date(int argc, const char **argv);
 int cmd__delta(int argc, const char **argv);
+int cmd__delete_gpgsig(int argc, const char **argv);
 int cmd__dir_iterator(int argc, const char **argv);
 int cmd__drop_caches(int argc, const char **argv);
 int cmd__dump_cache_tree(int argc, const char **argv);
index 32b34733790834362612816eb5eac63cc6acdf33..57b9b2db9b3f8244dfec5a6cf5b6dc6a6dcb1138 100644 (file)
@@ -71,8 +71,8 @@ test_cmp_branch_tree () {
                find . -type d -name .git -prune -o -type f -print
        ) | sort >module-git-"$1".list &&
        test_cmp module-cvs-"$1".list module-git-"$1".list &&
-       cat module-cvs-"$1".list | while read f
+       while read f
        do
                test_cmp_branch_file "$1" "$f" || return 1
-       done
+       done <module-cvs-"$1".list
 }
index d0736dd1a00d59cb1774860568136e94f8d23f04..b8a5bcb187653c960d9dec38374a4cd3587c1654 100644 (file)
@@ -15,3 +15,15 @@ empty_blob sha256:473a0f4c3be8a93681a267e3b1e9a7dcda1185436fe141f7749120a3037218
 
 empty_tree sha1:4b825dc642cb6eb9a060e54bf8d69288fbee4904
 empty_tree sha256:6ef19b41225c5369f1c104d45d8d85efa9b057b53b14b4b9b939dd74decc5321
+
+blob17_1 sha1:263
+blob17_1 sha256:34
+
+blob17_2 sha1:410
+blob17_2 sha256:174
+
+blob17_3 sha1:523
+blob17_3 sha256:313
+
+blob17_4 sha1:790
+blob17_4 sha256:481
index fcfc992b5b02e885fb3b8c337dfdc41d986a9052..412e4b450b16fbd63300e70faf026593fc073f57 100755 (executable)
@@ -33,7 +33,7 @@ do
 done
 
 git ls-tree -r HEAD >GEN_src_list
-nr_src_files=$(cat GEN_src_list | wc -l)
+nr_src_files=$(wc -l <GEN_src_list)
 
 src_branch=$(git symbolic-ref --short HEAD)
 
index 736516cc6a478ee548b79f7d9a0f2cae0b51430e..bf3bf604abe3470f19cc5a10e5c88c905cb28329 100755 (executable)
@@ -40,7 +40,7 @@ test_expect_success 'final setup + check rev-parse --git-dir' '
 
 test_expect_success 'check hash-object' '
        echo "foo" >bar &&
-       SHA=$(cat bar | git hash-object -w --stdin) &&
+       SHA=$(git hash-object -w --stdin <bar) &&
        test_path_is_file "$REAL/objects/$(objpath $SHA)"
 '
 
index 1cb6aa6824321656264e427f299899acf0754357..46e74ad1072b1d662297204960d38d823534c208 100755 (executable)
@@ -239,7 +239,7 @@ test_expect_success 'grow / shrink' '
        echo value40 >> expect &&
        echo size >> in &&
        echo 64 39 >> expect &&
-       cat in | test-tool hashmap > out &&
+       test-tool hashmap <in >out &&
        test_cmp expect out
 
 '
index 1b55f59c237c59d625241aea56679fbf9bb3bb25..ad151a346708a5898eb5bdc536131baddafdf987 100755 (executable)
@@ -131,8 +131,8 @@ do
                test_when_finished "rm -f crlf.utf${i}.raw lf.utf${i}.raw" &&
                test_when_finished "git reset --hard HEAD^" &&
 
-               cat lf.utf8.raw | write_utf${i} >lf.utf${i}.raw &&
-               cat crlf.utf8.raw | write_utf${i} >crlf.utf${i}.raw &&
+               write_utf${i} <lf.utf8.raw >lf.utf${i}.raw &&
+               write_utf${i} <crlf.utf8.raw >crlf.utf${i}.raw &&
                cp crlf.utf${i}.raw eol.utf${i} &&
 
                cat >expectIndexLF <<-EOF &&
index d1b3be872576789541410fa66d4473a006db99b2..f10f42ff1e4a8716609a1e8dd86e1441e020db3d 100755 (executable)
@@ -401,6 +401,21 @@ test_expect_success 'strip comments with changed comment char' '
        test -z "$(echo "; comment" | git -c core.commentchar=";" stripspace -s)"
 '
 
+test_expect_success 'strip comments with changed comment string' '
+       test ! -z "$(echo "// comment" | git -c core.commentchar=// stripspace)" &&
+       test -z "$(echo "// comment" | git -c core.commentchar="//" stripspace -s)"
+'
+
+test_expect_success 'newline as commentchar is forbidden' '
+       test_must_fail git -c core.commentChar="$LF" stripspace -s 2>err &&
+       grep "core.commentchar cannot contain newline" err
+'
+
+test_expect_success 'empty commentchar is forbidden' '
+       test_must_fail git -c core.commentchar= stripspace -s 2>err &&
+       grep "core.commentchar must have at least one character" err
+'
+
 test_expect_success '-c with single line' '
        printf "# foo\n" >expect &&
        printf "foo" | git stripspace -c >actual &&
index 4f2e0dcb02bda84409789b54f33237798c8f7e19..310a4500125f4a0d18f6c68a10cf1e8fe7d3beff 100755 (executable)
@@ -82,7 +82,7 @@ test_expect_success GETTEXT_ISO_LOCALE 'gettext.c: git init UTF-8 -> ISO-8859-1'
     printf "Bjó til tóma Git lind" >expect &&
     LANGUAGE=is LC_ALL="$is_IS_iso_locale" git init repo >actual &&
     test_when_finished "rm -rf repo" &&
-    grep "^$(cat expect | iconv -f UTF-8 -t ISO8859-1) " actual
+    grep "^$(iconv -f UTF-8 -t ISO8859-1 <expect) " actual
 '
 
 test_done
index cd3969e852bb06fa3361cd9e8e13d71c5fb514c4..69917d7b8459c6f3ad71326b25c9fabe731a5b4a 100755 (executable)
@@ -59,7 +59,9 @@ txt_to_synopsis () {
                -e '/^\[verse\]$/,/^$/ {
                        /^$/d;
                        /^\[verse\]$/d;
-
+                       s/_//g;
+                       s/++//g;
+                       s/`//g;
                        s/{litdd}/--/g;
                        s/'\''\(git[ a-z-]*\)'\''/\1/g;
 
index c309d2bae8a19816907b81d82cef9099b2fa21e9..7d4ab0b91aad75f8363f0f019664c81ae00b49cd 100755 (executable)
@@ -32,11 +32,16 @@ test_expect_success 'prepare a trivial repository' '
        HEAD=$(git rev-parse --verify HEAD)
 '
 
-test_expect_success 'pack_refs(PACK_REFS_ALL | PACK_REFS_PRUNE)' '
-       N=`find .git/refs -type f | wc -l` &&
+test_expect_success 'pack-refs --prune --all' '
+       test_path_is_missing .git/packed-refs &&
+       git pack-refs --no-prune --all &&
+       test_path_is_file .git/packed-refs &&
+       N=$(find .git/refs -type f | wc -l) &&
        test "$N" != 0 &&
-       test-tool ref-store main pack-refs PACK_REFS_PRUNE,PACK_REFS_ALL &&
-       N=`find .git/refs -type f` &&
+
+       git pack-refs --prune --all &&
+       test_path_is_file .git/packed-refs &&
+       N=$(find .git/refs -type f) &&
        test -z "$N"
 '
 
@@ -159,6 +164,13 @@ test_expect_success 'test --exclude takes precedence over --include' '
        git pack-refs --include "refs/heads/pack*" --exclude "refs/heads/pack*" &&
        test -f .git/refs/heads/dont_pack5'
 
+test_expect_success '--auto packs and prunes refs as usual' '
+       git branch auto &&
+       test_path_is_file .git/refs/heads/auto &&
+       git pack-refs --auto --all &&
+       test_path_is_missing .git/refs/heads/auto
+'
+
 test_expect_success 'see if up-to-date packed refs are preserved' '
        git branch q &&
        git pack-refs --all --prune &&
@@ -358,4 +370,14 @@ test_expect_success 'pack-refs does not drop broken refs during deletion' '
        test_cmp expect actual
 '
 
+test_expect_success 'maintenance --auto unconditionally packs loose refs' '
+       git update-ref refs/heads/something HEAD &&
+       test_path_is_file .git/refs/heads/something &&
+       git rev-parse refs/heads/something >expect &&
+       git maintenance run --task=pack-refs --auto &&
+       test_path_is_missing .git/refs/heads/something &&
+       git rev-parse refs/heads/something >actual &&
+       test_cmp expect actual
+'
+
 test_done
index 686781192eb7c2d820e28393b6d0352db3f4822e..931d888bbbcbc426dcf51a0478842fd4787bee1d 100755 (executable)
@@ -340,6 +340,26 @@ test_expect_success 'ref transaction: empty transaction in empty repo' '
        EOF
 '
 
+test_expect_success 'ref transaction: fails gracefully when auto compaction fails' '
+       test_when_finished "rm -rf repo" &&
+       git init repo &&
+       (
+               cd repo &&
+
+               test_commit A &&
+               for i in $(test_seq 10)
+               do
+                       git branch branch-$i &&
+                       for table in .git/reftable/*.ref
+                       do
+                               touch "$table.lock" || exit 1
+                       done ||
+                       exit 1
+               done &&
+               test_line_count = 13 .git/reftable/tables.list
+       )
+'
+
 test_expect_success 'pack-refs: compacts tables' '
        test_when_finished "rm -rf repo" &&
        git init repo &&
@@ -355,6 +375,65 @@ test_expect_success 'pack-refs: compacts tables' '
        test_line_count = 1 repo/.git/reftable/tables.list
 '
 
+test_expect_success 'pack-refs: compaction raises locking errors' '
+       test_when_finished "rm -rf repo" &&
+       git init repo &&
+       test_commit -C repo A &&
+       touch repo/.git/reftable/tables.list.lock &&
+       cat >expect <<-EOF &&
+       error: unable to compact stack: data is locked
+       EOF
+       test_must_fail git -C repo pack-refs 2>err &&
+       test_cmp expect err
+'
+
+for command in pack-refs gc "maintenance run --task=pack-refs"
+do
+test_expect_success "$command: auto compaction" '
+       test_when_finished "rm -rf repo" &&
+       git init repo &&
+       (
+               cd repo &&
+
+               test_commit A &&
+
+               # We need a bit of setup to ensure that git-gc(1) actually
+               # triggers, and that it does not write anything to the refdb.
+               git config gc.auto 1 &&
+               git config gc.autoDetach 0 &&
+               git config gc.reflogExpire never &&
+               git config gc.reflogExpireUnreachable never &&
+               test_oid blob17_1 | git hash-object -w --stdin &&
+
+               # The tables should have been auto-compacted, and thus auto
+               # compaction should not have to do anything.
+               ls -1 .git/reftable >tables-expect &&
+               test_line_count = 4 tables-expect &&
+               git $command --auto &&
+               ls -1 .git/reftable >tables-actual &&
+               test_cmp tables-expect tables-actual &&
+
+               test_oid blob17_2 | git hash-object -w --stdin &&
+
+               # Lock all tables write some refs. Auto-compaction will be
+               # unable to compact tables and thus fails gracefully, leaving
+               # the stack in a sub-optimal state.
+               ls .git/reftable/*.ref |
+               while read table
+               do
+                       touch "$table.lock" || exit 1
+               done &&
+               git branch B &&
+               git branch C &&
+               rm .git/reftable/*.lock &&
+               test_line_count = 5 .git/reftable/tables.list &&
+
+               git $command --auto &&
+               test_line_count = 1 .git/reftable/tables.list
+       )
+'
+done
+
 test_expect_success 'pack-refs: prunes stale tables' '
        test_when_finished "rm -rf repo" &&
        git init repo &&
index e0c6482797e1203a9e97ec27714d5ea48aaf1b6c..e12b2219721c4e9e1f29dd5eeb2e9b787d9b78db 100755 (executable)
@@ -112,65 +112,65 @@ strlen () {
 
 run_tests () {
     type=$1
-    sha1=$2
+    oid=$2
     size=$3
     content=$4
     pretty_content=$5
 
-    batch_output="$sha1 $type $size
+    batch_output="$oid $type $size
 $content"
 
     test_expect_success "$type exists" '
-       git cat-file -e $sha1
+       git cat-file -e $oid
     '
 
     test_expect_success "Type of $type is correct" '
        echo $type >expect &&
-       git cat-file -t $sha1 >actual &&
+       git cat-file -t $oid >actual &&
        test_cmp expect actual
     '
 
     test_expect_success "Size of $type is correct" '
        echo $size >expect &&
-       git cat-file -s $sha1 >actual &&
+       git cat-file -s $oid >actual &&
        test_cmp expect actual
     '
 
     test_expect_success "Type of $type is correct using --allow-unknown-type" '
        echo $type >expect &&
-       git cat-file -t --allow-unknown-type $sha1 >actual &&
+       git cat-file -t --allow-unknown-type $oid >actual &&
        test_cmp expect actual
     '
 
     test_expect_success "Size of $type is correct using --allow-unknown-type" '
        echo $size >expect &&
-       git cat-file -s --allow-unknown-type $sha1 >actual &&
+       git cat-file -s --allow-unknown-type $oid >actual &&
        test_cmp expect actual
     '
 
     test -z "$content" ||
     test_expect_success "Content of $type is correct" '
        echo_without_newline "$content" >expect &&
-       git cat-file $type $sha1 >actual &&
+       git cat-file $type $oid >actual &&
        test_cmp expect actual
     '
 
     test_expect_success "Pretty content of $type is correct" '
        echo_without_newline "$pretty_content" >expect &&
-       git cat-file -p $sha1 >actual &&
+       git cat-file -p $oid >actual &&
        test_cmp expect actual
     '
 
     test -z "$content" ||
     test_expect_success "--batch output of $type is correct" '
        echo "$batch_output" >expect &&
-       echo $sha1 | git cat-file --batch >actual &&
+       echo $oid | git cat-file --batch >actual &&
        test_cmp expect actual
     '
 
     test_expect_success "--batch-check output of $type is correct" '
-       echo "$sha1 $type $size" >expect &&
-       echo_without_newline $sha1 | git cat-file --batch-check >actual &&
+       echo "$oid $type $size" >expect &&
+       echo_without_newline $oid | git cat-file --batch-check >actual &&
        test_cmp expect actual
     '
 
@@ -179,33 +179,33 @@ $content"
        test -z "$content" ||
                test_expect_success "--batch-command $opt output of $type content is correct" '
                echo "$batch_output" >expect &&
-               test_write_lines "contents $sha1" | git cat-file --batch-command $opt >actual &&
+               test_write_lines "contents $oid" | git cat-file --batch-command $opt >actual &&
                test_cmp expect actual
        '
 
        test_expect_success "--batch-command $opt output of $type info is correct" '
-               echo "$sha1 $type $size" >expect &&
-               test_write_lines "info $sha1" |
+               echo "$oid $type $size" >expect &&
+               test_write_lines "info $oid" |
                git cat-file --batch-command $opt >actual &&
                test_cmp expect actual
        '
     done
 
     test_expect_success "custom --batch-check format" '
-       echo "$type $sha1" >expect &&
-       echo $sha1 | git cat-file --batch-check="%(objecttype) %(objectname)" >actual &&
+       echo "$type $oid" >expect &&
+       echo $oid | git cat-file --batch-check="%(objecttype) %(objectname)" >actual &&
        test_cmp expect actual
     '
 
     test_expect_success "custom --batch-command format" '
-       echo "$type $sha1" >expect &&
-       echo "info $sha1" | git cat-file --batch-command="%(objecttype) %(objectname)" >actual &&
+       echo "$type $oid" >expect &&
+       echo "info $oid" | git cat-file --batch-command="%(objecttype) %(objectname)" >actual &&
        test_cmp expect actual
     '
 
     test_expect_success '--batch-check with %(rest)' '
        echo "$type this is some extra content" >expect &&
-       echo "$sha1    this is some extra content" |
+       echo "$oid    this is some extra content" |
                git cat-file --batch-check="%(objecttype) %(rest)" >actual &&
        test_cmp expect actual
     '
@@ -216,7 +216,7 @@ $content"
                echo "$size" &&
                echo "$content"
        } >expect &&
-       echo $sha1 | git cat-file --batch="%(objectsize)" >actual &&
+       echo $oid | git cat-file --batch="%(objectsize)" >actual &&
        test_cmp expect actual
     '
 
@@ -226,114 +226,154 @@ $content"
                echo "$type" &&
                echo "$content"
        } >expect &&
-       echo $sha1 | git cat-file --batch="%(objecttype)" >actual &&
+       echo $oid | git cat-file --batch="%(objecttype)" >actual &&
        test_cmp expect actual
     '
 }
 
 hello_content="Hello World"
 hello_size=$(strlen "$hello_content")
-hello_sha1=$(echo_without_newline "$hello_content" | git hash-object --stdin)
+hello_oid=$(echo_without_newline "$hello_content" | git hash-object --stdin)
 
 test_expect_success "setup" '
+       git config core.repositoryformatversion 1 &&
+       git config extensions.objectformat $test_hash_algo &&
+       git config extensions.compatobjectformat $test_compat_hash_algo &&
        echo_without_newline "$hello_content" > hello &&
        git update-index --add hello
 '
 
-run_tests 'blob' $hello_sha1 $hello_size "$hello_content" "$hello_content"
+run_blob_tests () {
+    oid=$1
 
-test_expect_success '--batch-command --buffer with flush for blob info' '
-       echo "$hello_sha1 blob $hello_size" >expect &&
-       test_write_lines "info $hello_sha1" "flush" |
+    run_tests 'blob' $oid $hello_size "$hello_content" "$hello_content"
+
+    test_expect_success '--batch-command --buffer with flush for blob info' '
+       echo "$oid blob $hello_size" >expect &&
+       test_write_lines "info $oid" "flush" |
        GIT_TEST_CAT_FILE_NO_FLUSH_ON_EXIT=1 \
        git cat-file --batch-command --buffer >actual &&
        test_cmp expect actual
-'
+    '
 
-test_expect_success '--batch-command --buffer without flush for blob info' '
+    test_expect_success '--batch-command --buffer without flush for blob info' '
        touch output &&
-       test_write_lines "info $hello_sha1" |
+       test_write_lines "info $oid" |
        GIT_TEST_CAT_FILE_NO_FLUSH_ON_EXIT=1 \
        git cat-file --batch-command --buffer >>output &&
        test_must_be_empty output
-'
+    '
+}
+
+hello_compat_oid=$(git rev-parse --output-object-format=$test_compat_hash_algo $hello_oid)
+run_blob_tests $hello_oid
+run_blob_tests $hello_compat_oid
 
 test_expect_success '--batch-check without %(rest) considers whole line' '
-       echo "$hello_sha1 blob $hello_size" >expect &&
-       git update-index --add --cacheinfo 100644 $hello_sha1 "white space" &&
+       echo "$hello_oid blob $hello_size" >expect &&
+       git update-index --add --cacheinfo 100644 $hello_oid "white space" &&
        test_when_finished "git update-index --remove \"white space\"" &&
        echo ":white space" | git cat-file --batch-check >actual &&
        test_cmp expect actual
 '
 
-tree_sha1=$(git write-tree)
+tree_oid=$(git write-tree)
+tree_compat_oid=$(git rev-parse --output-object-format=$test_compat_hash_algo $tree_oid)
 tree_size=$(($(test_oid rawsz) + 13))
-tree_pretty_content="100644 blob $hello_sha1   hello${LF}"
+tree_compat_size=$(($(test_oid --hash=compat rawsz) + 13))
+tree_pretty_content="100644 blob $hello_oid    hello${LF}"
+tree_compat_pretty_content="100644 blob $hello_compat_oid      hello${LF}"
 
-run_tests 'tree' $tree_sha1 $tree_size "" "$tree_pretty_content"
+run_tests 'tree' $tree_oid $tree_size "" "$tree_pretty_content"
+run_tests 'tree' $tree_compat_oid $tree_compat_size "" "$tree_compat_pretty_content"
 
 commit_message="Initial commit"
-commit_sha1=$(echo_without_newline "$commit_message" | git commit-tree $tree_sha1)
+commit_oid=$(echo_without_newline "$commit_message" | git commit-tree $tree_oid)
+commit_compat_oid=$(git rev-parse --output-object-format=$test_compat_hash_algo $commit_oid)
 commit_size=$(($(test_oid hexsz) + 137))
-commit_content="tree $tree_sha1
+commit_compat_size=$(($(test_oid --hash=compat hexsz) + 137))
+commit_content="tree $tree_oid
+author $GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL> $GIT_AUTHOR_DATE
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+
+$commit_message"
+
+commit_compat_content="tree $tree_compat_oid
 author $GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL> $GIT_AUTHOR_DATE
 committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
 
 $commit_message"
 
-run_tests 'commit' $commit_sha1 $commit_size "$commit_content" "$commit_content"
+run_tests 'commit' $commit_oid $commit_size "$commit_content" "$commit_content"
+run_tests 'commit' $commit_compat_oid $commit_compat_size "$commit_compat_content" "$commit_compat_content"
 
-tag_header_without_timestamp="object $hello_sha1
-type blob
+tag_header_without_oid="type blob
 tag hellotag
 tagger $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>"
+tag_header_without_timestamp="object $hello_oid
+$tag_header_without_oid"
+tag_compat_header_without_timestamp="object $hello_compat_oid
+$tag_header_without_oid"
 tag_description="This is a tag"
 tag_content="$tag_header_without_timestamp 0 +0000
 
 $tag_description"
+tag_compat_content="$tag_compat_header_without_timestamp 0 +0000
 
-tag_sha1=$(echo_without_newline "$tag_content" | git hash-object -t tag --stdin -w)
+$tag_description"
+
+tag_oid=$(echo_without_newline "$tag_content" | git hash-object -t tag --stdin -w)
 tag_size=$(strlen "$tag_content")
 
-run_tests 'tag' $tag_sha1 $tag_size "$tag_content" "$tag_content"
+tag_compat_oid=$(git rev-parse --output-object-format=$test_compat_hash_algo $tag_oid)
+tag_compat_size=$(strlen "$tag_compat_content")
+
+run_tests 'tag' $tag_oid $tag_size "$tag_content" "$tag_content"
+run_tests 'tag' $tag_compat_oid $tag_compat_size "$tag_compat_content" "$tag_compat_content"
 
 test_expect_success "Reach a blob from a tag pointing to it" '
        echo_without_newline "$hello_content" >expect &&
-       git cat-file blob $tag_sha1 >actual &&
+       git cat-file blob $tag_oid >actual &&
        test_cmp expect actual
 '
 
-for batch in batch batch-check batch-command
+for oid in $hello_oid $hello_compat_oid
 do
-    for opt in t s e p
+    for batch in batch batch-check batch-command
     do
+       for opt in t s e p
+       do
        test_expect_success "Passing -$opt with --$batch fails" '
-           test_must_fail git cat-file --$batch -$opt $hello_sha1
+           test_must_fail git cat-file --$batch -$opt $oid
        '
 
        test_expect_success "Passing --$batch with -$opt fails" '
-           test_must_fail git cat-file -$opt --$batch $hello_sha1
+           test_must_fail git cat-file -$opt --$batch $oid
        '
-    done
+       done
 
-    test_expect_success "Passing <type> with --$batch fails" '
-       test_must_fail git cat-file --$batch blob $hello_sha1
-    '
+       test_expect_success "Passing <type> with --$batch fails" '
+       test_must_fail git cat-file --$batch blob $oid
+       '
 
-    test_expect_success "Passing --$batch with <type> fails" '
-       test_must_fail git cat-file blob --$batch $hello_sha1
-    '
+       test_expect_success "Passing --$batch with <type> fails" '
+       test_must_fail git cat-file blob --$batch $oid
+       '
 
-    test_expect_success "Passing sha1 with --$batch fails" '
-       test_must_fail git cat-file --$batch $hello_sha1
-    '
+       test_expect_success "Passing oid with --$batch fails" '
+       test_must_fail git cat-file --$batch $oid
+       '
+    done
 done
 
-for opt in t s e p
+for oid in $hello_oid $hello_compat_oid
 do
-    test_expect_success "Passing -$opt with --follow-symlinks fails" '
-           test_must_fail git cat-file --follow-symlinks -$opt $hello_sha1
+    for opt in t s e p
+    do
+       test_expect_success "Passing -$opt with --follow-symlinks fails" '
+           test_must_fail git cat-file --follow-symlinks -$opt $oid
        '
+    done
 done
 
 test_expect_success "--batch-check for a non-existent named object" '
@@ -360,12 +400,12 @@ test_expect_success "--batch-check for a non-existent hash" '
 
 test_expect_success "--batch for an existent and a non-existent hash" '
        cat >expect <<-EOF &&
-       $tag_sha1 tag $tag_size
+       $tag_oid tag $tag_size
        $tag_content
        0000000000000000000000000000000000000000 missing
        EOF
 
-       printf "$tag_sha1\n0000000000000000000000000000000000000000" >in &&
+       printf "$tag_oid\n0000000000000000000000000000000000000000" >in &&
        git cat-file --batch <in >actual &&
        test_cmp expect actual
 '
@@ -386,112 +426,102 @@ test_expect_success 'empty --batch-check notices missing object' '
        test_cmp expect actual
 '
 
-batch_input="$hello_sha1
-$commit_sha1
-$tag_sha1
+batch_tests () {
+    boid=$1
+    loid=$2
+    lsize=$3
+    coid=$4
+    csize=$5
+    ccontent=$6
+    toid=$7
+    tsize=$8
+    tcontent=$9
+
+    batch_input="$boid
+$coid
+$toid
 deadbeef
 
 "
 
-printf "%s\0" \
-       "$hello_sha1 blob $hello_size" \
+    printf "%s\0" \
+       "$boid blob $hello_size" \
        "$hello_content" \
-       "$commit_sha1 commit $commit_size" \
-       "$commit_content" \
-       "$tag_sha1 tag $tag_size" \
-       "$tag_content" \
+       "$coid commit $csize" \
+       "$ccontent" \
+       "$toid tag $tsize" \
+       "$tcontent" \
        "deadbeef missing" \
        " missing" >batch_output
 
-test_expect_success '--batch with multiple sha1s gives correct format' '
+    test_expect_success '--batch with multiple oids gives correct format' '
        tr "\0" "\n" <batch_output >expect &&
        echo_without_newline "$batch_input" >in &&
        git cat-file --batch <in >actual &&
        test_cmp expect actual
-'
+    '
 
-test_expect_success '--batch, -z with multiple sha1s gives correct format' '
+    test_expect_success '--batch, -z with multiple oids gives correct format' '
        echo_without_newline_nul "$batch_input" >in &&
        tr "\0" "\n" <batch_output >expect &&
        git cat-file --batch -z <in >actual &&
        test_cmp expect actual
-'
+    '
 
-test_expect_success '--batch, -Z with multiple sha1s gives correct format' '
+    test_expect_success '--batch, -Z with multiple oids gives correct format' '
        echo_without_newline_nul "$batch_input" >in &&
        git cat-file --batch -Z <in >actual &&
        test_cmp batch_output actual
-'
+    '
 
-batch_check_input="$hello_sha1
-$tree_sha1
-$commit_sha1
-$tag_sha1
+batch_check_input="$boid
+$loid
+$coid
+$toid
 deadbeef
 
 "
 
-printf "%s\0" \
-       "$hello_sha1 blob $hello_size" \
-       "$tree_sha1 tree $tree_size" \
-       "$commit_sha1 commit $commit_size" \
-       "$tag_sha1 tag $tag_size" \
+    printf "%s\0" \
+       "$boid blob $hello_size" \
+       "$loid tree $lsize" \
+       "$coid commit $csize" \
+       "$toid tag $tsize" \
        "deadbeef missing" \
        " missing" >batch_check_output
 
-test_expect_success "--batch-check with multiple sha1s gives correct format" '
+    test_expect_success "--batch-check with multiple oids gives correct format" '
        tr "\0" "\n" <batch_check_output >expect &&
        echo_without_newline "$batch_check_input" >in &&
        git cat-file --batch-check <in >actual &&
        test_cmp expect actual
-'
+    '
 
-test_expect_success "--batch-check, -z with multiple sha1s gives correct format" '
+    test_expect_success "--batch-check, -z with multiple oids gives correct format" '
        tr "\0" "\n" <batch_check_output >expect &&
        echo_without_newline_nul "$batch_check_input" >in &&
        git cat-file --batch-check -z <in >actual &&
        test_cmp expect actual
-'
+    '
 
-test_expect_success "--batch-check, -Z with multiple sha1s gives correct format" '
+    test_expect_success "--batch-check, -Z with multiple oids gives correct format" '
        echo_without_newline_nul "$batch_check_input" >in &&
        git cat-file --batch-check -Z <in >actual &&
        test_cmp batch_check_output actual
-'
-
-test_expect_success FUNNYNAMES 'setup with newline in input' '
-       touch -- "newline${LF}embedded" &&
-       git add -- "newline${LF}embedded" &&
-       git commit -m "file with newline embedded" &&
-       test_tick &&
-
-       printf "HEAD:newline${LF}embedded" >in
-'
-
-test_expect_success FUNNYNAMES '--batch-check, -z with newline in input' '
-       git cat-file --batch-check -z <in >actual &&
-       echo "$(git rev-parse "HEAD:newline${LF}embedded") blob 0" >expect &&
-       test_cmp expect actual
-'
-
-test_expect_success FUNNYNAMES '--batch-check, -Z with newline in input' '
-       git cat-file --batch-check -Z <in >actual &&
-       printf "%s\0" "$(git rev-parse "HEAD:newline${LF}embedded") blob 0" >expect &&
-       test_cmp expect actual
-'
+    '
 
-batch_command_multiple_info="info $hello_sha1
-info $tree_sha1
-info $commit_sha1
-info $tag_sha1
+batch_command_multiple_info="info $boid
+info $loid
+info $coid
+info $toid
 info deadbeef"
 
-test_expect_success '--batch-command with multiple info calls gives correct format' '
+    test_expect_success '--batch-command with multiple info calls gives correct format' '
        cat >expect <<-EOF &&
-       $hello_sha1 blob $hello_size
-       $tree_sha1 tree $tree_size
-       $commit_sha1 commit $commit_size
-       $tag_sha1 tag $tag_size
+       $boid blob $hello_size
+       $loid tree $lsize
+       $coid commit $csize
+       $toid tag $tsize
        deadbeef missing
        EOF
 
@@ -510,22 +540,22 @@ test_expect_success '--batch-command with multiple info calls gives correct form
        git cat-file --batch-command --buffer -Z <in >actual &&
 
        test_cmp expect_nul actual
-'
+    '
 
-batch_command_multiple_contents="contents $hello_sha1
-contents $commit_sha1
-contents $tag_sha1
+batch_command_multiple_contents="contents $boid
+contents $coid
+contents $toid
 contents deadbeef
 flush"
 
-test_expect_success '--batch-command with multiple command calls gives correct format' '
+    test_expect_success '--batch-command with multiple command calls gives correct format' '
        printf "%s\0" \
-               "$hello_sha1 blob $hello_size" \
+               "$boid blob $hello_size" \
                "$hello_content" \
-               "$commit_sha1 commit $commit_size" \
-               "$commit_content" \
-               "$tag_sha1 tag $tag_size" \
-               "$tag_content" \
+               "$coid commit $csize" \
+               "$ccontent" \
+               "$toid tag $tsize" \
+               "$tcontent" \
                "deadbeef missing" >expect_nul &&
        tr "\0" "\n" <expect_nul >expect &&
 
@@ -543,6 +573,33 @@ test_expect_success '--batch-command with multiple command calls gives correct f
        git cat-file --batch-command --buffer -Z <in >actual &&
 
        test_cmp expect_nul actual
+    '
+
+}
+
+batch_tests $hello_oid $tree_oid $tree_size $commit_oid $commit_size "$commit_content" $tag_oid $tag_size "$tag_content"
+batch_tests $hello_compat_oid $tree_compat_oid $tree_compat_size $commit_compat_oid $commit_compat_size "$commit_compat_content" $tag_compat_oid $tag_compat_size "$tag_compat_content"
+
+
+test_expect_success FUNNYNAMES 'setup with newline in input' '
+       touch -- "newline${LF}embedded" &&
+       git add -- "newline${LF}embedded" &&
+       git commit -m "file with newline embedded" &&
+       test_tick &&
+
+       printf "HEAD:newline${LF}embedded" >in
+'
+
+test_expect_success FUNNYNAMES '--batch-check, -z with newline in input' '
+       git cat-file --batch-check -z <in >actual &&
+       echo "$(git rev-parse "HEAD:newline${LF}embedded") blob 0" >expect &&
+       test_cmp expect actual
+'
+
+test_expect_success FUNNYNAMES '--batch-check, -Z with newline in input' '
+       git cat-file --batch-check -Z <in >actual &&
+       printf "%s\0" "$(git rev-parse "HEAD:newline${LF}embedded") blob 0" >expect &&
+       test_cmp expect actual
 '
 
 test_expect_success 'setup blobs which are likely to delta' '
@@ -569,7 +626,7 @@ test_expect_success 'confirm that neither loose blob is a delta' '
 # we will check only that one of the two objects is a delta
 # against the other, but not the order. We can do so by just
 # asking for the base of both, and checking whether either
-# sha1 appears in the output.
+# oid appears in the output.
 test_expect_success '%(deltabase) reports packed delta bases' '
        git repack -ad &&
        git cat-file --batch-check="%(deltabase)" <blobs >actual &&
@@ -583,12 +640,12 @@ test_expect_success 'setup bogus data' '
        bogus_short_type="bogus" &&
        bogus_short_content="bogus" &&
        bogus_short_size=$(strlen "$bogus_short_content") &&
-       bogus_short_sha1=$(echo_without_newline "$bogus_short_content" | git hash-object -t $bogus_short_type --literally -w --stdin) &&
+       bogus_short_oid=$(echo_without_newline "$bogus_short_content" | git hash-object -t $bogus_short_type --literally -w --stdin) &&
 
        bogus_long_type="abcdefghijklmnopqrstuvwxyz1234679" &&
        bogus_long_content="bogus" &&
        bogus_long_size=$(strlen "$bogus_long_content") &&
-       bogus_long_sha1=$(echo_without_newline "$bogus_long_content" | git hash-object -t $bogus_long_type --literally -w --stdin)
+       bogus_long_oid=$(echo_without_newline "$bogus_long_content" | git hash-object -t $bogus_long_type --literally -w --stdin)
 '
 
 for arg1 in '' --allow-unknown-type
@@ -608,9 +665,9 @@ do
 
                        if test "$arg1" = "--allow-unknown-type"
                        then
-                               git cat-file $arg1 $arg2 $bogus_short_sha1
+                               git cat-file $arg1 $arg2 $bogus_short_oid
                        else
-                               test_must_fail git cat-file $arg1 $arg2 $bogus_short_sha1 >out 2>actual &&
+                               test_must_fail git cat-file $arg1 $arg2 $bogus_short_oid >out 2>actual &&
                                test_must_be_empty out &&
                                test_cmp expect actual
                        fi
@@ -620,21 +677,21 @@ do
                        if test "$arg2" = "-p"
                        then
                                cat >expect <<-EOF
-                               error: header for $bogus_long_sha1 too long, exceeds 32 bytes
-                               fatal: Not a valid object name $bogus_long_sha1
+                               error: header for $bogus_long_oid too long, exceeds 32 bytes
+                               fatal: Not a valid object name $bogus_long_oid
                                EOF
                        else
                                cat >expect <<-EOF
-                               error: header for $bogus_long_sha1 too long, exceeds 32 bytes
+                               error: header for $bogus_long_oid too long, exceeds 32 bytes
                                fatal: git cat-file: could not get object info
                                EOF
                        fi &&
 
                        if test "$arg1" = "--allow-unknown-type"
                        then
-                               git cat-file $arg1 $arg2 $bogus_short_sha1
+                               git cat-file $arg1 $arg2 $bogus_short_oid
                        else
-                               test_must_fail git cat-file $arg1 $arg2 $bogus_long_sha1 >out 2>actual &&
+                               test_must_fail git cat-file $arg1 $arg2 $bogus_long_oid >out 2>actual &&
                                test_must_be_empty out &&
                                test_cmp expect actual
                        fi
@@ -668,28 +725,28 @@ do
 done
 
 test_expect_success '-e is OK with a broken object without --allow-unknown-type' '
-       git cat-file -e $bogus_short_sha1
+       git cat-file -e $bogus_short_oid
 '
 
 test_expect_success '-e can not be combined with --allow-unknown-type' '
-       test_expect_code 128 git cat-file -e --allow-unknown-type $bogus_short_sha1
+       test_expect_code 128 git cat-file -e --allow-unknown-type $bogus_short_oid
 '
 
 test_expect_success '-p cannot print a broken object even with --allow-unknown-type' '
-       test_must_fail git cat-file -p $bogus_short_sha1 &&
-       test_expect_code 128 git cat-file -p --allow-unknown-type $bogus_short_sha1
+       test_must_fail git cat-file -p $bogus_short_oid &&
+       test_expect_code 128 git cat-file -p --allow-unknown-type $bogus_short_oid
 '
 
 test_expect_success '<type> <hash> does not work with objects of broken types' '
        cat >err.expect <<-\EOF &&
        fatal: invalid object type "bogus"
        EOF
-       test_must_fail git cat-file $bogus_short_type $bogus_short_sha1 2>err.actual &&
+       test_must_fail git cat-file $bogus_short_type $bogus_short_oid 2>err.actual &&
        test_cmp err.expect err.actual
 '
 
 test_expect_success 'broken types combined with --batch and --batch-check' '
-       echo $bogus_short_sha1 >bogus-oid &&
+       echo $bogus_short_oid >bogus-oid &&
 
        cat >err.expect <<-\EOF &&
        fatal: invalid object type
@@ -711,52 +768,52 @@ test_expect_success 'the --allow-unknown-type option does not consider replaceme
        cat >expect <<-EOF &&
        $bogus_short_type
        EOF
-       git cat-file -t --allow-unknown-type $bogus_short_sha1 >actual &&
+       git cat-file -t --allow-unknown-type $bogus_short_oid >actual &&
        test_cmp expect actual &&
 
        # Create it manually, as "git replace" will die on bogus
        # types.
        head=$(git rev-parse --verify HEAD) &&
-       test_when_finished "test-tool ref-store main delete-refs 0 msg refs/replace/$bogus_short_sha1" &&
-       test-tool ref-store main update-ref msg "refs/replace/$bogus_short_sha1" $head $ZERO_OID REF_SKIP_OID_VERIFICATION &&
+       test_when_finished "test-tool ref-store main delete-refs 0 msg refs/replace/$bogus_short_oid" &&
+       test-tool ref-store main update-ref msg "refs/replace/$bogus_short_oid" $head $ZERO_OID REF_SKIP_OID_VERIFICATION &&
 
        cat >expect <<-EOF &&
        commit
        EOF
-       git cat-file -t --allow-unknown-type $bogus_short_sha1 >actual &&
+       git cat-file -t --allow-unknown-type $bogus_short_oid >actual &&
        test_cmp expect actual
 '
 
 test_expect_success "Type of broken object is correct" '
        echo $bogus_short_type >expect &&
-       git cat-file -t --allow-unknown-type $bogus_short_sha1 >actual &&
+       git cat-file -t --allow-unknown-type $bogus_short_oid >actual &&
        test_cmp expect actual
 '
 
 test_expect_success "Size of broken object is correct" '
        echo $bogus_short_size >expect &&
-       git cat-file -s --allow-unknown-type $bogus_short_sha1 >actual &&
+       git cat-file -s --allow-unknown-type $bogus_short_oid >actual &&
        test_cmp expect actual
 '
 
 test_expect_success 'clean up broken object' '
-       rm .git/objects/$(test_oid_to_path $bogus_short_sha1)
+       rm .git/objects/$(test_oid_to_path $bogus_short_oid)
 '
 
 test_expect_success "Type of broken object is correct when type is large" '
        echo $bogus_long_type >expect &&
-       git cat-file -t --allow-unknown-type $bogus_long_sha1 >actual &&
+       git cat-file -t --allow-unknown-type $bogus_long_oid >actual &&
        test_cmp expect actual
 '
 
 test_expect_success "Size of large broken object is correct when type is large" '
        echo $bogus_long_size >expect &&
-       git cat-file -s --allow-unknown-type $bogus_long_sha1 >actual &&
+       git cat-file -s --allow-unknown-type $bogus_long_oid >actual &&
        test_cmp expect actual
 '
 
 test_expect_success 'clean up broken object' '
-       rm .git/objects/$(test_oid_to_path $bogus_long_sha1)
+       rm .git/objects/$(test_oid_to_path $bogus_long_oid)
 '
 
 test_expect_success 'cat-file -t and -s on corrupt loose object' '
@@ -853,7 +910,7 @@ test_expect_success 'prep for symlink tests' '
        test_ln_s_add loop2 loop1 &&
        git add morx dir/subdir/ind2 dir/ind1 &&
        git commit -am "test" &&
-       echo $hello_sha1 blob $hello_size >found
+       echo $hello_oid blob $hello_size >found
 '
 
 test_expect_success 'git cat-file --batch-check --follow-symlinks works for non-links' '
@@ -941,7 +998,7 @@ test_expect_success 'git cat-file --batch-check --follow-symlinks works for dir/
        echo HEAD:dirlink/morx >>expect &&
        echo HEAD:dirlink/morx | git cat-file --batch-check --follow-symlinks >actual &&
        test_cmp expect actual &&
-       echo $hello_sha1 blob $hello_size >expect &&
+       echo $hello_oid blob $hello_size >expect &&
        echo HEAD:dirlink/ind1 | git cat-file --batch-check --follow-symlinks >actual &&
        test_cmp expect actual
 '
index ac3d173767ae72be7bc43e4df269d88fb040f7ba..64aea3848606ccda03b5dd82f878e8d1e8351706 100755 (executable)
@@ -124,8 +124,8 @@ test_expect_success 'check that appropriate filter is invoke when --path is used
        path0_sha=$(git hash-object --path=file0 file1) &&
        test "$file0_sha" = "$path0_sha" &&
        test "$file1_sha" = "$path1_sha" &&
-       path1_sha=$(cat file0 | git hash-object --path=file1 --stdin) &&
-       path0_sha=$(cat file1 | git hash-object --path=file0 --stdin) &&
+       path1_sha=$(git hash-object --path=file1 --stdin <file0) &&
+       path0_sha=$(git hash-object --path=file0 --stdin <file1) &&
        test "$file0_sha" = "$path0_sha" &&
        test "$file1_sha" = "$path1_sha"
 '
@@ -154,7 +154,7 @@ test_expect_success '--path works in a subdirectory' '
 test_expect_success 'check that --no-filters option works' '
        nofilters_file1=$(git hash-object --no-filters file1) &&
        test "$file0_sha" = "$nofilters_file1" &&
-       nofilters_file1=$(cat file1 | git hash-object --stdin) &&
+       nofilters_file1=$(git hash-object --stdin <file1) &&
        test "$file0_sha" = "$nofilters_file1"
 '
 
diff --git a/t/t1016-compatObjectFormat.sh b/t/t1016-compatObjectFormat.sh
new file mode 100755 (executable)
index 0000000..8132cd3
--- /dev/null
@@ -0,0 +1,281 @@
+#!/bin/sh
+#
+# Copyright (c) 2023 Eric Biederman
+#
+
+test_description='Test how well compatObjectFormat works'
+
+TEST_PASSES_SANITIZE_LEAK=true
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-gpg.sh
+
+# All of the follow variables must be defined in the environment:
+# GIT_AUTHOR_NAME
+# GIT_AUTHOR_EMAIL
+# GIT_AUTHOR_DATE
+# GIT_COMMITTER_NAME
+# GIT_COMMITTER_EMAIL
+# GIT_COMMITTER_DATE
+#
+# The test relies on these variables being set so that the two
+# different commits in two different repositories encoded with two
+# different hash functions result in the same content in the commits.
+# This means that when the commit is translated between hash functions
+# the commit is identical to the commit in the other repository.
+
+compat_hash () {
+    case "$1" in
+    "sha1")
+       echo "sha256"
+       ;;
+    "sha256")
+       echo "sha1"
+       ;;
+    esac
+}
+
+hello_oid () {
+    case "$1" in
+    "sha1")
+       echo "$hello_sha1_oid"
+       ;;
+    "sha256")
+       echo "$hello_sha256_oid"
+       ;;
+    esac
+}
+
+tree_oid () {
+    case "$1" in
+    "sha1")
+       echo "$tree_sha1_oid"
+       ;;
+    "sha256")
+       echo "$tree_sha256_oid"
+       ;;
+    esac
+}
+
+commit_oid () {
+    case "$1" in
+    "sha1")
+       echo "$commit_sha1_oid"
+       ;;
+    "sha256")
+       echo "$commit_sha256_oid"
+       ;;
+    esac
+}
+
+commit2_oid () {
+    case "$1" in
+    "sha1")
+       echo "$commit2_sha1_oid"
+       ;;
+    "sha256")
+       echo "$commit2_sha256_oid"
+       ;;
+    esac
+}
+
+del_sigcommit () {
+    local delete=$1
+
+    if test "$delete" = "sha256" ; then
+       local pattern="gpgsig-sha256"
+    else
+       local pattern="gpgsig"
+    fi
+    test-tool delete-gpgsig "$pattern"
+}
+
+
+del_sigtag () {
+    local storage=$1
+    local delete=$2
+
+    if test "$storage" = "$delete" ; then
+       local pattern="trailer"
+    elif test "$storage" = "sha256" ; then
+       local pattern="gpgsig"
+    else
+       local pattern="gpgsig-sha256"
+    fi
+    test-tool delete-gpgsig "$pattern"
+}
+
+base=$(pwd)
+for hash in sha1 sha256
+do
+       cd "$base"
+       mkdir -p repo-$hash
+       cd repo-$hash
+
+       test_expect_success "setup $hash repository" '
+               git init --object-format=$hash &&
+               git config core.repositoryformatversion 1 &&
+               git config extensions.objectformat $hash &&
+               git config extensions.compatobjectformat $(compat_hash $hash) &&
+               git config gpg.program $TEST_DIRECTORY/t1016/gpg &&
+               echo "Hellow World!" > hello &&
+               eval hello_${hash}_oid=$(git hash-object hello) &&
+               git update-index --add hello &&
+               git commit -m "Initial commit" &&
+               eval commit_${hash}_oid=$(git rev-parse HEAD) &&
+               eval tree_${hash}_oid=$(git rev-parse HEAD^{tree})
+       '
+       test_expect_success "create a $hash  tagged blob" '
+               git tag --no-sign -m "This is a tag" hellotag $(hello_oid $hash) &&
+               eval hellotag_${hash}_oid=$(git rev-parse hellotag)
+       '
+       test_expect_success "create a $hash tagged tree" '
+               git tag --no-sign -m "This is a tag" treetag $(tree_oid $hash) &&
+               eval treetag_${hash}_oid=$(git rev-parse treetag)
+       '
+       test_expect_success "create a $hash tagged commit" '
+               git tag --no-sign -m "This is a tag" committag $(commit_oid $hash) &&
+               eval committag_${hash}_oid=$(git rev-parse committag)
+       '
+       test_expect_success GPG2 "create a $hash signed commit" '
+               git commit --gpg-sign --allow-empty -m "This is a signed commit" &&
+               eval signedcommit_${hash}_oid=$(git rev-parse HEAD)
+       '
+       test_expect_success GPG2 "create a $hash signed tag" '
+               git tag -s -m "This is a signed tag" signedtag HEAD &&
+               eval signedtag_${hash}_oid=$(git rev-parse signedtag)
+       '
+       test_expect_success "create a $hash branch" '
+               git checkout -b branch $(commit_oid $hash) &&
+               echo "More more more give me more!" > more &&
+               eval more_${hash}_oid=$(git hash-object more) &&
+               echo "Another and another and another" > another &&
+               eval another_${hash}_oid=$(git hash-object another) &&
+               git update-index --add more another &&
+               git commit -m "Add more files!" &&
+               eval commit2_${hash}_oid=$(git rev-parse HEAD) &&
+               eval tree2_${hash}_oid=$(git rev-parse HEAD^{tree})
+       '
+       test_expect_success GPG2 "create another $hash signed tag" '
+               git tag -s -m "This is another signed tag" signedtag2 $(commit2_oid $hash) &&
+               eval signedtag2_${hash}_oid=$(git rev-parse signedtag2)
+       '
+       test_expect_success GPG2 "merge the $hash branches together" '
+               git merge -S -m "merge some signed tags together" signedtag signedtag2 &&
+               eval signedcommit2_${hash}_oid=$(git rev-parse HEAD)
+       '
+       test_expect_success GPG2 "create additional $hash signed commits" '
+               git commit --gpg-sign --allow-empty -m "This is an additional signed commit" &&
+               git cat-file commit HEAD | del_sigcommit sha256 > "../${hash}_signedcommit3" &&
+               git cat-file commit HEAD | del_sigcommit sha1 > "../${hash}_signedcommit4" &&
+               eval signedcommit3_${hash}_oid=$(git hash-object -t commit -w ../${hash}_signedcommit3) &&
+               eval signedcommit4_${hash}_oid=$(git hash-object -t commit -w ../${hash}_signedcommit4)
+       '
+       test_expect_success GPG2 "create additional $hash signed tags" '
+               git tag -s -m "This is an additional signed tag" signedtag34 HEAD &&
+               git cat-file tag signedtag34 | del_sigtag "${hash}" sha256 > ../${hash}_signedtag3 &&
+               git cat-file tag signedtag34 | del_sigtag "${hash}" sha1 > ../${hash}_signedtag4 &&
+               eval signedtag3_${hash}_oid=$(git hash-object -t tag -w ../${hash}_signedtag3) &&
+               eval signedtag4_${hash}_oid=$(git hash-object -t tag -w ../${hash}_signedtag4)
+       '
+done
+cd "$base"
+
+compare_oids () {
+    test "$#" = 5 && { local PREREQ=$1; shift; } || PREREQ=
+    local type="$1"
+    local name="$2"
+    local sha1_oid="$3"
+    local sha256_oid="$4"
+
+    echo ${sha1_oid} > ${name}_sha1_expected
+    echo ${sha256_oid} > ${name}_sha256_expected
+    echo ${type} > ${name}_type_expected
+
+    git --git-dir=repo-sha1/.git rev-parse --output-object-format=sha256 ${sha1_oid} > ${name}_sha1_sha256_found
+    git --git-dir=repo-sha256/.git rev-parse --output-object-format=sha1 ${sha256_oid} > ${name}_sha256_sha1_found
+    local sha1_sha256_oid=$(cat ${name}_sha1_sha256_found)
+    local sha256_sha1_oid=$(cat ${name}_sha256_sha1_found)
+
+    test_expect_success $PREREQ "Verify ${type} ${name}'s sha1 oid" '
+       git --git-dir=repo-sha256/.git rev-parse --output-object-format=sha1 ${sha256_oid} > ${name}_sha1 &&
+       test_cmp ${name}_sha1 ${name}_sha1_expected
+'
+
+    test_expect_success $PREREQ "Verify ${type} ${name}'s sha256 oid" '
+       git --git-dir=repo-sha1/.git rev-parse --output-object-format=sha256 ${sha1_oid} > ${name}_sha256 &&
+       test_cmp ${name}_sha256 ${name}_sha256_expected
+'
+
+    test_expect_success $PREREQ "Verify ${name}'s sha1 type" '
+       git --git-dir=repo-sha1/.git cat-file -t ${sha1_oid} > ${name}_type1 &&
+       git --git-dir=repo-sha256/.git cat-file -t ${sha256_sha1_oid} > ${name}_type2 &&
+       test_cmp ${name}_type1 ${name}_type2 &&
+       test_cmp ${name}_type1 ${name}_type_expected
+'
+
+    test_expect_success $PREREQ "Verify ${name}'s sha256 type" '
+       git --git-dir=repo-sha256/.git cat-file -t ${sha256_oid} > ${name}_type3 &&
+       git --git-dir=repo-sha1/.git cat-file -t ${sha1_sha256_oid} > ${name}_type4 &&
+       test_cmp ${name}_type3 ${name}_type4 &&
+       test_cmp ${name}_type3 ${name}_type_expected
+'
+
+    test_expect_success $PREREQ "Verify ${name}'s sha1 size" '
+       git --git-dir=repo-sha1/.git cat-file -s ${sha1_oid} > ${name}_size1 &&
+       git --git-dir=repo-sha256/.git cat-file -s ${sha256_sha1_oid} > ${name}_size2 &&
+       test_cmp ${name}_size1 ${name}_size2
+'
+
+    test_expect_success $PREREQ "Verify ${name}'s sha256 size" '
+       git --git-dir=repo-sha256/.git cat-file -s ${sha256_oid} > ${name}_size3 &&
+       git --git-dir=repo-sha1/.git cat-file -s ${sha1_sha256_oid} > ${name}_size4 &&
+       test_cmp ${name}_size3 ${name}_size4
+'
+
+    test_expect_success $PREREQ "Verify ${name}'s sha1 pretty content" '
+       git --git-dir=repo-sha1/.git cat-file -p ${sha1_oid} > ${name}_content1 &&
+       git --git-dir=repo-sha256/.git cat-file -p ${sha256_sha1_oid} > ${name}_content2 &&
+       test_cmp ${name}_content1 ${name}_content2
+'
+
+    test_expect_success $PREREQ "Verify ${name}'s sha256 pretty content" '
+       git --git-dir=repo-sha256/.git cat-file -p ${sha256_oid} > ${name}_content3 &&
+       git --git-dir=repo-sha1/.git cat-file -p ${sha1_sha256_oid} > ${name}_content4 &&
+       test_cmp ${name}_content3 ${name}_content4
+'
+
+    test_expect_success $PREREQ "Verify ${name}'s sha1 content" '
+       git --git-dir=repo-sha1/.git cat-file ${type} ${sha1_oid} > ${name}_content5 &&
+       git --git-dir=repo-sha256/.git cat-file ${type} ${sha256_sha1_oid} > ${name}_content6 &&
+       test_cmp ${name}_content5 ${name}_content6
+'
+
+    test_expect_success $PREREQ "Verify ${name}'s sha256 content" '
+       git --git-dir=repo-sha256/.git cat-file ${type} ${sha256_oid} > ${name}_content7 &&
+       git --git-dir=repo-sha1/.git cat-file ${type} ${sha1_sha256_oid} > ${name}_content8 &&
+       test_cmp ${name}_content7 ${name}_content8
+'
+
+}
+
+compare_oids 'blob' hello "$hello_sha1_oid" "$hello_sha256_oid"
+compare_oids 'tree' tree "$tree_sha1_oid" "$tree_sha256_oid"
+compare_oids 'commit' commit "$commit_sha1_oid" "$commit_sha256_oid"
+compare_oids GPG2 'commit' signedcommit "$signedcommit_sha1_oid" "$signedcommit_sha256_oid"
+compare_oids 'tag' hellotag "$hellotag_sha1_oid" "$hellotag_sha256_oid"
+compare_oids 'tag' treetag "$treetag_sha1_oid" "$treetag_sha256_oid"
+compare_oids 'tag' committag "$committag_sha1_oid" "$committag_sha256_oid"
+compare_oids GPG2 'tag' signedtag "$signedtag_sha1_oid" "$signedtag_sha256_oid"
+
+compare_oids 'blob' more "$more_sha1_oid" "$more_sha256_oid"
+compare_oids 'blob' another "$another_sha1_oid" "$another_sha256_oid"
+compare_oids 'tree' tree2 "$tree2_sha1_oid" "$tree2_sha256_oid"
+compare_oids 'commit' commit2 "$commit2_sha1_oid" "$commit2_sha256_oid"
+compare_oids GPG2 'tag' signedtag2 "$signedtag2_sha1_oid" "$signedtag2_sha256_oid"
+compare_oids GPG2 'commit' signedcommit2 "$signedcommit2_sha1_oid" "$signedcommit2_sha256_oid"
+compare_oids GPG2 'commit' signedcommit3 "$signedcommit3_sha1_oid" "$signedcommit3_sha256_oid"
+compare_oids GPG2 'commit' signedcommit4 "$signedcommit4_sha1_oid" "$signedcommit4_sha256_oid"
+compare_oids GPG2 'tag' signedtag3 "$signedtag3_sha1_oid" "$signedtag3_sha256_oid"
+compare_oids GPG2 'tag' signedtag4 "$signedtag4_sha1_oid" "$signedtag4_sha256_oid"
+
+test_done
diff --git a/t/t1016/gpg b/t/t1016/gpg
new file mode 100755 (executable)
index 0000000..2601cb1
--- /dev/null
@@ -0,0 +1,2 @@
+#!/bin/sh
+exec gpg --faked-system-time "20230918T154812" "$@"
index e49b8024ac53659e17f22eb39e252aba73e56a6b..ab3a105ffff2532b35074611de9e65d60b8db082 100755 (executable)
@@ -968,7 +968,7 @@ test_expect_success 'check-rules non-cone mode' '
        git -C bare sparse-checkout check-rules --no-cone --rules-file ../rules\
                >check-rules-file <all-files &&
 
-       cat rules | git -C repo sparse-checkout set --no-cone --stdin &&
+       git -C repo sparse-checkout set --no-cone --stdin <rules &&
        git -C repo ls-files -t >out &&
        sed -n "/^S /!s/^. //p" out >ls-files &&
 
index 31c38786870849e7a815f32a08933f059c9c8ffb..9b65d9eaf5f5e475b6d235b4670ec46603d4b0b3 100755 (executable)
@@ -11,6 +11,98 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
+test_expect_success 'setup whitespace config' '
+       sed -e "s/^|//" \
+           -e "s/[$]$//" \
+           -e "s/X/    /g" >.git/config <<-\EOF
+       [section]
+       |       solid = rock
+       |       sparse = big XX blue
+       |       sparseAndTail = big XX blue $
+       |       sparseAndTailQuoted = "big XX blue "
+       |       sparseAndBiggerTail = big XX blue X X
+       |       sparseAndBiggerTailQuoted = "big XX blue X X"
+       |       sparseAndBiggerTailQuotedPlus = "big XX blue X X"X $
+       |       headAndTail = Xbig blue $
+       |       headAndTailQuoted = "Xbig blue "
+       |       headAndTailQuotedPlus = "Xbig blue " $
+       |       annotated = big blueX# to be discarded
+       |       annotatedQuoted = "big blue"X# to be discarded
+       EOF
+'
+
+test_expect_success 'no internal whitespace' '
+       echo "rock" >expect &&
+       git config --get section.solid >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'internal whitespace' '
+       echo "big QQ blue" | q_to_tab >expect &&
+       git config --get section.sparse >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'internal and trailing whitespace' '
+       echo "big QQ blue" | q_to_tab >expect &&
+       git config --get section.sparseAndTail >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'internal and trailing whitespace, all quoted' '
+       echo "big QQ blue " | q_to_tab >expect &&
+       git config --get section.sparseAndTailQuoted >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'internal and more trailing whitespace' '
+       echo "big QQ blue" | q_to_tab >expect &&
+       git config --get section.sparseAndBiggerTail >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'internal and more trailing whitespace, all quoted' '
+       echo "big QQ blue Q Q" | q_to_tab >expect &&
+       git config --get section.sparseAndBiggerTailQuoted >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'internal and more trailing whitespace, not all quoted' '
+       echo "big QQ blue Q Q" | q_to_tab >expect &&
+       git config --get section.sparseAndBiggerTailQuotedPlus >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'leading and trailing whitespace' '
+       echo "big blue" >expect &&
+       git config --get section.headAndTail >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'leading and trailing whitespace, all quoted' '
+       echo "Qbig blue " | q_to_tab >expect &&
+       git config --get section.headAndTailQuoted >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'leading and trailing whitespace, not all quoted' '
+       echo "Qbig blue " | q_to_tab >expect &&
+       git config --get section.headAndTailQuotedPlus >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'inline comment' '
+       echo "big blue" >expect &&
+       git config --get section.annotated >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'inline comment, quoted' '
+       echo "big blue" >expect &&
+       git config --get section.annotatedQuoted >actual &&
+       test_cmp expect actual
+'
+
 test_expect_success 'clear default config' '
        rm -f .git/config
 '
@@ -69,14 +161,32 @@ test_expect_success 'replace with non-match (actually matching)' '
 
 cat > expect << EOF
 [section]
-       penguin = very blue
        Movie = BadPhysics
        UPPERCASE = true
-       penguin = kingpin
+       penguin = gentoo # Pygoscelis papua
+       disposition = peckish # find fish
+       foo = bar #abc
+       spsp = value # and comment
+       htsp = value    # and comment
 [Sections]
        WhatEver = Second
 EOF
 
+test_expect_success 'append comments' '
+       git config --replace-all --comment="Pygoscelis papua" section.penguin gentoo &&
+       git config --comment="find fish" section.disposition peckish &&
+       git config --comment="#abc" section.foo bar &&
+
+       git config --comment="and comment" section.spsp value &&
+       git config --comment="  # and comment" section.htsp value &&
+
+       test_cmp expect .git/config
+'
+
+test_expect_success 'Prohibited LF in comment' '
+       test_must_fail git config --comment="a${LF}b" section.k v
+'
+
 test_expect_success 'non-match result' 'test_cmp expect .git/config'
 
 test_expect_success 'find mixed-case key by canonical name' '
@@ -1066,9 +1176,25 @@ test_expect_success '--null --get-regexp' '
        test_cmp expect result
 '
 
-test_expect_success 'inner whitespace kept verbatim' '
-       git config section.val "foo       bar" &&
-       test_cmp_config "foo      bar" section.val
+test_expect_success 'inner whitespace kept verbatim, spaces only' '
+       echo "foo   bar" >expect &&
+       git config section.val "foo   bar" &&
+       git config --get section.val >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'inner whitespace kept verbatim, horizontal tabs only' '
+       echo "fooQQbar" | q_to_tab >expect &&
+       git config section.val "$(cat expect)" &&
+       git config --get section.val >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'inner whitespace kept verbatim, horizontal tabs and spaces' '
+       echo "foo Q  bar" | q_to_tab >expect &&
+       git config section.val "$(cat expect)" &&
+       git config --get section.val >actual &&
+       test_cmp expect actual
 '
 
 test_expect_success SYMLINKS 'symlinked configuration' '
index 6ebc3ef9453b71dbc7d90a052834af482dfd48ec..ec3443cc8786d126cbc0085de184b2e9af019566 100755 (executable)
@@ -622,7 +622,7 @@ test_expect_success 'stdin fails create with no ref' '
 test_expect_success 'stdin fails create with no new value' '
        echo "create $a" >stdin &&
        test_must_fail git update-ref --stdin <stdin 2>err &&
-       grep "fatal: create $a: missing <newvalue>" err
+       grep "fatal: create $a: missing <new-oid>" err
 '
 
 test_expect_success 'stdin fails create with too many arguments' '
@@ -640,7 +640,7 @@ test_expect_success 'stdin fails update with no ref' '
 test_expect_success 'stdin fails update with no new value' '
        echo "update $a" >stdin &&
        test_must_fail git update-ref --stdin <stdin 2>err &&
-       grep "fatal: update $a: missing <newvalue>" err
+       grep "fatal: update $a: missing <new-oid>" err
 '
 
 test_expect_success 'stdin fails update with too many arguments' '
@@ -765,21 +765,21 @@ test_expect_success 'stdin update ref fails with wrong old value' '
 test_expect_success 'stdin update ref fails with bad old value' '
        echo "update $c $m does-not-exist" >stdin &&
        test_must_fail git update-ref --stdin <stdin 2>err &&
-       grep "fatal: update $c: invalid <oldvalue>: does-not-exist" err &&
+       grep "fatal: update $c: invalid <old-oid>: does-not-exist" err &&
        test_must_fail git rev-parse --verify -q $c
 '
 
 test_expect_success 'stdin create ref fails with bad new value' '
        echo "create $c does-not-exist" >stdin &&
        test_must_fail git update-ref --stdin <stdin 2>err &&
-       grep "fatal: create $c: invalid <newvalue>: does-not-exist" err &&
+       grep "fatal: create $c: invalid <new-oid>: does-not-exist" err &&
        test_must_fail git rev-parse --verify -q $c
 '
 
 test_expect_success 'stdin create ref fails with zero new value' '
        echo "create $c " >stdin &&
        test_must_fail git update-ref --stdin <stdin 2>err &&
-       grep "fatal: create $c: zero <newvalue>" err &&
+       grep "fatal: create $c: zero <new-oid>" err &&
        test_must_fail git rev-parse --verify -q $c
 '
 
@@ -803,7 +803,7 @@ test_expect_success 'stdin delete ref fails with wrong old value' '
 test_expect_success 'stdin delete ref fails with zero old value' '
        echo "delete $a " >stdin &&
        test_must_fail git update-ref --stdin <stdin 2>err &&
-       grep "fatal: delete $a: zero <oldvalue>" err &&
+       grep "fatal: delete $a: zero <old-oid>" err &&
        git rev-parse $m >expect &&
        git rev-parse $a >actual &&
        test_cmp expect actual
@@ -1027,7 +1027,7 @@ test_expect_success 'stdin -z fails create with no ref' '
 test_expect_success 'stdin -z fails create with no new value' '
        printf $F "create $a" >stdin &&
        test_must_fail git update-ref -z --stdin <stdin 2>err &&
-       grep "fatal: create $a: unexpected end of input when reading <newvalue>" err
+       grep "fatal: create $a: unexpected end of input when reading <new-oid>" err
 '
 
 test_expect_success 'stdin -z fails create with too many arguments' '
@@ -1045,27 +1045,27 @@ test_expect_success 'stdin -z fails update with no ref' '
 test_expect_success 'stdin -z fails update with too few args' '
        printf $F "update $a" "$m" >stdin &&
        test_must_fail git update-ref -z --stdin <stdin 2>err &&
-       grep "fatal: update $a: unexpected end of input when reading <oldvalue>" err
+       grep "fatal: update $a: unexpected end of input when reading <old-oid>" err
 '
 
 test_expect_success 'stdin -z emits warning with empty new value' '
        git update-ref $a $m &&
        printf $F "update $a" "" "" >stdin &&
        git update-ref -z --stdin <stdin 2>err &&
-       grep "warning: update $a: missing <newvalue>, treating as zero" err &&
+       grep "warning: update $a: missing <new-oid>, treating as zero" err &&
        test_must_fail git rev-parse --verify -q $a
 '
 
 test_expect_success 'stdin -z fails update with no new value' '
        printf $F "update $a" >stdin &&
        test_must_fail git update-ref -z --stdin <stdin 2>err &&
-       grep "fatal: update $a: unexpected end of input when reading <newvalue>" err
+       grep "fatal: update $a: unexpected end of input when reading <new-oid>" err
 '
 
 test_expect_success 'stdin -z fails update with no old value' '
        printf $F "update $a" "$m" >stdin &&
        test_must_fail git update-ref -z --stdin <stdin 2>err &&
-       grep "fatal: update $a: unexpected end of input when reading <oldvalue>" err
+       grep "fatal: update $a: unexpected end of input when reading <old-oid>" err
 '
 
 test_expect_success 'stdin -z fails update with too many arguments' '
@@ -1083,7 +1083,7 @@ test_expect_success 'stdin -z fails delete with no ref' '
 test_expect_success 'stdin -z fails delete with no old value' '
        printf $F "delete $a" >stdin &&
        test_must_fail git update-ref -z --stdin <stdin 2>err &&
-       grep "fatal: delete $a: unexpected end of input when reading <oldvalue>" err
+       grep "fatal: delete $a: unexpected end of input when reading <old-oid>" err
 '
 
 test_expect_success 'stdin -z fails delete with too many arguments' '
@@ -1101,7 +1101,7 @@ test_expect_success 'stdin -z fails verify with too many arguments' '
 test_expect_success 'stdin -z fails verify with no old value' '
        printf $F "verify $a" >stdin &&
        test_must_fail git update-ref -z --stdin <stdin 2>err &&
-       grep "fatal: verify $a: unexpected end of input when reading <oldvalue>" err
+       grep "fatal: verify $a: unexpected end of input when reading <old-oid>" err
 '
 
 test_expect_success 'stdin -z fails option with unknown name' '
@@ -1160,7 +1160,7 @@ test_expect_success 'stdin -z update ref fails with wrong old value' '
 test_expect_success 'stdin -z update ref fails with bad old value' '
        printf $F "update $c" "$m" "does-not-exist" >stdin &&
        test_must_fail git update-ref -z --stdin <stdin 2>err &&
-       grep "fatal: update $c: invalid <oldvalue>: does-not-exist" err &&
+       grep "fatal: update $c: invalid <old-oid>: does-not-exist" err &&
        test_must_fail git rev-parse --verify -q $c
 '
 
@@ -1178,14 +1178,14 @@ test_expect_success 'stdin -z create ref fails with bad new value' '
        git update-ref -d "$c" &&
        printf $F "create $c" "does-not-exist" >stdin &&
        test_must_fail git update-ref -z --stdin <stdin 2>err &&
-       grep "fatal: create $c: invalid <newvalue>: does-not-exist" err &&
+       grep "fatal: create $c: invalid <new-oid>: does-not-exist" err &&
        test_must_fail git rev-parse --verify -q $c
 '
 
 test_expect_success 'stdin -z create ref fails with empty new value' '
        printf $F "create $c" "" >stdin &&
        test_must_fail git update-ref -z --stdin <stdin 2>err &&
-       grep "fatal: create $c: missing <newvalue>" err &&
+       grep "fatal: create $c: missing <new-oid>" err &&
        test_must_fail git rev-parse --verify -q $c
 '
 
@@ -1209,7 +1209,7 @@ test_expect_success 'stdin -z delete ref fails with wrong old value' '
 test_expect_success 'stdin -z delete ref fails with zero old value' '
        printf $F "delete $a" "$Z" >stdin &&
        test_must_fail git update-ref -z --stdin <stdin 2>err &&
-       grep "fatal: delete $a: zero <oldvalue>" err &&
+       grep "fatal: delete $a: zero <old-oid>" err &&
        git rev-parse $m >expect &&
        git rev-parse $a >actual &&
        test_cmp expect actual
index 6d47e2c725f7c7842a604b7a72fd4c4ca64e71bb..dc997e0a643752708fb04a8f64032127f02a5cfd 100755 (executable)
@@ -43,7 +43,7 @@ rsync --exclude-from t/t1509/excludes -Ha . "$R$(pwd)"
 # env might slip through, see test-lib.sh, unset.*PERL_PATH
 sed 's|^PERL_PATH=.*|PERL_PATH=/bin/true|' GIT-BUILD-OPTIONS > "$R$(pwd)/GIT-BUILD-OPTIONS"
 for cmd in git $BB;do 
-       ldd $cmd | grep '/' | sed 's,.*\s\(/[^ ]*\).*,\1,' | while read i; do
+       ldd $cmd | sed -n '/\//s,.*\s\(/[^ ]*\).*,\1,p' | while read i; do
                mkdir -p "$R$(dirname $i)"
                cp "$i" "$R/$i"
        done
index bce284c2978848967ffe88764e396fad5059df54..8d90d0285045294b3f39233957ea4487bc7929e0 100755 (executable)
@@ -176,7 +176,10 @@ test_expect_success 'tracking count is accurate after orphan check' '
        git config branch.child.merge refs/heads/main &&
        git checkout child^ &&
        git checkout child >stdout &&
-       test_cmp expect stdout
+       test_cmp expect stdout &&
+
+       git checkout --detach child >stdout &&
+       test_grep ! "can be fast-forwarded\." stdout
 '
 
 test_expect_success 'no advice given for explicit detached head state' '
index 16d6348b692806ab25faf89d77a73cd62fab4d93..ac404945d4c4e28b608f664f64c4b422557b2684 100755 (executable)
@@ -5,6 +5,7 @@ test_description='restore basic functionality'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
index 27e85be40ad6957f9070a609d83ae334776ed855..42d552211915754e14ddb79d869fc9ba8f517fce 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='git restore --patch'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./lib-patch-mode.sh
 
 test_expect_success 'setup' '
index 8198a1e5789cc963c32ecb028209a7a773144fd5..86c9c887881b94a586dc96b3f495a72c022fd827 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='restore --pathspec-from-file'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_tick
index 0bab134d71d3e785194562779ef63085de4e6545..7ec7f30b442b3bc04be5ac64cbf1898c0b5268b6 100755 (executable)
@@ -11,27 +11,27 @@ TEST_PASSES_SANITIZE_LEAK=true
 sane_unset GIT_TEST_SPLIT_INDEX
 
 test_set_index_version () {
-    GIT_INDEX_VERSION="$1"
-    export GIT_INDEX_VERSION
+       GIT_INDEX_VERSION="$1"
+       export GIT_INDEX_VERSION
 }
 
 test_set_index_version 3
 
-cat >expect.full <<EOF
-H 1
-H 2
-H sub/1
-H sub/2
-EOF
+test_expect_success 'setup' '
+       cat >expect.full <<-\EOF &&
+       H 1
+       H 2
+       H sub/1
+       H sub/2
+       EOF
 
-cat >expect.skip <<EOF
-S 1
-H 2
-S sub/1
-H sub/2
-EOF
+       cat >expect.skip <<-\EOF &&
+       S 1
+       H 2
+       S sub/1
+       H sub/2
+       EOF
 
-test_expect_success 'setup' '
        mkdir sub &&
        touch ./1 ./2 sub/1 sub/2 &&
        git add 1 2 sub/1 sub/2 &&
index 33aba87b9a4db314880a640b7ae65d7ba9c76a06..ccfa6a720d090c2f7f2a085f60065bdcfaf8d1d9 100755 (executable)
@@ -576,7 +576,7 @@ EOF
 
        # ...and that the comments for those sections are also
        # preserved.
-       cat config.branch | sed "s/\"source\"/\"dest\"/" >expect &&
+       sed "s/\"source\"/\"dest\"/" config.branch >expect &&
        sed -n -e "/Note the lack/,\$p" .git/config >actual &&
        test_cmp expect actual
 '
@@ -1154,9 +1154,9 @@ test_expect_success 'avoid ambiguous track and advise' '
        hint: tracking ref '\''refs/heads/main'\'':
        hint:   ambi1
        hint:   ambi2
-       hint: ''
+       hint:
        hint: This is typically a configuration error.
-       hint: ''
+       hint:
        hint: To support setting up tracking branches, ensure that
        hint: different remotes'\'' fetch refspecs map into different
        hint: tracking namespaces.
index 088a852dd47aa335d83424f21472f8384c91cf16..beca34605672d407244b26ebcffb1f70527f5915 100755 (executable)
@@ -442,7 +442,7 @@ test_expect_success 'add note by specifying "-C", "--no-stripspace" is the defau
        ${LF}
        EOF
 
-       cat expect | git hash-object -w --stdin >blob &&
+       git hash-object -w --stdin <expect >blob &&
        git notes add -C $(cat blob) &&
        git notes show >actual &&
        test_cmp expect actual &&
@@ -468,7 +468,7 @@ test_expect_success 'reuse note by specifying "-C" and "--stripspace"' '
        second-line
        EOF
 
-       cat data | git hash-object -w --stdin >blob &&
+       git hash-object -w --stdin <data >blob &&
        git notes add --stripspace -C $(cat blob) &&
        git notes show >actual &&
        test_cmp expect actual
@@ -492,7 +492,7 @@ test_expect_success 'reuse with "-C" and add note with "-m", "-m" will stripspac
        third-line
        EOF
 
-       cat data | git hash-object -w --stdin >blob &&
+       git hash-object -w --stdin <data >blob &&
        git notes add -C $(cat blob) -m "third-line" &&
        git notes show >actual &&
        test_cmp expect actual
@@ -511,7 +511,7 @@ test_expect_success 'add note with "-m" and reuse note with "-C", "-C" will not
        second-line
        EOF
 
-       cat data | git hash-object -w --stdin >blob &&
+       git hash-object -w --stdin <data >blob &&
        git notes add -m "first-line" -C $(cat blob)  &&
        git notes show >actual &&
        test_cmp expect actual
index 5e1045a0afc9ecfe122904db0a3c589ff804c732..1ee6b00fd57726e055b95903e09f97c1e24ebcd2 100755 (executable)
@@ -72,6 +72,17 @@ test_expect_success 'rebase --merge --empty=keep' '
        test_cmp expect actual
 '
 
+test_expect_success 'rebase --merge --empty=stop' '
+       git checkout -B testing localmods &&
+       test_must_fail git rebase --merge --empty=stop upstream &&
+
+       git rebase --skip &&
+
+       test_write_lines D C B A >expect &&
+       git log --format=%s >actual &&
+       test_cmp expect actual
+'
+
 test_expect_success 'rebase --merge --empty=ask' '
        git checkout -B testing localmods &&
        test_must_fail git rebase --merge --empty=ask upstream &&
@@ -101,9 +112,9 @@ test_expect_success 'rebase --interactive --empty=keep' '
        test_cmp expect actual
 '
 
-test_expect_success 'rebase --interactive --empty=ask' '
+test_expect_success 'rebase --interactive --empty=stop' '
        git checkout -B testing localmods &&
-       test_must_fail git rebase --interactive --empty=ask upstream &&
+       test_must_fail git rebase --interactive --empty=stop upstream &&
 
        git rebase --skip &&
 
@@ -112,7 +123,7 @@ test_expect_success 'rebase --interactive --empty=ask' '
        test_cmp expect actual
 '
 
-test_expect_success 'rebase --interactive uses default of --empty=ask' '
+test_expect_success 'rebase --interactive uses default of --empty=stop' '
        git checkout -B testing localmods &&
        test_must_fail git rebase --interactive upstream &&
 
@@ -167,4 +178,42 @@ test_expect_success 'rebase --merge does not leave state laying around' '
        test_path_is_missing .git/MERGE_MSG
 '
 
+test_expect_success 'rebase --exec --empty=drop' '
+       git checkout -B testing localmods &&
+       git rebase --exec "true" --empty=drop upstream &&
+
+       test_write_lines D C B A >expect &&
+       git log --format=%s >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'rebase --exec --empty=keep' '
+       git checkout -B testing localmods &&
+       git rebase --exec "true" --empty=keep upstream &&
+
+       test_write_lines D C2 C B A >expect &&
+       git log --format=%s >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'rebase --exec uses default of --empty=keep' '
+       git checkout -B testing localmods &&
+       git rebase --exec "true" upstream &&
+
+       test_write_lines D C2 C B A >expect &&
+       git log --format=%s >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'rebase --exec --empty=stop' '
+       git checkout -B testing localmods &&
+       test_must_fail git rebase --exec "true" --empty=stop upstream &&
+
+       git rebase --skip &&
+
+       test_write_lines D C B A >expect &&
+       git log --format=%s >actual &&
+       test_cmp expect actual
+'
+
 test_done
index c614c4f2e4ba285ba7ffc6b7b4384b69f0b9a851..821f08e5afb6983cf8f69abc525a54b6274885e6 100755 (executable)
@@ -58,4 +58,13 @@ test_expect_success 'unknown key in author-script' '
        check_resolve_fails
 '
 
+test_expect_success POSIXPERM,SANITY 'unwritable rebased-patches does not leak' '
+       >.git/rebased-patches &&
+       chmod a-w .git/rebased-patches &&
+
+       git checkout -b side HEAD^ &&
+       test_commit unrelated &&
+       test_must_fail git rebase --apply --onto tmp HEAD^
+'
+
 test_done
index aeab689a98d00919e0473380fc9bf40497b32c80..411027fb58c7dfb657409980970e71005f2c79c5 100755 (executable)
@@ -104,11 +104,19 @@ test_expect_success 'revert forbidden on dirty working tree' '
 '
 
 test_expect_success 'cherry-pick on unborn branch' '
-       git checkout --orphan unborn &&
+       git switch --orphan unborn &&
        git rm --cached -r . &&
-       rm -rf * &&
        git cherry-pick initial &&
-       git diff --quiet initial &&
+       git diff --exit-code initial &&
+       test_cmp_rev ! initial HEAD
+'
+
+test_expect_success 'cherry-pick on unborn branch with --allow-empty' '
+       git checkout --detach &&
+       git branch -D unborn &&
+       git switch --orphan unborn &&
+       git cherry-pick initial --allow-empty &&
+       git diff --exit-code initial &&
        test_cmp_rev ! initial HEAD
 '
 
@@ -170,6 +178,7 @@ test_expect_success 'advice from failed revert' '
        hint: You can instead skip this commit with "git revert --skip".
        hint: To abort and get back to the state before "git revert",
        hint: run "git revert --abort".
+       hint: Disable this message with "git config advice.mergeConflict false"
        EOF
        test_commit --append --no-tag "double-add dream" dream dream &&
        test_must_fail git revert HEAD^ 2>actual &&
index eba3c38d5ad861a5d71b7fa9615ee804b24e6fc4..9748443530cd713c8c9d06e6eaa67552a69e978b 100755 (executable)
@@ -84,7 +84,7 @@ test_expect_success 'cherry-pick a commit that becomes no-op (prep)' '
        git commit -m "add file2 on the side"
 '
 
-test_expect_success 'cherry-pick a no-op without --keep-redundant' '
+test_expect_success 'cherry-pick a no-op with neither --keep-redundant nor --empty' '
        git reset --hard &&
        git checkout fork^0 &&
        test_must_fail git cherry-pick main
@@ -99,4 +99,53 @@ test_expect_success 'cherry-pick a no-op with --keep-redundant' '
        test_cmp expect actual
 '
 
+test_expect_success '--keep-redundant-commits is incompatible with operations' '
+       test_must_fail git cherry-pick HEAD 2>output &&
+       test_grep "The previous cherry-pick is now empty" output &&
+       test_must_fail git cherry-pick --keep-redundant-commits --continue 2>output &&
+       test_grep "fatal: cherry-pick: --keep-redundant-commits cannot be used with --continue" output &&
+       test_must_fail git cherry-pick --keep-redundant-commits --skip 2>output &&
+       test_grep "fatal: cherry-pick: --keep-redundant-commits cannot be used with --skip" output &&
+       test_must_fail git cherry-pick --keep-redundant-commits --abort 2>output &&
+       test_grep "fatal: cherry-pick: --keep-redundant-commits cannot be used with --abort" output &&
+       test_must_fail git cherry-pick --keep-redundant-commits --quit 2>output &&
+       test_grep "fatal: cherry-pick: --keep-redundant-commits cannot be used with --quit" output &&
+       git cherry-pick --abort
+'
+
+test_expect_success '--empty is incompatible with operations' '
+       test_must_fail git cherry-pick HEAD 2>output &&
+       test_grep "The previous cherry-pick is now empty" output &&
+       test_must_fail git cherry-pick --empty=stop --continue 2>output &&
+       test_grep "fatal: cherry-pick: --empty cannot be used with --continue" output &&
+       test_must_fail git cherry-pick --empty=stop --skip 2>output &&
+       test_grep "fatal: cherry-pick: --empty cannot be used with --skip" output &&
+       test_must_fail git cherry-pick --empty=stop --abort 2>output &&
+       test_grep "fatal: cherry-pick: --empty cannot be used with --abort" output &&
+       test_must_fail git cherry-pick --empty=stop --quit 2>output &&
+       test_grep "fatal: cherry-pick: --empty cannot be used with --quit" output &&
+       git cherry-pick --abort
+'
+
+test_expect_success 'cherry-pick a no-op with --empty=stop' '
+       git reset --hard &&
+       git checkout fork^0 &&
+       test_must_fail git cherry-pick --empty=stop main 2>output &&
+       test_grep "The previous cherry-pick is now empty" output
+'
+
+test_expect_success 'cherry-pick a no-op with --empty=drop' '
+       git reset --hard &&
+       git checkout fork^0 &&
+       git cherry-pick --empty=drop main &&
+       test_commit_message HEAD -m "add file2 on the side"
+'
+
+test_expect_success 'cherry-pick a no-op with --empty=keep' '
+       git reset --hard &&
+       git checkout fork^0 &&
+       git cherry-pick --empty=keep main &&
+       test_commit_message HEAD -m "add file2 on main"
+'
+
 test_done
index c88d597b12682ccc611699f43c6451e35483fe6a..f3947b400a3a89970e400d3c3f5dc5d690292d83 100755 (executable)
@@ -60,6 +60,7 @@ test_expect_success 'advice from failed cherry-pick' '
        hint: You can instead skip this commit with "git cherry-pick --skip".
        hint: To abort and get back to the state before "git cherry-pick",
        hint: run "git cherry-pick --abort".
+       hint: Disable this message with "git config advice.mergeConflict false"
        EOF
        test_must_fail git cherry-pick picked 2>actual &&
 
@@ -74,6 +75,7 @@ test_expect_success 'advice from failed cherry-pick --no-commit' "
        error: could not apply \$picked... picked
        hint: after resolving the conflicts, mark the corrected paths
        hint: with 'git add <paths>' or 'git rm <paths>'
+       hint: Disable this message with \"git config advice.mergeConflict false\"
        EOF
        test_must_fail git cherry-pick --no-commit picked 2>actual &&
 
index 72020a51c4375fd793af7e0129443cbf8d11d929..7eb52b12edc55702f48812725bfddc282297f9a5 100755 (executable)
@@ -90,6 +90,38 @@ test_expect_success 'cherry-pick persists opts correctly' '
        test_cmp expect actual
 '
 
+test_expect_success 'cherry-pick persists --empty=stop correctly' '
+       pristine_detach yetanotherpick &&
+       # Picking `anotherpick` forces a conflict so that we stop. That
+       # commit is then skipped, after which we pick `yetanotherpick`
+       # while already on `yetanotherpick` to cause an empty commit
+       test_must_fail git cherry-pick --empty=stop anotherpick yetanotherpick &&
+       test_must_fail git cherry-pick --skip 2>msg &&
+       test_grep "The previous cherry-pick is now empty" msg &&
+       rm msg &&
+       git cherry-pick --abort
+'
+
+test_expect_success 'cherry-pick persists --empty=drop correctly' '
+       pristine_detach yetanotherpick &&
+       # Picking `anotherpick` forces a conflict so that we stop. That
+       # commit is then skipped, after which we pick `yetanotherpick`
+       # while already on `yetanotherpick` to cause an empty commit
+       test_must_fail git cherry-pick --empty=drop anotherpick yetanotherpick &&
+       git cherry-pick --skip &&
+       test_cmp_rev yetanotherpick HEAD
+'
+
+test_expect_success 'cherry-pick persists --empty=keep correctly' '
+       pristine_detach yetanotherpick &&
+       # Picking `anotherpick` forces a conflict so that we stop. That
+       # commit is then skipped, after which we pick `yetanotherpick`
+       # while already on `yetanotherpick` to cause an empty commit
+       test_must_fail git cherry-pick --empty=keep anotherpick yetanotherpick &&
+       git cherry-pick --skip &&
+       test_cmp_rev yetanotherpick HEAD^
+'
+
 test_expect_success 'revert persists opts correctly' '
        pristine_detach initial &&
        # to make sure that the session to revert a sequence
index f23d39f0d52ec6f5035acfb029550babc67859da..839c904745a2861487e04363060a951aaeb902c9 100755 (executable)
@@ -28,6 +28,16 @@ test_expect_success 'Test of git add' '
        touch foo && git add foo
 '
 
+test_expect_success 'Test with no pathspecs' '
+       cat >expect <<-EOF &&
+       Nothing specified, nothing added.
+       hint: Maybe you wanted to say ${SQ}git add .${SQ}?
+       hint: Disable this message with "git config advice.addEmptyPathspec false"
+       EOF
+       git add 2>actual &&
+       test_cmp expect actual
+'
+
 test_expect_success 'Post-check that foo is in the index' '
        git ls-files foo | grep foo
 '
@@ -339,6 +349,40 @@ test_expect_success '"git add ." in empty repo' '
        )
 '
 
+test_expect_success '"git add" a embedded repository' '
+       rm -fr outer && git init outer &&
+       (
+               cd outer &&
+               for i in 1 2
+               do
+                       name=inner$i &&
+                       git init $name &&
+                       git -C $name commit --allow-empty -m $name ||
+                               return 1
+               done &&
+               git add . 2>actual &&
+               cat >expect <<-EOF &&
+               warning: adding embedded git repository: inner1
+               hint: You${SQ}ve added another git repository inside your current repository.
+               hint: Clones of the outer repository will not contain the contents of
+               hint: the embedded repository and will not know how to obtain it.
+               hint: If you meant to add a submodule, use:
+               hint:
+               hint:   git submodule add <url> inner1
+               hint:
+               hint: If you added this path by mistake, you can remove it from the
+               hint: index with:
+               hint:
+               hint:   git rm --cached inner1
+               hint:
+               hint: See "git help submodule" for more information.
+               hint: Disable this message with "git config advice.addEmbeddedRepo false"
+               warning: adding embedded git repository: inner2
+               EOF
+               test_cmp expect actual
+       )
+'
+
 test_expect_success 'error on a repository with no commits' '
        rm -fr empty &&
        git init empty &&
@@ -370,8 +414,7 @@ cat >expect.err <<\EOF
 The following paths are ignored by one of your .gitignore files:
 ignored-file
 hint: Use -f if you really want to add them.
-hint: Turn this message off by running
-hint: "git config advice.addIgnoredFile false"
+hint: Disable this message with "git config advice.addIgnoredFile false"
 EOF
 cat >expect.out <<\EOF
 add 'track-this'
index 0b5339ac6ca8248582ce723e3d552a8d4513e294..bc55255b0a8da397ad14212035da9c4b9cb8d7d8 100755 (executable)
@@ -325,9 +325,9 @@ test_expect_success 'different prompts for mode change/deleted' '
        git -c core.filemode=true add -p >actual &&
        sed -n "s/^\(([0-9/]*) Stage .*?\).*/\1/p" actual >actual.filtered &&
        cat >expect <<-\EOF &&
-       (1/1) Stage deletion [y,n,q,a,d,?]?
-       (1/2) Stage mode change [y,n,q,a,d,j,J,g,/,?]?
-       (2/2) Stage this hunk [y,n,q,a,d,K,g,/,e,?]?
+       (1/1) Stage deletion [y,n,q,a,d,p,?]?
+       (1/2) Stage mode change [y,n,q,a,d,j,J,g,/,p,?]?
+       (2/2) Stage this hunk [y,n,q,a,d,K,g,/,e,p,?]?
        EOF
        test_cmp expect actual.filtered
 '
@@ -514,13 +514,13 @@ test_expect_success 'split hunk setup' '
 test_expect_success 'goto hunk' '
        test_when_finished "git reset" &&
        tr _ " " >expect <<-EOF &&
-       (2/2) Stage this hunk [y,n,q,a,d,K,g,/,e,?]? + 1:  -1,2 +1,3          +15
+       (2/2) Stage this hunk [y,n,q,a,d,K,g,/,e,p,?]? + 1:  -1,2 +1,3          +15
        _ 2:  -2,4 +3,8          +21
        go to which hunk? @@ -1,2 +1,3 @@
        _10
        +15
        _20
-       (1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,?]?_
+       (1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]?_
        EOF
        test_write_lines s y g 1 | git add -p >actual &&
        tail -n 7 <actual >actual.trimmed &&
@@ -530,11 +530,11 @@ test_expect_success 'goto hunk' '
 test_expect_success 'navigate to hunk via regex' '
        test_when_finished "git reset" &&
        tr _ " " >expect <<-EOF &&
-       (2/2) Stage this hunk [y,n,q,a,d,K,g,/,e,?]? @@ -1,2 +1,3 @@
+       (2/2) Stage this hunk [y,n,q,a,d,K,g,/,e,p,?]? @@ -1,2 +1,3 @@
        _10
        +15
        _20
-       (1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,?]?_
+       (1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]?_
        EOF
        test_write_lines s y /1,2 | git add -p >actual &&
        tail -n 5 <actual >actual.trimmed &&
@@ -715,21 +715,21 @@ test_expect_success 'colors can be overridden' '
        <BLUE>+<RESET><BLUE>new<RESET>
        <CYAN> more-context<RESET>
        <BLUE>+<RESET><BLUE>another-one<RESET>
-       <YELLOW>(1/1) Stage this hunk [y,n,q,a,d,s,e,?]? <RESET><BOLD>Split into 2 hunks.<RESET>
+       <YELLOW>(1/1) Stage this hunk [y,n,q,a,d,s,e,p,?]? <RESET><BOLD>Split into 2 hunks.<RESET>
        <MAGENTA>@@ -1,3 +1,3 @@<RESET>
        <CYAN> context<RESET>
        <BOLD>-old<RESET>
        <BLUE>+<RESET><BLUE>new<RESET>
        <CYAN> more-context<RESET>
-       <YELLOW>(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,?]? <RESET><MAGENTA>@@ -3 +3,2 @@<RESET>
+       <YELLOW>(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]? <RESET><MAGENTA>@@ -3 +3,2 @@<RESET>
        <CYAN> more-context<RESET>
        <BLUE>+<RESET><BLUE>another-one<RESET>
-       <YELLOW>(2/2) Stage this hunk [y,n,q,a,d,K,g,/,e,?]? <RESET><MAGENTA>@@ -1,3 +1,3 @@<RESET>
+       <YELLOW>(2/2) Stage this hunk [y,n,q,a,d,K,g,/,e,p,?]? <RESET><MAGENTA>@@ -1,3 +1,3 @@<RESET>
        <CYAN> context<RESET>
        <BOLD>-old<RESET>
        <BLUE>+new<RESET>
        <CYAN> more-context<RESET>
-       <YELLOW>(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,?]? <RESET>
+       <YELLOW>(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]? <RESET>
        EOF
        test_cmp expect actual
 '
index 67fd2345affd78507a6cb714b48565ed190cb5af..50ae222f08424e4b4ed22a82c066526a3da3659c 100755 (executable)
@@ -10,7 +10,7 @@ LIB_CRLF_BRANCHES=""
 create_crlf_ref () {
        branch="$1" &&
        cat >.crlf-orig-$branch.txt &&
-       cat .crlf-orig-$branch.txt | append_cr >.crlf-message-$branch.txt &&
+       append_cr <.crlf-orig-$branch.txt >.crlf-message-$branch.txt &&
        grep 'Subject' .crlf-orig-$branch.txt | tr '\n' ' ' | sed 's/[ ]*$//' | tr -d '\n' >.crlf-subject-$branch.txt &&
        grep 'Body' .crlf-orig-$branch.txt | append_cr >.crlf-body-$branch.txt &&
        LIB_CRLF_BRANCHES="${LIB_CRLF_BRANCHES} ${branch}" &&
@@ -97,7 +97,7 @@ test_expect_success 'branch: --verbose works with messages using CRLF' '
        git branch -v >tmp &&
        # Remove first two columns, and the line for the currently checked out branch
        current=$(git branch --show-current) &&
-       grep -v $current <tmp | awk "{\$1=\$2=\"\"}1"  >actual &&
+       awk "/$current/ { next } { \$1 = \$2 = \"\" } 1" <tmp >actual &&
        test_cmp expect actual
 '
 
index 7afc883ec374e1c4b747ed80ba64575d05017502..cb3307010c1ed97f1b7bdb91b366f3e6d17a2ac9 100755 (executable)
@@ -405,7 +405,7 @@ test_expect_success 'diff-tree -r B A == diff-tree -r -R A B' '
 
 test_expect_success 'diff can read from stdin' '
        test_must_fail git diff --no-index -- MN - < NN |
-               grep -v "^index" | sed "s#/-#/NN#" >.test-a &&
+               sed "/^index/d; s#/-#/NN#" >.test-a &&
        test_must_fail git diff --no-index -- MN NN |
                grep -v "^index" >.test-b &&
        test_cmp .test-a .test-b
index 1e3b2dbea48488ecb3a68a8dba82d57b6fbca2a7..3855d68dbc0a64797ddbd665e675aa3d1549defc 100755 (executable)
@@ -633,8 +633,8 @@ check_prefix () {
        test_cmp expect actual.paths
 }
 
-test_expect_success 'diff-files does not respect diff.noprefix' '
-       git -c diff.noprefix diff-files -p >actual &&
+test_expect_success 'diff-files does not respect diff.noPrefix' '
+       git -c diff.noPrefix diff-files -p >actual &&
        check_prefix actual a/file0 b/file0
 '
 
@@ -643,23 +643,58 @@ test_expect_success 'diff-files respects --no-prefix' '
        check_prefix actual file0 file0
 '
 
-test_expect_success 'diff respects diff.noprefix' '
-       git -c diff.noprefix diff >actual &&
+test_expect_success 'diff respects diff.noPrefix' '
+       git -c diff.noPrefix diff >actual &&
        check_prefix actual file0 file0
 '
 
-test_expect_success 'diff --default-prefix overrides diff.noprefix' '
-       git -c diff.noprefix diff --default-prefix >actual &&
+test_expect_success 'diff --default-prefix overrides diff.noPrefix' '
+       git -c diff.noPrefix diff --default-prefix >actual &&
        check_prefix actual a/file0 b/file0
 '
 
-test_expect_success 'diff respects diff.mnemonicprefix' '
-       git -c diff.mnemonicprefix diff >actual &&
+test_expect_success 'diff respects diff.mnemonicPrefix' '
+       git -c diff.mnemonicPrefix diff >actual &&
        check_prefix actual i/file0 w/file0
 '
 
-test_expect_success 'diff --default-prefix overrides diff.mnemonicprefix' '
-       git -c diff.mnemonicprefix diff --default-prefix >actual &&
+test_expect_success 'diff --default-prefix overrides diff.mnemonicPrefix' '
+       git -c diff.mnemonicPrefix diff --default-prefix >actual &&
+       check_prefix actual a/file0 b/file0
+'
+
+test_expect_success 'diff respects diff.srcPrefix' '
+       git -c diff.srcPrefix=x/ diff >actual &&
+       check_prefix actual x/file0 b/file0
+'
+
+test_expect_success 'diff respects diff.dstPrefix' '
+       git -c diff.dstPrefix=y/ diff >actual &&
+       check_prefix actual a/file0 y/file0
+'
+
+test_expect_success 'diff --src-prefix overrides diff.srcPrefix' '
+       git -c diff.srcPrefix=y/ diff --src-prefix=z/ >actual &&
+       check_prefix actual z/file0 b/file0
+'
+
+test_expect_success 'diff --dst-prefix overrides diff.dstPrefix' '
+       git -c diff.dstPrefix=y/ diff --dst-prefix=z/ >actual &&
+       check_prefix actual a/file0 z/file0
+'
+
+test_expect_success 'diff.{src,dst}Prefix ignored with diff.noPrefix' '
+       git -c diff.dstPrefix=y/ -c diff.srcPrefix=x/ -c diff.noPrefix diff >actual &&
+       check_prefix actual file0 file0
+'
+
+test_expect_success 'diff.{src,dst}Prefix ignored with diff.mnemonicPrefix' '
+       git -c diff.dstPrefix=x/ -c diff.srcPrefix=y/ -c diff.mnemonicPrefix diff >actual &&
+       check_prefix actual i/file0 w/file0
+'
+
+test_expect_success 'diff.{src,dst}Prefix ignored with --default-prefix' '
+       git -c diff.dstPrefix=x/ -c diff.srcPrefix=y/ diff --default-prefix >actual &&
        check_prefix actual a/file0 b/file0
 '
 
index c1ac09ecc7140a3dcfcdf906bb4533ba131881de..fdd865f7c38dea5b60910b46c2a113ec7f5c2a09 100755 (executable)
@@ -232,7 +232,7 @@ keep_only_cr () {
 test_expect_success 'external diff with autocrlf = true' '
        test_config core.autocrlf true &&
        GIT_EXTERNAL_DIFF=./fake-diff.sh git diff &&
-       test $(wc -l < crlfed.txt) = $(cat crlfed.txt | keep_only_cr | wc -c)
+       test $(wc -l <crlfed.txt) = $(keep_only_cr <crlfed.txt | wc -c)
 '
 
 test_expect_success 'diff --cached' '
index ece9fae207dbdbcc28a4ac999a351bd059c0968a..56210b5609919dbd625cfe12c49ab7e858534896 100755 (executable)
@@ -66,4 +66,28 @@ test_expect_success 'apply --index create' '
        git diff --exit-code
 '
 
+test_expect_success !MINGW 'apply with no-contents and a funny pathname' '
+       test_when_finished "rm -fr \"funny \"; git reset --hard" &&
+
+       mkdir "funny " &&
+       >"funny /empty" &&
+       git add "funny /empty" &&
+       git diff HEAD -- "funny /" >sample.patch &&
+       git diff -R HEAD -- "funny /" >elpmas.patch &&
+
+       git reset --hard &&
+
+       git apply --stat --check --apply sample.patch &&
+       test_must_be_empty "funny /empty" &&
+
+       git apply --stat --check --apply elpmas.patch &&
+       test_path_is_missing "funny /empty" &&
+
+       git apply -R --stat --check --apply elpmas.patch &&
+       test_must_be_empty "funny /empty" &&
+
+       git apply -R --stat --check --apply sample.patch &&
+       test_path_is_missing "funny /empty"
+'
+
 test_done
index 3b125762694e02c9f67dc204f1e182caecead90c..5e2b6c80eaedfcbc1600a24c425dd4127233ff32 100755 (executable)
@@ -1224,8 +1224,8 @@ test_expect_success 'record as an empty commit when meeting e-mail message that
 
 test_expect_success 'skip an empty patch in the middle of an am session' '
        git checkout empty-commit^ &&
-       test_must_fail git am empty-commit.patch >err &&
-       grep "Patch is empty." err &&
+       test_must_fail git am empty-commit.patch >out 2>err &&
+       grep "Patch is empty." out &&
        grep "To record the empty patch as an empty commit, run \"git am --allow-empty\"." err &&
        git am --skip &&
        test_path_is_missing .git/rebase-apply &&
@@ -1236,8 +1236,8 @@ test_expect_success 'skip an empty patch in the middle of an am session' '
 
 test_expect_success 'record an empty patch as an empty commit in the middle of an am session' '
        git checkout empty-commit^ &&
-       test_must_fail git am empty-commit.patch >err &&
-       grep "Patch is empty." err &&
+       test_must_fail git am empty-commit.patch >out 2>err &&
+       grep "Patch is empty." out &&
        grep "To record the empty patch as an empty commit, run \"git am --allow-empty\"." err &&
        git am --allow-empty >output &&
        grep "No changes - recorded it as an empty commit." output &&
index e3d655e6b8b5565d619298b16da010ee671667a2..158b49d4b603d10d5fe802fb7888c0257a754d88 100755 (executable)
@@ -30,40 +30,46 @@ test_expect_success 'set up basic repos' '
        >bar &&
        git add foo &&
        test_tick &&
-       git config i18n.commitEncoding $test_encoding &&
+       test_config i18n.commitEncoding $test_encoding &&
        commit_msg $test_encoding | git commit -F - &&
        git add bar &&
        test_tick &&
-       git commit -m "add bar" &&
-       git config --unset i18n.commitEncoding
+       git commit -m "add bar"
 '
 
 test_expect_success 'alias builtin format' '
        git log --pretty=oneline >expected &&
-       git config pretty.test-alias oneline &&
+       test_config pretty.test-alias oneline &&
        git log --pretty=test-alias >actual &&
        test_cmp expected actual
 '
 
 test_expect_success 'alias masking builtin format' '
        git log --pretty=oneline >expected &&
-       git config pretty.oneline "%H" &&
+       test_config pretty.oneline "%H" &&
        git log --pretty=oneline >actual &&
        test_cmp expected actual
 '
 
 test_expect_success 'alias user-defined format' '
        git log --pretty="format:%h" >expected &&
-       git config pretty.test-alias "format:%h" &&
+       test_config pretty.test-alias "format:%h" &&
        git log --pretty=test-alias >actual &&
        test_cmp expected actual
 '
 
+test_expect_success 'alias user-defined format is matched case-insensitively' '
+       git log --pretty="format:%h" >expected &&
+       test_config pretty.testone "format:%h" &&
+       test_config pretty.testtwo testOne &&
+       git log --pretty=testTwo >actual &&
+       test_cmp expected actual
+'
+
 test_expect_success 'alias user-defined tformat with %s (ISO8859-1 encoding)' '
-       git config i18n.logOutputEncoding $test_encoding &&
+       test_config i18n.logOutputEncoding $test_encoding &&
        git log --oneline >expected-s &&
        git log --pretty="tformat:%h %s" >actual-s &&
-       git config --unset i18n.logOutputEncoding &&
        test_cmp expected-s actual-s
 '
 
@@ -75,34 +81,34 @@ test_expect_success 'alias user-defined tformat with %s (utf-8 encoding)' '
 
 test_expect_success 'alias user-defined tformat' '
        git log --pretty="tformat:%h" >expected &&
-       git config pretty.test-alias "tformat:%h" &&
+       test_config pretty.test-alias "tformat:%h" &&
        git log --pretty=test-alias >actual &&
        test_cmp expected actual
 '
 
 test_expect_success 'alias non-existent format' '
-       git config pretty.test-alias format-that-will-never-exist &&
+       test_config pretty.test-alias format-that-will-never-exist &&
        test_must_fail git log --pretty=test-alias
 '
 
 test_expect_success 'alias of an alias' '
        git log --pretty="tformat:%h" >expected &&
-       git config pretty.test-foo "tformat:%h" &&
-       git config pretty.test-bar test-foo &&
+       test_config pretty.test-foo "tformat:%h" &&
+       test_config pretty.test-bar test-foo &&
        git log --pretty=test-bar >actual && test_cmp expected actual
 '
 
 test_expect_success 'alias masking an alias' '
        git log --pretty=format:"Two %H" >expected &&
-       git config pretty.duplicate "format:One %H" &&
-       git config --add pretty.duplicate "format:Two %H" &&
+       test_config pretty.duplicate "format:One %H" &&
+       test_config pretty.duplicate "format:Two %H" --add &&
        git log --pretty=duplicate >actual &&
        test_cmp expected actual
 '
 
 test_expect_success 'alias loop' '
-       git config pretty.test-foo test-bar &&
-       git config pretty.test-bar test-foo &&
+       test_config pretty.test-foo test-bar &&
+       test_config pretty.test-bar test-foo &&
        test_must_fail git log --pretty=test-foo
 '
 
@@ -156,7 +162,7 @@ test_expect_success 'NUL termination with --reflog --pretty=oneline' '
        for r in $revs
        do
                git show -s --pretty=oneline "$r" >raw &&
-               cat raw | lf_to_nul || return 1
+               lf_to_nul <raw || return 1
        done >expect &&
        # the trailing NUL is already produced so we do not need to
        # output another one
index 45f1d4f95e5d5af7cff901639b5ecaf541846968..661feb60709f2380e72cf894a6c62bac8d097a73 100755 (executable)
@@ -59,7 +59,7 @@ test_expect_success setup '
 # Also, it had the unwanted side-effect of deleting f.
 test_expect_success 'try to apply corrupted patch' '
        test_when_finished "git am --abort" &&
-       test_must_fail git -c advice.amWorkDir=false am bad-patch.diff 2>actual &&
+       test_must_fail git -c advice.amWorkDir=false -c advice.mergeConflict=false am bad-patch.diff 2>actual &&
        echo "error: git diff header lacks filename information (line 4)" >expected &&
        test_path_is_file f &&
        test_cmp expected actual
index 29e9974cdfd421f6fb7dd17fa2bb12db8d5d6e04..eea19907b550c4a97a7ca7760f1be30bea7d8db0 100755 (executable)
@@ -313,7 +313,7 @@ test_expect_success 'rename/add handling' '
                # First, check that the bar that appears at stage 3 does not
                # correspond to an individual blob anywhere in history
                #
-               hash=$(cat out | tr "\0" "\n" | head -n 3 | grep 3.bar | cut -f 2 -d " ") &&
+               hash=$(tr "\0" "\n" <out | head -n 3 | grep 3.bar | cut -f 2 -d " ") &&
                git rev-list --objects --all >all_blobs &&
                ! grep $hash all_blobs &&
 
@@ -380,7 +380,7 @@ test_expect_success SYMLINKS 'rename/add, where add is a mode conflict' '
                # First, check that the bar that appears at stage 3 does not
                # correspond to an individual blob anywhere in history
                #
-               hash=$(cat out | tr "\0" "\n" | head -n 3 | grep 3.bar | cut -f 2 -d " ") &&
+               hash=$(tr "\0" "\n" <out | head -n 3 | grep 3.bar | cut -f 2 -d " ") &&
                git rev-list --objects --all >all_blobs &&
                ! grep $hash all_blobs &&
 
@@ -630,8 +630,8 @@ test_expect_success 'mod6: chains of rename/rename(1to2) and add/add via collidi
                # conflict entries do not appear as individual blobs anywhere
                # in history.
                #
-               hash1=$(cat out | tr "\0" "\n" | head | grep 2.four | cut -f 2 -d " ") &&
-               hash2=$(cat out | tr "\0" "\n" | head | grep 3.two | cut -f 2 -d " ") &&
+               hash1=$(tr "\0" "\n" <out | head | grep 2.four | cut -f 2 -d " ") &&
+               hash2=$(tr "\0" "\n" <out | head | grep 3.two | cut -f 2 -d " ") &&
                git rev-list --objects --all >all_blobs &&
                ! grep $hash1 all_blobs &&
                ! grep $hash2 all_blobs &&
index 654d8cf3ee003ee4e8d66b6e88df06312014cb47..c8d06554541cb5d7c575b69c83bd7d3828d49924 100755 (executable)
@@ -70,7 +70,7 @@ test_expect_success 'respect NULs' '
 
        git mailsplit -d3 -o. "$DATA/nul-plain" &&
        test_cmp "$DATA/nul-plain" 001 &&
-       (cat 001 | git mailinfo msg patch) &&
+       git mailinfo msg patch <001 &&
        test_line_count = 4 patch
 
 '
index a58f91035d124d6710438c5524c91bdbf66b4e84..61e2be2903d344563fe2ebea718de962e60d7b78 100755 (executable)
@@ -465,7 +465,7 @@ test_with_bad_commit () {
        must_pass_arg="$2" &&
        (
                cd strict &&
-               test_expect_fail git index-pack "$must_fail_arg" "test-$(cat pack-name).pack"
+               test_must_fail git index-pack "$must_fail_arg" "test-$(cat pack-name).pack" &&
                git index-pack "$must_pass_arg" "test-$(cat pack-name).pack"
        )
 }
index 2ff3eef9a3b8cad7f95253c79283d3ac542802fd..79552d6ef7f69751b8cfa1bd71edfda47973257b 100755 (executable)
@@ -455,7 +455,7 @@ test_expect_success 'setup r1 - delete loose blobs' '
        test_parse_ls_files_stage_oids <ls_files_result |
        sort >expected &&
 
-       for id in `cat expected | sed "s|..|&/|"`
+       for id in `sed "s|..|&/|" expected`
        do
                rm r1/.git/objects/$id || return 1
        done
index 8b8bc47dc0b9d6a8d90928c628bbe122d5d04f31..d8cadeec73310d36ad11a1427ec44cc9fc2d3a66 100755 (executable)
@@ -123,7 +123,7 @@ remote: STDOUT post-update
 remote: STDERR post-update
 EOF
 test_expect_success 'send-pack stderr contains hook messages' '
-       grep ^remote: send.err | sed "s/ *\$//" >actual &&
+       sed -n "/^remote:/s/ *\$//p" send.err >actual &&
        test_cmp expect actual
 '
 
index b4bc24691c8150e3b0829cb97af1911d13d02504..c91a62b77afcfba1bf1228c33717db77c7e45318 100755 (executable)
@@ -303,7 +303,7 @@ test_expect_success GPGSM 'fail without key and heed user.signingkey x509' '
                EOF
                sed -n -e "s/^nonce /NONCE=/p" -e "/^$/q" dst/push-cert
        ) >expect.in &&
-       key=$(cat "${GNUPGHOME}/trustlist.txt" | cut -d" " -f1 | tr -d ":") &&
+       key=$(cut -d" " -f1 <"${GNUPGHOME}/trustlist.txt" | tr -d ":") &&
        sed -e "s/^KEY=/KEY=${key}/" expect.in >expect &&
 
        noop=$(git rev-parse noop) &&
index fb1b9c686db24a30f9dbd2f6f38f29bfc4624676..ca431856814e7b48204c8ae0475ed14ce5e78ed7 100755 (executable)
@@ -776,6 +776,18 @@ test_expect_success 'batch missing blob request does not inadvertently try to fe
 . "$TEST_DIRECTORY"/lib-httpd.sh
 start_httpd
 
+test_expect_success 'clone with includeIf' '
+       test_when_finished "rm -rf repo \"$HTTPD_DOCUMENT_ROOT_PATH/repo.git\"" &&
+       git clone --bare --no-local src "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+
+       test_when_finished "rm \"$HOME\"/.gitconfig" &&
+       cat >"$HOME"/.gitconfig <<-EOF &&
+       [includeIf "onbranch:something"]
+               path = /does/not/exist.inc
+       EOF
+       git clone $HTTPD_URL/smart/repo.git repo
+'
+
 test_expect_success 'partial clone using HTTP' '
        partial_clone "$HTTPD_DOCUMENT_ROOT_PATH/server" "$HTTPD_URL/smart/server"
 '
index bcfb358c51cc087efd75aa04488f9b16a75e7293..c5b10f57751b259f297be3c4dcb673f32994017b 100755 (executable)
@@ -30,6 +30,7 @@ GIT_DIR="$url/.git"
 export GIT_DIR
 
 force=
+object_format=
 
 mkdir -p "$dir"
 
@@ -61,7 +62,8 @@ do
                echo
                ;;
        list)
-               echo ":object-format $(git rev-parse --show-object-format=storage)"
+               test -n "$object_format" &&
+                       echo ":object-format $(git rev-parse --show-object-format=storage)"
                git for-each-ref --format='? %(refname)' 'refs/heads/' 'refs/tags/'
                head=$(git symbolic-ref HEAD)
                echo "@$head HEAD"
index 52822b9461a18c89faa35d7dc0be6a1e4690da18..43e1afd44c9b9f0a00ed34cb4144f8bb1946b602 100755 (executable)
@@ -670,7 +670,7 @@ test_expect_success 'rev-list W/ --missing=print' '
        awk -f print_2.awk ls_files_result |
        sort >expected &&
 
-       for id in `cat expected | sed "s|..|&/|"`
+       for id in `sed "s|..|&/|" expected`
        do
                rm r1/.git/objects/$id || return 1
        done &&
index b4f4a313f486a583ca62268f2106948cca8d7266..647ea1e8382913887e292977502181b223136b46 100755 (executable)
@@ -34,14 +34,14 @@ test_expect_success setup '
 test_expect_success 'Check "ours" is CRLF' '
        git reset --hard initial &&
        git merge side -s ours &&
-       cat file | remove_cr | append_cr >file.temp &&
+       remove_cr <file | append_cr >file.temp &&
        test_cmp file file.temp
 '
 
 test_expect_success 'Check that conflict file is CRLF' '
        git reset --hard a &&
        test_must_fail git merge side &&
-       cat file | remove_cr | append_cr >file.temp &&
+       remove_cr <file | append_cr >file.temp &&
        test_cmp file file.temp
 '
 
index 41288a60ceb549295699f2efd9cb8e187d3e297b..48a62cb85568bfb7f77a8c597096617dfa5fcf4c 100755 (executable)
@@ -15,6 +15,7 @@ test_description='CRLF merge conflict across text=auto change
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_have_prereq SED_STRIPS_CR && SED_OPTIONS=-b
index 18fe1c25e6a04b75d2fcdf9ac0c60da04c618ba8..43d40175f8dac940cfa669605899d78b235041c1 100755 (executable)
@@ -11,23 +11,7 @@ test_expect_success 'setup' '
        # behavior, make sure we always pack everything to one pack by
        # default
        git config gc.bigPackThreshold 2g &&
-
-       # These are simply values which, when hashed as a blob with a newline,
-       # produce a hash where the first byte is 0x17 in their respective
-       # algorithms.
-       test_oid_cache <<-EOF
-       obj1 sha1:263
-       obj1 sha256:34
-
-       obj2 sha1:410
-       obj2 sha256:174
-
-       obj3 sha1:523
-       obj3 sha256:313
-
-       obj4 sha1:790
-       obj4 sha256:481
-       EOF
+       test_oid_init
 '
 
 test_expect_success 'gc empty repository' '
@@ -114,8 +98,8 @@ test_expect_success 'pre-auto-gc hook can stop auto gc' '
                # We need to create two object whose sha1s start with 17
                # since this is what git gc counts.  As it happens, these
                # two blobs will do so.
-               test_commit "$(test_oid obj1)" &&
-               test_commit "$(test_oid obj2)" &&
+               test_commit "$(test_oid blob17_1)" &&
+               test_commit "$(test_oid blob17_2)" &&
 
                git gc --auto >../out.actual 2>../err.actual
        ) &&
@@ -146,13 +130,13 @@ test_expect_success 'auto gc with too many loose objects does not attempt to cre
        # We need to create two object whose sha1s start with 17
        # since this is what git gc counts.  As it happens, these
        # two blobs will do so.
-       test_commit "$(test_oid obj1)" &&
-       test_commit "$(test_oid obj2)" &&
+       test_commit "$(test_oid blob17_1)" &&
+       test_commit "$(test_oid blob17_2)" &&
        # Our first gc will create a pack; our second will create a second pack
        git gc --auto &&
        ls .git/objects/pack/pack-*.pack | sort >existing_packs &&
-       test_commit "$(test_oid obj3)" &&
-       test_commit "$(test_oid obj4)" &&
+       test_commit "$(test_oid blob17_3)" &&
+       test_commit "$(test_oid blob17_4)" &&
 
        git gc --auto 2>err &&
        test_grep ! "^warning:" err &&
index b41a47eb943a03b1588bdc87802b0645944ce2ec..696866d7794e1fdd72760ed093b9ff9737d97969 100755 (executable)
@@ -1777,10 +1777,10 @@ test_expect_success '--points-at finds annotated tags of tags' '
 '
 
 test_expect_success 'recursive tagging should give advice' '
-       sed -e "s/|$//" <<-EOF >expect &&
+       cat >expect <<-EOF &&
        hint: You have created a nested tag. The object referred to by your new tag is
        hint: already a tag. If you meant to tag the object that it points to, use:
-       hint: |
+       hint:
        hint:   git tag -f nested annotated-v4.0^{}
        hint: Disable this message with "git config advice.nestedTag false"
        EOF
index 10cc6c46051e95e8e8d52fae23396574f319b802..42352dc0dbe51ec6ccf9eccc32df6932fdf711e9 100755 (executable)
@@ -631,6 +631,72 @@ test_expect_success 'checkout --conflict=diff3' '
        test_cmp merged file
 '
 
+test_expect_success 'checkout --conflict=diff3 --no-conflict does not merge' '
+       setup_conflicting_index &&
+       echo "none of the above" >expect &&
+       cat expect >fild &&
+       cat expect >file &&
+       test_must_fail git checkout --conflict=diff3 --no-conflict -- fild file 2>err &&
+       test_cmp expect file &&
+       test_cmp expect fild &&
+       echo "error: path ${SQ}file${SQ} is unmerged" >expect &&
+       test_cmp expect err
+'
+
+test_expect_success 'checkout --conflict=diff3 --no-merge does not merge' '
+       setup_conflicting_index &&
+       echo "none of the above" >expect &&
+       cat expect >fild &&
+       cat expect >file &&
+       test_must_fail git checkout --conflict=diff3 --no-merge -- fild file 2>err &&
+       test_cmp expect file &&
+       test_cmp expect fild &&
+       echo "error: path ${SQ}file${SQ} is unmerged" >expect &&
+       test_cmp expect err
+'
+
+test_expect_success 'checkout --no-merge --conflict=diff3 does merge' '
+       setup_conflicting_index &&
+       echo "none of the above" >fild &&
+       echo "none of the above" >file &&
+       git checkout --no-merge --conflict=diff3 -- fild file &&
+       echo "ourside" >expect &&
+       test_cmp expect fild &&
+       cat >expect <<-\EOF &&
+       <<<<<<< ours
+       ourside
+       ||||||| base
+       original
+       =======
+       theirside
+       >>>>>>> theirs
+       EOF
+       test_cmp expect file
+'
+
+test_expect_success 'checkout --merge --conflict=diff3 --no-conflict does merge' '
+       setup_conflicting_index &&
+       echo "none of the above" >fild &&
+       echo "none of the above" >file &&
+       git checkout --merge --conflict=diff3 --no-conflict -- fild file &&
+       echo "ourside" >expect &&
+       test_cmp expect fild &&
+       cat >expect <<-\EOF &&
+       <<<<<<< ours
+       ourside
+       =======
+       theirside
+       >>>>>>> theirs
+       EOF
+       test_cmp expect file
+'
+
+test_expect_success 'checkout with invalid conflict style' '
+       test_must_fail git checkout --conflict=bad 2>actual -- file &&
+       echo "error: unknown conflict style ${SQ}bad${SQ}" >expect &&
+       test_cmp expect actual
+'
+
 test_expect_success 'failing checkout -b should not break working tree' '
        git clean -fd &&  # Remove untracked files in the way
        git reset --hard main &&
index 00c1f1aab1304c127a5dccdaeff62d7213b7a9e5..5c4a89df5c81dcd5559d56de5924aa0140cb6772 100755 (executable)
@@ -212,8 +212,7 @@ test_expect_success 'submodule add to .gitignored path fails' '
                The following paths are ignored by one of your .gitignore files:
                submod
                hint: Use -f if you really want to add them.
-               hint: Turn this message off by running
-               hint: "git config advice.addIgnoredFile false"
+               hint: Disable this message with "git config advice.addIgnoredFile false"
                EOF
                # Does not use test_commit due to the ignore
                echo "*" > .gitignore &&
index c3281b192e49ce130d3babc4f9fda39acd3d69b8..4c7db19ce7eff05cb660018f8b8db3182e244efe 100755 (executable)
@@ -101,6 +101,16 @@ test_expect_success 'verbose diff is stripped out with set core.commentChar' '
        test_grep "Aborting commit due to empty commit message." err
 '
 
+test_expect_success 'verbose diff is stripped with multi-byte comment char' '
+       (
+               GIT_EDITOR=cat &&
+               export GIT_EDITOR &&
+               test_must_fail git -c core.commentchar="foo>" commit -a -v >out 2>err
+       ) &&
+       grep "^foo> " out &&
+       test_grep "Aborting commit due to empty commit message." err
+'
+
 test_expect_success 'status does not verbose without --verbose' '
        git status >actual &&
        ! grep "^diff --git" actual
index a3c18a4fc2764aa669556fe56961d254727bdab5..773383fefb50a9ac673b84d89ebd63ba92792d3b 100755 (executable)
@@ -419,14 +419,19 @@ Changes not staged for commit:
 Untracked files not listed (use -u option to show untracked files)
 EOF
        git status -uno >output &&
+       test_cmp expect output &&
+       git status -ufalse >output &&
        test_cmp expect output
 '
 
-test_expect_success 'status (status.showUntrackedFiles no)' '
-       test_config status.showuntrackedfiles no &&
-       git status >output &&
-       test_cmp expect output
-'
+for no in no false 0
+do
+       test_expect_success "status (status.showUntrackedFiles $no)" '
+               test_config status.showuntrackedfiles "$no" &&
+               git status >output &&
+               test_cmp expect output
+       '
+done
 
 test_expect_success 'status -uno (advice.statusHints false)' '
        cat >expect <<EOF &&
@@ -488,14 +493,21 @@ Untracked files:
 
 EOF
        git status -unormal >output &&
+       test_cmp expect output &&
+       git status -utrue >output &&
+       test_cmp expect output &&
+       git status -uyes >output &&
        test_cmp expect output
 '
 
-test_expect_success 'status (status.showUntrackedFiles normal)' '
-       test_config status.showuntrackedfiles normal &&
-       git status >output &&
-       test_cmp expect output
-'
+for normal in normal true 1
+do
+       test_expect_success "status (status.showUntrackedFiles $normal)" '
+               test_config status.showuntrackedfiles $normal &&
+               git status >output &&
+               test_cmp expect output
+       '
+done
 
 cat >expect <<EOF
  M dir1/modified
@@ -1403,7 +1415,9 @@ test_expect_success "status (core.commentchar with submodule summary)" '
 
 test_expect_success "status (core.commentchar with two chars with submodule summary)" '
        test_config core.commentchar ";;" &&
-       test_must_fail git -c status.displayCommentPrefix=true status
+       sed "s/^/;/" <expect >expect.double &&
+       git -c status.displayCommentPrefix=true status >output &&
+       test_cmp expect.double output
 '
 
 test_expect_success "--ignore-submodules=all suppresses submodule summary" '
index be3735dff0837a6050780082f3acc4e1f7da37dc..71e1ef3a103e780bd3f95f4e703ba23c29f71be1 100755 (executable)
@@ -48,7 +48,7 @@ test_expect_success '--expire-to stores pruned objects (now)' '
                # ...in other words, the combined contents of this
                # repository and expired.git should be the same as the
                # set of objects we started with.
-               cat expired.objects remaining.objects | sort >actual &&
+               sort expired.objects remaining.objects >actual &&
                test_cmp expect actual &&
 
                # The "moved" objects (i.e., those in expired.git)
index 96ae5d5880d6a597d0c553c4535081c2e51356df..cc917b257e3bb82451d027d05dce9dd91825ec63 100755 (executable)
@@ -93,42 +93,42 @@ test_expect_success 'difftool forwards arguments to diff' '
 
 for opt in '' '--dir-diff'
 do
-       test_expect_success "difftool ${opt} ignores exit code" "
+       test_expect_success "difftool ${opt:-without options} ignores exit code" '
                test_config difftool.error.cmd false &&
                git difftool ${opt} -y -t error branch
-       "
+       '
 
-       test_expect_success "difftool ${opt} forwards exit code with --trust-exit-code" "
+       test_expect_success "difftool ${opt:-without options} 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 ${opt} forwards exit code with --trust-exit-code for built-ins" "
+       test_expect_success "difftool ${opt:-without options} 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 ${opt} honors difftool.trustExitCode = true" "
+       test_expect_success "difftool ${opt:-without options} 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 ${opt} honors difftool.trustExitCode = false" "
+       test_expect_success "difftool ${opt:-without options} 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 ${opt} ignores exit code with --no-trust-exit-code" "
+       test_expect_success "difftool ${opt:-without options} 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 ${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' &&
+       test_expect_success "difftool ${opt:-without options} 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
@@ -138,19 +138,19 @@ do
                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 ${opt} honors exit status if command not found" "
+       test_expect_success "difftool ${opt:-without options} 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'
+               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
-       "
+               test_expect_code ${expected_code} git difftool ${opt} -y -t nonexistent branch
+       '
 done
 
 test_expect_success 'difftool honors --gui' '
index ca04242ca016368a5644ef7d04c8b3dab0569260..eb64b766bdfa24ee6fa3e8578188844b10e1ef66 100755 (executable)
@@ -43,7 +43,7 @@ test_expect_success 'cat-file --textconv --path=<path> works' '
        sha1=$(git rev-parse -q --verify HEAD:world.txt) &&
        test_config diff.txt.textconv "tr A-Za-z N-ZA-Mn-za-m <" &&
        git cat-file --textconv --path=hello.txt $sha1 >rot13 &&
-       test uryyb = "$(cat rot13 | remove_cr)"
+       test uryyb = "$(remove_cr <rot13)"
 '
 
 test_expect_success '--path=<path> complains without --textconv/--filters' '
index 9a03b0f361ff6db76fb9df2b8bd6dd8d50c4d57b..dbfbd86e83a756b1155925d3b739a6396d86af3c 100755 (executable)
@@ -25,11 +25,11 @@ test_expect_success setup '
 
        git blame --line-porcelain file >blame_raw &&
 
-       grep -E "^[0-9a-f]+ [0-9]+ 1" blame_raw | sed -e "s/ .*//" >actual &&
+       sed -ne "/^[0-9a-f][0-9a-f]* [0-9][0-9]* 1/s/ .*//p" blame_raw >actual &&
        git rev-parse X >expect &&
        test_cmp expect actual &&
 
-       grep -E "^[0-9a-f]+ [0-9]+ 2" blame_raw | sed -e "s/ .*//" >actual &&
+       sed -ne "/^[0-9a-f][0-9a-f]* [0-9][0-9]* 2/s/ .*//p" blame_raw >actual &&
        git rev-parse X >expect &&
        test_cmp expect actual
 '
@@ -53,11 +53,11 @@ do
        test_expect_success "ignore_rev_changing_lines ($I)" '
                git blame --line-porcelain --ignore-rev $I file >blame_raw &&
 
-               grep -E "^[0-9a-f]+ [0-9]+ 1" blame_raw | sed -e "s/ .*//" >actual &&
+               sed -ne "/^[0-9a-f][0-9a-f]* [0-9][0-9]* 1/s/ .*//p" blame_raw >actual &&
                git rev-parse A >expect &&
                test_cmp expect actual &&
 
-               grep -E "^[0-9a-f]+ [0-9]+ 2" blame_raw | sed -e "s/ .*//" >actual &&
+               sed -ne "/^[0-9a-f][0-9a-f]* [0-9][0-9]* 2/s/ .*//p" blame_raw >actual &&
                git rev-parse B >expect &&
                test_cmp expect actual
        '
@@ -79,10 +79,10 @@ test_expect_success ignore_rev_adding_unblamable_lines '
        git rev-parse Y >expect &&
        git blame --line-porcelain file --ignore-rev Y >blame_raw &&
 
-       grep -E "^[0-9a-f]+ [0-9]+ 3" blame_raw | sed -e "s/ .*//" >actual &&
+       sed -ne "/^[0-9a-f][0-9a-f]* [0-9][0-9]* 3/s/ .*//p" blame_raw >actual &&
        test_cmp expect actual &&
 
-       grep -E "^[0-9a-f]+ [0-9]+ 4" blame_raw | sed -e "s/ .*//" >actual &&
+       sed -ne "/^[0-9a-f][0-9a-f]* [0-9][0-9]* 4/s/ .*//p" blame_raw >actual &&
        test_cmp expect actual
 '
 
@@ -92,11 +92,11 @@ test_expect_success ignore_revs_from_files '
        git rev-parse Y >ignore_y &&
        git blame --line-porcelain file --ignore-revs-file ignore_x --ignore-revs-file ignore_y >blame_raw &&
 
-       grep -E "^[0-9a-f]+ [0-9]+ 1" blame_raw | sed -e "s/ .*//" >actual &&
+       sed -ne "/^[0-9a-f][0-9a-f]* [0-9][0-9]* 1/s/ .*//p" blame_raw >actual &&
        git rev-parse A >expect &&
        test_cmp expect actual &&
 
-       grep -E "^[0-9a-f]+ [0-9]+ 2" blame_raw | sed -e "s/ .*//" >actual &&
+       sed -ne "/^[0-9a-f][0-9a-f]* [0-9][0-9]* 2/s/ .*//p" blame_raw >actual &&
        git rev-parse B >expect &&
        test_cmp expect actual
 '
@@ -106,11 +106,11 @@ test_expect_success ignore_revs_from_configs_and_files '
        git config --add blame.ignoreRevsFile ignore_x &&
        git blame --line-porcelain file --ignore-revs-file ignore_y >blame_raw &&
 
-       grep -E "^[0-9a-f]+ [0-9]+ 1" blame_raw | sed -e "s/ .*//" >actual &&
+       sed -ne "/^[0-9a-f][0-9a-f]* [0-9][0-9]* 1/s/ .*//p" blame_raw >actual &&
        git rev-parse A >expect &&
        test_cmp expect actual &&
 
-       grep -E "^[0-9a-f]+ [0-9]+ 2" blame_raw | sed -e "s/ .*//" >actual &&
+       sed -ne "/^[0-9a-f][0-9a-f]* [0-9][0-9]* 2/s/ .*//p" blame_raw >actual &&
        git rev-parse B >expect &&
        test_cmp expect actual
 '
@@ -121,10 +121,10 @@ test_expect_success override_ignore_revs_file '
        git blame --line-porcelain file --ignore-revs-file "" --ignore-revs-file ignore_y >blame_raw &&
        git rev-parse X >expect &&
 
-       grep -E "^[0-9a-f]+ [0-9]+ 1" blame_raw | sed -e "s/ .*//" >actual &&
+       sed -ne "/^[0-9a-f][0-9a-f]* [0-9][0-9]* 1/s/ .*//p" blame_raw >actual &&
        test_cmp expect actual &&
 
-       grep -E "^[0-9a-f]+ [0-9]+ 2" blame_raw | sed -e "s/ .*//" >actual &&
+       sed -ne "/^[0-9a-f][0-9a-f]* [0-9][0-9]* 2/s/ .*//p" blame_raw >actual &&
        test_cmp expect actual
        '
 test_expect_success bad_files_and_revs '
@@ -279,11 +279,11 @@ test_expect_success ignore_merge '
        test_merge M B &&
        git blame --line-porcelain file --ignore-rev M >blame_raw &&
 
-       grep -E "^[0-9a-f]+ [0-9]+ 1" blame_raw | sed -e "s/ .*//" >actual &&
+       sed -ne "/^[0-9a-f][0-9a-f]* [0-9][0-9]* 1/s/ .*//p" blame_raw >actual &&
        git rev-parse B >expect &&
        test_cmp expect actual &&
 
-       grep -E "^[0-9a-f]+ [0-9]+ 9" blame_raw | sed -e "s/ .*//" >actual &&
+       sed -ne "/^[0-9a-f][0-9a-f]* [0-9][0-9]* 9/s/ .*//p" blame_raw >actual &&
        git rev-parse C >expect &&
        test_cmp expect actual
 '
index a159ff96b71882362eaa7d7856289561601674cf..d3261e35b818400f0242a18a4b024877b1307823 100755 (executable)
@@ -38,7 +38,7 @@ test_expect_success 'setup svnrepo' '
 # SVN 1.7 will truncate "not-a%40{0]" to just "not-a".
 # Look at what SVN wound up naming the branch and use that.
 # Be sure to escape the @ if it shows up.
-non_reflog=$(svn_cmd ls "$svnrepo/pr ject/branches" | grep not-a | sed 's/\///' | sed 's/@/%40/')
+non_reflog=$(svn_cmd ls "$svnrepo/pr ject/branches" | sed -ne '/not-a/ { s/\///; s/@/%40/; p }')
 
 test_expect_success 'test clone with funky branch names' '
        git svn clone -s "$svnrepo/pr ject" project &&
index dbb5042b0b8f1a00212ad01bcc8f8907ab47569a..60e30fed3c2cfc22b2b4190655685a428ce36289 100755 (executable)
@@ -986,7 +986,7 @@ test_expect_success 'L: nested tree copy does not corrupt deltas' '
        test_when_finished "git update-ref -d refs/heads/L2" &&
        git fast-import <input &&
        git ls-tree L2 g/b/ >tmp &&
-       cat tmp | cut -f 2 >actual &&
+       cut -f 2 <tmp >actual &&
        test_cmp expect actual &&
        git fsck $(git rev-parse L2)
 '
@@ -2007,12 +2007,11 @@ test_expect_success 'Q: verify first notes commit' '
 '
 
 test_expect_success 'Q: verify first notes tree' '
-       cat >expect.unsorted <<-EOF &&
+       sort >expect <<-EOF &&
        100644 blob $commit1
        100644 blob $commit2
        100644 blob $commit3
        EOF
-       cat expect.unsorted | sort >expect &&
        git cat-file -p refs/notes/foobar~2^{tree} | sed "s/ [0-9a-f]*  / /" >actual &&
        test_cmp expect actual
 '
@@ -2048,12 +2047,11 @@ test_expect_success 'Q: verify second notes commit' '
 '
 
 test_expect_success 'Q: verify second notes tree' '
-       cat >expect.unsorted <<-EOF &&
+       sort >expect <<-EOF &&
        100644 blob $commit1
        100644 blob $commit2
        100644 blob $commit3
        EOF
-       cat expect.unsorted | sort >expect &&
        git cat-file -p refs/notes/foobar^^{tree} | sed "s/ [0-9a-f]*   / /" >actual &&
        test_cmp expect actual
 '
@@ -2088,10 +2086,9 @@ test_expect_success 'Q: verify third notes commit' '
 '
 
 test_expect_success 'Q: verify third notes tree' '
-       cat >expect.unsorted <<-EOF &&
+       sort >expect <<-EOF &&
        100644 blob $commit1
        EOF
-       cat expect.unsorted | sort >expect &&
        git cat-file -p refs/notes/foobar2^{tree} | sed "s/ [0-9a-f]*   / /" >actual &&
        test_cmp expect actual
 '
@@ -2115,10 +2112,9 @@ test_expect_success 'Q: verify fourth notes commit' '
 '
 
 test_expect_success 'Q: verify fourth notes tree' '
-       cat >expect.unsorted <<-EOF &&
+       sort >expect <<-EOF &&
        100644 blob $commit2
        EOF
-       cat expect.unsorted | sort >expect &&
        git cat-file -p refs/notes/foobar^{tree} | sed "s/ [0-9a-f]*    / /" >actual &&
        test_cmp expect actual
 '
index e9a12c18bbd3f8bd43659d0b4eee0e4dfbad30ab..1eb035ee4ce547d059e07c43f04102e66ebcb000 100755 (executable)
@@ -236,7 +236,7 @@ EOF
 
 test_expect_success 'set up faked signed tag' '
 
-       cat signed-tag-import | git fast-import
+       git fast-import <signed-tag-import
 
 '
 
@@ -537,7 +537,7 @@ test_expect_success 'full-tree re-shows unmodified files'        '
 
 test_expect_success 'set-up a few more tags for tag export tests' '
        git checkout -f main &&
-       HEAD_TREE=$(git show -s --pretty=raw HEAD | grep tree | sed "s/tree //") &&
+       HEAD_TREE=$(git show -s --pretty=raw HEAD | sed -n "/tree/s/tree //p") &&
        git tag    tree_tag        -m "tagging a tree" $HEAD_TREE &&
        git tag -a tree_tag-obj    -m "tagging a tree" $HEAD_TREE &&
        git tag    tag-obj_tag     -m "tagging a tag" tree_tag-obj &&
index 003c0b61d0ff45864630782a509a018ff4079116..e499c7f955125eb25ce0dfdcd78012adf8cf4d7a 100755 (executable)
@@ -117,12 +117,12 @@ END VERIFICATION REQUEST
 EOF
 
 test_expect_success 'pserver authentication' '
-       cat request-anonymous | git-cvsserver pserver >log 2>&1 &&
+       git-cvsserver pserver <request-anonymous >log 2>&1 &&
        sed -ne \$p log | grep "^I LOVE YOU\$"
 '
 
 test_expect_success 'pserver authentication failure (non-anonymous user)' '
-       if cat request-git | git-cvsserver pserver >log 2>&1
+       if git-cvsserver pserver <request-git >log 2>&1
        then
            false
        else
@@ -132,17 +132,17 @@ test_expect_success 'pserver authentication failure (non-anonymous user)' '
 '
 
 test_expect_success 'pserver authentication success (non-anonymous user with password)' '
-       cat login-git-ok | git-cvsserver pserver >log 2>&1 &&
+       git-cvsserver pserver <login-git-ok >log 2>&1 &&
        sed -ne \$p log | grep "^I LOVE YOU\$"
 '
 
 test_expect_success 'pserver authentication (login)' '
-       cat login-anonymous | git-cvsserver pserver >log 2>&1 &&
+       git-cvsserver pserver <login-anonymous >log 2>&1 &&
        sed -ne \$p log | grep "^I LOVE YOU\$"
 '
 
 test_expect_success 'pserver authentication failure (login/non-anonymous user)' '
-       if cat login-git | git-cvsserver pserver >log 2>&1
+       if git-cvsserver pserver <login-git >log 2>&1
        then
            false
        else
@@ -172,7 +172,7 @@ Root $WORKDIR
 EOF
 
 test_expect_success 'req_Root failure (relative pathname)' '
-       if cat request-relative | git-cvsserver pserver >log 2>&1
+       if git-cvsserver pserver <request-relative >log 2>&1
        then
                echo unexpected success
                false
@@ -183,28 +183,26 @@ test_expect_success 'req_Root failure (relative pathname)' '
 '
 
 test_expect_success 'req_Root failure (conflicting roots)' '
-       cat request-conflict | git-cvsserver pserver >log 2>&1 &&
+       git-cvsserver pserver <request-conflict >log 2>&1 &&
        tail log | grep "^error 1 Conflicting roots specified$"
 '
 
 test_expect_success 'req_Root (strict paths)' '
-       cat request-anonymous | git-cvsserver --strict-paths pserver "$SERVERDIR" >log 2>&1 &&
+       git-cvsserver --strict-paths pserver "$SERVERDIR" <request-anonymous >log 2>&1 &&
        sed -ne \$p log | grep "^I LOVE YOU\$"
 '
 
 test_expect_success 'req_Root failure (strict-paths)' '
-       ! cat request-anonymous |
-       git-cvsserver --strict-paths pserver "$WORKDIR" >log 2>&1
+       ! git-cvsserver --strict-paths pserver "$WORKDIR" <request-anonymous >log 2>&1
 '
 
 test_expect_success 'req_Root (w/o strict-paths)' '
-       cat request-anonymous | git-cvsserver pserver "$WORKDIR/" >log 2>&1 &&
+       git-cvsserver pserver "$WORKDIR/" <request-anonymous >log 2>&1 &&
        sed -ne \$p log | grep "^I LOVE YOU\$"
 '
 
 test_expect_success 'req_Root failure (w/o strict-paths)' '
-       ! cat request-anonymous |
-       git-cvsserver pserver "$WORKDIR/gitcvs" >log 2>&1
+       ! git-cvsserver pserver "$WORKDIR/gitcvs" <request-anonymous >log 2>&1
 '
 
 cat >request-base  <<EOF
@@ -217,27 +215,26 @@ Root /gitcvs.git
 EOF
 
 test_expect_success 'req_Root (base-path)' '
-       cat request-base | git-cvsserver --strict-paths --base-path "$WORKDIR/" pserver "$SERVERDIR" >log 2>&1 &&
+       git-cvsserver --strict-paths --base-path "$WORKDIR/" pserver "$SERVERDIR" <request-base >log 2>&1 &&
        sed -ne \$p log | grep "^I LOVE YOU\$"
 '
 
 test_expect_success 'req_Root failure (base-path)' '
-       ! cat request-anonymous |
-       git-cvsserver --strict-paths --base-path "$WORKDIR" pserver "$SERVERDIR" >log 2>&1
+       ! git-cvsserver --strict-paths --base-path "$WORKDIR" pserver "$SERVERDIR" <request-anonymous >log 2>&1
 '
 
 GIT_DIR="$SERVERDIR" git config --bool gitcvs.enabled false || exit 1
 
 test_expect_success 'req_Root (export-all)' '
-       cat request-anonymous | git-cvsserver --export-all pserver "$WORKDIR" >log 2>&1 &&
+       git-cvsserver --export-all pserver "$WORKDIR" <request-anonymous >log 2>&1 &&
        sed -ne \$p log | grep "^I LOVE YOU\$"
 '
 
 test_expect_success 'req_Root failure (export-all w/o directory list)' '
-       ! (cat request-anonymous | git-cvsserver --export-all pserver >log 2>&1 || false)'
+       ! (git-cvsserver --export-all pserver <request-anonymous >log 2>&1 || false)'
 
 test_expect_success 'req_Root (everything together)' '
-       cat request-base | git-cvsserver --export-all --strict-paths --base-path "$WORKDIR/" pserver "$SERVERDIR" >log 2>&1 &&
+       git-cvsserver --export-all --strict-paths --base-path "$WORKDIR/" pserver "$SERVERDIR" <request-base >log 2>&1 &&
        sed -ne \$p log | grep "^I LOVE YOU\$"
 '
 
index 2a6ee2a46787f07c763c6d7608628c65d8181526..bb236cd2b57a3c21885237c5698dfa04ede92b55 100755 (executable)
@@ -175,7 +175,7 @@ test_expect_success 'keyword file create' '
                cp k-text-k k-text-ko &&
                p4 add -t text+ko k-text-ko &&
 
-               cat k-text-k | iconv -f ascii -t utf-16 >k-utf16-k &&
+               iconv -f ascii -t utf-16 <k-text-k >k-utf16-k &&
                p4 add -t utf16+k k-utf16-k &&
 
                cp k-utf16-k k-utf16-ko &&
index af4b286f9d51af7655e86036d9938a691780af23..6ae7ced51be1d4e0d649cea05f2373e592be73ef 100755 (executable)
@@ -418,7 +418,7 @@ test_expect_success 'description with Jobs and values on separate lines' '
                        marshal_dump job0 <change &&
                        marshal_dump job1 <change
                ) | sort >jobs &&
-               cat jobname1 jobname2 | sort >expected &&
+               sort jobname1 jobname2 >expected &&
                test_cmp expected jobs
        )
 '
index a28dbbdd566ca69212f23958056d95b735a0127c..80c8c31e320fd5bbf0912d093682dcad3f287fc3 100755 (executable)
@@ -17,8 +17,8 @@ test_file_in_lfs () {
        sed -n '2,2 p' "$FILE" | grep "^oid " &&
        sed -n '3,3 p' "$FILE" | grep "^size " &&
        test_line_count = 3 "$FILE" &&
-       cat "$FILE" | grep "size $SIZE" &&
-       HASH=$(cat "$FILE" | grep "oid sha256:" | sed -e "s/oid sha256://g") &&
+       grep "size $SIZE" "$FILE" &&
+       HASH=$(sed -ne "/oid sha256:/s/oid sha256://gp" "$FILE") &&
        LFS_FILE=".git/lfs/objects/$(echo "$HASH" | cut -c1-2)/$(echo "$HASH" | cut -c3-4)/$HASH" &&
        echo $EXPECTED_CONTENT >expect &&
        test_path_is_file "$FILE" &&
index 6eaf116346be3ee52d2094715ac979aa059093c5..2eccf100c024e21363ac799a1d032470ede16ae9 100644 (file)
@@ -1655,7 +1655,16 @@ test_set_hash () {
 
 # Detect the hash algorithm in use.
 test_detect_hash () {
-       test_hash_algo="${GIT_TEST_DEFAULT_HASH:-sha1}"
+       case "$GIT_TEST_DEFAULT_HASH" in
+       "sha256")
+           test_hash_algo=sha256
+           test_compat_hash_algo=sha1
+           ;;
+       *)
+           test_hash_algo=sha1
+           test_compat_hash_algo=sha256
+           ;;
+       esac
 }
 
 # Detect the hash algorithm in use.
@@ -1712,6 +1721,12 @@ test_oid () {
        local algo="${test_hash_algo}" &&
 
        case "$1" in
+       --hash=storage)
+               algo="$test_hash_algo" &&
+               shift;;
+       --hash=compat)
+               algo="$test_compat_hash_algo" &&
+               shift;;
        --hash=*)
                algo="${1#--hash=}" &&
                shift;;
index c8af8dab795604998475e5fdff21137534c7f39b..79d3e0e7d9b32dd2938e635dc94acc6b49000569 100644 (file)
@@ -1962,6 +1962,7 @@ test_lazy_prereq DEFAULT_REPO_FORMAT '
 # Tests that verify the scheduler integration must set this locally
 # to avoid errors.
 GIT_TEST_MAINT_SCHEDULER="none:exit 1"
+export GIT_TEST_MAINT_SCHEDULER
 
 # Does this platform support `git fsmonitor--daemon`
 #
index d78b002f9eafe91e5ff1701242689c57f948fcd7..535834636131872b6f6f45bccea9a4bc868e31f2 100644 (file)
@@ -19,11 +19,13 @@ static int show(int *v)
        return v ? *v : MISSING;
 }
 
-static void test_prio_queue(int *input, int *result, size_t input_size)
+static void test_prio_queue(int *input, size_t input_size,
+                           int *result, size_t result_size)
 {
        struct prio_queue pq = { intcmp };
+       int j = 0;
 
-       for (int i = 0, j = 0; i < input_size; i++) {
+       for (int i = 0; i < input_size; i++) {
                void *peek, *get;
                switch(input[i]) {
                case GET:
@@ -31,16 +33,22 @@ static void test_prio_queue(int *input, int *result, size_t input_size)
                        get = prio_queue_get(&pq);
                        if (!check(peek == get))
                                return;
-                       if(!check_int(result[j++], ==, show(get)))
-                               test_msg("failed at result[] index %d", j-1);
+                       if (!check_uint(j, <, result_size))
+                               break;
+                       if (!check_int(result[j], ==, show(get)))
+                               test_msg("      j: %d", j);
+                       j++;
                        break;
                case DUMP:
                        while ((peek = prio_queue_peek(&pq))) {
                                get = prio_queue_get(&pq);
                                if (!check(peek == get))
                                        return;
-                               if(!check_int(result[j++], ==, show(get)))
-                                       test_msg("failed at result[] index %d", j-1);
+                               if (!check_uint(j, <, result_size))
+                                       break;
+                               if (!check_int(result[j], ==, show(get)))
+                                       test_msg("      j: %d", j);
+                               j++;
                        }
                        break;
                case STACK:
@@ -54,6 +62,7 @@ static void test_prio_queue(int *input, int *result, size_t input_size)
                        break;
                }
        }
+       check_uint(j, ==, result_size);
        clear_prio_queue(&pq);
 }
 
@@ -77,7 +86,8 @@ static void test_prio_queue(int *input, int *result, size_t input_size)
 {                                                              \
        int input[] = {INPUT};                                  \
        int result[] = {RESULT};                                \
-       test_prio_queue(input, result, ARRAY_SIZE(input));      \
+       test_prio_queue(input, ARRAY_SIZE(input),               \
+                       result, ARRAY_SIZE(result));            \
 }
 
 TEST_INPUT(BASIC_INPUT, BASIC_RESULT, basic)
index 57b4aa7d5acb515b03b2cf123744d8af34d30166..dc15d850b483e19923c3b8cf432a155e941febc4 100644 (file)
--- a/trailer.c
+++ b/trailer.c
@@ -871,7 +871,7 @@ static size_t find_trailer_block_start(const char *buf, size_t len)
 
        /* The first paragraph is the title and cannot be trailers */
        for (s = buf; s < buf + len; s = next_line(s)) {
-               if (s[0] == comment_line_char)
+               if (starts_with_mem(s, buf + len - s, comment_line_str))
                        continue;
                if (is_blank_line(s))
                        break;
@@ -891,7 +891,7 @@ static size_t find_trailer_block_start(const char *buf, size_t len)
                const char **p;
                ssize_t separator_pos;
 
-               if (bol[0] == comment_line_char) {
+               if (starts_with_mem(bol, buf + len - bol, comment_line_str)) {
                        non_trailer_lines += possible_continuation_lines;
                        possible_continuation_lines = 0;
                        continue;
@@ -1002,7 +1002,7 @@ void parse_trailers(const struct process_trailer_options *opts,
        for (i = 0; i < info->trailer_nr; i++) {
                int separator_pos;
                char *trailer = info->trailers[i];
-               if (trailer[0] == comment_line_char)
+               if (starts_with(trailer, comment_line_str))
                        continue;
                separator_pos = find_separator(trailer, separators);
                if (separator_pos >= 1) {
index b660b7942f9f9dbfc76193968d3511047b8afd84..8d284b24d5d5e79434932a12e38d06f15ac665da 100644 (file)
@@ -1210,16 +1210,13 @@ static struct ref *get_refs_list_using_list(struct transport *transport,
        data->get_refs_list_called = 1;
        helper = get_helper(transport);
 
-       if (data->object_format) {
-               write_str_in_full(helper->in, "option object-format\n");
-               if (recvline(data, &buf) || strcmp(buf.buf, "ok"))
-                       exit(128);
-       }
+       if (data->object_format)
+               set_helper_option(transport, "object-format", "true");
 
        if (data->push && for_push)
-               write_str_in_full(helper->in, "list for-push\n");
+               write_constant(helper->in, "list for-push\n");
        else
-               write_str_in_full(helper->in, "list\n");
+               write_constant(helper->in, "list\n");
 
        while (1) {
                char *eov, *eon;
index 690fa6569bd7fe03ca104e3e789fd67b58e41270..6565d9ad993bd830446277cc35a2aa54567cd18f 100644 (file)
 #include "json-writer.h"
 #include "environment.h"
 
-static const char *get_mode(const char *str, unsigned int *modep)
-{
-       unsigned char c;
-       unsigned int mode = 0;
-
-       if (*str == ' ')
-               return NULL;
-
-       while ((c = *str++) != ' ') {
-               if (c < '0' || c > '7')
-                       return NULL;
-               mode = (mode << 3) + (c - '0');
-       }
-       *modep = mode;
-       return str;
-}
-
 static int decode_tree_entry(struct tree_desc *desc, const char *buf, unsigned long size, struct strbuf *err)
 {
        const char *path;
-       unsigned int mode, len;
-       const unsigned hashsz = the_hash_algo->rawsz;
+       unsigned int len;
+       uint16_t mode;
+       const unsigned hashsz = desc->algo->rawsz;
 
        if (size < hashsz + 3 || buf[size - (hashsz + 1)]) {
                strbuf_addstr(err, _("too-short tree object"));
                return -1;
        }
 
-       path = get_mode(buf, &mode);
+       path = parse_mode(buf, &mode);
        if (!path) {
                strbuf_addstr(err, _("malformed mode in tree entry"));
                return -1;
@@ -54,15 +38,19 @@ static int decode_tree_entry(struct tree_desc *desc, const char *buf, unsigned l
        desc->entry.path = path;
        desc->entry.mode = (desc->flags & TREE_DESC_RAW_MODES) ? mode : canon_mode(mode);
        desc->entry.pathlen = len - 1;
-       oidread(&desc->entry.oid, (const unsigned char *)path + len);
+       oidread_algop(&desc->entry.oid, (const unsigned char *)path + len,
+                     desc->algo);
 
        return 0;
 }
 
-static int init_tree_desc_internal(struct tree_desc *desc, const void *buffer,
-                                  unsigned long size, struct strbuf *err,
+static int init_tree_desc_internal(struct tree_desc *desc,
+                                  const struct object_id *oid,
+                                  const void *buffer, unsigned long size,
+                                  struct strbuf *err,
                                   enum tree_desc_flags flags)
 {
+       desc->algo = (oid && oid->algo) ? &hash_algos[oid->algo] : the_hash_algo;
        desc->buffer = buffer;
        desc->size = size;
        desc->flags = flags;
@@ -71,19 +59,21 @@ static int init_tree_desc_internal(struct tree_desc *desc, const void *buffer,
        return 0;
 }
 
-void init_tree_desc(struct tree_desc *desc, const void *buffer, unsigned long size)
+void init_tree_desc(struct tree_desc *desc, const struct object_id *tree_oid,
+                   const void *buffer, unsigned long size)
 {
        struct strbuf err = STRBUF_INIT;
-       if (init_tree_desc_internal(desc, buffer, size, &err, 0))
+       if (init_tree_desc_internal(desc, tree_oid, buffer, size, &err, 0))
                die("%s", err.buf);
        strbuf_release(&err);
 }
 
-int init_tree_desc_gently(struct tree_desc *desc, const void *buffer, unsigned long size,
+int init_tree_desc_gently(struct tree_desc *desc, const struct object_id *oid,
+                         const void *buffer, unsigned long size,
                          enum tree_desc_flags flags)
 {
        struct strbuf err = STRBUF_INIT;
-       int result = init_tree_desc_internal(desc, buffer, size, &err, flags);
+       int result = init_tree_desc_internal(desc, oid, buffer, size, &err, flags);
        if (result)
                error("%s", err.buf);
        strbuf_release(&err);
@@ -102,7 +92,7 @@ void *fill_tree_descriptor(struct repository *r,
                if (!buf)
                        die(_("unable to read tree (%s)"), oid_to_hex(oid));
        }
-       init_tree_desc(desc, buf, size);
+       init_tree_desc(desc, oid, buf, size);
        return buf;
 }
 
@@ -119,7 +109,7 @@ static void entry_extract(struct tree_desc *t, struct name_entry *a)
 static int update_tree_entry_internal(struct tree_desc *desc, struct strbuf *err)
 {
        const void *buf = desc->buffer;
-       const unsigned char *end = (const unsigned char *)desc->entry.path + desc->entry.pathlen + 1 + the_hash_algo->rawsz;
+       const unsigned char *end = (const unsigned char *)desc->entry.path + desc->entry.pathlen + 1 + desc->algo->rawsz;
        unsigned long size = desc->size;
        unsigned long len = end - (const unsigned char *)buf;
 
@@ -633,7 +623,7 @@ int get_tree_entry(struct repository *r,
                retval = -1;
        } else {
                struct tree_desc t;
-               init_tree_desc(&t, tree, size);
+               init_tree_desc(&t, tree_oid, tree, size);
                retval = find_tree_entry(r, &t, name, oid, mode);
        }
        free(tree);
@@ -676,7 +666,7 @@ enum get_oid_result get_tree_entry_follow_symlinks(struct repository *r,
        struct tree_desc t;
        int follows_remaining = GET_TREE_ENTRY_FOLLOW_SYMLINKS_MAX_LINKS;
 
-       init_tree_desc(&t, NULL, 0UL);
+       init_tree_desc(&t, NULL, NULL, 0UL);
        strbuf_addstr(&namebuf, name);
        oidcpy(&current_tree_oid, tree_oid);
 
@@ -712,7 +702,7 @@ enum get_oid_result get_tree_entry_follow_symlinks(struct repository *r,
                                goto done;
 
                        /* descend */
-                       init_tree_desc(&t, tree, size);
+                       init_tree_desc(&t, &current_tree_oid, tree, size);
                }
 
                /* Handle symlinks to e.g. a//b by removing leading slashes */
@@ -746,7 +736,7 @@ enum get_oid_result get_tree_entry_follow_symlinks(struct repository *r,
                        free(parent->tree);
                        parents_nr--;
                        parent = &parents[parents_nr - 1];
-                       init_tree_desc(&t, parent->tree, parent->size);
+                       init_tree_desc(&t, &parent->oid, parent->tree, parent->size);
                        strbuf_remove(&namebuf, 0, remainder ? 3 : 2);
                        continue;
                }
@@ -826,7 +816,7 @@ enum get_oid_result get_tree_entry_follow_symlinks(struct repository *r,
                        contents_start = contents;
 
                        parent = &parents[parents_nr - 1];
-                       init_tree_desc(&t, parent->tree, parent->size);
+                       init_tree_desc(&t, &parent->oid, parent->tree, parent->size);
                        strbuf_splice(&namebuf, 0, len,
                                      contents_start, link_len);
                        if (remainder)
index a6bfa3da3a826bf5c218132b93582e53ace876be..0b1067fbc51affd9ff98ed44fcaad043f9870a22 100644 (file)
@@ -24,6 +24,7 @@ struct name_entry {
  * A semi-opaque data structure used to maintain the current state of the walk.
  */
 struct tree_desc {
+       const struct git_hash_algo *algo;
        /*
         * pointer into the memory representation of the tree. It always
         * points at the current entry being visited.
@@ -83,9 +84,11 @@ int update_tree_entry_gently(struct tree_desc *);
  * size parameters are assumed to be the same as the buffer and size
  * members of `struct tree`.
  */
-void init_tree_desc(struct tree_desc *desc, const void *buf, unsigned long size);
+void init_tree_desc(struct tree_desc *desc, const struct object_id *tree_oid,
+                   const void *buf, unsigned long size);
 
-int init_tree_desc_gently(struct tree_desc *desc, const void *buf, unsigned long size,
+int init_tree_desc_gently(struct tree_desc *desc, const struct object_id *oid,
+                         const void *buf, unsigned long size,
                          enum tree_desc_flags flags);
 
 /*
diff --git a/tree.c b/tree.c
index 508e5fd76fd5bb588796bb9c51a78557880ba074..7973d3f9a83228a70586cf211d8a8eec7b21f639 100644 (file)
--- a/tree.c
+++ b/tree.c
@@ -29,7 +29,7 @@ int read_tree_at(struct repository *r,
        if (parse_tree(tree))
                return -1;
 
-       init_tree_desc(&desc, tree->buffer, tree->size);
+       init_tree_desc(&desc, &tree->object.oid, tree->buffer, tree->size);
 
        while (tree_entry(&desc, &entry)) {
                if (retval != all_entries_interesting) {
index 65002a7220adc2e60aafe6a29555df1cee167ad0..c0fd632d921c4f229d56f5c406a4c97386f3788c 100644 (file)
--- a/walker.c
+++ b/walker.c
@@ -45,7 +45,7 @@ static int process_tree(struct walker *walker, struct tree *tree)
        if (parse_tree(tree))
                return -1;
 
-       init_tree_desc(&desc, tree->buffer, tree->size);
+       init_tree_desc(&desc, &tree->object.oid, tree->buffer, tree->size);
        while (tree_entry(&desc, &entry)) {
                struct object *obj = NULL;
 
index b02a05a74a341157fa8dff7da22936127bebf18e..cf5eea8c931a0cea85499aab6d24e5cbd392d839 100644 (file)
@@ -807,9 +807,9 @@ int should_prune_worktree(const char *id, struct strbuf *reason, char **wtpath,
 static int move_config_setting(const char *key, const char *value,
                               const char *from_file, const char *to_file)
 {
-       if (git_config_set_in_file_gently(to_file, key, value))
+       if (git_config_set_in_file_gently(to_file, key, NULL, value))
                return error(_("unable to set %s in '%s'"), key, to_file);
-       if (git_config_set_in_file_gently(from_file, key, NULL))
+       if (git_config_set_in_file_gently(from_file, key, NULL, NULL))
                return error(_("unable to unset %s in '%s'"), key, from_file);
        return 0;
 }
index 2db4bb3a1293bb69e081efcf98489a63ca2d812a..bdfc23e2ae7de8a9521c03420448e412d4262d84 100644 (file)
@@ -70,7 +70,7 @@ static void status_vprintf(struct wt_status *s, int at_bol, const char *color,
        strbuf_vaddf(&sb, fmt, ap);
        if (!sb.len) {
                if (s->display_comment_prefix) {
-                       strbuf_addch(&sb, comment_line_char);
+                       strbuf_addstr(&sb, comment_line_str);
                        if (!trail)
                                strbuf_addch(&sb, ' ');
                }
@@ -85,7 +85,7 @@ static void status_vprintf(struct wt_status *s, int at_bol, const char *color,
 
                strbuf_reset(&linebuf);
                if (at_bol && s->display_comment_prefix) {
-                       strbuf_addch(&linebuf, comment_line_char);
+                       strbuf_addstr(&linebuf, comment_line_str);
                        if (*line != '\n' && *line != '\t')
                                strbuf_addch(&linebuf, ' ');
                }
@@ -1028,7 +1028,7 @@ static void wt_longstatus_print_submodule_summary(struct wt_status *s, int uncom
        if (s->display_comment_prefix) {
                size_t len;
                summary_content = strbuf_detach(&summary, &len);
-               strbuf_add_commented_lines(&summary, summary_content, len, comment_line_char);
+               strbuf_add_commented_lines(&summary, summary_content, len, comment_line_str);
                free(summary_content);
        }
 
@@ -1090,7 +1090,7 @@ size_t wt_status_locate_end(const char *s, size_t len)
        const char *p;
        struct strbuf pattern = STRBUF_INIT;
 
-       strbuf_addf(&pattern, "\n%c %s", comment_line_char, cut_line);
+       strbuf_addf(&pattern, "\n%s %s", comment_line_str, cut_line);
        if (starts_with(s, pattern.buf + 1))
                len = 0;
        else if ((p = strstr(s, pattern.buf))) {
@@ -1106,8 +1106,8 @@ void wt_status_append_cut_line(struct strbuf *buf)
 {
        const char *explanation = _("Do not modify or remove the line above.\nEverything below it will be ignored.");
 
-       strbuf_commented_addf(buf, comment_line_char, "%s", cut_line);
-       strbuf_add_commented_lines(buf, explanation, strlen(explanation), comment_line_char);
+       strbuf_commented_addf(buf, comment_line_str, "%s", cut_line);
+       strbuf_add_commented_lines(buf, explanation, strlen(explanation), comment_line_str);
 }
 
 void wt_status_add_cut_line(struct wt_status *s)
@@ -1183,8 +1183,6 @@ static void wt_longstatus_print_tracking(struct wt_status *s)
        struct strbuf sb = STRBUF_INIT;
        const char *cp, *ep, *branch_name;
        struct branch *branch;
-       char comment_line_string[3];
-       int i;
        uint64_t t_begin = 0;
 
        assert(s->branch && !s->is_initial);
@@ -1209,20 +1207,15 @@ static void wt_longstatus_print_tracking(struct wt_status *s)
                }
        }
 
-       i = 0;
-       if (s->display_comment_prefix) {
-               comment_line_string[i++] = comment_line_char;
-               comment_line_string[i++] = ' ';
-       }
-       comment_line_string[i] = '\0';
-
        for (cp = sb.buf; (ep = strchr(cp, '\n')) != NULL; cp = ep + 1)
                color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s),
-                                "%s%.*s", comment_line_string,
+                                "%s%s%.*s",
+                                s->display_comment_prefix ? comment_line_str : "",
+                                s->display_comment_prefix ? " " : "",
                                 (int)(ep - cp), cp);
        if (s->display_comment_prefix)
-               color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), "%c",
-                                comment_line_char);
+               color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), "%s",
+                                comment_line_str);
        else
                fputs("\n", s->fp);
        strbuf_release(&sb);
@@ -1389,7 +1382,7 @@ static int read_rebase_todolist(const char *fname, struct string_list *lines)
                          git_path("%s", fname));
        }
        while (!strbuf_getline_lf(&line, f)) {
-               if (line.len && line.buf[0] == comment_line_char)
+               if (starts_with(line.buf, comment_line_str))
                        continue;
                strbuf_trim(&line);
                if (!line.len)
index 5e99ba47073493aa4ee80a9501dd4375ec903d8f..4e377ce62b8b2871bb73cf900acff0a9bab40c6c 100644 (file)
@@ -23,7 +23,8 @@ enum color_wt_status {
 };
 
 enum untracked_status_type {
-       SHOW_NO_UNTRACKED_FILES,
+       SHOW_UNTRACKED_FILES_ERROR = -1,
+       SHOW_NO_UNTRACKED_FILES = 0,
        SHOW_NORMAL_UNTRACKED_FILES,
        SHOW_ALL_UNTRACKED_FILES
 };
index 3162f517434353609e1fa45ab67ea3cf4623f8a3..16ed8ac492856f141165296d523a319dfd1b4108 100644 (file)
@@ -305,6 +305,22 @@ int xdiff_compare_lines(const char *l1, long s1,
        return xdl_recmatch(l1, s1, l2, s2, flags);
 }
 
+int parse_conflict_style_name(const char *value)
+{
+       if (!strcmp(value, "diff3"))
+               return XDL_MERGE_DIFF3;
+       else if (!strcmp(value, "zdiff3"))
+               return XDL_MERGE_ZEALOUS_DIFF3;
+       else if (!strcmp(value, "merge"))
+               return 0;
+       /*
+        * Please update _git_checkout() in git-completion.bash when
+        * you add new merge config
+        */
+       else
+               return -1;
+}
+
 int git_xmerge_style = -1;
 
 int git_xmerge_config(const char *var, const char *value,
@@ -313,17 +329,8 @@ int git_xmerge_config(const char *var, const char *value,
        if (!strcmp(var, "merge.conflictstyle")) {
                if (!value)
                        return config_error_nonbool(var);
-               if (!strcmp(value, "diff3"))
-                       git_xmerge_style = XDL_MERGE_DIFF3;
-               else if (!strcmp(value, "zdiff3"))
-                       git_xmerge_style = XDL_MERGE_ZEALOUS_DIFF3;
-               else if (!strcmp(value, "merge"))
-                       git_xmerge_style = 0;
-               /*
-                * Please update _git_checkout() in
-                * git-completion.bash when you add new merge config
-                */
-               else
+               git_xmerge_style = parse_conflict_style_name(value);
+               if (git_xmerge_style == -1)
                        return error(_("unknown style '%s' given for '%s'"),
                                     value, var);
                return 0;
index e6f80df04627ccfe1b3777fe380ef3aa5188769e..38537169b729528daffb42dc33a2be9cd62b7b7c 100644 (file)
@@ -51,6 +51,7 @@ int buffer_is_binary(const char *ptr, unsigned long size);
 void xdiff_set_find_func(xdemitconf_t *xecfg, const char *line, int cflags);
 void xdiff_clear_find_func(xdemitconf_t *xecfg);
 struct config_context;
+int parse_conflict_style_name(const char *value);
 int git_xmerge_config(const char *var, const char *value,
                      const struct config_context *ctx, void *cb);
 extern int git_xmerge_style;