]> git.ipfire.org Git - thirdparty/git.git/commitdiff
Merge branch 'ec/fetch-mark-common-refs-trace2'
authorJunio C Hamano <gitster@pobox.com>
Thu, 5 Dec 2019 20:52:43 +0000 (12:52 -0800)
committerJunio C Hamano <gitster@pobox.com>
Thu, 5 Dec 2019 20:52:44 +0000 (12:52 -0800)
Trace2 annotation.

* ec/fetch-mark-common-refs-trace2:
  fetch: add trace2 instrumentation

295 files changed:
Documentation/CodingGuidelines
Documentation/Makefile
Documentation/MyFirstContribution.txt
Documentation/MyFirstObjectWalk.txt [new file with mode: 0644]
Documentation/RelNotes/1.5.0.txt
Documentation/RelNotes/1.6.5.4.txt
Documentation/RelNotes/1.7.0.2.txt
Documentation/RelNotes/1.7.10.4.txt
Documentation/RelNotes/1.7.12.3.txt
Documentation/RelNotes/1.7.5.3.txt
Documentation/RelNotes/1.8.0.txt
Documentation/RelNotes/1.8.4.1.txt
Documentation/RelNotes/2.1.3.txt
Documentation/RelNotes/2.10.0.txt
Documentation/RelNotes/2.10.2.txt
Documentation/RelNotes/2.11.1.txt
Documentation/RelNotes/2.12.0.txt
Documentation/RelNotes/2.13.0.txt
Documentation/RelNotes/2.13.3.txt
Documentation/RelNotes/2.14.0.txt
Documentation/RelNotes/2.16.0.txt
Documentation/RelNotes/2.16.3.txt
Documentation/RelNotes/2.17.0.txt
Documentation/RelNotes/2.18.0.txt
Documentation/RelNotes/2.19.0.txt
Documentation/RelNotes/2.20.0.txt
Documentation/RelNotes/2.25.0.txt [new file with mode: 0644]
Documentation/RelNotes/2.3.3.txt
Documentation/RelNotes/2.3.7.txt
Documentation/RelNotes/2.4.3.txt
Documentation/RelNotes/2.7.0.txt
Documentation/RelNotes/2.8.0.txt
Documentation/RelNotes/2.9.3.txt
Documentation/config.txt
Documentation/config/add.txt
Documentation/config/format.txt
Documentation/config/tag.txt
Documentation/fetch-options.txt
Documentation/git-am.txt
Documentation/git-bisect-lk2009.txt
Documentation/git-bisect.txt
Documentation/git-bundle.txt
Documentation/git-check-attr.txt
Documentation/git-check-ignore.txt
Documentation/git-commit-graph.txt
Documentation/git-cvsserver.txt
Documentation/git-diff.txt
Documentation/git-fast-export.txt
Documentation/git-filter-branch.txt
Documentation/git-format-patch.txt
Documentation/git-multi-pack-index.txt
Documentation/git-notes.txt
Documentation/git-range-diff.txt
Documentation/git-rebase.txt
Documentation/git-rev-parse.txt
Documentation/git-shortlog.txt
Documentation/git-tag.txt
Documentation/git-update-index.txt
Documentation/git.txt
Documentation/gitattributes.txt
Documentation/gitmodules.txt
Documentation/gitrepository-layout.txt
Documentation/gitsubmodules.txt
Documentation/howto/separating-topic-branches.txt
Documentation/merge-options.txt
Documentation/merge-strategies.txt
Documentation/pretty-formats.txt
Documentation/rev-list-options.txt
Documentation/technical/api-config.txt [deleted file]
Documentation/technical/api-grep.txt [deleted file]
Documentation/technical/api-object-access.txt [deleted file]
Documentation/technical/api-quote.txt [deleted file]
Documentation/technical/api-submodule-config.txt
Documentation/technical/api-trace2.txt
Documentation/technical/api-xdiff-interface.txt [deleted file]
Documentation/technical/commit-graph.txt
Documentation/technical/hash-function-transition.txt
Documentation/technical/index-format.txt
Documentation/technical/pack-protocol.txt
Documentation/technical/partial-clone.txt
Documentation/technical/protocol-v2.txt
Documentation/technical/rerere.txt
GIT-VERSION-GEN
INSTALL
Makefile
RelNotes
add-interactive.c [new file with mode: 0644]
add-interactive.h [new file with mode: 0644]
apply.c
builtin/add.c
builtin/blame.c
builtin/bundle.c
builtin/clone.c
builtin/commit-graph.c
builtin/commit.c
builtin/fetch.c
builtin/fsck.c
builtin/gc.c
builtin/log.c
builtin/multi-pack-index.c
builtin/notes.c
builtin/pack-objects.c
builtin/rebase.c
builtin/repack.c
builtin/rev-parse.c
builtin/stash.c
builtin/update-index.c
builtin/worktree.c
bundle.c
bundle.h
cache.h
command-list.txt
commit-graph.c
commit.c
commit.h
compat/mingw.c
compat/mingw.h
compat/nedmalloc/malloc.c.h
compat/obstack.h
compat/regex/regcomp.c
compat/regex/regex.h
compat/regex/regex_internal.c
compat/regex/regexec.c
compat/vcbuild/find_vs_env.bat
config.h
configure.ac
connected.c
contrib/coccinelle/commit.cocci
contrib/coccinelle/object_id.cocci
contrib/completion/git-completion.bash
contrib/hooks/multimail/git_multimail.py
contrib/hooks/multimail/post-receive.example
contrib/hooks/post-receive-email
contrib/hooks/update-paranoid
contrib/mw-to-git/.perlcriticrc
contrib/mw-to-git/git-remote-mediawiki.perl
contrib/mw-to-git/t/install-wiki/db_install.php
contrib/mw-to-git/t/t9360-mw-to-git-clone.sh
contrib/mw-to-git/t/test-gitmw-lib.sh
contrib/svn-fe/svnrdump_sim.py
convert.c
daemon.c
diff.c
diff.h
dir.c
fetch-pack.c
fsck.c
fsck.h
fsmonitor.c
git-cvsimport.perl
git-legacy-stash.sh
git-send-email.perl
gitweb/gitweb.perl
graph.c
hashmap.c
hashmap.h
help.c
hex.c
http.c
kwset.h
merge-recursive.c
midx.c
midx.h
name-hash.c
pack-objects.c
pack-objects.h
parse-options-cb.c
parse-options.c
path.c
perl/Git.pm
pretty.c
promisor-remote.c
range-diff.h
read-cache.c
refs/refs-internal.h
repository.c
repository.h
rerere.c
send-pack.c
sequencer.c
sequencer.h
server-info.c
sha1dc/sha1.c
sha1dc_git.c
string-list.h
t/README
t/gitweb-lib.sh
t/helper/test-read-graph.c [new file with mode: 0644]
t/helper/test-tool.c
t/helper/test-tool.h
t/lib-rebase.sh
t/oid-info/hash-info
t/perf/bisect_regression
t/perf/p5303-many-packs.sh
t/t0000-basic.sh
t/t0027-auto-crlf.sh
t/t0028-working-tree-encoding.sh
t/t0060-path-utils.sh
t/t0500-progress-display.sh
t/t1050-large.sh
t/t1305-config-include.sh
t/t1400-update-ref.sh
t/t1450-fsck.sh
t/t1500-rev-parse.sh
t/t1512-rev-parse-disambiguation.sh
t/t2400-worktree-add.sh
t/t3060-ls-files-with-tree.sh
t/t3210-pack-refs.sh
t/t3301-notes.sh
t/t3404-rebase-interactive.sh
t/t3429-rebase-edit-todo.sh
t/t3430-rebase-merges.sh
t/t3434-rebase-i18n.sh [new file with mode: 0755]
t/t3434/ISO8859-1.txt [new file with mode: 0644]
t/t3434/eucJP.txt [new file with mode: 0644]
t/t3701-add-interactive.sh
t/t3900-i18n-commit.sh
t/t3903-stash.sh
t/t3905-stash-include-untracked.sh
t/t4010-diff-pathspec.sh
t/t4011-diff-symlink.sh
t/t4014-format-patch.sh
t/t4015-diff-whitespace.sh
t/t4018-diff-funcname.sh
t/t4018/elixir-do-not-pick-end [new file with mode: 0644]
t/t4018/elixir-ex-unit-test [new file with mode: 0644]
t/t4018/elixir-function [new file with mode: 0644]
t/t4018/elixir-macro [new file with mode: 0644]
t/t4018/elixir-module [new file with mode: 0644]
t/t4018/elixir-module-func [new file with mode: 0644]
t/t4018/elixir-nested-module [new file with mode: 0644]
t/t4018/elixir-private-function [new file with mode: 0644]
t/t4018/elixir-protocol [new file with mode: 0644]
t/t4018/elixir-protocol-implementation [new file with mode: 0644]
t/t4027-diff-submodule.sh
t/t4034-diff-words.sh
t/t4038-diff-combined.sh
t/t4039-diff-assume-unchanged.sh
t/t4041-diff-submodule-option.sh
t/t4044-diff-index-unique-abbrev.sh
t/t4045-diff-relative.sh
t/t4048-diff-combined-binary.sh
t/t4057-diff-combined-paths.sh
t/t4100/t-apply-1.patch
t/t4100/t-apply-3.patch
t/t4100/t-apply-5.patch
t/t4100/t-apply-7.patch
t/t4108-apply-threeway.sh
t/t4202-log.sh
t/t4203-mailmap.sh
t/t4205-log-pretty-formats.sh
t/t4213-log-tabexpand.sh
t/t4214-log-graph-octopus.sh
t/t4215-log-skewed-merges.sh [new file with mode: 0755]
t/t5318-commit-graph.sh
t/t5319-multi-pack-index.sh
t/t5324-split-commit-graph.sh
t/t5528-push-default.sh
t/t5535-fetch-push-symref.sh
t/t5545-push-options.sh
t/t5616-partial-clone.sh
t/t5702-protocol-v2.sh
t/t6006-rev-list-format.sh
t/t6016-rev-list-graph-simplify-history.sh
t/t6024-recursive-merge.sh
t/t6036-recursive-corner-cases.sh
t/t6042-merge-rename-corner-cases.sh
t/t6043-merge-rename-directories.sh
t/t6046-merge-skip-unneeded-updates.sh
t/t6102-rev-list-unexpected-objects.sh
t/t7004-tag.sh
t/t7012-skip-worktree-writing.sh
t/t7030-verify-tag.sh
t/t7508-status.sh
t/t7513-interpret-trailers.sh
t/t7519-status-fsmonitor.sh
t/t7519/fsmonitor-watchman
t/t9001-send-email.sh
t/t9301-fast-import-notes.sh
t/t9350-fast-export.sh
t/t9502-gitweb-standalone-parse-output.sh
t/t9809-git-p4-client-view.sh
t/t9902-completion.sh
t/test-lib.sh
tag.c
tar.h
templates/hooks--fsmonitor-watchman.sample
trace2/tr2_sid.c
trace2/tr2_tgt_perf.c
unpack-trees.c
url.c
usage.c
userdiff.c
utf8.c
xdiff-interface.h

index f45db5b72740b692a87a050271f5930743e4c4aa..ed4e443a3cd99f8f86b88951e42509f49b14f638 100644 (file)
@@ -75,7 +75,7 @@ For shell scripts specifically (not exhaustive):
 
  - If you want to find out if a command is available on the user's
    $PATH, you should use 'type <command>', instead of 'which <command>'.
-   The output of 'which' is not machine parseable and its exit code
+   The output of 'which' is not machine parsable and its exit code
    is not reliable across platforms.
 
  - We use POSIX compliant parameter substitutions and avoid bashisms;
@@ -203,7 +203,7 @@ For C programs:
    . since early 2012 with e1327023ea, we have been using an enum
      definition whose last element is followed by a comma.  This, like
      an array initializer that ends with a trailing comma, can be used
-     to reduce the patch noise when adding a new identifer at the end.
+     to reduce the patch noise when adding a new identifier at the end.
 
    . since mid 2017 with cbc0f81d, we have been using designated
      initializers for struct (e.g. "struct t v = { .val = 'a' };").
index 06d85ad9588c93675f146bd398c415a85fe71d62..8fe829cc1b83945f25ae59b427d36d11e024091f 100644 (file)
@@ -77,6 +77,7 @@ API_DOCS = $(patsubst %.txt,%,$(filter-out technical/api-index-skel.txt technica
 SP_ARTICLES += $(API_DOCS)
 
 TECH_DOCS += MyFirstContribution
+TECH_DOCS += MyFirstObjectWalk
 TECH_DOCS += SubmittingPatches
 TECH_DOCS += technical/hash-function-transition
 TECH_DOCS += technical/http-protocol
index 5e9b808f5f093dcfc6e2d081a230e835abbd30cb..b55837e646c94e4b5d28026add6bdb276640c713 100644 (file)
@@ -38,6 +38,26 @@ $ git clone https://github.com/git/git git
 $ cd git
 ----
 
+[[dependencies]]
+=== Installing Dependencies
+
+To build Git from source, you need to have a handful of dependencies installed
+on your system. For a hint of what's needed, you can take a look at
+`INSTALL`, paying close attention to the section about Git's dependencies on
+external programs and libraries. That document mentions a way to "test-drive"
+our freshly built Git without installing; that's the method we'll be using in
+this tutorial.
+
+Make sure that your environment has everything you need by building your brand
+new clone of Git from the above step:
+
+----
+$ make
+----
+
+NOTE: The Git build is parallelizable. `-j#` is not included above but you can
+use it as you prefer, here and elsewhere.
+
 [[identify-problem]]
 === Identify Problem to Solve
 
@@ -138,9 +158,6 @@ NOTE: When you are developing the Git project, it's preferred that you use the
 `DEVELOPER` flag; if there's some reason it doesn't work for you, you can turn
 it off, but it's a good idea to mention the problem to the mailing list.
 
-NOTE: The Git build is parallelizable. `-j#` is not included above but you can
-use it as you prefer, here and elsewhere.
-
 Great, now your new command builds happily on its own. But nobody invokes it.
 Let's change that.
 
@@ -534,6 +551,28 @@ you want to pass as a parameter something which would usually be interpreted as
 a flag.) `parse_options()` will terminate parsing when it reaches `--` and give
 you the rest of the options afterwards, untouched.
 
+Now that you have a usage hint, you can teach Git how to show it in the general
+command list shown by `git help git` or `git help -a`, which is generated from
+`command-list.txt`. Find the line for 'git-pull' so you can add your 'git-psuh'
+line above it in alphabetical order. Now, we can add some attributes about the
+command which impacts where it shows up in the aforementioned help commands. The
+top of `command-list.txt` shares some information about what each attribute
+means; in those help pages, the commands are sorted according to these
+attributes. `git psuh` is user-facing, or porcelain - so we will mark it as
+"mainporcelain". For "mainporcelain" commands, the comments at the top of
+`command-list.txt` indicate we can also optionally add an attribute from another
+list; since `git psuh` shows some information about the user's workspace but
+doesn't modify anything, let's mark it as "info". Make sure to keep your
+attributes in the same style as the rest of `command-list.txt` using spaces to
+align and delineate them:
+
+----
+git-prune-packed                        plumbingmanipulators
+git-psuh                                mainporcelain          info
+git-pull                                mainporcelain           remote
+git-push                                mainporcelain           remote
+----
+
 Build again. Now, when you run with `-h`, you should see your usage printed and
 your command terminated before anything else interesting happens. Great!
 
@@ -746,6 +785,14 @@ will automatically run your PRs through the CI even without the permission given
 but you will not be able to `/submit` your changes until someone allows you to
 use the tool.
 
+NOTE: You can typically find someone who can `/allow` you on GitGitGadget by
+either examining recent pull requests where someone has been granted `/allow`
+(https://github.com/gitgitgadget/git/pulls?utf8=%E2%9C%93&q=is%3Apr+is%3Aopen+%22%2Fallow%22[Search:
+is:pr is:open "/allow"]), in which case both the author and the person who
+granted the `/allow` can now `/allow` you, or by inquiring on the
+https://webchat.freenode.net/#git-devel[#git-devel] IRC channel on Freenode
+linking your pull request and asking for someone to `/allow` you.
+
 If the CI fails, you can update your changes with `git rebase -i` and push your
 branch again:
 
diff --git a/Documentation/MyFirstObjectWalk.txt b/Documentation/MyFirstObjectWalk.txt
new file mode 100644 (file)
index 0000000..4d24dae
--- /dev/null
@@ -0,0 +1,906 @@
+= My First Object Walk
+
+== What's an Object Walk?
+
+The object walk is a key concept in Git - this is the process that underpins
+operations like object transfer and fsck. Beginning from a given commit, the
+list of objects is found by walking parent relationships between commits (commit
+X based on commit W) and containment relationships between objects (tree Y is
+contained within commit X, and blob Z is located within tree Y, giving our
+working tree for commit X something like `y/z.txt`).
+
+A related concept is the revision walk, which is focused on commit objects and
+their parent relationships and does not delve into other object types. The
+revision walk is used for operations like `git log`.
+
+=== Related Reading
+
+- `Documentation/user-manual.txt` under "Hacking Git" contains some coverage of
+  the revision walker in its various incarnations.
+- `Documentation/technical/api-revision-walking.txt`
+- https://eagain.net/articles/git-for-computer-scientists/[Git for Computer Scientists]
+  gives a good overview of the types of objects in Git and what your object
+  walk is really describing.
+
+== Setting Up
+
+Create a new branch from `master`.
+
+----
+git checkout -b revwalk origin/master
+----
+
+We'll put our fiddling into a new command. For fun, let's name it `git walken`.
+Open up a new file `builtin/walken.c` and set up the command handler:
+
+----
+/*
+ * "git walken"
+ *
+ * Part of the "My First Object Walk" tutorial.
+ */
+
+#include "builtin.h"
+
+int cmd_walken(int argc, const char **argv, const char *prefix)
+{
+       trace_printf(_("cmd_walken incoming...\n"));
+       return 0;
+}
+----
+
+NOTE: `trace_printf()` differs from `printf()` in that it can be turned on or
+off at runtime. For the purposes of this tutorial, we will write `walken` as
+though it is intended for use as a "plumbing" command: that is, a command which
+is used primarily in scripts, rather than interactively by humans (a "porcelain"
+command). So we will send our debug output to `trace_printf()` instead. When
+running, enable trace output by setting the environment variable `GIT_TRACE`.
+
+Add usage text and `-h` handling, like all subcommands should consistently do
+(our test suite will notice and complain if you fail to do so).
+
+----
+int cmd_walken(int argc, const char **argv, const char *prefix)
+{
+       const char * const walken_usage[] = {
+               N_("git walken"),
+               NULL,
+       }
+       struct option options[] = {
+               OPT_END()
+       };
+
+       argc = parse_options(argc, argv, prefix, options, walken_usage, 0);
+
+       ...
+}
+----
+
+Also add the relevant line in `builtin.h` near `cmd_whatchanged()`:
+
+----
+int cmd_walken(int argc, const char **argv, const char *prefix);
+----
+
+Include the command in `git.c` in `commands[]` near the entry for `whatchanged`,
+maintaining alphabetical ordering:
+
+----
+{ "walken", cmd_walken, RUN_SETUP },
+----
+
+Add it to the `Makefile` near the line for `builtin/worktree.o`:
+
+----
+BUILTIN_OBJS += builtin/walken.o
+----
+
+Build and test out your command, without forgetting to ensure the `DEVELOPER`
+flag is set, and with `GIT_TRACE` enabled so the debug output can be seen:
+
+----
+$ echo DEVELOPER=1 >>config.mak
+$ make
+$ GIT_TRACE=1 ./bin-wrappers/git walken
+----
+
+NOTE: For a more exhaustive overview of the new command process, take a look at
+`Documentation/MyFirstContribution.txt`.
+
+NOTE: A reference implementation can be found at
+https://github.com/nasamuffin/git/tree/revwalk.
+
+=== `struct rev_cmdline_info`
+
+The definition of `struct rev_cmdline_info` can be found in `revision.h`.
+
+This struct is contained within the `rev_info` struct and is used to reflect
+parameters provided by the user over the CLI.
+
+`nr` represents the number of `rev_cmdline_entry` present in the array.
+
+`alloc` is used by the `ALLOC_GROW` macro. Check
+`Documentation/technical/api-allocation-growing.txt` - this variable is used to
+track the allocated size of the list.
+
+Per entry, we find:
+
+`item` is the object provided upon which to base the object walk. Items in Git
+can be blobs, trees, commits, or tags. (See `Documentation/gittutorial-2.txt`.)
+
+`name` is the object ID (OID) of the object - a hex string you may be familiar
+with from using Git to organize your source in the past. Check the tutorial
+mentioned above towards the top for a discussion of where the OID can come
+from.
+
+`whence` indicates some information about what to do with the parents of the
+specified object. We'll explore this flag more later on; take a look at
+`Documentation/revisions.txt` to get an idea of what could set the `whence`
+value.
+
+`flags` are used to hint the beginning of the revision walk and are the first
+block under the `#include`s in `revision.h`. The most likely ones to be set in
+the `rev_cmdline_info` are `UNINTERESTING` and `BOTTOM`, but these same flags
+can be used during the walk, as well.
+
+=== `struct rev_info`
+
+This one is quite a bit longer, and many fields are only used during the walk
+by `revision.c` - not configuration options. Most of the configurable flags in
+`struct rev_info` have a mirror in `Documentation/rev-list-options.txt`. It's a
+good idea to take some time and read through that document.
+
+== Basic Commit Walk
+
+First, let's see if we can replicate the output of `git log --oneline`. We'll
+refer back to the implementation frequently to discover norms when performing
+an object walk of our own.
+
+To do so, we'll first find all the commits, in order, which preceded the current
+commit. We'll extract the name and subject of the commit from each.
+
+Ideally, we will also be able to find out which ones are currently at the tip of
+various branches.
+
+=== Setting Up
+
+Preparing for your object walk has some distinct stages.
+
+1. Perform default setup for this mode, and others which may be invoked.
+2. Check configuration files for relevant settings.
+3. Set up the `rev_info` struct.
+4. Tweak the initialized `rev_info` to suit the current walk.
+5. Prepare the `rev_info` for the walk.
+6. Iterate over the objects, processing each one.
+
+==== Default Setups
+
+Before examining configuration files which may modify command behavior, set up
+default state for switches or options your command may have. If your command
+utilizes other Git components, ask them to set up their default states as well.
+For instance, `git log` takes advantage of `grep` and `diff` functionality, so
+its `init_log_defaults()` sets its own state (`decoration_style`) and asks
+`grep` and `diff` to initialize themselves by calling each of their
+initialization functions.
+
+For our first example within `git walken`, we don't intend to use any other
+components within Git, and we don't have any configuration to do.  However, we
+may want to add some later, so for now, we can add an empty placeholder. Create
+a new function in `builtin/walken.c`:
+
+----
+static void init_walken_defaults(void)
+{
+       /*
+        * We don't actually need the same components `git log` does; leave this
+        * empty for now.
+        */
+}
+----
+
+Make sure to add a line invoking it inside of `cmd_walken()`.
+
+----
+int cmd_walken(int argc, const char **argv, const char *prefix)
+{
+       init_walken_defaults();
+}
+----
+
+==== Configuring From `.gitconfig`
+
+Next, we should have a look at any relevant configuration settings (i.e.,
+settings readable and settable from `git config`). This is done by providing a
+callback to `git_config()`; within that callback, you can also invoke methods
+from other components you may need that need to intercept these options. Your
+callback will be invoked once per each configuration value which Git knows about
+(global, local, worktree, etc.).
+
+Similarly to the default values, we don't have anything to do here yet
+ourselves; however, we should call `git_default_config()` if we aren't calling
+any other existing config callbacks.
+
+Add a new function to `builtin/walken.c`:
+
+----
+static int git_walken_config(const char *var, const char *value, 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);
+}
+----
+
+Make sure to invoke `git_config()` with it in your `cmd_walken()`:
+
+----
+int cmd_walken(int argc, const char **argv, const char *prefix)
+{
+       ...
+
+       git_config(git_walken_config, NULL);
+
+       ...
+}
+----
+
+==== Setting Up `rev_info`
+
+Now that we've gathered external configuration and options, it's time to
+initialize the `rev_info` object which we will use to perform the walk. This is
+typically done by calling `repo_init_revisions()` with the repository you intend
+to target, as well as the `prefix` argument of `cmd_walken` and your `rev_info`
+struct.
+
+Add the `struct rev_info` and the `repo_init_revisions()` call:
+----
+int cmd_walken(int argc, const char **argv, const char *prefix)
+{
+       /* This can go wherever you like in your declarations.*/
+       struct rev_info rev;
+       ...
+
+       /* This should go after the git_config() call. */
+       repo_init_revisions(the_repository, &rev, prefix);
+
+       ...
+}
+----
+
+==== Tweaking `rev_info` For the Walk
+
+We're getting close, but we're still not quite ready to go. Now that `rev` is
+initialized, we can modify it to fit our needs. This is usually done within a
+helper for clarity, so let's add one:
+
+----
+static void final_rev_info_setup(struct rev_info *rev)
+{
+       /*
+        * We want to mimic the appearance of `git log --oneline`, so let's
+        * force oneline format.
+        */
+       get_commit_format("oneline", rev);
+
+       /* Start our object walk at HEAD. */
+       add_head_to_pending(rev);
+}
+----
+
+[NOTE]
+====
+Instead of using the shorthand `add_head_to_pending()`, you could do
+something like this:
+----
+       struct setup_revision_opt opt;
+
+       memset(&opt, 0, sizeof(opt));
+       opt.def = "HEAD";
+       opt.revarg_opt = REVARG_COMMITTISH;
+       setup_revisions(argc, argv, rev, &opt);
+----
+Using a `setup_revision_opt` gives you finer control over your walk's starting
+point.
+====
+
+Then let's invoke `final_rev_info_setup()` after the call to
+`repo_init_revisions()`:
+
+----
+int cmd_walken(int argc, const char **argv, const char *prefix)
+{
+       ...
+
+       final_rev_info_setup(&rev);
+
+       ...
+}
+----
+
+Later, we may wish to add more arguments to `final_rev_info_setup()`. But for
+now, this is all we need.
+
+==== Preparing `rev_info` For the Walk
+
+Now that `rev` is all initialized and configured, we've got one more setup step
+before we get rolling. We can do this in a helper, which will both prepare the
+`rev_info` for the walk, and perform the walk itself. Let's start the helper
+with the call to `prepare_revision_walk()`, which can return an error without
+dying on its own:
+
+----
+static void walken_commit_walk(struct rev_info *rev)
+{
+       if (prepare_revision_walk(rev))
+               die(_("revision walk setup failed"));
+}
+----
+
+NOTE: `die()` prints to `stderr` and exits the program. Since it will print to
+`stderr` it's likely to be seen by a human, so we will localize it.
+
+==== Performing the Walk!
+
+Finally! We are ready to begin the walk itself. Now we can see that `rev_info`
+can also be used as an iterator; we move to the next item in the walk by using
+`get_revision()` repeatedly. Add the listed variable declarations at the top and
+the walk loop below the `prepare_revision_walk()` call within your
+`walken_commit_walk()`:
+
+----
+static void walken_commit_walk(struct rev_info *rev)
+{
+       struct commit *commit;
+       struct strbuf prettybuf = STRBUF_INIT;
+
+       ...
+
+       while ((commit = get_revision(rev))) {
+               if (!commit)
+                       continue;
+
+               strbuf_reset(&prettybuf);
+               pp_commit_easy(CMIT_FMT_ONELINE, commit, &prettybuf);
+               puts(prettybuf.buf);
+       }
+       strbuf_release(&prettybuf);
+}
+----
+
+NOTE: `puts()` prints a `char*` to `stdout`. Since this is the part of the
+command we expect to be machine-parsed, we're sending it directly to stdout.
+
+Give it a shot.
+
+----
+$ make
+$ ./bin-wrappers/git walken
+----
+
+You should see all of the subject lines of all the commits in
+your tree's history, in order, ending with the initial commit, "Initial revision
+of "git", the information manager from hell". Congratulations! You've written
+your first revision walk. You can play with printing some additional fields
+from each commit if you're curious; have a look at the functions available in
+`commit.h`.
+
+=== Adding a Filter
+
+Next, let's try to filter the commits we see based on their author. This is
+equivalent to running `git log --author=<pattern>`. We can add a filter by
+modifying `rev_info.grep_filter`, which is a `struct grep_opt`.
+
+First some setup. Add `init_grep_defaults()` to `init_walken_defaults()` and add
+`grep_config()` to `git_walken_config()`:
+
+----
+static void init_walken_defaults(void)
+{
+       init_grep_defaults(the_repository);
+}
+
+...
+
+static int git_walken_config(const char *var, const char *value, void *cb)
+{
+       grep_config(var, value, cb);
+       return git_default_config(var, value, cb);
+}
+----
+
+Next, we can modify the `grep_filter`. This is done with convenience functions
+found in `grep.h`. For fun, we're filtering to only commits from folks using a
+`gmail.com` email address - a not-very-precise guess at who may be working on
+Git as a hobby. Since we're checking the author, which is a specific line in the
+header, we'll use the `append_header_grep_pattern()` helper. We can use
+the `enum grep_header_field` to indicate which part of the commit header we want
+to search.
+
+In `final_rev_info_setup()`, add your filter line:
+
+----
+static void final_rev_info_setup(int argc, const char **argv,
+               const char *prefix, struct rev_info *rev)
+{
+       ...
+
+       append_header_grep_pattern(&rev->grep_filter, GREP_HEADER_AUTHOR,
+               "gmail");
+       compile_grep_patterns(&rev->grep_filter);
+
+       ...
+}
+----
+
+`append_header_grep_pattern()` adds your new "gmail" pattern to `rev_info`, but
+it won't work unless we compile it with `compile_grep_patterns()`.
+
+NOTE: If you are using `setup_revisions()` (for example, if you are passing a
+`setup_revision_opt` instead of using `add_head_to_pending()`), you don't need
+to call `compile_grep_patterns()` because `setup_revisions()` calls it for you.
+
+NOTE: We could add the same filter via the `append_grep_pattern()` helper if we
+wanted to, but `append_header_grep_pattern()` adds the `enum grep_context` and
+`enum grep_pat_token` for us.
+
+=== Changing the Order
+
+There are a few ways that we can change the order of the commits during a
+revision walk. Firstly, we can use the `enum rev_sort_order` to choose from some
+typical orderings.
+
+`topo_order` is the same as `git log --topo-order`: we avoid showing a parent
+before all of its children have been shown, and we avoid mixing commits which
+are in different lines of history. (`git help log`'s section on `--topo-order`
+has a very nice diagram to illustrate this.)
+
+Let's see what happens when we run with `REV_SORT_BY_COMMIT_DATE` as opposed to
+`REV_SORT_BY_AUTHOR_DATE`. Add the following:
+
+----
+static void final_rev_info_setup(int argc, const char **argv,
+               const char *prefix, struct rev_info *rev)
+{
+       ...
+
+       rev->topo_order = 1;
+       rev->sort_order = REV_SORT_BY_COMMIT_DATE;
+
+       ...
+}
+----
+
+Let's output this into a file so we can easily diff it with the walk sorted by
+author date.
+
+----
+$ make
+$ ./bin-wrappers/git walken > commit-date.txt
+----
+
+Then, let's sort by author date and run it again.
+
+----
+static void final_rev_info_setup(int argc, const char **argv,
+               const char *prefix, struct rev_info *rev)
+{
+       ...
+
+       rev->topo_order = 1;
+       rev->sort_order = REV_SORT_BY_AUTHOR_DATE;
+
+       ...
+}
+----
+
+----
+$ make
+$ ./bin-wrappers/git walken > author-date.txt
+----
+
+Finally, compare the two. This is a little less helpful without object names or
+dates, but hopefully we get the idea.
+
+----
+$ diff -u commit-date.txt author-date.txt
+----
+
+This display indicates that commits can be reordered after they're written, for
+example with `git rebase`.
+
+Let's try one more reordering of commits. `rev_info` exposes a `reverse` flag.
+Set that flag somewhere inside of `final_rev_info_setup()`:
+
+----
+static void final_rev_info_setup(int argc, const char **argv, const char *prefix,
+               struct rev_info *rev)
+{
+       ...
+
+       rev->reverse = 1;
+
+       ...
+}
+----
+
+Run your walk again and note the difference in order. (If you remove the grep
+pattern, you should see the last commit this call gives you as your current
+HEAD.)
+
+== Basic Object Walk
+
+So far we've been walking only commits. But Git has more types of objects than
+that! Let's see if we can walk _all_ objects, and find out some information
+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
+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
+the name, these functions traverse all kinds of objects. Let's have a look at
+the arguments to `traverse_commit_list_filtered()`, which are a superset of the
+arguments to the unfiltered version.
+
+- `struct list_objects_filter_options *filter_options`: This is a struct which
+  stores a filter-spec as outlined in `Documentation/rev-list-options.txt`.
+- `struct rev_info *revs`: This is the `rev_info` used for the walk.
+- `show_commit_fn show_commit`: A callback which will be used to handle each
+  individual commit object.
+- `show_object_fn show_object`: A callback which will be used to handle each
+  non-commit object (so each blob, tree, or tag).
+- `void *show_data`: A context buffer which is passed in turn to `show_commit`
+  and `show_object`.
+- `struct oidset *omitted`: A linked-list of object IDs which the provided
+  filter caused to be omitted.
+
+It looks like this `traverse_commit_list_filtered()` uses callbacks we provide
+instead of needing us to call it repeatedly ourselves. Cool! Let's add the
+callbacks first.
+
+For the sake of this tutorial, we'll simply keep track of how many of each kind
+of object we find. At file scope in `builtin/walken.c` add the following
+tracking variables:
+
+----
+static int commit_count;
+static int tag_count;
+static int blob_count;
+static int tree_count;
+----
+
+Commits are handled by a different callback than other objects; let's do that
+one first:
+
+----
+static void walken_show_commit(struct commit *cmt, void *buf)
+{
+       commit_count++;
+}
+----
+
+The `cmt` argument is fairly self-explanatory. But it's worth mentioning that
+the `buf` argument is actually the context buffer that we can provide to the
+traversal calls - `show_data`, which we mentioned a moment ago.
+
+Since we have the `struct commit` object, we can look at all the same parts that
+we looked at in our earlier commit-only walk. For the sake of this tutorial,
+though, we'll just increment the commit counter and move on.
+
+The callback for non-commits is a little different, as we'll need to check
+which kind of object we're dealing with:
+
+----
+static void walken_show_object(struct object *obj, const char *str, void *buf)
+{
+       switch (obj->type) {
+       case OBJ_TREE:
+               tree_count++;
+               break;
+       case OBJ_BLOB:
+               blob_count++;
+               break;
+       case OBJ_TAG:
+               tag_count++;
+               break;
+       case OBJ_COMMIT:
+               BUG("unexpected commit object in walken_show_object\n");
+       default:
+               BUG("unexpected object type %s in walken_show_object\n",
+                       type_name(obj->type));
+       }
+}
+----
+
+Again, `obj` is fairly self-explanatory, and we can guess that `buf` is the same
+context pointer that `walken_show_commit()` receives: the `show_data` argument
+to `traverse_commit_list()` and `traverse_commit_list_filtered()`. Finally,
+`str` contains the name of the object, which ends up being something like
+`foo.txt` (blob), `bar/baz` (tree), or `v1.2.3` (tag).
+
+To help assure us that we aren't double-counting commits, we'll include some
+complaining if a commit object is routed through our non-commit callback; we'll
+also complain if we see an invalid object type. Since those two cases should be
+unreachable, and would only change in the event of a semantic change to the Git
+codebase, we complain by using `BUG()` - which is a signal to a developer that
+the change they made caused unintended consequences, and the rest of the
+codebase needs to be updated to understand that change. `BUG()` is not intended
+to be seen by the public, so it is not localized.
+
+Our main object walk implementation is substantially different from our commit
+walk implementation, so let's make a new function to perform the object walk. We
+can perform setup which is applicable to all objects here, too, to keep separate
+from setup which is applicable to commit-only walks.
+
+We'll start by enabling all types of objects in the `struct rev_info`.  We'll
+also turn on `tree_blobs_in_commit_order`, which means that we will walk a
+commit's tree and everything it points to immediately after we find each commit,
+as opposed to waiting for the end and walking through all trees after the commit
+history has been discovered. With the appropriate settings configured, we are
+ready to call `prepare_revision_walk()`.
+
+----
+static void walken_object_walk(struct rev_info *rev)
+{
+       rev->tree_objects = 1;
+       rev->blob_objects = 1;
+       rev->tag_objects = 1;
+       rev->tree_blobs_in_commit_order = 1;
+
+       if (prepare_revision_walk(rev))
+               die(_("revision walk setup failed"));
+
+       commit_count = 0;
+       tag_count = 0;
+       blob_count = 0;
+       tree_count = 0;
+----
+
+Let's start by calling just the unfiltered walk and reporting our counts.
+Complete your implementation of `walken_object_walk()`:
+
+----
+       traverse_commit_list(rev, walken_show_commit, walken_show_object, NULL);
+
+       printf("commits %d\nblobs %d\ntags %d\ntrees %d\n", commit_count,
+               blob_count, tag_count, tree_count);
+}
+----
+
+NOTE: This output is intended to be machine-parsed. Therefore, we are not
+sending it to `trace_printf()`, and we are not localizing it - we need scripts
+to be able to count on the formatting to be exactly the way it is shown here.
+If we were intending this output to be read by humans, we would need to localize
+it with `_()`.
+
+Finally, we'll ask `cmd_walken()` to use the object walk instead. Discussing
+command line options is out of scope for this tutorial, so we'll just hardcode
+a branch we can change at compile time. Where you call `final_rev_info_setup()`
+and `walken_commit_walk()`, instead branch like so:
+
+----
+       if (1) {
+               add_head_to_pending(&rev);
+               walken_object_walk(&rev);
+       } else {
+               final_rev_info_setup(argc, argv, prefix, &rev);
+               walken_commit_walk(&rev);
+       }
+----
+
+NOTE: For simplicity, we've avoided all the filters and sorts we applied in
+`final_rev_info_setup()` and simply added `HEAD` to our pending queue. If you
+want, you can certainly use the filters we added before by moving
+`final_rev_info_setup()` out of the conditional and removing the call to
+`add_head_to_pending()`.
+
+Now we can try to run our command! It should take noticeably longer than the
+commit walk, but an examination of the output will give you an idea why. Your
+output should look similar to this example, but with different counts:
+
+----
+Object walk completed. Found 55733 commits, 100274 blobs, 0 tags, and 104210 trees.
+----
+
+This makes sense. We have more trees than commits because the Git project has
+lots of subdirectories which can change, plus at least one tree per commit. We
+have no tags because we started on a commit (`HEAD`) and while tags can point to
+commits, commits can't point to tags.
+
+NOTE: You will have different counts when you run this yourself! The number of
+objects grows along with the Git project.
+
+=== Adding a Filter
+
+There are a handful of filters that we can apply to the object walk laid out in
+`Documentation/rev-list-options.txt`. These filters are typically useful for
+operations such as creating packfiles or performing a partial clone. They are
+defined in `list-objects-filter-options.h`. For the purposes of this tutorial we
+will use the "tree:1" filter, which causes the walk to omit all trees and blobs
+which are not directly referenced by commits reachable from the commit in
+`pending` when the walk begins. (`pending` is the list of objects which need to
+be traversed during a walk; you can imagine a breadth-first tree traversal to
+help understand. In our case, that means we omit trees and blobs not directly
+referenced by `HEAD` or `HEAD`'s history, because we begin the walk with only
+`HEAD` in the `pending` list.)
+
+First, we'll need to `#include "list-objects-filter-options.h`" and set up the
+`struct list_objects_filter_options` at the top of the function.
+
+----
+static void walken_object_walk(struct rev_info *rev)
+{
+       struct list_objects_filter_options filter_options = {};
+
+       ...
+----
+
+For now, we are not going to track the omitted objects, so we'll replace those
+parameters with `NULL`. For the sake of simplicity, we'll add a simple
+build-time branch to use our filter or not. Replace the line calling
+`traverse_commit_list()` with the following, which will remind us which kind of
+walk we've just performed:
+
+----
+       if (0) {
+               /* Unfiltered: */
+               trace_printf(_("Unfiltered object walk.\n"));
+               traverse_commit_list(rev, walken_show_commit,
+                               walken_show_object, NULL);
+       } else {
+               trace_printf(
+                       _("Filtered object walk with filterspec 'tree:1'.\n"));
+               parse_list_objects_filter(&filter_options, "tree:1");
+
+               traverse_commit_list_filtered(&filter_options, rev,
+                       walken_show_commit, walken_show_object, NULL, NULL);
+       }
+----
+
+`struct list_objects_filter_options` is usually built directly from a command
+line argument, so the module provides an easy way to build one from a string.
+Even though we aren't taking user input right now, we can still build one with
+a hardcoded string using `parse_list_objects_filter()`.
+
+With the filter spec "tree:1", we are expecting to see _only_ the root tree for
+each commit; therefore, the tree object count should be less than or equal to
+the number of commits. (For an example of why that's true: `git commit --revert`
+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.
+
+First, add the `struct oidset` and related items we will use to iterate it:
+
+----
+static void walken_object_walk(
+       ...
+
+       struct oidset omitted;
+       struct oidset_iter oit;
+       struct object_id *oid = NULL;
+       int omitted_count = 0;
+       oidset_init(&omitted, 0);
+
+       ...
+----
+
+Modify the call to `traverse_commit_list_filtered()` to include your `omitted`
+object:
+
+----
+       ...
+
+               traverse_commit_list_filtered(&filter_options, rev,
+                       walken_show_commit, walken_show_object, NULL, &omitted);
+
+       ...
+----
+
+Then, after your traversal, the `oidset` traversal is pretty straightforward.
+Count all the objects within and modify the print statement:
+
+----
+       /* Count the omitted objects. */
+       oidset_iter_init(&omitted, &oit);
+
+       while ((oid = oidset_iter_next(&oit)))
+               omitted_count++;
+
+       printf("commits %d\nblobs %d\ntags %d\ntrees%d\nomitted %d\n",
+               commit_count, blob_count, tag_count, tree_count, omitted_count);
+----
+
+By running your walk with and without the filter, you should find that the total
+object count in each case is identical. You can also time each invocation of
+the `walken` subcommand, with and without `omitted` being passed in, to confirm
+to yourself the runtime impact of tracking all omitted objects.
+
+=== Changing the Order
+
+Finally, let's demonstrate that you can also reorder walks of all objects, not
+just walks of commits. First, we'll make our handlers chattier - modify
+`walken_show_commit()` and `walken_show_object()` to print the object as they
+go:
+
+----
+static void walken_show_commit(struct commit *cmt, void *buf)
+{
+       trace_printf("commit: %s\n", oid_to_hex(&cmt->object.oid));
+       commit_count++;
+}
+
+static void walken_show_object(struct object *obj, const char *str, void *buf)
+{
+       trace_printf("%s: %s\n", type_name(obj->type), oid_to_hex(&obj->oid));
+
+       ...
+}
+----
+
+NOTE: Since we will be examining this output directly as humans, we'll use
+`trace_printf()` here. Additionally, since this change introduces a significant
+number of printed lines, using `trace_printf()` will allow us to easily silence
+those lines without having to recompile.
+
+(Leave the counter increment logic in place.)
+
+With only that change, run again (but save yourself some scrollback):
+
+----
+$ GIT_TRACE=1 ./bin-wrappers/git walken | head -n 10
+----
+
+Take a look at the top commit with `git show` and the object ID you printed; it
+should be the same as the output of `git show HEAD`.
+
+Next, let's change a setting on our `struct rev_info` within
+`walken_object_walk()`. Find where you're changing the other settings on `rev`,
+such as `rev->tree_objects` and `rev->tree_blobs_in_commit_order`, and add the
+`reverse` setting at the bottom:
+
+----
+       ...
+
+       rev->tree_objects = 1;
+       rev->blob_objects = 1;
+       rev->tag_objects = 1;
+       rev->tree_blobs_in_commit_order = 1;
+       rev->reverse = 1;
+
+       ...
+----
+
+Now, run again, but this time, let's grab the last handful of objects instead
+of the first handful:
+
+----
+$ make
+$ GIT_TRACE=1 ./bin-wrappers git walken | tail -n 10
+----
+
+The last commit object given should have the same OID as the one we saw at the
+top before, and running `git show <oid>` with that OID should give you again
+the same results as `git show HEAD`. Furthermore, if you run and examine the
+first ten lines again (with `head` instead of `tail` like we did before applying
+the `reverse` setting), you should see that now the first commit printed is the
+initial commit, `e83c5163`.
+
+== Wrapping Up
+
+Let's review. In this tutorial, we:
+
+- Built a commit walk from the ground up
+- Enabled a grep filter for that commit walk
+- Changed the sort order of that filtered commit walk
+- Built an object walk (tags, commits, trees, and blobs) from the ground up
+- Learned how to add a filter-spec to an object walk
+- Changed the display order of the filtered object walk
index daf4bdb0d7bb24319810fe0e73aa317663448c93..d6d42f31835889084b2ca6b7fbd5aa651d0b2055 100644 (file)
@@ -251,7 +251,7 @@ Updates in v1.5.0 since v1.4.4 series
    the repository when that happens.
 
 
-* Crufts removal
+* Cruft removal
 
  - We used to say "old commits are retrievable using reflog and
    'master@{yesterday}' syntax as long as you haven't run
@@ -379,7 +379,7 @@ Updates in v1.5.0 since v1.4.4 series
  - The value of i18n.commitencoding in the originating
    repository is recorded in the commit object on the "encoding"
    header, if it is not UTF-8.  git-log and friends notice this,
-   and reencodes the message to the log output encoding when
+   and re-encodes the message to the log output encoding when
    displaying, if they are different.  The log output encoding
    is determined by "git log --encoding=<encoding>",
    i18n.logoutputencoding configuration, or i18n.commitencoding
index d3a2a3e71243dc4612050d28a8cc779f34ab51ae..344333de66e59bb64c9031d7af8599cde1dad36d 100644 (file)
@@ -10,7 +10,7 @@ Fixes since v1.6.5.3
 
  * "git prune-packed" gave progress output even when its standard error is
    not connected to a terminal; this caused cron jobs that run it to
-   produce crufts.
+   produce cruft.
 
  * "git pack-objects --all-progress" is an option to ask progress output
    from write-object phase _if_ progress output were to be produced, and
index fcb46ca6a4ec321fd35093fa22858c2ff30dbbd6..73ed2b527843e7c2eb0a92251a849279732395a4 100644 (file)
@@ -34,7 +34,7 @@ Fixes since v1.7.0.1
  * "git status" in 1.7.0 lacked the optimization we used to have in 1.6.X series
    to speed up scanning of large working tree.
 
- * "gitweb" did not diagnose parsing errors properly while reading tis configuration
+ * "gitweb" did not diagnose parsing errors properly while reading its configuration
    file.
 
 And other minor fixes and documentation updates.
index 326670df6e6b87f6e9832d6c30883510b5515959..57597f2bf35d94f103655c80c7b09ff642737c37 100644 (file)
@@ -7,7 +7,7 @@ Fixes since v1.7.10.3
  * The message file for Swedish translation has been updated a bit.
 
  * A name taken from mailmap was copied into an internal buffer
-   incorrectly and could overun the buffer if it is too long.
+   incorrectly and could overrun the buffer if it is too long.
 
  * A malformed commit object that has a header line chomped in the
    middle could kill git with a NULL pointer dereference.
index ecda427a35efb4ade682a314f2f05724ea37e67e..4b822976b8adef8385a068927f2e54641a1d44e9 100644 (file)
@@ -25,7 +25,7 @@ Fixes since v1.7.12.2
    its Accept-Encoding header.
 
  * "git receive-pack" (the counterpart to "git push") did not give
-   progress output while processing objects it received to the puser
+   progress output while processing objects it received to the user
    when run over the smart-http protocol.
 
  * "git status" honored the ignore=dirty settings in .gitmodules but
index 9c03353af2e940a9c82bc53e2791f5d00804aed1..1d24edcf2fa6bb528a49ac7083269949708ef326 100644 (file)
@@ -22,7 +22,7 @@ Fixes since v1.7.5.2
  * "git log --stdin path" with an input that has additional pathspec
    used to corrupt memory.
 
- * "git send-pack" (hence "git push") over smalt-HTTP protocol could
+ * "git send-pack" (hence "git push") over smart-HTTP protocol could
    deadlock when the client side pack-object died early.
 
  * Compressed tarball gitweb generates used to be made with the timestamp
index 43883c14f08ac06b8cfa066e971135022837e218..63d6e4afa4df81f0ced6e3728e2b4ebfb3d1c0fc 100644 (file)
@@ -233,7 +233,7 @@ to them for details).
    together, misdetected branches.
 
  * "git receive-pack" (the counterpart to "git push") did not give
-   progress output while processing objects it received to the puser
+   progress output while processing objects it received to the user
    when run over the smart-http protocol.
 
  * When you misspell the command name you give to the "exec" action in
index 96090ef599d34e1bdfe19e56fd85dda41aced65c..c257beb114c9fc8bc09cfa77bac8e71a64db4f38 100644 (file)
@@ -15,7 +15,7 @@ Fixes since v1.8.4
    in 1.8.4-rc1).
 
  * "git rebase -i" and other scripted commands were feeding a
-   random, data dependant error message to 'echo' and expecting it
+   random, data dependent error message to 'echo' and expecting it
    to come out literally.
 
  * Setting the "submodule.<name>.path" variable to the empty
index acc9ebb886ea59cd10405b899e4b38521740f0f6..0dfb17c4fc6fca8fa62010992604c03a967dc951 100644 (file)
@@ -13,7 +13,7 @@ Git v2.1.3 Release Notes
    they are new enough to support the `--output` option.
 
  * "git pack-objects" forgot to disable the codepath to generate
-   object recheability bitmap when it needs to split the resulting
+   object reachability bitmap when it needs to split the resulting
    pack.
 
  * "gitweb" used deprecated CGI::startfrom, which was removed from
index f4da28ab669034e2e047c43cf939af795f1f3ba2..3792b7d03d5f791d05cebf62431132052558575f 100644 (file)
@@ -478,7 +478,7 @@ notes for details).
  * One part of "git am" had an oddball helper function that called
    stuff from outside "his" as opposed to calling what we have "ours",
    which was not gender-neutral and also inconsistent with the rest of
-   the system where outside stuff is usuall called "theirs" in
+   the system where outside stuff is usually called "theirs" in
    contrast to "ours".
 
  * "git blame file" allowed the lineage of lines in the uncommitted,
index c4d4397023de1360b77e7c91dc9316eb16973465..abbd331508a5ef674c462443e5d50d95b431990f 100644 (file)
@@ -86,7 +86,7 @@ Fixes since v2.10.1
    by refusing to check out a branch that is already checked out in
    another worktree.  However, this also prevented checking out a
    branch, which is designated as the primary branch of a bare
-   reopsitory, in a worktree that is connected to the bare
+   repository, in a worktree that is connected to the bare
    repository.  The check has been corrected to allow it.
 
  * "git rebase" immediately after "git clone" failed to find the fork
index 9cd14c8197f9bb95e7c13ce7e243cb7a591ec000..7d35cf186dbf762304f01d01be77d3e20cac98ce 100644 (file)
@@ -104,7 +104,7 @@ Fixes since v2.11
    "git difftool --dir-diff" from a subdirectory never worked. This
    has been fixed.
 
- * "git p4" that tracks multile p4 paths imported a single changelist
+ * "git p4" that tracks multiple p4 paths imported a single changelist
    that touches files in these multiple paths as one commit, followed
    by many empty commits.  This has been fixed.
 
index ef8b97da9b84342422cc2519a78e782f264c339d..d2f6a836146d51f86e4703fc3137f1f561a939d5 100644 (file)
@@ -315,7 +315,7 @@ notes for details).
    "git difftool --dir-diff" from a subdirectory never worked. This
    has been fixed.
 
- * "git p4" that tracks multile p4 paths imported a single changelist
+ * "git p4" that tracks multiple p4 paths imported a single changelist
    that touches files in these multiple paths as one commit, followed
    by many empty commits.  This has been fixed.
 
index aa99d4b3cef0f92ea5fc314ea8f77be5b665cb3e..2a47b4cb0cd4c7ed86f5bfc999973b7cb0cc5ab4 100644 (file)
@@ -177,7 +177,7 @@ UI, Workflows & Features
    been changed to enable "--decorate".
 
  * The output from "git status --short" has been extended to show
-   various kinds of dirtyness in submodules differently; instead of to
+   various kinds of dirtiness in submodules differently; instead of to
    "M" for modified, 'm' and '?' can be shown to signal changes only
    to the working tree of the submodule but not the commit that is
    checked out.
index 5d76ad53106b35fb650f2deec6f10edaeb9e2ef6..384e4de2652636806320814a511fe7e7ec6ab786 100644 (file)
@@ -25,7 +25,7 @@ Fixes since v2.13.2
  * The code to pick up and execute command alias definition from the
    configuration used to switch to the top of the working tree and
    then come back when the expanded alias was executed, which was
-   unnecessarilyl complex.  Attempt to simplify the logic by using the
+   unnecessarily complex.  Attempt to simplify the logic by using the
    early-config mechanism that does not chdir around.
 
  * "git add -p" were updated in 2.12 timeframe to cope with custom
@@ -35,7 +35,7 @@ Fixes since v2.13.2
  * Fix a recent regression to "git rebase -i" and add tests that would
    have caught it and others.
 
- * An unaligned 32-bit access in pack-bitmap code ahs been corrected.
+ * An unaligned 32-bit access in pack-bitmap code has been corrected.
 
  * Tighten error checks for invalid "git apply" input.
 
index 4246c68ff51eda9b4c32b5976816820999017c02..2711a2529dadbb4674b5e40e93093d33a5b9ec22 100644 (file)
@@ -141,7 +141,7 @@ Performance, Internal Implementation, Development Support etc.
  * Some platforms have ulong that is smaller than time_t, and our
    historical use of ulong for timestamp would mean they cannot
    represent some timestamp that the platform allows.  Invent a
-   separate and dedicated timestamp_t (so that we can distingiuish
+   separate and dedicated timestamp_t (so that we can distinguish
    timestamps and a vanilla ulongs, which along is already a good
    move), and then declare uintmax_t is the type to be used as the
    timestamp_t.
@@ -442,7 +442,7 @@ notes for details).
  * The code to pick up and execute command alias definition from the
    configuration used to switch to the top of the working tree and
    then come back when the expanded alias was executed, which was
-   unnecessarilyl complex.  Attempt to simplify the logic by using the
+   unnecessarily complex.  Attempt to simplify the logic by using the
    early-config mechanism that does not chdir around.
 
  * Fix configuration codepath to pay proper attention to commondir
index 0c81c5915fdbdbb9f9225211f7558954c1aa242d..b474781ed82773453c63f3503e079c23869997a5 100644 (file)
@@ -407,7 +407,7 @@ Fixes since v2.15
    (merge eef3df5a93 bw/pathspec-match-submodule-boundary later to maint).
 
  * Amending commits in git-gui broke the author name that is non-ascii
-   due to incorrect enconding conversion.
+   due to incorrect encoding conversion.
 
  * Recent update to the submodule configuration code broke "diff-tree"
    by accidentally stopping to read from the index upfront.
index 64a0bcb0d251fce9fe94cfdea020f445f5424a16..f0121a8f2d31f59dd2802064103a7ac46e5150f6 100644 (file)
@@ -24,7 +24,7 @@ Fixes since v2.16.2
 
  * The http tracing code, often used to debug connection issues,
    learned to redact potentially sensitive information from its output
-   so that it can be more safely sharable.
+   so that it can be more safely shareable.
 
  * Crash fix for a corner case where an error codepath tried to unlock
    what it did not acquire lock on.
index c2cf891f71adfe09f7432591333952d8577439c4..8b17c260336c90b5019cf6adbf74066a9b725427 100644 (file)
@@ -216,7 +216,7 @@ Fixes since v2.16
 
  * The http tracing code, often used to debug connection issues,
    learned to redact potentially sensitive information from its output
-   so that it can be more safely sharable.
+   so that it can be more safely shareable.
    (merge 8ba18e6fa4 jt/http-redact-cookies later to maint).
 
  * Crash fix for a corner case where an error codepath tried to unlock
index 3ea280cf68e5da48e352023257ef37529410174d..6c8a0e97c1a715a360dc10019219b4fd063d6068 100644 (file)
@@ -179,7 +179,7 @@ Performance, Internal Implementation, Development Support etc.
    (merge 00a3da2a13 nd/remove-ignore-env-field later to maint).
 
  * Code to find the length to uniquely abbreviate object names based
-   on packfile content, which is a relatively recent addtion, has been
+   on packfile content, which is a relatively recent addition, has been
    optimized to use the same fan-out table.
 
  * The mechanism to use parse-options API to automate the command line
index a06ccf6e2a2619df17ecb8525bb28a2f53423e7f..891c79b9cb2b6a8b4a3f232e1391c3501fdd2877 100644 (file)
@@ -106,7 +106,7 @@ Performance, Internal Implementation, Development Support etc.
  * The conversion to pass "the_repository" and then "a_repository"
    throughout the object access API continues.
 
- * Continuing with the idea to programatically enumerate various
+ * Continuing with the idea to programmatically enumerate various
    pieces of data required for command line completion, teach the
    codebase to report the list of configuration variables
    subcommands care about to help complete them.
index e71fe3dee13ea181f22bd38e3418b8a08cdd0b27..3dd7e6e1fcf9e0c330bda08e7fc24f7811e880f3 100644 (file)
@@ -119,7 +119,7 @@ UI, Workflows & Features
    alias expansion.
 
  * The documentation of "git gc" has been updated to mention that it
-   is no longer limited to "pruning away crufts" but also updates
+   is no longer limited to "pruning away cruft" but also updates
    ancillary files like commit-graph as a part of repository
    optimization.
 
diff --git a/Documentation/RelNotes/2.25.0.txt b/Documentation/RelNotes/2.25.0.txt
new file mode 100644 (file)
index 0000000..e4bb07a
--- /dev/null
@@ -0,0 +1,155 @@
+Git 2.25 Release Notes
+======================
+
+Updates since v2.24
+-------------------
+
+Backward compatibility notes
+
+
+UI, Workflows & Features
+
+ * A tutorial on object enumeration has been added.
+
+ * The branch description ("git branch --edit-description") has been
+   used to fill the body of the cover letters by the format-patch
+   command; this has been enhanced so that the subject can also be
+   filled.
+
+ * "git rebase --preserve-merges" has been marked as deprecated; this
+   release stops advertising it in the "git rebase -h" output.
+
+ * The code to generate multi-pack index learned to show (or not to
+   show) progress indicators.
+
+ * "git apply --3way" learned to honor merge.conflictStyle
+   configuration variable, like merges would.
+
+ * The custom format for "git log --format=<format>" learned the l/L
+   placeholder that is similar to e/E that fills in the e-mail
+   address, but only the local part on the left side of '@'.
+
+ * Documentation pages for "git shortlog" now list commit limiting
+   options explicitly.
+
+ * The patterns to detect function boundary for Elixir language has
+   been added.
+
+ * The completion script (in contrib/) learned that the "--onto"
+   option of "git rebase" can take its argument as the value of the
+   option.
+
+
+Performance, Internal Implementation, Development Support etc.
+
+ * Debugging support for lazy cloning has been a bit improved.
+
+ * Move the definition of a set of bitmask constants from 0ctal
+   literal to (1U<<count) notation.
+
+ * Test updates to prepare for SHA-2 transition continues.
+
+ * Crufty code and logic accumulated over time around the object
+   parsing and low-level object access used in "git fsck" have been
+   cleaned up.
+
+ * The implementation of "git log --graph" got refactored and then its
+   output got simplified.
+
+ * Follow recent push to move API docs from Documentation/ to header
+   files and update config.h
+
+ * "git bundle" has been taught to use the parse options API.  "git
+   bundle verify" learned "--quiet" and "git bundle create" learned
+   options to control the progress output.
+
+ * Handling of commit objects that use non UTF-8 encoding during
+   "rebase -i" has been improved.
+
+
+Fixes since v2.24
+-----------------
+
+ * "rebase -i" ceased to run post-commit hook by mistake in an earlier
+   update, which has been corrected.
+
+ * "git notes copy $original" ought to copy the notes attached to the
+   original object to HEAD, but a mistaken tightening to command line
+   parameter validation made earlier disabled that feature by mistake.
+
+ * When all files from some subdirectory were renamed to the root
+   directory, the directory rename heuristics would fail to detect that
+   as a rename/merge of the subdirectory to the root directory, which has
+   been corrected.
+
+ * Code clean-up and a bugfix in the logic used to tell worktree local
+   and repository global refs apart.
+   (merge f45f88b2e4 sg/dir-trie-fixes later to maint).
+
+ * "git stash save" in a working tree that is sparsely checked out
+   mistakenly removed paths that are outside the area of interest.
+   (merge 4a58c3d7f7 js/update-index-ignore-removal-for-skip-worktree later to maint).
+
+ * "git rev-parse --git-path HEAD.lock" did not give the right path
+   when run in a secondary worktree.
+   (merge 76a53d640f js/git-path-head-dot-lock-fix later to maint).
+
+ * "git merge --no-commit" needs "--no-ff" if you do not want to move
+   HEAD, which has been corrected in the manual page for "git bisect".
+   (merge 8dd327b246 ma/bisect-doc-sample-update later to maint).
+
+ * "git worktree add" internally calls "reset --hard" that should not
+   descend into submodules, even when submodule.recurse configuration
+   is set, but it was affected.  This has been corrected.
+   (merge 4782cf2ab6 pb/no-recursive-reset-hard-in-worktree-add later to maint).
+
+ * Messages from die() etc. can be mixed up from multiple processes
+   without even line buffering on Windows, which has been worked
+   around.
+   (merge 116d1fa6c6 js/vreportf-wo-buffering later to maint).
+
+ * HTTP transport had possible allocator/deallocator mismatch, which
+   has been corrected.
+
+ * The watchman integration for fsmonitor was racy, which has been
+   corrected to be more conservative.
+   (merge dd0b61f577 kw/fsmonitor-watchman-fix later to maint).
+
+ * Fetching from multiple remotes into the same repository in parallel
+   had a bad interaction with the recent change to (optionally) update
+   the commit-graph after a fetch job finishes, as these parallel
+   fetches compete with each other.  Which has been corrected.
+
+ * Recent update to "git stash pop" made the command empty the index
+   when run with the "--quiet" option, which has been corrected.
+
+ * "git fetch" codepath had a big "do not lazily fetch missing objects
+   when I ask if something exists" switch.  This has been corrected by
+   marking the "does this thing exist?" calls with "if not please do not
+   lazily fetch it" flag.
+
+ * Test update to avoid wasted cycles.
+   (merge e0316695ec sg/skip-skipped-prereq later to maint).
+
+ * Error handling after "git push" finishes sending the packdata and
+   waits for the response to the remote side has been improved.
+   (merge ad7a403268 jk/send-pack-remote-failure later to maint).
+
+ * Some codepaths in "gitweb" that forgot to escape URLs generated
+   based on end-user input have been corrected.
+   (merge a376e37b2c jk/gitweb-anti-xss later to maint).
+
+ * Other code cleanup, docfix, build fix, etc.
+   (merge 80736d7c5e jc/am-show-current-patch-docfix later to maint).
+   (merge 8b656572ca sg/commit-graph-usage-fix later to maint).
+   (merge 6c02042139 mr/clone-dir-exists-to-path-exists later to maint).
+   (merge 44ae131e38 sg/blame-indent-heuristics-is-now-the-default later to maint).
+   (merge 0115e5d929 dl/doc-diff-no-index-implies-exit-code later to maint).
+   (merge 270de6acbe en/t6024-style later to maint).
+   (merge 14c4776d75 ns/test-desc-typofix later to maint).
+   (merge 68d40f30c4 dj/typofix-merge-strat later to maint).
+   (merge f66e0401ab jk/optim-in-pack-idx-conversion later to maint).
+   (merge 169bed7421 rs/parse-options-dup-null-fix later to maint).
+   (merge 51bd6be32d rs/use-copy-array-in-mingw-shell-command-preparation later to maint).
+   (merge b018719927 ma/t7004 later to maint).
+   (merge 932757b0cc ar/install-doc-update-cmds-needing-the-shell later to maint).
index 5ef12644c22913a87677b42668fa6b4433f76cd5..850dc68edef0db621dcac801f41c9e08aaacd7a9 100644 (file)
@@ -12,7 +12,7 @@ Fixes since v2.3.2
  * Description given by "grep -h" for its --exclude-standard option
    was phrased poorly.
 
- * Documentaton for "git remote add" mentioned "--tags" and
+ * Documentation for "git remote add" mentioned "--tags" and
    "--no-tags" and it was not clear that fetch from the remote in
    the future will use the default behaviour when neither is given
    to override it.
index fc95812cb3f07569f78a921794b88d8e19906124..576918408161b65b54b3d35dbb41a7d7ece7495c 100644 (file)
@@ -4,7 +4,7 @@ Git v2.3.7 Release Notes
 Fixes since v2.3.6
 ------------------
 
- * An earlier update to the parser that disects a URL broke an
+ * An earlier update to the parser that dissects a URL broke an
    address, followed by a colon, followed by an empty string (instead
    of the port number), e.g. ssh://example.com:/path/to/repo.
 
index 914d2c1860007e0fc6f32596a3b6ed256437614f..422e930aa2b297d08ab91298f3e28d4fcea17a7a 100644 (file)
@@ -66,7 +66,7 @@ Fixes since v2.4.3
  * Some time ago, "git blame" (incorrectly) lost the convert_to_git()
    call when synthesizing a fake "tip" commit that represents the
    state in the working tree, which broke folks who record the history
-   with LF line ending to make their project portabile across
+   with LF line ending to make their project portable across
    platforms while terminating lines in their working tree files with
    CRLF for their platform.
 
index 563dadc57e84d17bc348975fce4e5102bcc03022..e3cbf3a73c46fac6cca4a3e3566e6222d9d36e9b 100644 (file)
@@ -40,7 +40,7 @@ UI, Workflows & Features
 
  * "git interpret-trailers" can now run outside of a Git repository.
 
- * "git p4" learned to reencode the pathname it uses to communicate
+ * "git p4" learned to re-encode the pathname it uses to communicate
    with the p4 depot with a new option.
 
  * Give progress meter to "git filter-branch".
index 5fbe1b86eeaca111ecef74b909c361172ca506ba..27320b6a9f37f57e80607aa2d0adfbc1df2a53a2 100644 (file)
@@ -189,7 +189,7 @@ Performance, Internal Implementation, Development Support etc.
  * Some calls to strcpy(3) triggers a false warning from static
    analyzers that are less intelligent than humans, and reducing the
    number of these false hits helps us notice real issues.  A few
-   calls to strcpy(3) in a couple of protrams that are already safe
+   calls to strcpy(3) in a couple of programs that are already safe
    has been rewritten to avoid false warnings.
 
  * The "name_path" API was an attempt to reduce the need to construct
index 695b86f612fa178b816696fef76467ffc71f3db7..305e08062b1b00b223effb13ce826580e019529f 100644 (file)
@@ -36,7 +36,7 @@ Fixes since v2.9.2
  * One part of "git am" had an oddball helper function that called
    stuff from outside "his" as opposed to calling what we have "ours",
    which was not gender-neutral and also inconsistent with the rest of
-   the system where outside stuff is usuall called "theirs" in
+   the system where outside stuff is usually called "theirs" in
    contrast to "ours".
 
  * The test framework learned a new helper test_match_signal to
index f50f1b4128ceda08513d1e4f7b1fe95aa1134d95..83e7bba8729627d3e329f8a1934ecf46fb30ff1f 100644 (file)
@@ -142,7 +142,7 @@ refer to linkgit:gitignore[5] for details. For convenience:
 
 `gitdir/i`::
        This is the same as `gitdir` except that matching is done
-       case-insensitively (e.g. on case-insensitive file sytems)
+       case-insensitively (e.g. on case-insensitive file systems)
 
 `onbranch`::
        The data that follows the keyword `onbranch:` is taken to be a
index 4d753f006ec1efd466b18d74841c8d43039d8ad9..c9f748f81cb1c7c3b4a2cc2298b310ac85cb1d5c 100644 (file)
@@ -5,3 +5,8 @@ add.ignore-errors (deprecated)::
        option of linkgit:git-add[1].  `add.ignore-errors` is deprecated,
        as it does not follow the usual naming convention for configuration
        variables.
+
+add.interactive.useBuiltin::
+       [EXPERIMENTAL] Set to `true` to use the experimental built-in
+       implementation of the interactive version of linkgit:git-add[1]
+       instead of the Perl script version. Is `false` by default.
index 40cad9278fd1734ce5bc7445611dbeb70d00168d..513fcd88d5b934bedfcb95909cd0593a39687b2e 100644 (file)
@@ -36,6 +36,12 @@ format.subjectPrefix::
        The default for format-patch is to output files with the '[PATCH]'
        subject prefix. Use this variable to change that prefix.
 
+format.coverFromDescription::
+       The default mode for format-patch to determine which parts of
+       the cover letter will be populated using the branch's
+       description. See the `--cover-from-description` option in
+       linkgit:git-format-patch[1].
+
 format.signature::
        The default for format-patch is to output a signature containing
        the Git version number. Use this variable to change that default.
index ef5adb3f420df087132a15a9d0e825e911407758..6d9110d84ce74f4de5782e9fcd5531719ed4627a 100644 (file)
@@ -13,7 +13,7 @@ tag.gpgSign::
        Use of this option when running in an automated script can
        result in a large number of tags being signed. It is therefore
        convenient to use an agent to avoid typing your gpg passphrase
-       several times. Note that this option doesn't affects tag signing
+       several times. Note that this option doesn't affect tag signing
        behavior enabled by "-u <keyid>" or "--local-user=<keyid>" options.
 
 tar.umask::
index 43b9ff3bce218e986e9dabd57a0e7981ff59166a..a2f78624a27e68dfe5a97adcac9cd67c109d1f52 100644 (file)
@@ -92,6 +92,10 @@ ifndef::git-pull[]
        Run `git gc --auto` at the end to perform garbage collection
        if needed. This is enabled by default.
 
+--[no-]write-commit-graph::
+       Write a commit-graph after fetching. This overrides the config
+       setting `fetch.writeCommitGraph`.
+
 -p::
 --prune::
        Before fetching, remove any remote-tracking references that no
index fc3b993c3338b5f9f8a034aaade14e77bdcd75b5..fc5750b3b81ef54e072ab916f4f0ce775c52ac99 100644 (file)
@@ -177,7 +177,7 @@ default.   You can use `--no-utf8` to override this.
        untouched.
 
 --show-current-patch::
-       Show the patch being applied when "git am" is stopped because
+       Show the entire e-mail message "git am" has stopped at, because
        of conflicts.
 
 DISCUSSION
index e99925184d031cac622395386ab6255e024bdff8..2957bc5e0a89e2a7c37d6163a089554c4468d9e7 100644 (file)
@@ -158,7 +158,7 @@ Test suites are very nice. But when they are used alone, they are
 supposed to be used so that all the tests are checked after each
 commit. This means that they are not very efficient, because many
 tests are run for no interesting result, and they suffer from
-combinational explosion.
+combinatorial explosion.
 
 In fact the problem is that big software often has many different
 configuration options and that each test case should pass for each
index 4b45d837a7e7c590fe3aa5f575009c43342b833c..7586c5a8437edfc146fa3b802bc5e72e5070ee9a 100644 (file)
@@ -413,7 +413,7 @@ $ cat ~/test.sh
 
 # tweak the working tree by merging the hot-fix branch
 # and then attempt a build
-if     git merge --no-commit hot-fix &&
+if     git merge --no-commit --no-ff hot-fix &&
        make
 then
        # run project specific test and report its status
index 7d6c9dcd177b6a1fa4a6184b230480aa08e07120..ccada80a4a4c9813804372b7b167f0015a7ec3a7 100644 (file)
@@ -9,8 +9,8 @@ git-bundle - Move objects and refs by archive
 SYNOPSIS
 --------
 [verse]
-'git bundle' create <file> <git-rev-list-args>
-'git bundle' verify <file>
+'git bundle' create [-q | --quiet | --progress | --all-progress] [--all-progress-implied] <file> <git-rev-list-args>
+'git bundle' verify [-q | --quiet] <file>
 'git bundle' list-heads <file> [<refname>...]
 'git bundle' unbundle <file> [<refname>...]
 
@@ -33,9 +33,11 @@ destination repository.
 OPTIONS
 -------
 
-create <file>::
+create [options] <file> <git-rev-list-args>::
        Used to create a bundle named 'file'.  This requires the
        'git-rev-list-args' arguments to define the bundle contents.
+       'options' contains the options specific to the 'git bundle create'
+       subcommand.
 
 verify <file>::
        Used to check that a bundle file is valid and will apply
@@ -75,6 +77,33 @@ unbundle <file>::
        necessarily everything in the pack (in this case, 'git bundle' acts
        like 'git fetch-pack').
 
+--progress::
+       Progress status is reported on the standard error stream
+       by default when it is attached to a terminal, unless -q
+       is specified. This flag forces progress status even if
+       the standard error stream is not directed to a terminal.
+
+--all-progress::
+       When --stdout is specified then progress report is
+       displayed during the object count and compression phases
+       but inhibited during the write-out phase. The reason is
+       that in some cases the output stream is directly linked
+       to another command which may wish to display progress
+       status of its own as it processes incoming pack data.
+       This flag is like --progress except that it forces progress
+       report for the write-out phase as well even if --stdout is
+       used.
+
+--all-progress-implied::
+       This is used to imply --all-progress whenever progress display
+       is activated.  Unlike --all-progress this flag doesn't actually
+       force any progress display by itself.
+
+-q::
+--quiet::
+       This flag makes the command not to report its progress
+       on the standard error stream.
+
 SPECIFYING REFERENCES
 ---------------------
 
index 3c0578217ba7f92f823e545021c5c06c31566f0d..84f41a8e82590f54bca103a067a95348093844b5 100644 (file)
@@ -32,7 +32,7 @@ OPTIONS
        instead of from the command-line.
 
 -z::
-       The output format is modified to be machine-parseable.
+       The output format is modified to be machine-parsable.
        If `--stdin` is also given, input paths are separated
        with a NUL character instead of a linefeed character.
 
index 8b42cb3fb20a78cde2eb4c6a7546a334cc71ec4b..8b2d49c79e1140bb33b63d8c388f4b2c339f7ccf 100644 (file)
@@ -39,7 +39,7 @@ OPTIONS
        instead of from the command-line.
 
 -z::
-       The output format is modified to be machine-parseable (see
+       The output format is modified to be machine-parsable (see
        below).  If `--stdin` is also given, input paths are separated
        with a NUL character instead of a linefeed character.
 
index 8c708a7a16ece24dfb3d617f2c8117c12d234655..bcd85c1976769376477fe43f5b15ec1394a31f05 100644 (file)
@@ -9,7 +9,6 @@ git-commit-graph - Write and verify Git commit-graph files
 SYNOPSIS
 --------
 [verse]
-'git commit-graph read' [--object-dir <dir>]
 'git commit-graph verify' [--object-dir <dir>] [--shallow] [--[no-]progress]
 'git commit-graph write' <options> [--object-dir <dir>] [--[no-]progress]
 
@@ -74,11 +73,6 @@ Finally, if `--expire-time=<datetime>` is not specified, let `datetime`
 be the current time. After writing the split commit-graph, delete all
 unused commit-graph whose modified times are older than `datetime`.
 
-'read'::
-
-Read the commit-graph file and output basic details about it.
-Used for debugging purposes.
-
 'verify'::
 
 Read the commit-graph file and verify its contents against the object
@@ -118,12 +112,6 @@ $ git show-ref -s | git commit-graph write --stdin-commits
 $ git rev-parse HEAD | git commit-graph write --stdin-commits --append
 ------------------------------------------------
 
-* Read basic information from the commit-graph file.
-+
-------------------------------------------------
-$ git commit-graph read
-------------------------------------------------
-
 
 GIT
 ---
index 79e22b1f3a131ea5301a1c11fb558ec16e6c16e6..1b1c71ad9d26812da7253e2305bce296ffbbd10e 100644 (file)
@@ -294,7 +294,7 @@ In `dbDriver` and `dbUser` you can use the following variables:
        Git directory name
 %g::
        Git directory name, where all characters except for
-       alpha-numeric ones, `.`, and `-` are replaced with
+       alphanumeric ones, `.`, and `-` are replaced with
        `_` (this should make it easier to use the directory
        name in a filename if wanted)
 %m::
index 72179d993cb9b68766e2d9093f323cb3720e38f4..37781cf175547c05b8ec3ec97aa2e700196c6471 100644 (file)
@@ -36,7 +36,7 @@ two blob objects, or changes between two files on disk.
        running the command in a working tree controlled by Git and
        at least one of the paths points outside the working tree,
        or when running the command outside a working tree
-       controlled by Git.
+       controlled by Git. This form implies `--exit-code`.
 
 'git diff' [<options>] --cached [<commit>] [--] [<path>...]::
 
index 37634bffd1b1de2320fbb4df871c5ce089df8dc1..e8950de3ba40b9b2ff6587150a7fd4b3309752d3 100644 (file)
@@ -142,7 +142,7 @@ by keeping the marks the same across runs.
        Specify how to handle `encoding` header in commit objects.  When
        asking to 'abort' (which is the default), this program will die
        when encountering such a commit object.  With 'yes', the commit
-       message will be reencoded into UTF-8.  With 'no', the original
+       message will be re-encoded into UTF-8.  With 'no', the original
        encoding will be preserved.
 
 --refspec::
index 5876598852f7b883241d8f9e4efc359b8aab3089..3686a67d3e202e26e99ca16cc57ea17fd5492935 100644 (file)
@@ -534,7 +534,7 @@ repo-filter' also provides
 https://github.com/newren/git-filter-repo/blob/master/contrib/filter-repo-demos/filter-lamely[filter-lamely],
 a drop-in git-filter-branch replacement (with a few caveats).  While
 filter-lamely suffers from all the same safety issues as
-git-filter-branch, it at least ameloriates the performance issues a
+git-filter-branch, it at least ameliorates the performance issues a
 little.
 
 [[SAFETY]]
@@ -649,7 +649,7 @@ create hoards of confusing empty commits
 commits from before the filtering operation are also pruned instead of
 just pruning commits that became empty due to filtering rules.
 
-* If --prune empty is specified, sometimes empty commits are missed
+* If --prune-empty is specified, sometimes empty commits are missed
 and left around anyway (a somewhat rare bug, but it happens...)
 
 * A minor issue, but users who have a goal to update all names and
index 2035d4d5d53df801186ee7a713a51634d6ac9fd9..00bdf9b1251a1df819545f3da758c51cc25d7bfa 100644 (file)
@@ -19,6 +19,7 @@ SYNOPSIS
                   [--start-number <n>] [--numbered-files]
                   [--in-reply-to=<message id>] [--suffix=.<sfx>]
                   [--ignore-if-in-upstream]
+                  [--cover-from-description=<mode>]
                   [--rfc] [--subject-prefix=<subject prefix>]
                   [(--reroll-count|-v) <n>]
                   [--to=<email>] [--cc=<email>]
@@ -172,6 +173,26 @@ will want to ensure that threading is disabled for `git send-email`.
        patches being generated, and any patch that matches is
        ignored.
 
+--cover-from-description=<mode>::
+       Controls which parts of the cover letter will be automatically
+       populated using the branch's description.
++
+If `<mode>` is `message` or `default`, the cover letter subject will be
+populated with placeholder text. The body of the cover letter will be
+populated with the branch's description. This is the default mode when
+no configuration nor command line option is specified.
++
+If `<mode>` is `subject`, the first paragraph of the branch description will
+populate the cover letter subject. The remainder of the description will
+populate the body of the cover letter.
++
+If `<mode>` is `auto`, if the first paragraph of the branch description
+is greater than 100 bytes, then the mode will be `message`, otherwise
+`subject` will be used.
++
+If `<mode>` is `none`, both the cover letter subject and body will be
+populated with placeholder text.
+
 --subject-prefix=<subject prefix>::
        Instead of the standard '[PATCH]' prefix in the subject
        line, instead use '[<subject prefix>]'. This
@@ -348,6 +369,7 @@ with configuration variables.
        signOff = true
        outputDirectory = <directory>
        coverLetter = auto
+       coverFromDescription = auto
 ------------
 
 
index 233b2b786271cc695268d2f7c0139d02228bd3c2..642d9ac5b723652f339fa45b03ea67806d44f735 100644 (file)
@@ -9,7 +9,7 @@ git-multi-pack-index - Write and verify multi-pack-indexes
 SYNOPSIS
 --------
 [verse]
-'git multi-pack-index' [--object-dir=<dir>] <subcommand>
+'git multi-pack-index' [--object-dir=<dir>] [--[no-]progress] <subcommand>
 
 DESCRIPTION
 -----------
@@ -23,6 +23,10 @@ OPTIONS
        `<dir>/packs/multi-pack-index` for the current MIDX file, and
        `<dir>/packs` for the pack-files to index.
 
+--[no-]progress::
+       Turn progress on/off explicitly. If neither is specified, progress is
+       shown if standard error is connected to a terminal.
+
 The following subcommands are available:
 
 write::
index f56a5a91975d592b19d03edafef943519718c588..ced2e8280ef5e4c4c930fd1abdab83834c293140 100644 (file)
@@ -10,7 +10,7 @@ SYNOPSIS
 [verse]
 'git notes' [list [<object>]]
 'git notes' add [-f] [--allow-empty] [-F <file> | -m <msg> | (-c | -C) <object>] [<object>]
-'git notes' copy [-f] ( --stdin | <from-object> <to-object> )
+'git notes' copy [-f] ( --stdin | <from-object> [<to-object>] )
 'git notes' append [--allow-empty] [-F <file> | -m <msg> | (-c | -C) <object>] [<object>]
 'git notes' edit [--allow-empty] [<object>]
 'git notes' show [<object>]
@@ -68,8 +68,8 @@ add::
        subcommand).
 
 copy::
-       Copy the notes for the first object onto the second object.
-       Abort if the second object already has notes, or if the first
+       Copy the notes for the first object onto the second object (defaults to
+       HEAD). Abort if the second object already has notes, or if the first
        object has none (use -f to overwrite existing notes to the
        second object). This subcommand is equivalent to:
        `git notes add [-f] -C $(git notes list <from-object>) <to-object>`
index 8a6ea2c6c5c7a39a9f34d6b93388f3343c1e3026..006feb7ec149561e8f99069ab5153912dbd53661 100644 (file)
@@ -242,7 +242,7 @@ corresponding.
 
 The overall time needed to compute this algorithm is the time needed to
 compute n+m commit diffs and then n*m diffs of patches, plus the time
-needed to compute the least-cost assigment between n and m diffs. Git
+needed to compute the least-cost assignment between n and m diffs. Git
 uses an implementation of the Jonker-Volgenant algorithm to solve the
 assignment problem, which has cubic runtime complexity. The matching
 found in this case will look like this:
index 639a4179d18e4d154f30351aaa4f3b3c79c48c1e..0c4f038dd6077fb47436e82c2ce80b78fa6b9304 100644 (file)
@@ -443,8 +443,8 @@ the `rebase-cousins` mode is turned on, such commits are instead rebased
 onto `<upstream>` (or `<onto>`, if specified).
 +
 The `--rebase-merges` mode is similar in spirit to the deprecated
-`--preserve-merges`, but in contrast to that option works well in interactive
-rebases: commits can be reordered, inserted and dropped at will.
+`--preserve-merges` but works with interactive rebases,
+where commits can be reordered, inserted and dropped at will.
 +
 It is currently only possible to recreate the merge commits using the
 `recursive` merge strategy; Different merge strategies can be used only via
index e72d332b8316763d40e7bcfe0e32bcaa61842cf8..9985477efe9c3e7defb639fa30517d0ea93eb7b6 100644 (file)
@@ -274,6 +274,13 @@ print a message to stderr and exit with nonzero status.
        Show the path to the shared index file in split index mode, or
        empty if not in split-index mode.
 
+--show-object-format[=(storage|input|output)]::
+       Show the object format (hash algorithm) used for the repository
+       for storage inside the `.git` directory, input, or output. For
+       input, multiple algorithms may be printed, space-separated.
+       If not specified, the default is "storage".
+
+
 Other Options
 ~~~~~~~~~~~~~
 
index bc80905a8a06b5121e2c33c83844301a8212c28c..a72ea7f7bafaabbc4ffc36e3fb89f1809917e94e 100644 (file)
@@ -76,6 +76,9 @@ them.
 Paths may need to be prefixed with `--` to separate them from
 options or the revision range, when confusion arises.
 
+:git-shortlog: 1
+include::rev-list-options.txt[]
+
 MAPPING AUTHORS
 ---------------
 
index 2e5599a67f86ad16969a5ea2697bdeb50ca801a6..f6d9791780288ff077f5c4bbd0fa4c4c47cd9cea 100644 (file)
@@ -65,7 +65,7 @@ OPTIONS
 --sign::
        Make a GPG-signed tag, using the default e-mail address's key.
        The default behavior of tag GPG-signing is controlled by `tag.gpgSign`
-       configuration variable if it exists, or disabled oder otherwise.
+       configuration variable if it exists, or disabled otherwise.
        See linkgit:git-config[1].
 
 --no-sign::
index 1c4d146a41ce091755b2e4baef0fa79f45a4b515..c7a6271daf61507459fe3bb1a0bf494def4f994f 100644 (file)
@@ -16,6 +16,7 @@ SYNOPSIS
             [--chmod=(+|-)x]
             [--[no-]assume-unchanged]
             [--[no-]skip-worktree]
+            [--[no-]ignore-skip-worktree-entries]
             [--[no-]fsmonitor-valid]
             [--ignore-submodules]
             [--[no-]split-index]
@@ -113,6 +114,11 @@ you will need to handle the situation manually.
        set and unset the "skip-worktree" bit for the paths. See
        section "Skip-worktree bit" below for more information.
 
+
+--[no-]ignore-skip-worktree-entries::
+       Do not remove skip-worktree (AKA "index-only") entries even when
+       the `--remove` option was specified.
+
 --[no-]fsmonitor-valid::
        When one of these flags is specified, the object name recorded
        for the paths are not updated. Instead, these options
@@ -426,7 +432,7 @@ specified by the splitIndex.sharedIndexExpire config variable (see
 linkgit:git-config[1]).
 
 To avoid deleting a shared index file that is still used, its
-modification time is updated to the current time everytime a new split
+modification time is updated to the current time every time a new split
 index based on the shared index file is either created or read from.
 
 UNTRACKED CACHE
index 9b82564d1aa9c04c4b0299c1a82c4ba45a2c3415..1b678e22db202726606bb65cf2f0970a9b1e509e 100644 (file)
@@ -271,8 +271,8 @@ In general, the interrogate commands do not touch the files in
 the working tree.
 
 
-Synching repositories
-~~~~~~~~~~~~~~~~~~~~~
+Syncing repositories
+~~~~~~~~~~~~~~~~~~~~
 
 include::cmds-synchingrepositories.txt[]
 
index c5a528c667b62abe81e6454b061834200011657e..508fe713c4cfb1e2126bc96fc201ca2cd49c6222 100644 (file)
@@ -293,10 +293,10 @@ web front ends do not visualize the contents of these files by default.
 
 In these cases you can tell Git the encoding of a file in the working
 directory with the `working-tree-encoding` attribute. If a file with this
-attribute is added to Git, then Git reencodes the content from the
+attribute is added to Git, then Git re-encodes the content from the
 specified encoding to UTF-8. Finally, Git stores the UTF-8 encoded
 content in its internal data structure (called "the index"). On checkout
-the content is reencoded back to the specified encoding.
+the content is re-encoded back to the specified encoding.
 
 Please note that using the `working-tree-encoding` attribute may have a
 number of pitfalls:
@@ -498,7 +498,7 @@ command. This is achieved by using the long-running process protocol
 When Git encounters the first file that needs to be cleaned or smudged,
 it starts the filter and performs the handshake. In the handshake, the
 welcome message sent by Git is "git-filter-client", only version 2 is
-suppported, and the supported capabilities are "clean", "smudge", and
+supported, and the supported capabilities are "clean", "smudge", and
 "delay".
 
 Afterwards Git sends a list of "key=value" pairs terminated with
@@ -812,6 +812,8 @@ patterns are available:
 
 - `dts` suitable for devicetree (DTS) files.
 
+- `elixir` suitable for source code in the Elixir language.
+
 - `fortran` suitable for source code in the Fortran language.
 
 - `fountain` suitable for Fountain documents.
index f2a65ba0cad1453da1910504d4c6150e2a7bdd66..953c3876f028aadc71c55d29be1b91ba197b39ed 100644 (file)
@@ -81,7 +81,7 @@ submodule.<name>.ignore::
            Committed differences and modifications to tracked files will show
            up.
 
-       none;; No modifiations to submodules are ignored, all of committed
+       none;; No modifications to submodules are ignored, all of committed
            differences, and modifications to tracked and untracked files are
            shown. This is the default option.
 
index d6388f10bbeb45caf9ccb1cf945161b70be625dc..1a2ef4c15055a20eaa65cbbf0f40525ba7e2ba48 100644 (file)
@@ -96,9 +96,9 @@ refs::
        directory.  The 'git prune' command knows to preserve
        objects reachable from refs found in this directory and
        its subdirectories.
-       This directory is ignored (except refs/bisect and
-       refs/worktree) if $GIT_COMMON_DIR is set and
-       "$GIT_COMMON_DIR/refs" will be used instead.
+       This directory is ignored (except refs/bisect,
+       refs/rewritten and refs/worktree) if $GIT_COMMON_DIR is
+       set and "$GIT_COMMON_DIR/refs" will be used instead.
 
 refs/heads/`name`::
        records tip-of-the-tree commit objects of branch `name`
@@ -240,8 +240,8 @@ remotes::
 logs::
        Records of changes made to refs are stored in this directory.
        See linkgit:git-update-ref[1] for more information. This
-       directory is ignored if $GIT_COMMON_DIR is set and
-       "$GIT_COMMON_DIR/logs" will be used instead.
+       directory is ignored (except logs/HEAD) if $GIT_COMMON_DIR is
+       set and "$GIT_COMMON_DIR/logs" will be used instead.
 
 logs/refs/heads/`name`::
        Records all changes made to the branch tip named `name`.
index 0a890205b8b508dd7336c834f0c327ee0e1a1d1b..c476f891b5cefdd74076f29acea25f270f3d2243 100644 (file)
@@ -3,7 +3,7 @@ gitsubmodules(7)
 
 NAME
 ----
-gitsubmodules - mounting one repository inside another
+gitsubmodules - Mounting one repository inside another
 
 SYNOPSIS
 --------
index bd1027433bb7c54cf16cb1bcdedac0d4ee669e4e..81be0d611501a07aa0e92047fa476070afc5b586 100644 (file)
@@ -81,7 +81,7 @@ After I am done, I'd try a pretend-merge between "topicA" and
               o---o---o---o---o---o
 
 The last diff better not to show anything other than cleanups
-for crufts.  Then I can finally clean things up:
+for cruft.  Then I can finally clean things up:
 
         $ git branch -D topic
         $ git reset --hard HEAD^ ;# nuke pretend merge
index 59b8ff1e51a5e173c208d1c8cd7fab71dcf37a2e..40dc4f5e8c6e2d5a59cfbbeea1df7254068a6ed2 100644 (file)
@@ -34,7 +34,7 @@ set to `no` at the beginning of them.
 
 --cleanup=<mode>::
        This option determines how the merge message will be cleaned up before
-       commiting. See linkgit:git-commit[1] for more details. In addition, if
+       committing. See linkgit:git-commit[1] for more details. In addition, if
        the '<mode>' is given a value of `scissors`, scissors will be appended
        to `MERGE_MSG` before being passed on to the commit machinery in the
        case of a merge conflict.
index aa66cbe41eaf070a74d98801ba973daeb8d15106..2912de706bf3921f817c9d18c66e526fd0a0fa8e 100644 (file)
@@ -32,7 +32,7 @@ The 'recursive' strategy can take the following options:
 ours;;
        This option forces conflicting hunks to be auto-resolved cleanly by
        favoring 'our' version.  Changes from the other tree that do not
-       conflict with our side are reflected to the merge result.
+       conflict with our side are reflected in the merge result.
        For a binary file, the entire contents are taken from our side.
 +
 This should not be confused with the 'ours' merge strategy, which does not
index b87e2e83e6d01de371f2f880f3f77fade22b45eb..31c6e8d2b86ad6b7a4db1059fe24516ad63c71e7 100644 (file)
@@ -163,6 +163,9 @@ The placeholders are:
 '%ae':: author email
 '%aE':: author email (respecting .mailmap, see linkgit:git-shortlog[1]
        or linkgit:git-blame[1])
+'%al':: author email local-part (the part before the '@' sign)
+'%aL':: author local-part (see '%al') respecting .mailmap, see
+       linkgit:git-shortlog[1] or linkgit:git-blame[1])
 '%ad':: author date (format respects --date= option)
 '%aD':: author date, RFC2822 style
 '%ar':: author date, relative
@@ -175,6 +178,9 @@ The placeholders are:
 '%ce':: committer email
 '%cE':: committer email (respecting .mailmap, see
        linkgit:git-shortlog[1] or linkgit:git-blame[1])
+'%cl':: author email local-part (the part before the '@' sign)
+'%cL':: author local-part (see '%cl') respecting .mailmap, see
+       linkgit:git-shortlog[1] or linkgit:git-blame[1])
 '%cd':: committer date (format respects --date= option)
 '%cD':: committer date, RFC2822 style
 '%cr':: committer date, relative
index 90ff9e2bea2e2fd0caf4744ed4e5c5e59f4c1243..e37df921b58ba96e0541ad043ac3d6f8f1d145aa 100644 (file)
@@ -579,6 +579,7 @@ above) if (1) they are referenced by tags, or (2) they change the
 contents of the paths given on the command line.  All other
 commits are marked as TREESAME (subject to be simplified away).
 
+ifndef::git-shortlog[]
 ifdef::git-rev-list[]
 Bisection Helpers
 ~~~~~~~~~~~~~~~~~
@@ -634,8 +635,9 @@ This option can be used along with `--bisect-vars`, in this case,
 after all the sorted commit objects, there will be the same text as if
 `--bisect-vars` had been used alone.
 endif::git-rev-list[]
+endif::git-shortlog[]
 
-
+ifndef::git-shortlog[]
 Commit Ordering
 ~~~~~~~~~~~~~~~
 
@@ -677,7 +679,9 @@ together.
        Output the commits chosen to be shown (see Commit Limiting
        section above) in reverse order. Cannot be combined with
        `--walk-reflogs`.
+endif::git-shortlog[]
 
+ifndef::git-shortlog[]
 Object Traversal
 ~~~~~~~~~~~~~~~~
 
@@ -817,7 +821,9 @@ endif::git-rev-list[]
 
 --do-walk::
        Overrides a previous `--no-walk`.
+endif::git-shortlog[]
 
+ifndef::git-shortlog[]
 Commit Formatting
 ~~~~~~~~~~~~~~~~~
 
@@ -973,7 +979,9 @@ ifdef::git-rev-list[]
        counts and print the count for equivalent commits separated
        by a tab.
 endif::git-rev-list[]
+endif::git-shortlog[]
 
+ifndef::git-shortlog[]
 ifndef::git-rev-list[]
 Diff Formatting
 ~~~~~~~~~~~~~~~
@@ -1016,3 +1024,4 @@ options may be given. See linkgit:git-diff-files[1] for more options.
 -t::
        Show the tree objects in the diff output. This implies `-r`.
 endif::git-rev-list[]
+endif::git-shortlog[]
diff --git a/Documentation/technical/api-config.txt b/Documentation/technical/api-config.txt
deleted file mode 100644 (file)
index 7d20716..0000000
+++ /dev/null
@@ -1,319 +0,0 @@
-config API
-==========
-
-The config API gives callers a way to access Git configuration files
-(and files which have the same syntax). See linkgit:git-config[1] for a
-discussion of the config file syntax.
-
-General Usage
--------------
-
-Config files are parsed linearly, and each variable found is passed to a
-caller-provided callback function. The callback function is responsible
-for any actions to be taken on the config option, and is free to ignore
-some options. It is not uncommon for the configuration to be parsed
-several times during the run of a Git program, with different callbacks
-picking out different variables useful to themselves.
-
-A config callback function takes three parameters:
-
-- the name of the parsed variable. This is in canonical "flat" form: the
-  section, subsection, and variable segments will be separated by dots,
-  and the section and variable segments will be all lowercase. E.g.,
-  `core.ignorecase`, `diff.SomeType.textconv`.
-
-- the value of the found variable, as a string. If the variable had no
-  value specified, the value will be NULL (typically this means it
-  should be interpreted as boolean true).
-
-- a void pointer passed in by the caller of the config API; this can
-  contain callback-specific data
-
-A config callback should return 0 for success, or -1 if the variable
-could not be parsed properly.
-
-Basic Config Querying
----------------------
-
-Most programs will simply want to look up variables in all config files
-that Git knows about, using the normal precedence rules. To do this,
-call `git_config` with a callback function and void data pointer.
-
-`git_config` will read all config sources in order of increasing
-priority. Thus a callback should typically overwrite previously-seen
-entries with new ones (e.g., if both the user-wide `~/.gitconfig` and
-repo-specific `.git/config` contain `color.ui`, the config machinery
-will first feed the user-wide one to the callback, and then the
-repo-specific one; by overwriting, the higher-priority repo-specific
-value is left at the end).
-
-The `config_with_options` function lets the caller examine config
-while adjusting some of the default behavior of `git_config`. It should
-almost never be used by "regular" Git code that is looking up
-configuration variables. It is intended for advanced callers like
-`git-config`, which are intentionally tweaking the normal config-lookup
-process. It takes two extra parameters:
-
-`config_source`::
-If this parameter is non-NULL, it specifies the source to parse for
-configuration, rather than looking in the usual files. See `struct
-git_config_source` in `config.h` for details. Regular `git_config` defaults
-to `NULL`.
-
-`opts`::
-Specify options to adjust the behavior of parsing config files. See `struct
-config_options` in `config.h` for details. As an example: regular `git_config`
-sets `opts.respect_includes` to `1` by default.
-
-Reading Specific Files
-----------------------
-
-To read a specific file in git-config format, use
-`git_config_from_file`. This takes the same callback and data parameters
-as `git_config`.
-
-Querying For Specific Variables
--------------------------------
-
-For programs wanting to query for specific variables in a non-callback
-manner, the config API provides two functions `git_config_get_value`
-and `git_config_get_value_multi`. They both read values from an internal
-cache generated previously from reading the config files.
-
-`int git_config_get_value(const char *key, const char **value)`::
-
-       Finds the highest-priority value for the configuration variable `key`,
-       stores the pointer to it in `value` and returns 0. When the
-       configuration variable `key` is not found, returns 1 without touching
-       `value`. The caller should not free or modify `value`, as it is owned
-       by the cache.
-
-`const struct string_list *git_config_get_value_multi(const char *key)`::
-
-       Finds and returns the value list, sorted in order of increasing priority
-       for the configuration variable `key`. When the configuration variable
-       `key` is not found, returns NULL. The caller should not free or modify
-       the returned pointer, as it is owned by the cache.
-
-`void git_config_clear(void)`::
-
-       Resets and invalidates the config cache.
-
-The config API also provides type specific API functions which do conversion
-as well as retrieval for the queried variable, including:
-
-`int git_config_get_int(const char *key, int *dest)`::
-
-       Finds and parses the value to an integer for the configuration variable
-       `key`. Dies on error; otherwise, stores the value of the parsed integer in
-       `dest` and returns 0. When the configuration variable `key` is not found,
-       returns 1 without touching `dest`.
-
-`int git_config_get_ulong(const char *key, unsigned long *dest)`::
-
-       Similar to `git_config_get_int` but for unsigned longs.
-
-`int git_config_get_bool(const char *key, int *dest)`::
-
-       Finds and parses the value into a boolean value, for the configuration
-       variable `key` respecting keywords like "true" and "false". Integer
-       values are converted into true/false values (when they are non-zero or
-       zero, respectively). Other values cause a die(). If parsing is successful,
-       stores the value of the parsed result in `dest` and returns 0. When the
-       configuration variable `key` is not found, returns 1 without touching
-       `dest`.
-
-`int git_config_get_bool_or_int(const char *key, int *is_bool, int *dest)`::
-
-       Similar to `git_config_get_bool`, except that integers are copied as-is,
-       and `is_bool` flag is unset.
-
-`int git_config_get_maybe_bool(const char *key, int *dest)`::
-
-       Similar to `git_config_get_bool`, except that it returns -1 on error
-       rather than dying.
-
-`int git_config_get_string_const(const char *key, const char **dest)`::
-
-       Allocates and copies the retrieved string into the `dest` parameter for
-       the configuration variable `key`; if NULL string is given, prints an
-       error message and returns -1. When the configuration variable `key` is
-       not found, returns 1 without touching `dest`.
-
-`int git_config_get_string(const char *key, char **dest)`::
-
-       Similar to `git_config_get_string_const`, except that retrieved value
-       copied into the `dest` parameter is a mutable string.
-
-`int git_config_get_pathname(const char *key, const char **dest)`::
-
-       Similar to `git_config_get_string`, but expands `~` or `~user` into
-       the user's home directory when found at the beginning of the path.
-
-`git_die_config(const char *key, const char *err, ...)`::
-
-       First prints the error message specified by the caller in `err` and then
-       dies printing the line number and the file name of the highest priority
-       value for the configuration variable `key`.
-
-`void git_die_config_linenr(const char *key, const char *filename, int linenr)`::
-
-       Helper function which formats the die error message according to the
-       parameters entered. Used by `git_die_config()`. It can be used by callers
-       handling `git_config_get_value_multi()` to print the correct error message
-       for the desired value.
-
-See test-config.c for usage examples.
-
-Value Parsing Helpers
----------------------
-
-To aid in parsing string values, the config API provides callbacks with
-a number of helper functions, including:
-
-`git_config_int`::
-Parse the string to an integer, including unit factors. Dies on error;
-otherwise, returns the parsed result.
-
-`git_config_ulong`::
-Identical to `git_config_int`, but for unsigned longs.
-
-`git_config_bool`::
-Parse a string into a boolean value, respecting keywords like "true" and
-"false". Integer values are converted into true/false values (when they
-are non-zero or zero, respectively). Other values cause a die(). If
-parsing is successful, the return value is the result.
-
-`git_config_bool_or_int`::
-Same as `git_config_bool`, except that integers are returned as-is, and
-an `is_bool` flag is unset.
-
-`git_parse_maybe_bool`::
-Same as `git_config_bool`, except that it returns -1 on error rather
-than dying.
-
-`git_config_string`::
-Allocates and copies the value string into the `dest` parameter; if no
-string is given, prints an error message and returns -1.
-
-`git_config_pathname`::
-Similar to `git_config_string`, but expands `~` or `~user` into the
-user's home directory when found at the beginning of the path.
-
-Include Directives
-------------------
-
-By default, the config parser does not respect include directives.
-However, a caller can use the special `git_config_include` wrapper
-callback to support them. To do so, you simply wrap your "real" callback
-function and data pointer in a `struct config_include_data`, and pass
-the wrapper to the regular config-reading functions. For example:
-
--------------------------------------------
-int read_file_with_include(const char *file, config_fn_t fn, void *data)
-{
-       struct config_include_data inc = CONFIG_INCLUDE_INIT;
-       inc.fn = fn;
-       inc.data = data;
-       return git_config_from_file(git_config_include, file, &inc);
-}
--------------------------------------------
-
-`git_config` respects includes automatically. The lower-level
-`git_config_from_file` does not.
-
-Custom Configsets
------------------
-
-A `config_set` can be used to construct an in-memory cache for
-config-like files that the caller specifies (i.e., files like `.gitmodules`,
-`~/.gitconfig` etc.). For example,
-
-----------------------------------------
-struct config_set gm_config;
-git_configset_init(&gm_config);
-int b;
-/* we add config files to the config_set */
-git_configset_add_file(&gm_config, ".gitmodules");
-git_configset_add_file(&gm_config, ".gitmodules_alt");
-
-if (!git_configset_get_bool(gm_config, "submodule.frotz.ignore", &b)) {
-       /* hack hack hack */
-}
-
-/* when we are done with the configset */
-git_configset_clear(&gm_config);
-----------------------------------------
-
-Configset API provides functions for the above mentioned work flow, including:
-
-`void git_configset_init(struct config_set *cs)`::
-
-       Initializes the config_set `cs`.
-
-`int git_configset_add_file(struct config_set *cs, const char *filename)`::
-
-       Parses the file and adds the variable-value pairs to the `config_set`,
-       dies if there is an error in parsing the file. Returns 0 on success, or
-       -1 if the file does not exist or is inaccessible. The user has to decide
-       if he wants to free the incomplete configset or continue using it when
-       the function returns -1.
-
-`int git_configset_get_value(struct config_set *cs, const char *key, const char **value)`::
-
-       Finds the highest-priority value for the configuration variable `key`
-       and config set `cs`, stores the pointer to it in `value` and returns 0.
-       When the configuration variable `key` is not found, returns 1 without
-       touching `value`. The caller should not free or modify `value`, as it
-       is owned by the cache.
-
-`const struct string_list *git_configset_get_value_multi(struct config_set *cs, const char *key)`::
-
-       Finds and returns the value list, sorted in order of increasing priority
-       for the configuration variable `key` and config set `cs`. When the
-       configuration variable `key` is not found, returns NULL. The caller
-       should not free or modify the returned pointer, as it is owned by the cache.
-
-`void git_configset_clear(struct config_set *cs)`::
-
-       Clears `config_set` structure, removes all saved variable-value pairs.
-
-In addition to above functions, the `config_set` API provides type specific
-functions in the vein of `git_config_get_int` and family but with an extra
-parameter, pointer to struct `config_set`.
-They all behave similarly to the `git_config_get*()` family described in
-"Querying For Specific Variables" above.
-
-Writing Config Files
---------------------
-
-Git gives multiple entry points in the Config API to write config values to
-files namely `git_config_set_in_file` and `git_config_set`, which write to
-a specific config file or to `.git/config` respectively. They both take a
-key/value pair as parameter.
-In the end they both call `git_config_set_multivar_in_file` which takes four
-parameters:
-
-- the name of the file, as a string, to which key/value pairs will be written.
-
-- the name of key, as a string. This is in canonical "flat" form: the section,
-  subsection, and variable segments will be separated by dots, and the section
-  and variable segments will be all lowercase.
-  E.g., `core.ignorecase`, `diff.SomeType.textconv`.
-
-- the value of the variable, as a string. If value is equal to NULL, it will
-  remove the matching key from the config file.
-
-- the value regex, as a string. It will disregard key/value pairs where value
-  does not match.
-
-- a multi_replace value, as an int. If value is equal to zero, nothing or only
-  one matching key/value is replaced, else all matching key/values (regardless
-  how many) are removed, before the new pair is written.
-
-It returns 0 on success.
-
-Also, there are functions `git_config_rename_section` and
-`git_config_rename_section_in_file` with parameters `old_name` and `new_name`
-for renaming or removing sections in the config files. If NULL is passed
-through `new_name` parameter, the section will be removed from the config file.
diff --git a/Documentation/technical/api-grep.txt b/Documentation/technical/api-grep.txt
deleted file mode 100644 (file)
index a69cc89..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-grep API
-========
-
-Talk about <grep.h>, things like:
-
-* grep_buffer()
-
-(JC)
diff --git a/Documentation/technical/api-object-access.txt b/Documentation/technical/api-object-access.txt
deleted file mode 100644 (file)
index 5b29622..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-object access API
-=================
-
-Talk about <sha1-file.c> and <object.h> family, things like
-
-* read_sha1_file()
-* read_object_with_reference()
-* has_sha1_file()
-* write_sha1_file()
-* pretend_object_file()
-* lookup_{object,commit,tag,blob,tree}
-* parse_{object,commit,tag,blob,tree}
-* Use of object flags
-
-(JC, Shawn, Daniel, Dscho, Linus)
diff --git a/Documentation/technical/api-quote.txt b/Documentation/technical/api-quote.txt
deleted file mode 100644 (file)
index e8a1bce..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-quote API
-=========
-
-Talk about <quote.h>, things like
-
-* sq_quote and unquote
-* c_style quote and unquote
-* quoting for foreign languages
-
-(JC)
index fb060893931f2e74c5857c2d03b019e1fa138976..c409559b86b6957eba3bc2371ea0c55768422d12 100644 (file)
@@ -58,7 +58,7 @@ Functions
 
 Whenever a submodule configuration is parsed in `parse_submodule_config_option`
 via e.g. `gitmodules_config()`, it will overwrite the null_sha1 entry.
-So in the normal case, when HEAD:.gitmodules is parsed first and then overlayed
+So in the normal case, when HEAD:.gitmodules is parsed first and then overlaid
 with the repository configuration, the null_sha1 entry contains the local
 configuration of a submodule (e.g. consolidated values from local git
 configuration and the .gitmodules file in the worktree).
index a045dbe422ff000fa29216028a1cfc7c4b071515..17490b528c0c991f7a4d5085f45610f782a999ca 100644 (file)
@@ -178,7 +178,7 @@ describe the simplified forms.
 
 == Public API
 
-All Trace2 API functions send a messsage to all of the active
+All Trace2 API functions send a message to all of the active
 Trace2 Targets.  This section describes the set of available
 messages.
 
@@ -377,7 +377,7 @@ of `pthread_create()`.
        and the thread elapsed time.
 +
 This function must be called by the thread-proc before it returns
-(so that the coorect TLS data is used and cleaned up.  It should
+(so that the correct TLS data is used and cleaned up).  It should
 not be called by the caller of `pthread_join()`.
 
 === Region and Data Messages
@@ -406,7 +406,7 @@ The `label` field is an arbitrary label used to describe the activity
 being started, such as "read_recursive" or "do_read_index".
 +
 The `repo` field, if set, will be used to get the "repo-id", so that
-recursive oerations can be attributed to the correct repository.
+recursive operations can be attributed to the correct repository.
 
 `void trace2_region_leave(const char *category, const char *label, const struct repository *repo)`::
 
@@ -421,7 +421,7 @@ This function pops the region nesting stack on the current thread
 and reports the elapsed time of the stack frame.
 +
 The `category`, `label`, and `repo` fields are the same as above.
-The `category` and `label` do not need to match the correpsonding
+The `category` and `label` do not need to match the corresponding
 "region_enter" message, but it makes the data stream easier to
 understand.
 
@@ -816,7 +816,7 @@ with "?".
 Note that the session-id of the child process is not available to
 the current/spawning process, so the child's PID is reported here as
 a hint for post-processing.  (But it is only a hint because the child
-proces may be a shell script which doesn't have a session-id.)
+process may be a shell script which doesn't have a session-id.)
 +
 Note that the `t_rel` field contains the observed run time in seconds
 for the child process (starting before the fork/exec/spawn and
@@ -1176,7 +1176,7 @@ d0 | main                     | atexit       |     |  0.028809 |           |
 +
 Regions may be nested.  This causes messages to be indented in the
 PERF target, for example.
-Elapsed times are relative to the start of the correpsonding nesting
+Elapsed times are relative to the start of the corresponding nesting
 level as expected.  For example, if we add region message to:
 +
 ----------------
@@ -1371,7 +1371,7 @@ d0 | main                     | atexit       |     |  0.030027 |           |
 In this example, the preload region took 0.009122 seconds.  The 7 threads
 took between 0.006069 and 0.008947 seconds to work on their portion of
 the index.  Thread "th01" worked on 508 items at offset 0.  Thread "th02"
-worked on 508 items at offset 2032.  Thread "th04" worked on 508 itemts
+worked on 508 items at offset 2032.  Thread "th04" worked on 508 items
 at offset 508.
 +
 This example also shows that thread names are assigned in a racy manner
diff --git a/Documentation/technical/api-xdiff-interface.txt b/Documentation/technical/api-xdiff-interface.txt
deleted file mode 100644 (file)
index 6296eca..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-xdiff interface API
-===================
-
-Talk about our calling convention to xdiff library, including
-xdiff_emit_consume_fn.
-
-(Dscho, JC)
index 729fbcb32f8793d06da3a985bb6d8a299b3a15dd..1507117dc23dc12589130f8794c9db9413f467d5 100644 (file)
@@ -22,11 +22,11 @@ as "commit-graph" either in the .git/objects/info directory or in the info
 directory of an alternate.
 
 The commit-graph file stores the commit graph structure along with some
-extra metadata to speed up graph walks. By listing commit OIDs in lexi-
-cographic order, we can identify an integer position for each commit and
-refer to the parents of a commit using those integer positions. We use
-binary search to find initial commits and then use the integer positions
-for fast lookups during the walk.
+extra metadata to speed up graph walks. By listing commit OIDs in
+lexicographic order, we can identify an integer position for each commit
+and refer to the parents of a commit using those integer positions. We
+use binary search to find initial commits and then use the integer
+positions for fast lookups during the walk.
 
 A consumer may load the following info for a commit from the graph:
 
@@ -85,7 +85,7 @@ have generation number represented by the macro GENERATION_NUMBER_ZERO = 0.
 Since the commit-graph file is closed under reachability, we can guarantee
 the following weaker condition on all commits:
 
-    If A and B are commits with generation numbers N amd M, respectively,
+    If A and B are commits with generation numbers N and M, respectively,
     and N < M, then A cannot reach B.
 
 Note how the strict inequality differs from the inequality when we have
index 2ae8fa470ada107bd26302543ac93197e0e00c2e..8a4596bec110deba54d6c14679aad9555ae10b81 100644 (file)
@@ -531,7 +531,7 @@ Until Git protocol gains SHA-256 support, using SHA-256 based storage
 on public-facing Git servers is strongly discouraged. Once Git
 protocol gains SHA-256 support, SHA-256 based servers are likely not
 to support SHA-1 compatibility, to avoid what may be a very expensive
-hash reencode during clone and to encourage peers to modernize.
+hash re-encode during clone and to encourage peers to modernize.
 
 The design described here allows fetches by SHA-1 clients of a
 personal SHA-256 repository because it's not much more difficult than
@@ -602,7 +602,7 @@ git --output-format=sha1 log abac87a^{sha1}..f787cac^{sha256}
 
 Choice of Hash
 --------------
-In early 2005, around the time that Git was written,  Xiaoyun Wang,
+In early 2005, around the time that Git was written, Xiaoyun Wang,
 Yiqun Lisa Yin, and Hongbo Yu announced an attack finding SHA-1
 collisions in 2^69 operations. In August they published details.
 Luckily, no practical demonstrations of a collision in full SHA-1 were
index 7c4d67aa6a7f04f147cd89335ee0125d6912061d..faa25c5c527e72bb63ae66e834b20e7f3f1a519a 100644 (file)
@@ -318,7 +318,7 @@ The remaining data of each directory block is grouped by type:
 == End of Index Entry
 
   The End of Index Entry (EOIE) is used to locate the end of the variable
-  length index entries and the begining of the extensions. Code can take
+  length index entries and the beginning of the extensions. Code can take
   advantage of this to quickly locate the index extensions without having
   to parse through all of the index entries.
 
@@ -351,7 +351,7 @@ The remaining data of each directory block is grouped by type:
 
   - A number of index offset entries each consisting of:
 
-    - 32-bit offset from the begining of the file to the first cache entry
+    - 32-bit offset from the beginning of the file to the first cache entry
        in this block of entries.
 
     - 32-bit count of cache entries in this block
index c73e72de0e9ccef9f5ad5143ae9d208a3fef1a14..d5ce4eea8a19b96e84af2105cc52f8aebdf6164f 100644 (file)
@@ -644,7 +644,7 @@ update was successful, or 'ng [refname] [error]' if the update was not.
   command-ok        = PKT-LINE("ok" SP refname)
   command-fail      = PKT-LINE("ng" SP refname SP error-msg)
 
-  error-msg         = 1*(OCTECT) ; where not "ok"
+  error-msg         = 1*(OCTET) ; where not "ok"
 ----
 
 Updates can be unsuccessful for a number of reasons.  The reference can have
index 210373e258890d7204c6dcf4a1befbd31e21139b..a6034d56105edf50339632158937f098f499c95c 100644 (file)
@@ -32,7 +32,7 @@ if/when needed.
 
 A remote that can later provide the missing objects is called a
 promisor remote, as it promises to send the objects when
-requested. Initialy Git supported only one promisor remote, the origin
+requested. Initially Git supported only one promisor remote, the origin
 remote from which the user cloned and that was configured in the
 "extensions.partialClone" config option. Later support for more than
 one promisor remote has been implemented.
index 40f91f6b1ee1efcf87c2e694b298d1da68901b8a..7e3766cafb39ccc2f37ec59f0dcd728afb58e587 100644 (file)
@@ -252,7 +252,7 @@ A `fetch` request can take the following arguments:
     ofs-delta
        Indicate that the client understands PACKv2 with delta referring
        to its base by position in pack rather than by an oid.  That is,
-       they can read OBJ_OFS_DELTA (ake type 6) in a packfile.
+       they can read OBJ_OFS_DELTA (aka type 6) in a packfile.
 
 If the 'shallow' feature is advertised the following arguments can be
 included in the clients request as well as the potential addition of the
index aa22d7ace8930fab0d54adc0e613055ea1aca0f4..af5f9fc24f9343e73aca771d310570251fe2b922 100644 (file)
@@ -117,7 +117,7 @@ early A became C or B, a late X became Y or Z".  We can see there are
 4 combinations of ("B or C", "C or B") x ("X or Y", "Y or X").
 
 By sorting, the conflict is given its canonical name, namely, "an
-early part became B or C, a late part becames X or Y", and whenever
+early part became B or C, a late part became X or Y", and whenever
 any of these four patterns appear, and we can get to the same conflict
 and resolution that we saw earlier.
 
index 5048d9bff64fd113d884bb7d311e6ff0cd29ab68..22e8d83d98551298b769022f6fdd606225c34be5 100755 (executable)
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 GVF=GIT-VERSION-FILE
-DEF_VER=v2.24.0
+DEF_VER=v2.24.GIT
 
 LF='
 '
diff --git a/INSTALL b/INSTALL
index c39006e8e7e5c5be2114b79d50135dc08e3d1aaa..22c364f34f573c615d968a85374ba2c429b7fabd 100644 (file)
--- a/INSTALL
+++ b/INSTALL
@@ -109,15 +109,15 @@ Issues of note:
 
  - Git is reasonably self-sufficient, but does depend on a few external
    programs and libraries.  Git can be used without most of them by adding
-   the approriate "NO_<LIBRARY>=YesPlease" to the make command line or
+   the appropriate "NO_<LIBRARY>=YesPlease" to the make command line or
    config.mak file.
 
        - "zlib", the compression library. Git won't build without it.
 
        - "ssh" is used to push and pull over the net.
 
-       - A POSIX-compliant shell is required to run many scripts needed
-         for everyday use (e.g. "bisect", "pull").
+       - A POSIX-compliant shell is required to run some scripts needed
+         for everyday use (e.g. "bisect", "request-pull").
 
        - "Perl" version 5.8 or later is needed to use some of the
          features (e.g. preparing a partial commit using "git add -i/-p",
index 58b92af54b2af48c132bf3d7beb8feaf05a450ed..b7d7374dacebc8e4841f92ca42a05ee0dde43ba9 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -727,6 +727,7 @@ TEST_BUILTINS_OBJS += test-prio-queue.o
 TEST_BUILTINS_OBJS += test-progress.o
 TEST_BUILTINS_OBJS += test-reach.o
 TEST_BUILTINS_OBJS += test-read-cache.o
+TEST_BUILTINS_OBJS += test-read-graph.o
 TEST_BUILTINS_OBJS += test-read-midx.o
 TEST_BUILTINS_OBJS += test-ref-store.o
 TEST_BUILTINS_OBJS += test-regex.o
@@ -823,6 +824,7 @@ LIB_H := $(sort $(patsubst ./%,%,$(shell git ls-files '*.h' ':!t/' ':!Documentat
        -name '*.h' -print)))
 
 LIB_OBJS += abspath.o
+LIB_OBJS += add-interactive.o
 LIB_OBJS += advice.o
 LIB_OBJS += alias.o
 LIB_OBJS += alloc.o
index fc657e7d2f4b22ce71b2983bd33d9fca131c0c37..091dd024b349d6bc908371eddb7c594059c4fd70 120000 (symlink)
--- a/RelNotes
+++ b/RelNotes
@@ -1 +1 @@
-Documentation/RelNotes/2.24.0.txt
\ No newline at end of file
+Documentation/RelNotes/2.25.0.txt
\ No newline at end of file
diff --git a/add-interactive.c b/add-interactive.c
new file mode 100644 (file)
index 0000000..d6cb98c
--- /dev/null
@@ -0,0 +1,652 @@
+#include "cache.h"
+#include "add-interactive.h"
+#include "color.h"
+#include "config.h"
+#include "diffcore.h"
+#include "revision.h"
+#include "refs.h"
+#include "string-list.h"
+
+struct add_i_state {
+       struct repository *r;
+       int use_color;
+       char header_color[COLOR_MAXLEN];
+       char help_color[COLOR_MAXLEN];
+       char prompt_color[COLOR_MAXLEN];
+       char error_color[COLOR_MAXLEN];
+       char reset_color[COLOR_MAXLEN];
+};
+
+static void init_color(struct repository *r, struct add_i_state *s,
+                      const char *slot_name, char *dst,
+                      const char *default_color)
+{
+       char *key = xstrfmt("color.interactive.%s", slot_name);
+       const char *value;
+
+       if (!s->use_color)
+               dst[0] = '\0';
+       else if (repo_config_get_value(r, key, &value) ||
+                color_parse(value, dst))
+               strlcpy(dst, default_color, COLOR_MAXLEN);
+
+       free(key);
+}
+
+static void init_add_i_state(struct add_i_state *s, struct repository *r)
+{
+       const char *value;
+
+       s->r = r;
+
+       if (repo_config_get_value(r, "color.interactive", &value))
+               s->use_color = -1;
+       else
+               s->use_color =
+                       git_config_colorbool("color.interactive", value);
+       s->use_color = want_color(s->use_color);
+
+       init_color(r, s, "header", s->header_color, GIT_COLOR_BOLD);
+       init_color(r, s, "help", s->help_color, GIT_COLOR_BOLD_RED);
+       init_color(r, s, "prompt", s->prompt_color, GIT_COLOR_BOLD_BLUE);
+       init_color(r, s, "error", s->error_color, GIT_COLOR_BOLD_RED);
+       init_color(r, s, "reset", s->reset_color, GIT_COLOR_RESET);
+}
+
+/*
+ * A "prefix item list" is a list of items that are identified by a string, and
+ * a unique prefix (if any) is determined for each item.
+ *
+ * It is implemented in the form of a pair of `string_list`s, the first one
+ * duplicating the strings, with the `util` field pointing at a structure whose
+ * first field must be `size_t prefix_length`.
+ *
+ * That `prefix_length` field will be computed by `find_unique_prefixes()`; It
+ * will be set to zero if no valid, unique prefix could be found.
+ *
+ * The second `string_list` is called `sorted` and does _not_ duplicate the
+ * strings but simply reuses the first one's, with the `util` field pointing at
+ * the `string_item_list` of the first `string_list`. It  will be populated and
+ * sorted by `find_unique_prefixes()`.
+ */
+struct prefix_item_list {
+       struct string_list items;
+       struct string_list sorted;
+       size_t min_length, max_length;
+};
+#define PREFIX_ITEM_LIST_INIT \
+       { STRING_LIST_INIT_DUP, STRING_LIST_INIT_NODUP, 1, 4 }
+
+static void prefix_item_list_clear(struct prefix_item_list *list)
+{
+       string_list_clear(&list->items, 1);
+       string_list_clear(&list->sorted, 0);
+}
+
+static void extend_prefix_length(struct string_list_item *p,
+                                const char *other_string, size_t max_length)
+{
+       size_t *len = p->util;
+
+       if (!*len || memcmp(p->string, other_string, *len))
+               return;
+
+       for (;;) {
+               char c = p->string[*len];
+
+               /*
+                * Is `p` a strict prefix of `other`? Or have we exhausted the
+                * maximal length of the prefix? Or is the current character a
+                * multi-byte UTF-8 one? If so, there is no valid, unique
+                * prefix.
+                */
+               if (!c || ++*len > max_length || !isascii(c)) {
+                       *len = 0;
+                       break;
+               }
+
+               if (c != other_string[*len - 1])
+                       break;
+       }
+}
+
+static void find_unique_prefixes(struct prefix_item_list *list)
+{
+       size_t i;
+
+       if (list->sorted.nr == list->items.nr)
+               return;
+
+       string_list_clear(&list->sorted, 0);
+       /* Avoid reallocating incrementally */
+       list->sorted.items = xmalloc(st_mult(sizeof(*list->sorted.items),
+                                            list->items.nr));
+       list->sorted.nr = list->sorted.alloc = list->items.nr;
+
+       for (i = 0; i < list->items.nr; i++) {
+               list->sorted.items[i].string = list->items.items[i].string;
+               list->sorted.items[i].util = list->items.items + i;
+       }
+
+       string_list_sort(&list->sorted);
+
+       for (i = 0; i < list->sorted.nr; i++) {
+               struct string_list_item *sorted_item = list->sorted.items + i;
+               struct string_list_item *item = sorted_item->util;
+               size_t *len = item->util;
+
+               *len = 0;
+               while (*len < list->min_length) {
+                       char c = item->string[(*len)++];
+
+                       if (!c || !isascii(c)) {
+                               *len = 0;
+                               break;
+                       }
+               }
+
+               if (i > 0)
+                       extend_prefix_length(item, sorted_item[-1].string,
+                                            list->max_length);
+               if (i + 1 < list->sorted.nr)
+                       extend_prefix_length(item, sorted_item[1].string,
+                                            list->max_length);
+       }
+}
+
+static ssize_t find_unique(const char *string, struct prefix_item_list *list)
+{
+       int index = string_list_find_insert_index(&list->sorted, string, 1);
+       struct string_list_item *item;
+
+       if (list->items.nr != list->sorted.nr)
+               BUG("prefix_item_list in inconsistent state (%"PRIuMAX
+                   " vs %"PRIuMAX")",
+                   (uintmax_t)list->items.nr, (uintmax_t)list->sorted.nr);
+
+       if (index < 0)
+               item = list->sorted.items[-1 - index].util;
+       else if (index > 0 &&
+                starts_with(list->sorted.items[index - 1].string, string))
+               return -1;
+       else if (index + 1 < list->sorted.nr &&
+                starts_with(list->sorted.items[index + 1].string, string))
+               return -1;
+       else if (index < list->sorted.nr)
+               item = list->sorted.items[index].util;
+       else
+               return -1;
+       return item - list->items.items;
+}
+
+struct list_options {
+       int columns;
+       const char *header;
+       void (*print_item)(int i, struct string_list_item *item, void *print_item_data);
+       void *print_item_data;
+};
+
+static void list(struct add_i_state *s, struct string_list *list,
+                struct list_options *opts)
+{
+       int i, last_lf = 0;
+
+       if (!list->nr)
+               return;
+
+       if (opts->header)
+               color_fprintf_ln(stdout, s->header_color,
+                                "%s", opts->header);
+
+       for (i = 0; i < list->nr; i++) {
+               opts->print_item(i, list->items + i, opts->print_item_data);
+
+               if ((opts->columns) && ((i + 1) % (opts->columns))) {
+                       putchar('\t');
+                       last_lf = 0;
+               }
+               else {
+                       putchar('\n');
+                       last_lf = 1;
+               }
+       }
+
+       if (!last_lf)
+               putchar('\n');
+}
+struct list_and_choose_options {
+       struct list_options list_opts;
+
+       const char *prompt;
+       void (*print_help)(struct add_i_state *s);
+};
+
+#define LIST_AND_CHOOSE_ERROR (-1)
+#define LIST_AND_CHOOSE_QUIT  (-2)
+
+/*
+ * Returns the selected index.
+ *
+ * If an error occurred, returns `LIST_AND_CHOOSE_ERROR`. Upon EOF,
+ * `LIST_AND_CHOOSE_QUIT` is returned.
+ */
+static ssize_t list_and_choose(struct add_i_state *s,
+                              struct prefix_item_list *items,
+                              struct list_and_choose_options *opts)
+{
+       struct strbuf input = STRBUF_INIT;
+       ssize_t res = LIST_AND_CHOOSE_ERROR;
+
+       find_unique_prefixes(items);
+
+       for (;;) {
+               char *p;
+
+               strbuf_reset(&input);
+
+               list(s, &items->items, &opts->list_opts);
+
+               color_fprintf(stdout, s->prompt_color, "%s", opts->prompt);
+               fputs("> ", stdout);
+               fflush(stdout);
+
+               if (strbuf_getline(&input, stdin) == EOF) {
+                       putchar('\n');
+                       res = LIST_AND_CHOOSE_QUIT;
+                       break;
+               }
+               strbuf_trim(&input);
+
+               if (!input.len)
+                       break;
+
+               if (!strcmp(input.buf, "?")) {
+                       opts->print_help(s);
+                       continue;
+               }
+
+               p = input.buf;
+               for (;;) {
+                       size_t sep = strcspn(p, " \t\r\n,");
+                       ssize_t index = -1;
+
+                       if (!sep) {
+                               if (!*p)
+                                       break;
+                               p++;
+                               continue;
+                       }
+
+                       if (isdigit(*p)) {
+                               char *endp;
+                               index = strtoul(p, &endp, 10) - 1;
+                               if (endp != p + sep)
+                                       index = -1;
+                       }
+
+                       if (p[sep])
+                               p[sep++] = '\0';
+                       if (index < 0)
+                               index = find_unique(p, items);
+
+                       if (index < 0 || index >= items->items.nr)
+                               color_fprintf_ln(stdout, s->error_color,
+                                                _("Huh (%s)?"), p);
+                       else {
+                               res = index;
+                               break;
+                       }
+
+                       p += sep;
+               }
+
+               if (res != LIST_AND_CHOOSE_ERROR)
+                       break;
+       }
+
+       strbuf_release(&input);
+       return res;
+}
+
+struct adddel {
+       uintmax_t add, del;
+       unsigned seen:1, binary:1;
+};
+
+struct file_item {
+       struct adddel index, worktree;
+};
+
+static void add_file_item(struct string_list *files, const char *name)
+{
+       struct file_item *item = xcalloc(sizeof(*item), 1);
+
+       string_list_append(files, name)->util = item;
+}
+
+struct pathname_entry {
+       struct hashmap_entry ent;
+       const char *name;
+       struct file_item *item;
+};
+
+static int pathname_entry_cmp(const void *unused_cmp_data,
+                             const struct hashmap_entry *he1,
+                             const struct hashmap_entry *he2,
+                             const void *name)
+{
+       const struct pathname_entry *e1 =
+               container_of(he1, const struct pathname_entry, ent);
+       const struct pathname_entry *e2 =
+               container_of(he2, const struct pathname_entry, ent);
+
+       return strcmp(e1->name, name ? (const char *)name : e2->name);
+}
+
+struct collection_status {
+       enum { FROM_WORKTREE = 0, FROM_INDEX = 1 } phase;
+
+       const char *reference;
+
+       struct string_list *files;
+       struct hashmap file_map;
+};
+
+static void collect_changes_cb(struct diff_queue_struct *q,
+                              struct diff_options *options,
+                              void *data)
+{
+       struct collection_status *s = data;
+       struct diffstat_t stat = { 0 };
+       int i;
+
+       if (!q->nr)
+               return;
+
+       compute_diffstat(options, &stat, q);
+
+       for (i = 0; i < stat.nr; i++) {
+               const char *name = stat.files[i]->name;
+               int hash = strhash(name);
+               struct pathname_entry *entry;
+               struct file_item *file_item;
+               struct adddel *adddel;
+
+               entry = hashmap_get_entry_from_hash(&s->file_map, hash, name,
+                                                   struct pathname_entry, ent);
+               if (!entry) {
+                       add_file_item(s->files, name);
+
+                       entry = xcalloc(sizeof(*entry), 1);
+                       hashmap_entry_init(&entry->ent, hash);
+                       entry->name = s->files->items[s->files->nr - 1].string;
+                       entry->item = s->files->items[s->files->nr - 1].util;
+                       hashmap_add(&s->file_map, &entry->ent);
+               }
+
+               file_item = entry->item;
+               adddel = s->phase == FROM_INDEX ?
+                       &file_item->index : &file_item->worktree;
+               adddel->seen = 1;
+               adddel->add = stat.files[i]->added;
+               adddel->del = stat.files[i]->deleted;
+               if (stat.files[i]->is_binary)
+                       adddel->binary = 1;
+       }
+       free_diffstat_info(&stat);
+}
+
+static int get_modified_files(struct repository *r, struct string_list *files,
+                             const struct pathspec *ps)
+{
+       struct object_id head_oid;
+       int is_initial = !resolve_ref_unsafe("HEAD", RESOLVE_REF_READING,
+                                            &head_oid, NULL);
+       struct collection_status s = { FROM_WORKTREE };
+
+       if (discard_index(r->index) < 0 ||
+           repo_read_index_preload(r, ps, 0) < 0)
+               return error(_("could not read index"));
+
+       string_list_clear(files, 1);
+       s.files = files;
+       hashmap_init(&s.file_map, pathname_entry_cmp, NULL, 0);
+
+       for (s.phase = FROM_WORKTREE; s.phase <= FROM_INDEX; s.phase++) {
+               struct rev_info rev;
+               struct setup_revision_opt opt = { 0 };
+
+               opt.def = is_initial ?
+                       empty_tree_oid_hex() : oid_to_hex(&head_oid);
+
+               init_revisions(&rev, NULL);
+               setup_revisions(0, NULL, &rev, &opt);
+
+               rev.diffopt.output_format = DIFF_FORMAT_CALLBACK;
+               rev.diffopt.format_callback = collect_changes_cb;
+               rev.diffopt.format_callback_data = &s;
+
+               if (ps)
+                       copy_pathspec(&rev.prune_data, ps);
+
+               if (s.phase == FROM_INDEX)
+                       run_diff_index(&rev, 1);
+               else {
+                       rev.diffopt.flags.ignore_dirty_submodules = 1;
+                       run_diff_files(&rev, 0);
+               }
+       }
+       hashmap_free_entries(&s.file_map, struct pathname_entry, ent);
+
+       /* While the diffs are ordered already, we ran *two* diffs... */
+       string_list_sort(files);
+
+       return 0;
+}
+
+static void render_adddel(struct strbuf *buf,
+                               struct adddel *ad, const char *no_changes)
+{
+       if (ad->binary)
+               strbuf_addstr(buf, _("binary"));
+       else if (ad->seen)
+               strbuf_addf(buf, "+%"PRIuMAX"/-%"PRIuMAX,
+                           (uintmax_t)ad->add, (uintmax_t)ad->del);
+       else
+               strbuf_addstr(buf, no_changes);
+}
+
+/* filters out prefixes which have special meaning to list_and_choose() */
+static int is_valid_prefix(const char *prefix, size_t prefix_len)
+{
+       return prefix_len && prefix &&
+               /*
+                * We expect `prefix` to be NUL terminated, therefore this
+                * `strcspn()` call is okay, even if it might do much more
+                * work than strictly necessary.
+                */
+               strcspn(prefix, " \t\r\n,") >= prefix_len &&    /* separators */
+               *prefix != '-' &&                               /* deselection */
+               !isdigit(*prefix) &&                            /* selection */
+               (prefix_len != 1 ||
+                (*prefix != '*' &&                             /* "all" wildcard */
+                 *prefix != '?'));                             /* prompt help */
+}
+
+struct print_file_item_data {
+       const char *modified_fmt;
+       struct strbuf buf, index, worktree;
+};
+
+static void print_file_item(int i, struct string_list_item *item,
+                           void *print_file_item_data)
+{
+       struct file_item *c = item->util;
+       struct print_file_item_data *d = print_file_item_data;
+
+       strbuf_reset(&d->index);
+       strbuf_reset(&d->worktree);
+       strbuf_reset(&d->buf);
+
+       render_adddel(&d->worktree, &c->worktree, _("nothing"));
+       render_adddel(&d->index, &c->index, _("unchanged"));
+       strbuf_addf(&d->buf, d->modified_fmt,
+                   d->index.buf, d->worktree.buf, item->string);
+
+       printf(" %2d: %s", i + 1, d->buf.buf);
+}
+
+static int run_status(struct add_i_state *s, const struct pathspec *ps,
+                     struct string_list *files, struct list_options *opts)
+{
+       if (get_modified_files(s->r, files, ps) < 0)
+               return -1;
+
+       list(s, files, opts);
+       putchar('\n');
+
+       return 0;
+}
+
+static int run_help(struct add_i_state *s, const struct pathspec *unused_ps,
+                   struct string_list *unused_files,
+                   struct list_options *unused_opts)
+{
+       color_fprintf_ln(stdout, s->help_color, "status        - %s",
+                        _("show paths with changes"));
+       color_fprintf_ln(stdout, s->help_color, "update        - %s",
+                        _("add working tree state to the staged set of changes"));
+       color_fprintf_ln(stdout, s->help_color, "revert        - %s",
+                        _("revert staged set of changes back to the HEAD version"));
+       color_fprintf_ln(stdout, s->help_color, "patch         - %s",
+                        _("pick hunks and update selectively"));
+       color_fprintf_ln(stdout, s->help_color, "diff          - %s",
+                        _("view diff between HEAD and index"));
+       color_fprintf_ln(stdout, s->help_color, "add untracked - %s",
+                        _("add contents of untracked files to the staged set of changes"));
+
+       return 0;
+}
+
+typedef int (*command_t)(struct add_i_state *s, const struct pathspec *ps,
+                        struct string_list *files,
+                        struct list_options *opts);
+
+struct command_item {
+       size_t prefix_length;
+       command_t command;
+};
+
+struct print_command_item_data {
+       const char *color, *reset;
+};
+
+static void print_command_item(int i, struct string_list_item *item,
+                              void *print_command_item_data)
+{
+       struct print_command_item_data *d = print_command_item_data;
+       struct command_item *util = item->util;
+
+       if (!util->prefix_length ||
+           !is_valid_prefix(item->string, util->prefix_length))
+               printf(" %2d: %s", i + 1, item->string);
+       else
+               printf(" %2d: %s%.*s%s%s", i + 1,
+                      d->color, (int)util->prefix_length, item->string,
+                      d->reset, item->string + util->prefix_length);
+}
+
+static void command_prompt_help(struct add_i_state *s)
+{
+       const char *help_color = s->help_color;
+       color_fprintf_ln(stdout, help_color, "%s", _("Prompt help:"));
+       color_fprintf_ln(stdout, help_color, "1          - %s",
+                        _("select a numbered item"));
+       color_fprintf_ln(stdout, help_color, "foo        - %s",
+                        _("select item based on unique prefix"));
+       color_fprintf_ln(stdout, help_color, "           - %s",
+                        _("(empty) select nothing"));
+}
+
+int run_add_i(struct repository *r, const struct pathspec *ps)
+{
+       struct add_i_state s = { NULL };
+       struct print_command_item_data data = { "[", "]" };
+       struct list_and_choose_options main_loop_opts = {
+               { 4, N_("*** Commands ***"), print_command_item, &data },
+               N_("What now"), command_prompt_help
+       };
+       struct {
+               const char *string;
+               command_t command;
+       } command_list[] = {
+               { "status", run_status },
+               { "help", run_help },
+       };
+       struct prefix_item_list commands = PREFIX_ITEM_LIST_INIT;
+
+       struct print_file_item_data print_file_item_data = {
+               "%12s %12s %s", STRBUF_INIT, STRBUF_INIT, STRBUF_INIT
+       };
+       struct list_options opts = {
+               0, NULL, print_file_item, &print_file_item_data
+       };
+       struct strbuf header = STRBUF_INIT;
+       struct string_list files = STRING_LIST_INIT_DUP;
+       ssize_t i;
+       int res = 0;
+
+       for (i = 0; i < ARRAY_SIZE(command_list); i++) {
+               struct command_item *util = xcalloc(sizeof(*util), 1);
+               util->command = command_list[i].command;
+               string_list_append(&commands.items, command_list[i].string)
+                       ->util = util;
+       }
+
+       init_add_i_state(&s, r);
+
+       /*
+        * When color was asked for, use the prompt color for
+        * highlighting, otherwise use square brackets.
+        */
+       if (s.use_color) {
+               data.color = s.prompt_color;
+               data.reset = s.reset_color;
+       }
+
+       strbuf_addstr(&header, "      ");
+       strbuf_addf(&header, print_file_item_data.modified_fmt,
+                   _("staged"), _("unstaged"), _("path"));
+       opts.header = header.buf;
+
+       if (discard_index(r->index) < 0 ||
+           repo_read_index(r) < 0 ||
+           repo_refresh_and_write_index(r, REFRESH_QUIET, 0, 1,
+                                        NULL, NULL, NULL) < 0)
+               warning(_("could not refresh index"));
+
+       res = run_status(&s, ps, &files, &opts);
+
+       for (;;) {
+               i = list_and_choose(&s, &commands, &main_loop_opts);
+               if (i == LIST_AND_CHOOSE_QUIT) {
+                       printf(_("Bye.\n"));
+                       res = 0;
+                       break;
+               }
+               if (i != LIST_AND_CHOOSE_ERROR) {
+                       struct command_item *util =
+                               commands.items.items[i].util;
+                       res = util->command(&s, ps, &files, &opts);
+               }
+       }
+
+       string_list_clear(&files, 1);
+       strbuf_release(&print_file_item_data.buf);
+       strbuf_release(&print_file_item_data.index);
+       strbuf_release(&print_file_item_data.worktree);
+       strbuf_release(&header);
+       prefix_item_list_clear(&commands);
+
+       return res;
+}
diff --git a/add-interactive.h b/add-interactive.h
new file mode 100644 (file)
index 0000000..7043b87
--- /dev/null
@@ -0,0 +1,8 @@
+#ifndef ADD_INTERACTIVE_H
+#define ADD_INTERACTIVE_H
+
+struct repository;
+struct pathspec;
+int run_add_i(struct repository *r, const struct pathspec *ps);
+
+#endif
diff --git a/apply.c b/apply.c
index f8a046a6a5acbd36d14ca85b39e43b19c1d8e9c5..d57ac0d052b71a442575ae821d3beb3d64af6036 100644 (file)
--- a/apply.c
+++ b/apply.c
@@ -32,7 +32,7 @@ static void git_apply_config(void)
 {
        git_config_get_string_const("apply.whitespace", &apply_default_whitespace);
        git_config_get_string_const("apply.ignorewhitespace", &apply_default_ignorewhitespace);
-       git_config(git_default_config, NULL);
+       git_config(git_xmerge_config, NULL);
 }
 
 static int parse_whitespace_option(struct apply_state *state, const char *option)
@@ -4183,8 +4183,8 @@ static void show_rename_copy(struct patch *p)
                old_name = slash_old + 1;
                new_name = slash_new + 1;
        }
-       /* p->old_name thru old_name is the common prefix, and old_name and new_name
-        * through the end of names are renames
+       /* p->old_name through old_name is the common prefix, and old_name and
+        * new_name through the end of names are renames
         */
        if (old_name != p->old_name)
                printf(" %s %.*s{%s => %s} (%d%%)\n", renamecopy,
index dd18e5c9b67038307401e8d33970d5c424c03cb7..d4686d5218a7b938788e9d78b15125bbd1206b5f 100644 (file)
@@ -20,6 +20,7 @@
 #include "bulk-checkin.h"
 #include "argv-array.h"
 #include "submodule.h"
+#include "add-interactive.h"
 
 static const char * const builtin_add_usage[] = {
        N_("git add [<options>] [--] <pathspec>..."),
@@ -185,6 +186,16 @@ int run_add_interactive(const char *revision, const char *patch_mode,
 {
        int status, i;
        struct argv_array argv = ARGV_ARRAY_INIT;
+       int use_builtin_add_i =
+               git_env_bool("GIT_TEST_ADD_I_USE_BUILTIN", -1);
+
+       if (!patch_mode) {
+               if (use_builtin_add_i < 0)
+                       git_config_get_bool("add.interactive.usebuiltin",
+                                           &use_builtin_add_i);
+               if (use_builtin_add_i == 1)
+                       return !!run_add_i(the_repository, pathspec);
+       }
 
        argv_array_push(&argv, "add--interactive");
        if (patch_mode)
@@ -319,6 +330,7 @@ static int add_config(const char *var, const char *value, void *cb)
                ignore_add_errors = git_config_bool(var, value);
                return 0;
        }
+
        return git_default_config(var, value, cb);
 }
 
index e946ba6cd9dd20b02f89ac5eba5f5cf3bf390ea2..bf1cecdf3f9ebbbd9a42381566c7ca2d86355e0d 100644 (file)
@@ -319,18 +319,18 @@ static const char *format_time(timestamp_t time, const char *tz_str,
        return time_buf.buf;
 }
 
-#define OUTPUT_ANNOTATE_COMPAT 001
-#define OUTPUT_LONG_OBJECT_NAME        002
-#define OUTPUT_RAW_TIMESTAMP   004
-#define OUTPUT_PORCELAIN       010
-#define OUTPUT_SHOW_NAME       020
-#define OUTPUT_SHOW_NUMBER     040
-#define OUTPUT_SHOW_SCORE      0100
-#define OUTPUT_NO_AUTHOR       0200
-#define OUTPUT_SHOW_EMAIL      0400
-#define OUTPUT_LINE_PORCELAIN  01000
-#define OUTPUT_COLOR_LINE      02000
-#define OUTPUT_SHOW_AGE_WITH_COLOR     04000
+#define OUTPUT_ANNOTATE_COMPAT      (1U<<0)
+#define OUTPUT_LONG_OBJECT_NAME     (1U<<1)
+#define OUTPUT_RAW_TIMESTAMP        (1U<<2)
+#define OUTPUT_PORCELAIN            (1U<<3)
+#define OUTPUT_SHOW_NAME            (1U<<4)
+#define OUTPUT_SHOW_NUMBER          (1U<<5)
+#define OUTPUT_SHOW_SCORE           (1U<<6)
+#define OUTPUT_NO_AUTHOR            (1U<<7)
+#define OUTPUT_SHOW_EMAIL           (1U<<8)
+#define OUTPUT_LINE_PORCELAIN       (1U<<9)
+#define OUTPUT_COLOR_LINE           (1U<<10)
+#define OUTPUT_SHOW_AGE_WITH_COLOR  (1U<<11)
 
 static void emit_porcelain_details(struct blame_origin *suspect, int repeat)
 {
@@ -861,14 +861,6 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
                OPT_STRING_LIST(0, "ignore-revs-file", &ignore_revs_file_list, N_("file"), N_("Ignore revisions from <file>")),
                OPT_BIT(0, "color-lines", &output_option, N_("color redundant metadata from previous line differently"), OUTPUT_COLOR_LINE),
                OPT_BIT(0, "color-by-age", &output_option, N_("color lines by age"), OUTPUT_SHOW_AGE_WITH_COLOR),
-
-               /*
-                * The following two options are parsed by parse_revision_opt()
-                * and are only included here to get included in the "-h"
-                * output:
-                */
-               { OPTION_LOWLEVEL_CALLBACK, 0, "indent-heuristic", NULL, NULL, N_("Use an experimental heuristic to improve diffs"), PARSE_OPT_NOARG, NULL, 0, parse_opt_unknown_cb },
-
                OPT_BIT(0, "minimal", &xdl_opts, N_("Spend extra cycles to find better match"), XDF_NEED_MINIMAL),
                OPT_STRING('S', NULL, &revs_file, N_("file"), N_("Use revisions from <file> instead of calling git-rev-list")),
                OPT_STRING(0, "contents", &contents_from, N_("file"), N_("Use <file>'s contents as the final image")),
index 1ea4bfdfc19868d9cb89bb7014c138f145f7dd67..f049d27a14405d540902ba5d368a59d670f7f614 100644 (file)
@@ -1,4 +1,6 @@
 #include "builtin.h"
+#include "argv-array.h"
+#include "parse-options.h"
 #include "cache.h"
 #include "bundle.h"
 
  * bundle supporting "fetch", "pull", and "ls-remote".
  */
 
-static const char builtin_bundle_usage[] =
-  "git bundle create <file> <git-rev-list args>\n"
-  "   or: git bundle verify <file>\n"
-  "   or: git bundle list-heads <file> [<refname>...]\n"
-  "   or: git bundle unbundle <file> [<refname>...]";
+static const char * const builtin_bundle_usage[] = {
+  N_("git bundle create [<options>] <file> <git-rev-list args>"),
+  N_("git bundle verify [<options>] <file>"),
+  N_("git bundle list-heads <file> [<refname>...]"),
+  N_("git bundle unbundle <file> [<refname>...]"),
+  NULL
+};
 
-int cmd_bundle(int argc, const char **argv, const char *prefix)
-{
+static const char * const builtin_bundle_create_usage[] = {
+  N_("git bundle create [<options>] <file> <git-rev-list args>"),
+  NULL
+};
+
+static const char * const builtin_bundle_verify_usage[] = {
+  N_("git bundle verify [<options>] <file>"),
+  NULL
+};
+
+static const char * const builtin_bundle_list_heads_usage[] = {
+  N_("git bundle list-heads <file> [<refname>...]"),
+  NULL
+};
+
+static const char * const builtin_bundle_unbundle_usage[] = {
+  N_("git bundle unbundle <file> [<refname>...]"),
+  NULL
+};
+
+static int verbose;
+
+static int parse_options_cmd_bundle(int argc,
+               const char **argv,
+               const char* prefix,
+               const char * const usagestr[],
+               const struct option options[],
+               const char **bundle_file) {
+       int newargc;
+       newargc = parse_options(argc, argv, NULL, options, usagestr,
+                            PARSE_OPT_STOP_AT_NON_OPTION);
+       if (argc < 1)
+               usage_with_options(usagestr, options);
+       *bundle_file = prefix_filename(prefix, argv[0]);
+       return newargc;
+}
+
+static int cmd_bundle_create(int argc, const char **argv, const char *prefix) {
+       int all_progress_implied = 0;
+       int progress = isatty(STDERR_FILENO);
+       struct argv_array pack_opts;
+
+       struct option options[] = {
+               OPT_SET_INT('q', "quiet", &progress,
+                           N_("do not show progress meter"), 0),
+               OPT_SET_INT(0, "progress", &progress,
+                           N_("show progress meter"), 1),
+               OPT_SET_INT(0, "all-progress", &progress,
+                           N_("show progress meter during object writing phase"), 2),
+               OPT_BOOL(0, "all-progress-implied",
+                        &all_progress_implied,
+                        N_("similar to --all-progress when progress meter is shown")),
+               OPT_END()
+       };
+       const char* bundle_file;
+
+       argc = parse_options_cmd_bundle(argc, argv, prefix,
+                       builtin_bundle_create_usage, options, &bundle_file);
+       /* bundle internals use argv[1] as further parameters */
+
+       argv_array_init(&pack_opts);
+       if (progress == 0)
+               argv_array_push(&pack_opts, "--quiet");
+       else if (progress == 1)
+               argv_array_push(&pack_opts, "--progress");
+       else if (progress == 2)
+               argv_array_push(&pack_opts, "--all-progress");
+       if (progress && all_progress_implied)
+               argv_array_push(&pack_opts, "--all-progress-implied");
+
+       if (!startup_info->have_repository)
+               die(_("Need a repository to create a bundle."));
+       return !!create_bundle(the_repository, bundle_file, argc, argv, &pack_opts);
+}
+
+static int cmd_bundle_verify(int argc, const char **argv, const char *prefix) {
        struct bundle_header header;
-       const char *cmd, *bundle_file;
        int bundle_fd = -1;
+       int quiet = 0;
 
-       if (argc < 3)
-               usage(builtin_bundle_usage);
+       struct option options[] = {
+               OPT_BOOL('q', "quiet", &quiet,
+                           N_("do not show bundle details")),
+               OPT_END()
+       };
+       const char* bundle_file;
 
-       cmd = argv[1];
-       bundle_file = prefix_filename(prefix, argv[2]);
-       argc -= 2;
-       argv += 2;
+       argc = parse_options_cmd_bundle(argc, argv, prefix,
+                       builtin_bundle_verify_usage, options, &bundle_file);
+       /* bundle internals use argv[1] as further parameters */
 
        memset(&header, 0, sizeof(header));
-       if (strcmp(cmd, "create") && (bundle_fd =
-                               read_bundle_header(bundle_file, &header)) < 0)
+       if ((bundle_fd = read_bundle_header(bundle_file, &header)) < 0)
+               return 1;
+       close(bundle_fd);
+       if (verify_bundle(the_repository, &header, !quiet))
                return 1;
+       fprintf(stderr, _("%s is okay\n"), bundle_file);
+       return 0;
+}
 
-       if (!strcmp(cmd, "verify")) {
-               close(bundle_fd);
-               if (argc != 1) {
-                       usage(builtin_bundle_usage);
-                       return 1;
-               }
-               if (verify_bundle(the_repository, &header, 1))
-                       return 1;
-               fprintf(stderr, _("%s is okay\n"), bundle_file);
-               return 0;
-       }
-       if (!strcmp(cmd, "list-heads")) {
-               close(bundle_fd);
-               return !!list_bundle_refs(&header, argc, argv);
+static int cmd_bundle_list_heads(int argc, const char **argv, const char *prefix) {
+       struct bundle_header header;
+       int bundle_fd = -1;
+
+       struct option options[] = {
+               OPT_END()
+       };
+       const char* bundle_file;
+
+       argc = parse_options_cmd_bundle(argc, argv, prefix,
+                       builtin_bundle_list_heads_usage, options, &bundle_file);
+       /* bundle internals use argv[1] as further parameters */
+
+       memset(&header, 0, sizeof(header));
+       if ((bundle_fd = read_bundle_header(bundle_file, &header)) < 0)
+               return 1;
+       close(bundle_fd);
+       return !!list_bundle_refs(&header, argc, argv);
+}
+
+static int cmd_bundle_unbundle(int argc, const char **argv, const char *prefix) {
+       struct bundle_header header;
+       int bundle_fd = -1;
+
+       struct option options[] = {
+               OPT_END()
+       };
+       const char* bundle_file;
+
+       argc = parse_options_cmd_bundle(argc, argv, prefix,
+                       builtin_bundle_unbundle_usage, options, &bundle_file);
+       /* bundle internals use argv[1] as further parameters */
+
+       memset(&header, 0, sizeof(header));
+       if ((bundle_fd = read_bundle_header(bundle_file, &header)) < 0)
+               return 1;
+       if (!startup_info->have_repository)
+               die(_("Need a repository to unbundle."));
+       return !!unbundle(the_repository, &header, bundle_fd, 0) ||
+               list_bundle_refs(&header, argc, argv);
+}
+
+int cmd_bundle(int argc, const char **argv, const char *prefix)
+{
+       struct option options[] = {
+               OPT__VERBOSE(&verbose, N_("be verbose; must be placed before a subcommand")),
+               OPT_END()
+       };
+       int result;
+
+       argc = parse_options(argc, argv, prefix, options, builtin_bundle_usage,
+               PARSE_OPT_STOP_AT_NON_OPTION);
+
+       packet_trace_identity("bundle");
+
+       if (argc < 2)
+               usage_with_options(builtin_bundle_usage, options);
+
+       else if (!strcmp(argv[0], "create"))
+               result = cmd_bundle_create(argc, argv, prefix);
+       else if (!strcmp(argv[0], "verify"))
+               result = cmd_bundle_verify(argc, argv, prefix);
+       else if (!strcmp(argv[0], "list-heads"))
+               result = cmd_bundle_list_heads(argc, argv, prefix);
+       else if (!strcmp(argv[0], "unbundle"))
+               result = cmd_bundle_unbundle(argc, argv, prefix);
+       else {
+               error(_("Unknown subcommand: %s"), argv[0]);
+               usage_with_options(builtin_bundle_usage, options);
        }
-       if (!strcmp(cmd, "create")) {
-               if (argc < 2) {
-                       usage(builtin_bundle_usage);
-                       return 1;
-               }
-               if (!startup_info->have_repository)
-                       die(_("Need a repository to create a bundle."));
-               return !!create_bundle(the_repository, bundle_file, argc, argv);
-       } else if (!strcmp(cmd, "unbundle")) {
-               if (!startup_info->have_repository)
-                       die(_("Need a repository to unbundle."));
-               return !!unbundle(the_repository, &header, bundle_fd, 0) ||
-                       list_bundle_refs(&header, argc, argv);
-       } else
-               usage(builtin_bundle_usage);
+       return result ? 1 : 0;
 }
index c46ee29f0ad57958199804ab4fd4bef209bb0bc6..40c0bf25beb0f491700938cfe0c166a220e77c0e 100644 (file)
@@ -899,7 +899,7 @@ static void dissociate_from_references(void)
        free(alternates);
 }
 
-static int dir_exists(const char *path)
+static int path_exists(const char *path)
 {
        struct stat sb;
        return !stat(path, &sb);
@@ -927,8 +927,6 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 
        struct argv_array ref_prefixes = ARGV_ARRAY_INIT;
 
-       fetch_if_missing = 0;
-
        packet_trace_identity("clone");
        argc = parse_options(argc, argv, prefix, builtin_clone_options,
                             builtin_clone_usage, 0);
@@ -981,7 +979,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
                dir = guess_dir_name(repo_name, is_bundle, option_bare);
        strip_trailing_slashes(dir);
 
-       dest_exists = dir_exists(dir);
+       dest_exists = path_exists(dir);
        if (dest_exists && !is_empty_dir(dir))
                die(_("destination path '%s' already exists and is not "
                        "an empty directory."), dir);
@@ -992,7 +990,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
                work_tree = NULL;
        else {
                work_tree = getenv("GIT_WORK_TREE");
-               if (work_tree && dir_exists(work_tree))
+               if (work_tree && path_exists(work_tree))
                        die(_("working tree '%s' already exists."), work_tree);
        }
 
@@ -1020,7 +1018,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
        }
 
        if (real_git_dir) {
-               if (dir_exists(real_git_dir))
+               if (path_exists(real_git_dir))
                        junk_git_dir_flags |= REMOVE_DIR_KEEP_TOPLEVEL;
                junk_git_dir = real_git_dir;
        } else {
@@ -1265,7 +1263,6 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
        }
 
        junk_mode = JUNK_LEAVE_REPO;
-       fetch_if_missing = 1;
        err = checkout(submodule_progress);
 
        strbuf_release(&reflog_msg);
index addc8d4cc0895567b6f09902d3353b0a62a9507c..e0c6fc4bbf6d42d704c84b5c888282a4e163767f 100644 (file)
@@ -8,8 +8,6 @@
 #include "object-store.h"
 
 static char const * const builtin_commit_graph_usage[] = {
-       N_("git commit-graph [--object-dir <objdir>]"),
-       N_("git commit-graph read [--object-dir <objdir>]"),
        N_("git commit-graph verify [--object-dir <objdir>] [--shallow] [--[no-]progress]"),
        N_("git commit-graph write [--object-dir <objdir>] [--append|--split] [--reachable|--stdin-packs|--stdin-commits] [--[no-]progress] <split options>"),
        NULL
@@ -20,11 +18,6 @@ static const char * const builtin_commit_graph_verify_usage[] = {
        NULL
 };
 
-static const char * const builtin_commit_graph_read_usage[] = {
-       N_("git commit-graph read [--object-dir <objdir>]"),
-       NULL
-};
-
 static const char * const builtin_commit_graph_write_usage[] = {
        N_("git commit-graph write [--object-dir <objdir>] [--append|--split] [--reachable|--stdin-packs|--stdin-commits] [--[no-]progress] <split options>"),
        NULL
@@ -94,66 +87,6 @@ static int graph_verify(int argc, const char **argv)
        return verify_commit_graph(the_repository, graph, flags);
 }
 
-static int graph_read(int argc, const char **argv)
-{
-       struct commit_graph *graph = NULL;
-       char *graph_name;
-       int open_ok;
-       int fd;
-       struct stat st;
-
-       static struct option builtin_commit_graph_read_options[] = {
-               OPT_STRING(0, "object-dir", &opts.obj_dir,
-                       N_("dir"),
-                       N_("The object directory to store the graph")),
-               OPT_END(),
-       };
-
-       trace2_cmd_mode("read");
-
-       argc = parse_options(argc, argv, NULL,
-                            builtin_commit_graph_read_options,
-                            builtin_commit_graph_read_usage, 0);
-
-       if (!opts.obj_dir)
-               opts.obj_dir = get_object_directory();
-
-       graph_name = get_commit_graph_filename(opts.obj_dir);
-
-       open_ok = open_commit_graph(graph_name, &fd, &st);
-       if (!open_ok)
-               die_errno(_("Could not open commit-graph '%s'"), graph_name);
-
-       graph = load_commit_graph_one_fd_st(fd, &st);
-       if (!graph)
-               return 1;
-
-       FREE_AND_NULL(graph_name);
-
-       printf("header: %08x %d %d %d %d\n",
-               ntohl(*(uint32_t*)graph->data),
-               *(unsigned char*)(graph->data + 4),
-               *(unsigned char*)(graph->data + 5),
-               *(unsigned char*)(graph->data + 6),
-               *(unsigned char*)(graph->data + 7));
-       printf("num_commits: %u\n", graph->num_commits);
-       printf("chunks:");
-
-       if (graph->chunk_oid_fanout)
-               printf(" oid_fanout");
-       if (graph->chunk_oid_lookup)
-               printf(" oid_lookup");
-       if (graph->chunk_commit_data)
-               printf(" commit_metadata");
-       if (graph->chunk_extra_edges)
-               printf(" extra_edges");
-       printf("\n");
-
-       UNLEAK(graph);
-
-       return 0;
-}
-
 extern int read_replace_refs;
 static struct split_commit_graph_opts split_opts;
 
@@ -269,8 +202,6 @@ int cmd_commit_graph(int argc, const char **argv, const char *prefix)
        save_commit_buffer = 0;
 
        if (argc > 0) {
-               if (!strcmp(argv[0], "read"))
-                       return graph_read(argc, argv);
                if (!strcmp(argv[0], "verify"))
                        return graph_verify(argc, argv);
                if (!strcmp(argv[0], "write"))
index e588bc6ad3c66c46ba904c14e39b325055209734..294dc574cdda59f85e07a136ef1807f18710da3f 100644 (file)
@@ -1463,28 +1463,6 @@ static int git_commit_config(const char *k, const char *v, void *cb)
        return git_status_config(k, v, s);
 }
 
-int run_commit_hook(int editor_is_used, const char *index_file, const char *name, ...)
-{
-       struct argv_array hook_env = ARGV_ARRAY_INIT;
-       va_list args;
-       int ret;
-
-       argv_array_pushf(&hook_env, "GIT_INDEX_FILE=%s", index_file);
-
-       /*
-        * Let the hook know that no editor will be launched.
-        */
-       if (!editor_is_used)
-               argv_array_push(&hook_env, "GIT_EDITOR=:");
-
-       va_start(args, name);
-       ret = run_hook_ve(hook_env.argv,name, args);
-       va_end(args);
-       argv_array_clear(&hook_env);
-
-       return ret;
-}
-
 int cmd_commit(int argc, const char **argv, const char *prefix)
 {
        const char *argv_gc_auto[] = {"gc", "--auto", NULL};
index 863c858fde9afb3daac09c7ecf83cf44834574d5..46ce7c2710c02b1f64a40bf8cf43bdd7d6280cdd 100644 (file)
@@ -77,6 +77,7 @@ static struct refspec refmap = REFSPEC_INIT_FETCH;
 static struct list_objects_filter_options filter_options;
 static struct string_list server_options = STRING_LIST_INIT_DUP;
 static struct string_list negotiation_tip = STRING_LIST_INIT_NODUP;
+static int fetch_write_commit_graph = -1;
 
 static int git_fetch_config(const char *k, const char *v, void *cb)
 {
@@ -198,6 +199,8 @@ static struct option builtin_fetch_options[] = {
                 N_("run 'gc --auto' after fetching")),
        OPT_BOOL(0, "show-forced-updates", &fetch_show_forced_updates,
                 N_("check for forced-updates on all updated branches")),
+       OPT_BOOL(0, "write-commit-graph", &fetch_write_commit_graph,
+                N_("write the commit-graph after fetching")),
        OPT_END()
 };
 
@@ -1074,7 +1077,8 @@ static int check_exist_and_connected(struct ref *ref_map)
         * we need all direct targets to exist.
         */
        for (r = rm; r; r = r->next) {
-               if (!has_object_file(&r->old_oid))
+               if (!has_object_file_with_flags(&r->old_oid,
+                                               OBJECT_INFO_SKIP_FETCH_OBJECT))
                        return -1;
        }
 
@@ -1400,7 +1404,7 @@ static int do_fetch(struct transport *transport,
 
                /*
                 * We're setting the upstream configuration for the
-                * current branch. The relevent upstream is the
+                * current branch. The relevant upstream is the
                 * fetched branch that is meant to be merged with the
                 * current one, i.e. the one fetched to FETCH_HEAD.
                 *
@@ -1411,7 +1415,7 @@ static int do_fetch(struct transport *transport,
                for (rm = ref_map; rm; rm = rm->next) {
                        if (!rm->peer_ref) {
                                if (source_ref) {
-                                       warning(_("multiple branch detected, incompatible with --set-upstream"));
+                                       warning(_("multiple branches detected, incompatible with --set-upstream"));
                                        goto skip;
                                } else {
                                        source_ref = rm;
@@ -1599,7 +1603,8 @@ static int fetch_multiple(struct string_list *list, int max_children)
                        return errcode;
        }
 
-       argv_array_pushl(&argv, "fetch", "--append", "--no-auto-gc", NULL);
+       argv_array_pushl(&argv, "fetch", "--append", "--no-auto-gc",
+                       "--no-write-commit-graph", NULL);
        add_options_to_argv(&argv);
 
        if (max_children != 1 && list->nr != 1) {
@@ -1822,8 +1827,6 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
                }
        }
 
-       fetch_if_missing = 0;
-
        if (remote) {
                if (filter_options.choice || has_promisor_remote())
                        fetch_one_setup_partial(remote);
@@ -1865,7 +1868,9 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
        string_list_clear(&list, 0);
 
        prepare_repo_settings(the_repository);
-       if (the_repository->settings.fetch_write_commit_graph) {
+       if (fetch_write_commit_graph > 0 ||
+           (fetch_write_commit_graph < 0 &&
+            the_repository->settings.fetch_write_commit_graph)) {
                int commit_graph_flags = COMMIT_GRAPH_WRITE_SPLIT;
                struct split_commit_graph_opts split_opts;
                memset(&split_opts, 0, sizeof(struct split_commit_graph_opts));
index 18403a94fa4224e0d108dec3cfeb8e9cd24481b8..8d13794b1412c82f3a19331548b4b2cb26da349b 100644 (file)
@@ -50,40 +50,20 @@ static int name_objects;
 #define ERROR_REFS 010
 #define ERROR_COMMIT_GRAPH 020
 
-static const char *describe_object(struct object *obj)
+static const char *describe_object(const struct object_id *oid)
 {
-       static struct strbuf bufs[] = {
-               STRBUF_INIT, STRBUF_INIT, STRBUF_INIT, STRBUF_INIT
-       };
-       static int b = 0;
-       struct strbuf *buf;
-       char *name = NULL;
-
-       if (name_objects)
-               name = lookup_decoration(fsck_walk_options.object_names, obj);
-
-       buf = bufs + b;
-       b = (b + 1) % ARRAY_SIZE(bufs);
-       strbuf_reset(buf);
-       strbuf_addstr(buf, oid_to_hex(&obj->oid));
-       if (name)
-               strbuf_addf(buf, " (%s)", name);
-
-       return buf->buf;
+       return fsck_describe_object(&fsck_walk_options, oid);
 }
 
-static const char *printable_type(struct object *obj)
+static const char *printable_type(const struct object_id *oid,
+                                 enum object_type type)
 {
        const char *ret;
 
-       if (obj->type == OBJ_NONE) {
-               enum object_type type = oid_object_info(the_repository,
-                                                       &obj->oid, NULL);
-               if (type > 0)
-                       object_as_type(the_repository, obj, type, 0);
-       }
+       if (type == OBJ_NONE)
+               type = oid_object_info(the_repository, oid, NULL);
 
-       ret = type_name(obj->type);
+       ret = type_name(type);
        if (!ret)
                ret = _("unknown");
 
@@ -118,26 +98,32 @@ static int objerror(struct object *obj, const char *err)
        errors_found |= ERROR_OBJECT;
        /* TRANSLATORS: e.g. error in tree 01bfda: <more explanation> */
        fprintf_ln(stderr, _("error in %s %s: %s"),
-                  printable_type(obj), describe_object(obj), err);
+                  printable_type(&obj->oid, obj->type),
+                  describe_object(&obj->oid), err);
        return -1;
 }
 
 static int fsck_error_func(struct fsck_options *o,
-       struct object *obj, int type, const char *message)
+                          const struct object_id *oid,
+                          enum object_type object_type,
+                          int msg_type, const char *message)
 {
-       switch (type) {
+       switch (msg_type) {
        case FSCK_WARN:
                /* TRANSLATORS: e.g. warning in tree 01bfda: <more explanation> */
                fprintf_ln(stderr, _("warning in %s %s: %s"),
-                          printable_type(obj), describe_object(obj), message);
+                          printable_type(oid, object_type),
+                          describe_object(oid), message);
                return 0;
        case FSCK_ERROR:
                /* TRANSLATORS: e.g. error in tree 01bfda: <more explanation> */
                fprintf_ln(stderr, _("error in %s %s: %s"),
-                          printable_type(obj), describe_object(obj), message);
+                          printable_type(oid, object_type),
+                          describe_object(oid), message);
                return 1;
        default:
-               BUG("%d (FSCK_IGNORE?) should never trigger this callback", type);
+               BUG("%d (FSCK_IGNORE?) should never trigger this callback",
+                   msg_type);
        }
 }
 
@@ -155,7 +141,8 @@ static int mark_object(struct object *obj, int type, void *data, struct fsck_opt
        if (!obj) {
                /* ... these references to parent->fld are safe here */
                printf_ln(_("broken link from %7s %s"),
-                         printable_type(parent), describe_object(parent));
+                         printable_type(&parent->oid, parent->type),
+                         describe_object(&parent->oid));
                printf_ln(_("broken link from %7s %s"),
                          (type == OBJ_ANY ? _("unknown") : type_name(type)),
                          _("unknown"));
@@ -183,10 +170,10 @@ static int mark_object(struct object *obj, int type, void *data, struct fsck_opt
                if (parent && !has_object_file(&obj->oid)) {
                        printf_ln(_("broken link from %7s %s\n"
                                    "              to %7s %s"),
-                                 printable_type(parent),
-                                 describe_object(parent),
-                                 printable_type(obj),
-                                 describe_object(obj));
+                                 printable_type(&parent->oid, parent->type),
+                                 describe_object(&parent->oid),
+                                 printable_type(&obj->oid, obj->type),
+                                 describe_object(&obj->oid));
                        errors_found |= ERROR_REACHABLE;
                }
                return 1;
@@ -292,8 +279,9 @@ static void check_reachable_object(struct object *obj)
                        return;
                if (has_object_pack(&obj->oid))
                        return; /* it is in pack - forget about it */
-               printf_ln(_("missing %s %s"), printable_type(obj),
-                         describe_object(obj));
+               printf_ln(_("missing %s %s"),
+                         printable_type(&obj->oid, obj->type),
+                         describe_object(&obj->oid));
                errors_found |= ERROR_REACHABLE;
                return;
        }
@@ -318,8 +306,9 @@ static void check_unreachable_object(struct object *obj)
         * since this is something that is prunable.
         */
        if (show_unreachable) {
-               printf_ln(_("unreachable %s %s"), printable_type(obj),
-                         describe_object(obj));
+               printf_ln(_("unreachable %s %s"),
+                         printable_type(&obj->oid, obj->type),
+                         describe_object(&obj->oid));
                return;
        }
 
@@ -337,12 +326,13 @@ static void check_unreachable_object(struct object *obj)
         */
        if (!(obj->flags & USED)) {
                if (show_dangling)
-                       printf_ln(_("dangling %s %s"), printable_type(obj),
-                                 describe_object(obj));
+                       printf_ln(_("dangling %s %s"),
+                                 printable_type(&obj->oid, obj->type),
+                                 describe_object(&obj->oid));
                if (write_lost_and_found) {
                        char *filename = git_pathdup("lost-found/%s/%s",
                                obj->type == OBJ_COMMIT ? "commit" : "other",
-                               describe_object(obj));
+                               describe_object(&obj->oid));
                        FILE *f;
 
                        if (safe_create_leading_directories_const(filename)) {
@@ -355,7 +345,7 @@ static void check_unreachable_object(struct object *obj)
                                if (stream_blob_to_fd(fileno(f), &obj->oid, NULL, 1))
                                        die_errno(_("could not write '%s'"), filename);
                        } else
-                               fprintf(f, "%s\n", describe_object(obj));
+                               fprintf(f, "%s\n", describe_object(&obj->oid));
                        if (fclose(f))
                                die_errno(_("could not finish '%s'"),
                                          filename);
@@ -374,7 +364,7 @@ static void check_unreachable_object(struct object *obj)
 static void check_object(struct object *obj)
 {
        if (verbose)
-               fprintf_ln(stderr, _("Checking %s"), describe_object(obj));
+               fprintf_ln(stderr, _("Checking %s"), describe_object(&obj->oid));
 
        if (obj->flags & REACHABLE)
                check_reachable_object(obj);
@@ -432,7 +422,8 @@ static int fsck_obj(struct object *obj, void *buffer, unsigned long size)
 
        if (verbose)
                fprintf_ln(stderr, _("Checking %s %s"),
-                          printable_type(obj), describe_object(obj));
+                          printable_type(&obj->oid, obj->type),
+                          describe_object(&obj->oid));
 
        if (fsck_walk(obj, NULL, &fsck_obj_options))
                objerror(obj, _("broken links"));
@@ -445,7 +436,7 @@ static int fsck_obj(struct object *obj, void *buffer, unsigned long size)
 
                if (!commit->parents && show_root)
                        printf_ln(_("root %s"),
-                                 describe_object(&commit->object));
+                                 describe_object(&commit->object.oid));
        }
 
        if (obj->type == OBJ_TAG) {
@@ -453,10 +444,10 @@ static int fsck_obj(struct object *obj, void *buffer, unsigned long size)
 
                if (show_tags && tag->tagged) {
                        printf_ln(_("tagged %s %s (%s) in %s"),
-                                 printable_type(tag->tagged),
-                                 describe_object(tag->tagged),
+                                 printable_type(&tag->tagged->oid, tag->tagged->type),
+                                 describe_object(&tag->tagged->oid),
                                  tag->tag,
-                                 describe_object(&tag->object));
+                                 describe_object(&tag->object.oid));
                }
        }
 
@@ -499,10 +490,10 @@ static void fsck_handle_reflog_oid(const char *refname, struct object_id *oid,
        if (!is_null_oid(oid)) {
                obj = lookup_object(the_repository, oid);
                if (obj && (obj->flags & HAS_OBJ)) {
-                       if (timestamp && name_objects)
-                               add_decoration(fsck_walk_options.object_names,
-                                       obj,
-                                       xstrfmt("%s@{%"PRItime"}", refname, timestamp));
+                       if (timestamp)
+                               fsck_put_object_name(&fsck_walk_options, oid,
+                                                    "%s@{%"PRItime"}",
+                                                    refname, timestamp);
                        obj->flags |= USED;
                        mark_object_reachable(obj);
                } else if (!is_promisor_object(oid)) {
@@ -566,9 +557,8 @@ static int fsck_handle_ref(const char *refname, const struct object_id *oid,
        }
        default_refs++;
        obj->flags |= USED;
-       if (name_objects)
-               add_decoration(fsck_walk_options.object_names,
-                       obj, xstrdup(refname));
+       fsck_put_object_name(&fsck_walk_options,
+                            oid, "%s", refname);
        mark_object_reachable(obj);
 
        return 0;
@@ -742,9 +732,7 @@ static int fsck_cache_tree(struct cache_tree *it)
                        return 1;
                }
                obj->flags |= USED;
-               if (name_objects)
-                       add_decoration(fsck_walk_options.object_names,
-                               obj, xstrdup(":"));
+               fsck_put_object_name(&fsck_walk_options, &it->oid, ":");
                mark_object_reachable(obj);
                if (obj->type != OBJ_TREE)
                        err |= objerror(obj, _("non-tree in cache-tree"));
@@ -830,8 +818,7 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
        }
 
        if (name_objects)
-               fsck_walk_options.object_names =
-                       xcalloc(1, sizeof(struct decoration));
+               fsck_enable_object_names(&fsck_walk_options);
 
        git_config(fsck_config, NULL);
 
@@ -890,9 +877,8 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
                        }
 
                        obj->flags |= USED;
-                       if (name_objects)
-                               add_decoration(fsck_walk_options.object_names,
-                                       obj, xstrdup(arg));
+                       fsck_put_object_name(&fsck_walk_options, &oid,
+                                            "%s", arg);
                        mark_object_reachable(obj);
                        continue;
                }
@@ -928,10 +914,8 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
                                continue;
                        obj = &blob->object;
                        obj->flags |= USED;
-                       if (name_objects)
-                               add_decoration(fsck_walk_options.object_names,
-                                       obj,
-                                       xstrfmt(":%s", active_cache[i]->name));
+                       fsck_put_object_name(&fsck_walk_options, &obj->oid,
+                                            ":%s", active_cache[i]->name);
                        mark_object_reachable(obj);
                }
                if (active_cache_tree)
index fadb45489f34a760f4c8f6c96c3cc5a5c5c115bd..3f76bf4aa73d1a05d7a6350da71ea628819ea30a 100644 (file)
@@ -458,7 +458,7 @@ static const char *lock_repo_for_gc(int force, pid_t* ret_pid)
 /*
  * Returns 0 if there was no previous error and gc can proceed, 1 if
  * gc should not proceed due to an error in the last run. Prints a
- * message and returns -1 if an error occured while reading gc.log
+ * message and returns -1 if an error occurred while reading gc.log
  */
 static int report_last_gc_error(void)
 {
@@ -601,7 +601,7 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
                if (detach_auto) {
                        int ret = report_last_gc_error();
                        if (ret < 0)
-                               /* an I/O error occured, already reported */
+                               /* an I/O error occurred, already reported */
                                exit(128);
                        if (ret == 1)
                                /* Last gc --auto failed. Skip this one. */
index 89873d2dc2a3dc1e0e50a97e14bf8b1894231593..a26f223ab4ad9ae8cfe9405fb5ff62dd48509775 100644 (file)
@@ -37,6 +37,7 @@
 #include "range-diff.h"
 
 #define MAIL_DEFAULT_WRAP 72
+#define COVER_FROM_AUTO_MAX_SUBJECT_LEN 100
 
 /* Set a default date-time format for git log ("log.date" config variable) */
 static const char *default_date_mode = NULL;
@@ -765,23 +766,51 @@ static void add_header(const char *value)
        item->string[len] = '\0';
 }
 
-#define THREAD_SHALLOW 1
-#define THREAD_DEEP 2
-static int thread;
+enum cover_setting {
+       COVER_UNSET,
+       COVER_OFF,
+       COVER_ON,
+       COVER_AUTO
+};
+
+enum thread_level {
+       THREAD_UNSET,
+       THREAD_SHALLOW,
+       THREAD_DEEP
+};
+
+enum cover_from_description {
+       COVER_FROM_NONE,
+       COVER_FROM_MESSAGE,
+       COVER_FROM_SUBJECT,
+       COVER_FROM_AUTO
+};
+
+static enum thread_level thread;
 static int do_signoff;
 static int base_auto;
 static char *from;
 static const char *signature = git_version_string;
 static const char *signature_file;
-static int config_cover_letter;
+static enum cover_setting config_cover_letter;
 static const char *config_output_directory;
+static enum cover_from_description cover_from_description_mode = COVER_FROM_MESSAGE;
 
-enum {
-       COVER_UNSET,
-       COVER_OFF,
-       COVER_ON,
-       COVER_AUTO
-};
+static enum cover_from_description parse_cover_from_description(const char *arg)
+{
+       if (!arg || !strcmp(arg, "default"))
+               return COVER_FROM_MESSAGE;
+       else if (!strcmp(arg, "none"))
+               return COVER_FROM_NONE;
+       else if (!strcmp(arg, "message"))
+               return COVER_FROM_MESSAGE;
+       else if (!strcmp(arg, "subject"))
+               return COVER_FROM_SUBJECT;
+       else if (!strcmp(arg, "auto"))
+               return COVER_FROM_AUTO;
+       else
+               die(_("%s: invalid cover from description mode"), arg);
+}
 
 static int git_format_config(const char *var, const char *value, void *cb)
 {
@@ -836,7 +865,7 @@ static int git_format_config(const char *var, const char *value, void *cb)
                        thread = THREAD_SHALLOW;
                        return 0;
                }
-               thread = git_config_bool(var, value) && THREAD_SHALLOW;
+               thread = git_config_bool(var, value) ? THREAD_SHALLOW : THREAD_UNSET;
                return 0;
        }
        if (!strcmp(var, "format.signoff")) {
@@ -888,6 +917,10 @@ static int git_format_config(const char *var, const char *value, void *cb)
                }
                return 0;
        }
+       if (!strcmp(var, "format.coverfromdescription")) {
+               cover_from_description_mode = parse_cover_from_description(value);
+               return 0;
+       }
 
        return git_log_config(var, value, cb);
 }
@@ -994,20 +1027,6 @@ static void print_signature(FILE *file)
        putc('\n', file);
 }
 
-static void add_branch_description(struct strbuf *buf, const char *branch_name)
-{
-       struct strbuf desc = STRBUF_INIT;
-       if (!branch_name || !*branch_name)
-               return;
-       read_branch_desc(&desc, branch_name);
-       if (desc.len) {
-               strbuf_addch(buf, '\n');
-               strbuf_addbuf(buf, &desc);
-               strbuf_addch(buf, '\n');
-       }
-       strbuf_release(&desc);
-}
-
 static char *find_branch_name(struct rev_info *rev)
 {
        int i, positive = -1;
@@ -1054,6 +1073,44 @@ static void show_diffstat(struct rev_info *rev,
        fprintf(rev->diffopt.file, "\n");
 }
 
+static void prepare_cover_text(struct pretty_print_context *pp,
+                              const char *branch_name,
+                              struct strbuf *sb,
+                              const char *encoding,
+                              int need_8bit_cte)
+{
+       const char *subject = "*** SUBJECT HERE ***";
+       const char *body = "*** BLURB HERE ***";
+       struct strbuf description_sb = STRBUF_INIT;
+       struct strbuf subject_sb = STRBUF_INIT;
+
+       if (cover_from_description_mode == COVER_FROM_NONE)
+               goto do_pp;
+
+       if (branch_name && *branch_name)
+               read_branch_desc(&description_sb, branch_name);
+       if (!description_sb.len)
+               goto do_pp;
+
+       if (cover_from_description_mode == COVER_FROM_SUBJECT ||
+                       cover_from_description_mode == COVER_FROM_AUTO)
+               body = format_subject(&subject_sb, description_sb.buf, " ");
+
+       if (cover_from_description_mode == COVER_FROM_MESSAGE ||
+                       (cover_from_description_mode == COVER_FROM_AUTO &&
+                        subject_sb.len > COVER_FROM_AUTO_MAX_SUBJECT_LEN))
+               body = description_sb.buf;
+       else
+               subject = subject_sb.buf;
+
+do_pp:
+       pp_title_line(pp, &subject, sb, encoding, need_8bit_cte);
+       pp_remainder(pp, &body, sb, 0);
+
+       strbuf_release(&description_sb);
+       strbuf_release(&subject_sb);
+}
+
 static void make_cover_letter(struct rev_info *rev, int use_stdout,
                              struct commit *origin,
                              int nr, struct commit **list,
@@ -1061,8 +1118,6 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,
                              int quiet)
 {
        const char *committer;
-       const char *body = "*** SUBJECT HERE ***\n\n*** BLURB HERE ***\n";
-       const char *msg;
        struct shortlog log;
        struct strbuf sb = STRBUF_INIT;
        int i;
@@ -1092,15 +1147,12 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,
        if (!branch_name)
                branch_name = find_branch_name(rev);
 
-       msg = body;
        pp.fmt = CMIT_FMT_EMAIL;
        pp.date_mode.type = DATE_RFC2822;
        pp.rev = rev;
        pp.print_email_subject = 1;
        pp_user_info(&pp, NULL, &sb, committer, encoding);
-       pp_title_line(&pp, &msg, &sb, encoding, need_8bit_cte);
-       pp_remainder(&pp, &msg, &sb, 0);
-       add_branch_description(&sb, branch_name);
+       prepare_cover_text(&pp, branch_name, &sb, encoding, need_8bit_cte);
        fprintf(rev->diffopt.file, "%s\n", sb.buf);
 
        strbuf_release(&sb);
@@ -1249,9 +1301,9 @@ static int output_directory_callback(const struct option *opt, const char *arg,
 
 static int thread_callback(const struct option *opt, const char *arg, int unset)
 {
-       int *thread = (int *)opt->value;
+       enum thread_level *thread = (enum thread_level *)opt->value;
        if (unset)
-               *thread = 0;
+               *thread = THREAD_UNSET;
        else if (!arg || !strcmp(arg, "shallow"))
                *thread = THREAD_SHALLOW;
        else if (!strcmp(arg, "deep"))
@@ -1542,6 +1594,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
        int use_patch_format = 0;
        int quiet = 0;
        int reroll_count = -1;
+       char *cover_from_description_arg = NULL;
        char *branch_name = NULL;
        char *base_commit = NULL;
        struct base_tree_info bases;
@@ -1578,6 +1631,9 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                { OPTION_CALLBACK, 0, "rfc", &rev, NULL,
                            N_("Use [RFC PATCH] instead of [PATCH]"),
                            PARSE_OPT_NOARG | PARSE_OPT_NONEG, rfc_callback },
+               OPT_STRING(0, "cover-from-description", &cover_from_description_arg,
+                           N_("cover-from-description-mode"),
+                           N_("generate parts of a cover letter based on a branch's description")),
                { OPTION_CALLBACK, 0, "subject-prefix", &rev, N_("prefix"),
                            N_("Use [<prefix>] instead of [PATCH]"),
                            PARSE_OPT_NONEG, subject_prefix_callback },
@@ -1669,6 +1725,9 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                             PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN |
                             PARSE_OPT_KEEP_DASHDASH);
 
+       if (cover_from_description_arg)
+               cover_from_description_mode = parse_cover_from_description(cover_from_description_arg);
+
        if (0 < reroll_count) {
                struct strbuf sprefix = STRBUF_INIT;
                strbuf_addf(&sprefix, "%s v%d",
index b1ea1a6aa17724915a529a882831640aec7ea7b8..5bf88cd2a8e2c338bb63590b42e9388edbf1e2d7 100644 (file)
@@ -6,21 +6,25 @@
 #include "trace2.h"
 
 static char const * const builtin_multi_pack_index_usage[] = {
-       N_("git multi-pack-index [--object-dir=<dir>] (write|verify|expire|repack --batch-size=<size>)"),
+       N_("git multi-pack-index [<options>] (write|verify|expire|repack --batch-size=<size>)"),
        NULL
 };
 
 static struct opts_multi_pack_index {
        const char *object_dir;
        unsigned long batch_size;
+       int progress;
 } opts;
 
 int cmd_multi_pack_index(int argc, const char **argv,
                         const char *prefix)
 {
+       unsigned flags = 0;
+
        static struct option builtin_multi_pack_index_options[] = {
                OPT_FILENAME(0, "object-dir", &opts.object_dir,
                  N_("object directory containing set of packfile and pack-index pairs")),
+               OPT_BOOL(0, "progress", &opts.progress, N_("force progress reporting")),
                OPT_MAGNITUDE(0, "batch-size", &opts.batch_size,
                  N_("during repack, collect pack-files of smaller size into a batch that is larger than this size")),
                OPT_END(),
@@ -28,12 +32,15 @@ int cmd_multi_pack_index(int argc, const char **argv,
 
        git_config(git_default_config, NULL);
 
+       opts.progress = isatty(2);
        argc = parse_options(argc, argv, prefix,
                             builtin_multi_pack_index_options,
                             builtin_multi_pack_index_usage, 0);
 
        if (!opts.object_dir)
                opts.object_dir = get_object_directory();
+       if (opts.progress)
+               flags |= MIDX_PROGRESS;
 
        if (argc == 0)
                usage_with_options(builtin_multi_pack_index_usage,
@@ -47,16 +54,17 @@ int cmd_multi_pack_index(int argc, const char **argv,
        trace2_cmd_mode(argv[0]);
 
        if (!strcmp(argv[0], "repack"))
-               return midx_repack(the_repository, opts.object_dir, (size_t)opts.batch_size);
+               return midx_repack(the_repository, opts.object_dir,
+                       (size_t)opts.batch_size, flags);
        if (opts.batch_size)
                die(_("--batch-size option is only for 'repack' subcommand"));
 
        if (!strcmp(argv[0], "write"))
-               return write_midx_file(opts.object_dir);
+               return write_midx_file(opts.object_dir, flags);
        if (!strcmp(argv[0], "verify"))
-               return verify_midx_file(the_repository, opts.object_dir);
+               return verify_midx_file(the_repository, opts.object_dir, flags);
        if (!strcmp(argv[0], "expire"))
-               return expire_midx_packs(the_repository, opts.object_dir);
+               return expire_midx_packs(the_repository, opts.object_dir, flags);
 
        die(_("unrecognized subcommand: %s"), argv[0]);
 }
index 02e97f55c5a01be452bb3eaa97ec396f58e2ed99..95456f316549c9a5890a4539446c643640429eb0 100644 (file)
@@ -513,7 +513,7 @@ static int copy(int argc, const char **argv, const char *prefix)
                }
        }
 
-       if (argc < 2) {
+       if (argc < 1) {
                error(_("too few parameters"));
                usage_with_options(git_notes_copy_usage, options);
        }
index 5876583220c7d15ace8f7d4fbc7a5c4cef7a8d3b..393c20a2d78b50f8852ed11bc9e943b27ba58d2e 100644 (file)
@@ -163,7 +163,7 @@ static void *get_delta(struct object_entry *entry)
        delta_buf = diff_delta(base_buf, base_size,
                               buf, size, &delta_size, 0);
        /*
-        * We succesfully computed this delta once but dropped it for
+        * We successfully computed this delta once but dropped it for
         * memory reasons. Something is very wrong if this time we
         * recompute and create a different delta.
         */
index 4a20582e724b00a565ee8b0fd05137a11b27568c..e755087b0f1ebe11472ab4e4ef7165a4714e4e2f 100644 (file)
@@ -1471,9 +1471,10 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
                        N_("let the user edit the list of commits to rebase"),
                        PARSE_OPT_NOARG | PARSE_OPT_NONEG,
                        parse_opt_interactive },
-               OPT_SET_INT('p', "preserve-merges", &options.type,
-                           N_("(DEPRECATED) try to recreate merges instead of "
-                              "ignoring them"), REBASE_PRESERVE_MERGES),
+               OPT_SET_INT_F('p', "preserve-merges", &options.type,
+                             N_("(DEPRECATED) try to recreate merges instead of "
+                                "ignoring them"),
+                             REBASE_PRESERVE_MERGES, PARSE_OPT_HIDDEN),
                OPT_RERERE_AUTOUPDATE(&options.allow_rerere_autoupdate),
                OPT_BOOL('k', "keep-empty", &options.keep_empty,
                         N_("preserve empty commits during rebase")),
index 094c2f8ea48caee257c5dca8c95bd31fb314406e..0781763b06e80d0188acfe3c902061729b762809 100644 (file)
@@ -233,6 +233,13 @@ static void repack_promisor_objects(const struct pack_objects_args *args,
                /*
                 * pack-objects creates the .pack and .idx files, but not the
                 * .promisor file. Create the .promisor file, which is empty.
+                *
+                * NEEDSWORK: fetch-pack sometimes generates non-empty
+                * .promisor files containing the ref names and associated
+                * hashes at the point of generation of the corresponding
+                * packfile, but this would not preserve their contents. Maybe
+                * concatenate the contents of all .promisor files instead of
+                * just creating a new empty file.
                 */
                promisor_name = mkpathdup("%s-%s.promisor", packtmp,
                                          line.buf);
@@ -562,7 +569,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
        remove_temporary_files();
 
        if (git_env_bool(GIT_TEST_MULTI_PACK_INDEX, 0))
-               write_midx_file(get_object_directory());
+               write_midx_file(get_object_directory(), 0);
 
        string_list_clear(&names, 0);
        string_list_clear(&rollback, 0);
index 308c67e4fc668ec59343bbaba210b505c90a9e2c..85ce2095bf21cb386dd9577823139da01f0d0967 100644 (file)
@@ -919,6 +919,17 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
                                show_datestring("--min-age=", arg);
                                continue;
                        }
+                       if (opt_with_value(arg, "--show-object-format", &arg)) {
+                               const char *val = arg ? arg : "storage";
+
+                               if (strcmp(val, "storage") &&
+                                   strcmp(val, "input") &&
+                                   strcmp(val, "output"))
+                                       die("unknown mode for --show-object-format: %s",
+                                           arg);
+                               puts(the_hash_algo->name);
+                               continue;
+                       }
                        if (show_flag(arg) && verify)
                                die_no_single_rev(quiet);
                        continue;
index 4e806176b0828b65cf405492ca2cd2d157e3fc4a..4ad3adf4ba5a01d78f75dfc59b7ee99b25880e0c 100644 (file)
@@ -483,13 +483,12 @@ static int do_apply_stash(const char *prefix, struct stash_info *info,
                if (ret)
                        return -1;
 
+               /* read back the result of update_index() back from the disk */
                discard_cache();
+               read_cache();
        }
 
-       if (quiet) {
-               if (refresh_and_write_cache(REFRESH_QUIET, 0, 0))
-                       warning("could not refresh index");
-       } else {
+       if (!quiet) {
                struct child_process cp = CHILD_PROCESS_INIT;
 
                /*
@@ -1088,8 +1087,9 @@ static int stash_working_tree(struct stash_info *info, const struct pathspec *ps
        }
 
        cp_upd_index.git_cmd = 1;
-       argv_array_pushl(&cp_upd_index.args, "update-index", "-z", "--add",
-                        "--remove", "--stdin", NULL);
+       argv_array_pushl(&cp_upd_index.args, "update-index",
+                        "--ignore-skip-worktree-entries",
+                        "-z", "--add", "--remove", "--stdin", NULL);
        argv_array_pushf(&cp_upd_index.env_array, "GIT_INDEX_FILE=%s",
                         stash_index_path.buf);
 
index 49302d98c55d19651cacef7b36197de65d805e50..d527b8f1066859d79d746805c42bb73fab95e78c 100644 (file)
@@ -35,6 +35,7 @@ static int verbose;
 static int mark_valid_only;
 static int mark_skip_worktree_only;
 static int mark_fsmonitor_only;
+static int ignore_skip_worktree_entries;
 #define MARK_FLAG 1
 #define UNMARK_FLAG 2
 static struct strbuf mtime_dir = STRBUF_INIT;
@@ -381,7 +382,8 @@ static int process_path(const char *path, struct stat *st, int stat_errno)
                 * so updating it does not make sense.
                 * On the other hand, removing it from index should work
                 */
-               if (allow_remove && remove_file_from_cache(path))
+               if (!ignore_skip_worktree_entries && allow_remove &&
+                   remove_file_from_cache(path))
                        return error("%s: cannot remove from the index", path);
                return 0;
        }
@@ -1014,6 +1016,8 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
                {OPTION_SET_INT, 0, "no-skip-worktree", &mark_skip_worktree_only, NULL,
                        N_("clear skip-worktree bit"),
                        PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, UNMARK_FLAG},
+               OPT_BOOL(0, "ignore-skip-worktree-entries", &ignore_skip_worktree_entries,
+                        N_("do not touch index-only entries")),
                OPT_SET_INT(0, "info-only", &info_only,
                        N_("add to index only; do not add content to object database"), 1),
                OPT_SET_INT(0, "force-remove", &force_remove,
index 4de44f579af509137c10ae2c459b0aef62c52f35..d6bc5263f175cedf42a87423da246c0a2f7f6f81 100644 (file)
@@ -376,7 +376,7 @@ static int add_worktree(const char *path, const char *refname,
        if (opts->checkout) {
                cp.argv = NULL;
                argv_array_clear(&cp.args);
-               argv_array_pushl(&cp.args, "reset", "--hard", NULL);
+               argv_array_pushl(&cp.args, "reset", "--hard", "--no-recurse-submodules", NULL);
                if (opts->quiet)
                        argv_array_push(&cp.args, "--quiet");
                cp.env = child_env.argv;
index a85ed3f7bcf06ab719da741348422d678f781bb4..99439e07a1064ae6420ca9c5a821ecec8022aef7 100644 (file)
--- a/bundle.c
+++ b/bundle.c
@@ -249,15 +249,16 @@ out:
 
 
 /* Write the pack data to bundle_fd */
-static int write_pack_data(int bundle_fd, struct rev_info *revs)
+static int write_pack_data(int bundle_fd, struct rev_info *revs, struct argv_array *pack_options)
 {
        struct child_process pack_objects = CHILD_PROCESS_INIT;
        int i;
 
        argv_array_pushl(&pack_objects.args,
-                        "pack-objects", "--all-progress-implied",
+                        "pack-objects",
                         "--stdout", "--thin", "--delta-base-offset",
                         NULL);
+       argv_array_pushv(&pack_objects.args, pack_options->argv);
        pack_objects.in = -1;
        pack_objects.out = bundle_fd;
        pack_objects.git_cmd = 1;
@@ -428,7 +429,7 @@ static int write_bundle_refs(int bundle_fd, struct rev_info *revs)
 }
 
 int create_bundle(struct repository *r, const char *path,
-                 int argc, const char **argv)
+                 int argc, const char **argv, struct argv_array *pack_options)
 {
        struct lock_file lock = LOCK_INIT;
        int bundle_fd = -1;
@@ -470,7 +471,7 @@ int create_bundle(struct repository *r, const char *path,
                goto err;
 
        /* write pack */
-       if (write_pack_data(bundle_fd, &revs))
+       if (write_pack_data(bundle_fd, &revs, pack_options))
                goto err;
 
        if (!bundle_to_stdout) {
index 37c37d7f653c68ab6752fab8203b59783e2f5c40..ceab0c747578e5c5ec7835e3d62c4521914fe7ce 100644 (file)
--- a/bundle.h
+++ b/bundle.h
@@ -1,6 +1,7 @@
 #ifndef BUNDLE_H
 #define BUNDLE_H
 
+#include "argv-array.h"
 #include "cache.h"
 
 struct ref_list {
@@ -19,7 +20,7 @@ struct bundle_header {
 int is_bundle(const char *path, int quiet);
 int read_bundle_header(const char *path, struct bundle_header *header);
 int create_bundle(struct repository *r, const char *path,
-                 int argc, const char **argv);
+                 int argc, const char **argv, struct argv_array *pack_options);
 int verify_bundle(struct repository *r, struct bundle_header *header, int verbose);
 #define BUNDLE_VERBOSE 1
 int unbundle(struct repository *r, struct bundle_header *header,
diff --git a/cache.h b/cache.h
index 04cabaac119540e89f58cd91c46b17ddb30a26ce..64669a4d347d50d9f884f8d9db5400fdc0719e8d 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -1451,7 +1451,8 @@ int get_oid_hex(const char *hex, struct object_id *sha1);
 int hex_to_bytes(unsigned char *binary, const char *hex, size_t len);
 
 /*
- * Convert a binary hash to its hex equivalent. The `_r` variant is reentrant,
+ * Convert a binary hash in "unsigned char []" or an object name in
+ * "struct object_id *" to its hex equivalent. The `_r` variant is reentrant,
  * and writes the NUL-terminated output to the buffer `out`, which must be at
  * least `GIT_MAX_HEXSZ + 1` bytes, and returns a pointer to out for
  * convenience.
@@ -1459,13 +1460,12 @@ int hex_to_bytes(unsigned char *binary, const char *hex, size_t len);
  * The non-`_r` variant returns a static buffer, but uses a ring of 4
  * buffers, making it safe to make multiple calls for a single statement, like:
  *
- *   printf("%s -> %s", sha1_to_hex(one), sha1_to_hex(two));
+ *   printf("%s -> %s", hash_to_hex(one), hash_to_hex(two));
+ *   printf("%s -> %s", oid_to_hex(one), oid_to_hex(two));
  */
 char *hash_to_hex_algop_r(char *buffer, const unsigned char *hash, const struct git_hash_algo *);
-char *sha1_to_hex_r(char *out, const unsigned char *sha1);
 char *oid_to_hex_r(char *out, const struct object_id *oid);
 char *hash_to_hex_algop(const unsigned char *hash, const struct git_hash_algo *);      /* static buffer result! */
-char *sha1_to_hex(const unsigned char *sha1);                                          /* same static buffer */
 char *hash_to_hex(const unsigned char *hash);                                          /* same static buffer */
 char *oid_to_hex(const struct object_id *oid);                                         /* same static buffer */
 
index a9ac72bef487ef680d84d71afdd9ca44c0ad623a..72e435c5a38718af18d732bf6f3216d856095074 100644 (file)
@@ -203,6 +203,7 @@ gitmodules                              guide
 gitnamespaces                           guide
 gitrepository-layout                    guide
 gitrevisions                            guide
+gitsubmodules                           guide
 gittutorial-2                           guide
 gittutorial                             guide
 gitworkflows                            guide
index 0aea7b2ae5263899b2b93e5acbcd1f391814e0f3..79bc5641cbc0c39db05faef0d7ae5d362062794c 100644 (file)
@@ -464,7 +464,7 @@ static void prepare_commit_graph_one(struct repository *r, const char *obj_dir)
 /*
  * Return 1 if commit_graph is non-NULL, and 0 otherwise.
  *
- * On the first invocation, this function attemps to load the commit
+ * On the first invocation, this function attempts to load the commit
  * graph if the_repository is configured to have one.
  */
 static int prepare_commit_graph(struct repository *r)
@@ -858,9 +858,6 @@ static void write_graph_chunk_data(struct hashfile *f, int hash_len,
                        die(_("unable to parse commit %s"),
                                oid_to_hex(&(*list)->object.oid));
                tree = get_commit_tree_oid(*list);
-               if (!tree)
-                       die(_("unable to get tree for %s"),
-                               oid_to_hex(&(*list)->object.oid));
                hashwrite(f, tree->hash, hash_len);
 
                parent = (*list)->parents;
index 40890ae7ce8a48a2da06bcde6fc5a9db77260707..434ec030d6b2a0074c46c376c6322c92fd5c8a39 100644 (file)
--- a/commit.c
+++ b/commit.c
@@ -19,6 +19,7 @@
 #include "advice.h"
 #include "refs.h"
 #include "commit-reach.h"
+#include "run-command.h"
 
 static struct commit_extra_header *read_commit_extra_header_lines(const char *buf, size_t len, const char **);
 
@@ -401,10 +402,22 @@ int parse_commit_buffer(struct repository *r, struct commit *item, const void *b
        struct commit_graft *graft;
        const int tree_entry_len = the_hash_algo->hexsz + 5;
        const int parent_entry_len = the_hash_algo->hexsz + 7;
+       struct tree *tree;
 
        if (item->object.parsed)
                return 0;
-       item->object.parsed = 1;
+
+       if (item->parents) {
+               /*
+                * Presumably this is leftover from an earlier failed parse;
+                * clear it out in preparation for us re-parsing (we'll hit the
+                * same error, but that's good, since it lets our caller know
+                * the result cannot be trusted.
+                */
+               free_commit_list(item->parents);
+               item->parents = NULL;
+       }
+
        tail += size;
        if (tail <= bufptr + tree_entry_len + 1 || memcmp(bufptr, "tree ", 5) ||
                        bufptr[tree_entry_len] != '\n')
@@ -412,7 +425,12 @@ int parse_commit_buffer(struct repository *r, struct commit *item, const void *b
        if (get_oid_hex(bufptr + 5, &parent) < 0)
                return error("bad tree pointer in commit %s",
                             oid_to_hex(&item->object.oid));
-       set_commit_tree(item, lookup_tree(r, &parent));
+       tree = lookup_tree(r, &parent);
+       if (!tree)
+               return error("bad tree pointer %s in commit %s",
+                            oid_to_hex(&parent),
+                            oid_to_hex(&item->object.oid));
+       set_commit_tree(item, tree);
        bufptr += tree_entry_len + 1; /* "tree " + "hex sha1" + "\n" */
        pptr = &item->parents;
 
@@ -432,8 +450,11 @@ int parse_commit_buffer(struct repository *r, struct commit *item, const void *b
                if (graft && (graft->nr_parent < 0 || grafts_replace_parents))
                        continue;
                new_parent = lookup_commit(r, &parent);
-               if (new_parent)
-                       pptr = &commit_list_insert(new_parent, pptr)->next;
+               if (!new_parent)
+                       return error("bad parent %s in commit %s",
+                                    oid_to_hex(&parent),
+                                    oid_to_hex(&item->object.oid));
+               pptr = &commit_list_insert(new_parent, pptr)->next;
        }
        if (graft) {
                int i;
@@ -442,7 +463,9 @@ int parse_commit_buffer(struct repository *r, struct commit *item, const void *b
                        new_parent = lookup_commit(r,
                                                   &graft->parent[i]);
                        if (!new_parent)
-                               continue;
+                               return error("bad graft parent %s in commit %s",
+                                            oid_to_hex(&graft->parent[i]),
+                                            oid_to_hex(&item->object.oid));
                        pptr = &commit_list_insert(new_parent, pptr)->next;
                }
        }
@@ -451,6 +474,7 @@ int parse_commit_buffer(struct repository *r, struct commit *item, const void *b
        if (check_graph)
                load_commit_graph_info(r, item);
 
+       item->object.parsed = 1;
        return 0;
 }
 
@@ -1581,3 +1605,26 @@ size_t ignore_non_trailer(const char *buf, size_t len)
        }
        return boc ? len - boc : len - cutoff;
 }
+
+int run_commit_hook(int editor_is_used, const char *index_file,
+                   const char *name, ...)
+{
+       struct argv_array hook_env = ARGV_ARRAY_INIT;
+       va_list args;
+       int ret;
+
+       argv_array_pushf(&hook_env, "GIT_INDEX_FILE=%s", index_file);
+
+       /*
+        * Let the hook know that no editor will be launched.
+        */
+       if (!editor_is_used)
+               argv_array_push(&hook_env, "GIT_EDITOR=:");
+
+       va_start(args, name);
+       ret = run_hook_ve(hook_env.argv,name, args);
+       va_end(args);
+       argv_array_clear(&hook_env);
+
+       return ret;
+}
index f5295ca7f3efe9b7665d1c6728814bfe2901d059..221cdaa34b826bc160048567ffdf4872e1b37c47 100644 (file)
--- a/commit.h
+++ b/commit.h
@@ -132,7 +132,7 @@ const void *repo_get_commit_buffer(struct repository *r,
 #endif
 
 /*
- * Tell the commit subsytem that we are done with a particular commit buffer.
+ * Tell the commit subsystem that we are done with a particular commit buffer.
  * The commit and buffer should be the input and return value, respectively,
  * from an earlier call to get_commit_buffer.  The buffer may or may not be
  * freed by this call; callers should not access the memory afterwards.
index fe609239dd6ba22a843b243647868f887014d40e..2f4654c968ce02be29e9e886fd8c8c46b2a8f4b6 100644 (file)
@@ -1566,7 +1566,7 @@ static int try_shell_exec(const char *cmd, char *const *argv)
                while (argv[argc]) argc++;
                ALLOC_ARRAY(argv2, argc + 1);
                argv2[0] = (char *)cmd; /* full path to the script file */
-               memcpy(&argv2[1], &argv[1], sizeof(*argv) * argc);
+               COPY_ARRAY(&argv2[1], &argv[1], argc);
                exec_id = trace2_exec(prog, argv2);
                pid = mingw_spawnv(prog, argv2, 1);
                if (pid >= 0) {
index 9ad204c57ce21d22550f7e28bc23bcbb0fa50e48..1a46334399c2d3b2ed558c6b3b347413a5afd7a5 100644 (file)
@@ -558,7 +558,7 @@ int xwcstoutf(char *utf, const wchar_t *wcs, size_t utflen);
 
 /*
  * A critical section used in the implementation of the spawn
- * functions (mingw_spawnv[p]e()) and waitpid(). Intialised in
+ * functions (mingw_spawnv[p]e()) and waitpid(). Initialised in
  * the replacement main() macro below.
  */
 extern CRITICAL_SECTION pinfo_cs;
index 913434959002f252fecfc22e4027ae02f8053f63..814845d4b33fc89652be9fe0c1cd0d55e59023a3 100644 (file)
@@ -1564,7 +1564,7 @@ static FORCEINLINE void* win32direct_mmap(size_t size) {
   return (ptr != 0)? ptr: MFAIL;
 }
 
-/* This function supports releasing coalesed segments */
+/* This function supports releasing coalesced segments */
 static FORCEINLINE int win32munmap(void* ptr, size_t size) {
   MEMORY_BASIC_INFORMATION minfo;
   char* cptr = (char*)ptr;
@@ -1655,7 +1655,7 @@ static FORCEINLINE int win32munmap(void* ptr, size_t size) {
     #define CALL_MREMAP(addr, osz, nsz, mv)     MFAIL
 #endif /* HAVE_MMAP && HAVE_MREMAP */
 
-/* mstate bit set if continguous morecore disabled or failed */
+/* mstate bit set if contiguous morecore disabled or failed */
 #define USE_NONCONTIGUOUS_BIT (4U)
 
 /* segment bit set in create_mspace_with_base */
@@ -2485,7 +2485,7 @@ typedef struct malloc_segment* msegmentptr;
 
   Trim support
     Fields holding the amount of unused topmost memory that should trigger
-    timming, and a counter to force periodic scanning to release unused
+    timing, and a counter to force periodic scanning to release unused
     non-topmost segments.
 
   Locking
index ae36ed6a669d96266f01f4983b9d6a418e1dcfef..01e7c818400544ae8aa69dcf544f633d99057d7f 100644 (file)
@@ -79,7 +79,7 @@ change its address during its lifetime.
 When the chars burst over a chunk boundary, we allocate a larger
 chunk, and then copy the partly formed object from the end of the old
 chunk to the beginning of the new larger chunk.  We then carry on
-accreting characters to the end of the object as we normally would.
+accrediting characters to the end of the object as we normally would.
 
 A special macro is provided to add a single char at a time to a
 growing object.  This allows the use of register variables, which
index c0d838834ad8714cc29148bf23895ab30498947e..d1bc09e49b667522ddf5b7aa2576e10835cd8def 100644 (file)
@@ -3462,7 +3462,7 @@ build_equiv_class (bitset_t sbcset, const unsigned char *name)
        /* This isn't a valid character.  */
        return REG_ECOLLATE;
 
-      /* Build single byte matcing table for this equivalence class.  */
+      /* Build single byte matching table for this equivalence class.  */
       char_buf[1] = (unsigned char) '\0';
       len = weights[idx1 & 0xffffff];
       for (ch = 0; ch < SBC_MAX; ++ch)
index 4d81358a83d0a4a4086c6ecfc7d84988eba860e9..08a26096637712689a071bf58586939bb6ff7c75 100644 (file)
@@ -322,7 +322,7 @@ typedef enum
   /* POSIX regcomp return error codes.  (In the order listed in the
      standard.)  */
   REG_BADPAT,          /* Invalid pattern.  */
-  REG_ECOLLATE,                /* Inalid collating element.  */
+  REG_ECOLLATE,                /* Invalid collating element.  */
   REG_ECTYPE,          /* Invalid character class name.  */
   REG_EESCAPE,         /* Trailing backslash.  */
   REG_ESUBREG,         /* Invalid back reference.  */
index 59bf151336c22d65ae3470ba3ded73d99f7130fe..ec51cf34461ef2eeafea7087b2abe9cafc260932 100644 (file)
@@ -1616,7 +1616,7 @@ free_state (re_dfastate_t *state)
   re_free (state);
 }
 
-/* Create the new state which is independ of contexts.
+/* Create the new state which is independent of contexts.
    Return the new state if succeeded, otherwise return NULL.  */
 
 static re_dfastate_t *
index 1b5d89fd5ed1a2c143bc6dafdf027b1f5f2cf790..49358ae475c1f70bb977b6b1b0fc7f1790e19415 100644 (file)
@@ -2420,7 +2420,7 @@ find_recover_state (reg_errcode_t *err, re_match_context_t *mctx)
 /* From the node set CUR_NODES, pick up the nodes whose types are
    OP_OPEN_SUBEXP and which have corresponding back references in the regular
    expression. And register them to use them later for evaluating the
-   correspoding back references.  */
+   corresponding back references.  */
 
 static reg_errcode_t
 internal_function
@@ -3347,7 +3347,7 @@ build_trtable (const re_dfa_t *dfa, re_dfastate_t *state)
   dests_node = dests_alloc->dests_node;
   dests_ch = dests_alloc->dests_ch;
 
-  /* Initialize transiton table.  */
+  /* Initialize transition table.  */
   state->word_trtable = state->trtable = NULL;
 
   /* At first, group all nodes belonging to `state' into several
index 40194dd2309bea21764f2b44c6b3571355cb1fbb..b35d264c0e6bed7471992ac50b52e6271ae69d65 100644 (file)
@@ -18,7 +18,7 @@ REM and MAKE, we must blend these two different worlds.  This script
 REM attempts to do that.
 REM ================================================================
 REM This BAT file starts in a plain (non-developer) command prompt,
-REM searches for the "best" commmand prompt setup script, installs
+REM searches for the "best" command prompt setup script, installs
 REM it into the current CMD process, and exports the various MSVC
 REM environment variables for use by MAKE.
 REM
index f0ed464004da601a26d574b1cdc158abb50ae2bc..91fd4c5e96ae79f48d32c511387fbf166224154a 100644 (file)
--- a/config.h
+++ b/config.h
@@ -4,6 +4,22 @@
 #include "hashmap.h"
 #include "string-list.h"
 
+
+/**
+ * The config API gives callers a way to access Git configuration files
+ * (and files which have the same syntax).
+ *
+ * General Usage
+ * -------------
+ *
+ * Config files are parsed linearly, and each variable found is passed to a
+ * caller-provided callback function. The callback function is responsible
+ * for any actions to be taken on the config option, and is free to ignore
+ * some options. It is not uncommon for the configuration to be parsed
+ * several times during the run of a Git program, with different callbacks
+ * picking out different variables useful to themselves.
+ */
+
 struct object_id;
 
 /* git_config_parse_key() returns these negated: */
@@ -71,9 +87,34 @@ struct config_options {
        } error_action;
 };
 
+/**
+ * A config callback function takes three parameters:
+ *
+ * - the name of the parsed variable. This is in canonical "flat" form: the
+ *   section, subsection, and variable segments will be separated by dots,
+ *   and the section and variable segments will be all lowercase. E.g.,
+ *   `core.ignorecase`, `diff.SomeType.textconv`.
+ *
+ * - the value of the found variable, as a string. If the variable had no
+ *   value specified, the value will be NULL (typically this means it
+ *   should be interpreted as boolean true).
+ *
+ * - a void pointer passed in by the caller of the config API; this can
+ *   contain callback-specific data
+ *
+ * A config callback should return 0 for success, or -1 if the variable
+ * could not be parsed properly.
+ */
 typedef int (*config_fn_t)(const char *, const char *, void *);
+
 int git_default_config(const char *, const char *, void *);
+
+/**
+ * Read a specific file in git-config format.
+ * This function takes the same callback and data parameters as `git_config`.
+ */
 int git_config_from_file(config_fn_t fn, const char *, void *);
+
 int git_config_from_file_with_options(config_fn_t fn, const char *,
                                      void *,
                                      const struct config_options *);
@@ -88,34 +129,157 @@ void git_config_push_parameter(const char *text);
 int git_config_from_parameters(config_fn_t fn, void *data);
 void read_early_config(config_fn_t cb, void *data);
 void read_very_early_config(config_fn_t cb, void *data);
+
+/**
+ * Most programs will simply want to look up variables in all config files
+ * that Git knows about, using the normal precedence rules. To do this,
+ * call `git_config` with a callback function and void data pointer.
+ *
+ * `git_config` will read all config sources in order of increasing
+ * priority. Thus a callback should typically overwrite previously-seen
+ * entries with new ones (e.g., if both the user-wide `~/.gitconfig` and
+ * repo-specific `.git/config` contain `color.ui`, the config machinery
+ * will first feed the user-wide one to the callback, and then the
+ * repo-specific one; by overwriting, the higher-priority repo-specific
+ * value is left at the end).
+ */
 void git_config(config_fn_t fn, void *);
+
+/**
+ * Lets the caller examine config while adjusting some of the default
+ * behavior of `git_config`. It should almost never be used by "regular"
+ * Git code that is looking up configuration variables.
+ * It is intended for advanced callers like `git-config`, which are
+ * intentionally tweaking the normal config-lookup process.
+ * It takes two extra parameters:
+ *
+ * - `config_source`
+ * If this parameter is non-NULL, it specifies the source to parse for
+ * configuration, rather than looking in the usual files. See `struct
+ * git_config_source` in `config.h` for details. Regular `git_config` defaults
+ * to `NULL`.
+ *
+ * - `opts`
+ * Specify options to adjust the behavior of parsing config files. See `struct
+ * config_options` in `config.h` for details. As an example: regular `git_config`
+ * sets `opts.respect_includes` to `1` by default.
+ */
 int config_with_options(config_fn_t fn, void *,
                        struct git_config_source *config_source,
                        const struct config_options *opts);
+
+/**
+ * Value Parsing Helpers
+ * ---------------------
+ *
+ * The following helper functions aid in parsing string values
+ */
+
 int git_parse_ssize_t(const char *, ssize_t *);
 int git_parse_ulong(const char *, unsigned long *);
+
+/**
+ * Same as `git_config_bool`, except that it returns -1 on error rather
+ * than dying.
+ */
 int git_parse_maybe_bool(const char *);
+
+/**
+ * Parse the string to an integer, including unit factors. Dies on error;
+ * otherwise, returns the parsed result.
+ */
 int git_config_int(const char *, const char *);
+
 int64_t git_config_int64(const char *, const char *);
+
+/**
+ * Identical to `git_config_int`, but for unsigned longs.
+ */
 unsigned long git_config_ulong(const char *, const char *);
+
 ssize_t git_config_ssize_t(const char *, const char *);
+
+/**
+ * Same as `git_config_bool`, except that integers are returned as-is, and
+ * an `is_bool` flag is unset.
+ */
 int git_config_bool_or_int(const char *, const char *, int *);
+
+/**
+ * Parse a string into a boolean value, respecting keywords like "true" and
+ * "false". Integer values are converted into true/false values (when they
+ * are non-zero or zero, respectively). Other values cause a die(). If
+ * parsing is successful, the return value is the result.
+ */
 int git_config_bool(const char *, const char *);
+
+/**
+ * Allocates and copies the value string into the `dest` parameter; if no
+ * string is given, prints an error message and returns -1.
+ */
 int git_config_string(const char **, const char *, const char *);
+
+/**
+ * Similar to `git_config_string`, but expands `~` or `~user` into the
+ * user's home directory when found at the beginning of the path.
+ */
 int git_config_pathname(const char **, const char *, const char *);
+
 int git_config_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 *);
+
+/**
+ * write config values to a specific config file, takes a key/value pair as
+ * parameter.
+ */
 void git_config_set_in_file(const char *, const char *, const char *);
+
 int git_config_set_gently(const char *, const char *);
+
+/**
+ * write config values to `.git/config`, takes a key/value pair as parameter.
+ */
 void git_config_set(const char *, const char *);
+
 int git_config_parse_key(const char *, char **, int *);
 int git_config_key_is_valid(const char *key);
 int git_config_set_multivar_gently(const char *, const char *, const char *, int);
 void git_config_set_multivar(const char *, const char *, const char *, int);
 int git_config_set_multivar_in_file_gently(const char *, const char *, const char *, const char *, int);
+
+/**
+ * takes four parameters:
+ *
+ * - the name of the file, as a string, to which key/value pairs will be written.
+ *
+ * - the name of key, as a string. This is in canonical "flat" form: the section,
+ *   subsection, and variable segments will be separated by dots, and the section
+ *   and variable segments will be all lowercase.
+ *   E.g., `core.ignorecase`, `diff.SomeType.textconv`.
+ *
+ * - the value of the variable, as a string. If value is equal to NULL, it will
+ *   remove the matching key from the config file.
+ *
+ * - the value regex, as a string. It will disregard key/value pairs where value
+ *   does not match.
+ *
+ * - a multi_replace value, as an int. If value is equal to zero, nothing or only
+ *   one matching key/value is replaced, else all matching key/values (regardless
+ *   how many) are removed, before the new pair is written.
+ *
+ * It returns 0 on success.
+ */
 void git_config_set_multivar_in_file(const char *, const char *, const char *, const char *, int);
+
+/**
+ * rename or remove sections in the config file
+ * parameters `old_name` and `new_name`
+ * If NULL is passed through `new_name` parameter,
+ * the section will be removed from the config file.
+ */
 int git_config_rename_section(const char *, const char *);
+
 int git_config_rename_section_in_file(const char *, const char *, const char *);
 int git_config_copy_section(const char *, const char *);
 int git_config_copy_section_in_file(const char *, const char *, const char *);
@@ -142,6 +306,30 @@ enum config_scope current_config_scope(void);
 const char *current_config_origin_type(void);
 const char *current_config_name(void);
 
+/**
+ * Include Directives
+ * ------------------
+ *
+ * By default, the config parser does not respect include directives.
+ * However, a caller can use the special `git_config_include` wrapper
+ * callback to support them. To do so, you simply wrap your "real" callback
+ * function and data pointer in a `struct config_include_data`, and pass
+ * the wrapper to the regular config-reading functions. For example:
+ *
+ * -------------------------------------------
+ * int read_file_with_include(const char *file, config_fn_t fn, void *data)
+ * {
+ * struct config_include_data inc = CONFIG_INCLUDE_INIT;
+ * inc.fn = fn;
+ * inc.data = data;
+ * return git_config_from_file(git_config_include, file, &inc);
+ * }
+ * -------------------------------------------
+ *
+ * `git_config` respects includes automatically. The lower-level
+ * `git_config_from_file` does not.
+ *
+ */
 struct config_include_data {
        int depth;
        config_fn_t fn;
@@ -169,6 +357,33 @@ int parse_config_key(const char *var,
                     const char **subsection, int *subsection_len,
                     const char **key);
 
+/**
+ * Custom Configsets
+ * -----------------
+ *
+ * A `config_set` can be used to construct an in-memory cache for
+ * config-like files that the caller specifies (i.e., files like `.gitmodules`,
+ * `~/.gitconfig` etc.). For example,
+ *
+ * ----------------------------------------
+ * struct config_set gm_config;
+ * git_configset_init(&gm_config);
+ * int b;
+ * //we add config files to the config_set
+ * git_configset_add_file(&gm_config, ".gitmodules");
+ * git_configset_add_file(&gm_config, ".gitmodules_alt");
+ *
+ * if (!git_configset_get_bool(gm_config, "submodule.frotz.ignore", &b)) {
+ * //hack hack hack
+ * }
+ *
+ * when we are done with the configset:
+ * git_configset_clear(&gm_config);
+ * ----------------------------------------
+ *
+ * Configset API provides functions for the above mentioned work flow
+ */
+
 struct config_set_element {
        struct hashmap_entry ent;
        char *key;
@@ -197,16 +412,47 @@ struct config_set {
        struct configset_list list;
 };
 
+/**
+ * Initializes the config_set `cs`.
+ */
 void git_configset_init(struct config_set *cs);
+
+/**
+ * Parses the file and adds the variable-value pairs to the `config_set`,
+ * dies if there is an error in parsing the file. Returns 0 on success, or
+ * -1 if the file does not exist or is inaccessible. The user has to decide
+ * if he wants to free the incomplete configset or continue using it when
+ * the function returns -1.
+ */
 int git_configset_add_file(struct config_set *cs, const char *filename);
+
+/**
+ * Finds and returns the value list, sorted in order of increasing priority
+ * for the configuration variable `key` and config set `cs`. When the
+ * configuration variable `key` is not found, returns NULL. The caller
+ * should not free or modify the returned pointer, as it is owned by the cache.
+ */
 const struct string_list *git_configset_get_value_multi(struct config_set *cs, const char *key);
+
+/**
+ * Clears `config_set` structure, removes all saved variable-value pairs.
+ */
 void git_configset_clear(struct config_set *cs);
 
 /*
  * These functions return 1 if not found, and 0 if found, leaving the found
  * value in the 'dest' pointer.
  */
+
+/*
+ * Finds the highest-priority value for the configuration variable `key`
+ * and config set `cs`, stores the pointer to it in `value` and returns 0.
+ * When the configuration variable `key` is not found, returns 1 without
+ * touching `value`. The caller should not free or modify `value`, as it
+ * is owned by the cache.
+ */
 int git_configset_get_value(struct config_set *cs, const char *key, const char **dest);
+
 int git_configset_get_string_const(struct config_set *cs, const char *key, const char **dest);
 int git_configset_get_string(struct config_set *cs, const char *key, char **dest);
 int git_configset_get_int(struct config_set *cs, const char *key, int *dest);
@@ -240,17 +486,94 @@ int repo_config_get_maybe_bool(struct repository *repo,
 int repo_config_get_pathname(struct repository *repo,
                             const char *key, const char **dest);
 
+/**
+ * Querying For Specific Variables
+ * -------------------------------
+ *
+ * For programs wanting to query for specific variables in a non-callback
+ * manner, the config API provides two functions `git_config_get_value`
+ * and `git_config_get_value_multi`. They both read values from an internal
+ * cache generated previously from reading the config files.
+ */
+
+/**
+ * Finds the highest-priority value for the configuration variable `key`,
+ * stores the pointer to it in `value` and returns 0. When the
+ * configuration variable `key` is not found, returns 1 without touching
+ * `value`. The caller should not free or modify `value`, as it is owned
+ * by the cache.
+ */
 int git_config_get_value(const char *key, const char **value);
+
+/**
+ * Finds and returns the value list, sorted in order of increasing priority
+ * for the configuration variable `key`. When the configuration variable
+ * `key` is not found, returns NULL. The caller should not free or modify
+ * the returned pointer, as it is owned by the cache.
+ */
 const struct string_list *git_config_get_value_multi(const char *key);
+
+/**
+ * Resets and invalidates the config cache.
+ */
 void git_config_clear(void);
+
+/**
+ * Allocates and copies the retrieved string into the `dest` parameter for
+ * the configuration variable `key`; if NULL string is given, prints an
+ * error message and returns -1. When the configuration variable `key` is
+ * not found, returns 1 without touching `dest`.
+ */
 int git_config_get_string_const(const char *key, const char **dest);
+
+/**
+ * Similar to `git_config_get_string_const`, except that retrieved value
+ * copied into the `dest` parameter is a mutable string.
+ */
 int git_config_get_string(const char *key, char **dest);
+
+/**
+ * Finds and parses the value to an integer for the configuration variable
+ * `key`. Dies on error; otherwise, stores the value of the parsed integer in
+ * `dest` and returns 0. When the configuration variable `key` is not found,
+ * returns 1 without touching `dest`.
+ */
 int git_config_get_int(const char *key, int *dest);
+
+/**
+ * Similar to `git_config_get_int` but for unsigned longs.
+ */
 int git_config_get_ulong(const char *key, unsigned long *dest);
+
+/**
+ * Finds and parses the value into a boolean value, for the configuration
+ * variable `key` respecting keywords like "true" and "false". Integer
+ * values are converted into true/false values (when they are non-zero or
+ * zero, respectively). Other values cause a die(). If parsing is successful,
+ * stores the value of the parsed result in `dest` and returns 0. When the
+ * configuration variable `key` is not found, returns 1 without touching
+ * `dest`.
+ */
 int git_config_get_bool(const char *key, int *dest);
+
+/**
+ * Similar to `git_config_get_bool`, except that integers are copied as-is,
+ * and `is_bool` flag is unset.
+ */
 int git_config_get_bool_or_int(const char *key, int *is_bool, int *dest);
+
+/**
+ * Similar to `git_config_get_bool`, except that it returns -1 on error
+ * rather than dying.
+ */
 int git_config_get_maybe_bool(const char *key, int *dest);
+
+/**
+ * Similar to `git_config_get_string`, but expands `~` or `~user` into
+ * the user's home directory when found at the beginning of the path.
+ */
 int git_config_get_pathname(const char *key, const char **dest);
+
 int git_config_get_index_threads(int *dest);
 int git_config_get_untracked_cache(void);
 int git_config_get_split_index(void);
@@ -270,7 +593,19 @@ struct key_value_info {
        enum config_scope scope;
 };
 
+/**
+ * First prints the error message specified by the caller in `err` and then
+ * dies printing the line number and the file name of the highest priority
+ * value for the configuration variable `key`.
+ */
 NORETURN void git_die_config(const char *key, const char *err, ...) __attribute__((format(printf, 2, 3)));
+
+/**
+ * Helper function which formats the die error message according to the
+ * parameters entered. Used by `git_die_config()`. It can be used by callers
+ * handling `git_config_get_value_multi()` to print the correct error message
+ * for the desired value.
+ */
 NORETURN void git_die_config_linenr(const char *key, const char *filename, int linenr);
 
 #define LOOKUP_CONFIG(mapping, var) \
index a43b4764023315695c8d691832a39aed8b861ce6..66aedb9288fe5da15df3f681b636ae87e869ca64 100644 (file)
@@ -85,7 +85,7 @@ AC_DEFUN([GIT_PARSE_WITH],
 
 # GIT_PARSE_WITH_SET_MAKE_VAR(WITHNAME, VAR, HELP_TEXT)
 # -----------------------------------------------------
-# Set VAR to the value specied by --with-WITHNAME.
+# Set VAR to the value specified by --with-WITHNAME.
 # No verification of arguments is performed, but warnings are issued
 # if either 'yes' or 'no' is specified.
 # HELP_TEXT is presented when --help is called.
@@ -844,12 +844,61 @@ AC_MSG_CHECKING([for old iconv()])
 AC_COMPILE_IFELSE([OLDICONVTEST_SRC],
        [AC_MSG_RESULT([no])],
        [AC_MSG_RESULT([yes])
+       AC_DEFINE(HAVE_OLD_ICONV, 1)
        OLD_ICONV=UnfortunatelyYes])
 
 GIT_UNSTASH_FLAGS($ICONVDIR)
 
 GIT_CONF_SUBST([OLD_ICONV])
 
+#
+# Define ICONV_OMITS_BOM if you are on a system which
+# iconv omits bom for utf-{16,32}
+if test -z "$NO_ICONV"; then
+AC_CACHE_CHECK([whether iconv omits bom for utf-16 and utf-32],
+ [ac_cv_iconv_omits_bom],
+[
+old_LIBS="$LIBS"
+if test -n "$NEEDS_LIBICONV"; then
+       LIBS="$LIBS -liconv"
+fi
+
+AC_RUN_IFELSE(
+       [AC_LANG_PROGRAM([AC_INCLUDES_DEFAULT
+       #include <iconv.h>
+       #ifdef HAVE_OLD_ICONV
+       typedef const char *iconv_ibp;
+       #else
+       typedef char *iconv_ibp;
+       #endif
+       ],
+       [[
+       int v;
+       iconv_t conv;
+       char in[] = "a"; iconv_ibp pin = in;
+       char out[20] = ""; char *pout = out;
+       size_t isz = sizeof in;
+       size_t osz = sizeof out;
+
+       conv = iconv_open("UTF-16", "UTF-8");
+       iconv(conv, &pin, &isz, &pout, &osz);
+       iconv_close(conv);
+       v = (unsigned char)(out[0]) + (unsigned char)(out[1]);
+       return v != 0xfe + 0xff;
+       ]])],
+       [ac_cv_iconv_omits_bom=no],
+       [ac_cv_iconv_omits_bom=yes])
+
+LIBS="$old_LIBS"
+])
+if test "x$ac_cv_iconv_omits_bom" = xyes; then
+       ICONV_OMITS_BOM=Yes
+else
+       ICONV_OMITS_BOM=
+fi
+GIT_CONF_SUBST([ICONV_OMITS_BOM])
+fi
+
 ## Checks for typedefs, structures, and compiler characteristics.
 AC_MSG_NOTICE([CHECKS for typedefs, structures, and compiler characteristics])
 #
index 36c4e5dedb6a7bc76e29bc5e9f8f44f12b7c7057..c337f5f7f4dd0797a8dd3c4e54fb77fd1b4f6df4 100644 (file)
@@ -62,7 +62,8 @@ int check_connected(oid_iterate_fn fn, void *cb_data,
                 * received the objects pointed to by each wanted ref.
                 */
                do {
-                       if (!repo_has_object_file(the_repository, &oid))
+                       if (!repo_has_object_file_with_flags(the_repository, &oid,
+                                                            OBJECT_INFO_SKIP_FETCH_OBJECT))
                                return 1;
                } while (!fn(cb_data, &oid));
                return 0;
index d03453341e84c912e14d8b421b47857e7eb244b1..778e4704f6a2baa8484a13fea3a0cf4d2b35f44b 100644 (file)
@@ -20,7 +20,7 @@ expression s;
 + set_commit_tree(c, s)
   ...>}
 
-// These excluded functions must access c->maybe_tree direcly.
+// These excluded functions must access c->maybe_tree directly.
 // Note that if c->maybe_tree is written somewhere outside of these
 // functions, then the recommended transformation will be bogus with
 // repo_get_commit_tree() on the LHS.
index 3e536a9834b62353f984f04e0051e20aa5378f48..ddf4f22bd722fdad0ce585a660d06dcc4214648b 100644 (file)
@@ -10,38 +10,6 @@ struct object_id *OIDPTR;
 - is_null_sha1(OIDPTR->hash)
 + is_null_oid(OIDPTR)
 
-@@
-struct object_id OID;
-@@
-- sha1_to_hex(OID.hash)
-+ oid_to_hex(&OID)
-
-@@
-identifier f != oid_to_hex;
-struct object_id *OIDPTR;
-@@
-  f(...) {<...
-- sha1_to_hex(OIDPTR->hash)
-+ oid_to_hex(OIDPTR)
-  ...>}
-
-@@
-expression E;
-struct object_id OID;
-@@
-- sha1_to_hex_r(E, OID.hash)
-+ oid_to_hex_r(E, &OID)
-
-@@
-identifier f != oid_to_hex_r;
-expression E;
-struct object_id *OIDPTR;
-@@
-   f(...) {<...
-- sha1_to_hex_r(E, OIDPTR->hash)
-+ oid_to_hex_r(E, OIDPTR)
-  ...>}
-
 @@
 struct object_id OID;
 @@
index 00fbe6c03d8539f5c4813da674b591c85f022700..67705da6416b82a6a6170c849dd714229e90df33 100644 (file)
@@ -550,7 +550,7 @@ __git_index_files ()
                                                 esc_idx, 1)
                        } else if (esc == "n") {
                                # Uh-oh, a newline character.
-                               # We cant reliably put a pathname
+                               # We cannot reliably put a pathname
                                # containing a newline into COMPREPLY,
                                # and the newline would create a mess.
                                # Skip this path.
@@ -565,7 +565,7 @@ __git_index_files ()
                        }
                }
                # Drop closing double quote, if there is one.
-               # (There isnt any if this is a directory, as it was
+               # (There is not any if this is a directory, as it was
                # already stripped with the trailing path components.)
                if (substr(p, length(p), 1) == "\"")
                        out = out substr(p, 1, length(p) - 1)
@@ -2043,6 +2043,10 @@ _git_rebase ()
                __gitcomp "$__git_whitespacelist" "" "${cur##--whitespace=}"
                return
                ;;
+       --onto=*)
+               __git_complete_refs --cur="${cur##--onto=}"
+               return
+               ;;
        --*)
                __gitcomp_builtin rebase "" \
                        "$__git_rebase_interactive_inprogress_options"
@@ -2843,6 +2847,7 @@ _git_svn ()
                        --log-window-size= --no-checkout --quiet
                        --repack-flags --use-log-author --localtime
                        --add-author-from
+                       --recursive
                        --ignore-paths= --include-paths= $remote_opts
                        "
                local init_opts="
index 8823399e7522e59f9d4f9b9304c1b0ac919cec1f..f563be82fc7e1bd35b44ceb9fde0e232b92b76a5 100755 (executable)
@@ -95,7 +95,7 @@ if PYTHON3:
     unicode = str
 
     def write_str(f, msg):
-        # Try outputing with the default encoding. If it fails,
+        # Try outputting with the default encoding. If it fails,
         # try UTF-8.
         try:
             f.buffer.write(msg.encode(sys.getdefaultencoding()))
@@ -2129,7 +2129,7 @@ class SMTPMailer(Mailer):
                 # equivalent to
                 #     self.smtp.ehlo()
                 #     self.smtp.starttls()
-                # with acces to the ssl layer
+                # with access to the ssl layer
                 self.smtp.ehlo()
                 if not self.smtp.has_extn("starttls"):
                     raise smtplib.SMTPException("STARTTLS extension not supported by server")
@@ -2148,7 +2148,7 @@ class SMTPMailer(Mailer):
                         cert_reqs=ssl.CERT_NONE
                         )
                     self.environment.get_logger().error(
-                        '*** Warning, the server certificat is not verified (smtp) ***\n'
+                        '*** Warning, the server certificate is not verified (smtp) ***\n'
                         '***          set the option smtpCACerts                   ***\n'
                         )
                 if not hasattr(self.smtp.sock, "read"):
@@ -3189,7 +3189,7 @@ class ProjectdescEnvironmentMixin(Environment):
         self.COMPUTED_KEYS += ['projectdesc']
 
     def get_projectdesc(self):
-        """Return a one-line descripition of the project."""
+        """Return a one-line description of the project."""
 
         git_dir = get_git_dir()
         try:
index b9bb11834e1f2f0160ed6b56c4b6cd363006c731..0f98c5a23db146d8855eeb72f4d99370e0089bc9 100755 (executable)
@@ -56,7 +56,7 @@ config = git_multimail.Config('multimailhook')
 
 # Set some Git configuration variables. Equivalent to passing var=val
 # to "git -c var=val" each time git is called, or to adding the
-# configuration in .git/config (must come before instanciating the
+# configuration in .git/config (must come before instantiating the
 # environment) :
 #git_multimail.Config.add_config_parameters('multimailhook.commitEmailFormat=html')
 #git_multimail.Config.add_config_parameters(('user.name=foo', 'user.email=foo@example.com'))
index 8747b84334f35fd378f51e6a9a433cfacc630960..ff565eb3d8811d04356c590e8d0a5c92288eb30b 100755 (executable)
@@ -329,7 +329,7 @@ generate_update_branch_email()
        #
        #  git rev-parse --not --all | grep -v $(git rev-parse $refname)
        #
-       # Get's us to something pretty safe (apart from the small time
+       # Gets us to something pretty safe (apart from the small time
        # between refname being read, and git rev-parse running - for that,
        # I give up)
        #
index d18b317b2f018d1d1a5a9677a7bdaf8956d65186..0092d67b8a474367239f8f196a517c288de47dab 100755 (executable)
@@ -49,7 +49,7 @@ opcode.
 Repository sections are matched on the basename of the repository
 (after removing the .git suffix).
 
-The opcode abbrevations are:
+The opcode abbreviations are:
 
   C: create new ref
   D: delete existing ref
index 158958d36357bd217f8b257cbdbdc65b049d6c07..b7333267adad494dd460dbc59771bf07d862eaf7 100644 (file)
@@ -14,7 +14,7 @@
 
 # This rule states that each system call should have its return value checked
 # The problem is that it includes the print call. Checking every print call's
-# return value would be harmful to the code readabilty.
+# return value would be harmful to the code readability.
 # This configuration keeps all default function but print.
 [InputOutput::RequireCheckedSyscalls]
 functions = open say close
index af9cbc9d0f7bc296ec728c3b965c26a057c8cf72..d8ff2e69c4980652e866047c0a322e5436318fb9 100755 (executable)
@@ -79,7 +79,7 @@ chomp($export_media);
 $export_media = !($export_media eq 'false');
 
 my $wiki_login = run_git("config --get remote.${remotename}.mwLogin");
-# Note: mwPassword is discourraged. Use the credential system instead.
+# Note: mwPassword is discouraged. Use the credential system instead.
 my $wiki_passwd = run_git("config --get remote.${remotename}.mwPassword");
 my $wiki_domain = run_git("config --get remote.${remotename}.mwDomain");
 chomp($wiki_login);
index 0f3f4e018a05e14b8278f0d4cb80e5958208714e..b033849800bc2cbb2052ca57a501bc2d54304530 100644 (file)
@@ -24,7 +24,7 @@ $url = 'http://localhost:'.$port.'/wiki/mw-config/index.php';
 $db_dir = urlencode($tmp);
 $tmp_cookie = tempnam($tmp, "COOKIE_");
 /*
- * Fetchs a page with cURL.
+ * Fetches a page with cURL.
  */
 function get($page_name = "") {
        $curl = curl_init();
index cfbfe7ddf622c46559c626360970df303a9eb8d1..9106833578e0e23c43157d1b6291296bab6e6c13 100755 (executable)
@@ -143,7 +143,7 @@ test_expect_success 'Git clone works with one specific page cloned ' '
 test_expect_success 'Git clone works with multiple specific page cloned ' '
        wiki_reset &&
        wiki_editpage foo "I will be there" false &&
-       wiki_editpage bar "I will not disapear" false &&
+       wiki_editpage bar "I will not disappear" false &&
        wiki_editpage namnam "I be erased" false &&
        wiki_editpage nyancat "nyan nyan nyan you will not erase me" false &&
        wiki_delete_page namnam &&
index 6546294f159e940eb8f7adc3e695f16d9395eebc..3948a002828320e028cef03f7b5aa34a1ee683ce 100755 (executable)
@@ -279,7 +279,7 @@ start_lighttpd () {
        "$LIGHTTPD_DIR"/lighttpd -f "$WEB"/lighttpd.conf
 
        if test $? -ne 0 ; then
-               echo "Could not execute http deamon lighttpd"
+               echo "Could not execute http daemon lighttpd"
                exit 1
        fi
 }
index 50c6a4f89dd435933f22cedc06eb66bfc28c92e9..8a3cee617524a42aed553616a0b9551e5d7310c8 100755 (executable)
@@ -54,7 +54,7 @@ if __name__ == "__main__":
         print("usage: %s dump URL -rLOWER:UPPER")
         sys.exit(1)
     if not sys.argv[1] == 'dump':
-        raise NotImplementedError('only "dump" is suppported.')
+        raise NotImplementedError('only "dump" is supported.')
     url = sys.argv[2]
     r = ('0', 'HEAD')
     if len(sys.argv) == 4 and sys.argv[3][0:2] == '-r':
index 25ac525d5fa5ea5ac68f8a7a998fe2359b515530..797e0bd0b14cbd665a638cf0a22ca7f9f3df04d3 100644 (file)
--- a/convert.c
+++ b/convert.c
@@ -270,8 +270,12 @@ static int will_convert_lf_to_crlf(struct text_stat *stats,
 static int validate_encoding(const char *path, const char *enc,
                      const char *data, size_t len, int die_on_error)
 {
+       const char *stripped;
+
        /* We only check for UTF here as UTF?? can be an alias for UTF-?? */
-       if (istarts_with(enc, "UTF")) {
+       if (skip_iprefix(enc, "UTF", &stripped)) {
+               skip_prefix(stripped, "-", &stripped);
+
                /*
                 * Check for detectable errors in UTF encodings
                 */
@@ -285,15 +289,10 @@ static int validate_encoding(const char *path, const char *enc,
                         */
                        const char *advise_msg = _(
                                "The file '%s' contains a byte order "
-                               "mark (BOM). Please use UTF-%s as "
+                               "mark (BOM). Please use UTF-%.*s as "
                                "working-tree-encoding.");
-                       const char *stripped = NULL;
-                       char *upper = xstrdup_toupper(enc);
-                       upper[strlen(upper)-2] = '\0';
-                       if (skip_prefix(upper, "UTF", &stripped))
-                               skip_prefix(stripped, "-", &stripped);
-                       advise(advise_msg, path, stripped);
-                       free(upper);
+                       int stripped_len = strlen(stripped) - strlen("BE");
+                       advise(advise_msg, path, stripped_len, stripped);
                        if (die_on_error)
                                die(error_msg, path, enc);
                        else {
@@ -308,12 +307,7 @@ static int validate_encoding(const char *path, const char *enc,
                                "mark (BOM). Please use UTF-%sBE or UTF-%sLE "
                                "(depending on the byte order) as "
                                "working-tree-encoding.");
-                       const char *stripped = NULL;
-                       char *upper = xstrdup_toupper(enc);
-                       if (skip_prefix(upper, "UTF", &stripped))
-                               skip_prefix(stripped, "-", &stripped);
                        advise(advise_msg, path, stripped, stripped);
-                       free(upper);
                        if (die_on_error)
                                die(error_msg, path, enc);
                        else {
@@ -418,7 +412,7 @@ static int encode_to_git(const char *path, const char *src, size_t src_len,
        if (!dst) {
                /*
                 * We could add the blob "as-is" to Git. However, on checkout
-                * we would try to reencode to the original encoding. This
+                * we would try to re-encode to the original encoding. This
                 * would fail and we would leave the user with a messed-up
                 * working tree. Let's try to avoid this by screaming loud.
                 */
index 9d2e0d20ef302aaeac02aaeba791509dd43a2712..fd669ed3b4278e9f999ed1ed33747a3e96f817e6 100644 (file)
--- a/daemon.c
+++ b/daemon.c
@@ -598,7 +598,7 @@ static void canonicalize_client(struct strbuf *out, const char *in)
  * Read the host as supplied by the client connection.
  *
  * Returns a pointer to the character after the NUL byte terminating the host
- * arguemnt, or 'extra_args' if there is no host arguemnt.
+ * argument, or 'extra_args' if there is no host argument.
  */
 static char *parse_host_arg(struct hostinfo *hi, char *extra_args, int buflen)
 {
@@ -652,7 +652,7 @@ static void parse_extra_args(struct hostinfo *hi, struct argv_array *env,
                 * service that will be run.
                 *
                 * If there ends up being a particular arg in the future that
-                * git-daemon needs to parse specificly (like the 'host' arg)
+                * git-daemon needs to parse specifically (like the 'host' arg)
                 * then it can be parsed here and not added to 'git_protocol'.
                 */
                if (*arg) {
diff --git a/diff.c b/diff.c
index afe4400a60e072f42e090f87aa32e84653edfd0a..8e2914c0312ce7004c6b6ce3ab4b84b846630d65 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -2495,22 +2495,6 @@ static void pprint_rename(struct strbuf *name, const char *a, const char *b)
        }
 }
 
-struct diffstat_t {
-       int nr;
-       int alloc;
-       struct diffstat_file {
-               char *from_name;
-               char *name;
-               char *print_name;
-               const char *comments;
-               unsigned is_unmerged:1;
-               unsigned is_binary:1;
-               unsigned is_renamed:1;
-               unsigned is_interesting:1;
-               uintmax_t added, deleted;
-       } **files;
-};
-
 static struct diffstat_file *diffstat_add(struct diffstat_t *diffstat,
                                          const char *name_a,
                                          const char *name_b)
@@ -2551,7 +2535,7 @@ static int scale_linear(int it, int width, int max_change)
        /*
         * make sure that at least one '-' or '+' is printed if
         * there is any change to this path. The easiest way is to
-        * scale linearly as if the alloted width is one column shorter
+        * scale linearly as if the allotted width is one column shorter
         * than it is, and then add 1 to the result.
         */
        return 1 + (it * (width - 1) / max_change);
@@ -3157,7 +3141,7 @@ static void show_dirstat_by_line(struct diffstat_t *data, struct diff_options *o
        gather_dirstat(options, &dir, changed, "", 0);
 }
 
-static void free_diffstat_info(struct diffstat_t *diffstat)
+void free_diffstat_info(struct diffstat_t *diffstat)
 {
        int i;
        for (i = 0; i < diffstat->nr; i++) {
@@ -3196,7 +3180,7 @@ static int is_conflict_marker(const char *line, int marker_size, unsigned long l
        for (cnt = 1; cnt < marker_size; cnt++)
                if (line[cnt] != firstchar)
                        return 0;
-       /* line[1] thru line[marker_size-1] are same as firstchar */
+       /* line[1] through line[marker_size-1] are same as firstchar */
        if (len < marker_size + 1 || !isspace(line[marker_size]))
                return 0;
        return 1;
@@ -6283,12 +6267,7 @@ void diff_flush(struct diff_options *options)
            dirstat_by_line) {
                struct diffstat_t diffstat;
 
-               memset(&diffstat, 0, sizeof(struct diffstat_t));
-               for (i = 0; i < q->nr; i++) {
-                       struct diff_filepair *p = q->queue[i];
-                       if (check_pair_status(p))
-                               diff_flush_stat(p, options, &diffstat);
-               }
+               compute_diffstat(options, &diffstat, q);
                if (output_format & DIFF_FORMAT_NUMSTAT)
                        show_numstat(&diffstat, options);
                if (output_format & DIFF_FORMAT_DIFFSTAT)
@@ -6621,6 +6600,20 @@ static int is_submodule_ignored(const char *path, struct diff_options *options)
        return ignored;
 }
 
+void compute_diffstat(struct diff_options *options,
+                     struct diffstat_t *diffstat,
+                     struct diff_queue_struct *q)
+{
+       int i;
+
+       memset(diffstat, 0, sizeof(struct diffstat_t));
+       for (i = 0; i < q->nr; i++) {
+               struct diff_filepair *p = q->queue[i];
+               if (check_pair_status(p))
+                       diff_flush_stat(p, options, diffstat);
+       }
+}
+
 void diff_addremove(struct diff_options *options,
                    int addremove, unsigned mode,
                    const struct object_id *oid,
diff --git a/diff.h b/diff.h
index 7f8f024febc52e954aa06f2f2c52d954aa2d4eb2..d986ddc3b53c3dafa5cd50a9fefdfbfe64992bfc 100644 (file)
--- a/diff.h
+++ b/diff.h
@@ -245,6 +245,22 @@ void diff_emit_submodule_error(struct diff_options *o, const char *err);
 void diff_emit_submodule_pipethrough(struct diff_options *o,
                                     const char *line, int len);
 
+struct diffstat_t {
+       int nr;
+       int alloc;
+       struct diffstat_file {
+               char *from_name;
+               char *name;
+               char *print_name;
+               const char *comments;
+               unsigned is_unmerged:1;
+               unsigned is_binary:1;
+               unsigned is_renamed:1;
+               unsigned is_interesting:1;
+               uintmax_t added, deleted;
+       } **files;
+};
+
 enum color_diff {
        DIFF_RESET = 0,
        DIFF_CONTEXT = 1,
@@ -334,6 +350,10 @@ void diff_change(struct diff_options *,
 
 struct diff_filepair *diff_unmerge(struct diff_options *, const char *path);
 
+void compute_diffstat(struct diff_options *options, struct diffstat_t *diffstat,
+                     struct diff_queue_struct *q);
+void free_diffstat_info(struct diffstat_t *diffstat);
+
 #define DIFF_SETUP_REVERSE             1
 #define DIFF_SETUP_USE_SIZE_CACHE      4
 
diff --git a/dir.c b/dir.c
index 61f559f98008afe61e748fb1821fb45e0bec913f..9047373f6b5ae08d369c8ed78021b4c43c98b008 100644 (file)
--- a/dir.c
+++ b/dir.c
@@ -2489,7 +2489,7 @@ static int remove_dir_recurse(struct strbuf *path, int flag, int *kept_up)
                                 * wanted anyway
                                 */
                                continue;
-                       /* fall thru */
+                       /* fall through */
                } else if (S_ISDIR(st.st_mode)) {
                        if (!remove_dir_recurse(path, flag, &kept_down))
                                continue; /* happy */
index 26b614780ac0e6bb35f0268510a7f370e51df6d7..1734a573b010dd2f87541154cc70e5bb71b00463 100644 (file)
@@ -674,7 +674,8 @@ static void mark_complete_and_common_ref(struct fetch_negotiator *negotiator,
                struct object *o;
 
                if (!has_object_file_with_flags(&ref->old_oid,
-                                               OBJECT_INFO_QUICK))
+                                               OBJECT_INFO_QUICK |
+                                                       OBJECT_INFO_SKIP_FETCH_OBJECT))
                        continue;
                o = parse_object(the_repository, &ref->old_oid);
                if (!o)
@@ -767,8 +768,33 @@ static int sideband_demux(int in, int out, void *data)
        return ret;
 }
 
+static void write_promisor_file(const char *keep_name,
+                               struct ref **sought, int nr_sought)
+{
+       struct strbuf promisor_name = STRBUF_INIT;
+       int suffix_stripped;
+       FILE *output;
+       int i;
+
+       strbuf_addstr(&promisor_name, keep_name);
+       suffix_stripped = strbuf_strip_suffix(&promisor_name, ".keep");
+       if (!suffix_stripped)
+               BUG("name of pack lockfile should end with .keep (was '%s')",
+                   keep_name);
+       strbuf_addstr(&promisor_name, ".promisor");
+
+       output = xfopen(promisor_name.buf, "w");
+       for (i = 0; i < nr_sought; i++)
+               fprintf(output, "%s %s\n", oid_to_hex(&sought[i]->old_oid),
+                       sought[i]->name);
+       fclose(output);
+
+       strbuf_release(&promisor_name);
+}
+
 static int get_pack(struct fetch_pack_args *args,
-                   int xd[2], char **pack_lockfile)
+                   int xd[2], char **pack_lockfile,
+                   struct ref **sought, int nr_sought)
 {
        struct async demux;
        int do_keep = args->keep_pack;
@@ -830,7 +856,13 @@ static int get_pack(struct fetch_pack_args *args,
                }
                if (args->check_self_contained_and_connected)
                        argv_array_push(&cmd.args, "--check-self-contained-and-connected");
-               if (args->from_promisor)
+               /*
+                * If we're obtaining the filename of a lockfile, we'll use
+                * that filename to write a .promisor file with more
+                * information below. If not, we need index-pack to do it for
+                * us.
+                */
+               if (!(do_keep && pack_lockfile) && args->from_promisor)
                        argv_array_push(&cmd.args, "--promisor");
        }
        else {
@@ -884,6 +916,14 @@ static int get_pack(struct fetch_pack_args *args,
                die(_("%s failed"), cmd_name);
        if (use_sideband && finish_async(&demux))
                die(_("error in sideband demultiplexer"));
+
+       /*
+        * Now that index-pack has succeeded, write the promisor file using the
+        * obtained .keep filename if necessary
+        */
+       if (do_keep && pack_lockfile && args->from_promisor)
+               write_promisor_file(*pack_lockfile, sought, nr_sought);
+
        return 0;
 }
 
@@ -906,8 +946,15 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,
        struct object_id oid;
        const char *agent_feature;
        int agent_len;
-       struct fetch_negotiator negotiator;
-       fetch_negotiator_init(r, &negotiator);
+       struct fetch_negotiator negotiator_alloc;
+       struct fetch_negotiator *negotiator;
+
+       if (args->no_dependents) {
+               negotiator = NULL;
+       } else {
+               negotiator = &negotiator_alloc;
+               fetch_negotiator_init(r, negotiator);
+       }
 
        sort_ref_list(&ref, ref_compare_name);
        QSORT(sought, nr_sought, cmp_ref_by_name);
@@ -994,7 +1041,7 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,
                die(_("Server does not support --deepen"));
 
        if (!args->no_dependents) {
-               mark_complete_and_common_ref(&negotiator, args, &ref);
+               mark_complete_and_common_ref(negotiator, args, &ref);
                filter_refs(args, &ref, sought, nr_sought);
                if (everything_local(args, &ref)) {
                        packet_flush(fd[1]);
@@ -1003,7 +1050,7 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,
        } else {
                filter_refs(args, &ref, sought, nr_sought);
        }
-       if (find_common(&negotiator, args, fd, &oid, ref) < 0)
+       if (find_common(negotiator, args, fd, &oid, ref) < 0)
                if (!args->keep_pack)
                        /* When cloning, it is not unusual to have
                         * no common commit.
@@ -1019,11 +1066,12 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,
                alternate_shallow_file = setup_temporary_shallow(si->shallow);
        else
                alternate_shallow_file = NULL;
-       if (get_pack(args, fd, pack_lockfile))
+       if (get_pack(args, fd, pack_lockfile, sought, nr_sought))
                die(_("git fetch-pack: fetch failed."));
 
  all_done:
-       negotiator.release(&negotiator);
+       if (negotiator)
+               negotiator->release(negotiator);
        return ref;
 }
 
@@ -1241,7 +1289,8 @@ static int process_acks(struct fetch_negotiator *negotiator,
                                struct commit *commit;
                                oidset_insert(common, &oid);
                                commit = lookup_commit(the_repository, &oid);
-                               negotiator->ack(negotiator, commit);
+                               if (negotiator)
+                                       negotiator->ack(negotiator, commit);
                        }
                        continue;
                }
@@ -1393,8 +1442,16 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,
        struct packet_reader reader;
        int in_vain = 0, negotiation_started = 0;
        int haves_to_send = INITIAL_FLUSH;
-       struct fetch_negotiator negotiator;
-       fetch_negotiator_init(r, &negotiator);
+       struct fetch_negotiator negotiator_alloc;
+       struct fetch_negotiator *negotiator;
+
+       if (args->no_dependents) {
+               negotiator = NULL;
+       } else {
+               negotiator = &negotiator_alloc;
+               fetch_negotiator_init(r, negotiator);
+       }
+
        packet_reader_init(&reader, fd[0], NULL, 0,
                           PACKET_READ_CHOMP_NEWLINE |
                           PACKET_READ_DIE_ON_ERR_PACKET);
@@ -1418,15 +1475,15 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,
 
                        /* Filter 'ref' by 'sought' and those that aren't local */
                        if (!args->no_dependents) {
-                               mark_complete_and_common_ref(&negotiator, args, &ref);
+                               mark_complete_and_common_ref(negotiator, args, &ref);
                                filter_refs(args, &ref, sought, nr_sought);
                                if (everything_local(args, &ref))
                                        state = FETCH_DONE;
                                else
                                        state = FETCH_SEND_REQUEST;
 
-                               mark_tips(&negotiator, args->negotiation_tips);
-                               for_each_cached_alternate(&negotiator,
+                               mark_tips(negotiator, args->negotiation_tips);
+                               for_each_cached_alternate(negotiator,
                                                          insert_one_alternate_object);
                        } else {
                                filter_refs(args, &ref, sought, nr_sought);
@@ -1440,7 +1497,7 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,
                                                    "negotiation_v2",
                                                    the_repository);
                        }
-                       if (send_fetch_request(&negotiator, fd[1], args, ref,
+                       if (send_fetch_request(negotiator, fd[1], args, ref,
                                               &common,
                                               &haves_to_send, &in_vain,
                                               reader.use_sideband))
@@ -1450,7 +1507,7 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,
                        break;
                case FETCH_PROCESS_ACKS:
                        /* Process ACKs/NAKs */
-                       switch (process_acks(&negotiator, &reader, &common)) {
+                       switch (process_acks(negotiator, &reader, &common)) {
                        case 2:
                                state = FETCH_GET_PACK;
                                break;
@@ -1475,7 +1532,7 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,
 
                        /* get the pack */
                        process_section_header(&reader, "packfile", 0);
-                       if (get_pack(args, fd, pack_lockfile))
+                       if (get_pack(args, fd, pack_lockfile, sought, nr_sought))
                                die(_("git fetch-pack: fetch failed."));
 
                        state = FETCH_DONE;
@@ -1485,7 +1542,8 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,
                }
        }
 
-       negotiator.release(&negotiator);
+       if (negotiator)
+               negotiator->release(negotiator);
        oidset_clear(&common);
        return ref;
 }
diff --git a/fsck.c b/fsck.c
index cdb7d8db03017e36811e2bf3803ff0a4ca032a3f..ac4ba4c8e8899d42a9684b9ea43f4e72a57f3761 100644 (file)
--- a/fsck.c
+++ b/fsck.c
@@ -43,21 +43,17 @@ static struct oidset gitmodules_done = OIDSET_INIT;
        FUNC(MISSING_AUTHOR, ERROR) \
        FUNC(MISSING_COMMITTER, ERROR) \
        FUNC(MISSING_EMAIL, ERROR) \
-       FUNC(MISSING_GRAFT, ERROR) \
        FUNC(MISSING_NAME_BEFORE_EMAIL, ERROR) \
        FUNC(MISSING_OBJECT, ERROR) \
-       FUNC(MISSING_PARENT, ERROR) \
        FUNC(MISSING_SPACE_BEFORE_DATE, ERROR) \
        FUNC(MISSING_SPACE_BEFORE_EMAIL, ERROR) \
        FUNC(MISSING_TAG, ERROR) \
        FUNC(MISSING_TAG_ENTRY, ERROR) \
-       FUNC(MISSING_TAG_OBJECT, ERROR) \
        FUNC(MISSING_TREE, ERROR) \
        FUNC(MISSING_TREE_OBJECT, ERROR) \
        FUNC(MISSING_TYPE, ERROR) \
        FUNC(MISSING_TYPE_ENTRY, ERROR) \
        FUNC(MULTIPLE_AUTHORS, ERROR) \
-       FUNC(TAG_OBJECT_NOT_TAG, ERROR) \
        FUNC(TREE_NOT_SORTED, ERROR) \
        FUNC(UNKNOWN_TYPE, ERROR) \
        FUNC(ZERO_PADDED_DATE, ERROR) \
@@ -281,14 +277,16 @@ static void append_msg_id(struct strbuf *sb, const char *msg_id)
        strbuf_addstr(sb, ": ");
 }
 
-static int object_on_skiplist(struct fsck_options *opts, struct object *obj)
+static int object_on_skiplist(struct fsck_options *opts,
+                             const struct object_id *oid)
 {
-       return opts && obj && oidset_contains(&opts->skiplist, &obj->oid);
+       return opts && oid && oidset_contains(&opts->skiplist, oid);
 }
 
-__attribute__((format (printf, 4, 5)))
-static int report(struct fsck_options *options, struct object *object,
-       enum fsck_msg_id id, const char *fmt, ...)
+__attribute__((format (printf, 5, 6)))
+static int report(struct fsck_options *options,
+                 const struct object_id *oid, enum object_type object_type,
+                 enum fsck_msg_id id, const char *fmt, ...)
 {
        va_list ap;
        struct strbuf sb = STRBUF_INIT;
@@ -297,7 +295,7 @@ static int report(struct fsck_options *options, struct object *object,
        if (msg_type == FSCK_IGNORE)
                return 0;
 
-       if (object_on_skiplist(options, object))
+       if (object_on_skiplist(options, oid))
                return 0;
 
        if (msg_type == FSCK_FATAL)
@@ -309,49 +307,71 @@ static int report(struct fsck_options *options, struct object *object,
 
        va_start(ap, fmt);
        strbuf_vaddf(&sb, fmt, ap);
-       result = options->error_func(options, object, msg_type, sb.buf);
+       result = options->error_func(options, oid, object_type,
+                                    msg_type, sb.buf);
        strbuf_release(&sb);
        va_end(ap);
 
        return result;
 }
 
-static char *get_object_name(struct fsck_options *options, struct object *obj)
+void fsck_enable_object_names(struct fsck_options *options)
 {
        if (!options->object_names)
+               options->object_names = kh_init_oid_map();
+}
+
+const char *fsck_get_object_name(struct fsck_options *options,
+                                const struct object_id *oid)
+{
+       khiter_t pos;
+       if (!options->object_names)
+               return NULL;
+       pos = kh_get_oid_map(options->object_names, *oid);
+       if (pos >= kh_end(options->object_names))
                return NULL;
-       return lookup_decoration(options->object_names, obj);
+       return kh_value(options->object_names, pos);
 }
 
-static void put_object_name(struct fsck_options *options, struct object *obj,
-       const char *fmt, ...)
+void fsck_put_object_name(struct fsck_options *options,
+                         const struct object_id *oid,
+                         const char *fmt, ...)
 {
        va_list ap;
        struct strbuf buf = STRBUF_INIT;
-       char *existing;
+       khiter_t pos;
+       int hashret;
 
        if (!options->object_names)
                return;
-       existing = lookup_decoration(options->object_names, obj);
-       if (existing)
+
+       pos = kh_put_oid_map(options->object_names, *oid, &hashret);
+       if (!hashret)
                return;
        va_start(ap, fmt);
        strbuf_vaddf(&buf, fmt, ap);
-       add_decoration(options->object_names, obj, strbuf_detach(&buf, NULL));
+       kh_value(options->object_names, pos) = strbuf_detach(&buf, NULL);
        va_end(ap);
 }
 
-static const char *describe_object(struct fsck_options *o, struct object *obj)
+const char *fsck_describe_object(struct fsck_options *options,
+                                const struct object_id *oid)
 {
-       static struct strbuf buf = STRBUF_INIT;
-       char *name;
-
-       strbuf_reset(&buf);
-       strbuf_addstr(&buf, oid_to_hex(&obj->oid));
-       if (o->object_names && (name = lookup_decoration(o->object_names, obj)))
-               strbuf_addf(&buf, " (%s)", name);
+       static struct strbuf bufs[] = {
+               STRBUF_INIT, STRBUF_INIT, STRBUF_INIT, STRBUF_INIT
+       };
+       static int b = 0;
+       struct strbuf *buf;
+       const char *name = fsck_get_object_name(options, oid);
+
+       buf = bufs + b;
+       b = (b + 1) % ARRAY_SIZE(bufs);
+       strbuf_reset(buf);
+       strbuf_addstr(buf, oid_to_hex(oid));
+       if (name)
+               strbuf_addf(buf, " (%s)", name);
 
-       return buf.buf;
+       return buf->buf;
 }
 
 static int fsck_walk_tree(struct tree *tree, void *data, struct fsck_options *options)
@@ -364,7 +384,7 @@ static int fsck_walk_tree(struct tree *tree, void *data, struct fsck_options *op
        if (parse_tree(tree))
                return -1;
 
-       name = get_object_name(options, &tree->object);
+       name = fsck_get_object_name(options, &tree->object.oid);
        if (init_tree_desc_gently(&desc, tree->buffer, tree->size))
                return -1;
        while (tree_entry_gently(&desc, &entry)) {
@@ -377,20 +397,21 @@ static int fsck_walk_tree(struct tree *tree, void *data, struct fsck_options *op
                if (S_ISDIR(entry.mode)) {
                        obj = (struct object *)lookup_tree(the_repository, &entry.oid);
                        if (name && obj)
-                               put_object_name(options, obj, "%s%s/", name,
-                                       entry.path);
+                               fsck_put_object_name(options, &entry.oid, "%s%s/",
+                                                    name, entry.path);
                        result = options->walk(obj, OBJ_TREE, data, options);
                }
                else if (S_ISREG(entry.mode) || S_ISLNK(entry.mode)) {
                        obj = (struct object *)lookup_blob(the_repository, &entry.oid);
                        if (name && obj)
-                               put_object_name(options, obj, "%s%s", name,
-                                       entry.path);
+                               fsck_put_object_name(options, &entry.oid, "%s%s",
+                                                    name, entry.path);
                        result = options->walk(obj, OBJ_BLOB, data, options);
                }
                else {
                        result = error("in tree %s: entry %s has bad mode %.6o",
-                                       describe_object(options, &tree->object), entry.path, entry.mode);
+                                      fsck_describe_object(options, &tree->object.oid),
+                                      entry.path, entry.mode);
                }
                if (result < 0)
                        return result;
@@ -411,10 +432,10 @@ static int fsck_walk_commit(struct commit *commit, void *data, struct fsck_optio
        if (parse_commit(commit))
                return -1;
 
-       name = get_object_name(options, &commit->object);
+       name = fsck_get_object_name(options, &commit->object.oid);
        if (name)
-               put_object_name(options, &get_commit_tree(commit)->object,
-                               "%s:", name);
+               fsck_put_object_name(options, get_commit_tree_oid(commit),
+                                    "%s:", name);
 
        result = options->walk((struct object *)get_commit_tree(commit),
                               OBJ_TREE, data, options);
@@ -442,16 +463,17 @@ static int fsck_walk_commit(struct commit *commit, void *data, struct fsck_optio
 
        while (parents) {
                if (name) {
-                       struct object *obj = &parents->item->object;
+                       struct object_id *oid = &parents->item->object.oid;
 
                        if (counter++)
-                               put_object_name(options, obj, "%s^%d",
-                                       name, counter);
+                               fsck_put_object_name(options, oid, "%s^%d",
+                                                    name, counter);
                        else if (generation > 0)
-                               put_object_name(options, obj, "%.*s~%d",
-                                       name_prefix_len, name, generation + 1);
+                               fsck_put_object_name(options, oid, "%.*s~%d",
+                                                    name_prefix_len, name,
+                                                    generation + 1);
                        else
-                               put_object_name(options, obj, "%s^", name);
+                               fsck_put_object_name(options, oid, "%s^", name);
                }
                result = options->walk((struct object *)parents->item, OBJ_COMMIT, data, options);
                if (result < 0)
@@ -465,12 +487,12 @@ static int fsck_walk_commit(struct commit *commit, void *data, struct fsck_optio
 
 static int fsck_walk_tag(struct tag *tag, void *data, struct fsck_options *options)
 {
-       char *name = get_object_name(options, &tag->object);
+       const char *name = fsck_get_object_name(options, &tag->object.oid);
 
        if (parse_tag(tag))
                return -1;
        if (name)
-               put_object_name(options, tag->tagged, "%s", name);
+               fsck_put_object_name(options, &tag->tagged->oid, "%s", name);
        return options->walk(tag->tagged, OBJ_ANY, data, options);
 }
 
@@ -492,7 +514,8 @@ int fsck_walk(struct object *obj, void *data, struct fsck_options *options)
        case OBJ_TAG:
                return fsck_walk_tag((struct tag *)obj, data, options);
        default:
-               error("Unknown object type for %s", describe_object(options, obj));
+               error("Unknown object type for %s",
+                     fsck_describe_object(options, &obj->oid));
                return -1;
        }
 }
@@ -543,7 +566,9 @@ static int verify_ordered(unsigned mode1, const char *name1, unsigned mode2, con
        return c1 < c2 ? 0 : TREE_UNORDERED;
 }
 
-static int fsck_tree(struct tree *item, struct fsck_options *options)
+static int fsck_tree(const struct object_id *oid,
+                    const char *buffer, unsigned long size,
+                    struct fsck_options *options)
 {
        int retval = 0;
        int has_null_sha1 = 0;
@@ -560,8 +585,8 @@ static int fsck_tree(struct tree *item, struct fsck_options *options)
        unsigned o_mode;
        const char *o_name;
 
-       if (init_tree_desc_gently(&desc, item->buffer, item->size)) {
-               retval += report(options, &item->object, FSCK_MSG_BAD_TREE, "cannot be parsed as a tree");
+       if (init_tree_desc_gently(&desc, buffer, size)) {
+               retval += report(options, oid, OBJ_TREE, FSCK_MSG_BAD_TREE, "cannot be parsed as a tree");
                return retval;
        }
 
@@ -587,13 +612,14 @@ static int fsck_tree(struct tree *item, struct fsck_options *options)
                        if (!S_ISLNK(mode))
                                oidset_insert(&gitmodules_found, oid);
                        else
-                               retval += report(options, &item->object,
+                               retval += report(options,
+                                                oid, OBJ_TREE,
                                                 FSCK_MSG_GITMODULES_SYMLINK,
                                                 ".gitmodules is a symbolic link");
                }
 
                if (update_tree_entry_gently(&desc)) {
-                       retval += report(options, &item->object, FSCK_MSG_BAD_TREE, "cannot be parsed as a tree");
+                       retval += report(options, oid, OBJ_TREE, FSCK_MSG_BAD_TREE, "cannot be parsed as a tree");
                        break;
                }
 
@@ -638,30 +664,31 @@ static int fsck_tree(struct tree *item, struct fsck_options *options)
        }
 
        if (has_null_sha1)
-               retval += report(options, &item->object, FSCK_MSG_NULL_SHA1, "contains entries pointing to null sha1");
+               retval += report(options, oid, OBJ_TREE, FSCK_MSG_NULL_SHA1, "contains entries pointing to null sha1");
        if (has_full_path)
-               retval += report(options, &item->object, FSCK_MSG_FULL_PATHNAME, "contains full pathnames");
+               retval += report(options, oid, OBJ_TREE, FSCK_MSG_FULL_PATHNAME, "contains full pathnames");
        if (has_empty_name)
-               retval += report(options, &item->object, FSCK_MSG_EMPTY_NAME, "contains empty pathname");
+               retval += report(options, oid, OBJ_TREE, FSCK_MSG_EMPTY_NAME, "contains empty pathname");
        if (has_dot)
-               retval += report(options, &item->object, FSCK_MSG_HAS_DOT, "contains '.'");
+               retval += report(options, oid, OBJ_TREE, FSCK_MSG_HAS_DOT, "contains '.'");
        if (has_dotdot)
-               retval += report(options, &item->object, FSCK_MSG_HAS_DOTDOT, "contains '..'");
+               retval += report(options, oid, OBJ_TREE, FSCK_MSG_HAS_DOTDOT, "contains '..'");
        if (has_dotgit)
-               retval += report(options, &item->object, FSCK_MSG_HAS_DOTGIT, "contains '.git'");
+               retval += report(options, oid, OBJ_TREE, FSCK_MSG_HAS_DOTGIT, "contains '.git'");
        if (has_zero_pad)
-               retval += report(options, &item->object, FSCK_MSG_ZERO_PADDED_FILEMODE, "contains zero-padded file modes");
+               retval += report(options, oid, OBJ_TREE, FSCK_MSG_ZERO_PADDED_FILEMODE, "contains zero-padded file modes");
        if (has_bad_modes)
-               retval += report(options, &item->object, FSCK_MSG_BAD_FILEMODE, "contains bad file modes");
+               retval += report(options, oid, OBJ_TREE, FSCK_MSG_BAD_FILEMODE, "contains bad file modes");
        if (has_dup_entries)
-               retval += report(options, &item->object, FSCK_MSG_DUPLICATE_ENTRIES, "contains duplicate file entries");
+               retval += report(options, oid, OBJ_TREE, FSCK_MSG_DUPLICATE_ENTRIES, "contains duplicate file entries");
        if (not_properly_sorted)
-               retval += report(options, &item->object, FSCK_MSG_TREE_NOT_SORTED, "not properly sorted");
+               retval += report(options, oid, OBJ_TREE, FSCK_MSG_TREE_NOT_SORTED, "not properly sorted");
        return retval;
 }
 
 static int verify_headers(const void *data, unsigned long size,
-                         struct object *obj, struct fsck_options *options)
+                         const struct object_id *oid, enum object_type type,
+                         struct fsck_options *options)
 {
        const char *buffer = (const char *)data;
        unsigned long i;
@@ -669,7 +696,7 @@ static int verify_headers(const void *data, unsigned long size,
        for (i = 0; i < size; i++) {
                switch (buffer[i]) {
                case '\0':
-                       return report(options, obj,
+                       return report(options, oid, type,
                                FSCK_MSG_NUL_IN_HEADER,
                                "unterminated header: NUL at offset %ld", i);
                case '\n':
@@ -687,11 +714,13 @@ static int verify_headers(const void *data, unsigned long size,
        if (size && buffer[size - 1] == '\n')
                return 0;
 
-       return report(options, obj,
+       return report(options, oid, type,
                FSCK_MSG_UNTERMINATED_HEADER, "unterminated header");
 }
 
-static int fsck_ident(const char **ident, struct object *obj, struct fsck_options *options)
+static int fsck_ident(const char **ident,
+                     const struct object_id *oid, enum object_type type,
+                     struct fsck_options *options)
 {
        const char *p = *ident;
        char *end;
@@ -701,28 +730,28 @@ static int fsck_ident(const char **ident, struct object *obj, struct fsck_option
                (*ident)++;
 
        if (*p == '<')
-               return report(options, obj, FSCK_MSG_MISSING_NAME_BEFORE_EMAIL, "invalid author/committer line - missing space before email");
+               return report(options, oid, type, FSCK_MSG_MISSING_NAME_BEFORE_EMAIL, "invalid author/committer line - missing space before email");
        p += strcspn(p, "<>\n");
        if (*p == '>')
-               return report(options, obj, FSCK_MSG_BAD_NAME, "invalid author/committer line - bad name");
+               return report(options, oid, type, FSCK_MSG_BAD_NAME, "invalid author/committer line - bad name");
        if (*p != '<')
-               return report(options, obj, FSCK_MSG_MISSING_EMAIL, "invalid author/committer line - missing email");
+               return report(options, oid, type, FSCK_MSG_MISSING_EMAIL, "invalid author/committer line - missing email");
        if (p[-1] != ' ')
-               return report(options, obj, FSCK_MSG_MISSING_SPACE_BEFORE_EMAIL, "invalid author/committer line - missing space before email");
+               return report(options, oid, type, FSCK_MSG_MISSING_SPACE_BEFORE_EMAIL, "invalid author/committer line - missing space before email");
        p++;
        p += strcspn(p, "<>\n");
        if (*p != '>')
-               return report(options, obj, FSCK_MSG_BAD_EMAIL, "invalid author/committer line - bad email");
+               return report(options, oid, type, FSCK_MSG_BAD_EMAIL, "invalid author/committer line - bad email");
        p++;
        if (*p != ' ')
-               return report(options, obj, FSCK_MSG_MISSING_SPACE_BEFORE_DATE, "invalid author/committer line - missing space before date");
+               return report(options, oid, type, FSCK_MSG_MISSING_SPACE_BEFORE_DATE, "invalid author/committer line - missing space before date");
        p++;
        if (*p == '0' && p[1] != ' ')
-               return report(options, obj, FSCK_MSG_ZERO_PADDED_DATE, "invalid author/committer line - zero-padded date");
+               return report(options, oid, type, FSCK_MSG_ZERO_PADDED_DATE, "invalid author/committer line - zero-padded date");
        if (date_overflows(parse_timestamp(p, &end, 10)))
-               return report(options, obj, FSCK_MSG_BAD_DATE_OVERFLOW, "invalid author/committer line - date causes integer overflow");
+               return report(options, oid, type, FSCK_MSG_BAD_DATE_OVERFLOW, "invalid author/committer line - date causes integer overflow");
        if ((end == p || *end != ' '))
-               return report(options, obj, FSCK_MSG_BAD_DATE, "invalid author/committer line - bad date");
+               return report(options, oid, type, FSCK_MSG_BAD_DATE, "invalid author/committer line - bad date");
        p = end + 1;
        if ((*p != '+' && *p != '-') ||
            !isdigit(p[1]) ||
@@ -730,83 +759,60 @@ static int fsck_ident(const char **ident, struct object *obj, struct fsck_option
            !isdigit(p[3]) ||
            !isdigit(p[4]) ||
            (p[5] != '\n'))
-               return report(options, obj, FSCK_MSG_BAD_TIMEZONE, "invalid author/committer line - bad time zone");
+               return report(options, oid, type, FSCK_MSG_BAD_TIMEZONE, "invalid author/committer line - bad time zone");
        p += 6;
        return 0;
 }
 
-static int fsck_commit_buffer(struct commit *commit, const char *buffer,
-       unsigned long size, struct fsck_options *options)
+static int fsck_commit(const struct object_id *oid,
+                      const char *buffer, unsigned long size,
+                      struct fsck_options *options)
 {
-       struct object_id tree_oid, oid;
-       struct commit_graft *graft;
-       unsigned parent_count, parent_line_count = 0, author_count;
+       struct object_id tree_oid, parent_oid;
+       unsigned author_count;
        int err;
        const char *buffer_begin = buffer;
        const char *p;
 
-       if (verify_headers(buffer, size, &commit->object, options))
+       if (verify_headers(buffer, size, oid, OBJ_COMMIT, options))
                return -1;
 
        if (!skip_prefix(buffer, "tree ", &buffer))
-               return report(options, &commit->object, FSCK_MSG_MISSING_TREE, "invalid format - expected 'tree' line");
+               return report(options, oid, OBJ_COMMIT, FSCK_MSG_MISSING_TREE, "invalid format - expected 'tree' line");
        if (parse_oid_hex(buffer, &tree_oid, &p) || *p != '\n') {
-               err = report(options, &commit->object, FSCK_MSG_BAD_TREE_SHA1, "invalid 'tree' line format - bad sha1");
+               err = report(options, oid, OBJ_COMMIT, FSCK_MSG_BAD_TREE_SHA1, "invalid 'tree' line format - bad sha1");
                if (err)
                        return err;
        }
        buffer = p + 1;
        while (skip_prefix(buffer, "parent ", &buffer)) {
-               if (parse_oid_hex(buffer, &oid, &p) || *p != '\n') {
-                       err = report(options, &commit->object, FSCK_MSG_BAD_PARENT_SHA1, "invalid 'parent' line format - bad sha1");
+               if (parse_oid_hex(buffer, &parent_oid, &p) || *p != '\n') {
+                       err = report(options, oid, OBJ_COMMIT, FSCK_MSG_BAD_PARENT_SHA1, "invalid 'parent' line format - bad sha1");
                        if (err)
                                return err;
                }
                buffer = p + 1;
-               parent_line_count++;
-       }
-       graft = lookup_commit_graft(the_repository, &commit->object.oid);
-       parent_count = commit_list_count(commit->parents);
-       if (graft) {
-               if (graft->nr_parent == -1 && !parent_count)
-                       ; /* shallow commit */
-               else if (graft->nr_parent != parent_count) {
-                       err = report(options, &commit->object, FSCK_MSG_MISSING_GRAFT, "graft objects missing");
-                       if (err)
-                               return err;
-               }
-       } else {
-               if (parent_count != parent_line_count) {
-                       err = report(options, &commit->object, FSCK_MSG_MISSING_PARENT, "parent objects missing");
-                       if (err)
-                               return err;
-               }
        }
        author_count = 0;
        while (skip_prefix(buffer, "author ", &buffer)) {
                author_count++;
-               err = fsck_ident(&buffer, &commit->object, options);
+               err = fsck_ident(&buffer, oid, OBJ_COMMIT, options);
                if (err)
                        return err;
        }
        if (author_count < 1)
-               err = report(options, &commit->object, FSCK_MSG_MISSING_AUTHOR, "invalid format - expected 'author' line");
+               err = report(options, oid, OBJ_COMMIT, FSCK_MSG_MISSING_AUTHOR, "invalid format - expected 'author' line");
        else if (author_count > 1)
-               err = report(options, &commit->object, FSCK_MSG_MULTIPLE_AUTHORS, "invalid format - multiple 'author' lines");
+               err = report(options, oid, OBJ_COMMIT, FSCK_MSG_MULTIPLE_AUTHORS, "invalid format - multiple 'author' lines");
        if (err)
                return err;
        if (!skip_prefix(buffer, "committer ", &buffer))
-               return report(options, &commit->object, FSCK_MSG_MISSING_COMMITTER, "invalid format - expected 'committer' line");
-       err = fsck_ident(&buffer, &commit->object, options);
+               return report(options, oid, OBJ_COMMIT, FSCK_MSG_MISSING_COMMITTER, "invalid format - expected 'committer' line");
+       err = fsck_ident(&buffer, oid, OBJ_COMMIT, options);
        if (err)
                return err;
-       if (!get_commit_tree(commit)) {
-               err = report(options, &commit->object, FSCK_MSG_BAD_TREE, "could not load commit's tree %s", oid_to_hex(&tree_oid));
-               if (err)
-                       return err;
-       }
        if (memchr(buffer_begin, '\0', size)) {
-               err = report(options, &commit->object, FSCK_MSG_NUL_IN_COMMIT,
+               err = report(options, oid, OBJ_COMMIT, FSCK_MSG_NUL_IN_COMMIT,
                             "NUL byte in the commit object body");
                if (err)
                        return err;
@@ -814,91 +820,60 @@ static int fsck_commit_buffer(struct commit *commit, const char *buffer,
        return 0;
 }
 
-static int fsck_commit(struct commit *commit, const char *data,
-       unsigned long size, struct fsck_options *options)
-{
-       const char *buffer = data ?  data : get_commit_buffer(commit, &size);
-       int ret = fsck_commit_buffer(commit, buffer, size, options);
-       if (!data)
-               unuse_commit_buffer(commit, buffer);
-       return ret;
-}
-
-static int fsck_tag_buffer(struct tag *tag, const char *data,
-       unsigned long size, struct fsck_options *options)
+static int fsck_tag(const struct object_id *oid, const char *buffer,
+                   unsigned long size, struct fsck_options *options)
 {
-       struct object_id oid;
+       struct object_id tagged_oid;
        int ret = 0;
-       const char *buffer;
-       char *to_free = NULL, *eol;
+       char *eol;
        struct strbuf sb = STRBUF_INIT;
        const char *p;
 
-       if (data)
-               buffer = data;
-       else {
-               enum object_type type;
-
-               buffer = to_free =
-                       read_object_file(&tag->object.oid, &type, &size);
-               if (!buffer)
-                       return report(options, &tag->object,
-                               FSCK_MSG_MISSING_TAG_OBJECT,
-                               "cannot read tag object");
-
-               if (type != OBJ_TAG) {
-                       ret = report(options, &tag->object,
-                               FSCK_MSG_TAG_OBJECT_NOT_TAG,
-                               "expected tag got %s",
-                           type_name(type));
-                       goto done;
-               }
-       }
-
-       ret = verify_headers(buffer, size, &tag->object, options);
+       ret = verify_headers(buffer, size, oid, OBJ_TAG, options);
        if (ret)
                goto done;
 
        if (!skip_prefix(buffer, "object ", &buffer)) {
-               ret = report(options, &tag->object, FSCK_MSG_MISSING_OBJECT, "invalid format - expected 'object' line");
+               ret = report(options, oid, OBJ_TAG, FSCK_MSG_MISSING_OBJECT, "invalid format - expected 'object' line");
                goto done;
        }
-       if (parse_oid_hex(buffer, &oid, &p) || *p != '\n') {
-               ret = report(options, &tag->object, FSCK_MSG_BAD_OBJECT_SHA1, "invalid 'object' line format - bad sha1");
+       if (parse_oid_hex(buffer, &tagged_oid, &p) || *p != '\n') {
+               ret = report(options, oid, OBJ_TAG, FSCK_MSG_BAD_OBJECT_SHA1, "invalid 'object' line format - bad sha1");
                if (ret)
                        goto done;
        }
        buffer = p + 1;
 
        if (!skip_prefix(buffer, "type ", &buffer)) {
-               ret = report(options, &tag->object, FSCK_MSG_MISSING_TYPE_ENTRY, "invalid format - expected 'type' line");
+               ret = report(options, oid, OBJ_TAG, FSCK_MSG_MISSING_TYPE_ENTRY, "invalid format - expected 'type' line");
                goto done;
        }
        eol = strchr(buffer, '\n');
        if (!eol) {
-               ret = report(options, &tag->object, FSCK_MSG_MISSING_TYPE, "invalid format - unexpected end after 'type' line");
+               ret = report(options, oid, OBJ_TAG, FSCK_MSG_MISSING_TYPE, "invalid format - unexpected end after 'type' line");
                goto done;
        }
        if (type_from_string_gently(buffer, eol - buffer, 1) < 0)
-               ret = report(options, &tag->object, FSCK_MSG_BAD_TYPE, "invalid 'type' value");
+               ret = report(options, oid, OBJ_TAG, FSCK_MSG_BAD_TYPE, "invalid 'type' value");
        if (ret)
                goto done;
        buffer = eol + 1;
 
        if (!skip_prefix(buffer, "tag ", &buffer)) {
-               ret = report(options, &tag->object, FSCK_MSG_MISSING_TAG_ENTRY, "invalid format - expected 'tag' line");
+               ret = report(options, oid, OBJ_TAG, FSCK_MSG_MISSING_TAG_ENTRY, "invalid format - expected 'tag' line");
                goto done;
        }
        eol = strchr(buffer, '\n');
        if (!eol) {
-               ret = report(options, &tag->object, FSCK_MSG_MISSING_TAG, "invalid format - unexpected end after 'type' line");
+               ret = report(options, oid, OBJ_TAG, FSCK_MSG_MISSING_TAG, "invalid format - unexpected end after 'type' line");
                goto done;
        }
        strbuf_addf(&sb, "refs/tags/%.*s", (int)(eol - buffer), buffer);
        if (check_refname_format(sb.buf, 0)) {
-               ret = report(options, &tag->object, FSCK_MSG_BAD_TAG_NAME,
-                          "invalid 'tag' name: %.*s",
-                          (int)(eol - buffer), buffer);
+               ret = report(options, oid, OBJ_TAG,
+                            FSCK_MSG_BAD_TAG_NAME,
+                            "invalid 'tag' name: %.*s",
+                            (int)(eol - buffer), buffer);
                if (ret)
                        goto done;
        }
@@ -906,32 +881,20 @@ static int fsck_tag_buffer(struct tag *tag, const char *data,
 
        if (!skip_prefix(buffer, "tagger ", &buffer)) {
                /* early tags do not contain 'tagger' lines; warn only */
-               ret = report(options, &tag->object, FSCK_MSG_MISSING_TAGGER_ENTRY, "invalid format - expected 'tagger' line");
+               ret = report(options, oid, OBJ_TAG, FSCK_MSG_MISSING_TAGGER_ENTRY, "invalid format - expected 'tagger' line");
                if (ret)
                        goto done;
        }
        else
-               ret = fsck_ident(&buffer, &tag->object, options);
+               ret = fsck_ident(&buffer, oid, OBJ_TAG, options);
 
 done:
        strbuf_release(&sb);
-       free(to_free);
        return ret;
 }
 
-static int fsck_tag(struct tag *tag, const char *data,
-       unsigned long size, struct fsck_options *options)
-{
-       struct object *tagged = tag->tagged;
-
-       if (!tagged)
-               return report(options, &tag->object, FSCK_MSG_BAD_TAG_OBJECT, "could not load tagged object");
-
-       return fsck_tag_buffer(tag, data, size, options);
-}
-
 struct fsck_gitmodules_data {
-       struct object *obj;
+       const struct object_id *oid;
        struct fsck_options *options;
        int ret;
 };
@@ -949,19 +912,22 @@ static int fsck_gitmodules_fn(const char *var, const char *value, void *vdata)
 
        name = xmemdupz(subsection, subsection_len);
        if (check_submodule_name(name) < 0)
-               data->ret |= report(data->options, data->obj,
+               data->ret |= report(data->options,
+                                   data->oid, OBJ_BLOB,
                                    FSCK_MSG_GITMODULES_NAME,
                                    "disallowed submodule name: %s",
                                    name);
        if (!strcmp(key, "url") && value &&
            looks_like_command_line_option(value))
-               data->ret |= report(data->options, data->obj,
+               data->ret |= report(data->options,
+                                   data->oid, OBJ_BLOB,
                                    FSCK_MSG_GITMODULES_URL,
                                    "disallowed submodule url: %s",
                                    value);
        if (!strcmp(key, "path") && value &&
            looks_like_command_line_option(value))
-               data->ret |= report(data->options, data->obj,
+               data->ret |= report(data->options,
+                                   data->oid, OBJ_BLOB,
                                    FSCK_MSG_GITMODULES_PATH,
                                    "disallowed submodule path: %s",
                                    value);
@@ -970,17 +936,17 @@ static int fsck_gitmodules_fn(const char *var, const char *value, void *vdata)
        return 0;
 }
 
-static int fsck_blob(struct blob *blob, const char *buf,
+static int fsck_blob(const struct object_id *oid, const char *buf,
                     unsigned long size, struct fsck_options *options)
 {
        struct fsck_gitmodules_data data;
        struct config_options config_opts = { 0 };
 
-       if (!oidset_contains(&gitmodules_found, &blob->object.oid))
+       if (!oidset_contains(&gitmodules_found, oid))
                return 0;
-       oidset_insert(&gitmodules_done, &blob->object.oid);
+       oidset_insert(&gitmodules_done, oid);
 
-       if (object_on_skiplist(options, &blob->object))
+       if (object_on_skiplist(options, oid))
                return 0;
 
        if (!buf) {
@@ -989,18 +955,18 @@ static int fsck_blob(struct blob *blob, const char *buf,
                 * blob too gigantic to load into memory. Let's just consider
                 * that an error.
                 */
-               return report(options, &blob->object,
+               return report(options, oid, OBJ_BLOB,
                              FSCK_MSG_GITMODULES_LARGE,
                              ".gitmodules too large to parse");
        }
 
-       data.obj = &blob->object;
+       data.oid = oid;
        data.options = options;
        data.ret = 0;
        config_opts.error_action = CONFIG_ERROR_SILENT;
        if (git_config_from_mem(fsck_gitmodules_fn, CONFIG_ORIGIN_BLOB,
                                ".gitmodules", buf, size, &data, &config_opts))
-               data.ret |= report(options, &blob->object,
+               data.ret |= report(options, oid, OBJ_BLOB,
                                   FSCK_MSG_GITMODULES_PARSE,
                                   "could not parse gitmodules blob");
 
@@ -1011,31 +977,33 @@ int fsck_object(struct object *obj, void *data, unsigned long size,
        struct fsck_options *options)
 {
        if (!obj)
-               return report(options, obj, FSCK_MSG_BAD_OBJECT_SHA1, "no valid object to fsck");
+               return report(options, NULL, OBJ_NONE, FSCK_MSG_BAD_OBJECT_SHA1, "no valid object to fsck");
 
        if (obj->type == OBJ_BLOB)
-               return fsck_blob((struct blob *)obj, data, size, options);
+               return fsck_blob(&obj->oid, data, size, options);
        if (obj->type == OBJ_TREE)
-               return fsck_tree((struct tree *) obj, options);
+               return fsck_tree(&obj->oid, data, size, options);
        if (obj->type == OBJ_COMMIT)
-               return fsck_commit((struct commit *) obj, (const char *) data,
-                       size, options);
+               return fsck_commit(&obj->oid, data, size, options);
        if (obj->type == OBJ_TAG)
-               return fsck_tag((struct tag *) obj, (const char *) data,
-                       size, options);
+               return fsck_tag(&obj->oid, data, size, options);
 
-       return report(options, obj, FSCK_MSG_UNKNOWN_TYPE, "unknown type '%d' (internal fsck error)",
-                         obj->type);
+       return report(options, &obj->oid, obj->type,
+                     FSCK_MSG_UNKNOWN_TYPE,
+                     "unknown type '%d' (internal fsck error)",
+                     obj->type);
 }
 
 int fsck_error_function(struct fsck_options *o,
-       struct object *obj, int msg_type, const char *message)
+                       const struct object_id *oid,
+                       enum object_type object_type,
+                       int msg_type, const char *message)
 {
        if (msg_type == FSCK_WARN) {
-               warning("object %s: %s", describe_object(o, obj), message);
+               warning("object %s: %s", fsck_describe_object(o, oid), message);
                return 0;
        }
-       error("object %s: %s", describe_object(o, obj), message);
+       error("object %s: %s", fsck_describe_object(o, oid), message);
        return 1;
 }
 
@@ -1047,7 +1015,6 @@ int fsck_finish(struct fsck_options *options)
 
        oidset_iter_init(&gitmodules_found, &iter);
        while ((oid = oidset_iter_next(&iter))) {
-               struct blob *blob;
                enum object_type type;
                unsigned long size;
                char *buf;
@@ -1055,29 +1022,22 @@ int fsck_finish(struct fsck_options *options)
                if (oidset_contains(&gitmodules_done, oid))
                        continue;
 
-               blob = lookup_blob(the_repository, oid);
-               if (!blob) {
-                       struct object *obj = lookup_unknown_object(oid);
-                       ret |= report(options, obj,
-                                     FSCK_MSG_GITMODULES_BLOB,
-                                     "non-blob found at .gitmodules");
-                       continue;
-               }
-
                buf = read_object_file(oid, &type, &size);
                if (!buf) {
-                       if (is_promisor_object(&blob->object.oid))
+                       if (is_promisor_object(oid))
                                continue;
-                       ret |= report(options, &blob->object,
+                       ret |= report(options,
+                                     oid, OBJ_BLOB,
                                      FSCK_MSG_GITMODULES_MISSING,
                                      "unable to read .gitmodules blob");
                        continue;
                }
 
                if (type == OBJ_BLOB)
-                       ret |= fsck_blob(blob, buf, size, options);
+                       ret |= fsck_blob(oid, buf, size, options);
                else
-                       ret |= report(options, &blob->object,
+                       ret |= report(options,
+                                     oid, type,
                                      FSCK_MSG_GITMODULES_BLOB,
                                      "non-blob found at .gitmodules");
                free(buf);
diff --git a/fsck.h b/fsck.h
index b95595ae5fee6c065c4f54ad66ddc0edb8824d37..69cf715e798ce29085b529b88374c5de75e69529 100644 (file)
--- a/fsck.h
+++ b/fsck.h
@@ -27,10 +27,12 @@ typedef int (*fsck_walk_func)(struct object *obj, int type, void *data, struct f
 
 /* callback for fsck_object, type is FSCK_ERROR or FSCK_WARN */
 typedef int (*fsck_error)(struct fsck_options *o,
-       struct object *obj, int type, const char *message);
+                         const struct object_id *oid, enum object_type object_type,
+                         int msg_type, const char *message);
 
 int fsck_error_function(struct fsck_options *o,
-       struct object *obj, int type, const char *message);
+                       const struct object_id *oid, enum object_type object_type,
+                       int msg_type, const char *message);
 
 struct fsck_options {
        fsck_walk_func walk;
@@ -38,7 +40,7 @@ struct fsck_options {
        unsigned strict:1;
        int *msg_type;
        struct oidset skiplist;
-       struct decoration *object_names;
+       kh_oid_map_t *object_names;
 };
 
 #define FSCK_OPTIONS_DEFAULT { NULL, fsck_error_function, 0, NULL, OIDSET_INIT }
@@ -52,7 +54,11 @@ struct fsck_options {
  *    0                everything OK
  */
 int fsck_walk(struct object *obj, void *data, struct fsck_options *options);
-/* If NULL is passed for data, we assume the object is local and read it. */
+
+/*
+ * Blob objects my pass a NULL data pointer, which indicates they are too large
+ * to fit in memory. All other types must pass a real buffer.
+ */
 int fsck_object(struct object *obj, void *data, unsigned long size,
        struct fsck_options *options);
 
@@ -63,4 +69,29 @@ int fsck_object(struct object *obj, void *data, unsigned long size,
  */
 int fsck_finish(struct fsck_options *options);
 
+/*
+ * Subsystem for storing human-readable names for each object.
+ *
+ * If fsck_enable_object_names() has not been called, all other functions are
+ * noops.
+ *
+ * Use fsck_put_object_name() to seed initial names (e.g. from refnames); the
+ * fsck code will extend that while walking trees, etc.
+ *
+ * Use fsck_get_object_name() to get a single name (or NULL if none). Or the
+ * more convenient describe_object(), which always produces an output string
+ * with the oid combined with the name (if any). Note that the return value
+ * points to a rotating array of static buffers, and may be invalidated by a
+ * subsequent call.
+ */
+void fsck_enable_object_names(struct fsck_options *options);
+const char *fsck_get_object_name(struct fsck_options *options,
+                                const struct object_id *oid);
+__attribute__((format (printf,3,4)))
+void fsck_put_object_name(struct fsck_options *options,
+                         const struct object_id *oid,
+                         const char *fmt, ...);
+const char *fsck_describe_object(struct fsck_options *options,
+                                const struct object_id *oid);
+
 #endif
index 1f4aa1b150dbe948910705c9ff4e75ad0a6a12a1..0477500b392b2baee699b51f2afa39381e66504d 100644 (file)
@@ -55,7 +55,8 @@ int read_fsmonitor_extension(struct index_state *istate, const void *data,
        }
        istate->fsmonitor_dirty = fsmonitor_dirty;
 
-       if (istate->fsmonitor_dirty->bit_size > istate->cache_nr)
+       if (!istate->split_index &&
+           istate->fsmonitor_dirty->bit_size > istate->cache_nr)
                BUG("fsmonitor_dirty has more entries than the index (%"PRIuMAX" > %u)",
                    (uintmax_t)istate->fsmonitor_dirty->bit_size, istate->cache_nr);
 
@@ -83,7 +84,8 @@ void write_fsmonitor_extension(struct strbuf *sb, struct index_state *istate)
        uint32_t ewah_size = 0;
        int fixup = 0;
 
-       if (istate->fsmonitor_dirty->bit_size > istate->cache_nr)
+       if (!istate->split_index &&
+           istate->fsmonitor_dirty->bit_size > istate->cache_nr)
                BUG("fsmonitor_dirty has more entries than the index (%"PRIuMAX" > %u)",
                    (uintmax_t)istate->fsmonitor_dirty->bit_size, istate->cache_nr);
 
index b31613cb8aa8decd3f808d5d29e047243fa1eac6..1057f389d3b299f47d9575f254df7dc9305b0d30 100755 (executable)
@@ -600,7 +600,7 @@ my $cvs = CVSconn->new($opt_d, $cvs_tree);
 sub pdate($) {
        my ($d) = @_;
        m#(\d{2,4})/(\d\d)/(\d\d)\s(\d\d):(\d\d)(?::(\d\d))?#
-               or die "Unparseable date: $d\n";
+               or die "Unparsable date: $d\n";
        my $y=$1;
        $y+=100 if $y<70;
        $y+=1900 if $y<1000;
index 07ad4a545910da5ff5a591c159da292d5101dec3..53fa5743018f19d8428f4a24379907bcbb6d80bf 100755 (executable)
@@ -193,7 +193,8 @@ create_stash () {
                        GIT_INDEX_FILE="$TMPindex" &&
                        export GIT_INDEX_FILE &&
                        git diff-index --name-only -z HEAD -- "$@" >"$TMP-stagenames" &&
-                       git update-index -z --add --remove --stdin <"$TMP-stagenames" &&
+                       git update-index --ignore-skip-worktree-entries \
+                               -z --add --remove --stdin <"$TMP-stagenames" &&
                        git write-tree &&
                        rm -f "$TMPindex"
                ) ) ||
index 5f92c89c1c1be5ba2fadedd1d7a8d013fb7577fd..dc95656f75c1c4a066665b91d31b387936998961 100755 (executable)
@@ -1228,7 +1228,7 @@ sub process_address_list {
 # domain name that corresponds the IP address in the HELO/EHLO
 # handshake. This is used to verify the connection and prevent
 # spammers from trying to hide their identity. If the DNS and IP don't
-# match, the receiveing MTA may deny the connection.
+# match, the receiving MTA may deny the connection.
 #
 # Here is a deny example of Net::SMTP with the default "localhost.localdomain"
 #
index 7fef19fe591c2dd077ec4688f20e3c7bec521f13..0f857d790b5035b128485ab41e7afa7cb3c0d81e 100755 (executable)
@@ -1657,15 +1657,15 @@ sub quot_cec {
        my $cntrl = shift;
        my %opts = @_;
        my %es = ( # character escape codes, aka escape sequences
-               "\t" => '\t',   # tab            (HT)
-               "\n" => '\n',   # line feed      (LF)
-               "\r" => '\r',   # carrige return (CR)
-               "\f" => '\f',   # form feed      (FF)
-               "\b" => '\b',   # backspace      (BS)
-               "\a" => '\a',   # alarm (bell)   (BEL)
-               "\e" => '\e',   # escape         (ESC)
-               "\013" => '\v', # vertical tab   (VT)
-               "\000" => '\0', # nul character  (NUL)
+               "\t" => '\t',   # tab             (HT)
+               "\n" => '\n',   # line feed       (LF)
+               "\r" => '\r',   # carriage return (CR)
+               "\f" => '\f',   # form feed       (FF)
+               "\b" => '\b',   # backspace       (BS)
+               "\a" => '\a',   # alarm (bell)    (BEL)
+               "\e" => '\e',   # escape          (ESC)
+               "\013" => '\v', # vertical tab    (VT)
+               "\000" => '\0', # nul character   (NUL)
        );
        my $chr = ( (exists $es{$cntrl})
                    ? $es{$cntrl}
@@ -4048,7 +4048,7 @@ sub print_feed_meta {
 
                        $href_params{'extra_options'} = undef;
                        $href_params{'action'} = $type;
-                       $link_attr{'-href'} = href(%href_params);
+                       $link_attr{'-href'} = esc_attr(href(%href_params));
                        print "<link ".
                              "rel=\"$link_attr{'-rel'}\" ".
                              "title=\"$link_attr{'-title'}\" ".
@@ -4057,7 +4057,7 @@ sub print_feed_meta {
                              "/>\n";
 
                        $href_params{'extra_options'} = '--no-merges';
-                       $link_attr{'-href'} = href(%href_params);
+                       $link_attr{'-href'} = esc_attr(href(%href_params));
                        $link_attr{'-title'} .= ' (no merges)';
                        print "<link ".
                              "rel=\"$link_attr{'-rel'}\" ".
@@ -4070,10 +4070,12 @@ sub print_feed_meta {
        } else {
                printf('<link rel="alternate" title="%s projects list" '.
                       'href="%s" type="text/plain; charset=utf-8" />'."\n",
-                      esc_attr($site_name), href(project=>undef, action=>"project_index"));
+                      esc_attr($site_name),
+                      esc_attr(href(project=>undef, action=>"project_index")));
                printf('<link rel="alternate" title="%s projects feeds" '.
                       'href="%s" type="text/x-opml" />'."\n",
-                      esc_attr($site_name), href(project=>undef, action=>"opml"));
+                      esc_attr($site_name),
+                      esc_attr(href(project=>undef, action=>"opml")));
        }
 }
 
@@ -4287,8 +4289,8 @@ sub git_footer_html {
        if (defined $action &&
            $action eq 'blame_incremental') {
                print qq!<script type="text/javascript">\n!.
-                     qq!startBlame("!. href(action=>"blame_data", -replay=>1) .qq!",\n!.
-                     qq!           "!. href() .qq!");\n!.
+                     qq!startBlame("!. esc_attr(href(action=>"blame_data", -replay=>1)) .qq!",\n!.
+                     qq!           "!. esc_attr(href()) .qq!");\n!.
                      qq!</script>\n!;
        } else {
                my ($jstimezone, $tz_cookie, $datetime_class) =
@@ -7155,8 +7157,8 @@ sub git_blob {
                        print qq! alt="!.esc_attr($file_name).qq!" title="!.esc_attr($file_name).qq!"!;
                }
                print qq! src="! .
-                     href(action=>"blob_plain", hash=>$hash,
-                          hash_base=>$hash_base, file_name=>$file_name) .
+                     esc_attr(href(action=>"blob_plain", hash=>$hash,
+                          hash_base=>$hash_base, file_name=>$file_name)) .
                      qq!" />\n!;
        } else {
                my $nr;
@@ -8239,6 +8241,7 @@ sub git_feed {
        } else {
                $alt_url = href(-full=>1, action=>"summary");
        }
+       $alt_url = esc_attr($alt_url);
        print qq!<?xml version="1.0" encoding="utf-8"?>\n!;
        if ($format eq 'rss') {
                print <<XML;
@@ -8276,7 +8279,7 @@ XML
                      $alt_url . '" />' . "\n" .
                      '<link rel="self" type="' . $content_type . '" href="' .
                      $cgi->self_url() . '" />' . "\n" .
-                     "<id>" . href(-full=>1) . "</id>\n" .
+                     "<id>" . esc_url(href(-full=>1)) . "</id>\n" .
                      # use project owner for feed author
                      "<author><name>$owner</name></author>\n";
                if (defined $favicon) {
@@ -8322,7 +8325,7 @@ XML
                              "<author>" . esc_html($co{'author'}) . "</author>\n" .
                              "<pubDate>$cd{'rfc2822'}</pubDate>\n" .
                              "<guid isPermaLink=\"true\">$co_url</guid>\n" .
-                             "<link>$co_url</link>\n" .
+                             "<link>" . esc_html($co_url) . "</link>\n" .
                              "<description>" . esc_html($co{'title'}) . "</description>\n" .
                              "<content:encoded>" .
                              "<![CDATA[\n";
@@ -8344,8 +8347,8 @@ XML
                        }
                        print "</contributor>\n" .
                              "<published>$cd{'iso-8601'}</published>\n" .
-                             "<link rel=\"alternate\" type=\"text/html\" href=\"$co_url\" />\n" .
-                             "<id>$co_url</id>\n" .
+                             "<link rel=\"alternate\" type=\"text/html\" href=\"" . esc_attr($co_url) . "\" />\n" .
+                             "<id>" . esc_html($co_url) . "</id>\n" .
                              "<content type=\"xhtml\" xml:base=\"" . esc_url($my_url) . "\">\n" .
                              "<div xmlns=\"http://www.w3.org/1999/xhtml\">\n";
                }
@@ -8452,8 +8455,8 @@ XML
                }
 
                my $path = esc_html(chop_str($proj{'path'}, 25, 5));
-               my $rss  = href('project' => $proj{'path'}, 'action' => 'rss', -full => 1);
-               my $html = href('project' => $proj{'path'}, 'action' => 'summary', -full => 1);
+               my $rss  = esc_attr(href('project' => $proj{'path'}, 'action' => 'rss', -full => 1));
+               my $html = esc_attr(href('project' => $proj{'path'}, 'action' => 'summary', -full => 1));
                print "<outline type=\"rss\" text=\"$path\" title=\"$path\" xmlUrl=\"$rss\" htmlUrl=\"$html\"/>\n";
        }
        print <<XML;
diff --git a/graph.c b/graph.c
index f53135485f565baf7a0de1a5e4cffe2a4b2b55bd..e3fd0ea5f806a2598800041e97f049b2e5ba9ae3 100644 (file)
--- a/graph.c
+++ b/graph.c
@@ -112,14 +112,42 @@ static const char *column_get_color_code(unsigned short color)
        return column_colors[color];
 }
 
-static void strbuf_write_column(struct strbuf *sb, const struct column *c,
-                               char col_char)
+struct graph_line {
+       struct strbuf *buf;
+       size_t width;
+};
+
+static inline void graph_line_addch(struct graph_line *line, int c)
+{
+       strbuf_addch(line->buf, c);
+       line->width++;
+}
+
+static inline void graph_line_addchars(struct graph_line *line, int c, size_t n)
+{
+       strbuf_addchars(line->buf, c, n);
+       line->width += n;
+}
+
+static inline void graph_line_addstr(struct graph_line *line, const char *s)
+{
+       strbuf_addstr(line->buf, s);
+       line->width += strlen(s);
+}
+
+static inline void graph_line_addcolor(struct graph_line *line, unsigned short color)
+{
+       strbuf_addstr(line->buf, column_get_color_code(color));
+}
+
+static void graph_line_write_column(struct graph_line *line, const struct column *c,
+                                   char col_char)
 {
        if (c->color < column_colors_max)
-               strbuf_addstr(sb, column_get_color_code(c->color));
-       strbuf_addch(sb, col_char);
+               graph_line_addcolor(line, c->color);
+       graph_line_addch(line, col_char);
        if (c->color < column_colors_max)
-               strbuf_addstr(sb, column_get_color_code(column_colors_max));
+               graph_line_addcolor(line, column_colors_max);
 }
 
 struct git_graph {
@@ -174,10 +202,64 @@ struct git_graph {
         * previous commit.
         */
        int prev_commit_index;
+       /*
+        * Which layout variant to use to display merge commits. If the
+        * commit's first parent is known to be in a column to the left of the
+        * merge, then this value is 0 and we use the layout on the left.
+        * Otherwise, the value is 1 and the layout on the right is used. This
+        * field tells us how many columns the first parent occupies.
+        *
+        *              0)                      1)
+        *
+        *              | | | *-.               | | *---.
+        *              | |_|/|\ \              | | |\ \ \
+        *              |/| | | | |             | | | | | *
+        */
+       int merge_layout;
+       /*
+        * The number of columns added to the graph by the current commit. For
+        * 2-way and octopus merges, this is is usually one less than the
+        * number of parents:
+        *
+        *              | | |                   | |    \
+        *              | * |                   | *---. \
+        *              | |\ \                  | |\ \ \ \
+        *              | | | |                 | | | | | |
+        *
+        *              num_parents: 2          num_parents: 4
+        *              edges_added: 1          edges_added: 3
+        *
+        * For left-skewed merges, the first parent fuses with its neighbor and
+        * so one less column is added:
+        *
+        *              | | |                   | |  \
+        *              | * |                   | *-. \
+        *              |/| |                   |/|\ \ \
+        *              | | |                   | | | | |
+        *
+        *              num_parents: 2          num_parents: 4
+        *              edges_added: 0          edges_added: 2
+        *
+        * This number determines how edges to the right of the merge are
+        * displayed in commit and post-merge lines; if no columns have been
+        * added then a vertical line should be used where a right-tracking
+        * line would otherwise be used.
+        *
+        *              | * \                   | * |
+        *              | |\ \                  |/| |
+        *              | | * \                 | * |
+        */
+       int edges_added;
+       /*
+        * The number of columns added by the previous commit, which is used to
+        * smooth edges appearing to the right of a commit in a commit line
+        * following a post-merge line.
+        */
+       int prev_edges_added;
        /*
         * The maximum number of columns that can be stored in the columns
         * and new_columns arrays.  This is also half the number of entries
-        * that can be stored in the mapping and new_mapping arrays.
+        * that can be stored in the mapping and old_mapping arrays.
         */
        int column_capacity;
        /*
@@ -215,12 +297,12 @@ struct git_graph {
         */
        int *mapping;
        /*
-        * A temporary array for computing the next mapping state
-        * while we are outputting a mapping line.  This is stored as part
-        * of the git_graph simply so we don't have to allocate a new
-        * temporary array each time we have to output a collapsing line.
+        * A copy of the contents of the mapping array from the last commit,
+        * which we use to improve the display of columns that are tracking
+        * from right to left through a commit line.  We also use this to
+        * avoid allocating a fresh array when we compute the next mapping.
         */
-       int *new_mapping;
+       int *old_mapping;
        /*
         * The current default column color being used.  This is
         * stored as an index into the array column_colors.
@@ -285,6 +367,9 @@ struct git_graph *graph_init(struct rev_info *opt)
        graph->prev_state = GRAPH_PADDING;
        graph->commit_index = 0;
        graph->prev_commit_index = 0;
+       graph->merge_layout = 0;
+       graph->edges_added = 0;
+       graph->prev_edges_added = 0;
        graph->num_columns = 0;
        graph->num_new_columns = 0;
        graph->mapping_size = 0;
@@ -303,7 +388,7 @@ struct git_graph *graph_init(struct rev_info *opt)
        ALLOC_ARRAY(graph->columns, graph->column_capacity);
        ALLOC_ARRAY(graph->new_columns, graph->column_capacity);
        ALLOC_ARRAY(graph->mapping, 2 * graph->column_capacity);
-       ALLOC_ARRAY(graph->new_mapping, 2 * graph->column_capacity);
+       ALLOC_ARRAY(graph->old_mapping, 2 * graph->column_capacity);
 
        /*
         * The diff output prefix callback, with this we can make
@@ -333,7 +418,7 @@ static void graph_ensure_capacity(struct git_graph *graph, int num_columns)
        REALLOC_ARRAY(graph->columns, graph->column_capacity);
        REALLOC_ARRAY(graph->new_columns, graph->column_capacity);
        REALLOC_ARRAY(graph->mapping, graph->column_capacity * 2);
-       REALLOC_ARRAY(graph->new_mapping, graph->column_capacity * 2);
+       REALLOC_ARRAY(graph->old_mapping, graph->column_capacity * 2);
 }
 
 /*
@@ -432,74 +517,76 @@ static unsigned short graph_find_commit_color(const struct git_graph *graph,
        return graph_get_current_column_color(graph);
 }
 
-static void graph_insert_into_new_columns(struct git_graph *graph,
-                                         struct commit *commit,
-                                         int *mapping_index)
+static int graph_find_new_column_by_commit(struct git_graph *graph,
+                                          struct commit *commit)
 {
        int i;
-
-       /*
-        * If the commit is already in the new_columns list, we don't need to
-        * add it.  Just update the mapping correctly.
-        */
        for (i = 0; i < graph->num_new_columns; i++) {
-               if (graph->new_columns[i].commit == commit) {
-                       graph->mapping[*mapping_index] = i;
-                       *mapping_index += 2;
-                       return;
-               }
+               if (graph->new_columns[i].commit == commit)
+                       return i;
        }
-
-       /*
-        * This commit isn't already in new_columns.  Add it.
-        */
-       graph->new_columns[graph->num_new_columns].commit = commit;
-       graph->new_columns[graph->num_new_columns].color = graph_find_commit_color(graph, commit);
-       graph->mapping[*mapping_index] = graph->num_new_columns;
-       *mapping_index += 2;
-       graph->num_new_columns++;
+       return -1;
 }
 
-static void graph_update_width(struct git_graph *graph,
-                              int is_commit_in_existing_columns)
+static void graph_insert_into_new_columns(struct git_graph *graph,
+                                         struct commit *commit,
+                                         int idx)
 {
-       /*
-        * Compute the width needed to display the graph for this commit.
-        * This is the maximum width needed for any row.  All other rows
-        * will be padded to this width.
-        *
-        * Compute the number of columns in the widest row:
-        * Count each existing column (graph->num_columns), and each new
-        * column added by this commit.
-        */
-       int max_cols = graph->num_columns + graph->num_parents;
+       int i = graph_find_new_column_by_commit(graph, commit);
+       int mapping_idx;
 
        /*
-        * Even if the current commit has no parents to be printed, it
-        * still takes up a column for itself.
+        * If the commit is not already in the new_columns array, then add it
+        * and record it as being in the final column.
         */
-       if (graph->num_parents < 1)
-               max_cols++;
+       if (i < 0) {
+               i = graph->num_new_columns++;
+               graph->new_columns[i].commit = commit;
+               graph->new_columns[i].color = graph_find_commit_color(graph, commit);
+       }
 
-       /*
-        * We added a column for the current commit as part of
-        * graph->num_parents.  If the current commit was already in
-        * graph->columns, then we have double counted it.
-        */
-       if (is_commit_in_existing_columns)
-               max_cols--;
+       if (graph->num_parents > 1 && idx > -1 && graph->merge_layout == -1) {
+               /*
+                * If this is the first parent of a merge, choose a layout for
+                * the merge line based on whether the parent appears in a
+                * column to the left of the merge
+                */
+               int dist, shift;
 
-       /*
-        * Each column takes up 2 spaces
-        */
-       graph->width = max_cols * 2;
+               dist = idx - i;
+               shift = (dist > 1) ? 2 * dist - 3 : 1;
+
+               graph->merge_layout = (dist > 0) ? 0 : 1;
+               graph->edges_added = graph->num_parents + graph->merge_layout  - 2;
+
+               mapping_idx = graph->width + (graph->merge_layout - 1) * shift;
+               graph->width += 2 * graph->merge_layout;
+
+       } else if (graph->edges_added > 0 && i == graph->mapping[graph->width - 2]) {
+               /*
+                * If some columns have been added by a merge, but this commit
+                * was found in the last existing column, then adjust the
+                * numbers so that the two edges immediately join, i.e.:
+                *
+                *              * |             * |
+                *              |\ \    =>      |\|
+                *              | |/            | *
+                *              | *
+                */
+               mapping_idx = graph->width - 2;
+               graph->edges_added = -1;
+       } else {
+               mapping_idx = graph->width;
+               graph->width += 2;
+       }
+
+       graph->mapping[mapping_idx] = i;
 }
 
 static void graph_update_columns(struct git_graph *graph)
 {
        struct commit_list *parent;
        int max_new_columns;
-       int mapping_idx;
        int i, seen_this, is_commit_in_columns;
 
        /*
@@ -532,6 +619,10 @@ static void graph_update_columns(struct git_graph *graph)
        for (i = 0; i < graph->mapping_size; i++)
                graph->mapping[i] = -1;
 
+       graph->width = 0;
+       graph->prev_edges_added = graph->edges_added;
+       graph->edges_added = 0;
+
        /*
         * Populate graph->new_columns and graph->mapping
         *
@@ -542,7 +633,6 @@ static void graph_update_columns(struct git_graph *graph)
         * supposed to end up after the collapsing is performed.
         */
        seen_this = 0;
-       mapping_idx = 0;
        is_commit_in_columns = 1;
        for (i = 0; i <= graph->num_columns; i++) {
                struct commit *col_commit;
@@ -556,9 +646,9 @@ static void graph_update_columns(struct git_graph *graph)
                }
 
                if (col_commit == graph->commit) {
-                       int old_mapping_idx = mapping_idx;
                        seen_this = 1;
                        graph->commit_index = i;
+                       graph->merge_layout = -1;
                        for (parent = first_interesting_parent(graph);
                             parent;
                             parent = next_interesting_parent(graph, parent)) {
@@ -571,21 +661,18 @@ static void graph_update_columns(struct git_graph *graph)
                                    !is_commit_in_columns) {
                                        graph_increment_column_color(graph);
                                }
-                               graph_insert_into_new_columns(graph,
-                                                             parent->item,
-                                                             &mapping_idx);
+                               graph_insert_into_new_columns(graph, parent->item, i);
                        }
                        /*
-                        * We always need to increment mapping_idx by at
+                        * We always need to increment graph->width by at
                         * least 2, even if it has no interesting parents.
                         * The current commit always takes up at least 2
                         * spaces.
                         */
-                       if (mapping_idx == old_mapping_idx)
-                               mapping_idx += 2;
+                       if (graph->num_parents == 0)
+                               graph->width += 2;
                } else {
-                       graph_insert_into_new_columns(graph, col_commit,
-                                                     &mapping_idx);
+                       graph_insert_into_new_columns(graph, col_commit, -1);
                }
        }
 
@@ -595,11 +682,43 @@ static void graph_update_columns(struct git_graph *graph)
        while (graph->mapping_size > 1 &&
               graph->mapping[graph->mapping_size - 1] < 0)
                graph->mapping_size--;
+}
+
+static int graph_num_dashed_parents(struct git_graph *graph)
+{
+       return graph->num_parents + graph->merge_layout - 3;
+}
 
+static int graph_num_expansion_rows(struct git_graph *graph)
+{
        /*
-        * Compute graph->width for this commit
+        * Normally, we need two expansion rows for each dashed parent line from
+        * an octopus merge:
+        *
+        *              | *
+        *              | |\
+        *              | | \
+        *              | |  \
+        *              | *-. \
+        *              | |\ \ \
+        *
+        * If the merge is skewed to the left, then its parents occupy one less
+        * column, and we don't need as many expansion rows to route around it;
+        * in some cases that means we don't need any expansion rows at all:
+        *
+        *              | *
+        *              | |\
+        *              | * \
+        *              |/|\ \
         */
-       graph_update_width(graph, is_commit_in_columns);
+       return graph_num_dashed_parents(graph) * 2;
+}
+
+static int graph_needs_pre_commit_line(struct git_graph *graph)
+{
+       return graph->num_parents >= 3 &&
+              graph->commit_index < (graph->num_columns - 1) &&
+              graph->expansion_row < graph_num_expansion_rows(graph);
 }
 
 void graph_update(struct git_graph *graph, struct commit *commit)
@@ -657,8 +776,7 @@ void graph_update(struct git_graph *graph, struct commit *commit)
         */
        if (graph->state != GRAPH_PADDING)
                graph->state = GRAPH_SKIP;
-       else if (graph->num_parents >= 3 &&
-                graph->commit_index < (graph->num_columns - 1))
+       else if (graph_needs_pre_commit_line(graph))
                graph->state = GRAPH_PRE_COMMIT;
        else
                graph->state = GRAPH_COMMIT;
@@ -686,8 +804,7 @@ static int graph_is_mapping_correct(struct git_graph *graph)
        return 1;
 }
 
-static void graph_pad_horizontally(struct git_graph *graph, struct strbuf *sb,
-                                  int chars_written)
+static void graph_pad_horizontally(struct git_graph *graph, struct graph_line *line)
 {
        /*
         * Add additional spaces to the end of the strbuf, so that all
@@ -696,34 +813,22 @@ static void graph_pad_horizontally(struct git_graph *graph, struct strbuf *sb,
         * This way, fields printed to the right of the graph will remain
         * aligned for the entire commit.
         */
-       if (chars_written < graph->width)
-               strbuf_addchars(sb, ' ', graph->width - chars_written);
+       if (line->width < graph->width)
+               graph_line_addchars(line, ' ', graph->width - line->width);
 }
 
 static void graph_output_padding_line(struct git_graph *graph,
-                                     struct strbuf *sb)
+                                     struct graph_line *line)
 {
        int i;
 
-       /*
-        * We could conceivable be called with a NULL commit
-        * if our caller has a bug, and invokes graph_next_line()
-        * immediately after graph_init(), without first calling
-        * graph_update().  Return without outputting anything in this
-        * case.
-        */
-       if (!graph->commit)
-               return;
-
        /*
         * Output a padding row, that leaves all branch lines unchanged
         */
        for (i = 0; i < graph->num_new_columns; i++) {
-               strbuf_write_column(sb, &graph->new_columns[i], '|');
-               strbuf_addch(sb, ' ');
+               graph_line_write_column(line, &graph->new_columns[i], '|');
+               graph_line_addch(line, ' ');
        }
-
-       graph_pad_horizontally(graph, sb, graph->num_new_columns * 2);
 }
 
 
@@ -733,28 +838,24 @@ int graph_width(struct git_graph *graph)
 }
 
 
-static void graph_output_skip_line(struct git_graph *graph, struct strbuf *sb)
+static void graph_output_skip_line(struct git_graph *graph, struct graph_line *line)
 {
        /*
         * Output an ellipsis to indicate that a portion
         * of the graph is missing.
         */
-       strbuf_addstr(sb, "...");
-       graph_pad_horizontally(graph, sb, 3);
+       graph_line_addstr(line, "...");
 
-       if (graph->num_parents >= 3 &&
-           graph->commit_index < (graph->num_columns - 1))
+       if (graph_needs_pre_commit_line(graph))
                graph_update_state(graph, GRAPH_PRE_COMMIT);
        else
                graph_update_state(graph, GRAPH_COMMIT);
 }
 
 static void graph_output_pre_commit_line(struct git_graph *graph,
-                                        struct strbuf *sb)
+                                        struct graph_line *line)
 {
-       int num_expansion_rows;
        int i, seen_this;
-       int chars_written;
 
        /*
         * This function formats a row that increases the space around a commit
@@ -764,27 +865,24 @@ static void graph_output_pre_commit_line(struct git_graph *graph,
         * We need 2 extra rows for every parent over 2.
         */
        assert(graph->num_parents >= 3);
-       num_expansion_rows = (graph->num_parents - 2) * 2;
 
        /*
         * graph->expansion_row tracks the current expansion row we are on.
         * It should be in the range [0, num_expansion_rows - 1]
         */
        assert(0 <= graph->expansion_row &&
-              graph->expansion_row < num_expansion_rows);
+              graph->expansion_row < graph_num_expansion_rows(graph));
 
        /*
         * Output the row
         */
        seen_this = 0;
-       chars_written = 0;
        for (i = 0; i < graph->num_columns; i++) {
                struct column *col = &graph->columns[i];
                if (col->commit == graph->commit) {
                        seen_this = 1;
-                       strbuf_write_column(sb, col, '|');
-                       strbuf_addchars(sb, ' ', graph->expansion_row);
-                       chars_written += 1 + graph->expansion_row;
+                       graph_line_write_column(line, col, '|');
+                       graph_line_addchars(line, ' ', graph->expansion_row);
                } else if (seen_this && (graph->expansion_row == 0)) {
                        /*
                         * This is the first line of the pre-commit output.
@@ -797,33 +895,27 @@ static void graph_output_pre_commit_line(struct git_graph *graph,
                         */
                        if (graph->prev_state == GRAPH_POST_MERGE &&
                            graph->prev_commit_index < i)
-                               strbuf_write_column(sb, col, '\\');
+                               graph_line_write_column(line, col, '\\');
                        else
-                               strbuf_write_column(sb, col, '|');
-                       chars_written++;
+                               graph_line_write_column(line, col, '|');
                } else if (seen_this && (graph->expansion_row > 0)) {
-                       strbuf_write_column(sb, col, '\\');
-                       chars_written++;
+                       graph_line_write_column(line, col, '\\');
                } else {
-                       strbuf_write_column(sb, col, '|');
-                       chars_written++;
+                       graph_line_write_column(line, col, '|');
                }
-               strbuf_addch(sb, ' ');
-               chars_written++;
+               graph_line_addch(line, ' ');
        }
 
-       graph_pad_horizontally(graph, sb, chars_written);
-
        /*
         * Increment graph->expansion_row,
         * and move to state GRAPH_COMMIT if necessary
         */
        graph->expansion_row++;
-       if (graph->expansion_row >= num_expansion_rows)
+       if (!graph_needs_pre_commit_line(graph))
                graph_update_state(graph, GRAPH_COMMIT);
 }
 
-static void graph_output_commit_char(struct git_graph *graph, struct strbuf *sb)
+static void graph_output_commit_char(struct git_graph *graph, struct graph_line *line)
 {
        /*
         * For boundary commits, print 'o'
@@ -831,72 +923,67 @@ static void graph_output_commit_char(struct git_graph *graph, struct strbuf *sb)
         */
        if (graph->commit->object.flags & BOUNDARY) {
                assert(graph->revs->boundary);
-               strbuf_addch(sb, 'o');
+               graph_line_addch(line, 'o');
                return;
        }
 
        /*
         * get_revision_mark() handles all other cases without assert()
         */
-       strbuf_addstr(sb, get_revision_mark(graph->revs, graph->commit));
+       graph_line_addstr(line, get_revision_mark(graph->revs, graph->commit));
 }
 
 /*
- * Draw the horizontal dashes of an octopus merge and return the number of
- * characters written.
+ * Draw the horizontal dashes of an octopus merge.
  */
-static int graph_draw_octopus_merge(struct git_graph *graph,
-                                   struct strbuf *sb)
+static void graph_draw_octopus_merge(struct git_graph *graph, struct graph_line *line)
 {
        /*
-        * Here dashless_parents represents the number of parents which don't
-        * need to have dashes (the edges labeled "0" and "1").  And
-        * dashful_parents are the remaining ones.
+        * The parents of a merge commit can be arbitrarily reordered as they
+        * are mapped onto display columns, for example this is a valid merge:
         *
-        * | *---.
-        * | |\ \ \
-        * | | | | |
-        * x 0 1 2 3
+        *      | | *---.
+        *      | | |\ \ \
+        *      | | |/ / /
+        *      | |/| | /
+        *      | |_|_|/
+        *      |/| | |
+        *      3 1 0 2
         *
-        */
-       const int dashless_parents = 2;
-       int dashful_parents = graph->num_parents - dashless_parents;
-
-       /*
-        * Usually, we add one new column for each parent (like the diagram
-        * above) but sometimes the first parent goes into an existing column,
-        * like this:
+        * The numbers denote which parent of the merge each visual column
+        * corresponds to; we can't assume that the parents will initially
+        * display in the order given by new_columns.
         *
-        * | *---.
-        * | |\ \ \
-        * |/ / / /
-        * x 0 1 2
+        * To find the right color for each dash, we need to consult the
+        * mapping array, starting from the column 2 places to the right of the
+        * merge commit, and use that to find out which logical column each
+        * edge will collapse to.
         *
-        * In which case the number of parents will be one greater than the
-        * number of added columns.
+        * Commits are rendered once all edges have collapsed to their correct
+        * logcial column, so commit_index gives us the right visual offset for
+        * the merge commit.
         */
-       int added_cols = (graph->num_new_columns - graph->num_columns);
-       int parent_in_old_cols = graph->num_parents - added_cols;
 
-       /*
-        * In both cases, commit_index corresponds to the edge labeled "0".
-        */
-       int first_col = graph->commit_index + dashless_parents
-           - parent_in_old_cols;
+       int i, j;
+       struct column *col;
 
-       int i;
-       for (i = 0; i < dashful_parents; i++) {
-               strbuf_write_column(sb, &graph->new_columns[i+first_col], '-');
-               strbuf_write_column(sb, &graph->new_columns[i+first_col],
-                                   i == dashful_parents-1 ? '.' : '-');
+       int dashed_parents = graph_num_dashed_parents(graph);
+
+       for (i = 0; i < dashed_parents; i++) {
+               j = graph->mapping[(graph->commit_index + i + 2) * 2];
+               col = &graph->new_columns[j];
+
+               graph_line_write_column(line, col, '-');
+               graph_line_write_column(line, col, (i == dashed_parents - 1) ? '.' : '-');
        }
-       return 2 * dashful_parents;
+
+       return;
 }
 
-static void graph_output_commit_line(struct git_graph *graph, struct strbuf *sb)
+static void graph_output_commit_line(struct git_graph *graph, struct graph_line *line)
 {
        int seen_this = 0;
-       int i, chars_written;
+       int i;
 
        /*
         * Output the row containing this commit
@@ -906,7 +993,6 @@ static void graph_output_commit_line(struct git_graph *graph, struct strbuf *sb)
         * children that we have already processed.)
         */
        seen_this = 0;
-       chars_written = 0;
        for (i = 0; i <= graph->num_columns; i++) {
                struct column *col = &graph->columns[i];
                struct commit *col_commit;
@@ -920,19 +1006,17 @@ static void graph_output_commit_line(struct git_graph *graph, struct strbuf *sb)
 
                if (col_commit == graph->commit) {
                        seen_this = 1;
-                       graph_output_commit_char(graph, sb);
-                       chars_written++;
+                       graph_output_commit_char(graph, line);
 
                        if (graph->num_parents > 2)
-                               chars_written += graph_draw_octopus_merge(graph,
-                                                                         sb);
-               } else if (seen_this && (graph->num_parents > 2)) {
-                       strbuf_write_column(sb, col, '\\');
-                       chars_written++;
-               } else if (seen_this && (graph->num_parents == 2)) {
+                               graph_draw_octopus_merge(graph, line);
+               } else if (seen_this && (graph->edges_added > 1)) {
+                       graph_line_write_column(line, col, '\\');
+               } else if (seen_this && (graph->edges_added == 1)) {
                        /*
-                        * This is a 2-way merge commit.
-                        * There is no GRAPH_PRE_COMMIT stage for 2-way
+                        * This is either a right-skewed 2-way merge
+                        * commit, or a left-skewed 3-way merge.
+                        * There is no GRAPH_PRE_COMMIT stage for such
                         * merges, so this is the first line of output
                         * for this commit.  Check to see what the previous
                         * line of output was.
@@ -944,21 +1028,21 @@ static void graph_output_commit_line(struct git_graph *graph, struct strbuf *sb)
                         * makes the output look nicer.
                         */
                        if (graph->prev_state == GRAPH_POST_MERGE &&
+                           graph->prev_edges_added > 0 &&
                            graph->prev_commit_index < i)
-                               strbuf_write_column(sb, col, '\\');
+                               graph_line_write_column(line, col, '\\');
                        else
-                               strbuf_write_column(sb, col, '|');
-                       chars_written++;
+                               graph_line_write_column(line, col, '|');
+               } else if (graph->prev_state == GRAPH_COLLAPSING &&
+                          graph->old_mapping[2 * i + 1] == i &&
+                          graph->mapping[2 * i] < i) {
+                       graph_line_write_column(line, col, '/');
                } else {
-                       strbuf_write_column(sb, col, '|');
-                       chars_written++;
+                       graph_line_write_column(line, col, '|');
                }
-               strbuf_addch(sb, ' ');
-               chars_written++;
+               graph_line_addch(line, ' ');
        }
 
-       graph_pad_horizontally(graph, sb, chars_written);
-
        /*
         * Update graph->state
         */
@@ -970,26 +1054,19 @@ static void graph_output_commit_line(struct git_graph *graph, struct strbuf *sb)
                graph_update_state(graph, GRAPH_COLLAPSING);
 }
 
-static struct column *find_new_column_by_commit(struct git_graph *graph,
-                                               struct commit *commit)
-{
-       int i;
-       for (i = 0; i < graph->num_new_columns; i++) {
-               if (graph->new_columns[i].commit == commit)
-                       return &graph->new_columns[i];
-       }
-       return NULL;
-}
+const char merge_chars[] = {'/', '|', '\\'};
 
-static void graph_output_post_merge_line(struct git_graph *graph, struct strbuf *sb)
+static void graph_output_post_merge_line(struct git_graph *graph, struct graph_line *line)
 {
        int seen_this = 0;
-       int i, j, chars_written;
+       int i, j;
+
+       struct commit_list *first_parent = first_interesting_parent(graph);
+       int seen_parent = 0;
 
        /*
         * Output the post-merge row
         */
-       chars_written = 0;
        for (i = 0; i <= graph->num_columns; i++) {
                struct column *col = &graph->columns[i];
                struct commit *col_commit;
@@ -1008,37 +1085,44 @@ static void graph_output_post_merge_line(struct git_graph *graph, struct strbuf
                         * new_columns and use those to format the
                         * edges.
                         */
-                       struct commit_list *parents = NULL;
-                       struct column *par_column;
+                       struct commit_list *parents = first_parent;
+                       int par_column;
+                       int idx = graph->merge_layout;
+                       char c;
                        seen_this = 1;
-                       parents = first_interesting_parent(graph);
-                       assert(parents);
-                       par_column = find_new_column_by_commit(graph, parents->item);
-                       assert(par_column);
-
-                       strbuf_write_column(sb, par_column, '|');
-                       chars_written++;
-                       for (j = 0; j < graph->num_parents - 1; j++) {
+
+                       for (j = 0; j < graph->num_parents; j++) {
+                               par_column = graph_find_new_column_by_commit(graph, parents->item);
+                               assert(par_column >= 0);
+
+                               c = merge_chars[idx];
+                               graph_line_write_column(line, &graph->new_columns[par_column], c);
+                               if (idx == 2) {
+                                       if (graph->edges_added > 0 || j < graph->num_parents - 1)
+                                               graph_line_addch(line, ' ');
+                               } else {
+                                       idx++;
+                               }
                                parents = next_interesting_parent(graph, parents);
-                               assert(parents);
-                               par_column = find_new_column_by_commit(graph, parents->item);
-                               assert(par_column);
-                               strbuf_write_column(sb, par_column, '\\');
-                               strbuf_addch(sb, ' ');
                        }
-                       chars_written += j * 2;
+                       if (graph->edges_added == 0)
+                               graph_line_addch(line, ' ');
+
                } else if (seen_this) {
-                       strbuf_write_column(sb, col, '\\');
-                       strbuf_addch(sb, ' ');
-                       chars_written += 2;
+                       if (graph->edges_added > 0)
+                               graph_line_write_column(line, col, '\\');
+                       else
+                               graph_line_write_column(line, col, '|');
+                       graph_line_addch(line, ' ');
                } else {
-                       strbuf_write_column(sb, col, '|');
-                       strbuf_addch(sb, ' ');
-                       chars_written += 2;
+                       graph_line_write_column(line, col, '|');
+                       if (graph->merge_layout != 0 || i != graph->commit_index - 1)
+                               graph_line_addch(line, seen_parent ? '_' : ' ');
                }
-       }
 
-       graph_pad_horizontally(graph, sb, chars_written);
+               if (col_commit == first_parent->item)
+                       seen_parent = 1;
+       }
 
        /*
         * Update graph->state
@@ -1049,7 +1133,7 @@ static void graph_output_post_merge_line(struct git_graph *graph, struct strbuf
                graph_update_state(graph, GRAPH_COLLAPSING);
 }
 
-static void graph_output_collapsing_line(struct git_graph *graph, struct strbuf *sb)
+static void graph_output_collapsing_line(struct git_graph *graph, struct graph_line *line)
 {
        int i;
        short used_horizontal = 0;
@@ -1057,13 +1141,18 @@ static void graph_output_collapsing_line(struct git_graph *graph, struct strbuf
        int horizontal_edge_target = -1;
 
        /*
-        * Clear out the new_mapping array
+        * Swap the mapping and old_mapping arrays
+        */
+       SWAP(graph->mapping, graph->old_mapping);
+
+       /*
+        * Clear out the mapping array
         */
        for (i = 0; i < graph->mapping_size; i++)
-               graph->new_mapping[i] = -1;
+               graph->mapping[i] = -1;
 
        for (i = 0; i < graph->mapping_size; i++) {
-               int target = graph->mapping[i];
+               int target = graph->old_mapping[i];
                if (target < 0)
                        continue;
 
@@ -1084,14 +1173,14 @@ static void graph_output_collapsing_line(struct git_graph *graph, struct strbuf
                         * This column is already in the
                         * correct place
                         */
-                       assert(graph->new_mapping[i] == -1);
-                       graph->new_mapping[i] = target;
-               } else if (graph->new_mapping[i - 1] < 0) {
+                       assert(graph->mapping[i] == -1);
+                       graph->mapping[i] = target;
+               } else if (graph->mapping[i - 1] < 0) {
                        /*
                         * Nothing is to the left.
                         * Move to the left by one
                         */
-                       graph->new_mapping[i - 1] = target;
+                       graph->mapping[i - 1] = target;
                        /*
                         * If there isn't already an edge moving horizontally
                         * select this one.
@@ -1107,9 +1196,9 @@ static void graph_output_collapsing_line(struct git_graph *graph, struct strbuf
                                 * line.
                                 */
                                for (j = (target * 2)+3; j < (i - 2); j += 2)
-                                       graph->new_mapping[j] = target;
+                                       graph->mapping[j] = target;
                        }
-               } else if (graph->new_mapping[i - 1] == target) {
+               } else if (graph->mapping[i - 1] == target) {
                        /*
                         * There is a branch line to our left
                         * already, and it is our target.  We
@@ -1117,7 +1206,7 @@ static void graph_output_collapsing_line(struct git_graph *graph, struct strbuf
                         * the same parent commit.
                         *
                         * We don't have to add anything to the
-                        * output or new_mapping, since the
+                        * output or mapping, since the
                         * existing branch line has already taken
                         * care of it.
                         */
@@ -1133,10 +1222,10 @@ static void graph_output_collapsing_line(struct git_graph *graph, struct strbuf
                         * The branch to the left of that space
                         * should be our eventual target.
                         */
-                       assert(graph->new_mapping[i - 1] > target);
-                       assert(graph->new_mapping[i - 2] < 0);
-                       assert(graph->new_mapping[i - 3] == target);
-                       graph->new_mapping[i - 2] = target;
+                       assert(graph->mapping[i - 1] > target);
+                       assert(graph->mapping[i - 2] < 0);
+                       assert(graph->mapping[i - 3] == target);
+                       graph->mapping[i - 2] = target;
                        /*
                         * Mark this branch as the horizontal edge to
                         * prevent any other edges from moving
@@ -1147,21 +1236,26 @@ static void graph_output_collapsing_line(struct git_graph *graph, struct strbuf
                }
        }
 
+       /*
+        * Copy the current mapping array into old_mapping
+        */
+       COPY_ARRAY(graph->old_mapping, graph->mapping, graph->mapping_size);
+
        /*
         * The new mapping may be 1 smaller than the old mapping
         */
-       if (graph->new_mapping[graph->mapping_size - 1] < 0)
+       if (graph->mapping[graph->mapping_size - 1] < 0)
                graph->mapping_size--;
 
        /*
         * Output out a line based on the new mapping info
         */
        for (i = 0; i < graph->mapping_size; i++) {
-               int target = graph->new_mapping[i];
+               int target = graph->mapping[i];
                if (target < 0)
-                       strbuf_addch(sb, ' ');
+                       graph_line_addch(line, ' ');
                else if (target * 2 == i)
-                       strbuf_write_column(sb, &graph->new_columns[target], '|');
+                       graph_line_write_column(line, &graph->new_columns[target], '|');
                else if (target == horizontal_edge_target &&
                         i != horizontal_edge - 1) {
                                /*
@@ -1170,24 +1264,17 @@ static void graph_output_collapsing_line(struct git_graph *graph, struct strbuf
                                 * won't continue into the next line.
                                 */
                                if (i != (target * 2)+3)
-                                       graph->new_mapping[i] = -1;
+                                       graph->mapping[i] = -1;
                                used_horizontal = 1;
-                       strbuf_write_column(sb, &graph->new_columns[target], '_');
+                       graph_line_write_column(line, &graph->new_columns[target], '_');
                } else {
                        if (used_horizontal && i < horizontal_edge)
-                               graph->new_mapping[i] = -1;
-                       strbuf_write_column(sb, &graph->new_columns[target], '/');
+                               graph->mapping[i] = -1;
+                       graph_line_write_column(line, &graph->new_columns[target], '/');
 
                }
        }
 
-       graph_pad_horizontally(graph, sb, graph->mapping_size);
-
-       /*
-        * Swap mapping and new_mapping
-        */
-       SWAP(graph->mapping, graph->new_mapping);
-
        /*
         * If graph->mapping indicates that all of the branch lines
         * are already in the correct positions, we are done.
@@ -1199,35 +1286,49 @@ static void graph_output_collapsing_line(struct git_graph *graph, struct strbuf
 
 int graph_next_line(struct git_graph *graph, struct strbuf *sb)
 {
+       int shown_commit_line = 0;
+       struct graph_line line = { .buf = sb, .width = 0 };
+
+       /*
+        * We could conceivable be called with a NULL commit
+        * if our caller has a bug, and invokes graph_next_line()
+        * immediately after graph_init(), without first calling
+        * graph_update().  Return without outputting anything in this
+        * case.
+        */
+       if (!graph->commit)
+               return -1;
+
        switch (graph->state) {
        case GRAPH_PADDING:
-               graph_output_padding_line(graph, sb);
-               return 0;
+               graph_output_padding_line(graph, &line);
+               break;
        case GRAPH_SKIP:
-               graph_output_skip_line(graph, sb);
-               return 0;
+               graph_output_skip_line(graph, &line);
+               break;
        case GRAPH_PRE_COMMIT:
-               graph_output_pre_commit_line(graph, sb);
-               return 0;
+               graph_output_pre_commit_line(graph, &line);
+               break;
        case GRAPH_COMMIT:
-               graph_output_commit_line(graph, sb);
-               return 1;
+               graph_output_commit_line(graph, &line);
+               shown_commit_line = 1;
+               break;
        case GRAPH_POST_MERGE:
-               graph_output_post_merge_line(graph, sb);
-               return 0;
+               graph_output_post_merge_line(graph, &line);
+               break;
        case GRAPH_COLLAPSING:
-               graph_output_collapsing_line(graph, sb);
-               return 0;
+               graph_output_collapsing_line(graph, &line);
+               break;
        }
 
-       assert(0);
-       return 0;
+       graph_pad_horizontally(graph, &line);
+       return shown_commit_line;
 }
 
 static void graph_padding_line(struct git_graph *graph, struct strbuf *sb)
 {
        int i;
-       int chars_written = 0;
+       struct graph_line line = { .buf = sb, .width = 0 };
 
        if (graph->state != GRAPH_COMMIT) {
                graph_next_line(graph, sb);
@@ -1244,20 +1345,17 @@ static void graph_padding_line(struct git_graph *graph, struct strbuf *sb)
        for (i = 0; i < graph->num_columns; i++) {
                struct column *col = &graph->columns[i];
 
-               strbuf_write_column(sb, col, '|');
-               chars_written++;
+               graph_line_write_column(&line, col, '|');
 
                if (col->commit == graph->commit && graph->num_parents > 2) {
                        int len = (graph->num_parents - 2) * 2;
-                       strbuf_addchars(sb, ' ', len);
-                       chars_written += len;
+                       graph_line_addchars(&line, ' ', len);
                } else {
-                       strbuf_addch(sb, ' ');
-                       chars_written++;
+                       graph_line_addch(&line, ' ');
                }
        }
 
-       graph_pad_horizontally(graph, sb, chars_written);
+       graph_pad_horizontally(graph, &line);
 
        /*
         * Update graph->prev_state since we have output a padding line
index 39c13110bc32f05f0cbb4ec1cf22bdc6ea6995b2..09813e1a46676c1475c1ad903f1d9358bf56827e 100644 (file)
--- a/hashmap.c
+++ b/hashmap.c
@@ -51,7 +51,7 @@ unsigned int memihash(const void *buf, size_t len)
 }
 
 /*
- * Incoporate another chunk of data into a memihash
+ * Incorporate another chunk of data into a memihash
  * computation.
  */
 unsigned int memihash_cont(unsigned int hash_seed, const void *buf, size_t len)
index bd2701549f55650748c7cfd5afd2dad6c2321f54..79ae9f80de1c19d5798006c34bc88ee786a0357a 100644 (file)
--- a/hashmap.h
+++ b/hashmap.h
@@ -59,7 +59,7 @@
  *
  *         if (!strcmp("print_all_by_key", action)) {
  *             struct long2string k, *e;
- *             hashmap_entry_init(&k->ent, memhash(&key, sizeof(long)));
+ *             hashmap_entry_init(&k.ent, memhash(&key, sizeof(long)));
  *             k.key = key;
  *
  *             flags &= ~COMPARE_VALUE;
  *
  *         if (!strcmp("has_exact_match_no_heap_alloc", action)) {
  *             struct long2string k;
- *             hashmap_entry_init(&k->ent, memhash(&key, sizeof(long)));
+ *             hashmap_entry_init(&k.ent, memhash(&key, sizeof(long)));
  *             k.key = key;
  *
  *             flags |= COMPARE_VALUE;
  *             printf("%sfound\n",
- *                    hashmap_get(&map, &k->ent, value) ? "" : "not ");
+ *                    hashmap_get(&map, &k.ent, value) ? "" : "not ");
  *         }
  *
  *         if (!strcmp("end", action)) {
@@ -502,7 +502,7 @@ static inline void hashmap_disable_item_counting(struct hashmap *map)
 }
 
 /*
- * Re-enable item couting when adding/removing items.
+ * Re-enable item counting when adding/removing items.
  * If counting is currently disabled, it will force count them.
  * It WILL NOT automatically rehash them.
  */
diff --git a/help.c b/help.c
index 9ff2be6b188fe890e0a9ba28b63effbdf99036ef..cf67624a94bc47ad7d373d64a1d3b20f562c8fc1 100644 (file)
--- a/help.c
+++ b/help.c
@@ -34,7 +34,7 @@ static struct category_description main_categories[] = {
        { CAT_foreignscminterface, N_("Interacting with Others") },
        { CAT_plumbingmanipulators, N_("Low-level Commands / Manipulators") },
        { CAT_plumbinginterrogators, N_("Low-level Commands / Interrogators") },
-       { CAT_synchingrepositories, N_("Low-level Commands / Synching Repositories") },
+       { CAT_synchingrepositories, N_("Low-level Commands / Syncing Repositories") },
        { CAT_purehelpers, N_("Low-level Commands / Internal Helpers") },
        { 0, NULL }
 };
diff --git a/hex.c b/hex.c
index 7850a8879d5e0a00c0e89e197427cafa23d8e865..fd7f00c43f969a30a76fe8ac8bf2255ac0320676 100644 (file)
--- a/hex.c
+++ b/hex.c
@@ -90,11 +90,6 @@ char *hash_to_hex_algop_r(char *buffer, const unsigned char *hash,
        return buffer;
 }
 
-char *sha1_to_hex_r(char *buffer, const unsigned char *sha1)
-{
-       return hash_to_hex_algop_r(buffer, sha1, &hash_algos[GIT_HASH_SHA1]);
-}
-
 char *oid_to_hex_r(char *buffer, const struct object_id *oid)
 {
        return hash_to_hex_algop_r(buffer, oid->hash, the_hash_algo);
@@ -108,11 +103,6 @@ char *hash_to_hex_algop(const unsigned char *hash, const struct git_hash_algo *a
        return hash_to_hex_algop_r(hexbuffer[bufno], hash, algop);
 }
 
-char *sha1_to_hex(const unsigned char *sha1)
-{
-       return hash_to_hex_algop(sha1, &hash_algos[GIT_HASH_SHA1]);
-}
-
 char *hash_to_hex(const unsigned char *hash)
 {
        return hash_to_hex_algop(hash, the_hash_algo);
diff --git a/http.c b/http.c
index 027a86d75dc4d19e1e7b2361c84fd505c575a0e4..5f348169c3cdcafbfdc561a468996f7118ce3a56 100644 (file)
--- a/http.c
+++ b/http.c
@@ -150,7 +150,7 @@ static unsigned long empty_auth_useless =
 
 static struct curl_slist *pragma_header;
 static struct curl_slist *no_pragma_header;
-static struct curl_slist *extra_http_headers;
+static struct string_list extra_http_headers = STRING_LIST_INIT_DUP;
 
 static struct active_request_slot *active_queue_head;
 
@@ -414,11 +414,9 @@ static int http_options(const char *var, const char *value, void *cb)
                if (!value) {
                        return config_error_nonbool(var);
                } else if (!*value) {
-                       curl_slist_free_all(extra_http_headers);
-                       extra_http_headers = NULL;
+                       string_list_clear(&extra_http_headers, 0);
                } else {
-                       extra_http_headers =
-                               curl_slist_append(extra_http_headers, value);
+                       string_list_append(&extra_http_headers, value);
                }
                return 0;
        }
@@ -1202,8 +1200,7 @@ void http_cleanup(void)
 #endif
        curl_global_cleanup();
 
-       curl_slist_free_all(extra_http_headers);
-       extra_http_headers = NULL;
+       string_list_clear(&extra_http_headers, 0);
 
        curl_slist_free_all(pragma_header);
        pragma_header = NULL;
@@ -1627,10 +1624,11 @@ int run_one_slot(struct active_request_slot *slot,
 
 struct curl_slist *http_copy_default_headers(void)
 {
-       struct curl_slist *headers = NULL, *h;
+       struct curl_slist *headers = NULL;
+       const struct string_list_item *item;
 
-       for (h = extra_http_headers; h; h = h->next)
-               headers = curl_slist_append(headers, h->data);
+       for_each_string_list_item(item, &extra_http_headers)
+               headers = curl_slist_append(headers, item->string);
 
        return headers;
 }
diff --git a/kwset.h b/kwset.h
index df99a92178231a53b4b03d7ee1f4dc1d1988a0c8..f50ecae573b9ff77e0169fa93e733e68ef9fa300 100644 (file)
--- a/kwset.h
+++ b/kwset.h
@@ -1,3 +1,6 @@
+#ifndef KWSET_H
+#define KWSET_H
+
 /* This file has been copied from commit e7ac713d^ in the GNU grep git
  * repository. A few small changes have been made to adapt the code to
  * Git.
@@ -59,3 +62,4 @@ size_t kwsexec(kwset_t, char const *, size_t, struct kwsmatch *);
 /* Deallocate the given keyword set and all its associated storage. */
 void kwsfree(kwset_t);
 
+#endif /* KWSET_H */
index 42be7c9960ad5c02f3968a27977203a638d79bbd..11869ad81ce82334834e48acfb2fc353db08bec9 100644 (file)
@@ -1951,6 +1951,16 @@ static char *apply_dir_rename(struct dir_rename_entry *entry,
                return NULL;
 
        oldlen = strlen(entry->dir);
+       if (entry->new_dir.len == 0)
+               /*
+                * If someone renamed/merged a subdirectory into the root
+                * directory (e.g. 'some/subdir' -> ''), then we want to
+                * avoid returning
+                *     '' + '/filename'
+                * as the rename; we need to make old_path + oldlen advance
+                * past the '/' character.
+                */
+               oldlen++;
        newlen = entry->new_dir.len + (strlen(old_path) - oldlen) + 1;
        strbuf_grow(&new_path, newlen);
        strbuf_addbuf(&new_path, &entry->new_dir);
@@ -1963,8 +1973,8 @@ static void get_renamed_dir_portion(const char *old_path, const char *new_path,
                                    char **old_dir, char **new_dir)
 {
        char *end_of_old, *end_of_new;
-       int old_len, new_len;
 
+       /* Default return values: NULL, meaning no rename */
        *old_dir = NULL;
        *new_dir = NULL;
 
@@ -1975,43 +1985,91 @@ static void get_renamed_dir_portion(const char *old_path, const char *new_path,
         *    "a/b/c/d" was renamed to "a/b/some/thing/else"
         * so, for this example, this function returns "a/b/c/d" in
         * *old_dir and "a/b/some/thing/else" in *new_dir.
-        *
-        * Also, if the basename of the file changed, we don't care.  We
-        * want to know which portion of the directory, if any, changed.
+        */
+
+       /*
+        * If the basename of the file changed, we don't care.  We want
+        * to know which portion of the directory, if any, changed.
         */
        end_of_old = strrchr(old_path, '/');
        end_of_new = strrchr(new_path, '/');
 
-       if (end_of_old == NULL || end_of_new == NULL)
+       /*
+        * If end_of_old is NULL, old_path wasn't in a directory, so there
+        * could not be a directory rename (our rule elsewhere that a
+        * directory which still exists is not considered to have been
+        * renamed means the root directory can never be renamed -- because
+        * the root directory always exists).
+        */
+       if (end_of_old == NULL)
+               return; /* Note: *old_dir and *new_dir are still NULL */
+
+       /*
+        * If new_path contains no directory (end_of_new is NULL), then we
+        * have a rename of old_path's directory to the root directory.
+        */
+       if (end_of_new == NULL) {
+               *old_dir = xstrndup(old_path, end_of_old - old_path);
+               *new_dir = xstrdup("");
                return;
+       }
+
+       /* Find the first non-matching character traversing backwards */
        while (*--end_of_new == *--end_of_old &&
               end_of_old != old_path &&
               end_of_new != new_path)
                ; /* Do nothing; all in the while loop */
+
        /*
-        * We've found the first non-matching character in the directory
-        * paths.  That means the current directory we were comparing
-        * represents the rename.  Move end_of_old and end_of_new back
-        * to the full directory name.
+        * If both got back to the beginning of their strings, then the
+        * directory didn't change at all, only the basename did.
         */
-       if (*end_of_old == '/')
-               end_of_old++;
-       if (*end_of_old != '/')
-               end_of_new++;
-       end_of_old = strchr(end_of_old, '/');
-       end_of_new = strchr(end_of_new, '/');
+       if (end_of_old == old_path && end_of_new == new_path &&
+           *end_of_old == *end_of_new)
+               return; /* Note: *old_dir and *new_dir are still NULL */
 
        /*
-        * It may have been the case that old_path and new_path were the same
-        * directory all along.  Don't claim a rename if they're the same.
+        * If end_of_new got back to the beginning of its string, and
+        * end_of_old got back to the beginning of some subdirectory, then
+        * we have a rename/merge of a subdirectory into the root, which
+        * needs slightly special handling.
+        *
+        * Note: There is no need to consider the opposite case, with a
+        * rename/merge of the root directory into some subdirectory
+        * because as noted above the root directory always exists so it
+        * cannot be considered to be renamed.
         */
-       old_len = end_of_old - old_path;
-       new_len = end_of_new - new_path;
-
-       if (old_len != new_len || strncmp(old_path, new_path, old_len)) {
-               *old_dir = xstrndup(old_path, old_len);
-               *new_dir = xstrndup(new_path, new_len);
+       if (end_of_new == new_path &&
+           end_of_old != old_path && end_of_old[-1] == '/') {
+               *old_dir = xstrndup(old_path, --end_of_old - old_path);
+               *new_dir = xstrdup("");
+               return;
        }
+
+       /*
+        * We've found the first non-matching character in the directory
+        * paths.  That means the current characters we were looking at
+        * were part of the first non-matching subdir name going back from
+        * the end of the strings.  Get the whole name by advancing both
+        * end_of_old and end_of_new to the NEXT '/' character.  That will
+        * represent the entire directory rename.
+        *
+        * The reason for the increment is cases like
+        *    a/b/star/foo/whatever.c -> a/b/tar/foo/random.c
+        * After dropping the basename and going back to the first
+        * non-matching character, we're now comparing:
+        *    a/b/s          and         a/b/
+        * and we want to be comparing:
+        *    a/b/star/      and         a/b/tar/
+        * but without the pre-increment, the one on the right would stay
+        * a/b/.
+        */
+       end_of_old = strchr(++end_of_old, '/');
+       end_of_new = strchr(++end_of_new, '/');
+
+       /* Copy the old and new directories into *old_dir and *new_dir. */
+       *old_dir = xstrndup(old_path, end_of_old - old_path);
+       *new_dir = xstrndup(new_path, end_of_new - new_path);
 }
 
 static void remove_hashmap_entries(struct hashmap *dir_renames,
diff --git a/midx.c b/midx.c
index f29afc0d2daba3ae9364561be0ea7fe18acc0cc7..37ec28623af67f01ec6fbc2f42c6e727eddbdaf4 100644 (file)
--- a/midx.c
+++ b/midx.c
@@ -448,6 +448,8 @@ struct pack_list {
        uint32_t nr;
        uint32_t alloc;
        struct multi_pack_index *m;
+       struct progress *progress;
+       unsigned pack_paths_checked;
 };
 
 static void add_pack_to_midx(const char *full_path, size_t full_path_len,
@@ -456,6 +458,7 @@ static void add_pack_to_midx(const char *full_path, size_t full_path_len,
        struct pack_list *packs = (struct pack_list *)data;
 
        if (ends_with(file_name, ".idx")) {
+               display_progress(packs->progress, ++packs->pack_paths_checked);
                if (packs->m && midx_contains_pack(packs->m, file_name))
                        return;
 
@@ -785,7 +788,7 @@ static size_t write_midx_large_offsets(struct hashfile *f, uint32_t nr_large_off
 }
 
 static int write_midx_internal(const char *object_dir, struct multi_pack_index *m,
-                              struct string_list *packs_to_drop)
+                              struct string_list *packs_to_drop, unsigned flags)
 {
        unsigned char cur_chunk, num_chunks = 0;
        char *midx_name;
@@ -799,6 +802,7 @@ static int write_midx_internal(const char *object_dir, struct multi_pack_index *
        uint64_t chunk_offsets[MIDX_MAX_CHUNKS + 1];
        uint32_t nr_entries, num_large_offsets = 0;
        struct pack_midx_entry *entries = NULL;
+       struct progress *progress = NULL;
        int large_offsets_needed = 0;
        int pack_name_concat_len = 0;
        int dropped_packs = 0;
@@ -833,7 +837,14 @@ static int write_midx_internal(const char *object_dir, struct multi_pack_index *
                }
        }
 
+       packs.pack_paths_checked = 0;
+       if (flags & MIDX_PROGRESS)
+               packs.progress = start_progress(_("Adding packfiles to multi-pack-index"), 0);
+       else
+               packs.progress = NULL;
+
        for_each_file_in_pack_dir(object_dir, add_pack_to_midx, &packs);
+       stop_progress(&packs.progress);
 
        if (packs.m && packs.nr == packs.m->num_packs && !packs_to_drop)
                goto cleanup;
@@ -958,6 +969,9 @@ static int write_midx_internal(const char *object_dir, struct multi_pack_index *
                written += MIDX_CHUNKLOOKUP_WIDTH;
        }
 
+       if (flags & MIDX_PROGRESS)
+               progress = start_progress(_("Writing chunks to multi-pack-index"),
+                                         num_chunks);
        for (i = 0; i < num_chunks; i++) {
                if (written != chunk_offsets[i])
                        BUG("incorrect chunk offset (%"PRIu64" != %"PRIu64") for chunk id %"PRIx32,
@@ -990,7 +1004,10 @@ static int write_midx_internal(const char *object_dir, struct multi_pack_index *
                                BUG("trying to write unknown chunk id %"PRIx32,
                                    chunk_ids[i]);
                }
+
+               display_progress(progress, i + 1);
        }
+       stop_progress(&progress);
 
        if (written != chunk_offsets[num_chunks])
                BUG("incorrect final offset %"PRIu64" != %"PRIu64,
@@ -1016,9 +1033,9 @@ cleanup:
        return result;
 }
 
-int write_midx_file(const char *object_dir)
+int write_midx_file(const char *object_dir, unsigned flags)
 {
-       return write_midx_internal(object_dir, NULL, NULL);
+       return write_midx_internal(object_dir, NULL, NULL, flags);
 }
 
 void clear_midx_file(struct repository *r)
@@ -1076,19 +1093,20 @@ static int compare_pair_pos_vs_id(const void *_a, const void *_b)
                        display_progress(progress, _n); \
        } while (0)
 
-int verify_midx_file(struct repository *r, const char *object_dir)
+int verify_midx_file(struct repository *r, const char *object_dir, unsigned flags)
 {
        struct pair_pos_vs_id *pairs = NULL;
        uint32_t i;
-       struct progress *progress;
+       struct progress *progress = NULL;
        struct multi_pack_index *m = load_multi_pack_index(object_dir, 1);
        verify_midx_error = 0;
 
        if (!m)
                return 0;
 
-       progress = start_progress(_("Looking for referenced packfiles"),
-                                 m->num_packs);
+       if (flags & MIDX_PROGRESS)
+               progress = start_progress(_("Looking for referenced packfiles"),
+                                         m->num_packs);
        for (i = 0; i < m->num_packs; i++) {
                if (prepare_midx_pack(r, m, i))
                        midx_report("failed to load pack in position %d", i);
@@ -1106,8 +1124,9 @@ int verify_midx_file(struct repository *r, const char *object_dir)
                                    i, oid_fanout1, oid_fanout2, i + 1);
        }
 
-       progress = start_sparse_progress(_("Verifying OID order in MIDX"),
-                                        m->num_objects - 1);
+       if (flags & MIDX_PROGRESS)
+               progress = start_sparse_progress(_("Verifying OID order in multi-pack-index"),
+                                                m->num_objects - 1);
        for (i = 0; i < m->num_objects - 1; i++) {
                struct object_id oid1, oid2;
 
@@ -1134,13 +1153,15 @@ int verify_midx_file(struct repository *r, const char *object_dir)
                pairs[i].pack_int_id = nth_midxed_pack_int_id(m, i);
        }
 
-       progress = start_sparse_progress(_("Sorting objects by packfile"),
-                                        m->num_objects);
+       if (flags & MIDX_PROGRESS)
+               progress = start_sparse_progress(_("Sorting objects by packfile"),
+                                                m->num_objects);
        display_progress(progress, 0); /* TODO: Measure QSORT() progress */
        QSORT(pairs, m->num_objects, compare_pair_pos_vs_id);
        stop_progress(&progress);
 
-       progress = start_sparse_progress(_("Verifying object offsets"), m->num_objects);
+       if (flags & MIDX_PROGRESS)
+               progress = start_sparse_progress(_("Verifying object offsets"), m->num_objects);
        for (i = 0; i < m->num_objects; i++) {
                struct object_id oid;
                struct pack_entry e;
@@ -1183,23 +1204,34 @@ int verify_midx_file(struct repository *r, const char *object_dir)
        return verify_midx_error;
 }
 
-int expire_midx_packs(struct repository *r, const char *object_dir)
+int expire_midx_packs(struct repository *r, const char *object_dir, unsigned flags)
 {
        uint32_t i, *count, result = 0;
        struct string_list packs_to_drop = STRING_LIST_INIT_DUP;
        struct multi_pack_index *m = load_multi_pack_index(object_dir, 1);
+       struct progress *progress = NULL;
 
        if (!m)
                return 0;
 
        count = xcalloc(m->num_packs, sizeof(uint32_t));
+
+       if (flags & MIDX_PROGRESS)
+               progress = start_progress(_("Counting referenced objects"),
+                                         m->num_objects);
        for (i = 0; i < m->num_objects; i++) {
                int pack_int_id = nth_midxed_pack_int_id(m, i);
                count[pack_int_id]++;
+               display_progress(progress, i + 1);
        }
+       stop_progress(&progress);
 
+       if (flags & MIDX_PROGRESS)
+               progress = start_progress(_("Finding and deleting unreferenced packfiles"),
+                                         m->num_packs);
        for (i = 0; i < m->num_packs; i++) {
                char *pack_name;
+               display_progress(progress, i + 1);
 
                if (count[i])
                        continue;
@@ -1217,11 +1249,12 @@ int expire_midx_packs(struct repository *r, const char *object_dir)
                unlink_pack_path(pack_name, 0);
                free(pack_name);
        }
+       stop_progress(&progress);
 
        free(count);
 
        if (packs_to_drop.nr)
-               result = write_midx_internal(object_dir, m, &packs_to_drop);
+               result = write_midx_internal(object_dir, m, &packs_to_drop, flags);
 
        string_list_clear(&packs_to_drop, 0);
        return result;
@@ -1315,7 +1348,7 @@ static int fill_included_packs_batch(struct repository *r,
        return 0;
 }
 
-int midx_repack(struct repository *r, const char *object_dir, size_t batch_size)
+int midx_repack(struct repository *r, const char *object_dir, size_t batch_size, unsigned flags)
 {
        int result = 0;
        uint32_t i;
@@ -1340,6 +1373,12 @@ int midx_repack(struct repository *r, const char *object_dir, size_t batch_size)
        strbuf_addstr(&base_name, object_dir);
        strbuf_addstr(&base_name, "/pack/pack");
        argv_array_push(&cmd.args, base_name.buf);
+
+       if (flags & MIDX_PROGRESS)
+               argv_array_push(&cmd.args, "--progress");
+       else
+               argv_array_push(&cmd.args, "-q");
+
        strbuf_release(&base_name);
 
        cmd.git_cmd = 1;
@@ -1370,7 +1409,7 @@ int midx_repack(struct repository *r, const char *object_dir, size_t batch_size)
                goto cleanup;
        }
 
-       result = write_midx_internal(object_dir, m, NULL);
+       result = write_midx_internal(object_dir, m, NULL, flags);
        m = NULL;
 
 cleanup:
diff --git a/midx.h b/midx.h
index f0ae656b5d767644d60ef7b101350ca29cde7585..e6fa356b5caaf67451751c121325d74e79007f31 100644 (file)
--- a/midx.h
+++ b/midx.h
@@ -37,6 +37,8 @@ struct multi_pack_index {
        char object_dir[FLEX_ARRAY];
 };
 
+#define MIDX_PROGRESS     (1 << 0)
+
 struct multi_pack_index *load_multi_pack_index(const char *object_dir, int local);
 int prepare_midx_pack(struct repository *r, struct multi_pack_index *m, uint32_t pack_int_id);
 int bsearch_midx(const struct object_id *oid, struct multi_pack_index *m, uint32_t *result);
@@ -47,11 +49,11 @@ int fill_midx_entry(struct repository *r, const struct object_id *oid, struct pa
 int midx_contains_pack(struct multi_pack_index *m, const char *idx_or_pack_name);
 int prepare_multi_pack_index_one(struct repository *r, const char *object_dir, int local);
 
-int write_midx_file(const char *object_dir);
+int write_midx_file(const char *object_dir, unsigned flags);
 void clear_midx_file(struct repository *r);
-int verify_midx_file(struct repository *r, const char *object_dir);
-int expire_midx_packs(struct repository *r, const char *object_dir);
-int midx_repack(struct repository *r, const char *object_dir, size_t batch_size);
+int verify_midx_file(struct repository *r, const char *object_dir, unsigned flags);
+int expire_midx_packs(struct repository *r, const char *object_dir, unsigned flags);
+int midx_repack(struct repository *r, const char *object_dir, size_t batch_size, unsigned flags);
 
 void close_midx(struct multi_pack_index *m);
 
index ceb1d7bd6f7c594160b8162f306afb37556dc0e5..fb526a3775f69eb02d6b7e47629bf60a8a0153fa 100644 (file)
@@ -138,7 +138,7 @@ static int lazy_nr_dir_threads;
 
 /*
  * Set a minimum number of cache_entries that we will handle per
- * thread and use that to decide how many threads to run (upto
+ * thread and use that to decide how many threads to run (up to
  * the number on the system).
  *
  * For guidance setting the lower per-thread bound, see:
@@ -218,7 +218,7 @@ static int lookup_lazy_params(struct index_state *istate)
  * However, the hashmap is going to put items into bucket
  * chains based on their hash values.  Use that to create n
  * mutexes and lock on mutex[bucket(hash) % n].  This will
- * decrease the collision rate by (hopefully) by a factor of n.
+ * decrease the collision rate by (hopefully) a factor of n.
  */
 static void init_dir_mutex(void)
 {
index c6250d77f4dec714de1073ccc0260309c24f6312..5e5a3c62d9db8cf11b4c64c196ae0fb830fe5004 100644 (file)
@@ -119,7 +119,10 @@ void oe_map_new_pack(struct packing_data *pack)
 {
        uint32_t i;
 
-       REALLOC_ARRAY(pack->in_pack, pack->nr_alloc);
+       if (pack->in_pack)
+               BUG("packing_data has already been converted to pack array");
+
+       ALLOC_ARRAY(pack->in_pack, pack->nr_alloc);
 
        for (i = 0; i < pack->nr_objects; i++)
                pack->in_pack[i] = oe_in_pack(pack, pack->objects + i);
index 6fe6ae5ee86ccaa6ffe619ab5c4280a76841f340..d3975e079b1060c5dc0b5b1dda2b9e921a632730 100644 (file)
@@ -251,12 +251,21 @@ static inline void oe_set_in_pack(struct packing_data *pack,
                                  struct object_entry *e,
                                  struct packed_git *p)
 {
-       if (!p->index)
+       if (pack->in_pack_by_idx) {
+               if (p->index) {
+                       e->in_pack_idx = p->index;
+                       return;
+               }
+               /*
+                * We're accessing packs by index, but this pack doesn't have
+                * an index (e.g., because it was added since we created the
+                * in_pack_by_idx array). Bail to oe_map_new_pack(), which
+                * will convert us to using the full in_pack array, and then
+                * fall through to our in_pack handling.
+                */
                oe_map_new_pack(pack);
-       if (pack->in_pack_by_idx)
-               e->in_pack_idx = p->index;
-       else
-               pack->in_pack[e - pack->objects] = p;
+       }
+       pack->in_pack[e - pack->objects] = p;
 }
 
 static inline struct object_entry *oe_delta(
index 1240a8514e040954cbc7f091e7053b5c76ac6627..c2062ae742a62ecf73dc44507650308be6479126 100644 (file)
@@ -161,6 +161,7 @@ int parse_opt_tertiary(const struct option *opt, const char *arg, int unset)
 
 struct option *parse_options_dup(const struct option *o)
 {
+       const struct option *orig = o;
        struct option *opts;
        int nr = 0;
 
@@ -170,7 +171,7 @@ struct option *parse_options_dup(const struct option *o)
        }
 
        ALLOC_ARRAY(opts, nr + 1);
-       memcpy(opts, o - nr, sizeof(*o) * nr);
+       COPY_ARRAY(opts, orig, nr);
        memset(opts + nr, 0, sizeof(*opts));
        opts[nr].type = OPTION_END;
        return opts;
index b42f54d48b96c05a207ffb02eac8c235c2b723b0..60fae3ad213782dffa82e3c7dd246b47c566bf56 100644 (file)
@@ -623,7 +623,7 @@ static int show_gitcomp(const struct option *opts)
  * Scan and may produce a new option[] array, which should be used
  * instead of the original 'options'.
  *
- * Right now this is only used to preprocess and substitue
+ * Right now this is only used to preprocess and substitute
  * OPTION_ALIAS.
  */
 static struct option *preprocess_options(struct parse_opt_ctx_t *ctx,
diff --git a/path.c b/path.c
index e3da1f3c4e2c7ed077c1ed3a98103b178045a45a..0607156d2c96b0b841f2b4ea1558a48e0fdb1d30 100644 (file)
--- a/path.c
+++ b/path.c
@@ -11,6 +11,7 @@
 #include "path.h"
 #include "packfile.h"
 #include "object-store.h"
+#include "lockfile.h"
 
 static int get_st_mode_bits(const char *path, int *mode)
 {
@@ -101,36 +102,36 @@ struct common_dir {
        /* Not considered garbage for report_linked_checkout_garbage */
        unsigned ignore_garbage:1;
        unsigned is_dir:1;
-       /* Not common even though its parent is */
-       unsigned exclude:1;
-       const char *dirname;
+       /* Belongs to the common dir, though it may contain paths that don't */
+       unsigned is_common:1;
+       const char *path;
 };
 
 static struct common_dir common_list[] = {
-       { 0, 1, 0, "branches" },
-       { 0, 1, 0, "common" },
-       { 0, 1, 0, "hooks" },
-       { 0, 1, 0, "info" },
-       { 0, 0, 1, "info/sparse-checkout" },
-       { 1, 1, 0, "logs" },
-       { 1, 1, 1, "logs/HEAD" },
-       { 0, 1, 1, "logs/refs/bisect" },
-       { 0, 1, 1, "logs/refs/rewritten" },
-       { 0, 1, 1, "logs/refs/worktree" },
-       { 0, 1, 0, "lost-found" },
-       { 0, 1, 0, "objects" },
-       { 0, 1, 0, "refs" },
-       { 0, 1, 1, "refs/bisect" },
-       { 0, 1, 1, "refs/rewritten" },
-       { 0, 1, 1, "refs/worktree" },
-       { 0, 1, 0, "remotes" },
-       { 0, 1, 0, "worktrees" },
-       { 0, 1, 0, "rr-cache" },
-       { 0, 1, 0, "svn" },
-       { 0, 0, 0, "config" },
-       { 1, 0, 0, "gc.pid" },
-       { 0, 0, 0, "packed-refs" },
-       { 0, 0, 0, "shallow" },
+       { 0, 1, 1, "branches" },
+       { 0, 1, 1, "common" },
+       { 0, 1, 1, "hooks" },
+       { 0, 1, 1, "info" },
+       { 0, 0, 0, "info/sparse-checkout" },
+       { 1, 1, 1, "logs" },
+       { 1, 0, 0, "logs/HEAD" },
+       { 0, 1, 0, "logs/refs/bisect" },
+       { 0, 1, 0, "logs/refs/rewritten" },
+       { 0, 1, 0, "logs/refs/worktree" },
+       { 0, 1, 1, "lost-found" },
+       { 0, 1, 1, "objects" },
+       { 0, 1, 1, "refs" },
+       { 0, 1, 0, "refs/bisect" },
+       { 0, 1, 0, "refs/rewritten" },
+       { 0, 1, 0, "refs/worktree" },
+       { 0, 1, 1, "remotes" },
+       { 0, 1, 1, "worktrees" },
+       { 0, 1, 1, "rr-cache" },
+       { 0, 1, 1, "svn" },
+       { 0, 0, 1, "config" },
+       { 1, 0, 1, "gc.pid" },
+       { 0, 0, 1, "packed-refs" },
+       { 0, 0, 1, "shallow" },
        { 0, 0, 0, NULL }
 };
 
@@ -236,30 +237,41 @@ static void *add_to_trie(struct trie *root, const char *key, void *value)
        return old;
 }
 
-typedef int (*match_fn)(const char *unmatched, void *data, void *baton);
+typedef int (*match_fn)(const char *unmatched, void *value, void *baton);
 
 /*
  * Search a trie for some key.  Find the longest /-or-\0-terminated
- * prefix of the key for which the trie contains a value.  Call fn
- * with the unmatched portion of the key and the found value, and
- * return its return value.  If there is no such prefix, return -1.
+ * prefix of the key for which the trie contains a value.  If there is
+ * no such prefix, return -1.  Otherwise call fn with the unmatched
+ * portion of the key and the found value.  If fn returns 0 or
+ * positive, then return its return value.  If fn returns negative,
+ * then call fn with the next-longest /-terminated prefix of the key
+ * (i.e. a parent directory) for which the trie contains a value, and
+ * handle its return value the same way.  If there is no shorter
+ * /-terminated prefix with a value left, then return the negative
+ * return value of the most recent fn invocation.
  *
  * The key is partially normalized: consecutive slashes are skipped.
  *
- * For example, consider the trie containing only [refs,
- * refs/worktree] (both with values).
- *
- * | key             | unmatched  | val from node | return value |
- * |-----------------|------------|---------------|--------------|
- * | a               | not called | n/a           | -1           |
- * | refs            | \0         | refs          | as per fn    |
- * | refs/           | /          | refs          | as per fn    |
- * | refs/w          | /w         | refs          | as per fn    |
- * | refs/worktree   | \0         | refs/worktree | as per fn    |
- * | refs/worktree/  | /          | refs/worktree | as per fn    |
- * | refs/worktree/a | /a         | refs/worktree | as per fn    |
- * |-----------------|------------|---------------|--------------|
+ * For example, consider the trie containing only [logs,
+ * logs/refs/bisect], both with values, but not logs/refs.
  *
+ * | key                | unmatched      | prefix to node   | return value |
+ * |--------------------|----------------|------------------|--------------|
+ * | a                  | not called     | n/a              | -1           |
+ * | logstore           | not called     | n/a              | -1           |
+ * | logs               | \0             | logs             | as per fn    |
+ * | logs/              | /              | logs             | as per fn    |
+ * | logs/refs          | /refs          | logs             | as per fn    |
+ * | logs/refs/         | /refs/         | logs             | as per fn    |
+ * | logs/refs/b        | /refs/b        | logs             | as per fn    |
+ * | logs/refs/bisected | /refs/bisected | logs             | as per fn    |
+ * | logs/refs/bisect   | \0             | logs/refs/bisect | as per fn    |
+ * | logs/refs/bisect/  | /              | logs/refs/bisect | as per fn    |
+ * | logs/refs/bisect/a | /a             | logs/refs/bisect | as per fn    |
+ * | (If fn in the previous line returns -1, then fn is called once more:) |
+ * | logs/refs/bisect/a | /refs/bisect/a | logs             | as per fn    |
+ * |--------------------|----------------|------------------|--------------|
  */
 static int trie_find(struct trie *root, const char *key, match_fn fn,
                     void *baton)
@@ -288,9 +300,13 @@ static int trie_find(struct trie *root, const char *key, match_fn fn,
 
        /* Matched the entire compressed section */
        key += i;
-       if (!*key)
+       if (!*key) {
                /* End of key */
-               return fn(key, root->value, baton);
+               if (root->value)
+                       return fn(key, root->value, baton);
+               else
+                       return -1;
+       }
 
        /* Partial path normalization: skip consecutive slashes */
        while (key[0] == '/' && key[1] == '/')
@@ -320,8 +336,8 @@ static void init_common_trie(void)
        if (common_trie_done_setup)
                return;
 
-       for (p = common_list; p->dirname; p++)
-               add_to_trie(&common_trie, p->dirname, p);
+       for (p = common_list; p->path; p++)
+               add_to_trie(&common_trie, p->path, p);
 
        common_trie_done_setup = 1;
 }
@@ -334,14 +350,11 @@ static int check_common(const char *unmatched, void *value, void *baton)
 {
        struct common_dir *dir = value;
 
-       if (!dir)
-               return 0;
-
        if (dir->is_dir && (unmatched[0] == 0 || unmatched[0] == '/'))
-               return !dir->exclude;
+               return dir->is_common;
 
        if (!dir->is_dir && unmatched[0] == 0)
-               return !dir->exclude;
+               return dir->is_common;
 
        return 0;
 }
@@ -350,9 +363,14 @@ static void update_common_dir(struct strbuf *buf, int git_dir_len,
                              const char *common_dir)
 {
        char *base = buf->buf + git_dir_len;
+       int has_lock_suffix = strbuf_strip_suffix(buf, LOCK_SUFFIX);
+
        init_common_trie();
        if (trie_find(&common_trie, base, check_common, NULL) > 0)
                replace_dir(buf, git_dir_len, common_dir);
+
+       if (has_lock_suffix)
+               strbuf_addstr(buf, LOCK_SUFFIX);
 }
 
 void report_linked_checkout_garbage(void)
@@ -365,8 +383,8 @@ void report_linked_checkout_garbage(void)
                return;
        strbuf_addf(&sb, "%s/", get_git_dir());
        len = sb.len;
-       for (p = common_list; p->dirname; p++) {
-               const char *path = p->dirname;
+       for (p = common_list; p->path; p++) {
+               const char *path = p->path;
                if (p->ignore_garbage)
                        continue;
                strbuf_setlen(&sb, len);
index 62c472e0ce41d507e88df86f6a022d1a08fbc4a6..54c9ed0ddee52e4d5b59b59b74e0dbc738ae3ac9 100644 (file)
@@ -563,7 +563,7 @@ sub get_record {
 Query user C<PROMPT> and return answer from user.
 
 Honours GIT_ASKPASS and SSH_ASKPASS environment variables for querying
-the user. If no *_ASKPASS variable is set or an error occoured,
+the user. If no *_ASKPASS variable is set or an error occurred,
 the terminal is tried as a fallback.
 If C<ISPASSWORD> is set and true, the terminal disables echo.
 
index b32f0369531c6f14799329890880600e714e6b4b..93eb6e837071a3e54437a92c0e2338e1ce1ef6f4 100644 (file)
--- a/pretty.c
+++ b/pretty.c
@@ -696,7 +696,7 @@ static size_t format_person_part(struct strbuf *sb, char part,
        mail = s.mail_begin;
        maillen = s.mail_end - s.mail_begin;
 
-       if (part == 'N' || part == 'E') /* mailmap lookup */
+       if (part == 'N' || part == 'E' || part == 'L') /* mailmap lookup */
                mailmap_name(&mail, &maillen, &name, &namelen);
        if (part == 'n' || part == 'N') {       /* name */
                strbuf_add(sb, name, namelen);
@@ -706,6 +706,13 @@ static size_t format_person_part(struct strbuf *sb, char part,
                strbuf_add(sb, mail, maillen);
                return placeholder_len;
        }
+       if (part == 'l' || part == 'L') {       /* local-part */
+               const char *at = memchr(mail, '@', maillen);
+               if (at)
+                       maillen = at - mail;
+               strbuf_add(sb, mail, maillen);
+               return placeholder_len;
+       }
 
        if (!s.date_begin)
                goto skip;
index 9bd5b79d59446dede45ea075fc8d7307fcb98648..9f338c945faf997a11d2a55065ddbc14d8be6cdc 100644 (file)
@@ -16,10 +16,8 @@ static int fetch_refs(const char *remote_name, struct ref *ref)
 {
        struct remote *remote;
        struct transport *transport;
-       int original_fetch_if_missing = fetch_if_missing;
        int res;
 
-       fetch_if_missing = 0;
        remote = remote_get(remote_name);
        if (!remote->url[0])
                die(_("Remote with no URL"));
@@ -28,7 +26,6 @@ static int fetch_refs(const char *remote_name, struct ref *ref)
        transport_set_option(transport, TRANS_OPT_FROM_PROMISOR, "1");
        transport_set_option(transport, TRANS_OPT_NO_DEPENDENTS, "1");
        res = transport_fetch_refs(transport, ref);
-       fetch_if_missing = original_fetch_if_missing;
 
        return res;
 }
index 08a50b6e98fc768f42e48b33c70ffbf17a7e8e4c..77347a9f883ce8d5a74af29b6c3fb3683bef489c 100644 (file)
@@ -6,7 +6,7 @@
 #define RANGE_DIFF_CREATION_FACTOR_DEFAULT 60
 
 /*
- * Compare series of commmits in RANGE1 and RANGE2, and emit to the
+ * Compare series of commits in RANGE1 and RANGE2, and emit to the
  * standard output.  NULL can be passed to DIFFOPT to use the built-in
  * default.
  */
index 133f790fa4cd52e503da5ae6be3f78389f7674b7..06744287a11acd4f623eaaaee9a4ef1d4173542b 100644 (file)
@@ -1790,7 +1790,7 @@ static struct cache_entry *create_from_disk(struct mem_pool *ce_mem_pool,
                const unsigned char *cp = (const unsigned char *)name;
                size_t strip_len, previous_len;
 
-               /* If we're at the begining of a block, ignore the previous name */
+               /* If we're at the beginning of a block, ignore the previous name */
                strip_len = decode_varint(&cp);
                if (previous_ce) {
                        previous_len = previous_ce->ce_namelen;
index f2d8c0123a7724bca18eb76de71748d3760391ee..ff2436c0fb706d8d34de926eca0f481b74b3170f 100644 (file)
@@ -262,7 +262,7 @@ int refs_rename_ref_available(struct ref_store *refs,
  * after calling ref_iterator_advance() again or calling
  * ref_iterator_abort(), you must make a copy. When the iteration has
  * been exhausted, ref_iterator_advance() releases any resources
- * assocated with the iteration, frees the ref_iterator object, and
+ * associated with the iteration, frees the ref_iterator object, and
  * returns ITER_DONE. If you want to abort the iteration early, call
  * ref_iterator_abort(), which also frees the ref_iterator object and
  * any associated resources. If there was an internal error advancing
index 682c239fe3eda6c16b7be45fc5c920a325d61817..a4174ddb0629cdd690142fb42c5d4182492485c4 100644 (file)
@@ -200,9 +200,9 @@ int repo_submodule_init(struct repository *subrepo,
 
        if (repo_init(subrepo, gitdir.buf, worktree.buf)) {
                /*
-                * If initilization fails then it may be due to the submodule
+                * If initialization fails then it may be due to the submodule
                 * not being populated in the superproject's worktree.  Instead
-                * we can try to initilize the submodule by finding it's gitdir
+                * we can try to initialize the submodule by finding it's gitdir
                 * in the superproject's 'modules' directory.  In this case the
                 * submodule would not have a worktree.
                 */
index fe421978130fd443b24de16152e8feca0679372b..040057dea6f4f32bf11d429b3ad10a37d4b10520 100644 (file)
@@ -172,7 +172,7 @@ void repo_clear(struct repository *repo);
  * be allocated if needed.
  *
  * Return the number of index entries in the populated index or a value less
- * than zero if an error occured.  If the repository's index has already been
+ * than zero if an error occurred.  If the repository's index has already been
  * populated then the number of entries will simply be returned.
  */
 int repo_read_index(struct repository *repo);
index 3e51fdfe589ce67c5a745845907e15909f4ed905..9281131a9f10cdf1f83c54b42e4fd7034ded16db 100644 (file)
--- a/rerere.c
+++ b/rerere.c
@@ -431,7 +431,7 @@ static int handle_conflict(struct strbuf *out, struct rerere_io *io,
  * and NUL concatenated together.
  *
  * Return 1 if conflict hunks are found, 0 if there are no conflict
- * hunks and -1 if an error occured.
+ * hunks and -1 if an error occurred.
  */
 static int handle_path(unsigned char *hash, struct rerere_io *io, int marker_size)
 {
index 34c77cbb1af91dd1a959f7e9f5b756dc40fd722d..a7322d32788060fc3644161f05c0580d42829c1f 100644 (file)
@@ -565,8 +565,6 @@ int send_pack(struct send_pack_args *args,
 
        if (need_pack_data && cmds_sent) {
                if (pack_objects(out, remote_refs, extra_have, args) < 0) {
-                       for (ref = remote_refs; ref; ref = ref->next)
-                               ref->status = REF_STATUS_NONE;
                        if (args->stateless_rpc)
                                close(out);
                        if (git_connection_is_socket(conn))
@@ -574,10 +572,12 @@ int send_pack(struct send_pack_args *args,
 
                        /*
                         * Do not even bother with the return value; we know we
-                        * are failing, and just want the error() side effects.
+                        * are failing, and just want the error() side effects,
+                        * as well as marking refs with their remote status (if
+                        * we get one).
                         */
                        if (status_report)
-                               receive_unpack_status(&reader);
+                               receive_status(&reader, remote_refs);
 
                        if (use_sideband) {
                                close(demux.out);
index 9d5964fd81fe09985c9283c49ad83f98b055d661..e65076f221fd3ce71efde6a5612c6a20fed12df7 100644 (file)
@@ -131,7 +131,7 @@ static GIT_PATH_FUNC(rebase_path_rewritten_pending,
        "rebase-merge/rewritten-pending")
 
 /*
- * The path of the file containig the OID of the "squash onto" commit, i.e.
+ * The path of the file containing the OID of the "squash onto" commit, i.e.
  * the dummy commit used for `reset [new root]`.
  */
 static GIT_PATH_FUNC(rebase_path_squash_onto, "rebase-merge/squash-onto")
@@ -1126,25 +1126,22 @@ static int run_prepare_commit_msg_hook(struct repository *r,
                                       struct strbuf *msg,
                                       const char *commit)
 {
-       struct argv_array hook_env = ARGV_ARRAY_INIT;
-       int ret;
-       const char *name;
+       int ret = 0;
+       const char *name, *arg1 = NULL, *arg2 = NULL;
 
        name = git_path_commit_editmsg();
        if (write_message(msg->buf, msg->len, name, 0))
                return -1;
 
-       argv_array_pushf(&hook_env, "GIT_INDEX_FILE=%s", r->index_file);
-       argv_array_push(&hook_env, "GIT_EDITOR=:");
-       if (commit)
-               ret = run_hook_le(hook_env.argv, "prepare-commit-msg", name,
-                                 "commit", commit, NULL);
-       else
-               ret = run_hook_le(hook_env.argv, "prepare-commit-msg", name,
-                                 "message", NULL);
-       if (ret)
+       if (commit) {
+               arg1 = "commit";
+               arg2 = commit;
+       } else {
+               arg1 = "message";
+       }
+       if (run_commit_hook(0, r->index_file, "prepare-commit-msg", name,
+                           arg1, arg2, NULL))
                ret = error(_("'prepare-commit-msg' hook failed"));
-       argv_array_clear(&hook_env);
 
        return ret;
 }
@@ -1403,6 +1400,7 @@ static int try_to_commit(struct repository *r,
                goto out;
        }
 
+       run_commit_hook(0, r->index_file, "post-commit", NULL);
        if (flags & AMEND_MSG)
                commit_post_rewrite(r, current_head, oid);
 
@@ -1576,6 +1574,7 @@ static int update_squash_messages(struct repository *r,
        struct strbuf buf = STRBUF_INIT;
        int res;
        const char *message, *body;
+       const char *encoding = get_commit_output_encoding();
 
        if (opts->current_fixup_count > 0) {
                struct strbuf header = STRBUF_INIT;
@@ -1602,7 +1601,7 @@ static int update_squash_messages(struct repository *r,
                        return error(_("need a HEAD to fixup"));
                if (!(head_commit = lookup_commit_reference(r, &head)))
                        return error(_("could not read HEAD"));
-               if (!(head_message = get_commit_buffer(head_commit, NULL)))
+               if (!(head_message = logmsg_reencode(head_commit, NULL, encoding)))
                        return error(_("could not read HEAD's commit message"));
 
                find_commit_subject(head_message, &body);
@@ -1623,7 +1622,7 @@ static int update_squash_messages(struct repository *r,
                unuse_commit_buffer(head_commit, head_message);
        }
 
-       if (!(message = get_commit_buffer(commit, NULL)))
+       if (!(message = logmsg_reencode(commit, NULL, encoding)))
                return error(_("could not read commit message of %s"),
                             oid_to_hex(&commit->object.oid));
        find_commit_subject(message, &body);
@@ -2564,14 +2563,17 @@ static int walk_revs_populate_todo(struct todo_list *todo_list,
        enum todo_command command = opts->action == REPLAY_PICK ?
                TODO_PICK : TODO_REVERT;
        const char *command_string = todo_command_info[command].str;
+       const char *encoding;
        struct commit *commit;
 
        if (prepare_revs(opts))
                return -1;
 
+       encoding = get_log_output_encoding();
+
        while ((commit = get_revision(opts->revs))) {
                struct todo_item *item = append_new_todo(todo_list);
-               const char *commit_buffer = get_commit_buffer(commit, NULL);
+               const char *commit_buffer = logmsg_reencode(commit, NULL, encoding);
                const char *subject;
                int subject_len;
 
@@ -2968,7 +2970,8 @@ static int make_patch(struct repository *r,
 
        strbuf_addf(&buf, "%s/message", get_dir(opts));
        if (!file_exists(buf.buf)) {
-               const char *commit_buffer = get_commit_buffer(commit, NULL);
+               const char *encoding = get_commit_output_encoding();
+               const char *commit_buffer = logmsg_reencode(commit, NULL, encoding);
                find_commit_subject(commit_buffer, &subject);
                res |= write_message(subject, strlen(subject), buf.buf, 1);
                unuse_commit_buffer(commit, commit_buffer);
@@ -3370,7 +3373,8 @@ static int do_merge(struct repository *r,
        }
 
        if (commit) {
-               const char *message = get_commit_buffer(commit, NULL);
+               const char *encoding = get_commit_output_encoding();
+               const char *message = logmsg_reencode(commit, NULL, encoding);
                const char *body;
                int len;
 
@@ -4151,9 +4155,10 @@ static int commit_staged_changes(struct repository *r,
                                 */
                                struct commit *commit;
                                const char *path = rebase_path_squash_msg();
+                               const char *encoding = get_commit_output_encoding();
 
                                if (parse_head(r, &commit) ||
-                                   !(p = get_commit_buffer(commit, NULL)) ||
+                                   !(p = logmsg_reencode(commit, NULL, encoding)) ||
                                    write_message(p, strlen(p), path, 0)) {
                                        unuse_commit_buffer(commit, p);
                                        return error(_("could not write file: "
@@ -4425,7 +4430,6 @@ static const char *label_oid(struct object_id *oid, const char *label,
        struct labels_entry *labels_entry;
        struct string_entry *string_entry;
        struct object_id dummy;
-       size_t len;
        int i;
 
        string_entry = oidmap_get(&state->commit2label, oid);
@@ -4445,10 +4449,10 @@ static const char *label_oid(struct object_id *oid, const char *label,
         * abbreviation for any uninteresting commit's names that does not
         * clash with any other label.
         */
+       strbuf_reset(&state->buf);
        if (!label) {
                char *p;
 
-               strbuf_reset(&state->buf);
                strbuf_grow(&state->buf, GIT_MAX_HEXSZ);
                label = p = state->buf.buf;
 
@@ -4471,32 +4475,55 @@ static const char *label_oid(struct object_id *oid, const char *label,
                                p[i] = save;
                        }
                }
-       } else if (((len = strlen(label)) == the_hash_algo->hexsz &&
-                   !get_oid_hex(label, &dummy)) ||
-                  (len == 1 && *label == '#') ||
-                  hashmap_get_from_hash(&state->labels,
-                                        strihash(label), label)) {
+       } else {
+               struct strbuf *buf = &state->buf;
+
                /*
-                * If the label already exists, or if the label is a valid full
-                * OID, or the label is a '#' (which we use as a separator
-                * between merge heads and oneline), we append a dash and a
-                * number to make it unique.
+                * Sanitize labels by replacing non-alpha-numeric characters
+                * (including white-space ones) by dashes, as they might be
+                * illegal in file names (and hence in ref names).
+                *
+                * Note that we retain non-ASCII UTF-8 characters (identified
+                * via the most significant bit). They should be all acceptable
+                * in file names. We do not validate the UTF-8 here, that's not
+                * the job of this function.
                 */
-               struct strbuf *buf = &state->buf;
+               for (; *label; label++)
+                       if ((*label & 0x80) || isalnum(*label))
+                               strbuf_addch(buf, *label);
+                       /* avoid leading dash and double-dashes */
+                       else if (buf->len && buf->buf[buf->len - 1] != '-')
+                               strbuf_addch(buf, '-');
+               if (!buf->len) {
+                       strbuf_addstr(buf, "rev-");
+                       strbuf_add_unique_abbrev(buf, oid, default_abbrev);
+               }
+               label = buf->buf;
 
-               strbuf_reset(buf);
-               strbuf_add(buf, label, len);
+               if ((buf->len == the_hash_algo->hexsz &&
+                    !get_oid_hex(label, &dummy)) ||
+                   (buf->len == 1 && *label == '#') ||
+                   hashmap_get_from_hash(&state->labels,
+                                         strihash(label), label)) {
+                       /*
+                        * If the label already exists, or if the label is a
+                        * valid full OID, or the label is a '#' (which we use
+                        * as a separator between merge heads and oneline), we
+                        * append a dash and a number to make it unique.
+                        */
+                       size_t len = buf->len;
 
-               for (i = 2; ; i++) {
-                       strbuf_setlen(buf, len);
-                       strbuf_addf(buf, "-%d", i);
-                       if (!hashmap_get_from_hash(&state->labels,
-                                                  strihash(buf->buf),
-                                                  buf->buf))
-                               break;
-               }
+                       for (i = 2; ; i++) {
+                               strbuf_setlen(buf, len);
+                               strbuf_addf(buf, "-%d", i);
+                               if (!hashmap_get_from_hash(&state->labels,
+                                                          strihash(buf->buf),
+                                                          buf->buf))
+                                       break;
+                       }
 
-               label = buf->buf;
+                       label = buf->buf;
+               }
        }
 
        FLEX_ALLOC_STR(labels_entry, label, label);
@@ -4540,10 +4567,15 @@ static int make_script_with_merges(struct pretty_print_context *pp,
        strbuf_init(&state.buf, 32);
 
        if (revs->cmdline.nr && (revs->cmdline.rev[0].flags & BOTTOM)) {
+               struct labels_entry *onto_label_entry;
                struct object_id *oid = &revs->cmdline.rev[0].item->oid;
                FLEX_ALLOC_STR(entry, string, "onto");
                oidcpy(&entry->entry.oid, oid);
                oidmap_put(&state.commit2label, entry);
+
+               FLEX_ALLOC_STR(onto_label_entry, label, "onto");
+               hashmap_entry_init(&onto_label_entry->entry, strihash("onto"));
+               hashmap_add(&state.labels, &onto_label_entry->entry);
        }
 
        /*
@@ -4598,10 +4630,6 @@ static int make_script_with_merges(struct pretty_print_context *pp,
                else
                        strbuf_addbuf(&label, &oneline);
 
-               for (p1 = label.buf; *p1; p1++)
-                       if (isspace(*p1))
-                               *(char *)p1 = '-';
-
                strbuf_reset(&buf);
                strbuf_addf(&buf, "%s -C %s",
                            cmd_merge, oid_to_hex(&commit->object.oid));
@@ -4644,7 +4672,7 @@ static int make_script_with_merges(struct pretty_print_context *pp,
                                label_oid(oid, "branch-point", &state);
                }
 
-               /* Add HEAD as implict "tip of branch" */
+               /* Add HEAD as implicit "tip of branch" */
                if (!iter->next)
                        tips_tail = &commit_list_insert(iter->item,
                                                        tips_tail)->next;
@@ -4826,7 +4854,7 @@ void todo_list_add_exec_commands(struct todo_list *todo_list,
         * are considered part of the pick, so we insert the commands *after*
         * those chains if there are any.
         *
-        * As we insert the exec commands immediatly after rearranging
+        * As we insert the exec commands immediately after rearranging
         * any fixups and before the user edits the list, a fixup chain
         * can never contain comments (any comments are empty picks that
         * have been commented out because the user did not specify
@@ -5169,7 +5197,7 @@ int todo_list_rearrange_squash(struct todo_list *todo_list)
                *commit_todo_item_at(&commit_todo, item->commit) = item;
 
                parse_commit(item->commit);
-               commit_buffer = get_commit_buffer(item->commit, NULL);
+               commit_buffer = logmsg_reencode(item->commit, NULL, "UTF-8");
                find_commit_subject(commit_buffer, &subject);
                format_subject(&buf, subject, " ");
                subject = subjects[i] = strbuf_detach(&buf, &subject_len);
index 574260f6215f60e8c1aedb227c80a26c34da6c94..9f9ae291e3c4ad8bc30abc3c68e18e413df9b6f6 100644 (file)
@@ -202,11 +202,10 @@ void print_commit_summary(struct repository *repo,
 
 int read_author_script(const char *path, char **name, char **email, char **date,
                       int allow_missing);
-#endif
-
 void parse_strategy_opts(struct replay_opts *opts, char *raw_opts);
 int write_basic_state(struct replay_opts *opts, const char *head_name,
                      struct commit *onto, const char *orig_head);
 void sequencer_post_commit_cleanup(struct repository *r, int verbose);
 int sequencer_get_last_command(struct repository* r,
                               enum replay_action *action);
+#endif /* SEQUENCER_H */
index 4d8199b1d916f70f136b767132916fdebb73318e..bae2cdfd5127e42ca66622270326d8094fe13f40 100644 (file)
@@ -93,7 +93,7 @@ static int update_info_file(char *path,
                uic.old_fp = fopen_or_warn(path, "r");
 
        /*
-        * uic_printf will compare incremental comparison aginst old_fp
+        * uic_printf will compare incremental comparison against old_fp
         * and mark uic as stale if needed
         */
        ret = generate(&uic);
index 9d3cf81d4d712f9dd31b86b5542c5cba0401633a..63cd5c923a3c6f29510d1b59436b883c517ec588 100644 (file)
@@ -72,7 +72,7 @@
 /* Not under GCC-alike or glibc */
 #elif defined(_BYTE_ORDER) && defined(_BIG_ENDIAN) && defined(_LITTLE_ENDIAN)
 /*
- * *BSD and newlib (embeded linux, cygwin, etc).
+ * *BSD and newlib (embedded linux, cygwin, etc).
  * the defined(_BIG_ENDIAN) && defined(_LITTLE_ENDIAN) part prevents
  * this condition from matching with Solaris/sparc.
  * (Solaris defines only one endian macro)
index e0cc9d988c70337977853101c397960132af1981..5c300e812e0a11960dba743773720246e1a84cf1 100644 (file)
@@ -19,7 +19,7 @@ void git_SHA1DCFinal(unsigned char hash[20], SHA1_CTX *ctx)
        if (!SHA1DCFinal(hash, ctx))
                return;
        die("SHA-1 appears to be part of a collision attack: %s",
-           sha1_to_hex(hash));
+           hash_to_hex_algop(hash, &hash_algos[GIT_HASH_SHA1]));
 }
 
 /*
index f964399949ce58dc1edd561fb0614776dfa2d01f..7bb0ad07e61774b595b6b448a15a5d36a5cc8877 100644 (file)
@@ -179,7 +179,7 @@ void string_list_remove(struct string_list *list, const char *string,
 
 /**
  * Check if the given string is part of a sorted list. If it is part of the list,
- * return the coresponding string_list_item, NULL otherwise.
+ * return the corresponding string_list_item, NULL otherwise.
  */
 struct string_list_item *string_list_lookup(struct string_list *list, const char *string);
 
index 60d5b77bccd952ffe8128a62827d7a15e728f614..5132ec83f8590b444cd149582f9bb2d6500303e5 100644 (file)
--- a/t/README
+++ b/t/README
@@ -397,6 +397,10 @@ GIT_TEST_STASH_USE_BUILTIN=<boolean>, when false, disables the
 built-in version of git-stash. See 'stash.useBuiltin' in
 git-config(1).
 
+GIT_TEST_ADD_I_USE_BUILTIN=<boolean>, when true, enables the
+built-in version of git add -i. See 'add.interactive.useBuiltin' in
+git-config(1).
+
 GIT_TEST_INDEX_THREADS=<n> enables exercising the multi-threaded loading
 of the index for the whole test suite by bypassing the default number of
 cache entries and thread minimums. Setting this to 1 will make the
index 006d2a8152dc499f3588b130470285703b60dc92..1f32ca66ea5128baf8ecec6adebea07853e5b2f9 100644 (file)
@@ -58,10 +58,11 @@ gitweb_run () {
        GATEWAY_INTERFACE='CGI/1.1'
        HTTP_ACCEPT='*/*'
        REQUEST_METHOD='GET'
-       QUERY_STRING=""$1""
-       PATH_INFO=""$2""
+       QUERY_STRING=$1
+       PATH_INFO=$2
+       REQUEST_URI=/gitweb.cgi$PATH_INFO
        export GATEWAY_INTERFACE HTTP_ACCEPT REQUEST_METHOD \
-               QUERY_STRING PATH_INFO
+               QUERY_STRING PATH_INFO REQUEST_URI
 
        GITWEB_CONFIG=$(pwd)/gitweb_config.perl
        export GITWEB_CONFIG
diff --git a/t/helper/test-read-graph.c b/t/helper/test-read-graph.c
new file mode 100644 (file)
index 0000000..d2884ef
--- /dev/null
@@ -0,0 +1,53 @@
+#include "test-tool.h"
+#include "cache.h"
+#include "commit-graph.h"
+#include "repository.h"
+#include "object-store.h"
+
+int cmd__read_graph(int argc, const char **argv)
+{
+       struct commit_graph *graph = NULL;
+       char *graph_name;
+       int open_ok;
+       int fd;
+       struct stat st;
+       const char *object_dir;
+
+       setup_git_directory();
+       object_dir = get_object_directory();
+
+       graph_name = get_commit_graph_filename(object_dir);
+
+       open_ok = open_commit_graph(graph_name, &fd, &st);
+       if (!open_ok)
+               die_errno(_("Could not open commit-graph '%s'"), graph_name);
+
+       graph = load_commit_graph_one_fd_st(fd, &st);
+       if (!graph)
+               return 1;
+
+       FREE_AND_NULL(graph_name);
+
+       printf("header: %08x %d %d %d %d\n",
+               ntohl(*(uint32_t*)graph->data),
+               *(unsigned char*)(graph->data + 4),
+               *(unsigned char*)(graph->data + 5),
+               *(unsigned char*)(graph->data + 6),
+               *(unsigned char*)(graph->data + 7));
+       printf("num_commits: %u\n", graph->num_commits);
+       printf("chunks:");
+
+       if (graph->chunk_oid_fanout)
+               printf(" oid_fanout");
+       if (graph->chunk_oid_lookup)
+               printf(" oid_lookup");
+       if (graph->chunk_commit_data)
+               printf(" commit_metadata");
+       if (graph->chunk_extra_edges)
+               printf(" extra_edges");
+       printf("\n");
+
+       UNLEAK(graph);
+
+       return 0;
+}
index 19ee26d931d9fb5cff2e4b2d0de55b503daaf571..f20989d4497b596685abfb72bad1f048e41b212c 100644 (file)
@@ -45,6 +45,7 @@ static struct test_cmd cmds[] = {
        { "progress", cmd__progress },
        { "reach", cmd__reach },
        { "read-cache", cmd__read_cache },
+       { "read-graph", cmd__read_graph },
        { "read-midx", cmd__read_midx },
        { "ref-store", cmd__ref_store },
        { "regex", cmd__regex },
index c2aa56ef50d90fc61db8720bbbc9955264036b73..8ed2af71d1b2382def4215862a6b5585876a62cc 100644 (file)
@@ -35,6 +35,7 @@ int cmd__prio_queue(int argc, const char **argv);
 int cmd__progress(int argc, const char **argv);
 int cmd__reach(int argc, const char **argv);
 int cmd__read_cache(int argc, const char **argv);
+int cmd__read_graph(int argc, const char **argv);
 int cmd__read_midx(int argc, const char **argv);
 int cmd__ref_store(int argc, const char **argv);
 int cmd__regex(int argc, const char **argv);
index 6d87961e419e10257b3f619d42004bae0a35d7cf..b72c051f4761609246263c18f5e55a65f0ee9a8f 100644 (file)
@@ -119,3 +119,31 @@ make_empty () {
        git commit --allow-empty -m "$1" &&
        git tag "$1"
 }
+
+# Call this (inside test_expect_success) at the end of a test file to
+# check that no tests have changed editor related environment
+# variables or config settings
+test_editor_unchanged () {
+       # We're only interested in exported variables hence 'sh -c'
+       sh -c 'cat >actual <<-EOF
+       EDITOR=$EDITOR
+       FAKE_COMMIT_AMEND=$FAKE_COMMIT_AMEND
+       FAKE_COMMIT_MESSAGE=$FAKE_COMMIT_MESSAGE
+       FAKE_LINES=$FAKE_LINES
+       GIT_EDITOR=$GIT_EDITOR
+       GIT_SEQUENCE_EDITOR=$GIT_SEQUENCE_EDITOR
+       core.editor=$(git config core.editor)
+       sequence.editor=$(git config sequence.editor)
+       EOF'
+       cat >expect <<-\EOF
+       EDITOR=:
+       FAKE_COMMIT_AMEND=
+       FAKE_COMMIT_MESSAGE=
+       FAKE_LINES=
+       GIT_EDITOR=
+       GIT_SEQUENCE_EDITOR=
+       core.editor=
+       sequence.editor=
+       EOF
+       test_cmp expect actual
+}
index ccdbfdf9743d6adab98bd7b33c55a1b35523eaa7..d0736dd1a00d59cb1774860568136e94f8d23f04 100644 (file)
@@ -6,3 +6,12 @@ hexsz sha256:64
 
 zero sha1:0000000000000000000000000000000000000000
 zero sha256:0000000000000000000000000000000000000000000000000000000000000000
+
+algo sha1:sha1
+algo sha256:sha256
+
+empty_blob sha1:e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
+empty_blob sha256:473a0f4c3be8a93681a267e3b1e9a7dcda1185436fe141f7749120a303721813
+
+empty_tree sha1:4b825dc642cb6eb9a060e54bf8d69288fbee4904
+empty_tree sha256:6ef19b41225c5369f1c104d45d8d85efa9b057b53b14b4b9b939dd74decc5321
index a94d9955d0120971b1bc251847c3715b56313ff8..ce47e1662a9283ec5424c0a11773b2c166a7ecdb 100755 (executable)
@@ -51,7 +51,7 @@ oldtime=$(echo "$oldtime" | sed -e 's/^\([0-9]\+\.[0-9]\+\).*$/\1/')
 newtime=$(echo "$newtime" | sed -e 's/^\([0-9]\+\.[0-9]\+\).*$/\1/')
 
 test $(echo "$newtime" "$oldtime" | awk '{ print ($1 > $2) }') = 1 ||
-       die "New time '$newtime' shoud be greater than old time '$oldtime'"
+       die "New time '$newtime' should be greater than old time '$oldtime'"
 
 tmpdir=$(mktemp -d -t bisect_regression_XXXXXX) || die "Failed to create temp directory"
 echo "$oldtime" >"$tmpdir/oldtime" || die "Failed to write to '$tmpdir/oldtime'"
index 377985194116f65790e27231ca7b16b8433240ea..a369152c473232c09e67d962cbcf7ea9b6689d7c 100755 (executable)
@@ -77,6 +77,7 @@ do
        # actual pack generation, without smudging the on-disk setup
        # between trials.
        test_perf "repack ($nr_packs)" '
+               GIT_TEST_FULL_IN_PACK_ARRAY=1 \
                git pack-objects --keep-true-parents \
                  --honor-pack-keep --non-empty --all \
                  --reflog --indexed-objects --delta-base-offset \
index 4d3f7ba295967e752dcf38f1fb050152ca679eef..7aabde1a691f55972d308b2072b699237949baf5 100755 (executable)
@@ -126,7 +126,7 @@ check_sub_test_lib_test () {
 
 check_sub_test_lib_test_err () {
        name="$1" # stdin is the expected output from the test
-       # expected error output is in descriptior 3
+       # expected error output is in descriptor 3
        (
                cd "$name" &&
                sed -e 's/^> //' -e 's/Z$//' >expect.out &&
index 959b6da449004c526d6f5d106d9061d9763108e9..9fcd56fab373145be53a844c374cce704ce29e97 100755 (executable)
@@ -215,7 +215,7 @@ stats_ascii () {
 }
 
 
-# contruct the attr/ returned by git ls-files --eol
+# construct the attr/ returned by git ls-files --eol
 # Take none (=empty), one or two args
 # convert.c: eol=XX overrides text=auto
 attr_ascii () {
index 7aa0945d8d711d14739da69510b835fcc0e8edb3..bfc4fb9af5f274fe9adcd631d730b74ed368551a 100755 (executable)
@@ -17,7 +17,7 @@ test_lazy_prereq NO_UTF32_BOM '
 write_utf16 () {
        if test_have_prereq NO_UTF16_BOM
        then
-               printf '\xfe\xff'
+               printf '\376\377'
        fi &&
        iconv -f UTF-8 -t UTF-16
 }
@@ -25,7 +25,7 @@ write_utf16 () {
 write_utf32 () {
        if test_have_prereq NO_UTF32_BOM
        then
-               printf '\x00\x00\xfe\xff'
+               printf '\0\0\376\377'
        fi &&
        iconv -f UTF-8 -t UTF-32
 }
index c7b53e494ba43fdcff874a506468a4ecc0881a48..553e94bc5a7a41314121871e6958d2d32f20dc74 100755 (executable)
@@ -285,9 +285,13 @@ test_git_path GIT_OBJECT_DIRECTORY=foo objects/foo foo/foo
 test_git_path GIT_OBJECT_DIRECTORY=foo objects2 .git/objects2
 test_expect_success 'setup common repository' 'git --git-dir=bar init'
 test_git_path GIT_COMMON_DIR=bar index                    .git/index
+test_git_path GIT_COMMON_DIR=bar index.lock               .git/index.lock
 test_git_path GIT_COMMON_DIR=bar HEAD                     .git/HEAD
 test_git_path GIT_COMMON_DIR=bar logs/HEAD                .git/logs/HEAD
+test_git_path GIT_COMMON_DIR=bar logs/HEAD.lock           .git/logs/HEAD.lock
 test_git_path GIT_COMMON_DIR=bar logs/refs/bisect/foo     .git/logs/refs/bisect/foo
+test_git_path GIT_COMMON_DIR=bar logs/refs                bar/logs/refs
+test_git_path GIT_COMMON_DIR=bar logs/refs/               bar/logs/refs/
 test_git_path GIT_COMMON_DIR=bar logs/refs/bisec/foo      bar/logs/refs/bisec/foo
 test_git_path GIT_COMMON_DIR=bar logs/refs/bisec          bar/logs/refs/bisec
 test_git_path GIT_COMMON_DIR=bar logs/refs/bisectfoo      bar/logs/refs/bisectfoo
index 24ccbd8d3b1e6d8dd3c153e3ceec0d8809b61dd4..d2d088d9a0c31752739e1fdb118677278aabaf4f 100755 (executable)
@@ -76,7 +76,7 @@ EOF
 '
 
 test_expect_success 'progress display breaks long lines #2' '
-       # Note: we dont need that many spaces after the title to cover up
+       # Note: we do not need that many spaces after the title to cover up
        # the last line before breaking the progress line.
        sed -e "s/Z$//" >expect <<\EOF &&
 Working hard.......2.........3.........4.........5.........6:   0% (1/100000)<CR>
@@ -104,7 +104,7 @@ EOF
 '
 
 test_expect_success 'progress display breaks long lines #3 - even the first is too long' '
-       # Note: we dont actually need any spaces at the end of the title
+       # Note: we do not actually need any spaces at the end of the title
        # line, because there is no previous progress line to cover up.
        sed -e "s/Z$//" >expect <<\EOF &&
 Working hard.......2.........3.........4.........5.........6:                   Z
index dcb4dbba673eb2eb6e13117babb6b75e8e59a49a..d3b2adb28be7fc5aad0f6bf93110a646732663b8 100755 (executable)
@@ -194,15 +194,15 @@ test_expect_success 'pack-objects with large loose object' '
        test_cmp huge actual
 '
 
-test_expect_success 'tar achiving' '
+test_expect_success 'tar archiving' '
        git archive --format=tar HEAD >/dev/null
 '
 
-test_expect_success 'zip achiving, store only' '
+test_expect_success 'zip archiving, store only' '
        git archive --format=zip -0 HEAD >/dev/null
 '
 
-test_expect_success 'zip achiving, deflate' '
+test_expect_success 'zip archiving, deflate' '
        git archive --format=zip HEAD >/dev/null
 '
 
index d20b4d150d42c9fd6c14eb5f36e01a442d045cee..f1e1b289f989ea94e92b1eaf2ac1009df741deb6 100755 (executable)
@@ -63,7 +63,7 @@ test_expect_success 'listing includes option and expansion' '
        test.one=1
        EOF
        git config --list >actual.full &&
-       grep -v ^core actual.full >actual &&
+       grep -v -e ^core -e ^extensions actual.full >actual &&
        test_cmp expect actual
 '
 
index 1fbd94040818ce818c4c7119ee3419620f9c16ff..69a7f27311c3238b911271f8e75425135aa497a8 100755 (executable)
@@ -344,14 +344,16 @@ test_expect_success "verifying $m's log (logged by config)" '
        test_cmp expect .git/logs/$m
 '
 
-git update-ref $m $D
-cat >.git/logs/$m <<EOF
-$Z $C $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150320 -0500
-$C $A $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150350 -0500
-$A $B $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150380 -0500
-$F $Z $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150680 -0500
-$Z $E $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150980 -0500
-EOF
+test_expect_success 'set up for querying the reflog' '
+       git update-ref $m $D &&
+       cat >.git/logs/$m <<-EOF
+       $Z $C $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150320 -0500
+       $C $A $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150350 -0500
+       $A $B $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150380 -0500
+       $F $Z $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150680 -0500
+       $Z $E $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150980 -0500
+       EOF
+'
 
 ed="Thu, 26 May 2005 18:32:00 -0500"
 gd="Thu, 26 May 2005 18:33:00 -0500"
index 50d28e6fdb6079a97b3b3a1bb0d633e736cbf45e..7c7ff7e961d4066a9d65841c87140f284f9d7dee 100755 (executable)
@@ -616,7 +616,7 @@ test_expect_success 'fsck --name-objects' '
                remove_object $(git rev-parse julius:caesar.t) &&
                test_must_fail git fsck --name-objects >out &&
                tree=$(git rev-parse --verify julius:) &&
-               test_i18ngrep -E "$tree \((refs/heads/master|HEAD)@\{[0-9]*\}:" out
+               test_i18ngrep "$tree (refs/tags/julius:" out
        )
 '
 
index 01abee533dedfd1e2d8bd347d06fc5c0c8b7833a..0177fd815c03d9f3dafeb99e3e915bd89ba714c2 100755 (executable)
@@ -59,6 +59,7 @@ test_rev_parse () {
 ROOT=$(pwd)
 
 test_expect_success 'setup' '
+       test_oid_init &&
        mkdir -p sub/dir work &&
        cp -R .git repo.git
 '
@@ -131,6 +132,20 @@ test_expect_success 'rev-parse --is-shallow-repository in non-shallow repo' '
        test_cmp expect actual
 '
 
+test_expect_success 'rev-parse --show-object-format in repo' '
+       echo "$(test_oid algo)" >expect &&
+       git rev-parse --show-object-format >actual &&
+       test_cmp expect actual &&
+       git rev-parse --show-object-format=storage >actual &&
+       test_cmp expect actual &&
+       git rev-parse --show-object-format=input >actual &&
+       test_cmp expect actual &&
+       git rev-parse --show-object-format=output >actual &&
+       test_cmp expect actual &&
+       test_must_fail git rev-parse --show-object-format=squeamish-ossifrage 2>err &&
+       grep "unknown mode for --show-object-format: squeamish-ossifrage" err
+'
+
 test_expect_success 'showing the superproject correctly' '
        git rev-parse --show-superproject-working-tree >out &&
        test_must_be_empty out &&
index c19fb500cb225273fce730c656fea00a3ada624d..25744e270a467bcff246d4be8f32c9afcfb8a7ca 100755 (executable)
@@ -339,7 +339,7 @@ test_expect_success C_LOCALE_OUTPUT 'ambiguity hints' '
 test_expect_success C_LOCALE_OUTPUT 'ambiguity hints respect type' '
        test_must_fail git rev-parse 000000000^{commit} 2>stderr &&
        grep ^hint: stderr >hints &&
-       # 5 commits, 1 tag (which is a commitish), plus intro line
+       # 5 commits, 1 tag (which is a committish), plus intro line
        test_line_count = 7 hints
 '
 
index e819ba741ec96018755a08ef956998edfdd1dcc4..8a9831413c38e69ce121497e112042cf3d436748 100755 (executable)
@@ -587,4 +587,28 @@ test_expect_success '"add" should not fail because of another bad worktree' '
        )
 '
 
+test_expect_success '"add" with uninitialized submodule, with submodule.recurse unset' '
+       test_create_repo submodule &&
+       test_commit -C submodule first &&
+       test_create_repo project &&
+       git -C project submodule add ../submodule &&
+       git -C project add submodule &&
+       test_tick &&
+       git -C project commit -m add_sub &&
+       git clone project project-clone &&
+       git -C project-clone worktree add ../project-2
+'
+test_expect_success '"add" with uninitialized submodule, with submodule.recurse set' '
+       git -C project-clone -c submodule.recurse worktree add ../project-3
+'
+
+test_expect_success '"add" with initialized submodule, with submodule.recurse unset' '
+       git -C project-clone submodule update --init &&
+       git -C project-clone worktree add ../project-4
+'
+
+test_expect_success '"add" with initialized submodule, with submodule.recurse set' '
+       git -C project-clone -c submodule.recurse worktree add ../project-5
+'
+
 test_done
index 44f378ce41d2f6a151b04759e930ef8f09242343..52ed665fcd2dd7fbcbfbb77be4272b26f81c4f1f 100755 (executable)
@@ -47,7 +47,7 @@ test_expect_success setup '
        git add .
 '
 
-test_expect_success 'git -ls-files --with-tree should succeed from subdir' '
+test_expect_success 'git ls-files --with-tree should succeed from subdir' '
        # We have to run from a sub-directory to trigger prune_path
        # Then we finally get to run our --with-tree test
        (
@@ -57,7 +57,7 @@ test_expect_success 'git -ls-files --with-tree should succeed from subdir' '
 '
 
 test_expect_success \
-    'git -ls-files --with-tree should add entries from named tree.' \
+    'git ls-files --with-tree should add entries from named tree.' \
     'test_cmp expected output'
 
 test_done
index 9ea5fa4fd246374d6c49dfc35c12cfdbd84957c2..f41b2afb9964b57a0010a80af7412a88678d6e25 100755 (executable)
@@ -240,7 +240,7 @@ test_expect_success 'retry acquiring packed-refs.lock' '
 
 test_expect_success SYMLINKS 'pack symlinked packed-refs' '
        # First make sure that symlinking works when reading:
-       git update-ref refs/heads/loosy refs/heads/master &&
+       git update-ref refs/heads/lossy refs/heads/master &&
        git for-each-ref >all-refs-before &&
        mv .git/packed-refs .git/my-deviant-packed-refs &&
        ln -s my-deviant-packed-refs .git/packed-refs &&
index d3fa298c6a1b382a2c6818495a401753a7dd4375..d66a5f6faa0ae8c4aecde52decde951d07080615 100755 (executable)
@@ -864,6 +864,24 @@ test_expect_success 'append to note from other note with "git notes append -c"'
 '
 
 test_expect_success 'copy note with "git notes copy"' '
+       commit=$(git rev-parse 4th) &&
+       cat >expect <<-EOF &&
+               commit $commit
+               Author: A U Thor <author@example.com>
+               Date:   Thu Apr 7 15:16:13 2005 -0700
+
+               ${indent}4th
+
+               Notes:
+               ${indent}This is a blob object
+       EOF
+       git notes copy 8th 4th &&
+       git log 3rd..4th >actual &&
+       test_cmp expect actual &&
+       test "$(git note list 4th)" = "$(git note list 8th)"
+'
+
+test_expect_success 'copy note with "git notes copy" with default' '
        test_commit 11th &&
        commit=$(git rev-parse HEAD) &&
        cat >expect <<-EOF &&
@@ -878,7 +896,7 @@ test_expect_success 'copy note with "git notes copy"' '
                ${indent}
                ${indent}yet another note
        EOF
-       git notes copy HEAD^ HEAD &&
+       git notes copy HEAD^ &&
        git log -1 >actual &&
        test_cmp expect actual &&
        test "$(git notes list HEAD)" = "$(git notes list HEAD^)"
@@ -892,6 +910,24 @@ test_expect_success 'prevent overwrite with "git notes copy"' '
 '
 
 test_expect_success 'allow overwrite with "git notes copy -f"' '
+       commit=$(git rev-parse HEAD) &&
+       cat >expect <<-EOF &&
+               commit $commit
+               Author: A U Thor <author@example.com>
+               Date:   Thu Apr 7 15:23:13 2005 -0700
+
+               ${indent}11th
+
+               Notes:
+               ${indent}This is a blob object
+       EOF
+       git notes copy -f HEAD~3 HEAD &&
+       git log -1 >actual &&
+       test_cmp expect actual &&
+       test "$(git notes list HEAD)" = "$(git notes list HEAD~3)"
+'
+
+test_expect_success 'allow overwrite with "git notes copy -f" with default' '
        commit=$(git rev-parse HEAD) &&
        cat >expect <<-EOF &&
                commit $commit
@@ -905,7 +941,7 @@ test_expect_success 'allow overwrite with "git notes copy -f"' '
                ${indent}
                ${indent}yet another note
        EOF
-       git notes copy -f HEAD~2 HEAD &&
+       git notes copy -f HEAD~2 &&
        git log -1 >actual &&
        test_cmp expect actual &&
        test "$(git notes list HEAD)" = "$(git notes list HEAD~2)"
@@ -1167,8 +1203,10 @@ test_expect_success 'GIT_NOTES_REWRITE_REF overrides config' '
 '
 
 test_expect_success 'git notes copy diagnoses too many or too few parameters' '
-       test_must_fail git notes copy &&
-       test_must_fail git notes copy one two three
+       test_must_fail git notes copy 2>error &&
+       test_i18ngrep "too few parameters" error &&
+       test_must_fail git notes copy one two three 2>error &&
+       test_i18ngrep "too many parameters" error
 '
 
 test_expect_success 'git notes get-ref expands refs/heads/master to refs/notes/refs/heads/master' '
index d2dfbe46b9298719c41f35501d876c06c73235eb..bf0dc756d2e6ab5b8626c04d76f21d03fb712b28 100755 (executable)
@@ -76,8 +76,11 @@ test_expect_success 'rebase -i with empty HEAD' '
        cat >expect <<-\EOF &&
        error: nothing to do
        EOF
-       set_fake_editor &&
-       test_must_fail env FAKE_LINES="1 exec_true" git rebase -i HEAD^ >actual 2>&1 &&
+       (
+               set_fake_editor &&
+               test_must_fail env FAKE_LINES="1 exec_true" \
+                       git rebase -i HEAD^ >actual 2>&1
+       ) &&
        test_i18ncmp expect actual
 '
 
@@ -136,8 +139,11 @@ test_expect_success 'rebase -i sets work tree properly' '
 
 test_expect_success 'rebase -i with the exec command checks tree cleanness' '
        git checkout master &&
-       set_fake_editor &&
-       test_must_fail env FAKE_LINES="exec_echo_foo_>file1 1" git rebase -i HEAD^ &&
+       (
+               set_fake_editor &&
+               test_must_fail env FAKE_LINES="exec_echo_foo_>file1 1" \
+                       git rebase -i HEAD^
+       ) &&
        test_cmp_rev master^ HEAD &&
        git reset --hard &&
        git rebase --continue
@@ -163,9 +169,11 @@ test_expect_success 'rebase -x with newline in command fails' '
 test_expect_success 'rebase -i with exec of inexistent command' '
        git checkout master &&
        test_when_finished "git rebase --abort" &&
-       set_fake_editor &&
-       test_must_fail env FAKE_LINES="exec_this-command-does-not-exist 1" \
-       git rebase -i HEAD^ >actual 2>&1 &&
+       (
+               set_fake_editor &&
+               test_must_fail env FAKE_LINES="exec_this-command-does-not-exist 1" \
+                       git rebase -i HEAD^ >actual 2>&1
+       ) &&
        ! grep "Maybe git-rebase is broken" actual
 '
 
@@ -176,7 +184,6 @@ test_expect_success 'implicit interactive rebase does not invoke sequence editor
 
 test_expect_success 'no changes are a nop' '
        git checkout branch2 &&
-       set_fake_editor &&
        git rebase -i F &&
        test "$(git symbolic-ref -q HEAD)" = "refs/heads/branch2" &&
        test $(git rev-parse I) = $(git rev-parse HEAD)
@@ -186,7 +193,6 @@ test_expect_success 'test the [branch] option' '
        git checkout -b dead-end &&
        git rm file6 &&
        git commit -m "stop here" &&
-       set_fake_editor &&
        git rebase -i F branch2 &&
        test "$(git symbolic-ref -q HEAD)" = "refs/heads/branch2" &&
        test $(git rev-parse I) = $(git rev-parse branch2) &&
@@ -195,7 +201,6 @@ test_expect_success 'test the [branch] option' '
 
 test_expect_success 'test --onto <branch>' '
        git checkout -b test-onto branch2 &&
-       set_fake_editor &&
        git rebase -i --onto branch1 F &&
        test "$(git symbolic-ref -q HEAD)" = "refs/heads/test-onto" &&
        test $(git rev-parse HEAD^) = $(git rev-parse branch1) &&
@@ -205,7 +210,6 @@ test_expect_success 'test --onto <branch>' '
 test_expect_success 'rebase on top of a non-conflicting commit' '
        git checkout branch1 &&
        git tag original-branch1 &&
-       set_fake_editor &&
        git rebase -i branch2 &&
        test file6 = $(git diff --name-only original-branch1) &&
        test "$(git symbolic-ref -q HEAD)" = "refs/heads/branch1" &&
@@ -225,8 +229,10 @@ test_expect_success 'reflog for the branch shows correct finish message' '
 '
 
 test_expect_success 'exchange two commits' '
-       set_fake_editor &&
-       FAKE_LINES="2 1" git rebase -i HEAD~2 &&
+       (
+               set_fake_editor &&
+               FAKE_LINES="2 1" git rebase -i HEAD~2
+       ) &&
        test H = $(git cat-file commit HEAD^ | sed -ne \$p) &&
        test G = $(git cat-file commit HEAD | sed -ne \$p) &&
        blob1=$(git rev-parse --short HEAD^:file1) &&
@@ -252,7 +258,6 @@ test_expect_success 'stop on conflicting pick' '
        >>>>>>> $commit... G
        EOF
        git tag new-branch1 &&
-       set_fake_editor &&
        test_must_fail git rebase -i master &&
        test "$(git rev-parse HEAD~3)" = "$(git rev-parse master)" &&
        test_cmp expect .git/rebase-merge/patch &&
@@ -281,7 +286,6 @@ test_expect_success 'abort' '
 test_expect_success 'abort with error when new base cannot be checked out' '
        git rm --cached file1 &&
        git commit -m "remove file in base" &&
-       set_fake_editor &&
        test_must_fail git rebase -i master > output 2>&1 &&
        test_i18ngrep "The following untracked working tree files would be overwritten by checkout:" \
                output &&
@@ -296,7 +300,6 @@ test_expect_success 'retain authorship' '
        test_tick &&
        GIT_AUTHOR_NAME="Twerp Snog" git commit -m "different author" &&
        git tag twerp &&
-       set_fake_editor &&
        git rebase -i --onto master HEAD^ &&
        git show HEAD | grep "^Author: Twerp Snog"
 '
@@ -314,7 +317,6 @@ test_expect_success 'retain authorship w/ conflicts' '
        test_commit b conflict b conflict-b &&
        GIT_AUTHOR_NAME=$oGIT_AUTHOR_NAME &&
 
-       set_fake_editor &&
        test_must_fail git rebase -i conflict-a &&
        echo resolved >conflict &&
        git add conflict &&
@@ -330,9 +332,11 @@ test_expect_success 'squash' '
        test_tick &&
        GIT_AUTHOR_NAME="Nitfol" git commit -m "nitfol" file7 &&
        echo "******************************" &&
-       set_fake_editor &&
-       FAKE_LINES="1 squash 2" EXPECT_HEADER_COUNT=2 \
-               git rebase -i --onto master HEAD~2 &&
+       (
+               set_fake_editor &&
+               FAKE_LINES="1 squash 2" EXPECT_HEADER_COUNT=2 \
+                       git rebase -i --onto master HEAD~2
+       ) &&
        test B = $(cat file7) &&
        test $(git rev-parse HEAD^) = $(git rev-parse master)
 '
@@ -343,7 +347,6 @@ test_expect_success 'retain authorship when squashing' '
 
 test_expect_success REBASE_P '-p handles "no changes" gracefully' '
        HEAD=$(git rev-parse HEAD) &&
-       set_fake_editor &&
        git rebase -i -p HEAD^ &&
        git update-index --refresh &&
        git diff-files --quiet &&
@@ -353,8 +356,10 @@ test_expect_success REBASE_P '-p handles "no changes" gracefully' '
 
 test_expect_failure REBASE_P 'exchange two commits with -p' '
        git checkout H &&
-       set_fake_editor &&
-       FAKE_LINES="2 1" git rebase -i -p HEAD~2 &&
+       (
+               set_fake_editor &&
+               FAKE_LINES="2 1" git rebase -i -p HEAD~2
+       ) &&
        test H = $(git cat-file commit HEAD^ | sed -ne \$p) &&
        test G = $(git cat-file commit HEAD | sed -ne \$p)
 '
@@ -388,7 +393,6 @@ test_expect_success REBASE_P 'preserve merges with -p' '
        git commit -m M file1 &&
        git checkout -b to-be-rebased &&
        test_tick &&
-       set_fake_editor &&
        git rebase -i -p --onto branch1 master &&
        git update-index --refresh &&
        git diff-files --quiet &&
@@ -403,8 +407,10 @@ test_expect_success REBASE_P 'preserve merges with -p' '
 '
 
 test_expect_success REBASE_P 'edit ancestor with -p' '
-       set_fake_editor &&
-       FAKE_LINES="1 2 edit 3 4" git rebase -i -p HEAD~3 &&
+       (
+               set_fake_editor &&
+               FAKE_LINES="1 2 edit 3 4" git rebase -i -p HEAD~3
+       ) &&
        echo 2 > unrelated-file &&
        test_tick &&
        git commit -m L2-modified --amend unrelated-file &&
@@ -418,11 +424,13 @@ test_expect_success REBASE_P 'edit ancestor with -p' '
 test_expect_success '--continue tries to commit' '
        git reset --hard D &&
        test_tick &&
-       set_fake_editor &&
-       test_must_fail git rebase -i --onto new-branch1 HEAD^ &&
-       echo resolved > file1 &&
-       git add file1 &&
-       FAKE_COMMIT_MESSAGE="chouette!" git rebase --continue &&
+       (
+               set_fake_editor &&
+               test_must_fail git rebase -i --onto new-branch1 HEAD^ &&
+               echo resolved > file1 &&
+               git add file1 &&
+               FAKE_COMMIT_MESSAGE="chouette!" git rebase --continue
+       ) &&
        test $(git rev-parse HEAD^) = $(git rev-parse new-branch1) &&
        git show HEAD | grep chouette
 '
@@ -430,7 +438,6 @@ test_expect_success '--continue tries to commit' '
 test_expect_success 'verbose flag is heeded, even after --continue' '
        git reset --hard master@{1} &&
        test_tick &&
-       set_fake_editor &&
        test_must_fail git rebase -v -i --onto new-branch1 HEAD^ &&
        echo resolved > file1 &&
        git add file1 &&
@@ -440,10 +447,13 @@ test_expect_success 'verbose flag is heeded, even after --continue' '
 
 test_expect_success C_LOCALE_OUTPUT 'multi-squash only fires up editor once' '
        base=$(git rev-parse HEAD~4) &&
-       set_fake_editor &&
-       FAKE_COMMIT_AMEND="ONCE" FAKE_LINES="1 squash 2 squash 3 squash 4" \
-               EXPECT_HEADER_COUNT=4 \
-               git rebase -i $base &&
+       (
+               set_fake_editor &&
+               FAKE_COMMIT_AMEND="ONCE" \
+                       FAKE_LINES="1 squash 2 squash 3 squash 4" \
+                       EXPECT_HEADER_COUNT=4 \
+                       git rebase -i $base
+       ) &&
        test $base = $(git rev-parse HEAD^) &&
        test 1 = $(git show | grep ONCE | wc -l)
 '
@@ -451,9 +461,12 @@ test_expect_success C_LOCALE_OUTPUT 'multi-squash only fires up editor once' '
 test_expect_success C_LOCALE_OUTPUT 'multi-fixup does not fire up editor' '
        git checkout -b multi-fixup E &&
        base=$(git rev-parse HEAD~4) &&
-       set_fake_editor &&
-       FAKE_COMMIT_AMEND="NEVER" FAKE_LINES="1 fixup 2 fixup 3 fixup 4" \
-               git rebase -i $base &&
+       (
+               set_fake_editor &&
+               FAKE_COMMIT_AMEND="NEVER" \
+                       FAKE_LINES="1 fixup 2 fixup 3 fixup 4" \
+                       git rebase -i $base
+       ) &&
        test $base = $(git rev-parse HEAD^) &&
        test 0 = $(git show | grep NEVER | wc -l) &&
        git checkout @{-1} &&
@@ -463,12 +476,15 @@ test_expect_success C_LOCALE_OUTPUT 'multi-fixup does not fire up editor' '
 test_expect_success 'commit message used after conflict' '
        git checkout -b conflict-fixup conflict-branch &&
        base=$(git rev-parse HEAD~4) &&
-       set_fake_editor &&
-       test_must_fail env FAKE_LINES="1 fixup 3 fixup 4" git rebase -i $base &&
-       echo three > conflict &&
-       git add conflict &&
-       FAKE_COMMIT_AMEND="ONCE" EXPECT_HEADER_COUNT=2 \
-               git rebase --continue &&
+       (
+               set_fake_editor &&
+               test_must_fail env FAKE_LINES="1 fixup 3 fixup 4" \
+                       git rebase -i $base &&
+               echo three > conflict &&
+               git add conflict &&
+               FAKE_COMMIT_AMEND="ONCE" EXPECT_HEADER_COUNT=2 \
+                       git rebase --continue
+       ) &&
        test $base = $(git rev-parse HEAD^) &&
        test 1 = $(git show | grep ONCE | wc -l) &&
        git checkout @{-1} &&
@@ -478,12 +494,15 @@ test_expect_success 'commit message used after conflict' '
 test_expect_success 'commit message retained after conflict' '
        git checkout -b conflict-squash conflict-branch &&
        base=$(git rev-parse HEAD~4) &&
-       set_fake_editor &&
-       test_must_fail env FAKE_LINES="1 fixup 3 squash 4" git rebase -i $base &&
-       echo three > conflict &&
-       git add conflict &&
-       FAKE_COMMIT_AMEND="TWICE" EXPECT_HEADER_COUNT=2 \
-               git rebase --continue &&
+       (
+               set_fake_editor &&
+               test_must_fail env FAKE_LINES="1 fixup 3 squash 4" \
+                       git rebase -i $base &&
+               echo three > conflict &&
+               git add conflict &&
+               FAKE_COMMIT_AMEND="TWICE" EXPECT_HEADER_COUNT=2 \
+                       git rebase --continue
+       ) &&
        test $base = $(git rev-parse HEAD^) &&
        test 2 = $(git show | grep TWICE | wc -l) &&
        git checkout @{-1} &&
@@ -500,10 +519,13 @@ test_expect_success C_LOCALE_OUTPUT 'squash and fixup generate correct log messa
        EOF
        git checkout -b squash-fixup E &&
        base=$(git rev-parse HEAD~4) &&
-       set_fake_editor &&
-       FAKE_COMMIT_AMEND="ONCE" FAKE_LINES="1 fixup 2 squash 3 fixup 4" \
-               EXPECT_HEADER_COUNT=4 \
-               git rebase -i $base &&
+       (
+               set_fake_editor &&
+               FAKE_COMMIT_AMEND="ONCE" \
+                       FAKE_LINES="1 fixup 2 squash 3 fixup 4" \
+                       EXPECT_HEADER_COUNT=4 \
+                       git rebase -i $base
+       ) &&
        git cat-file commit HEAD | sed -e 1,/^\$/d > actual-squash-fixup &&
        test_cmp expect-squash-fixup actual-squash-fixup &&
        git cat-file commit HEAD@{2} |
@@ -517,10 +539,13 @@ test_expect_success C_LOCALE_OUTPUT 'squash and fixup generate correct log messa
 test_expect_success C_LOCALE_OUTPUT 'squash ignores comments' '
        git checkout -b skip-comments E &&
        base=$(git rev-parse HEAD~4) &&
-       set_fake_editor &&
-       FAKE_COMMIT_AMEND="ONCE" FAKE_LINES="# 1 # squash 2 # squash 3 # squash 4 #" \
-               EXPECT_HEADER_COUNT=4 \
-               git rebase -i $base &&
+       (
+               set_fake_editor &&
+               FAKE_COMMIT_AMEND="ONCE" \
+                       FAKE_LINES="# 1 # squash 2 # squash 3 # squash 4 #" \
+                       EXPECT_HEADER_COUNT=4 \
+                       git rebase -i $base
+       ) &&
        test $base = $(git rev-parse HEAD^) &&
        test 1 = $(git show | grep ONCE | wc -l) &&
        git checkout @{-1} &&
@@ -530,10 +555,13 @@ test_expect_success C_LOCALE_OUTPUT 'squash ignores comments' '
 test_expect_success C_LOCALE_OUTPUT 'squash ignores blank lines' '
        git checkout -b skip-blank-lines E &&
        base=$(git rev-parse HEAD~4) &&
-       set_fake_editor &&
-       FAKE_COMMIT_AMEND="ONCE" FAKE_LINES="> 1 > squash 2 > squash 3 > squash 4 >" \
-               EXPECT_HEADER_COUNT=4 \
-               git rebase -i $base &&
+       (
+               set_fake_editor &&
+               FAKE_COMMIT_AMEND="ONCE" \
+                       FAKE_LINES="> 1 > squash 2 > squash 3 > squash 4 >" \
+                       EXPECT_HEADER_COUNT=4 \
+                       git rebase -i $base
+       ) &&
        test $base = $(git rev-parse HEAD^) &&
        test 1 = $(git show | grep ONCE | wc -l) &&
        git checkout @{-1} &&
@@ -543,17 +571,21 @@ test_expect_success C_LOCALE_OUTPUT 'squash ignores blank lines' '
 test_expect_success 'squash works as expected' '
        git checkout -b squash-works no-conflict-branch &&
        one=$(git rev-parse HEAD~3) &&
-       set_fake_editor &&
-       FAKE_LINES="1 s 3 2" EXPECT_HEADER_COUNT=2 \
-               git rebase -i HEAD~3 &&
+       (
+               set_fake_editor &&
+               FAKE_LINES="1 s 3 2" EXPECT_HEADER_COUNT=2 git rebase -i HEAD~3
+       ) &&
        test $one = $(git rev-parse HEAD~2)
 '
 
 test_expect_success 'interrupted squash works as expected' '
        git checkout -b interrupted-squash conflict-branch &&
        one=$(git rev-parse HEAD~3) &&
-       set_fake_editor &&
-       test_must_fail env FAKE_LINES="1 squash 3 2" git rebase -i HEAD~3 &&
+       (
+               set_fake_editor &&
+               test_must_fail env FAKE_LINES="1 squash 3 2" \
+                       git rebase -i HEAD~3
+       ) &&
        test_write_lines one two four > conflict &&
        git add conflict &&
        test_must_fail git rebase --continue &&
@@ -566,8 +598,11 @@ test_expect_success 'interrupted squash works as expected' '
 test_expect_success 'interrupted squash works as expected (case 2)' '
        git checkout -b interrupted-squash2 conflict-branch &&
        one=$(git rev-parse HEAD~3) &&
-       set_fake_editor &&
-       test_must_fail env FAKE_LINES="3 squash 1 2" git rebase -i HEAD~3 &&
+       (
+               set_fake_editor &&
+               test_must_fail env FAKE_LINES="3 squash 1 2" \
+                       git rebase -i HEAD~3
+       ) &&
        test_write_lines one four > conflict &&
        git add conflict &&
        test_must_fail git rebase --continue &&
@@ -587,11 +622,13 @@ test_expect_success '--continue tries to commit, even for "edit"' '
        git commit -m "unrelated change" &&
        parent=$(git rev-parse HEAD^) &&
        test_tick &&
-       set_fake_editor &&
-       FAKE_LINES="edit 1" git rebase -i HEAD^ &&
-       echo edited > file7 &&
-       git add file7 &&
-       FAKE_COMMIT_MESSAGE="chouette!" git rebase --continue &&
+       (
+               set_fake_editor &&
+               FAKE_LINES="edit 1" git rebase -i HEAD^ &&
+               echo edited > file7 &&
+               git add file7 &&
+               FAKE_COMMIT_MESSAGE="chouette!" git rebase --continue
+       ) &&
        test edited = $(git show HEAD:file7) &&
        git show HEAD | grep chouette &&
        test $parent = $(git rev-parse HEAD^)
@@ -600,34 +637,41 @@ test_expect_success '--continue tries to commit, even for "edit"' '
 test_expect_success 'aborted --continue does not squash commits after "edit"' '
        old=$(git rev-parse HEAD) &&
        test_tick &&
-       set_fake_editor &&
-       FAKE_LINES="edit 1" git rebase -i HEAD^ &&
-       echo "edited again" > file7 &&
-       git add file7 &&
-       test_must_fail env FAKE_COMMIT_MESSAGE=" " git rebase --continue &&
+       (
+               set_fake_editor &&
+               FAKE_LINES="edit 1" git rebase -i HEAD^ &&
+               echo "edited again" > file7 &&
+               git add file7 &&
+               test_must_fail env FAKE_COMMIT_MESSAGE=" " git rebase --continue
+       ) &&
        test $old = $(git rev-parse HEAD) &&
        git rebase --abort
 '
 
 test_expect_success 'auto-amend only edited commits after "edit"' '
        test_tick &&
-       set_fake_editor &&
-       FAKE_LINES="edit 1" git rebase -i HEAD^ &&
-       echo "edited again" > file7 &&
-       git add file7 &&
-       FAKE_COMMIT_MESSAGE="edited file7 again" git commit &&
-       echo "and again" > file7 &&
-       git add file7 &&
-       test_tick &&
-       test_must_fail env FAKE_COMMIT_MESSAGE="and again" git rebase --continue &&
+       (
+               set_fake_editor &&
+               FAKE_LINES="edit 1" git rebase -i HEAD^ &&
+               echo "edited again" > file7 &&
+               git add file7 &&
+               FAKE_COMMIT_MESSAGE="edited file7 again" git commit &&
+               echo "and again" > file7 &&
+               git add file7 &&
+               test_tick &&
+               test_must_fail env FAKE_COMMIT_MESSAGE="and again" \
+                       git rebase --continue
+       ) &&
        git rebase --abort
 '
 
 test_expect_success 'clean error after failed "exec"' '
        test_tick &&
        test_when_finished "git rebase --abort || :" &&
-       set_fake_editor &&
-       test_must_fail env FAKE_LINES="1 exec_false" git rebase -i HEAD^ &&
+       (
+               set_fake_editor &&
+               test_must_fail env FAKE_LINES="1 exec_false" git rebase -i HEAD^
+       ) &&
        echo "edited again" > file7 &&
        git add file7 &&
        test_must_fail git rebase --continue 2>error &&
@@ -638,8 +682,10 @@ test_expect_success 'rebase a detached HEAD' '
        grandparent=$(git rev-parse HEAD~2) &&
        git checkout $(git rev-parse HEAD) &&
        test_tick &&
-       set_fake_editor &&
-       FAKE_LINES="2 1" git rebase -i HEAD~2 &&
+       (
+               set_fake_editor &&
+               FAKE_LINES="2 1" git rebase -i HEAD~2
+       ) &&
        test $grandparent = $(git rev-parse HEAD~2)
 '
 
@@ -654,9 +700,10 @@ test_expect_success 'rebase a commit violating pre-commit' '
        test_must_fail git commit -m doesnt-verify file1 &&
        git commit -m doesnt-verify --no-verify file1 &&
        test_tick &&
-       set_fake_editor &&
-       FAKE_LINES=2 git rebase -i HEAD~2
-
+       (
+               set_fake_editor &&
+               FAKE_LINES=2 git rebase -i HEAD~2
+       )
 '
 
 test_expect_success 'rebase with a file named HEAD in worktree' '
@@ -676,8 +723,10 @@ test_expect_success 'rebase with a file named HEAD in worktree' '
                git commit -m "Add body"
        ) &&
 
-       set_fake_editor &&
-       FAKE_LINES="1 squash 2" git rebase -i @{-1} &&
+       (
+               set_fake_editor &&
+               FAKE_LINES="1 squash 2" git rebase -i @{-1}
+       ) &&
        test "$(git show -s --pretty=format:%an)" = "Squashed Away"
 
 '
@@ -688,7 +737,6 @@ test_expect_success 'do "noop" when there is nothing to cherry-pick' '
        GIT_EDITOR=: git commit --amend \
                --author="Somebody else <somebody@else.com>" &&
        test $(git rev-parse branch3) != $(git rev-parse branch4) &&
-       set_fake_editor &&
        git rebase -i branch3 &&
        test $(git rev-parse branch3) = $(git rev-parse branch4)
 
@@ -713,13 +761,14 @@ test_expect_success 'submodule rebase setup' '
                git commit -a -m "submodule second"
        ) &&
        test_tick &&
-       set_fake_editor &&
        git commit -a -m "Three changes submodule"
 '
 
 test_expect_success 'submodule rebase -i' '
-       set_fake_editor &&
-       FAKE_LINES="1 squash 2 3" git rebase -i A
+       (
+               set_fake_editor &&
+               FAKE_LINES="1 squash 2 3" git rebase -i A
+       )
 '
 
 test_expect_success 'submodule conflict setup' '
@@ -736,7 +785,6 @@ test_expect_success 'submodule conflict setup' '
 '
 
 test_expect_success 'rebase -i continue with only submodule staged' '
-       set_fake_editor &&
        test_must_fail git rebase -i submodule-base &&
        git add sub &&
        git rebase --continue &&
@@ -746,7 +794,6 @@ test_expect_success 'rebase -i continue with only submodule staged' '
 test_expect_success 'rebase -i continue with unstaged submodule' '
        git checkout submodule-topic &&
        git reset --hard &&
-       set_fake_editor &&
        test_must_fail git rebase -i submodule-base &&
        git reset &&
        git rebase --continue &&
@@ -759,7 +806,6 @@ test_expect_success 'avoid unnecessary reset' '
        test-tool chmtime =123456789 file3 &&
        git update-index --refresh &&
        HEAD=$(git rev-parse HEAD) &&
-       set_fake_editor &&
        git rebase -i HEAD~4 &&
        test $HEAD = $(git rev-parse HEAD) &&
        MTIME=$(test-tool chmtime --get file3) &&
@@ -768,16 +814,22 @@ test_expect_success 'avoid unnecessary reset' '
 
 test_expect_success 'reword' '
        git checkout -b reword-branch master &&
-       set_fake_editor &&
-       FAKE_LINES="1 2 3 reword 4" FAKE_COMMIT_MESSAGE="E changed" git rebase -i A &&
-       git show HEAD | grep "E changed" &&
-       test $(git rev-parse master) != $(git rev-parse HEAD) &&
-       test $(git rev-parse master^) = $(git rev-parse HEAD^) &&
-       FAKE_LINES="1 2 reword 3 4" FAKE_COMMIT_MESSAGE="D changed" git rebase -i A &&
-       git show HEAD^ | grep "D changed" &&
-       FAKE_LINES="reword 1 2 3 4" FAKE_COMMIT_MESSAGE="B changed" git rebase -i A &&
-       git show HEAD~3 | grep "B changed" &&
-       FAKE_LINES="1 r 2 pick 3 p 4" FAKE_COMMIT_MESSAGE="C changed" git rebase -i A &&
+       (
+               set_fake_editor &&
+               FAKE_LINES="1 2 3 reword 4" FAKE_COMMIT_MESSAGE="E changed" \
+                       git rebase -i A &&
+               git show HEAD | grep "E changed" &&
+               test $(git rev-parse master) != $(git rev-parse HEAD) &&
+               test $(git rev-parse master^) = $(git rev-parse HEAD^) &&
+               FAKE_LINES="1 2 reword 3 4" FAKE_COMMIT_MESSAGE="D changed" \
+                       git rebase -i A &&
+               git show HEAD^ | grep "D changed" &&
+               FAKE_LINES="reword 1 2 3 4" FAKE_COMMIT_MESSAGE="B changed" \
+                       git rebase -i A &&
+               git show HEAD~3 | grep "B changed" &&
+               FAKE_LINES="1 r 2 pick 3 p 4" FAKE_COMMIT_MESSAGE="C changed" \
+                       git rebase -i A
+       ) &&
        git show HEAD~2 | grep "C changed"
 '
 
@@ -788,7 +840,6 @@ test_expect_success 'rebase -i can copy notes' '
        test_commit n2 &&
        test_commit n3 &&
        git notes add -m"a note" n3 &&
-       set_fake_editor &&
        git rebase -i --onto n1 n2 &&
        test "a note" = "$(git notes show HEAD)"
 '
@@ -801,8 +852,11 @@ test_expect_success 'rebase -i can copy notes over a fixup' '
        EOF
        git reset --hard n3 &&
        git notes add -m"an earlier note" n2 &&
-       set_fake_editor &&
-       GIT_NOTES_REWRITE_MODE=concatenate FAKE_LINES="1 f 2" git rebase -i n1 &&
+       (
+               set_fake_editor &&
+               GIT_NOTES_REWRITE_MODE=concatenate FAKE_LINES="1 f 2" \
+                       git rebase -i n1
+       ) &&
        git notes show > output &&
        test_cmp expect output
 '
@@ -811,8 +865,10 @@ test_expect_success 'rebase while detaching HEAD' '
        git symbolic-ref HEAD &&
        grandparent=$(git rev-parse HEAD~2) &&
        test_tick &&
-       set_fake_editor &&
-       FAKE_LINES="2 1" git rebase -i HEAD~2 HEAD^0 &&
+       (
+               set_fake_editor &&
+               FAKE_LINES="2 1" git rebase -i HEAD~2 HEAD^0
+       ) &&
        test $grandparent = $(git rev-parse HEAD~2) &&
        test_must_fail git symbolic-ref HEAD
 '
@@ -821,7 +877,6 @@ test_tick # Ensure that the rebased commits get a different timestamp.
 test_expect_success 'always cherry-pick with --no-ff' '
        git checkout no-ff-branch &&
        git tag original-no-ff-branch &&
-       set_fake_editor &&
        git rebase -i --no-ff A &&
        for p in 0 1 2
        do
@@ -853,8 +908,10 @@ test_expect_success 'set up commits with funny messages' '
 test_expect_success 'rebase-i history with funny messages' '
        git rev-list A..funny >expect &&
        test_tick &&
-       set_fake_editor &&
-       FAKE_LINES="1 2 3 4" git rebase -i A &&
+       (
+               set_fake_editor &&
+               FAKE_LINES="1 2 3 4" git rebase -i A
+       ) &&
        git rev-list A.. >actual &&
        test_cmp expect actual
 '
@@ -868,9 +925,9 @@ test_expect_success 'prepare for rebase -i --exec' '
 '
 
 test_expect_success 'running "git rebase -i --exec git show HEAD"' '
-       set_fake_editor &&
-       git rebase -i --exec "git show HEAD" HEAD~2 >actual &&
        (
+               set_fake_editor &&
+               git rebase -i --exec "git show HEAD" HEAD~2 >actual &&
                FAKE_LINES="1 exec_git_show_HEAD 2 exec_git_show_HEAD" &&
                export FAKE_LINES &&
                git rebase -i HEAD~2 >expect
@@ -881,9 +938,9 @@ test_expect_success 'running "git rebase -i --exec git show HEAD"' '
 
 test_expect_success 'running "git rebase --exec git show HEAD -i"' '
        git reset --hard execute &&
-       set_fake_editor &&
-       git rebase --exec "git show HEAD" -i HEAD~2 >actual &&
        (
+               set_fake_editor &&
+               git rebase --exec "git show HEAD" -i HEAD~2 >actual &&
                FAKE_LINES="1 exec_git_show_HEAD 2 exec_git_show_HEAD" &&
                export FAKE_LINES &&
                git rebase -i HEAD~2 >expect
@@ -894,9 +951,9 @@ test_expect_success 'running "git rebase --exec git show HEAD -i"' '
 
 test_expect_success 'running "git rebase -ix git show HEAD"' '
        git reset --hard execute &&
-       set_fake_editor &&
-       git rebase -ix "git show HEAD" HEAD~2 >actual &&
        (
+               set_fake_editor &&
+               git rebase -ix "git show HEAD" HEAD~2 >actual &&
                FAKE_LINES="1 exec_git_show_HEAD 2 exec_git_show_HEAD" &&
                export FAKE_LINES &&
                git rebase -i HEAD~2 >expect
@@ -908,9 +965,9 @@ test_expect_success 'running "git rebase -ix git show HEAD"' '
 
 test_expect_success 'rebase -ix with several <CMD>' '
        git reset --hard execute &&
-       set_fake_editor &&
-       git rebase -ix "git show HEAD; pwd" HEAD~2 >actual &&
        (
+               set_fake_editor &&
+               git rebase -ix "git show HEAD; pwd" HEAD~2 >actual &&
                FAKE_LINES="1 exec_git_show_HEAD;_pwd 2 exec_git_show_HEAD;_pwd" &&
                export FAKE_LINES &&
                git rebase -i HEAD~2 >expect
@@ -921,9 +978,9 @@ test_expect_success 'rebase -ix with several <CMD>' '
 
 test_expect_success 'rebase -ix with several instances of --exec' '
        git reset --hard execute &&
-       set_fake_editor &&
-       git rebase -i --exec "git show HEAD" --exec "pwd" HEAD~2 >actual &&
        (
+               set_fake_editor &&
+               git rebase -i --exec "git show HEAD" --exec "pwd" HEAD~2 >actual &&
                FAKE_LINES="1 exec_git_show_HEAD exec_pwd 2
                                exec_git_show_HEAD exec_pwd" &&
                export FAKE_LINES &&
@@ -942,13 +999,11 @@ test_expect_success C_LOCALE_OUTPUT 'rebase -ix with --autosquash' '
        echo bis >bis.txt &&
        git add bis.txt &&
        git commit -m "fixup! two_exec" &&
-       set_fake_editor &&
-       (
-               git checkout -b autosquash_actual &&
-               git rebase -i --exec "git show HEAD" --autosquash HEAD~4 >actual
-       ) &&
+       git checkout -b autosquash_actual &&
+       git rebase -i --exec "git show HEAD" --autosquash HEAD~4 >actual &&
        git checkout autosquash &&
        (
+               set_fake_editor &&
                git checkout -b autosquash_expected &&
                FAKE_LINES="1 fixup 3 fixup 4 exec_git_show_HEAD 2 exec_git_show_HEAD" &&
                export FAKE_LINES &&
@@ -969,7 +1024,6 @@ test_expect_success 'rebase --exec works without -i ' '
 
 test_expect_success 'rebase -i --exec without <CMD>' '
        git reset --hard execute &&
-       set_fake_editor &&
        test_must_fail git rebase -i --exec 2>actual &&
        test_i18ngrep "requires a value" actual &&
        git checkout master
@@ -977,8 +1031,10 @@ test_expect_success 'rebase -i --exec without <CMD>' '
 
 test_expect_success 'rebase -i --root re-order and drop commits' '
        git checkout E &&
-       set_fake_editor &&
-       FAKE_LINES="3 1 2 5" git rebase -i --root &&
+       (
+               set_fake_editor &&
+               FAKE_LINES="3 1 2 5" git rebase -i --root
+       ) &&
        test E = $(git cat-file commit HEAD | sed -ne \$p) &&
        test B = $(git cat-file commit HEAD^ | sed -ne \$p) &&
        test A = $(git cat-file commit HEAD^^ | sed -ne \$p) &&
@@ -991,24 +1047,30 @@ test_expect_success 'rebase -i --root retain root commit author and message' '
        echo B >file7 &&
        git add file7 &&
        GIT_AUTHOR_NAME="Twerp Snog" git commit -m "different author" &&
-       set_fake_editor &&
-       FAKE_LINES="2" git rebase -i --root &&
+       (
+               set_fake_editor &&
+               FAKE_LINES="2" git rebase -i --root
+       ) &&
        git cat-file commit HEAD | grep -q "^author Twerp Snog" &&
        git cat-file commit HEAD | grep -q "^different author$"
 '
 
 test_expect_success 'rebase -i --root temporary sentinel commit' '
        git checkout B &&
-       set_fake_editor &&
-       test_must_fail env FAKE_LINES="2" git rebase -i --root &&
+       (
+               set_fake_editor &&
+               test_must_fail env FAKE_LINES="2" git rebase -i --root
+       ) &&
        git cat-file commit HEAD | grep "^tree $EMPTY_TREE" &&
        git rebase --abort
 '
 
 test_expect_success 'rebase -i --root fixup root commit' '
        git checkout B &&
-       set_fake_editor &&
-       FAKE_LINES="1 fixup 2" git rebase -i --root &&
+       (
+               set_fake_editor &&
+               FAKE_LINES="1 fixup 2" git rebase -i --root
+       ) &&
        test A = $(git cat-file commit HEAD | sed -ne \$p) &&
        test B = $(git show HEAD:file1) &&
        test 0 = $(git cat-file commit HEAD | grep -c ^parent\ )
@@ -1017,9 +1079,11 @@ test_expect_success 'rebase -i --root fixup root commit' '
 test_expect_success 'rebase -i --root reword original root commit' '
        test_when_finished "test_might_fail git rebase --abort" &&
        git checkout -b reword-original-root-branch master &&
-       set_fake_editor &&
-       FAKE_LINES="reword 1 2" FAKE_COMMIT_MESSAGE="A changed" \
-       git rebase -i --root &&
+       (
+               set_fake_editor &&
+               FAKE_LINES="reword 1 2" FAKE_COMMIT_MESSAGE="A changed" \
+                       git rebase -i --root
+       ) &&
        git show HEAD^ | grep "A changed" &&
        test -z "$(git show -s --format=%p HEAD^)"
 '
@@ -1027,9 +1091,11 @@ test_expect_success 'rebase -i --root reword original root commit' '
 test_expect_success 'rebase -i --root reword new root commit' '
        test_when_finished "test_might_fail git rebase --abort" &&
        git checkout -b reword-now-root-branch master &&
-       set_fake_editor &&
-       FAKE_LINES="reword 3 1" FAKE_COMMIT_MESSAGE="C changed" \
-       git rebase -i --root &&
+       (
+               set_fake_editor &&
+               FAKE_LINES="reword 3 1" FAKE_COMMIT_MESSAGE="C changed" \
+               git rebase -i --root
+       ) &&
        git show HEAD^ | grep "C changed" &&
        test -z "$(git show -s --format=%p HEAD^)"
 '
@@ -1041,8 +1107,10 @@ test_expect_success 'rebase -i --root when root has untracked file conflict' '
        git rm file1 &&
        git commit -m "remove file 1 add file 2" &&
        echo z >file1 &&
-       set_fake_editor &&
-       test_must_fail env FAKE_LINES="1 2" git rebase -i --root &&
+       (
+               set_fake_editor &&
+               test_must_fail env FAKE_LINES="1 2" git rebase -i --root
+       ) &&
        rm file1 &&
        git rebase --continue &&
        test "$(git log -1 --format=%B)" = "remove file 1 add file 2" &&
@@ -1052,11 +1120,13 @@ test_expect_success 'rebase -i --root when root has untracked file conflict' '
 test_expect_success 'rebase -i --root reword root when root has untracked file conflict' '
        test_when_finished "reset_rebase" &&
        echo z>file1 &&
-       set_fake_editor &&
-       test_must_fail env FAKE_LINES="reword 1 2" \
-               FAKE_COMMIT_MESSAGE="Modified A" git rebase -i --root &&
-       rm file1 &&
-       FAKE_COMMIT_MESSAGE="Reworded A" git rebase --continue &&
+       (
+               set_fake_editor &&
+               test_must_fail env FAKE_LINES="reword 1 2" \
+                       FAKE_COMMIT_MESSAGE="Modified A" git rebase -i --root &&
+               rm file1 &&
+               FAKE_COMMIT_MESSAGE="Reworded A" git rebase --continue
+       ) &&
        test "$(git log -1 --format=%B HEAD^)" = "Reworded A" &&
        test "$(git rev-list --count HEAD)" = 2
 '
@@ -1065,19 +1135,23 @@ test_expect_success C_LOCALE_OUTPUT 'rebase --edit-todo does not work on non-int
        git checkout reword-original-root-branch &&
        git reset --hard &&
        git checkout conflict-branch &&
-       set_fake_editor &&
-       test_must_fail git rebase -f --onto HEAD~2 HEAD~ &&
-       test_must_fail git rebase --edit-todo &&
+       (
+               set_fake_editor &&
+               test_must_fail git rebase -f --onto HEAD~2 HEAD~ &&
+               test_must_fail git rebase --edit-todo
+       ) &&
        git rebase --abort
 '
 
 test_expect_success 'rebase --edit-todo can be used to modify todo' '
        git reset --hard &&
        git checkout no-conflict-branch^0 &&
-       set_fake_editor &&
-       FAKE_LINES="edit 1 2 3" git rebase -i HEAD~3 &&
-       FAKE_LINES="2 1" git rebase --edit-todo &&
-       git rebase --continue &&
+       (
+               set_fake_editor &&
+               FAKE_LINES="edit 1 2 3" git rebase -i HEAD~3 &&
+               FAKE_LINES="2 1" git rebase --edit-todo &&
+               git rebase --continue
+       ) &&
        test M = $(git cat-file commit HEAD^ | sed -ne \$p) &&
        test L = $(git cat-file commit HEAD | sed -ne \$p)
 '
@@ -1085,7 +1159,6 @@ test_expect_success 'rebase --edit-todo can be used to modify todo' '
 test_expect_success 'rebase -i produces readable reflog' '
        git reset --hard &&
        git branch -f branch-reflog-test H &&
-       set_fake_editor &&
        git rebase -i --onto I F branch-reflog-test &&
        cat >expect <<-\EOF &&
        rebase -i (finish): returning to refs/heads/branch-reflog-test
@@ -1106,8 +1179,10 @@ test_expect_success 'rebase -i respects core.commentchar' '
        sed -e "2,\$s/^/\\\\/" "$1" >"$1.tmp" &&
        mv "$1.tmp" "$1"
        EOF
-       test_set_editor "$(pwd)/remove-all-but-first.sh" &&
-       git rebase -i B &&
+       (
+               test_set_editor "$(pwd)/remove-all-but-first.sh" &&
+               git rebase -i B
+       ) &&
        test B = $(git cat-file commit HEAD^ | sed -ne \$p)
 '
 
@@ -1116,9 +1191,11 @@ test_expect_success 'rebase -i respects core.commentchar=auto' '
        write_script copy-edit-script.sh <<-\EOF &&
        cp "$1" edit-script
        EOF
-       test_set_editor "$(pwd)/copy-edit-script.sh" &&
        test_when_finished "git rebase --abort || :" &&
-       git rebase -i HEAD^ &&
+       (
+               test_set_editor "$(pwd)/copy-edit-script.sh" &&
+               git rebase -i HEAD^
+       ) &&
        test -z "$(grep -ve "^#" -e "^\$" -e "^pick" edit-script)"
 '
 
@@ -1153,8 +1230,11 @@ test_expect_success 'interrupted rebase -i with --strategy and -X' '
        echo five >conflict &&
        echo Z >file1 &&
        git commit -a -m "one file conflict" &&
-       set_fake_editor &&
-       FAKE_LINES="edit 1 2" git rebase -i --strategy=recursive -Xours conflict-branch &&
+       (
+               set_fake_editor &&
+               FAKE_LINES="edit 1 2" git rebase -i --strategy=recursive \
+                       -Xours conflict-branch
+       ) &&
        git rebase --continue &&
        test $(git show conflict-branch:conflict) = $(cat conflict) &&
        test $(cat file1) = Z
@@ -1195,8 +1275,10 @@ test_expect_success SHA1 'short SHA-1 collide' '
 
 test_expect_success 'respect core.abbrev' '
        git config core.abbrev 12 &&
-       set_cat_todo_editor &&
-       test_must_fail git rebase -i HEAD~4 >todo-list &&
+       (
+               set_cat_todo_editor &&
+               test_must_fail git rebase -i HEAD~4 >todo-list
+       ) &&
        test 4 = $(grep -c "pick [0-9a-f]\{12,\}" todo-list)
 '
 
@@ -1204,16 +1286,20 @@ test_expect_success 'todo count' '
        write_script dump-raw.sh <<-\EOF &&
                cat "$1"
        EOF
-       test_set_editor "$(pwd)/dump-raw.sh" &&
-       git rebase -i HEAD~4 >actual &&
+       (
+               test_set_editor "$(pwd)/dump-raw.sh" &&
+               git rebase -i HEAD~4 >actual
+       ) &&
        test_i18ngrep "^# Rebase ..* onto ..* ([0-9]" actual
 '
 
 test_expect_success 'rebase -i commits that overwrite untracked files (pick)' '
        git checkout --force branch2 &&
        git clean -f &&
-       set_fake_editor &&
-       FAKE_LINES="edit 1 2" git rebase -i A &&
+       (
+               set_fake_editor &&
+               FAKE_LINES="edit 1 2" git rebase -i A
+       ) &&
        test_cmp_rev HEAD F &&
        test_path_is_missing file6 &&
        >file6 &&
@@ -1228,8 +1314,10 @@ test_expect_success 'rebase -i commits that overwrite untracked files (squash)'
        git checkout --force branch2 &&
        git clean -f &&
        git tag original-branch2 &&
-       set_fake_editor &&
-       FAKE_LINES="edit 1 squash 2" git rebase -i A &&
+       (
+               set_fake_editor &&
+               FAKE_LINES="edit 1 squash 2" git rebase -i A
+       ) &&
        test_cmp_rev HEAD F &&
        test_path_is_missing file6 &&
        >file6 &&
@@ -1244,8 +1332,10 @@ test_expect_success 'rebase -i commits that overwrite untracked files (squash)'
 test_expect_success 'rebase -i commits that overwrite untracked files (no ff)' '
        git checkout --force branch2 &&
        git clean -f &&
-       set_fake_editor &&
-       FAKE_LINES="edit 1 2" git rebase -i --no-ff A &&
+       (
+               set_fake_editor &&
+               FAKE_LINES="edit 1 2" git rebase -i --no-ff A
+       ) &&
        test $(git cat-file commit HEAD | sed -ne \$p) = F &&
        test_path_is_missing file6 &&
        >file6 &&
@@ -1268,8 +1358,10 @@ test_expect_success 'rebase --continue removes CHERRY_PICK_HEAD' '
        git tag seq-onto &&
        git reset --hard HEAD~2 &&
        git cherry-pick seq-onto &&
-       set_fake_editor &&
-       test_must_fail env FAKE_LINES= git rebase -i seq-onto &&
+       (
+               set_fake_editor &&
+               test_must_fail env FAKE_LINES= git rebase -i seq-onto
+       ) &&
        test -d .git/rebase-merge &&
        git rebase --continue &&
        git diff --exit-code seq-onto &&
@@ -1288,8 +1380,10 @@ rebase_setup_and_clean () {
 
 test_expect_success 'drop' '
        rebase_setup_and_clean drop-test &&
-       set_fake_editor &&
-       FAKE_LINES="1 drop 2 3 d 4 5" git rebase -i --root &&
+       (
+               set_fake_editor &&
+               FAKE_LINES="1 drop 2 3 d 4 5" git rebase -i --root
+       ) &&
        test E = $(git cat-file commit HEAD | sed -ne \$p) &&
        test C = $(git cat-file commit HEAD^ | sed -ne \$p) &&
        test A = $(git cat-file commit HEAD^^ | sed -ne \$p)
@@ -1298,9 +1392,10 @@ test_expect_success 'drop' '
 test_expect_success 'rebase -i respects rebase.missingCommitsCheck = ignore' '
        test_config rebase.missingCommitsCheck ignore &&
        rebase_setup_and_clean missing-commit &&
-       set_fake_editor &&
-       FAKE_LINES="1 2 3 4" \
-               git rebase -i --root 2>actual &&
+       (
+               set_fake_editor &&
+               FAKE_LINES="1 2 3 4" git rebase -i --root 2>actual
+       ) &&
        test D = $(git cat-file commit HEAD | sed -ne \$p) &&
        test_i18ngrep \
                "Successfully rebased and updated refs/heads/missing-commit" \
@@ -1316,9 +1411,10 @@ test_expect_success 'rebase -i respects rebase.missingCommitsCheck = warn' '
        EOF
        test_config rebase.missingCommitsCheck warn &&
        rebase_setup_and_clean missing-commit &&
-       set_fake_editor &&
-       FAKE_LINES="1 2 3 4" \
-               git rebase -i --root 2>actual.2 &&
+       (
+               set_fake_editor &&
+               FAKE_LINES="1 2 3 4" git rebase -i --root 2>actual.2
+       ) &&
        head -n4 actual.2 >actual &&
        test_i18ncmp expect actual &&
        test D = $(git cat-file commit HEAD | sed -ne \$p)
@@ -1340,14 +1436,15 @@ test_expect_success 'rebase -i respects rebase.missingCommitsCheck = error' '
        EOF
        test_config rebase.missingCommitsCheck error &&
        rebase_setup_and_clean missing-commit &&
-       set_fake_editor &&
-       test_must_fail env FAKE_LINES="1 2 4" \
-               git rebase -i --root 2>actual &&
-       test_i18ncmp expect actual &&
-       cp .git/rebase-merge/git-rebase-todo.backup \
-               .git/rebase-merge/git-rebase-todo &&
-       FAKE_LINES="1 2 drop 3 4 drop 5" \
-               git rebase --edit-todo &&
+       (
+               set_fake_editor &&
+               test_must_fail env FAKE_LINES="1 2 4" \
+                       git rebase -i --root 2>actual &&
+               test_i18ncmp expect actual &&
+               cp .git/rebase-merge/git-rebase-todo.backup \
+                       .git/rebase-merge/git-rebase-todo &&
+               FAKE_LINES="1 2 drop 3 4 drop 5" git rebase --edit-todo
+       ) &&
        git rebase --continue &&
        test D = $(git cat-file commit HEAD | sed -ne \$p) &&
        test B = $(git cat-file commit HEAD^ | sed -ne \$p)
@@ -1368,21 +1465,27 @@ test_expect_success 'respects rebase.abbreviateCommands with fixup, squash and e
        x git show HEAD
        EOF
        git checkout abbrevcmd &&
-       set_cat_todo_editor &&
        test_config rebase.abbreviateCommands true &&
-       test_must_fail git rebase -i --exec "git show HEAD" \
-               --autosquash master >actual &&
+       (
+               set_cat_todo_editor &&
+               test_must_fail git rebase -i --exec "git show HEAD" \
+                       --autosquash master >actual
+       ) &&
        test_cmp expected actual
 '
 
 test_expect_success 'static check of bad command' '
        rebase_setup_and_clean bad-cmd &&
-       set_fake_editor &&
-       test_must_fail env FAKE_LINES="1 2 3 bad 4 5" \
+       (
+               set_fake_editor &&
+               test_must_fail env FAKE_LINES="1 2 3 bad 4 5" \
                git rebase -i --root 2>actual &&
-       test_i18ngrep "badcmd $(git rev-list --oneline -1 master~1)" actual &&
-       test_i18ngrep "You can fix this with .git rebase --edit-todo.." actual &&
-       FAKE_LINES="1 2 3 drop 4 5" git rebase --edit-todo &&
+               test_i18ngrep "badcmd $(git rev-list --oneline -1 master~1)" \
+                               actual &&
+               test_i18ngrep "You can fix this with .git rebase --edit-todo.." \
+                               actual &&
+               FAKE_LINES="1 2 3 drop 4 5" git rebase --edit-todo
+       ) &&
        git rebase --continue &&
        test E = $(git cat-file commit HEAD | sed -ne \$p) &&
        test C = $(git cat-file commit HEAD^ | sed -ne \$p)
@@ -1398,19 +1501,24 @@ test_expect_success 'tabs and spaces are accepted in the todolist' '
        ) >"$1.new"
        mv "$1.new" "$1"
        EOF
-       test_set_editor "$(pwd)/add-indent.sh" &&
-       git rebase -i HEAD^^^ &&
+       (
+               test_set_editor "$(pwd)/add-indent.sh" &&
+               git rebase -i HEAD^^^
+       ) &&
        test E = $(git cat-file commit HEAD | sed -ne \$p)
 '
 
 test_expect_success 'static check of bad SHA-1' '
        rebase_setup_and_clean bad-sha &&
-       set_fake_editor &&
-       test_must_fail env FAKE_LINES="1 2 edit fakesha 3 4 5 #" \
-               git rebase -i --root 2>actual &&
-       test_i18ngrep "edit XXXXXXX False commit" actual &&
-       test_i18ngrep "You can fix this with .git rebase --edit-todo.." actual &&
-       FAKE_LINES="1 2 4 5 6" git rebase --edit-todo &&
+       (
+               set_fake_editor &&
+               test_must_fail env FAKE_LINES="1 2 edit fakesha 3 4 5 #" \
+                       git rebase -i --root 2>actual &&
+                       test_i18ngrep "edit XXXXXXX False commit" actual &&
+                       test_i18ngrep "You can fix this with .git rebase --edit-todo.." \
+                                       actual &&
+               FAKE_LINES="1 2 4 5 6" git rebase --edit-todo
+       ) &&
        git rebase --continue &&
        test E = $(git cat-file commit HEAD | sed -ne \$p)
 '
@@ -1429,39 +1537,71 @@ test_expect_success 'editor saves as CR/LF' '
 
 test_expect_success 'rebase -i --gpg-sign=<key-id>' '
        test_when_finished "test_might_fail git rebase --abort" &&
-       set_fake_editor &&
-       FAKE_LINES="edit 1" git rebase -i --gpg-sign="\"S I Gner\"" HEAD^ \
-               >out 2>err &&
+       (
+               set_fake_editor &&
+               FAKE_LINES="edit 1" git rebase -i --gpg-sign="\"S I Gner\"" \
+                       HEAD^ >out 2>err
+       ) &&
        test_i18ngrep "$SQ-S\"S I Gner\"$SQ" err
 '
 
 test_expect_success 'rebase -i --gpg-sign=<key-id> overrides commit.gpgSign' '
        test_when_finished "test_might_fail git rebase --abort" &&
        test_config commit.gpgsign true &&
-       set_fake_editor &&
-       FAKE_LINES="edit 1" git rebase -i --gpg-sign="\"S I Gner\"" HEAD^ \
-               >out 2>err &&
+       (
+               set_fake_editor &&
+               FAKE_LINES="edit 1" git rebase -i --gpg-sign="\"S I Gner\"" \
+                       HEAD^ >out 2>err
+       ) &&
        test_i18ngrep "$SQ-S\"S I Gner\"$SQ" err
 '
 
 test_expect_success 'valid author header after --root swap' '
        rebase_setup_and_clean author-header no-conflict-branch &&
-       set_fake_editor &&
        git commit --amend --author="Au ${SQ}thor <author@example.com>" --no-edit &&
        git cat-file commit HEAD | grep ^author >expected &&
-       FAKE_LINES="5 1" git rebase -i --root &&
+       (
+               set_fake_editor &&
+               FAKE_LINES="5 1" git rebase -i --root
+       ) &&
        git cat-file commit HEAD^ | grep ^author >actual &&
        test_cmp expected actual
 '
 
 test_expect_success 'valid author header when author contains single quote' '
        rebase_setup_and_clean author-header no-conflict-branch &&
-       set_fake_editor &&
        git commit --amend --author="Au ${SQ}thor <author@example.com>" --no-edit &&
        git cat-file commit HEAD | grep ^author >expected &&
-       FAKE_LINES="2" git rebase -i HEAD~2 &&
+       (
+               set_fake_editor &&
+               FAKE_LINES="2" git rebase -i HEAD~2
+       ) &&
        git cat-file commit HEAD | grep ^author >actual &&
        test_cmp expected actual
 '
 
+test_expect_success 'post-commit hook is called' '
+       test_when_finished "rm -f .git/hooks/post-commit" &&
+       >actual &&
+       mkdir -p .git/hooks &&
+       write_script .git/hooks/post-commit <<-\EOS &&
+       git rev-parse HEAD >>actual
+       EOS
+       (
+               set_fake_editor &&
+               FAKE_LINES="edit 4 1 reword 2 fixup 3" git rebase -i A E &&
+               echo x>file3 &&
+               git add file3 &&
+               FAKE_COMMIT_MESSAGE=edited git rebase --continue
+       ) &&
+       git rev-parse HEAD@{5} HEAD@{4} HEAD@{3} HEAD@{2} HEAD@{1} HEAD \
+               >expect &&
+       test_cmp expect actual
+'
+
+# This must be the last test in this file
+test_expect_success '$EDITOR and friends are unchanged' '
+       test_editor_unchanged
+'
+
 test_done
index 8739cb60a77a61f6a6a2e08ebb4ae64c92bafb67..aaeac6eade8e13f4ebd9ff40346d4a42fe2cd0b1 100755 (executable)
@@ -17,7 +17,7 @@ test_expect_success 'rebase exec modifies rebase-todo' '
        test -e F
 '
 
-test_expect_success SHA1 'loose object cache vs re-reading todo list' '
+test_expect_success 'loose object cache vs re-reading todo list' '
        GIT_REBASE_TODO=.git/rebase-merge/git-rebase-todo &&
        export GIT_REBASE_TODO &&
        write_script append-todo.sh <<-\EOS &&
index 9efcf4808ac92f13272e279fe4d7b660fccf8da3..7ab0152d3e2fbe0a84d7bf462f049e72201a4bfe 100755 (executable)
@@ -408,7 +408,7 @@ test_expect_success 'octopus merges' '
        | | * three
        | * | two
        | |/
-       * | one
+       * / one
        |/
        o before-octopus
        EOF
@@ -468,4 +468,31 @@ test_expect_success '--rebase-merges with strategies' '
        test_cmp expect G.t
 '
 
+test_expect_success '--rebase-merges with commit that can generate bad characters for filename' '
+       git checkout -b colon-in-label E &&
+       git merge -m "colon: this should work" G &&
+       git rebase --rebase-merges --force-rebase E
+'
+
+test_expect_success '--rebase-merges with message matched with onto label' '
+       git checkout -b onto-label E &&
+       git merge -m onto G &&
+       git rebase --rebase-merges --force-rebase E &&
+       test_cmp_graph <<-\EOF
+       *   onto
+       |\
+       | * G
+       | * F
+       * |   E
+       |\ \
+       | * | B
+       * | | D
+       | |/
+       |/|
+       * | C
+       |/
+       * A
+       EOF
+'
+
 test_done
diff --git a/t/t3434-rebase-i18n.sh b/t/t3434-rebase-i18n.sh
new file mode 100755 (executable)
index 0000000..4b5b128
--- /dev/null
@@ -0,0 +1,84 @@
+#!/bin/sh
+#
+# Copyright (c) 2019 Doan Tran Cong Danh
+#
+
+test_description='rebase with changing encoding
+
+Initial setup:
+
+1 - 2              master
+ \
+  3 - 4            first
+   \
+    5 - 6          second
+'
+
+. ./test-lib.sh
+
+compare_msg () {
+       iconv -f "$2" -t "$3" "$TEST_DIRECTORY/t3434/$1" >expect &&
+       git cat-file commit HEAD >raw &&
+       sed "1,/^$/d" raw >actual &&
+       test_cmp expect actual
+}
+
+test_expect_success setup '
+       test_commit one &&
+       git branch first &&
+       test_commit two &&
+       git switch first &&
+       test_commit three &&
+       git branch second &&
+       test_commit four &&
+       git switch second &&
+       test_commit five &&
+       test_commit six
+'
+
+test_expect_success 'rebase --rebase-merges update encoding eucJP to UTF-8' '
+       git switch -c merge-eucJP-UTF-8 first &&
+       git config i18n.commitencoding eucJP &&
+       git merge -F "$TEST_DIRECTORY/t3434/eucJP.txt" second &&
+       git config i18n.commitencoding UTF-8 &&
+       git rebase --rebase-merges master &&
+       compare_msg eucJP.txt eucJP UTF-8
+'
+
+test_expect_failure 'rebase --rebase-merges update encoding eucJP to ISO-2022-JP' '
+       git switch -c merge-eucJP-ISO-2022-JP first &&
+       git config i18n.commitencoding eucJP &&
+       git merge -F "$TEST_DIRECTORY/t3434/eucJP.txt" second &&
+       git config i18n.commitencoding ISO-2022-JP &&
+       git rebase --rebase-merges master &&
+       compare_msg eucJP.txt eucJP ISO-2022-JP
+'
+
+test_rebase_continue_update_encode () {
+       old=$1
+       new=$2
+       msgfile=$3
+       test_expect_success "rebase --continue update from $old to $new" '
+               (git rebase --abort || : abort current git-rebase failure) &&
+               git switch -c conflict-$old-$new one &&
+               echo for-conflict >two.t &&
+               git add two.t &&
+               git config i18n.commitencoding $old &&
+               git commit -F "$TEST_DIRECTORY/t3434/$msgfile" &&
+               git config i18n.commitencoding $new &&
+               test_must_fail git rebase -m master &&
+               test -f .git/rebase-merge/message &&
+               git stripspace <.git/rebase-merge/message >two.t &&
+               git add two.t &&
+               git rebase --continue &&
+               compare_msg $msgfile $old $new &&
+               : git-commit assume invalid utf-8 is latin1 &&
+               test_cmp expect two.t
+       '
+}
+
+test_rebase_continue_update_encode ISO-8859-1 UTF-8 ISO8859-1.txt
+test_rebase_continue_update_encode eucJP UTF-8 eucJP.txt
+test_rebase_continue_update_encode eucJP ISO-2022-JP eucJP.txt
+
+test_done
diff --git a/t/t3434/ISO8859-1.txt b/t/t3434/ISO8859-1.txt
new file mode 100644 (file)
index 0000000..7cbef0e
--- /dev/null
@@ -0,0 +1,3 @@
+ÄËÑÏÖ
+
+Ábçdèfg
diff --git a/t/t3434/eucJP.txt b/t/t3434/eucJP.txt
new file mode 100644 (file)
index 0000000..546f2aa
--- /dev/null
@@ -0,0 +1,4 @@
+¤Ï¤ì¤Ò¤Û¤Õ
+
+¤·¤Æ¤¤¤ë¤Î¤¬¡¢¤¤¤ë¤Î¤Ç¡£
+ßÀÉͤۤì¤×¤ê¤Ý¤ì¤Þ¤Ó¤°¤ê¤í¤Ø¡£
index d50e165ca82f99d7e7bcb5814b73e8cc8f160da9..d4f9386621b468ae805febf7382268ed8bf53d8d 100755 (executable)
@@ -647,4 +647,29 @@ test_expect_success 'checkout -p works with pathological context lines' '
        test_write_lines a b a b a a b a b a >expect &&
        test_cmp expect a
 '
+
+test_expect_success 'show help from add--helper' '
+       git reset --hard &&
+       cat >expect <<-EOF &&
+
+       <BOLD>*** Commands ***<RESET>
+         1: <BOLD;BLUE>s<RESET>tatus     2: <BOLD;BLUE>u<RESET>pdate     3: <BOLD;BLUE>r<RESET>evert     4: <BOLD;BLUE>a<RESET>dd untracked
+         5: <BOLD;BLUE>p<RESET>atch      6: <BOLD;BLUE>d<RESET>iff       7: <BOLD;BLUE>q<RESET>uit       8: <BOLD;BLUE>h<RESET>elp
+       <BOLD;BLUE>What now<RESET>> <BOLD;RED>status        - show paths with changes<RESET>
+       <BOLD;RED>update        - add working tree state to the staged set of changes<RESET>
+       <BOLD;RED>revert        - revert staged set of changes back to the HEAD version<RESET>
+       <BOLD;RED>patch         - pick hunks and update selectively<RESET>
+       <BOLD;RED>diff          - view diff between HEAD and index<RESET>
+       <BOLD;RED>add untracked - add contents of untracked files to the staged set of changes<RESET>
+       <BOLD>*** Commands ***<RESET>
+         1: <BOLD;BLUE>s<RESET>tatus     2: <BOLD;BLUE>u<RESET>pdate     3: <BOLD;BLUE>r<RESET>evert     4: <BOLD;BLUE>a<RESET>dd untracked
+         5: <BOLD;BLUE>p<RESET>atch      6: <BOLD;BLUE>d<RESET>iff       7: <BOLD;BLUE>q<RESET>uit       8: <BOLD;BLUE>h<RESET>elp
+       <BOLD;BLUE>What now<RESET>>$SP
+       Bye.
+       EOF
+       test_write_lines h | GIT_PAGER_IN_USE=true TERM=vt100 git add -i >actual.colored &&
+       test_decode_color <actual.colored >actual &&
+       test_i18ncmp expect actual
+'
+
 test_done
index b92ff95977281e7ebb727fe5079e03aa301f4111..d277a9f4b7216431d2e6fbc414b60188b36df12b 100755 (executable)
@@ -204,4 +204,41 @@ test_commit_autosquash_flags eucJP fixup
 
 test_commit_autosquash_flags ISO-2022-JP squash
 
+test_commit_autosquash_multi_encoding () {
+       flag=$1
+       old=$2
+       new=$3
+       msg=$4
+       test_expect_success "commit --$flag into $old from $new" '
+               git checkout -b $flag-$old-$new C0 &&
+               git config i18n.commitencoding $old &&
+               echo $old >>F &&
+               git commit -a -F "$TEST_DIRECTORY"/t3900/$msg &&
+               test_tick &&
+               echo intermediate stuff >>G &&
+               git add G &&
+               git commit -a -m "intermediate commit" &&
+               test_tick &&
+               git config i18n.commitencoding $new &&
+               echo $new-$flag >>F &&
+               git commit -a --$flag HEAD^ &&
+               git rebase --autosquash -i HEAD^^^ &&
+               git rev-list HEAD >actual &&
+               test_line_count = 3 actual &&
+               iconv -f $old -t UTF-8 "$TEST_DIRECTORY"/t3900/$msg >expect &&
+               if test $flag = squash; then
+                       subject="$(head -1 expect)" &&
+                       printf "\nsquash! %s\n" "$subject" >>expect
+               fi &&
+               git cat-file commit HEAD^ >raw &&
+               (sed "1,/^$/d" raw | iconv -f $new -t utf-8) >actual &&
+               test_cmp expect actual
+       '
+}
+
+test_commit_autosquash_multi_encoding fixup UTF-8 ISO-8859-1 1-UTF-8.txt
+test_commit_autosquash_multi_encoding squash ISO-8859-1 UTF-8 ISO8859-1.txt
+test_commit_autosquash_multi_encoding squash eucJP ISO-2022-JP eucJP.txt
+test_commit_autosquash_multi_encoding fixup ISO-2022-JP UTF-8 ISO-2022-JP.txt
+
 test_done
index 580bfbdc23f4924cea78358629e09a974ad6aa6c..ea56e85e70d5d1c2f991689d85a26433674add1a 100755 (executable)
@@ -244,8 +244,11 @@ test_expect_success 'save -q is quiet' '
        test_must_be_empty output.out
 '
 
-test_expect_success 'pop -q is quiet' '
+test_expect_success 'pop -q works and is quiet' '
        git stash pop -q >output.out 2>&1 &&
+       echo bar >expect &&
+       git show :file >actual &&
+       test_cmp expect actual &&
        test_must_be_empty output.out
 '
 
@@ -254,6 +257,8 @@ test_expect_success 'pop -q --index works and is quiet' '
        git add file &&
        git stash save --quiet &&
        git stash pop -q --index >output.out 2>&1 &&
+       git diff-files file2 >file2.diff &&
+       test_must_be_empty file2.diff &&
        test foo = "$(git show :file)" &&
        test_must_be_empty output.out
 '
@@ -1269,4 +1274,15 @@ test_expect_success 'stash apply should succeed with unmodified file' '
        git stash apply
 '
 
+test_expect_success 'stash handles skip-worktree entries nicely' '
+       test_commit A &&
+       echo changed >A.t &&
+       git add A.t &&
+       git update-index --skip-worktree A.t &&
+       rm A.t &&
+       git stash &&
+
+       git rev-parse --verify refs/stash:A.t
+'
+
 test_done
index 29ca76f2fbea9275462339ce2f308b6d6920aac5..f075c7f1f3165adc256e0018d26c34e8f7823b96 100755 (executable)
@@ -277,8 +277,8 @@ test_expect_success 'stash -u -- <ignored> leaves ignored file alone' '
        test_path_is_file ignored.d/bar
 '
 
-test_expect_success 'stash -u -- <non-existant> shows no changes when there are none' '
-       git stash push -u -- non-existant >actual &&
+test_expect_success 'stash -u -- <non-existent> shows no changes when there are none' '
+       git stash push -u -- non-existent >actual &&
        echo "No local changes to save" >expect &&
        test_i18ncmp expect actual
 '
index 281f8fad0c71687aecec7479328ce8609cda5c83..e5ca359edfa6087f5833c2e77324b7f9960d06cb 100755 (executable)
@@ -17,11 +17,15 @@ test_expect_success \
     'echo frotz >file0 &&
      mkdir path1 &&
      echo rezrov >path1/file1 &&
+     before0=$(git hash-object file0) &&
+     before1=$(git hash-object path1/file1) &&
      git update-index --add file0 path1/file1 &&
      tree=$(git write-tree) &&
      echo "$tree" &&
      echo nitfol >file0 &&
      echo yomin >path1/file1 &&
+     after0=$(git hash-object file0) &&
+     after1=$(git hash-object path1/file1) &&
      git update-index file0 path1/file1'
 
 cat >expected <<\EOF
@@ -31,32 +35,32 @@ test_expect_success \
     'git diff-index --cached $tree -- path >current &&
      compare_diff_raw current expected'
 
-cat >expected <<\EOF
-:100644 100644 766498d93a4b06057a8e49d23f4068f1170ff38f 0a41e115ab61be0328a19b29f18cdcb49338d516 M     path1/file1
+cat >expected <<EOF
+:100644 100644 $before1 $after1 M      path1/file1
 EOF
 test_expect_success \
     'limit to path1 should show path1/file1' \
     'git diff-index --cached $tree -- path1 >current &&
      compare_diff_raw current expected'
 
-cat >expected <<\EOF
-:100644 100644 766498d93a4b06057a8e49d23f4068f1170ff38f 0a41e115ab61be0328a19b29f18cdcb49338d516 M     path1/file1
+cat >expected <<EOF
+:100644 100644 $before1 $after1 M      path1/file1
 EOF
 test_expect_success \
     'limit to path1/ should show path1/file1' \
     'git diff-index --cached $tree -- path1/ >current &&
      compare_diff_raw current expected'
 
-cat >expected <<\EOF
-:100644 100644 766498d93a4b06057a8e49d23f4068f1170ff38f 0a41e115ab61be0328a19b29f18cdcb49338d516 M     path1/file1
+cat >expected <<EOF
+:100644 100644 $before1 $after1 M      path1/file1
 EOF
 test_expect_success \
     '"*file1" should show path1/file1' \
     'git diff-index --cached $tree -- "*file1" >current &&
      compare_diff_raw current expected'
 
-cat >expected <<\EOF
-:100644 100644 8e4020bb5a8d8c873b25de15933e75cc0fc275df dca6b92303befc93086aa025d90a5facd7eb2812 M     file0
+cat >expected <<EOF
+:100644 100644 $before0 $after0 M      file0
 EOF
 test_expect_success \
     'limit to file0 should show file0' \
index 5ae19b987d65d081b78c10f5eefd8aaf930c97a8..717034bb50b57f3edc5d19989f0b178e912e08e6 100755 (executable)
@@ -9,11 +9,24 @@ test_description='Test diff of symlinks.
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/diff-lib.sh
 
+# Print the short OID of a symlink with the given name.
+symlink_oid () {
+       local oid=$(printf "%s" "$1" | git hash-object --stdin) &&
+       git rev-parse --short "$oid"
+}
+
+# Print the short OID of the given file.
+short_oid () {
+       local oid=$(git hash-object "$1") &&
+       git rev-parse --short "$oid"
+}
+
 test_expect_success 'diff new symlink and file' '
-       cat >expected <<-\EOF &&
+       symlink=$(symlink_oid xyzzy) &&
+       cat >expected <<-EOF &&
        diff --git a/frotz b/frotz
        new file mode 120000
-       index 0000000..7c465af
+       index 0000000..$symlink
        --- /dev/null
        +++ b/frotz
        @@ -0,0 +1 @@
@@ -21,7 +34,7 @@ test_expect_success 'diff new symlink and file' '
        \ No newline at end of file
        diff --git a/nitfol b/nitfol
        new file mode 100644
-       index 0000000..7c465af
+       index 0000000..$symlink
        --- /dev/null
        +++ b/nitfol
        @@ -0,0 +1 @@
@@ -46,10 +59,10 @@ test_expect_success 'diff unchanged symlink and file'  '
 '
 
 test_expect_success 'diff removed symlink and file' '
-       cat >expected <<-\EOF &&
+       cat >expected <<-EOF &&
        diff --git a/frotz b/frotz
        deleted file mode 120000
-       index 7c465af..0000000
+       index $symlink..0000000
        --- a/frotz
        +++ /dev/null
        @@ -1 +0,0 @@
@@ -57,7 +70,7 @@ test_expect_success 'diff removed symlink and file' '
        \ No newline at end of file
        diff --git a/nitfol b/nitfol
        deleted file mode 100644
-       index 7c465af..0000000
+       index $symlink..0000000
        --- a/nitfol
        +++ /dev/null
        @@ -1 +0,0 @@
@@ -90,9 +103,10 @@ test_expect_success 'diff identical, but newly created symlink and file' '
 '
 
 test_expect_success 'diff different symlink and file' '
-       cat >expected <<-\EOF &&
+       new=$(symlink_oid yxyyz) &&
+       cat >expected <<-EOF &&
        diff --git a/frotz b/frotz
-       index 7c465af..df1db54 120000
+       index $symlink..$new 120000
        --- a/frotz
        +++ b/frotz
        @@ -1 +1 @@
@@ -101,7 +115,7 @@ test_expect_success 'diff different symlink and file' '
        +yxyyz
        \ No newline at end of file
        diff --git a/nitfol b/nitfol
-       index 7c465af..df1db54 100644
+       index $symlink..$new 100644
        --- a/nitfol
        +++ b/nitfol
        @@ -1 +1 @@
@@ -137,14 +151,16 @@ test_expect_success SYMLINKS 'setup symlinks with attributes' '
 '
 
 test_expect_success SYMLINKS 'symlinks do not respect userdiff config by path' '
-       cat >expect <<-\EOF &&
+       file=$(short_oid file.bin) &&
+       link=$(symlink_oid file.bin) &&
+       cat >expect <<-EOF &&
        diff --git a/file.bin b/file.bin
        new file mode 100644
-       index 0000000..d95f3ad
+       index 0000000..$file
        Binary files /dev/null and b/file.bin differ
        diff --git a/link.bin b/link.bin
        new file mode 120000
-       index 0000000..dce41ec
+       index 0000000..$link
        --- /dev/null
        +++ b/link.bin
        @@ -0,0 +1 @@
index b8969998b5090984e608391167531cfc771f0ad3..69267b16f0baccc642ed3c7a4692b1a0c415d478 100755 (executable)
@@ -1517,6 +1517,178 @@ test_expect_success 'format patch ignores color.ui' '
        test_cmp expect actual
 '
 
+test_expect_success 'cover letter with invalid --cover-from-description and config' '
+       test_config branch.rebuild-1.description "config subject
+
+body" &&
+       test_must_fail git format-patch --cover-letter --cover-from-description garbage master &&
+       test_config format.coverFromDescription garbage &&
+       test_must_fail git format-patch --cover-letter master
+'
+
+test_expect_success 'cover letter with format.coverFromDescription = default' '
+       test_config branch.rebuild-1.description "config subject
+
+body" &&
+       test_config format.coverFromDescription default &&
+       git checkout rebuild-1 &&
+       git format-patch --stdout --cover-letter master >actual &&
+       grep "^Subject: \[PATCH 0/2\] \*\*\* SUBJECT HERE \*\*\*$" actual &&
+       ! grep "^\*\*\* BLURB HERE \*\*\*$" actual &&
+       grep "^config subject$" actual &&
+       grep "^body$" actual
+'
+
+test_expect_success 'cover letter with --cover-from-description default' '
+       test_config branch.rebuild-1.description "config subject
+
+body" &&
+       git checkout rebuild-1 &&
+       git format-patch --stdout --cover-letter --cover-from-description default master >actual &&
+       grep "^Subject: \[PATCH 0/2\] \*\*\* SUBJECT HERE \*\*\*$" actual &&
+       ! grep "^\*\*\* BLURB HERE \*\*\*$" actual &&
+       grep "^config subject$" actual &&
+       grep "^body$" actual
+'
+
+test_expect_success 'cover letter with format.coverFromDescription = none' '
+       test_config branch.rebuild-1.description "config subject
+
+body" &&
+       test_config format.coverFromDescription none &&
+       git checkout rebuild-1 &&
+       git format-patch --stdout --cover-letter master >actual &&
+       grep "^Subject: \[PATCH 0/2\] \*\*\* SUBJECT HERE \*\*\*$" actual &&
+       grep "^\*\*\* BLURB HERE \*\*\*$" actual &&
+       ! grep "^config subject$" actual &&
+       ! grep "^body$" actual
+'
+
+test_expect_success 'cover letter with --cover-from-description none' '
+       test_config branch.rebuild-1.description "config subject
+
+body" &&
+       git checkout rebuild-1 &&
+       git format-patch --stdout --cover-letter --cover-from-description none master >actual &&
+       grep "^Subject: \[PATCH 0/2\] \*\*\* SUBJECT HERE \*\*\*$" actual &&
+       grep "^\*\*\* BLURB HERE \*\*\*$" actual &&
+       ! grep "^config subject$" actual &&
+       ! grep "^body$" actual
+'
+
+test_expect_success 'cover letter with format.coverFromDescription = message' '
+       test_config branch.rebuild-1.description "config subject
+
+body" &&
+       test_config format.coverFromDescription message &&
+       git checkout rebuild-1 &&
+       git format-patch --stdout --cover-letter master >actual &&
+       grep "^Subject: \[PATCH 0/2\] \*\*\* SUBJECT HERE \*\*\*$" actual &&
+       ! grep "^\*\*\* BLURB HERE \*\*\*$" actual &&
+       grep "^config subject$" actual &&
+       grep "^body$" actual
+'
+
+test_expect_success 'cover letter with --cover-from-description message' '
+       test_config branch.rebuild-1.description "config subject
+
+body" &&
+       git checkout rebuild-1 &&
+       git format-patch --stdout --cover-letter --cover-from-description message master >actual &&
+       grep "^Subject: \[PATCH 0/2\] \*\*\* SUBJECT HERE \*\*\*$" actual &&
+       ! grep "^\*\*\* BLURB HERE \*\*\*$" actual &&
+       grep "^config subject$" actual &&
+       grep "^body$" actual
+'
+
+test_expect_success 'cover letter with format.coverFromDescription = subject' '
+       test_config branch.rebuild-1.description "config subject
+
+body" &&
+       test_config format.coverFromDescription subject &&
+       git checkout rebuild-1 &&
+       git format-patch --stdout --cover-letter master >actual &&
+       grep "^Subject: \[PATCH 0/2\] config subject$" actual &&
+       ! grep "^\*\*\* BLURB HERE \*\*\*$" actual &&
+       ! grep "^config subject$" actual &&
+       grep "^body$" actual
+'
+
+test_expect_success 'cover letter with --cover-from-description subject' '
+       test_config branch.rebuild-1.description "config subject
+
+body" &&
+       git checkout rebuild-1 &&
+       git format-patch --stdout --cover-letter --cover-from-description subject master >actual &&
+       grep "^Subject: \[PATCH 0/2\] config subject$" actual &&
+       ! grep "^\*\*\* BLURB HERE \*\*\*$" actual &&
+       ! grep "^config subject$" actual &&
+       grep "^body$" actual
+'
+
+test_expect_success 'cover letter with format.coverFromDescription = auto (short subject line)' '
+       test_config branch.rebuild-1.description "config subject
+
+body" &&
+       test_config format.coverFromDescription auto &&
+       git checkout rebuild-1 &&
+       git format-patch --stdout --cover-letter master >actual &&
+       grep "^Subject: \[PATCH 0/2\] config subject$" actual &&
+       ! grep "^\*\*\* BLURB HERE \*\*\*$" actual &&
+       ! grep "^config subject$" actual &&
+       grep "^body$" actual
+'
+
+test_expect_success 'cover letter with --cover-from-description auto (short subject line)' '
+       test_config branch.rebuild-1.description "config subject
+
+body" &&
+       git checkout rebuild-1 &&
+       git format-patch --stdout --cover-letter --cover-from-description auto master >actual &&
+       grep "^Subject: \[PATCH 0/2\] config subject$" actual &&
+       ! grep "^\*\*\* BLURB HERE \*\*\*$" actual &&
+       ! grep "^config subject$" actual &&
+       grep "^body$" actual
+'
+
+test_expect_success 'cover letter with format.coverFromDescription = auto (long subject line)' '
+       test_config branch.rebuild-1.description "this is a really long first line and it is over 100 characters long which is the threshold for long subjects
+
+body" &&
+       test_config format.coverFromDescription auto &&
+       git checkout rebuild-1 &&
+       git format-patch --stdout --cover-letter master >actual &&
+       grep "^Subject: \[PATCH 0/2\] \*\*\* SUBJECT HERE \*\*\*$" actual &&
+       ! grep "^\*\*\* BLURB HERE \*\*\*$" actual &&
+       grep "^this is a really long first line and it is over 100 characters long which is the threshold for long subjects$" actual &&
+       grep "^body$" actual
+'
+
+test_expect_success 'cover letter with --cover-from-description auto (long subject line)' '
+       test_config branch.rebuild-1.description "this is a really long first line and it is over 100 characters long which is the threshold for long subjects
+
+body" &&
+       git checkout rebuild-1 &&
+       git format-patch --stdout --cover-letter --cover-from-description auto master >actual &&
+       grep "^Subject: \[PATCH 0/2\] \*\*\* SUBJECT HERE \*\*\*$" actual &&
+       ! grep "^\*\*\* BLURB HERE \*\*\*$" actual &&
+       grep "^this is a really long first line and it is over 100 characters long which is the threshold for long subjects$" actual &&
+       grep "^body$" actual
+'
+
+test_expect_success 'cover letter with command-line --cover-from-description overrides config' '
+       test_config branch.rebuild-1.description "config subject
+
+body" &&
+       test_config format.coverFromDescription none &&
+       git checkout rebuild-1 &&
+       git format-patch --stdout --cover-letter --cover-from-description subject master >actual &&
+       grep "^Subject: \[PATCH 0/2\] config subject$" actual &&
+       ! grep "^\*\*\* BLURB HERE \*\*\*$" actual &&
+       ! grep "^config subject$" actual &&
+       grep "^body$" actual
+'
+
 test_expect_success 'cover letter using branch description (1)' '
        git checkout rebuild-1 &&
        test_config branch.rebuild-1.description hello &&
index 6b087df3dcbd01df9c7e533f9e039518beaa773a..eadaf57262637297029040bea4370cc0c407514d 100755 (executable)
@@ -16,6 +16,7 @@ test_expect_success "Ray Lehtiniemi's example" '
        } while (0);
        EOF
        git update-index --add x &&
+       before=$(git rev-parse --short $(git hash-object x)) &&
 
        cat <<-\EOF >x &&
        do
@@ -24,10 +25,11 @@ test_expect_success "Ray Lehtiniemi's example" '
        }
        while (0);
        EOF
+       after=$(git rev-parse --short $(git hash-object x)) &&
 
-       cat <<-\EOF >expect &&
+       cat <<-EOF >expect &&
        diff --git a/x b/x
-       index adf3937..6edc172 100644
+       index $before..$after 100644
        --- a/x
        +++ b/x
        @@ -1,3 +1,5 @@
@@ -61,6 +63,7 @@ test_expect_success 'another test, without options' '
        EOF
 
        git update-index x &&
+       before=$(git rev-parse --short $(git hash-object x)) &&
 
        tr "_" " " <<-\EOF >x &&
        _       whitespace at beginning
@@ -70,10 +73,11 @@ test_expect_success 'another test, without options' '
        unchanged line
        CR at end
        EOF
+       after=$(git rev-parse --short $(git hash-object x)) &&
 
-       tr "Q_" "\015 " <<-\EOF >expect &&
+       tr "Q_" "\015 " <<-EOF >expect &&
        diff --git a/x b/x
-       index d99af23..22d9f73 100644
+       index $before..$after 100644
        --- a/x
        +++ b/x
        @@ -1,6 +1,6 @@
@@ -108,9 +112,9 @@ test_expect_success 'another test, without options' '
        git diff -w --ignore-cr-at-eol >out &&
        test_must_be_empty out &&
 
-       tr "Q_" "\015 " <<-\EOF >expect &&
+       tr "Q_" "\015 " <<-EOF >expect &&
        diff --git a/x b/x
-       index d99af23..22d9f73 100644
+       index $before..$after 100644
        --- a/x
        +++ b/x
        @@ -1,6 +1,6 @@
@@ -132,9 +136,9 @@ test_expect_success 'another test, without options' '
        git diff -b --ignore-cr-at-eol >out &&
        test_cmp expect out &&
 
-       tr "Q_" "\015 " <<-\EOF >expect &&
+       tr "Q_" "\015 " <<-EOF >expect &&
        diff --git a/x b/x
-       index d99af23..22d9f73 100644
+       index $before..$after 100644
        --- a/x
        +++ b/x
        @@ -1,6 +1,6 @@
@@ -154,9 +158,9 @@ test_expect_success 'another test, without options' '
        git diff --ignore-space-at-eol --ignore-cr-at-eol >out &&
        test_cmp expect out &&
 
-       tr "Q_" "\015 " <<-\EOF >expect &&
+       tr "Q_" "\015 " <<-EOF >expect &&
        diff --git a/x b/x
-       index_d99af23..22d9f73 100644
+       index_$before..$after 100644
        --- a/x
        +++ b/x
        @@ -1,6 +1,6 @@
@@ -786,23 +790,25 @@ test_expect_success 'whitespace-only changes not reported' '
        test_must_be_empty actual
 '
 
-cat <<EOF >expect
-diff --git a/x b/z
-similarity index NUM%
-rename from x
-rename to z
-index 380c32a..a97b785 100644
-EOF
 test_expect_success 'whitespace-only changes reported across renames' '
        git reset --hard &&
        for i in 1 2 3 4 5 6 7 8 9; do echo "$i$i$i$i$i$i"; done >x &&
        git add x &&
+       before=$(git rev-parse --short $(git hash-object x)) &&
        git commit -m "base" &&
        sed -e "5s/^/ /" x >z &&
        git rm x &&
        git add z &&
+       after=$(git rev-parse --short $(git hash-object z)) &&
        git diff -w -M --cached |
        sed -e "/^similarity index /s/[0-9][0-9]*/NUM/" >actual &&
+       cat <<-EOF >expect &&
+       diff --git a/x b/z
+       similarity index NUM%
+       rename from x
+       rename to z
+       index $before..$after 100644
+       EOF
        test_cmp expect actual
 '
 
@@ -858,13 +864,15 @@ test_expect_success 'diff that introduces a line with only tabs' '
        git config core.whitespace blank-at-eol &&
        git reset --hard &&
        echo "test" >x &&
+       before=$(git rev-parse --short $(git hash-object x)) &&
        git commit -m "initial" x &&
        echo "{NTN}" | tr "NT" "\n\t" >>x &&
+       after=$(git rev-parse --short $(git hash-object x)) &&
        git diff --color | test_decode_color >current &&
 
-       cat >expected <<-\EOF &&
+       cat >expected <<-EOF &&
        <BOLD>diff --git a/x b/x<RESET>
-       <BOLD>index 9daeafb..2874b91 100644<RESET>
+       <BOLD>index $before..$after 100644<RESET>
        <BOLD>--- a/x<RESET>
        <BOLD>+++ b/x<RESET>
        <CYAN>@@ -1 +1,4 @@<RESET>
@@ -883,19 +891,21 @@ test_expect_success 'diff that introduces and removes ws breakages' '
                echo "0. blank-at-eol " &&
                echo "1. blank-at-eol "
        } >x &&
+       before=$(git rev-parse --short $(git hash-object x)) &&
        git commit -a --allow-empty -m preimage &&
        {
                echo "0. blank-at-eol " &&
                echo "1. still-blank-at-eol " &&
                echo "2. and a new line "
        } >x &&
+       after=$(git rev-parse --short $(git hash-object x)) &&
 
        git diff --color |
        test_decode_color >current &&
 
-       cat >expected <<-\EOF &&
+       cat >expected <<-EOF &&
        <BOLD>diff --git a/x b/x<RESET>
-       <BOLD>index d0233a2..700886e 100644<RESET>
+       <BOLD>index $before..$after 100644<RESET>
        <BOLD>--- a/x<RESET>
        <BOLD>+++ b/x<RESET>
        <CYAN>@@ -1,2 +1,3 @@<RESET>
@@ -915,16 +925,18 @@ test_expect_success 'ws-error-highlight test setup' '
                echo "0. blank-at-eol " &&
                echo "1. blank-at-eol "
        } >x &&
+       before=$(git rev-parse --short $(git hash-object x)) &&
        git commit -a --allow-empty -m preimage &&
        {
                echo "0. blank-at-eol " &&
                echo "1. still-blank-at-eol " &&
                echo "2. and a new line "
        } >x &&
+       after=$(git rev-parse --short $(git hash-object x)) &&
 
-       cat >expect.default-old <<-\EOF &&
+       cat >expect.default-old <<-EOF &&
        <BOLD>diff --git a/x b/x<RESET>
-       <BOLD>index d0233a2..700886e 100644<RESET>
+       <BOLD>index $before..$after 100644<RESET>
        <BOLD>--- a/x<RESET>
        <BOLD>+++ b/x<RESET>
        <CYAN>@@ -1,2 +1,3 @@<RESET>
@@ -934,9 +946,9 @@ test_expect_success 'ws-error-highlight test setup' '
        <GREEN>+<RESET><GREEN>2. and a new line<RESET><BLUE> <RESET>
        EOF
 
-       cat >expect.all <<-\EOF &&
+       cat >expect.all <<-EOF &&
        <BOLD>diff --git a/x b/x<RESET>
-       <BOLD>index d0233a2..700886e 100644<RESET>
+       <BOLD>index $before..$after 100644<RESET>
        <BOLD>--- a/x<RESET>
        <BOLD>+++ b/x<RESET>
        <CYAN>@@ -1,2 +1,3 @@<RESET>
@@ -946,9 +958,9 @@ test_expect_success 'ws-error-highlight test setup' '
        <GREEN>+<RESET><GREEN>2. and a new line<RESET><BLUE> <RESET>
        EOF
 
-       cat >expect.none <<-\EOF
+       cat >expect.none <<-EOF
        <BOLD>diff --git a/x b/x<RESET>
-       <BOLD>index d0233a2..700886e 100644<RESET>
+       <BOLD>index $before..$after 100644<RESET>
        <BOLD>--- a/x<RESET>
        <BOLD>+++ b/x<RESET>
        <CYAN>@@ -1,2 +1,3 @@<RESET>
@@ -1022,14 +1034,15 @@ test_expect_success 'detect moved code, complete file' '
        EOF
        git add test.c &&
        git commit -m "add main function" &&
+       file=$(git rev-parse --short HEAD:test.c) &&
        git mv test.c main.c &&
        test_config color.diff.oldMoved "normal red" &&
        test_config color.diff.newMoved "normal green" &&
        git diff HEAD --color-moved=zebra --color --no-renames | test_decode_color >actual &&
-       cat >expected <<-\EOF &&
+       cat >expected <<-EOF &&
        <BOLD>diff --git a/main.c b/main.c<RESET>
        <BOLD>new file mode 100644<RESET>
-       <BOLD>index 0000000..a986c57<RESET>
+       <BOLD>index 0000000..$file<RESET>
        <BOLD>--- /dev/null<RESET>
        <BOLD>+++ b/main.c<RESET>
        <CYAN>@@ -0,0 +1,5 @@<RESET>
@@ -1040,7 +1053,7 @@ test_expect_success 'detect moved code, complete file' '
        <BGREEN>+<RESET><BGREEN>}<RESET>
        <BOLD>diff --git a/test.c b/test.c<RESET>
        <BOLD>deleted file mode 100644<RESET>
-       <BOLD>index a986c57..0000000<RESET>
+       <BOLD>index $file..0000000<RESET>
        <BOLD>--- a/test.c<RESET>
        <BOLD>+++ /dev/null<RESET>
        <CYAN>@@ -1,5 +0,0 @@<RESET>
@@ -1094,6 +1107,8 @@ test_expect_success 'detect malicious moved code, inside file' '
        EOF
        git add main.c test.c &&
        git commit -m "add main and test file" &&
+       before_main=$(git rev-parse --short HEAD:main.c) &&
+       before_test=$(git rev-parse --short HEAD:test.c) &&
        cat <<-\EOF >main.c &&
                #include<stdio.h>
                int stuff()
@@ -1126,10 +1141,12 @@ test_expect_success 'detect malicious moved code, inside file' '
                        bar();
                }
        EOF
+       after_main=$(git rev-parse --short $(git hash-object main.c)) &&
+       after_test=$(git rev-parse --short $(git hash-object test.c)) &&
        git diff HEAD --no-renames --color-moved=zebra --color | test_decode_color >actual &&
-       cat <<-\EOF >expected &&
+       cat <<-EOF >expected &&
        <BOLD>diff --git a/main.c b/main.c<RESET>
-       <BOLD>index 27a619c..7cf9336 100644<RESET>
+       <BOLD>index $before_main..$after_main 100644<RESET>
        <BOLD>--- a/main.c<RESET>
        <BOLD>+++ b/main.c<RESET>
        <CYAN>@@ -5,13 +5,6 @@<RESET> <RESET>printf("Hello ");<RESET>
@@ -1147,7 +1164,7 @@ test_expect_success 'detect malicious moved code, inside file' '
         {<RESET>
         foo();<RESET>
        <BOLD>diff --git a/test.c b/test.c<RESET>
-       <BOLD>index 1dc1d85..2bedec9 100644<RESET>
+       <BOLD>index $before_test..$after_test 100644<RESET>
        <BOLD>--- a/test.c<RESET>
        <BOLD>+++ b/test.c<RESET>
        <CYAN>@@ -4,6 +4,13 @@<RESET> <RESET>int bar()<RESET>
@@ -1176,9 +1193,9 @@ test_expect_success 'plain moved code, inside file' '
        test_config color.diff.newMovedAlternative "yellow" &&
        # needs previous test as setup
        git diff HEAD --no-renames --color-moved=plain --color | test_decode_color >actual &&
-       cat <<-\EOF >expected &&
+       cat <<-EOF >expected &&
        <BOLD>diff --git a/main.c b/main.c<RESET>
-       <BOLD>index 27a619c..7cf9336 100644<RESET>
+       <BOLD>index $before_main..$after_main 100644<RESET>
        <BOLD>--- a/main.c<RESET>
        <BOLD>+++ b/main.c<RESET>
        <CYAN>@@ -5,13 +5,6 @@<RESET> <RESET>printf("Hello ");<RESET>
@@ -1196,7 +1213,7 @@ test_expect_success 'plain moved code, inside file' '
         {<RESET>
         foo();<RESET>
        <BOLD>diff --git a/test.c b/test.c<RESET>
-       <BOLD>index 1dc1d85..2bedec9 100644<RESET>
+       <BOLD>index $before_test..$after_test 100644<RESET>
        <BOLD>--- a/test.c<RESET>
        <BOLD>+++ b/test.c<RESET>
        <CYAN>@@ -4,6 +4,13 @@<RESET> <RESET>int bar()<RESET>
index 6f5ef0035e92998eb74b14081aa021247abf67d8..c0f48395432539f807dd9474d71529378f71306b 100755 (executable)
@@ -32,6 +32,7 @@ diffpatterns="
        csharp
        css
        dts
+       elixir
        fortran
        fountain
        golang
diff --git a/t/t4018/elixir-do-not-pick-end b/t/t4018/elixir-do-not-pick-end
new file mode 100644 (file)
index 0000000..fae08ba
--- /dev/null
@@ -0,0 +1,5 @@
+defmodule RIGHT do
+end
+#
+#
+# ChangeMe; do not pick up 'end' line
diff --git a/t/t4018/elixir-ex-unit-test b/t/t4018/elixir-ex-unit-test
new file mode 100644 (file)
index 0000000..0560a2b
--- /dev/null
@@ -0,0 +1,6 @@
+defmodule Test do
+  test "RIGHT" do
+    assert true == true
+    assert ChangeMe
+  end
+end
diff --git a/t/t4018/elixir-function b/t/t4018/elixir-function
new file mode 100644 (file)
index 0000000..d452f49
--- /dev/null
@@ -0,0 +1,5 @@
+def function(RIGHT, arg) do
+  # comment
+  # comment
+  ChangeMe
+end
diff --git a/t/t4018/elixir-macro b/t/t4018/elixir-macro
new file mode 100644 (file)
index 0000000..4f925e9
--- /dev/null
@@ -0,0 +1,5 @@
+defmacro foo(RIGHT) do
+  # Code
+  # Code
+  ChangeMe
+end
diff --git a/t/t4018/elixir-module b/t/t4018/elixir-module
new file mode 100644 (file)
index 0000000..91a4e7a
--- /dev/null
@@ -0,0 +1,9 @@
+defmodule RIGHT do
+  @moduledoc """
+  Foo bar
+  """
+
+  def ChangeMe(a) where is_map(a) do
+    a
+  end
+end
diff --git a/t/t4018/elixir-module-func b/t/t4018/elixir-module-func
new file mode 100644 (file)
index 0000000..c9910d0
--- /dev/null
@@ -0,0 +1,8 @@
+defmodule Foo do
+  def fun(RIGHT) do
+     # Code
+     # Code
+     # Code
+     ChangeMe
+  end
+end
diff --git a/t/t4018/elixir-nested-module b/t/t4018/elixir-nested-module
new file mode 100644 (file)
index 0000000..771ebc5
--- /dev/null
@@ -0,0 +1,9 @@
+defmodule MyApp.RIGHT do
+  @moduledoc """
+  Foo bar
+  """
+
+  def ChangeMe(a) where is_map(a) do
+    a
+  end
+end
diff --git a/t/t4018/elixir-private-function b/t/t4018/elixir-private-function
new file mode 100644 (file)
index 0000000..1aabe33
--- /dev/null
@@ -0,0 +1,5 @@
+defp function(RIGHT, arg) do
+  # comment
+  # comment
+  ChangeMe
+end
diff --git a/t/t4018/elixir-protocol b/t/t4018/elixir-protocol
new file mode 100644 (file)
index 0000000..7d91736
--- /dev/null
@@ -0,0 +1,6 @@
+defprotocol RIGHT do
+  @doc """
+  Calculates the size (and not the length!) of a data structure
+  """
+  def size(data, ChangeMe)
+end
diff --git a/t/t4018/elixir-protocol-implementation b/t/t4018/elixir-protocol-implementation
new file mode 100644 (file)
index 0000000..f9234bb
--- /dev/null
@@ -0,0 +1,5 @@
+defimpl RIGHT do
+  # Docs
+  # Docs
+  def foo(ChangeMe), do: :ok
+end
index 9aa8e2b39b45a6c2b5ec48a9d98b94831edb2caa..e29deaf4a509523b00e6bf9ac5d2436957a1b022 100755 (executable)
@@ -6,6 +6,7 @@ test_description='difference in submodules'
 . "$TEST_DIRECTORY"/diff-lib.sh
 
 test_expect_success setup '
+       test_oid_init &&
        test_tick &&
        test_create_repo sub &&
        (
@@ -36,7 +37,8 @@ test_expect_success setup '
 '
 
 test_expect_success 'git diff --raw HEAD' '
-       git diff --raw --abbrev=40 HEAD >actual &&
+       hexsz=$(test_oid hexsz) &&
+       git diff --raw --abbrev=$hexsz HEAD >actual &&
        test_cmp expect actual
 '
 
@@ -245,23 +247,21 @@ test_expect_success 'git diff (empty submodule dir)' '
 '
 
 test_expect_success 'conflicted submodule setup' '
-
-       # 39 efs
-       c=fffffffffffffffffffffffffffffffffffffff &&
+       c=$(test_oid ff_1) &&
        (
                echo "000000 $ZERO_OID 0        sub" &&
                echo "160000 1$c 1      sub" &&
                echo "160000 2$c 2      sub" &&
                echo "160000 3$c 3      sub"
        ) | git update-index --index-info &&
-       echo >expect.nosub '\''diff --cc sub
+       echo >expect.nosub "diff --cc sub
 index 2ffffff,3ffffff..0000000
 --- a/sub
 +++ b/sub
 @@@ -1,1 -1,1 +1,1 @@@
-- Subproject commit 2fffffffffffffffffffffffffffffffffffffff
- -Subproject commit 3fffffffffffffffffffffffffffffffffffffff
-++Subproject commit 0000000000000000000000000000000000000000'\'' &&
+- Subproject commit 2$c
+ -Subproject commit 3$c
+++Subproject commit $ZERO_OID" &&
 
        hh=$(git rev-parse HEAD) &&
        sed -e "s/$ZERO_OID/$hh/" expect.nosub >expect.withsub
index 9a93c2a3e0dd8a4763092549df77172657009c56..fb145aa173ee4ea4c79bcb01336c433e16fa81e5 100755 (executable)
@@ -19,9 +19,11 @@ cat >post.simple <<-\EOF
 
        aeff = aeff * ( aaa )
 EOF
-cat >expect.letter-runs-are-words <<-\EOF
+pre=$(git rev-parse --short $(git hash-object pre.simple))
+post=$(git rev-parse --short $(git hash-object post.simple))
+cat >expect.letter-runs-are-words <<-EOF
        <BOLD>diff --git a/pre b/post<RESET>
-       <BOLD>index 330b04f..5ed8eff 100644<RESET>
+       <BOLD>index $pre..$post 100644<RESET>
        <BOLD>--- a/pre<RESET>
        <BOLD>+++ b/post<RESET>
        <CYAN>@@ -1,3 +1,7 @@<RESET>
@@ -33,9 +35,9 @@ cat >expect.letter-runs-are-words <<-\EOF
 
        <GREEN>aeff = aeff * ( aaa<RESET> )
 EOF
-cat >expect.non-whitespace-is-word <<-\EOF
+cat >expect.non-whitespace-is-word <<-EOF
        <BOLD>diff --git a/pre b/post<RESET>
-       <BOLD>index 330b04f..5ed8eff 100644<RESET>
+       <BOLD>index $pre..$post 100644<RESET>
        <BOLD>--- a/pre<RESET>
        <BOLD>+++ b/post<RESET>
        <CYAN>@@ -1,3 +1,7 @@<RESET>
@@ -49,9 +51,12 @@ cat >expect.non-whitespace-is-word <<-\EOF
 EOF
 
 word_diff () {
+       pre=$(git rev-parse --short $(git hash-object pre)) &&
+       post=$(git rev-parse --short $(git hash-object post)) &&
        test_must_fail git diff --no-index "$@" pre post >output &&
        test_decode_color <output >output.decrypted &&
-       test_cmp expect output.decrypted
+       sed -e "2s/index [^ ]*/index $pre..$post/" expect >expected
+       test_cmp expected output.decrypted
 }
 
 test_language_driver () {
@@ -77,9 +82,9 @@ test_expect_success 'set up pre and post with runs of whitespace' '
 '
 
 test_expect_success 'word diff with runs of whitespace' '
-       cat >expect <<-\EOF &&
+       cat >expect <<-EOF &&
                <BOLD>diff --git a/pre b/post<RESET>
-               <BOLD>index 330b04f..5ed8eff 100644<RESET>
+               <BOLD>index $pre..$post 100644<RESET>
                <BOLD>--- a/pre<RESET>
                <BOLD>+++ b/post<RESET>
                <CYAN>@@ -1,3 +1,7 @@<RESET>
@@ -97,9 +102,9 @@ test_expect_success 'word diff with runs of whitespace' '
 '
 
 test_expect_success '--word-diff=porcelain' '
-       sed 's/#.*$//' >expect <<-\EOF &&
+       sed 's/#.*$//' >expect <<-EOF &&
                diff --git a/pre b/post
-               index 330b04f..5ed8eff 100644
+               index $pre..$post 100644
                --- a/pre
                +++ b/post
                @@ -1,3 +1,7 @@
@@ -121,9 +126,9 @@ test_expect_success '--word-diff=porcelain' '
 '
 
 test_expect_success '--word-diff=plain' '
-       cat >expect <<-\EOF &&
+       cat >expect <<-EOF &&
                diff --git a/pre b/post
-               index 330b04f..5ed8eff 100644
+               index $pre..$post 100644
                --- a/pre
                +++ b/post
                @@ -1,3 +1,7 @@
@@ -140,9 +145,9 @@ test_expect_success '--word-diff=plain' '
 '
 
 test_expect_success '--word-diff=plain --color' '
-       cat >expect <<-\EOF &&
+       cat >expect <<-EOF &&
                <BOLD>diff --git a/pre b/post<RESET>
-               <BOLD>index 330b04f..5ed8eff 100644<RESET>
+               <BOLD>index $pre..$post 100644<RESET>
                <BOLD>--- a/pre<RESET>
                <BOLD>+++ b/post<RESET>
                <CYAN>@@ -1,3 +1,7 @@<RESET>
@@ -158,9 +163,9 @@ test_expect_success '--word-diff=plain --color' '
 '
 
 test_expect_success 'word diff without context' '
-       cat >expect <<-\EOF &&
+       cat >expect <<-EOF &&
                <BOLD>diff --git a/pre b/post<RESET>
-               <BOLD>index 330b04f..5ed8eff 100644<RESET>
+               <BOLD>index $pre..$post 100644<RESET>
                <BOLD>--- a/pre<RESET>
                <BOLD>+++ b/post<RESET>
                <CYAN>@@ -1 +1 @@<RESET>
@@ -207,9 +212,9 @@ test_expect_success 'command-line overrides config' '
 '
 
 test_expect_success 'command-line overrides config: --word-diff-regex' '
-       cat >expect <<-\EOF &&
+       cat >expect <<-EOF &&
                <BOLD>diff --git a/pre b/post<RESET>
-               <BOLD>index 330b04f..5ed8eff 100644<RESET>
+               <BOLD>index $pre..$post 100644<RESET>
                <BOLD>--- a/pre<RESET>
                <BOLD>+++ b/post<RESET>
                <CYAN>@@ -1,3 +1,7 @@<RESET>
@@ -234,9 +239,9 @@ test_expect_success 'setup: remove diff driver regex' '
 '
 
 test_expect_success 'use configured regex' '
-       cat >expect <<-\EOF &&
+       cat >expect <<-EOF &&
                <BOLD>diff --git a/pre b/post<RESET>
-               <BOLD>index 330b04f..5ed8eff 100644<RESET>
+               <BOLD>index $pre..$post 100644<RESET>
                <BOLD>--- a/pre<RESET>
                <BOLD>+++ b/post<RESET>
                <CYAN>@@ -1,3 +1,7 @@<RESET>
@@ -254,9 +259,11 @@ test_expect_success 'use configured regex' '
 test_expect_success 'test parsing words for newline' '
        echo "aaa (aaa)" >pre &&
        echo "aaa (aaa) aaa" >post &&
-       cat >expect <<-\EOF &&
+       pre=$(git rev-parse --short $(git hash-object pre)) &&
+       post=$(git rev-parse --short $(git hash-object post)) &&
+       cat >expect <<-EOF &&
                <BOLD>diff --git a/pre b/post<RESET>
-               <BOLD>index c29453b..be22f37 100644<RESET>
+               <BOLD>index $pre..$post 100644<RESET>
                <BOLD>--- a/pre<RESET>
                <BOLD>+++ b/post<RESET>
                <CYAN>@@ -1 +1 @@<RESET>
@@ -268,9 +275,11 @@ test_expect_success 'test parsing words for newline' '
 test_expect_success 'test when words are only removed at the end' '
        echo "(:" >pre &&
        echo "(" >post &&
-       cat >expect <<-\EOF &&
+       pre=$(git rev-parse --short $(git hash-object pre)) &&
+       post=$(git rev-parse --short $(git hash-object post)) &&
+       cat >expect <<-EOF &&
                <BOLD>diff --git a/pre b/post<RESET>
-               <BOLD>index 289cb9d..2d06f37 100644<RESET>
+               <BOLD>index $pre..$post 100644<RESET>
                <BOLD>--- a/pre<RESET>
                <BOLD>+++ b/post<RESET>
                <CYAN>@@ -1 +1 @@<RESET>
@@ -282,9 +291,11 @@ test_expect_success 'test when words are only removed at the end' '
 test_expect_success '--word-diff=none' '
        echo "(:" >pre &&
        echo "(" >post &&
-       cat >expect <<-\EOF &&
+       pre=$(git rev-parse --short $(git hash-object pre)) &&
+       post=$(git rev-parse --short $(git hash-object post)) &&
+       cat >expect <<-EOF &&
                diff --git a/pre b/post
-               index 289cb9d..2d06f37 100644
+               index $pre..$post 100644
                --- a/pre
                +++ b/post
                @@ -1 +1 @@
@@ -317,16 +328,6 @@ test_language_driver ruby
 test_language_driver tex
 
 test_expect_success 'word-diff with diff.sbe' '
-       cat >expect <<-\EOF &&
-       diff --git a/pre b/post
-       index a1a53b5..bc8fe6d 100644
-       --- a/pre
-       +++ b/post
-       @@ -1,3 +1,3 @@
-       a
-
-       [-b-]{+c+}
-       EOF
        cat >pre <<-\EOF &&
        a
 
@@ -337,21 +338,35 @@ test_expect_success 'word-diff with diff.sbe' '
 
        c
        EOF
+       pre=$(git rev-parse --short $(git hash-object pre)) &&
+       post=$(git rev-parse --short $(git hash-object post)) &&
+       cat >expect <<-EOF &&
+       diff --git a/pre b/post
+       index $pre..$post 100644
+       --- a/pre
+       +++ b/post
+       @@ -1,3 +1,3 @@
+       a
+
+       [-b-]{+c+}
+       EOF
        test_config diff.suppress-blank-empty true &&
        word_diff --word-diff=plain
 '
 
 test_expect_success 'word-diff with no newline at EOF' '
-       cat >expect <<-\EOF &&
+       printf "%s" "a a a a a" >pre &&
+       printf "%s" "a a ab a a" >post &&
+       pre=$(git rev-parse --short $(git hash-object pre)) &&
+       post=$(git rev-parse --short $(git hash-object post)) &&
+       cat >expect <<-EOF &&
        diff --git a/pre b/post
-       index 7bf316e..3dd0303 100644
+       index $pre..$post 100644
        --- a/pre
        +++ b/post
        @@ -1 +1 @@
        a a [-a-]{+ab+} a a
        EOF
-       printf "%s" "a a a a a" >pre &&
-       printf "%s" "a a ab a a" >post &&
        word_diff --word-diff=plain
 '
 
index b9d876efa20469ae4cce824f9fd5924e13c936a1..b5a56895e8e8368996c8c7f9314b4bf93fddd234 100755 (executable)
@@ -440,11 +440,13 @@ test_expect_success 'setup for --combined-all-paths' '
        git branch side2c &&
        git checkout side1c &&
        test_seq 1 10 >filename-side1c &&
+       side1cf=$(git hash-object filename-side1c) &&
        git add filename-side1c &&
        git commit -m with &&
        git checkout side2c &&
        test_seq 1 9 >filename-side2c &&
        echo ten >>filename-side2c &&
+       side2cf=$(git hash-object filename-side2c) &&
        git add filename-side2c &&
        git commit -m iam &&
        git checkout -b mergery side1c &&
@@ -452,13 +454,14 @@ test_expect_success 'setup for --combined-all-paths' '
        git rm filename-side1c &&
        echo eleven >>filename-side2c &&
        git mv filename-side2c filename-merged &&
+       mergedf=$(git hash-object filename-merged) &&
        git add filename-merged &&
        git commit
 '
 
 test_expect_success '--combined-all-paths and --raw' '
-       cat <<-\EOF >expect &&
-       ::100644 100644 100644 f00c965d8307308469e537302baa73048488f162 088bd5d92c2a8e0203ca8e7e4c2a5c692f6ae3f7 333b9c62519f285e1854830ade0fe1ef1d40ee1b RR    filename-side1c filename-side2c filename-merged
+       cat <<-EOF >expect &&
+       ::100644 100644 100644 $side1cf $side2cf $mergedf RR    filename-side1c filename-side2c filename-merged
        EOF
        git diff-tree -c -M --raw --combined-all-paths HEAD >actual.tmp &&
        sed 1d <actual.tmp >actual &&
@@ -482,11 +485,13 @@ test_expect_success FUNNYNAMES 'setup for --combined-all-paths with funny names'
        git checkout side1d &&
        test_seq 1 10 >"$(printf "file\twith\ttabs")" &&
        git add file* &&
+       side1df=$(git hash-object *tabs) &&
        git commit -m with &&
        git checkout side2d &&
        test_seq 1 9 >"$(printf "i\tam\ttabbed")" &&
        echo ten >>"$(printf "i\tam\ttabbed")" &&
        git add *tabbed &&
+       side2df=$(git hash-object *tabbed) &&
        git commit -m iam &&
        git checkout -b funny-names-mergery side1d &&
        git merge --no-commit side2d &&
@@ -494,12 +499,14 @@ test_expect_success FUNNYNAMES 'setup for --combined-all-paths with funny names'
        echo eleven >>"$(printf "i\tam\ttabbed")" &&
        git mv "$(printf "i\tam\ttabbed")" "$(printf "fickle\tnaming")" &&
        git add fickle* &&
-       git commit
+       headf=$(git hash-object fickle*) &&
+       git commit &&
+       head=$(git rev-parse HEAD)
 '
 
 test_expect_success FUNNYNAMES '--combined-all-paths and --raw and funny names' '
-       cat <<-\EOF >expect &&
-       ::100644 100644 100644 f00c965d8307308469e537302baa73048488f162 088bd5d92c2a8e0203ca8e7e4c2a5c692f6ae3f7 333b9c62519f285e1854830ade0fe1ef1d40ee1b RR    "file\twith\ttabs"      "i\tam\ttabbed" "fickle\tnaming"
+       cat <<-EOF >expect &&
+       ::100644 100644 100644 $side1df $side2df $headf RR      "file\twith\ttabs"      "i\tam\ttabbed" "fickle\tnaming"
        EOF
        git diff-tree -c -M --raw --combined-all-paths HEAD >actual.tmp &&
        sed 1d <actual.tmp >actual &&
@@ -507,7 +514,7 @@ test_expect_success FUNNYNAMES '--combined-all-paths and --raw and funny names'
 '
 
 test_expect_success FUNNYNAMES '--combined-all-paths and --raw -and -z and funny names' '
-       printf "aaf8087c3cbd4db8e185a2d074cf27c53cfb75d7\0::100644 100644 100644 f00c965d8307308469e537302baa73048488f162 088bd5d92c2a8e0203ca8e7e4c2a5c692f6ae3f7 333b9c62519f285e1854830ade0fe1ef1d40ee1b RR\0file\twith\ttabs\0i\tam\ttabbed\0fickle\tnaming\0" >expect &&
+       printf "$head\0::100644 100644 100644 $side1df $side2df $headf RR\0file\twith\ttabs\0i\tam\ttabbed\0fickle\tnaming\0" >expect &&
        git diff-tree -c -M --raw --combined-all-paths -z HEAD >actual &&
        test_cmp expect actual
 '
index 53ac44b0f0036803b48979c3c5f343ca02b1b80a..0eb0314a8b35daecd3a442592bac4de1c480ffe0 100755 (executable)
@@ -12,6 +12,7 @@ test_expect_success 'setup' '
        git commit -m zero &&
        echo one > one &&
        echo two > two &&
+       blob=$(git hash-object one) &&
        git add one two &&
        git commit -m onetwo &&
        git update-index --assume-unchanged one &&
@@ -20,7 +21,7 @@ test_expect_success 'setup' '
 '
 
 test_expect_success 'diff-index does not examine assume-unchanged entries' '
-       git diff-index HEAD^ -- one | grep -q 5626abf0f72e58d7a153368ba57db4c673c0e171
+       git diff-index HEAD^ -- one | grep -q $blob
 '
 
 test_expect_success 'diff-files does not examine assume-unchanged entries' '
index 619bf970983e481af1537eb539d66bdd6b12971f..f85213658560c5f1f268e11bc2388a3279361c5f 100755 (executable)
@@ -284,7 +284,7 @@ test_expect_success 'submodule contains untracked content (all ignored)' '
        test_must_be_empty actual
 '
 
-test_expect_success 'submodule contains untracked and modifed content' '
+test_expect_success 'submodule contains untracked and modified content' '
        echo new > sm1/foo6 &&
        git diff-index -p --submodule=log HEAD >actual &&
        cat >expected <<-EOF &&
@@ -294,7 +294,7 @@ test_expect_success 'submodule contains untracked and modifed content' '
        test_cmp expected actual
 '
 
-test_expect_success 'submodule contains untracked and modifed content (untracked ignored)' '
+test_expect_success 'submodule contains untracked and modified content (untracked ignored)' '
        echo new > sm1/foo6 &&
        git diff-index -p --ignore-submodules=untracked --submodule=log HEAD >actual &&
        cat >expected <<-EOF &&
@@ -303,19 +303,19 @@ test_expect_success 'submodule contains untracked and modifed content (untracked
        test_cmp expected actual
 '
 
-test_expect_success 'submodule contains untracked and modifed content (dirty ignored)' '
+test_expect_success 'submodule contains untracked and modified content (dirty ignored)' '
        echo new > sm1/foo6 &&
        git diff-index -p --ignore-submodules=dirty --submodule=log HEAD >actual &&
        test_must_be_empty actual
 '
 
-test_expect_success 'submodule contains untracked and modifed content (all ignored)' '
+test_expect_success 'submodule contains untracked and modified content (all ignored)' '
        echo new > sm1/foo6 &&
        git diff-index -p --ignore-submodules --submodule=log HEAD >actual &&
        test_must_be_empty actual
 '
 
-test_expect_success 'submodule contains modifed content' '
+test_expect_success 'submodule contains modified content' '
        rm -f sm1/new-file &&
        git diff-index -p --submodule=log HEAD >actual &&
        cat >expected <<-EOF &&
@@ -369,7 +369,7 @@ test_expect_success 'modified submodule contains untracked content (all ignored)
        test_must_be_empty actual
 '
 
-test_expect_success 'modified submodule contains untracked and modifed content' '
+test_expect_success 'modified submodule contains untracked and modified content' '
        echo modification >> sm1/foo6 &&
        git diff-index -p --submodule=log HEAD >actual &&
        cat >expected <<-EOF &&
@@ -381,7 +381,7 @@ test_expect_success 'modified submodule contains untracked and modifed content'
        test_cmp expected actual
 '
 
-test_expect_success 'modified submodule contains untracked and modifed content (untracked ignored)' '
+test_expect_success 'modified submodule contains untracked and modified content (untracked ignored)' '
        echo modification >> sm1/foo6 &&
        git diff-index -p --ignore-submodules=untracked --submodule=log HEAD >actual &&
        cat >expected <<-EOF &&
@@ -392,7 +392,7 @@ test_expect_success 'modified submodule contains untracked and modifed content (
        test_cmp expected actual
 '
 
-test_expect_success 'modified submodule contains untracked and modifed content (dirty ignored)' '
+test_expect_success 'modified submodule contains untracked and modified content (dirty ignored)' '
        echo modification >> sm1/foo6 &&
        git diff-index -p --ignore-submodules=dirty --submodule=log HEAD >actual &&
        cat >expected <<-EOF &&
@@ -402,13 +402,13 @@ test_expect_success 'modified submodule contains untracked and modifed content (
        test_cmp expected actual
 '
 
-test_expect_success 'modified submodule contains untracked and modifed content (all ignored)' '
+test_expect_success 'modified submodule contains untracked and modified content (all ignored)' '
        echo modification >> sm1/foo6 &&
        git diff-index -p --ignore-submodules --submodule=log HEAD >actual &&
        test_must_be_empty actual
 '
 
-test_expect_success 'modified submodule contains modifed content' '
+test_expect_success 'modified submodule contains modified content' '
        rm -f sm1/new-file &&
        git diff-index -p --submodule=log HEAD >actual &&
        cat >expected <<-EOF &&
index 647905e01fb963efce34cd30b351a31a671cbb5e..4701796d10e1028debe4581f2fa10038b38be003 100755 (executable)
@@ -3,34 +3,48 @@
 test_description='test unique sha1 abbreviation on "index from..to" line'
 . ./test-lib.sh
 
-if ! test_have_prereq SHA1
-then
-       skip_all='not using SHA-1 for objects'
-       test_done
-fi
-
-cat >expect_initial <<EOF
-100644 blob 51d2738463ea4ca66f8691c91e33ce64b7d41bb1   foo
-EOF
+test_expect_success 'setup' '
+       test_oid_cache <<-EOF &&
+       val1 sha1:4827
+       val1 sha256:5664
 
-cat >expect_update <<EOF
-100644 blob 51d2738efb4ad8a1e40bed839ab8e116f0a15e47   foo
-EOF
+       val2 sha1:11742
+       val2 sha256:10625
 
-test_expect_success 'setup' '
-       echo 4827 > foo &&
+       hash1 sha1:51d2738463ea4ca66f8691c91e33ce64b7d41bb1
+       hash1 sha256:ae31dfff0af93b2c62b0098a039b38569c43b0a7e97b873000ca42d128f27350
+
+       hasht1 sha1:51d27384
+       hasht1 sha256:ae31dfff
+
+       hash2 sha1:51d2738efb4ad8a1e40bed839ab8e116f0a15e47
+       hash2 sha256:ae31dffada88a46fd5f53c7ed5aa25a7a8951f1d5e88456c317c8d5484d263e5
+
+       hasht2 sha1:51d2738e
+       hasht2 sha256:ae31dffa
+       EOF
+
+       cat >expect_initial <<-EOF &&
+       100644 blob $(test_oid hash1)   foo
+       EOF
+
+       cat >expect_update <<-EOF &&
+       100644 blob $(test_oid hash2)   foo
+       EOF
+
+       echo "$(test_oid val1)" > foo &&
        git add foo &&
        git commit -m "initial" &&
        git cat-file -p HEAD: > actual &&
        test_cmp expect_initial actual &&
-       echo 11742 > foo &&
+       echo "$(test_oid val2)" > foo &&
        git commit -a -m "update" &&
        git cat-file -p HEAD: > actual &&
        test_cmp expect_update actual
 '
 
 cat >expect <<EOF
-index 51d27384..51d2738e 100644
+index $(test_oid hasht1)..$(test_oid hasht2) 100644
 EOF
 
 test_expect_success 'diff does not produce ambiguous index line' '
index 36f8ed8a818714abc234ca940a59693f4be21046..258808708e1093819a32e68dfc76b6faf506f388 100755 (executable)
@@ -70,7 +70,7 @@ check_raw () {
        expect=$1
        shift
        cat >expected <<-EOF
-       :000000 100644 0000000000000000000000000000000000000000 $blob A $expect
+       :000000 100644 $ZERO_OID $blob A        $expect
        EOF
        test_expect_success "--raw $*" "
                git -C '$dir' diff --no-abbrev --raw $* HEAD^ >actual &&
index 87a8949500bbfb450624de27f17f1300761cf20e..7f9ad9fa3d1f97b9aeedbb0c1d351be5749837ee 100755 (executable)
@@ -9,24 +9,27 @@ test_expect_success 'setup binary merge conflict' '
        git commit -m one &&
        echo twoQ2 | q_to_nul >binary &&
        git commit -a -m two &&
+       two=$(git rev-parse --short HEAD:binary) &&
        git checkout -b branch-binary HEAD^ &&
        echo threeQ3 | q_to_nul >binary &&
        git commit -a -m three &&
+       three=$(git rev-parse --short HEAD:binary) &&
        test_must_fail git merge master &&
        echo resolvedQhooray | q_to_nul >binary &&
-       git commit -a -m resolved
+       git commit -a -m resolved &&
+       res=$(git rev-parse --short HEAD:binary)
 '
 
-cat >expect <<'EOF'
+cat >expect <<EOF
 resolved
 
 diff --git a/binary b/binary
-index 7ea6ded..9563691 100644
+index $three..$res 100644
 Binary files a/binary and b/binary differ
 resolved
 
 diff --git a/binary b/binary
-index 6197570..9563691 100644
+index $two..$res 100644
 Binary files a/binary and b/binary differ
 EOF
 test_expect_success 'diff -m indicates binary-ness' '
@@ -34,11 +37,11 @@ test_expect_success 'diff -m indicates binary-ness' '
        test_cmp expect actual
 '
 
-cat >expect <<'EOF'
+cat >expect <<EOF
 resolved
 
 diff --combined binary
-index 7ea6ded,6197570..9563691
+index $three,$two..$res
 Binary files differ
 EOF
 test_expect_success 'diff -c indicates binary-ness' '
@@ -46,11 +49,11 @@ test_expect_success 'diff -c indicates binary-ness' '
        test_cmp expect actual
 '
 
-cat >expect <<'EOF'
+cat >expect <<EOF
 resolved
 
 diff --cc binary
-index 7ea6ded,6197570..9563691
+index $three,$two..$res
 Binary files differ
 EOF
 test_expect_success 'diff --cc indicates binary-ness' '
@@ -62,23 +65,26 @@ test_expect_success 'setup non-binary with binary attribute' '
        git checkout master &&
        test_commit one text &&
        test_commit two text &&
+       two=$(git rev-parse --short HEAD:text) &&
        git checkout -b branch-text HEAD^ &&
        test_commit three text &&
+       three=$(git rev-parse --short HEAD:text) &&
        test_must_fail git merge master &&
        test_commit resolved text &&
+       res=$(git rev-parse --short HEAD:text) &&
        echo text -diff >.gitattributes
 '
 
-cat >expect <<'EOF'
+cat >expect <<EOF
 resolved
 
 diff --git a/text b/text
-index 2bdf67a..2ab19ae 100644
+index $three..$res 100644
 Binary files a/text and b/text differ
 resolved
 
 diff --git a/text b/text
-index f719efd..2ab19ae 100644
+index $two..$res 100644
 Binary files a/text and b/text differ
 EOF
 test_expect_success 'diff -m respects binary attribute' '
@@ -86,11 +92,11 @@ test_expect_success 'diff -m respects binary attribute' '
        test_cmp expect actual
 '
 
-cat >expect <<'EOF'
+cat >expect <<EOF
 resolved
 
 diff --combined text
-index 2bdf67a,f719efd..2ab19ae
+index $three,$two..$res
 Binary files differ
 EOF
 test_expect_success 'diff -c respects binary attribute' '
@@ -98,11 +104,11 @@ test_expect_success 'diff -c respects binary attribute' '
        test_cmp expect actual
 '
 
-cat >expect <<'EOF'
+cat >expect <<EOF
 resolved
 
 diff --cc text
-index 2bdf67a,f719efd..2ab19ae
+index $three,$two..$res
 Binary files differ
 EOF
 test_expect_success 'diff --cc respects binary attribute' '
@@ -115,11 +121,11 @@ test_expect_success 'setup textconv attribute' '
        git config diff.upcase.textconv "tr a-z A-Z <"
 '
 
-cat >expect <<'EOF'
+cat >expect <<EOF
 resolved
 
 diff --git a/text b/text
-index 2bdf67a..2ab19ae 100644
+index $three..$res 100644
 --- a/text
 +++ b/text
 @@ -1 +1 @@
@@ -128,7 +134,7 @@ index 2bdf67a..2ab19ae 100644
 resolved
 
 diff --git a/text b/text
-index f719efd..2ab19ae 100644
+index $two..$res 100644
 --- a/text
 +++ b/text
 @@ -1 +1 @@
@@ -140,11 +146,11 @@ test_expect_success 'diff -m respects textconv attribute' '
        test_cmp expect actual
 '
 
-cat >expect <<'EOF'
+cat >expect <<EOF
 resolved
 
 diff --combined text
-index 2bdf67a,f719efd..2ab19ae
+index $three,$two..$res
 --- a/text
 +++ b/text
 @@@ -1,1 -1,1 +1,1 @@@
@@ -157,11 +163,11 @@ test_expect_success 'diff -c respects textconv attribute' '
        test_cmp expect actual
 '
 
-cat >expect <<'EOF'
+cat >expect <<EOF
 resolved
 
 diff --cc text
-index 2bdf67a,f719efd..2ab19ae
+index $three,$two..$res
 --- a/text
 +++ b/text
 @@@ -1,1 -1,1 +1,1 @@@
@@ -174,9 +180,9 @@ test_expect_success 'diff --cc respects textconv attribute' '
        test_cmp expect actual
 '
 
-cat >expect <<'EOF'
+cat >expect <<EOF
 diff --combined text
-index 2bdf67a,f719efd..2ab19ae
+index $three,$two..$res
 --- a/text
 +++ b/text
 @@@ -1,1 -1,1 +1,1 @@@
@@ -190,9 +196,9 @@ test_expect_success 'diff-tree plumbing does not respect textconv' '
        test_cmp expect actual
 '
 
-cat >expect <<'EOF'
+cat >expect <<EOF
 diff --cc text
-index 2bdf67a,f719efd..0000000
+index $three,$two..0000000
 --- a/text
 +++ b/text
 @@@ -1,1 -1,1 +1,5 @@@
index dff36b77ec8856573da26685376d156d49f8e021..4f4b541658a1b604ee27aff76a4e2047fca8111c 100755 (executable)
@@ -33,7 +33,7 @@ test_expect_success 'trivial merge - combine-diff empty' '
 '
 
 
-test_expect_success 'only one trully conflicting path' '
+test_expect_success 'only one truly conflicting path' '
        git checkout side &&
        for i in $(test_seq 2 9)
        do
index 90ab54f0f586c87ace077be87fba396c8f2781a0..43394f828561756d10dfd95a877bd460975e6d04 100644 (file)
@@ -75,8 +75,8 @@ diff --git a/Documentation/git.txt b/Documentation/git.txt
 +link:git-ssh-pull.html[git-ssh-pull]::
        Pulls from a remote repository over ssh connection
  
- Interogators:
-@@ -156,8 +156,8 @@ Interogators:
+ Interrogators:
+@@ -156,8 +156,8 @@ Interrogators:
  link:git-diff-helper.html[git-diff-helper]::
        Generates patch format output for git-diff-*
  
index 90cdbaa5bb62a4adfa0a5eb3f6fe223b9de50e7b..cac172e7791dcd6d31902658e983236f0df2a3af 100644 (file)
@@ -211,7 +211,7 @@ dissimilarity index 82%
 -
 -              /* If this is an exact directory match, we may have
 -               * directory files following this path. Match on them.
--               * Otherwise, we're at a pach subcomponent, and we need
+-               * Otherwise, we're at a patch subcomponent, and we need
 -               * to try to match again.
 -               */
 -              if (mtype == 0)
index 5f6ddc105950eac20bcacb7987fabff015320ee3..57ec79d8879a13e08a79d9eaef8e1fcad1468704 100644 (file)
@@ -185,8 +185,8 @@ diff a/Documentation/git.txt b/Documentation/git.txt
 +link:git-ssh-pull.html[git-ssh-pull]::
        Pulls from a remote repository over ssh connection
  
- Interogators:
-@@ -156,8 +156,8 @@ Interogators:
+ Interrogators:
+@@ -156,8 +156,8 @@ Interrogators:
  link:git-diff-helper.html[git-diff-helper]::
        Generates patch format output for git-diff-*
  
index 07c6589e74fa5afbff9bdfd8d7b7b41f873a005b..fa2430510864e816238cc8c11b7a6573f7a35a9e 100644 (file)
@@ -335,7 +335,7 @@ diff a/ls-tree.c b/ls-tree.c
  
 -              /* If this is an exact directory match, we may have
 -               * directory files following this path. Match on them.
--               * Otherwise, we're at a pach subcomponent, and we need
+-               * Otherwise, we're at a patch subcomponent, and we need
 -               * to try to match again.
 +      if (e->directory) {
 +              /* If this is a directory, we have the following cases:
index fa5d4efb89dc4ec83e3cd84fcb147958f361ab59..d7349ced6be0aa1d717dbd0c312a65c8314235b3 100755 (executable)
@@ -4,23 +4,17 @@ test_description='git apply --3way'
 
 . ./test-lib.sh
 
-create_file () {
-       for i
-       do
-               echo "$i"
-       done
-}
-
-sanitize_conflicted_diff () {
+print_sanitized_conflicted_diff () {
+       git diff HEAD >diff.raw &&
        sed -e '
                /^index /d
-               s/^\(+[<>][<>][<>][<>]*\) .*/\1/
-       '
+               s/^\(+[<>|][<>|][<>|][<>|]*\) .*/\1/
+       ' diff.raw
 }
 
 test_expect_success setup '
        test_tick &&
-       create_file >one 1 2 3 4 5 6 7 &&
+       test_write_lines 1 2 3 4 5 6 7 >one &&
        cat one >two &&
        git add one two &&
        git commit -m initial &&
@@ -28,13 +22,13 @@ test_expect_success setup '
        git branch side &&
 
        test_tick &&
-       create_file >one 1 two 3 4 5 six 7 &&
-       create_file >two 1 two 3 4 5 6 7 &&
+       test_write_lines 1 two 3 4 5 six 7 >one &&
+       test_write_lines 1 two 3 4 5 6 7 >two &&
        git commit -a -m master &&
 
        git checkout side &&
-       create_file >one 1 2 3 4 five 6 7 &&
-       create_file >two 1 2 3 4 five 6 7 &&
+       test_write_lines 1 2 3 4 five 6 7 >one &&
+       test_write_lines 1 2 3 4 five 6 7 >two &&
        git commit -a -m side &&
 
        git checkout master
@@ -52,7 +46,7 @@ test_expect_success 'apply without --3way' '
        git diff-index --exit-code --cached HEAD
 '
 
-test_expect_success 'apply with --3way' '
+test_apply_with_3way () {
        # Merging side should be similar to applying this patch
        git diff ...side >P.diff &&
 
@@ -61,22 +55,31 @@ test_expect_success 'apply with --3way' '
        git checkout master^0 &&
        test_must_fail git merge --no-commit side &&
        git ls-files -s >expect.ls &&
-       git diff HEAD | sanitize_conflicted_diff >expect.diff &&
+       print_sanitized_conflicted_diff >expect.diff &&
 
        # should fail to apply
        git reset --hard &&
        git checkout master^0 &&
        test_must_fail git apply --index --3way P.diff &&
        git ls-files -s >actual.ls &&
-       git diff HEAD | sanitize_conflicted_diff >actual.diff &&
+       print_sanitized_conflicted_diff >actual.diff &&
 
        # The result should resemble the corresponding merge
        test_cmp expect.ls actual.ls &&
        test_cmp expect.diff actual.diff
+}
+
+test_expect_success 'apply with --3way' '
+       test_apply_with_3way
+'
+
+test_expect_success 'apply with --3way with merge.conflictStyle = diff3' '
+       test_config merge.conflictStyle diff3 &&
+       test_apply_with_3way
 '
 
 test_expect_success 'apply with --3way with rerere enabled' '
-       git config rerere.enabled true &&
+       test_config rerere.enabled true &&
 
        # Merging side should be similar to applying this patch
        git diff ...side >P.diff &&
@@ -87,7 +90,7 @@ test_expect_success 'apply with --3way with rerere enabled' '
        test_must_fail git merge --no-commit side &&
 
        # Manually resolve and record the resolution
-       create_file 1 two 3 4 five six 7 >one &&
+       test_write_lines 1 two 3 4 five six 7 >one &&
        git rerere &&
        cat one >expect &&
 
@@ -104,14 +107,14 @@ test_expect_success 'apply -3 with add/add conflict setup' '
        git reset --hard &&
 
        git checkout -b adder &&
-       create_file 1 2 3 4 5 6 7 >three &&
-       create_file 1 2 3 4 5 6 7 >four &&
+       test_write_lines 1 2 3 4 5 6 7 >three &&
+       test_write_lines 1 2 3 4 5 6 7 >four &&
        git add three four &&
        git commit -m "add three and four" &&
 
        git checkout -b another adder^ &&
-       create_file 1 2 3 4 5 6 7 >three &&
-       create_file 1 2 3 four 5 6 7 >four &&
+       test_write_lines 1 2 3 4 5 6 7 >three &&
+       test_write_lines 1 2 3 four 5 6 7 >four &&
        git add three four &&
        git commit -m "add three and four" &&
 
@@ -121,7 +124,7 @@ test_expect_success 'apply -3 with add/add conflict setup' '
        git checkout adder^0 &&
        test_must_fail git merge --no-commit another &&
        git ls-files -s >expect.ls &&
-       git diff HEAD | sanitize_conflicted_diff >expect.diff
+       print_sanitized_conflicted_diff >expect.diff
 '
 
 test_expect_success 'apply -3 with add/add conflict' '
@@ -131,7 +134,7 @@ test_expect_success 'apply -3 with add/add conflict' '
        test_must_fail git apply --index --3way P.diff &&
        # ... and leave conflicts in the index and in the working tree
        git ls-files -s >actual.ls &&
-       git diff HEAD | sanitize_conflicted_diff >actual.diff &&
+       print_sanitized_conflicted_diff >actual.diff &&
 
        # The result should resemble the corresponding merge
        test_cmp expect.ls actual.ls &&
index e803ba402e9e2251b53f5b0acfc15f052722e1e9..ab0d0213650a8b36e60188d2b398db0d24b08a19 100755 (executable)
@@ -667,7 +667,7 @@ cat > expect <<\EOF
 * | | fifth
 * | | fourth
 |/ /
-* | third
+* / third
 |/
 * second
 * initial
index 918ada69eb96662a2814a7f44c4eb0c3714bd0da..586c3a86b1d2cc08baf945eefe44a34efcd98ecf 100755 (executable)
@@ -13,8 +13,8 @@ fuzz_blame () {
 }
 
 test_expect_success setup '
-       cat >contacts <<-\EOF &&
-       A U Thor <author@example.com>
+       cat >contacts <<- EOF &&
+       $GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL>
        nick1 <bugs@company.xx>
        EOF
 
@@ -33,19 +33,19 @@ test_expect_success 'check-mailmap no arguments' '
 '
 
 test_expect_success 'check-mailmap arguments' '
-       cat >expect <<-\EOF &&
-       A U Thor <author@example.com>
+       cat >expect <<- EOF &&
+       $GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL>
        nick1 <bugs@company.xx>
        EOF
        git check-mailmap \
-               "A U Thor <author@example.com>" \
+               "$GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL>" \
                "nick1 <bugs@company.xx>" >actual &&
        test_cmp expect actual
 '
 
 test_expect_success 'check-mailmap --stdin' '
-       cat >expect <<-\EOF &&
-       A U Thor <author@example.com>
+       cat >expect <<- EOF &&
+       $GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL>
        nick1 <bugs@company.xx>
        EOF
        git check-mailmap --stdin <contacts >actual &&
@@ -66,8 +66,8 @@ test_expect_success 'check-mailmap bogus contact' '
        test_must_fail git check-mailmap bogus
 '
 
-cat >expect <<\EOF
-A U Thor (1):
+cat >expect << EOF
+$GIT_AUTHOR_NAME (1):
       initial
 
 nick1 (1):
@@ -90,7 +90,7 @@ nick1 (1):
 EOF
 
 test_expect_success 'default .mailmap' '
-       echo "Repo Guy <author@example.com>" > .mailmap &&
+       echo "Repo Guy <$GIT_AUTHOR_EMAIL>" > .mailmap &&
        git shortlog HEAD >actual &&
        test_cmp expect actual
 '
@@ -122,7 +122,7 @@ Internal Guy (1):
 
 EOF
 test_expect_success 'mailmap.file override' '
-       echo "External Guy <author@example.com>" >> internal_mailmap/.mailmap &&
+       echo "External Guy <$GIT_AUTHOR_EMAIL>" >> internal_mailmap/.mailmap &&
        git config mailmap.file internal_mailmap/.mailmap &&
        git shortlog HEAD >actual &&
        test_cmp expect actual
@@ -178,8 +178,8 @@ test_expect_success 'name entry after email entry, case-insensitive' '
        test_cmp expect actual
 '
 
-cat >expect <<\EOF
-A U Thor (1):
+cat >expect << EOF
+$GIT_AUTHOR_NAME (1):
       initial
 
 nick1 (1):
@@ -195,18 +195,18 @@ test_expect_success 'No mailmap files, but configured' '
 test_expect_success 'setup mailmap blob tests' '
        git checkout -b map &&
        test_when_finished "git checkout master" &&
-       cat >just-bugs <<-\EOF &&
+       cat >just-bugs <<- EOF &&
        Blob Guy <bugs@company.xx>
        EOF
-       cat >both <<-\EOF &&
-       Blob Guy <author@example.com>
+       cat >both <<- EOF &&
+       Blob Guy <$GIT_AUTHOR_EMAIL>
        Blob Guy <bugs@company.xx>
        EOF
-       printf "Tricky Guy <author@example.com>" >no-newline &&
+       printf "Tricky Guy <$GIT_AUTHOR_EMAIL>" >no-newline &&
        git add just-bugs both no-newline &&
        git commit -m "my mailmaps" &&
-       echo "Repo Guy <author@example.com>" >.mailmap &&
-       echo "Internal Guy <author@example.com>" >internal.map
+       echo "Repo Guy <$GIT_AUTHOR_EMAIL>" >.mailmap &&
+       echo "Internal Guy <$GIT_AUTHOR_EMAIL>" >internal.map
 '
 
 test_expect_success 'mailmap.blob set' '
@@ -266,12 +266,12 @@ test_expect_success 'mailmap.blob defaults to off in non-bare repo' '
        git init non-bare &&
        (
                cd non-bare &&
-               test_commit one .mailmap "Fake Name <author@example.com>" &&
+               test_commit one .mailmap "Fake Name <$GIT_AUTHOR_EMAIL>" &&
                echo "     1    Fake Name" >expect &&
                git shortlog -ns HEAD >actual &&
                test_cmp expect actual &&
                rm .mailmap &&
-               echo "     1    A U Thor" >expect &&
+               echo "     1    $GIT_AUTHOR_NAME" >expect &&
                git shortlog -ns HEAD >actual &&
                test_cmp expect actual
        )
@@ -305,26 +305,26 @@ test_expect_success 'cleanup after mailmap.blob tests' '
 '
 
 test_expect_success 'single-character name' '
-       echo "     1    A <author@example.com>" >expect &&
+       echo "     1    A <$GIT_AUTHOR_EMAIL>" >expect &&
        echo "     1    nick1 <bugs@company.xx>" >>expect &&
-       echo "A <author@example.com>" >.mailmap &&
+       echo "A <$GIT_AUTHOR_EMAIL>" >.mailmap &&
        test_when_finished "rm .mailmap" &&
        git shortlog -es HEAD >actual &&
        test_cmp expect actual
 '
 
 test_expect_success 'preserve canonical email case' '
-       echo "     1    A U Thor <AUTHOR@example.com>" >expect &&
+       echo "     1    $GIT_AUTHOR_NAME <AUTHOR@example.com>" >expect &&
        echo "     1    nick1 <bugs@company.xx>" >>expect &&
-       echo "<AUTHOR@example.com> <author@example.com>" >.mailmap &&
+       echo "<AUTHOR@example.com> <$GIT_AUTHOR_EMAIL>" >.mailmap &&
        test_when_finished "rm .mailmap" &&
        git shortlog -es HEAD >actual &&
        test_cmp expect actual
 '
 
 # Extended mailmap configurations should give us the following output for shortlog
-cat >expect <<\EOF
-A U Thor <author@example.com> (1):
+cat >expect << EOF
+$GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL> (1):
       initial
 
 CTO <cto@company.xx> (1):
@@ -370,7 +370,7 @@ test_expect_success 'Shortlog output (complex mapping)' '
        git commit --author "CTO <cto@coompany.xx>" -m seventh &&
 
        mkdir -p internal_mailmap &&
-       echo "Committed <committer@example.com>" > internal_mailmap/.mailmap &&
+       echo "Committed <$GIT_COMMITTER_EMAIL>" > internal_mailmap/.mailmap &&
        echo "<cto@company.xx>                       <cto@coompany.xx>" >> internal_mailmap/.mailmap &&
        echo "Some Dude <some@dude.xx>         nick1 <bugs@company.xx>" >> internal_mailmap/.mailmap &&
        echo "Other Author <other@author.xx>   nick2 <bugs@company.xx>" >> internal_mailmap/.mailmap &&
@@ -384,27 +384,27 @@ test_expect_success 'Shortlog output (complex mapping)' '
 '
 
 # git log with --pretty format which uses the name and email mailmap placemarkers
-cat >expect <<\EOF
+cat >expect << EOF
 Author CTO <cto@coompany.xx> maps to CTO <cto@company.xx>
-Committer C O Mitter <committer@example.com> maps to Committed <committer@example.com>
+Committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> maps to Committed <$GIT_COMMITTER_EMAIL>
 
 Author claus <me@company.xx> maps to Santa Claus <santa.claus@northpole.xx>
-Committer C O Mitter <committer@example.com> maps to Committed <committer@example.com>
+Committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> maps to Committed <$GIT_COMMITTER_EMAIL>
 
 Author santa <me@company.xx> maps to Santa Claus <santa.claus@northpole.xx>
-Committer C O Mitter <committer@example.com> maps to Committed <committer@example.com>
+Committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> maps to Committed <$GIT_COMMITTER_EMAIL>
 
 Author nick2 <nick2@company.xx> maps to Other Author <other@author.xx>
-Committer C O Mitter <committer@example.com> maps to Committed <committer@example.com>
+Committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> maps to Committed <$GIT_COMMITTER_EMAIL>
 
 Author nick2 <bugs@company.xx> maps to Other Author <other@author.xx>
-Committer C O Mitter <committer@example.com> maps to Committed <committer@example.com>
+Committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> maps to Committed <$GIT_COMMITTER_EMAIL>
 
 Author nick1 <bugs@company.xx> maps to Some Dude <some@dude.xx>
-Committer C O Mitter <committer@example.com> maps to Committed <committer@example.com>
+Committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> maps to Committed <$GIT_COMMITTER_EMAIL>
 
-Author A U Thor <author@example.com> maps to A U Thor <author@example.com>
-Committer C O Mitter <committer@example.com> maps to Committed <committer@example.com>
+Author $GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL> maps to $GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL>
+Committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> maps to Committed <$GIT_COMMITTER_EMAIL>
 EOF
 
 test_expect_success 'Log output (complex mapping)' '
@@ -412,14 +412,42 @@ test_expect_success 'Log output (complex mapping)' '
        test_cmp expect actual
 '
 
-cat >expect <<\EOF
+cat >expect << EOF
+Author email cto@coompany.xx has local-part cto
+Committer email $GIT_COMMITTER_EMAIL has local-part $TEST_COMMITTER_LOCALNAME
+
+Author email me@company.xx has local-part me
+Committer email $GIT_COMMITTER_EMAIL has local-part $TEST_COMMITTER_LOCALNAME
+
+Author email me@company.xx has local-part me
+Committer email $GIT_COMMITTER_EMAIL has local-part $TEST_COMMITTER_LOCALNAME
+
+Author email nick2@company.xx has local-part nick2
+Committer email $GIT_COMMITTER_EMAIL has local-part $TEST_COMMITTER_LOCALNAME
+
+Author email bugs@company.xx has local-part bugs
+Committer email $GIT_COMMITTER_EMAIL has local-part $TEST_COMMITTER_LOCALNAME
+
+Author email bugs@company.xx has local-part bugs
+Committer email $GIT_COMMITTER_EMAIL has local-part $TEST_COMMITTER_LOCALNAME
+
+Author email author@example.com has local-part author
+Committer email $GIT_COMMITTER_EMAIL has local-part $TEST_COMMITTER_LOCALNAME
+EOF
+
+test_expect_success 'Log output (local-part email address)' '
+       git log --pretty=format:"Author email %ae has local-part %al%nCommitter email %ce has local-part %cl%n" >actual &&
+       test_cmp expect actual
+'
+
+cat >expect << EOF
 Author: CTO <cto@company.xx>
 Author: Santa Claus <santa.claus@northpole.xx>
 Author: Santa Claus <santa.claus@northpole.xx>
 Author: Other Author <other@author.xx>
 Author: Other Author <other@author.xx>
 Author: Some Dude <some@dude.xx>
-Author: A U Thor <author@example.com>
+Author: $GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL>
 EOF
 
 test_expect_success 'Log output with --use-mailmap' '
@@ -427,14 +455,14 @@ test_expect_success 'Log output with --use-mailmap' '
        test_cmp expect actual
 '
 
-cat >expect <<\EOF
+cat >expect << EOF
 Author: CTO <cto@company.xx>
 Author: Santa Claus <santa.claus@northpole.xx>
 Author: Santa Claus <santa.claus@northpole.xx>
 Author: Other Author <other@author.xx>
 Author: Other Author <other@author.xx>
 Author: Some Dude <some@dude.xx>
-Author: A U Thor <author@example.com>
+Author: $GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL>
 EOF
 
 test_expect_success 'Log output with log.mailmap' '
@@ -443,28 +471,28 @@ test_expect_success 'Log output with log.mailmap' '
 '
 
 test_expect_success 'log.mailmap=false disables mailmap' '
-       cat >expect <<-\EOF &&
+       cat >expect <<- EOF &&
        Author: CTO <cto@coompany.xx>
        Author: claus <me@company.xx>
        Author: santa <me@company.xx>
        Author: nick2 <nick2@company.xx>
        Author: nick2 <bugs@company.xx>
        Author: nick1 <bugs@company.xx>
-       Author: A U Thor <author@example.com>
+       Author: $GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL>
        EOF
        git -c log.mailmap=False log | grep Author > actual &&
        test_cmp expect actual
 '
 
 test_expect_success '--no-use-mailmap disables mailmap' '
-       cat >expect <<-\EOF &&
+       cat >expect <<- EOF &&
        Author: CTO <cto@coompany.xx>
        Author: claus <me@company.xx>
        Author: santa <me@company.xx>
        Author: nick2 <nick2@company.xx>
        Author: nick2 <bugs@company.xx>
        Author: nick1 <bugs@company.xx>
-       Author: A U Thor <author@example.com>
+       Author: $GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL>
        EOF
        git log --no-use-mailmap | grep Author > actual &&
        test_cmp expect actual
@@ -500,8 +528,8 @@ test_expect_success 'Only grep replaced author with --use-mailmap' '
 '
 
 # git blame
-cat >expect <<\EOF
-^OBJI (A U Thor     DATE 1) one
+cat >expect <<EOF
+^OBJI ($GIT_AUTHOR_NAME     DATE 1) one
 OBJID (Some Dude    DATE 2) two
 OBJID (Other Author DATE 3) three
 OBJID (Other Author DATE 4) four
index f42a69faa2fde46d958577d10c50d2a6379ebf60..2f251b27d057077117c658df1211aedd9f5ef7e4 100755 (executable)
@@ -640,7 +640,7 @@ test_expect_success 'pretty format %(trailers:key=foo) multiple keys' '
        test_cmp expect actual
 '
 
-test_expect_success '%(trailers:key=nonexistant) becomes empty' '
+test_expect_success '%(trailers:key=nonexistent) becomes empty' '
        git log --no-walk --pretty="x%(trailers:key=Nacked-by)x" >actual &&
        echo "xx" >expect &&
        test_cmp expect actual
index 7f90f58c03e6fff921e65f9b4138de9dcf972941..53a4af324495dda3738388af31dcecf31f8ee1ee 100755 (executable)
@@ -36,7 +36,7 @@ count_expand ()
        esac
 
        # Prefix the output with the command line arguments, and
-       # replace SP with a dot both in the expecte and actual output
+       # replace SP with a dot both in the expected and actual output
        # so that test_cmp would show the difference together with the
        # breakage in a way easier to consume by the debugging user.
        {
index 3ae8e51e5002691e43615c9285d5501a802dff1a..40d27db674cf1053e8554591d7b697388d13e028 100755 (executable)
@@ -26,15 +26,14 @@ test_expect_success 'set up merge history' '
 test_expect_success 'log --graph with tricky octopus merge, no color' '
        cat >expect.uncolored <<-\EOF &&
        * left
-       | *---.   octopus-merge
-       | |\ \ \
-       |/ / / /
+       | *-.   octopus-merge
+       |/|\ \
        | | | * 4
        | | * | 3
        | | |/
-       | * | 2
+       | * / 2
        | |/
-       * | 1
+       * / 1
        |/
        * initial
        EOF
@@ -47,15 +46,14 @@ test_expect_success 'log --graph with tricky octopus merge with colors' '
        test_config log.graphColors red,green,yellow,blue,magenta,cyan &&
        cat >expect.colors <<-\EOF &&
        * left
-       <RED>|<RESET> *<BLUE>-<RESET><BLUE>-<RESET><MAGENTA>-<RESET><MAGENTA>.<RESET>   octopus-merge
-       <RED>|<RESET> <RED>|<RESET><YELLOW>\<RESET> <BLUE>\<RESET> <MAGENTA>\<RESET>
-       <RED>|<RESET><RED>/<RESET> <YELLOW>/<RESET> <BLUE>/<RESET> <MAGENTA>/<RESET>
+       <RED>|<RESET> *<MAGENTA>-<RESET><MAGENTA>.<RESET>   octopus-merge
+       <RED>|<RESET><RED>/<RESET><YELLOW>|<RESET><BLUE>\<RESET> <MAGENTA>\<RESET>
        <RED>|<RESET> <YELLOW>|<RESET> <BLUE>|<RESET> * 4
        <RED>|<RESET> <YELLOW>|<RESET> * <MAGENTA>|<RESET> 3
        <RED>|<RESET> <YELLOW>|<RESET> <MAGENTA>|<RESET><MAGENTA>/<RESET>
-       <RED>|<RESET> * <MAGENTA>|<RESET> 2
+       <RED>|<RESET> * <MAGENTA>/<RESET> 2
        <RED>|<RESET> <MAGENTA>|<RESET><MAGENTA>/<RESET>
-       * <MAGENTA>|<RESET> 1
+       * <MAGENTA>/<RESET> 1
        <MAGENTA>|<RESET><MAGENTA>/<RESET>
        * initial
        EOF
@@ -74,9 +72,9 @@ test_expect_success 'log --graph with normal octopus merge, no color' '
        | | | * 4
        | | * | 3
        | | |/
-       | * | 2
+       | * / 2
        | |/
-       * | 1
+       * / 1
        |/
        * initial
        EOF
@@ -92,9 +90,9 @@ test_expect_success 'log --graph with normal octopus merge with colors' '
        <RED>|<RESET> <GREEN>|<RESET> <YELLOW>|<RESET> * 4
        <RED>|<RESET> <GREEN>|<RESET> * <BLUE>|<RESET> 3
        <RED>|<RESET> <GREEN>|<RESET> <BLUE>|<RESET><BLUE>/<RESET>
-       <RED>|<RESET> * <BLUE>|<RESET> 2
+       <RED>|<RESET> * <BLUE>/<RESET> 2
        <RED>|<RESET> <BLUE>|<RESET><BLUE>/<RESET>
-       * <BLUE>|<RESET> 1
+       * <BLUE>/<RESET> 1
        <BLUE>|<RESET><BLUE>/<RESET>
        * initial
        EOF
@@ -112,9 +110,9 @@ test_expect_success 'log --graph with normal octopus merge and child, no color'
        | | | * 4
        | | * | 3
        | | |/
-       | * | 2
+       | * / 2
        | |/
-       * | 1
+       * / 1
        |/
        * initial
        EOF
@@ -123,7 +121,7 @@ test_expect_success 'log --graph with normal octopus merge and child, no color'
        test_cmp expect.uncolored actual
 '
 
-test_expect_failure 'log --graph with normal octopus and child merge with colors' '
+test_expect_success 'log --graph with normal octopus and child merge with colors' '
        cat >expect.colors <<-\EOF &&
        * after-merge
        *<BLUE>-<RESET><BLUE>-<RESET><MAGENTA>-<RESET><MAGENTA>.<RESET>   octopus-merge
@@ -131,9 +129,9 @@ test_expect_failure 'log --graph with normal octopus and child merge with colors
        <GREEN>|<RESET> <YELLOW>|<RESET> <BLUE>|<RESET> * 4
        <GREEN>|<RESET> <YELLOW>|<RESET> * <MAGENTA>|<RESET> 3
        <GREEN>|<RESET> <YELLOW>|<RESET> <MAGENTA>|<RESET><MAGENTA>/<RESET>
-       <GREEN>|<RESET> * <MAGENTA>|<RESET> 2
+       <GREEN>|<RESET> * <MAGENTA>/<RESET> 2
        <GREEN>|<RESET> <MAGENTA>|<RESET><MAGENTA>/<RESET>
-       * <MAGENTA>|<RESET> 1
+       * <MAGENTA>/<RESET> 1
        <MAGENTA>|<RESET><MAGENTA>/<RESET>
        * initial
        EOF
@@ -147,15 +145,14 @@ test_expect_success 'log --graph with tricky octopus merge and its child, no col
        cat >expect.uncolored <<-\EOF &&
        * left
        | * after-merge
-       | *---.   octopus-merge
-       | |\ \ \
-       |/ / / /
+       | *-.   octopus-merge
+       |/|\ \
        | | | * 4
        | | * | 3
        | | |/
-       | * | 2
+       | * / 2
        | |/
-       * | 1
+       * / 1
        |/
        * initial
        EOF
@@ -164,20 +161,19 @@ test_expect_success 'log --graph with tricky octopus merge and its child, no col
        test_cmp expect.uncolored actual
 '
 
-test_expect_failure 'log --graph with tricky octopus merge and its child with colors' '
+test_expect_success 'log --graph with tricky octopus merge and its child with colors' '
        test_config log.graphColors red,green,yellow,blue,magenta,cyan &&
        cat >expect.colors <<-\EOF &&
        * left
        <RED>|<RESET> * after-merge
-       <RED>|<RESET> *<MAGENTA>-<RESET><MAGENTA>-<RESET><CYAN>-<RESET><CYAN>.<RESET>   octopus-merge
-       <RED>|<RESET> <RED>|<RESET><BLUE>\<RESET> <MAGENTA>\<RESET> <CYAN>\<RESET>
-       <RED>|<RESET><RED>/<RESET> <BLUE>/<RESET> <MAGENTA>/<RESET> <CYAN>/<RESET>
+       <RED>|<RESET> *<CYAN>-<RESET><CYAN>.<RESET>   octopus-merge
+       <RED>|<RESET><RED>/<RESET><BLUE>|<RESET><MAGENTA>\<RESET> <CYAN>\<RESET>
        <RED>|<RESET> <BLUE>|<RESET> <MAGENTA>|<RESET> * 4
        <RED>|<RESET> <BLUE>|<RESET> * <CYAN>|<RESET> 3
        <RED>|<RESET> <BLUE>|<RESET> <CYAN>|<RESET><CYAN>/<RESET>
-       <RED>|<RESET> * <CYAN>|<RESET> 2
+       <RED>|<RESET> * <CYAN>/<RESET> 2
        <RED>|<RESET> <CYAN>|<RESET><CYAN>/<RESET>
-       * <CYAN>|<RESET> 1
+       * <CYAN>/<RESET> 1
        <CYAN>|<RESET><CYAN>/<RESET>
        * initial
        EOF
@@ -209,7 +205,7 @@ test_expect_success 'log --graph with crossover in octopus merge, no color' '
        test_cmp expect.uncolored actual
 '
 
-test_expect_failure 'log --graph with crossover in octopus merge with colors' '
+test_expect_success 'log --graph with crossover in octopus merge with colors' '
        test_config log.graphColors red,green,yellow,blue,magenta,cyan &&
        cat >expect.colors <<-\EOF &&
        * after-4
@@ -257,7 +253,7 @@ test_expect_success 'log --graph with crossover in octopus merge and its child,
        test_cmp expect.uncolored actual
 '
 
-test_expect_failure 'log --graph with crossover in octopus merge and its child with colors' '
+test_expect_success 'log --graph with crossover in octopus merge and its child with colors' '
        test_config log.graphColors red,green,yellow,blue,magenta,cyan &&
        cat >expect.colors <<-\EOF &&
        * after-4
@@ -353,7 +349,7 @@ test_expect_success 'log --graph with unrelated commit and octopus child, no col
        test_cmp expect.uncolored actual
 '
 
-test_expect_failure 'log --graph with unrelated commit and octopus child with colors' '
+test_expect_success 'log --graph with unrelated commit and octopus child with colors' '
        test_config log.graphColors red,green,yellow,blue,magenta,cyan &&
        cat >expect.colors <<-\EOF &&
        * after-initial
diff --git a/t/t4215-log-skewed-merges.sh b/t/t4215-log-skewed-merges.sh
new file mode 100755 (executable)
index 0000000..18709a7
--- /dev/null
@@ -0,0 +1,243 @@
+#!/bin/sh
+
+test_description='git log --graph of skewed merges'
+
+. ./test-lib.sh
+
+check_graph () {
+       cat >expect &&
+       git log --graph --pretty=tformat:%s "$@" >actual.raw &&
+       sed "s/ *$//" actual.raw >actual &&
+       test_cmp expect actual
+}
+
+test_expect_success 'log --graph with merge fusing with its left and right neighbors' '
+       git checkout --orphan _p &&
+       test_commit A &&
+       test_commit B &&
+       git checkout -b _q @^ && test_commit C &&
+       git checkout -b _r @^ && test_commit D &&
+       git checkout _p && git merge --no-ff _q _r -m E &&
+       git checkout _r && test_commit F &&
+       git checkout _p && git merge --no-ff _r -m G &&
+       git checkout @^^ && git merge --no-ff _p -m H &&
+
+       check_graph <<-\EOF
+       *   H
+       |\
+       | *   G
+       | |\
+       | | * F
+       | * | E
+       |/|\|
+       | | * D
+       | * | C
+       | |/
+       * / B
+       |/
+       * A
+       EOF
+'
+
+test_expect_success 'log --graph with left-skewed merge' '
+       git checkout --orphan 0_p && test_commit 0_A &&
+       git checkout -b 0_q 0_p && test_commit 0_B &&
+       git checkout -b 0_r 0_p &&
+       test_commit 0_C &&
+       test_commit 0_D &&
+       git checkout -b 0_s 0_p && test_commit 0_E &&
+       git checkout -b 0_t 0_p && git merge --no-ff 0_r^ 0_s -m 0_F &&
+       git checkout 0_p && git merge --no-ff 0_s -m 0_G &&
+       git checkout @^ && git merge --no-ff 0_q 0_r 0_t 0_p -m 0_H &&
+
+       check_graph <<-\EOF
+       *-----.   0_H
+       |\ \ \ \
+       | | | | * 0_G
+       | |_|_|/|
+       |/| | | |
+       | | | * | 0_F
+       | |_|/|\|
+       |/| | | |
+       | | | | * 0_E
+       | |_|_|/
+       |/| | |
+       | | * | 0_D
+       | | |/
+       | | * 0_C
+       | |/
+       |/|
+       | * 0_B
+       |/
+       * 0_A
+       EOF
+'
+
+test_expect_success 'log --graph with nested left-skewed merge' '
+       git checkout --orphan 1_p &&
+       test_commit 1_A &&
+       test_commit 1_B &&
+       test_commit 1_C &&
+       git checkout -b 1_q @^ && test_commit 1_D &&
+       git checkout 1_p && git merge --no-ff 1_q -m 1_E &&
+       git checkout -b 1_r @~3 && test_commit 1_F &&
+       git checkout 1_p && git merge --no-ff 1_r -m 1_G &&
+       git checkout @^^ && git merge --no-ff 1_p -m 1_H &&
+
+       check_graph <<-\EOF
+       *   1_H
+       |\
+       | *   1_G
+       | |\
+       | | * 1_F
+       | * | 1_E
+       |/| |
+       | * | 1_D
+       * | | 1_C
+       |/ /
+       * / 1_B
+       |/
+       * 1_A
+       EOF
+'
+
+test_expect_success 'log --graph with nested left-skewed merge following normal merge' '
+       git checkout --orphan 2_p &&
+       test_commit 2_A &&
+       test_commit 2_B &&
+       test_commit 2_C &&
+       git checkout -b 2_q @^^ &&
+       test_commit 2_D &&
+       test_commit 2_E &&
+       git checkout -b 2_r @^ && test_commit 2_F &&
+       git checkout 2_q &&
+       git merge --no-ff 2_r -m 2_G &&
+       git merge --no-ff 2_p^ -m 2_H &&
+       git checkout -b 2_s @^^ && git merge --no-ff 2_q -m 2_J &&
+       git checkout 2_p && git merge --no-ff 2_s -m 2_K &&
+
+       check_graph <<-\EOF
+       *   2_K
+       |\
+       | *   2_J
+       | |\
+       | | *   2_H
+       | | |\
+       | | * | 2_G
+       | |/| |
+       | | * | 2_F
+       | * | | 2_E
+       | |/ /
+       | * | 2_D
+       * | | 2_C
+       | |/
+       |/|
+       * | 2_B
+       |/
+       * 2_A
+       EOF
+'
+
+test_expect_success 'log --graph with nested right-skewed merge following left-skewed merge' '
+       git checkout --orphan 3_p &&
+       test_commit 3_A &&
+       git checkout -b 3_q &&
+       test_commit 3_B &&
+       test_commit 3_C &&
+       git checkout -b 3_r @^ &&
+       test_commit 3_D &&
+       git checkout 3_q && git merge --no-ff 3_r -m 3_E &&
+       git checkout 3_p && git merge --no-ff 3_q -m 3_F &&
+       git checkout 3_r && test_commit 3_G &&
+       git checkout 3_p && git merge --no-ff 3_r -m 3_H &&
+       git checkout @^^ && git merge --no-ff 3_p -m 3_J &&
+
+       check_graph <<-\EOF
+       *   3_J
+       |\
+       | *   3_H
+       | |\
+       | | * 3_G
+       | * | 3_F
+       |/| |
+       | * | 3_E
+       | |\|
+       | | * 3_D
+       | * | 3_C
+       | |/
+       | * 3_B
+       |/
+       * 3_A
+       EOF
+'
+
+test_expect_success 'log --graph with right-skewed merge following a left-skewed one' '
+       git checkout --orphan 4_p &&
+       test_commit 4_A &&
+       test_commit 4_B &&
+       test_commit 4_C &&
+       git checkout -b 4_q @^^ && test_commit 4_D &&
+       git checkout -b 4_r 4_p^ && git merge --no-ff 4_q -m 4_E &&
+       git checkout -b 4_s 4_p^^ &&
+       git merge --no-ff 4_r -m 4_F &&
+       git merge --no-ff 4_p -m 4_G &&
+       git checkout @^^ && git merge --no-ff 4_s -m 4_H &&
+
+       check_graph --date-order <<-\EOF
+       *   4_H
+       |\
+       | *   4_G
+       | |\
+       | * | 4_F
+       |/| |
+       | * |   4_E
+       | |\ \
+       | | * | 4_D
+       | |/ /
+       |/| |
+       | | * 4_C
+       | |/
+       | * 4_B
+       |/
+       * 4_A
+       EOF
+'
+
+test_expect_success 'log --graph with octopus merge with column joining its penultimate parent' '
+       git checkout --orphan 5_p &&
+       test_commit 5_A &&
+       git branch 5_q &&
+       git branch 5_r &&
+       test_commit 5_B &&
+       git checkout 5_q && test_commit 5_C &&
+       git checkout 5_r && test_commit 5_D &&
+       git checkout 5_p &&
+       git merge --no-ff 5_q 5_r -m 5_E &&
+       git checkout 5_q && test_commit 5_F &&
+       git checkout -b 5_s 5_p^ &&
+       git merge --no-ff 5_p 5_q -m 5_G &&
+       git checkout 5_r &&
+       git merge --no-ff 5_s -m 5_H &&
+
+       check_graph <<-\EOF
+       *   5_H
+       |\
+       | *-.   5_G
+       | |\ \
+       | | | * 5_F
+       | | * |   5_E
+       | |/|\ \
+       | |_|/ /
+       |/| | /
+       | | |/
+       * | | 5_D
+       | | * 5_C
+       | |/
+       |/|
+       | * 5_B
+       |/
+       * 5_A
+       EOF
+'
+
+test_done
index d42b3efe39183627aaeb3eef461e6593dd762e4a..5a3e4e331ad2f28025f3b56373c917f3ef438117 100755 (executable)
@@ -85,7 +85,7 @@ graph_read_expect() {
        num_commits: $1
        chunks: oid_fanout oid_lookup commit_metadata$OPTIONAL
        EOF
-       git commit-graph read >output &&
+       test-tool read-graph >output &&
        test_cmp expect output
 }
 
@@ -660,7 +660,7 @@ test_expect_success 'corrupt commit-graph write (missing tree)' '
                git commit-tree -p "$broken" -m "good" "$tree" >good &&
                test_must_fail git commit-graph write --stdin-commits \
                        <good 2>test_err &&
-               test_i18ngrep "unable to get tree for" test_err
+               test_i18ngrep "unable to parse commit" test_err
        )
 '
 
index c72ca0439993bb25b3d0e25fa2ce5d399b49a2b7..cd2f87be6afe241a35e7c3b20fac09d10908f633 100755 (executable)
@@ -147,6 +147,21 @@ test_expect_success 'write midx with two packs' '
 
 compare_results_with_midx "two packs"
 
+test_expect_success 'write progress off for redirected stderr' '
+       git multi-pack-index --object-dir=$objdir write 2>err &&
+       test_line_count = 0 err
+'
+
+test_expect_success 'write force progress on for stderr' '
+       git multi-pack-index --object-dir=$objdir --progress write 2>err &&
+       test_file_not_empty err
+'
+
+test_expect_success 'write with the --no-progress option' '
+       git multi-pack-index --object-dir=$objdir --no-progress write 2>err &&
+       test_line_count = 0 err
+'
+
 test_expect_success 'add more packs' '
        for j in $(test_seq 11 20)
        do
@@ -169,6 +184,21 @@ test_expect_success 'verify multi-pack-index success' '
        git multi-pack-index verify --object-dir=$objdir
 '
 
+test_expect_success 'verify progress off for redirected stderr' '
+       git multi-pack-index verify --object-dir=$objdir 2>err &&
+       test_line_count = 0 err
+'
+
+test_expect_success 'verify force progress on for stderr' '
+       git multi-pack-index verify --object-dir=$objdir --progress 2>err &&
+       test_file_not_empty err
+'
+
+test_expect_success 'verify with the --no-progress option' '
+       git multi-pack-index verify --object-dir=$objdir --no-progress 2>err &&
+       test_line_count = 0 err
+'
+
 # usage: corrupt_midx_and_verify <pos> <data> <objdir> <string>
 corrupt_midx_and_verify() {
        POS=$1 &&
@@ -284,6 +314,21 @@ test_expect_success 'git-fsck incorrect offset' '
                "git -c core.multipackindex=true fsck"
 '
 
+test_expect_success 'repack progress off for redirected stderr' '
+       git multi-pack-index --object-dir=$objdir repack 2>err &&
+       test_line_count = 0 err
+'
+
+test_expect_success 'repack force progress on for stderr' '
+       git multi-pack-index --object-dir=$objdir --progress repack 2>err &&
+       test_file_not_empty err
+'
+
+test_expect_success 'repack with the --no-progress option' '
+       git multi-pack-index --object-dir=$objdir --no-progress repack 2>err &&
+       test_line_count = 0 err
+'
+
 test_expect_success 'repack removes multi-pack-index' '
        test_path_is_file $objdir/pack/multi-pack-index &&
        GIT_TEST_MULTI_PACK_INDEX=0 git repack -adf &&
@@ -413,6 +458,30 @@ test_expect_success 'expire does not remove any packs' '
        )
 '
 
+test_expect_success 'expire progress off for redirected stderr' '
+       (
+               cd dup &&
+               git multi-pack-index expire 2>err &&
+               test_line_count = 0 err
+       )
+'
+
+test_expect_success 'expire force progress on for stderr' '
+       (
+               cd dup &&
+               git multi-pack-index --progress expire 2>err &&
+               test_file_not_empty err
+       )
+'
+
+test_expect_success 'expire with the --no-progress option' '
+       (
+               cd dup &&
+               git multi-pack-index --no-progress expire 2>err &&
+               test_line_count = 0 err
+       )
+'
+
 test_expect_success 'expire removes unreferenced packs' '
        (
                cd dup &&
index 115aabd1416b8f3e243282aa811c273ca2a15e41..c24823431f2314169874adc705188f40da4c1618 100755 (executable)
@@ -25,7 +25,7 @@ graph_read_expect() {
        num_commits: $1
        chunks: oid_fanout oid_lookup commit_metadata
        EOF
-       git commit-graph read >output &&
+       test-tool read-graph >output &&
        test_cmp expect output
 }
 
index 44309566f13431fe5e70f834b4cd6bb0a674624e..4d1e0c363ea1343738ddeccdfa8ddbd056031f45 100755 (executable)
@@ -163,7 +163,7 @@ test_pushdefault_workflow success current master
 # update parent1's foo (which is our upstream)
 test_pushdefault_workflow success upstream foo
 
-# upsream is foo which is not the name of the current branch
+# upstream is foo which is not the name of the current branch
 test_pushdefault_workflow failure simple master
 
 # master and foo are updated
index 8ed58d27f24351b324051028372ac072ab9e5ea9..e8f6d233ffb80ba1a449d47df022f7765b12f6af 100755 (executable)
@@ -1,6 +1,6 @@
 #!/bin/sh
 
-test_description='avoiding conflicting update thru symref aliasing'
+test_description='avoiding conflicting update through symref aliasing'
 
 . ./test-lib.sh
 
index 04b34c4de19f858c530e6944822d778c26391a72..38e6f7340e74474b6899eff3543e7af31f1bf0cf 100755 (executable)
@@ -115,7 +115,7 @@ test_expect_success 'push options and submodules' '
 
        git -C parent submodule add ../upstream workbench &&
        git -C parent/workbench remote add up ../../upstream &&
-       git -C parent commit -m "add submoule" &&
+       git -C parent commit -m "add submodule" &&
 
        test_commit -C parent/workbench two &&
        git -C parent add workbench &&
index 79f7b65f8c4eeed492c032593912e00d21296091..fea56cda6d3a256161a21cb7e79e6e96f328bf17 100755 (executable)
@@ -46,6 +46,14 @@ test_expect_success 'do partial clone 1' '
        test "$(git -C pc1 config --local remote.origin.partialclonefilter)" = "blob:none"
 '
 
+test_expect_success 'verify that .promisor file contains refs fetched' '
+       ls pc1/.git/objects/pack/pack-*.promisor >promisorlist &&
+       test_line_count = 1 promisorlist &&
+       git -C srv.bare rev-list HEAD >headhash &&
+       grep "$(cat headhash) HEAD" $(cat promisorlist) &&
+       grep "$(cat headhash) refs/heads/master" $(cat promisorlist)
+'
+
 # checkout master to force dynamic object fetch of blobs at HEAD.
 test_expect_success 'verify checkout with dynamic object fetch' '
        git -C pc1 rev-list --quiet --objects --missing=print HEAD >observed &&
@@ -296,6 +304,76 @@ test_expect_success 'partial clone with unresolvable sparse filter fails cleanly
        test_i18ngrep "unable to parse sparse filter data in" err
 '
 
+setup_triangle () {
+       rm -rf big-blob.txt server client promisor-remote &&
+
+       printf "line %d\n" $(test_seq 1 100) >big-blob.txt &&
+
+       # Create a server with 2 commits: a commit with a big blob and a child
+       # commit with an incremental change. Also, create a partial clone
+       # client that only contains the first commit.
+       git init server &&
+       git -C server config --local uploadpack.allowfilter 1 &&
+       cp big-blob.txt server &&
+       git -C server add big-blob.txt &&
+       git -C server commit -m "initial" &&
+       git clone --bare --filter=tree:0 "file://$(pwd)/server" client &&
+       echo another line >>server/big-blob.txt &&
+       git -C server commit -am "append line to big blob" &&
+
+       # Create a promisor remote that only contains the blob from the first
+       # commit, and set it as the promisor remote of client. Thus, whenever
+       # the client lazy fetches, the lazy fetch will succeed only if it is
+       # for this blob.
+       git init promisor-remote &&
+       test_commit -C promisor-remote one && # so that ref advertisement is not empty
+       git -C promisor-remote config --local uploadpack.allowanysha1inwant 1 &&
+       git -C promisor-remote hash-object -w --stdin <big-blob.txt &&
+       git -C client remote set-url origin "file://$(pwd)/promisor-remote"
+}
+
+# NEEDSWORK: The tests beginning with "fetch lazy-fetches" below only
+# test that "fetch" avoid fetching trees and blobs, but not commits or
+# tags. Revisit this if Git is ever taught to support partial clones
+# with commits and/or tags filtered out.
+
+test_expect_success 'fetch lazy-fetches only to resolve deltas' '
+       setup_triangle &&
+
+       # Exercise to make sure it works. Git will not fetch anything from the
+       # promisor remote other than for the big blob (because it needs to
+       # resolve the delta).
+       GIT_TRACE_PACKET="$(pwd)/trace" git -C client \
+               fetch "file://$(pwd)/server" master &&
+
+       # Verify the assumption that the client needed to fetch the delta base
+       # to resolve the delta.
+       git hash-object big-blob.txt >hash &&
+       grep "want $(cat hash)" trace
+'
+
+test_expect_success 'fetch lazy-fetches only to resolve deltas, protocol v2' '
+       setup_triangle &&
+
+       git -C server config --local protocol.version 2 &&
+       git -C client config --local protocol.version 2 &&
+       git -C promisor-remote config --local protocol.version 2 &&
+
+       # Exercise to make sure it works. Git will not fetch anything from the
+       # promisor remote other than for the big blob (because it needs to
+       # resolve the delta).
+       GIT_TRACE_PACKET="$(pwd)/trace" git -C client \
+               fetch "file://$(pwd)/server" master &&
+
+       # Verify that protocol version 2 was used.
+       grep "fetch< version 2" trace &&
+
+       # Verify the assumption that the client needed to fetch the delta base
+       # to resolve the delta.
+       git hash-object big-blob.txt >hash &&
+       grep "want $(cat hash)" trace
+'
+
 . "$TEST_DIRECTORY"/lib-httpd.sh
 start_httpd
 
index ae9175cedfb2b5e51acc92ec797014e6459e3c9a..e73067d23fe747210f0ea767a7e8952b81ed3d30 100755 (executable)
@@ -32,7 +32,7 @@ test_expect_success 'list refs with git:// using protocol v2' '
        test_cmp expect actual
 '
 
-test_expect_success 'ref advertisment is filtered with ls-remote using protocol v2' '
+test_expect_success 'ref advertisement is filtered with ls-remote using protocol v2' '
        test_when_finished "rm -f log" &&
 
        GIT_TRACE_PACKET="$(pwd)/log" git -c protocol.version=2 \
@@ -154,7 +154,7 @@ test_expect_success 'list refs with file:// using protocol v2' '
        test_cmp expect actual
 '
 
-test_expect_success 'ref advertisment is filtered with ls-remote using protocol v2' '
+test_expect_success 'ref advertisement is filtered with ls-remote using protocol v2' '
        test_when_finished "rm -f log" &&
 
        GIT_TRACE_PACKET="$(pwd)/log" git -c protocol.version=2 \
@@ -225,7 +225,7 @@ test_expect_success 'fetch with file:// using protocol v2' '
        grep "fetch< version 2" log
 '
 
-test_expect_success 'ref advertisment is filtered during fetch using protocol v2' '
+test_expect_success 'ref advertisement is filtered during fetch using protocol v2' '
        test_when_finished "rm -f log" &&
 
        test_commit -C file_parent three &&
@@ -682,9 +682,9 @@ test_expect_success 'push with http:// and a config of v2 does not request v2' '
        git -C "$HTTPD_DOCUMENT_ROOT_PATH/http_parent" log -1 --format=%s client_branch >expect &&
        test_cmp expect actual &&
 
-       # Client didnt request to use protocol v2
+       # Client did not request to use protocol v2
        ! grep "Git-Protocol: version=2" log &&
-       # Server didnt respond using protocol v2
+       # Server did not respond using protocol v2
        ! grep "git< version 2" log
 '
 
index cfb74d0e03541e136c46e7c84a7e537e19dc1509..ebdc49c4965edecf1d32aba7a3a2346f770bad92 100755 (executable)
@@ -109,31 +109,35 @@ commit $head1
 EOF
 
 # we don't test relative here
-test_format author %an%n%ae%n%ad%n%aD%n%at <<EOF
+test_format author %an%n%ae%n%al%n%ad%n%aD%n%at <<EOF
 commit $head2
-A U Thor
-author@example.com
+$GIT_AUTHOR_NAME
+$GIT_AUTHOR_EMAIL
+$TEST_AUTHOR_LOCALNAME
 Thu Apr 7 15:13:13 2005 -0700
 Thu, 7 Apr 2005 15:13:13 -0700
 1112911993
 commit $head1
-A U Thor
-author@example.com
+$GIT_AUTHOR_NAME
+$GIT_AUTHOR_EMAIL
+$TEST_AUTHOR_LOCALNAME
 Thu Apr 7 15:13:13 2005 -0700
 Thu, 7 Apr 2005 15:13:13 -0700
 1112911993
 EOF
 
-test_format committer %cn%n%ce%n%cd%n%cD%n%ct <<EOF
+test_format committer %cn%n%ce%n%cl%n%cd%n%cD%n%ct <<EOF
 commit $head2
-C O Mitter
-committer@example.com
+$GIT_COMMITTER_NAME
+$GIT_COMMITTER_EMAIL
+$TEST_COMMITTER_LOCALNAME
 Thu Apr 7 15:13:13 2005 -0700
 Thu, 7 Apr 2005 15:13:13 -0700
 1112911993
 commit $head1
-C O Mitter
-committer@example.com
+$GIT_COMMITTER_NAME
+$GIT_COMMITTER_EMAIL
+$TEST_COMMITTER_LOCALNAME
 Thu Apr 7 15:13:13 2005 -0700
 Thu, 7 Apr 2005 15:13:13 -0700
 1112911993
@@ -410,7 +414,7 @@ test_expect_success 'empty email' '
        test_tick &&
        C=$(GIT_AUTHOR_EMAIL= git commit-tree HEAD^{tree} </dev/null) &&
        A=$(git show --pretty=format:%an,%ae,%ad%n -s $C) &&
-       verbose test "$A" = "A U Thor,,Thu Apr 7 15:14:13 2005 -0700"
+       verbose test "$A" = "$GIT_AUTHOR_NAME,,Thu Apr 7 15:14:13 2005 -0700"
 '
 
 test_expect_success 'del LF before empty (1)' '
@@ -495,7 +499,7 @@ test_expect_success '%gd shortens ref name' '
 '
 
 test_expect_success 'reflog identity' '
-       echo "C O Mitter:committer@example.com" >expect &&
+       echo "$GIT_COMMITTER_NAME:$GIT_COMMITTER_EMAIL" >expect &&
        git log -g -1 --format="%gn:%ge" >actual &&
        test_cmp expect actual
 '
index f7181d1d6a143c60a5c4b26960bd42aa2c88035d..f5e6e92f5b30cde2ddbaec3b53d47e67e1881908 100755 (executable)
@@ -67,11 +67,10 @@ test_expect_success '--graph --all' '
        echo "| * $C4" >> expected &&
        echo "| * $C3" >> expected &&
        echo "* | $A5" >> expected &&
-       echo "| |     " >> expected &&
-       echo "|  \\    " >> expected &&
-       echo "*-. \\   $A4" >> expected &&
-       echo "|\\ \\ \\  " >> expected &&
-       echo "| | |/  " >> expected &&
+       echo "| |   " >> expected &&
+       echo "|  \\  " >> expected &&
+       echo "*-. | $A4" >> expected &&
+       echo "|\\ \\| " >> expected &&
        echo "| | * $C2" >> expected &&
        echo "| | * $C1" >> expected &&
        echo "| * | $B2" >> expected &&
@@ -97,11 +96,10 @@ test_expect_success '--graph --simplify-by-decoration' '
        echo "| * $C4" >> expected &&
        echo "| * $C3" >> expected &&
        echo "* | $A5" >> expected &&
-       echo "| |     " >> expected &&
-       echo "|  \\    " >> expected &&
-       echo "*-. \\   $A4" >> expected &&
-       echo "|\\ \\ \\  " >> expected &&
-       echo "| | |/  " >> expected &&
+       echo "| |   " >> expected &&
+       echo "|  \\  " >> expected &&
+       echo "*-. | $A4" >> expected &&
+       echo "|\\ \\| " >> expected &&
        echo "| | * $C2" >> expected &&
        echo "| | * $C1" >> expected &&
        echo "| * | $B2" >> expected &&
@@ -131,9 +129,8 @@ test_expect_success '--graph --simplify-by-decoration prune branch B' '
        echo "| * $C4" >> expected &&
        echo "| * $C3" >> expected &&
        echo "* | $A5" >> expected &&
-       echo "* |   $A4" >> expected &&
-       echo "|\\ \\  " >> expected &&
-       echo "| |/  " >> expected &&
+       echo "* | $A4" >> expected &&
+       echo "|\\| " >> expected &&
        echo "| * $C2" >> expected &&
        echo "| * $C1" >> expected &&
        echo "* | $A3" >> expected &&
@@ -151,9 +148,8 @@ test_expect_success '--graph --full-history -- bar.txt' '
        echo "|\\  " >> expected &&
        echo "| * $C4" >> expected &&
        echo "* | $A5" >> expected &&
-       echo "* |   $A4" >> expected &&
-       echo "|\\ \\  " >> expected &&
-       echo "| |/  " >> expected &&
+       echo "* | $A4" >> expected &&
+       echo "|\\| " >> expected &&
        echo "* | $A3" >> expected &&
        echo "|/  " >> expected &&
        echo "* $A2" >> expected &&
@@ -255,7 +251,7 @@ test_expect_success '--graph --boundary ^C3' '
        echo "* | | | $A3" >> expected &&
        echo "o | | | $A2" >> expected &&
        echo "|/ / /  " >> expected &&
-       echo "o | | $A1" >> expected &&
+       echo "o / / $A1" >> expected &&
        echo " / /  " >> expected &&
        echo "| o $C3" >> expected &&
        echo "|/  " >> expected &&
index 27c7de90ce64aa17594ca8cd43d87e35534aeb01..0c9e3c20e8ce6a88ff5fe672f3fec82adacaa940 100755 (executable)
@@ -14,85 +14,90 @@ test_description='Test merge without common ancestors'
 GIT_COMMITTER_DATE="2006-12-12 23:28:00 +0100"
 export GIT_COMMITTER_DATE
 
-test_expect_success "setup tests" '
-echo 1 > a1 &&
-git add a1 &&
-GIT_AUTHOR_DATE="2006-12-12 23:00:00" git commit -m 1 a1 &&
-
-git checkout -b A master &&
-echo A > a1 &&
-GIT_AUTHOR_DATE="2006-12-12 23:00:01" git commit -m A a1 &&
-
-git checkout -b B master &&
-echo B > a1 &&
-GIT_AUTHOR_DATE="2006-12-12 23:00:02" git commit -m B a1 &&
-
-git checkout -b D A &&
-git rev-parse B > .git/MERGE_HEAD &&
-echo D > a1 &&
-git update-index a1 &&
-GIT_AUTHOR_DATE="2006-12-12 23:00:03" git commit -m D &&
-
-git symbolic-ref HEAD refs/heads/other &&
-echo 2 > a1 &&
-GIT_AUTHOR_DATE="2006-12-12 23:00:04" git commit -m 2 a1 &&
-
-git checkout -b C &&
-echo C > a1 &&
-GIT_AUTHOR_DATE="2006-12-12 23:00:05" git commit -m C a1 &&
-
-git checkout -b E C &&
-git rev-parse B > .git/MERGE_HEAD &&
-echo E > a1 &&
-git update-index a1 &&
-GIT_AUTHOR_DATE="2006-12-12 23:00:06" git commit -m E &&
-
-git checkout -b G E &&
-git rev-parse A > .git/MERGE_HEAD &&
-echo G > a1 &&
-git update-index a1 &&
-GIT_AUTHOR_DATE="2006-12-12 23:00:07" git commit -m G &&
-
-git checkout -b F D &&
-git rev-parse C > .git/MERGE_HEAD &&
-echo F > a1 &&
-git update-index a1 &&
-GIT_AUTHOR_DATE="2006-12-12 23:00:08" git commit -m F
+test_expect_success 'setup tests' '
+       echo 1 >a1 &&
+       git add a1 &&
+       GIT_AUTHOR_DATE="2006-12-12 23:00:00" git commit -m 1 a1 &&
+
+       git checkout -b A master &&
+       echo A >a1 &&
+       GIT_AUTHOR_DATE="2006-12-12 23:00:01" git commit -m A a1 &&
+
+       git checkout -b B master &&
+       echo B >a1 &&
+       GIT_AUTHOR_DATE="2006-12-12 23:00:02" git commit -m B a1 &&
+
+       git checkout -b D A &&
+       git rev-parse B >.git/MERGE_HEAD &&
+       echo D >a1 &&
+       git update-index a1 &&
+       GIT_AUTHOR_DATE="2006-12-12 23:00:03" git commit -m D &&
+
+       git symbolic-ref HEAD refs/heads/other &&
+       echo 2 >a1 &&
+       GIT_AUTHOR_DATE="2006-12-12 23:00:04" git commit -m 2 a1 &&
+
+       git checkout -b C &&
+       echo C >a1 &&
+       GIT_AUTHOR_DATE="2006-12-12 23:00:05" git commit -m C a1 &&
+
+       git checkout -b E C &&
+       git rev-parse B >.git/MERGE_HEAD &&
+       echo E >a1 &&
+       git update-index a1 &&
+       GIT_AUTHOR_DATE="2006-12-12 23:00:06" git commit -m E &&
+
+       git checkout -b G E &&
+       git rev-parse A >.git/MERGE_HEAD &&
+       echo G >a1 &&
+       git update-index a1 &&
+       GIT_AUTHOR_DATE="2006-12-12 23:00:07" git commit -m G &&
+
+       git checkout -b F D &&
+       git rev-parse C >.git/MERGE_HEAD &&
+       echo F >a1 &&
+       git update-index a1 &&
+       GIT_AUTHOR_DATE="2006-12-12 23:00:08" git commit -m F
 '
 
 test_expect_success 'combined merge conflicts' '
        test_must_fail env GIT_TEST_COMMIT_GRAPH=0 git merge -m final G
 '
 
-cat > expect << EOF
-<<<<<<< HEAD
-F
-=======
-G
->>>>>>> G
-EOF
+test_expect_success 'result contains a conflict' '
+       cat >expect <<-\EOF &&
+       <<<<<<< HEAD
+       F
+       =======
+       G
+       >>>>>>> G
+       EOF
 
-test_expect_success "result contains a conflict" "test_cmp expect a1"
+       test_cmp expect a1
+'
+
+test_expect_success 'virtual trees were processed' '
+       git ls-files --stage >out &&
 
-git ls-files --stage > out
-cat > expect << EOF
-100644 ec3fe2a791706733f2d8fa7ad45d9a9672031f5e 1      a1
-100644 cf84443e49e1b366fac938711ddf4be2d4d1d9e9 2      a1
-100644 fd7923529855d0b274795ae3349c5e0438333979 3      a1
-EOF
+       cat >expect <<-\EOF &&
+       100644 ec3fe2a791706733f2d8fa7ad45d9a9672031f5e 1       a1
+       100644 cf84443e49e1b366fac938711ddf4be2d4d1d9e9 2       a1
+       100644 fd7923529855d0b274795ae3349c5e0438333979 3       a1
+       EOF
 
-test_expect_success "virtual trees were processed" "test_cmp expect out"
+       test_cmp expect out
+'
 
 test_expect_success 'refuse to merge binary files' '
        git reset --hard &&
-       printf "\0" > binary-file &&
+       printf "\0" >binary-file &&
        git add binary-file &&
        git commit -m binary &&
        git checkout G &&
-       printf "\0\0" > binary-file &&
+       printf "\0\0" >binary-file &&
        git add binary-file &&
        git commit -m binary2 &&
-       test_must_fail git merge F > merge.out 2> merge.err &&
+       test_must_fail git merge F >merge.out 2>merge.err &&
        grep "Cannot merge binary files: binary-file (HEAD vs. F)" merge.err
 '
 
@@ -116,7 +121,6 @@ test_expect_success 'mark rename/delete as unmerged' '
        test 1 = $(git ls-files --unmerged | wc -l) &&
        test_must_fail git rev-parse --verify :2:a2 &&
        git rev-parse --verify :3:a2
-
 '
 
 test_done
index 7fddcc8c73c9cf3d2f43f2c7b1310b02dc06b6e7..7d73afdcdaafea81ed09b163ea425a9c881a8a9f 100755 (executable)
@@ -1532,7 +1532,7 @@ test_expect_success 'setup nested conflicts' '
                mv -f b_R1 b &&
                mv -f a_R1 a &&
                git add b a &&
-               test_tick && git commit -m "verson R1 of files" &&
+               test_tick && git commit -m "version R1 of files" &&
                git tag R1 &&
 
                # Create first merge on left side
@@ -1696,7 +1696,7 @@ test_expect_success 'setup virtual merge base with nested conflicts' '
                git checkout R &&
                echo right >>content &&
                git add content &&
-               test_tick && git commit -m "verson R1 of content" &&
+               test_tick && git commit -m "version R1 of content" &&
                git tag R1 &&
 
                # Create L2
index c5b57f40c3d127cffda505f9ffe91effffafb135..b047cf1c1c3735faa99e3a3b2453503f4f9c4a8f 100755 (executable)
@@ -5,7 +5,7 @@ test_description="recursive merge corner cases w/ renames but not criss-crosses"
 
 . ./test-lib.sh
 
-test_expect_success 'setup rename/delete + untracked file' '
+test_setup_rename_delete_untracked () {
        test_create_repo rename-delete-untracked &&
        (
                cd rename-delete-untracked &&
@@ -29,9 +29,10 @@ test_expect_success 'setup rename/delete + untracked file' '
                git commit -m track-people-instead-of-objects &&
                echo "Myyy PRECIOUSSS" >ring
        )
-'
+}
 
 test_expect_success "Does git preserve Gollum's precious artifact?" '
+       test_setup_rename_delete_untracked &&
        (
                cd rename-delete-untracked &&
 
@@ -49,7 +50,7 @@ test_expect_success "Does git preserve Gollum's precious artifact?" '
 #
 # We should be able to merge B & C cleanly
 
-test_expect_success 'setup rename/modify/add-source conflict' '
+test_setup_rename_modify_add_source () {
        test_create_repo rename-modify-add-source &&
        (
                cd rename-modify-add-source &&
@@ -70,9 +71,10 @@ test_expect_success 'setup rename/modify/add-source conflict' '
                git add a &&
                git commit -m C
        )
-'
+}
 
 test_expect_failure 'rename/modify/add-source conflict resolvable' '
+       test_setup_rename_modify_add_source &&
        (
                cd rename-modify-add-source &&
 
@@ -88,7 +90,7 @@ test_expect_failure 'rename/modify/add-source conflict resolvable' '
        )
 '
 
-test_expect_success 'setup resolvable conflict missed if rename missed' '
+test_setup_break_detection_1 () {
        test_create_repo break-detection-1 &&
        (
                cd break-detection-1 &&
@@ -110,9 +112,10 @@ test_expect_success 'setup resolvable conflict missed if rename missed' '
                git add a &&
                git commit -m C
        )
-'
+}
 
 test_expect_failure 'conflict caused if rename not detected' '
+       test_setup_break_detection_1 &&
        (
                cd break-detection-1 &&
 
@@ -135,7 +138,7 @@ test_expect_failure 'conflict caused if rename not detected' '
        )
 '
 
-test_expect_success 'setup conflict resolved wrong if rename missed' '
+test_setup_break_detection_2 () {
        test_create_repo break-detection-2 &&
        (
                cd break-detection-2 &&
@@ -160,9 +163,10 @@ test_expect_success 'setup conflict resolved wrong if rename missed' '
                git add a &&
                git commit -m E
        )
-'
+}
 
 test_expect_failure 'missed conflict if rename not detected' '
+       test_setup_break_detection_2 &&
        (
                cd break-detection-2 &&
 
@@ -182,7 +186,7 @@ test_expect_failure 'missed conflict if rename not detected' '
 #   Commit B: rename a->b
 #   Commit C: rename a->b, add unrelated a
 
-test_expect_success 'setup undetected rename/add-source causes data loss' '
+test_setup_break_detection_3 () {
        test_create_repo break-detection-3 &&
        (
                cd break-detection-3 &&
@@ -202,9 +206,10 @@ test_expect_success 'setup undetected rename/add-source causes data loss' '
                git add a &&
                git commit -m C
        )
-'
+}
 
 test_expect_failure 'detect rename/add-source and preserve all data' '
+       test_setup_break_detection_3 &&
        (
                cd break-detection-3 &&
 
@@ -231,6 +236,7 @@ test_expect_failure 'detect rename/add-source and preserve all data' '
 '
 
 test_expect_failure 'detect rename/add-source and preserve all data, merge other way' '
+       test_setup_break_detection_3 &&
        (
                cd break-detection-3 &&
 
@@ -256,10 +262,10 @@ test_expect_failure 'detect rename/add-source and preserve all data, merge other
        )
 '
 
-test_expect_success 'setup content merge + rename/directory conflict' '
-       test_create_repo rename-directory-1 &&
+test_setup_rename_directory () {
+       test_create_repo rename-directory-$1 &&
        (
-               cd rename-directory-1 &&
+               cd rename-directory-$1 &&
 
                printf "1\n2\n3\n4\n5\n6\n" >file &&
                git add file &&
@@ -290,11 +296,12 @@ test_expect_success 'setup content merge + rename/directory conflict' '
                test_tick &&
                git commit -m left
        )
-'
+}
 
 test_expect_success 'rename/directory conflict + clean content merge' '
+       test_setup_rename_directory 1a &&
        (
-               cd rename-directory-1 &&
+               cd rename-directory-1a &&
 
                git checkout left-clean^0 &&
 
@@ -320,8 +327,9 @@ test_expect_success 'rename/directory conflict + clean content merge' '
 '
 
 test_expect_success 'rename/directory conflict + content merge conflict' '
+       test_setup_rename_directory 1b &&
        (
-               cd rename-directory-1 &&
+               cd rename-directory-1b &&
 
                git reset --hard &&
                git clean -fdqx &&
@@ -358,7 +366,7 @@ test_expect_success 'rename/directory conflict + content merge conflict' '
        )
 '
 
-test_expect_success 'setup content merge + rename/directory conflict w/ disappearing dir' '
+test_setup_rename_directory_2 () {
        test_create_repo rename-directory-2 &&
        (
                cd rename-directory-2 &&
@@ -385,9 +393,10 @@ test_expect_success 'setup content merge + rename/directory conflict w/ disappea
                test_tick &&
                git commit -m left
        )
-'
+}
 
 test_expect_success 'disappearing dir in rename/directory conflict handled' '
+       test_setup_rename_directory_2 &&
        (
                cd rename-directory-2 &&
 
@@ -416,10 +425,10 @@ test_expect_success 'disappearing dir in rename/directory conflict handled' '
 #   Commit A: rename a->b, modifying b too
 #   Commit B: modify a, add different b
 
-test_expect_success 'setup rename-with-content-merge vs. add' '
-       test_create_repo rename-with-content-merge-and-add &&
+test_setup_rename_with_content_merge_and_add () {
+       test_create_repo rename-with-content-merge-and-add-$1 &&
        (
-               cd rename-with-content-merge-and-add &&
+               cd rename-with-content-merge-and-add-$1 &&
 
                test_seq 1 5 >a &&
                git add a &&
@@ -438,11 +447,12 @@ test_expect_success 'setup rename-with-content-merge vs. add' '
                git add a b &&
                git commit -m B
        )
-'
+}
 
 test_expect_success 'handle rename-with-content-merge vs. add' '
+       test_setup_rename_with_content_merge_and_add AB &&
        (
-               cd rename-with-content-merge-and-add &&
+               cd rename-with-content-merge-and-add-AB &&
 
                git checkout A^0 &&
 
@@ -483,8 +493,9 @@ test_expect_success 'handle rename-with-content-merge vs. add' '
 '
 
 test_expect_success 'handle rename-with-content-merge vs. add, merge other way' '
+       test_setup_rename_with_content_merge_and_add BA &&
        (
-               cd rename-with-content-merge-and-add &&
+               cd rename-with-content-merge-and-add-BA &&
 
                git reset --hard &&
                git clean -fdx &&
@@ -539,7 +550,7 @@ test_expect_success 'handle rename-with-content-merge vs. add, merge other way'
 #   * The working copy should have two files, both of form c~<unique>; does it?
 #   * Nothing else should be present.  Is anything?
 
-test_expect_success 'setup rename/rename (2to1) + modify/modify' '
+test_setup_rename_rename_2to1 () {
        test_create_repo rename-rename-2to1 &&
        (
                cd rename-rename-2to1 &&
@@ -562,9 +573,10 @@ test_expect_success 'setup rename/rename (2to1) + modify/modify' '
                git add a &&
                git commit -m C
        )
-'
+}
 
 test_expect_success 'handle rename/rename (2to1) conflict correctly' '
+       test_setup_rename_rename_2to1 &&
        (
                cd rename-rename-2to1 &&
 
@@ -610,7 +622,7 @@ test_expect_success 'handle rename/rename (2to1) conflict correctly' '
 #   Commit A: new file: a
 #   Commit B: rename a->b
 #   Commit C: rename a->c
-test_expect_success 'setup simple rename/rename (1to2) conflict' '
+test_setup_rename_rename_1to2 () {
        test_create_repo rename-rename-1to2 &&
        (
                cd rename-rename-1to2 &&
@@ -631,9 +643,10 @@ test_expect_success 'setup simple rename/rename (1to2) conflict' '
                test_tick &&
                git commit -m C
        )
-'
+}
 
 test_expect_success 'merge has correct working tree contents' '
+       test_setup_rename_rename_1to2 &&
        (
                cd rename-rename-1to2 &&
 
@@ -667,7 +680,7 @@ test_expect_success 'merge has correct working tree contents' '
 #
 # Merging of B & C should NOT be clean; there's a rename/rename conflict
 
-test_expect_success 'setup rename/rename(1to2)/add-source conflict' '
+test_setup_rename_rename_1to2_add_source_1 () {
        test_create_repo rename-rename-1to2-add-source-1 &&
        (
                cd rename-rename-1to2-add-source-1 &&
@@ -687,9 +700,10 @@ test_expect_success 'setup rename/rename(1to2)/add-source conflict' '
                git add a &&
                git commit -m C
        )
-'
+}
 
 test_expect_failure 'detect conflict with rename/rename(1to2)/add-source merge' '
+       test_setup_rename_rename_1to2_add_source_1 &&
        (
                cd rename-rename-1to2-add-source-1 &&
 
@@ -714,7 +728,7 @@ test_expect_failure 'detect conflict with rename/rename(1to2)/add-source merge'
        )
 '
 
-test_expect_success 'setup rename/rename(1to2)/add-source resolvable conflict' '
+test_setup_rename_rename_1to2_add_source_2 () {
        test_create_repo rename-rename-1to2-add-source-2 &&
        (
                cd rename-rename-1to2-add-source-2 &&
@@ -737,9 +751,10 @@ test_expect_success 'setup rename/rename(1to2)/add-source resolvable conflict' '
                test_tick &&
                git commit -m two
        )
-'
+}
 
 test_expect_failure 'rename/rename/add-source still tracks new a file' '
+       test_setup_rename_rename_1to2_add_source_2 &&
        (
                cd rename-rename-1to2-add-source-2 &&
 
@@ -759,7 +774,7 @@ test_expect_failure 'rename/rename/add-source still tracks new a file' '
        )
 '
 
-test_expect_success 'setup rename/rename(1to2)/add-dest conflict' '
+test_setup_rename_rename_1to2_add_dest () {
        test_create_repo rename-rename-1to2-add-dest &&
        (
                cd rename-rename-1to2-add-dest &&
@@ -784,9 +799,10 @@ test_expect_success 'setup rename/rename(1to2)/add-dest conflict' '
                test_tick &&
                git commit -m two
        )
-'
+}
 
 test_expect_success 'rename/rename/add-dest merge still knows about conflicting file versions' '
+       test_setup_rename_rename_1to2_add_dest &&
        (
                cd rename-rename-1to2-add-dest &&
 
@@ -838,7 +854,7 @@ test_expect_success 'rename/rename/add-dest merge still knows about conflicting
 #   Commit B: rename foo->bar
 #   Expected: CONFLICT (rename/add/delete), two-way merged bar
 
-test_expect_success 'rad-setup: rename/add/delete conflict' '
+test_setup_rad () {
        test_create_repo rad &&
        (
                cd rad &&
@@ -860,9 +876,10 @@ test_expect_success 'rad-setup: rename/add/delete conflict' '
                git mv foo bar &&
                git commit -m "rename foo to bar"
        )
-'
+}
 
 test_expect_failure 'rad-check: rename/add/delete conflict' '
+       test_setup_rad &&
        (
                cd rad &&
 
@@ -904,7 +921,7 @@ test_expect_failure 'rad-check: rename/add/delete conflict' '
 #   Commit B: rename bar->baz, rm foo
 #   Expected: CONFLICT (rename/rename/delete/delete), two-way merged baz
 
-test_expect_success 'rrdd-setup: rename/rename(2to1)/delete/delete conflict' '
+test_setup_rrdd () {
        test_create_repo rrdd &&
        (
                cd rrdd &&
@@ -927,9 +944,10 @@ test_expect_success 'rrdd-setup: rename/rename(2to1)/delete/delete conflict' '
                git rm foo &&
                git commit -m "Rename bar, remove foo"
        )
-'
+}
 
 test_expect_failure 'rrdd-check: rename/rename(2to1)/delete/delete conflict' '
+       test_setup_rrdd &&
        (
                cd rrdd &&
 
@@ -973,7 +991,7 @@ test_expect_failure 'rrdd-check: rename/rename(2to1)/delete/delete conflict' '
 #   Expected: six CONFLICT(rename/rename) messages, each path in two of the
 #             multi-way merged contents found in two, four, six
 
-test_expect_success 'mod6-setup: chains of rename/rename(1to2) and rename/rename(2to1)' '
+test_setup_mod6 () {
        test_create_repo mod6 &&
        (
                cd mod6 &&
@@ -1009,9 +1027,10 @@ test_expect_success 'mod6-setup: chains of rename/rename(1to2) and rename/rename
                test_tick &&
                git commit -m "B"
        )
-'
+}
 
 test_expect_failure 'mod6-check: chains of rename/rename(1to2) and rename/rename(2to1)' '
+       test_setup_mod6 &&
        (
                cd mod6 &&
 
@@ -1108,7 +1127,8 @@ test_conflicts_with_adds_and_renames() {
        #      files.  Is it present?
        #   4) There should not be any three~* files in the working
        #      tree
-       test_expect_success "setup simple $sideL/$sideR conflict" '
+       test_setup_collision_conflict () {
+       #test_expect_success "setup simple $sideL/$sideR conflict" '
                test_create_repo simple_${sideL}_${sideR} &&
                (
                        cd simple_${sideL}_${sideR} &&
@@ -1185,9 +1205,11 @@ test_conflicts_with_adds_and_renames() {
                        fi &&
                        test_tick && git commit -m R
                )
-       '
+       #'
+       }
 
        test_expect_success "check simple $sideL/$sideR conflict" '
+               test_setup_collision_conflict &&
                (
                        cd simple_${sideL}_${sideR} &&
 
@@ -1254,7 +1276,7 @@ test_conflicts_with_adds_and_renames add    add
 #
 #   So, we have four different conflicting files that all end up at path
 #   'three'.
-test_expect_success 'setup nested conflicts from rename/rename(2to1)' '
+test_setup_nested_conflicts_from_rename_rename () {
        test_create_repo nested_conflicts_from_rename_rename &&
        (
                cd nested_conflicts_from_rename_rename &&
@@ -1305,9 +1327,10 @@ test_expect_success 'setup nested conflicts from rename/rename(2to1)' '
                git add one three &&
                test_tick && git commit -m german
        )
-'
+}
 
 test_expect_success 'check nested conflicts from rename/rename(2to1)' '
+       test_setup_nested_conflicts_from_rename_rename &&
        (
                cd nested_conflicts_from_rename_rename &&
 
index c966147d5d73ee669321f3efcf57e32a80259076..83792c5ef1f5ebbdd49bb1455b262ef9d01145f9 100755 (executable)
@@ -38,7 +38,7 @@ test_description="recursive merge with directory renames"
 #   Commit B: z/{b,c,d,e/f}
 #   Expected: y/{b,c,d,e/f}
 
-test_expect_success '1a-setup: Simple directory rename detection' '
+test_setup_1a () {
        test_create_repo 1a &&
        (
                cd 1a &&
@@ -67,9 +67,10 @@ test_expect_success '1a-setup: Simple directory rename detection' '
                test_tick &&
                git commit -m "B"
        )
-'
+}
 
-test_expect_success '1a-check: Simple directory rename detection' '
+test_expect_success '1a: Simple directory rename detection' '
+       test_setup_1a &&
        (
                cd 1a &&
 
@@ -103,7 +104,7 @@ test_expect_success '1a-check: Simple directory rename detection' '
 #   Commit B: y/{b,c,d}
 #   Expected: y/{b,c,d,e}
 
-test_expect_success '1b-setup: Merge a directory with another' '
+test_setup_1b () {
        test_create_repo 1b &&
        (
                cd 1b &&
@@ -134,9 +135,10 @@ test_expect_success '1b-setup: Merge a directory with another' '
                test_tick &&
                git commit -m "B"
        )
-'
+}
 
-test_expect_success '1b-check: Merge a directory with another' '
+test_expect_success '1b: Merge a directory with another' '
+       test_setup_1b &&
        (
                cd 1b &&
 
@@ -165,7 +167,7 @@ test_expect_success '1b-check: Merge a directory with another' '
 #   Commit B: z/{b,c,d}
 #   Expected: y/{b,c,d}  (because x/d -> z/d -> y/d)
 
-test_expect_success '1c-setup: Transitive renaming' '
+test_setup_1c () {
        test_create_repo 1c &&
        (
                cd 1c &&
@@ -193,9 +195,10 @@ test_expect_success '1c-setup: Transitive renaming' '
                test_tick &&
                git commit -m "B"
        )
-'
+}
 
-test_expect_success '1c-check: Transitive renaming' '
+test_expect_success '1c: Transitive renaming' '
+       test_setup_1c &&
        (
                cd 1c &&
 
@@ -227,7 +230,7 @@ test_expect_success '1c-check: Transitive renaming' '
 #   Note: y/m & z/n should definitely move into x.  By the same token, both
 #         y/wham_1 & z/wham_2 should too...giving us a conflict.
 
-test_expect_success '1d-setup: Directory renames cause a rename/rename(2to1) conflict' '
+test_setup_1d () {
        test_create_repo 1d &&
        (
                cd 1d &&
@@ -262,9 +265,10 @@ test_expect_success '1d-setup: Directory renames cause a rename/rename(2to1) con
                test_tick &&
                git commit -m "B"
        )
-'
+}
 
-test_expect_success '1d-check: Directory renames cause a rename/rename(2to1) conflict' '
+test_expect_success '1d: Directory renames cause a rename/rename(2to1) conflict' '
+       test_setup_1d &&
        (
                cd 1d &&
 
@@ -313,7 +317,7 @@ test_expect_success '1d-check: Directory renames cause a rename/rename(2to1) con
 #   Commit B: z/{oldb,oldc,d}
 #   Expected: y/{newb,newc,d}
 
-test_expect_success '1e-setup: Renamed directory, with all files being renamed too' '
+test_setup_1e () {
        test_create_repo 1e &&
        (
                cd 1e &&
@@ -342,9 +346,10 @@ test_expect_success '1e-setup: Renamed directory, with all files being renamed t
                test_tick &&
                git commit -m "B"
        )
-'
+}
 
-test_expect_success '1e-check: Renamed directory, with all files being renamed too' '
+test_expect_success '1e: Renamed directory, with all files being renamed too' '
+       test_setup_1e &&
        (
                cd 1e &&
 
@@ -371,7 +376,7 @@ test_expect_success '1e-check: Renamed directory, with all files being renamed t
 #   Commit B: y/{b,c}, x/{d,e,f}
 #   Expected: y/{b,c}, x/{d,e,f,g}
 
-test_expect_success '1f-setup: Split a directory into two other directories' '
+test_setup_1f () {
        test_create_repo 1f &&
        (
                cd 1f &&
@@ -408,9 +413,10 @@ test_expect_success '1f-setup: Split a directory into two other directories' '
                test_tick &&
                git commit -m "B"
        )
-'
+}
 
-test_expect_success '1f-check: Split a directory into two other directories' '
+test_expect_success '1f: Split a directory into two other directories' '
+       test_setup_1f &&
        (
                cd 1f &&
 
@@ -459,7 +465,7 @@ test_expect_success '1f-check: Split a directory into two other directories' '
 #   Commit A: y/b, w/c
 #   Commit B: z/{b,c,d}
 #   Expected: y/b, w/c, z/d, with warning about z/ -> (y/ vs. w/) conflict
-test_expect_success '2a-setup: Directory split into two on one side, with equal numbers of paths' '
+test_setup_2a () {
        test_create_repo 2a &&
        (
                cd 2a &&
@@ -489,9 +495,10 @@ test_expect_success '2a-setup: Directory split into two on one side, with equal
                test_tick &&
                git commit -m "B"
        )
-'
+}
 
-test_expect_success '2a-check: Directory split into two on one side, with equal numbers of paths' '
+test_expect_success '2a: Directory split into two on one side, with equal numbers of paths' '
+       test_setup_2a &&
        (
                cd 2a &&
 
@@ -520,7 +527,7 @@ test_expect_success '2a-check: Directory split into two on one side, with equal
 #   Commit A: y/b, w/c
 #   Commit B: z/{b,c}, x/d
 #   Expected: y/b, w/c, x/d; No warning about z/ -> (y/ vs. w/) conflict
-test_expect_success '2b-setup: Directory split into two on one side, with equal numbers of paths' '
+test_setup_2b () {
        test_create_repo 2b &&
        (
                cd 2b &&
@@ -551,9 +558,10 @@ test_expect_success '2b-setup: Directory split into two on one side, with equal
                test_tick &&
                git commit -m "B"
        )
-'
+}
 
-test_expect_success '2b-check: Directory split into two on one side, with equal numbers of paths' '
+test_expect_success '2b: Directory split into two on one side, with equal numbers of paths' '
+       test_setup_2b &&
        (
                cd 2b &&
 
@@ -601,7 +609,7 @@ test_expect_success '2b-check: Directory split into two on one side, with equal
 #   Commit A: z/{b,c,d} (no change)
 #   Commit B: y/{b,c}, x/d
 #   Expected: y/{b,c}, x/d
-test_expect_success '3a-setup: Avoid implicit rename if involved as source on other side' '
+test_setup_3a () {
        test_create_repo 3a &&
        (
                cd 3a &&
@@ -632,9 +640,10 @@ test_expect_success '3a-setup: Avoid implicit rename if involved as source on ot
                test_tick &&
                git commit -m "B"
        )
-'
+}
 
-test_expect_success '3a-check: Avoid implicit rename if involved as source on other side' '
+test_expect_success '3a: Avoid implicit rename if involved as source on other side' '
+       test_setup_3a &&
        (
                cd 3a &&
 
@@ -664,7 +673,7 @@ test_expect_success '3a-check: Avoid implicit rename if involved as source on ot
 #         get it involved in directory rename detection.  If it were, we might
 #         end up with CONFLICT:(z/d -> y/d vs. x/d vs. w/d), i.e. a
 #         rename/rename/rename(1to3) conflict, which is just weird.
-test_expect_success '3b-setup: Avoid implicit rename if involved as source on current side' '
+test_setup_3b () {
        test_create_repo 3b &&
        (
                cd 3b &&
@@ -697,9 +706,10 @@ test_expect_success '3b-setup: Avoid implicit rename if involved as source on cu
                test_tick &&
                git commit -m "B"
        )
-'
+}
 
-test_expect_success '3b-check: Avoid implicit rename if involved as source on current side' '
+test_expect_success '3b: Avoid implicit rename if involved as source on current side' '
+       test_setup_3b &&
        (
                cd 3b &&
 
@@ -744,7 +754,7 @@ test_expect_success '3b-check: Avoid implicit rename if involved as source on cu
 #
 # What if we were to attempt to do directory rename detection when someone
 # "mostly" moved a directory but still left some files around, or,
-# equivalently, fully renamed a directory in one commmit and then recreated
+# equivalently, fully renamed a directory in one commit and then recreated
 # that directory in a later commit adding some new files and then tried to
 # merge?
 #
@@ -786,7 +796,7 @@ test_expect_success '3b-check: Avoid implicit rename if involved as source on cu
 #   Expected: y/{b,c,d}, z/{e,f}
 #   NOTE: Even though most files from z moved to y, we don't want f to follow.
 
-test_expect_success '4a-setup: Directory split, with original directory still present' '
+test_setup_4a () {
        test_create_repo 4a &&
        (
                cd 4a &&
@@ -818,9 +828,10 @@ test_expect_success '4a-setup: Directory split, with original directory still pr
                test_tick &&
                git commit -m "B"
        )
-'
+}
 
-test_expect_success '4a-check: Directory split, with original directory still present' '
+test_expect_success '4a: Directory split, with original directory still present' '
+       test_setup_4a &&
        (
                cd 4a &&
 
@@ -874,7 +885,7 @@ test_expect_success '4a-check: Directory split, with original directory still pr
 #         of history, giving us no way to represent this conflict in the
 #         index.
 
-test_expect_success '5a-setup: Merge directories, other side adds files to original and target' '
+test_setup_5a () {
        test_create_repo 5a &&
        (
                cd 5a &&
@@ -907,9 +918,10 @@ test_expect_success '5a-setup: Merge directories, other side adds files to origi
                test_tick &&
                git commit -m "B"
        )
-'
+}
 
-test_expect_success '5a-check: Merge directories, other side adds files to original and target' '
+test_expect_success '5a: Merge directories, other side adds files to original and target' '
+       test_setup_5a &&
        (
                cd 5a &&
 
@@ -941,14 +953,14 @@ test_expect_success '5a-check: Merge directories, other side adds files to origi
 #   Commit B: z/{b,c,d_1,e}, y/d_3
 #   Expected: y/{b,c,e}, CONFLICT(add/add: y/d_2 vs. y/d_3)
 #   NOTE: If z/d_1 in commit B were to be involved in dir rename detection, as
-#         we normaly would since z/ is being renamed to y/, then this would be
+#         we normally would since z/ is being renamed to y/, then this would be
 #         a rename/delete (z/d_1 -> y/d_1 vs. deleted) AND an add/add/add
 #         conflict of y/d_1 vs. y/d_2 vs. y/d_3.  Add/add/add is not
 #         representable in the index, so the existence of y/d_3 needs to
 #         cause us to bail on directory rename detection for that path, falling
 #         back to git behavior without the directory rename detection.
 
-test_expect_success '5b-setup: Rename/delete in order to get add/add/add conflict' '
+test_setup_5b () {
        test_create_repo 5b &&
        (
                cd 5b &&
@@ -981,9 +993,10 @@ test_expect_success '5b-setup: Rename/delete in order to get add/add/add conflic
                test_tick &&
                git commit -m "B"
        )
-'
+}
 
-test_expect_success '5b-check: Rename/delete in order to get add/add/add conflict' '
+test_expect_success '5b: Rename/delete in order to get add/add/add conflict' '
+       test_setup_5b &&
        (
                cd 5b &&
 
@@ -1024,7 +1037,7 @@ test_expect_success '5b-check: Rename/delete in order to get add/add/add conflic
 #             y/d are y/d_2 and y/d_4.  We still do the move from z/e to y/e,
 #             though, because it doesn't have anything in the way.
 
-test_expect_success '5c-setup: Transitive rename would cause rename/rename/rename/add/add/add' '
+test_setup_5c () {
        test_create_repo 5c &&
        (
                cd 5c &&
@@ -1061,9 +1074,10 @@ test_expect_success '5c-setup: Transitive rename would cause rename/rename/renam
                test_tick &&
                git commit -m "B"
        )
-'
+}
 
-test_expect_success '5c-check: Transitive rename would cause rename/rename/rename/add/add/add' '
+test_expect_success '5c: Transitive rename would cause rename/rename/rename/add/add/add' '
+       test_setup_5c &&
        (
                cd 5c &&
 
@@ -1113,7 +1127,7 @@ test_expect_success '5c-check: Transitive rename would cause rename/rename/renam
 #         detection for z/d_2, but that doesn't prevent us from applying the
 #         directory rename detection for z/f -> y/f.
 
-test_expect_success '5d-setup: Directory/file/file conflict due to directory rename' '
+test_setup_5d () {
        test_create_repo 5d &&
        (
                cd 5d &&
@@ -1145,9 +1159,10 @@ test_expect_success '5d-setup: Directory/file/file conflict due to directory ren
                test_tick &&
                git commit -m "B"
        )
-'
+}
 
-test_expect_success '5d-check: Directory/file/file conflict due to directory rename' '
+test_expect_success '5d: Directory/file/file conflict due to directory rename' '
+       test_setup_5d &&
        (
                cd 5d &&
 
@@ -1205,7 +1220,7 @@ test_expect_success '5d-check: Directory/file/file conflict due to directory ren
 #         them under y/ doesn't accidentally catch z/d and make it look like
 #         it is also involved in a rename/delete conflict.
 
-test_expect_success '6a-setup: Tricky rename/delete' '
+test_setup_6a () {
        test_create_repo 6a &&
        (
                cd 6a &&
@@ -1235,9 +1250,10 @@ test_expect_success '6a-setup: Tricky rename/delete' '
                test_tick &&
                git commit -m "B"
        )
-'
+}
 
-test_expect_success '6a-check: Tricky rename/delete' '
+test_expect_success '6a: Tricky rename/delete' '
+       test_setup_6a &&
        (
                cd 6a &&
 
@@ -1271,7 +1287,7 @@ test_expect_success '6a-check: Tricky rename/delete' '
 #         but B did that rename and still decided to put the file into z/,
 #         so we probably shouldn't apply directory rename detection for it.
 
-test_expect_success '6b-setup: Same rename done on both sides' '
+test_setup_6b () {
        test_create_repo 6b &&
        (
                cd 6b &&
@@ -1300,9 +1316,10 @@ test_expect_success '6b-setup: Same rename done on both sides' '
                test_tick &&
                git commit -m "B"
        )
-'
+}
 
-test_expect_success '6b-check: Same rename done on both sides' '
+test_expect_success '6b: Same rename done on both sides' '
+       test_setup_6b &&
        (
                cd 6b &&
 
@@ -1334,7 +1351,7 @@ test_expect_success '6b-check: Same rename done on both sides' '
 #   NOTE: Seems obvious, but just checking that the implementation doesn't
 #         "accidentally detect a rename" and give us y/{b,c,d}.
 
-test_expect_success '6c-setup: Rename only done on same side' '
+test_setup_6c () {
        test_create_repo 6c &&
        (
                cd 6c &&
@@ -1362,9 +1379,10 @@ test_expect_success '6c-setup: Rename only done on same side' '
                test_tick &&
                git commit -m "B"
        )
-'
+}
 
-test_expect_success '6c-check: Rename only done on same side' '
+test_expect_success '6c: Rename only done on same side' '
+       test_setup_6c &&
        (
                cd 6c &&
 
@@ -1396,7 +1414,7 @@ test_expect_success '6c-check: Rename only done on same side' '
 #   NOTE: Again, this seems obvious but just checking that the implementation
 #         doesn't "accidentally detect a rename" and give us y/{b,c,d}.
 
-test_expect_success '6d-setup: We do not always want transitive renaming' '
+test_setup_6d () {
        test_create_repo 6d &&
        (
                cd 6d &&
@@ -1424,9 +1442,10 @@ test_expect_success '6d-setup: We do not always want transitive renaming' '
                test_tick &&
                git commit -m "B"
        )
-'
+}
 
-test_expect_success '6d-check: We do not always want transitive renaming' '
+test_expect_success '6d: We do not always want transitive renaming' '
+       test_setup_6d &&
        (
                cd 6d &&
 
@@ -1458,7 +1477,7 @@ test_expect_success '6d-check: We do not always want transitive renaming' '
 #         doesn't "accidentally detect a rename" and give us y/{b,c} +
 #         add/add conflict on y/d_1 vs y/d_2.
 
-test_expect_success '6e-setup: Add/add from one side' '
+test_setup_6e () {
        test_create_repo 6e &&
        (
                cd 6e &&
@@ -1487,9 +1506,10 @@ test_expect_success '6e-setup: Add/add from one side' '
                test_tick &&
                git commit -m "B"
        )
-'
+}
 
-test_expect_success '6e-check: Add/add from one side' '
+test_expect_success '6e: Add/add from one side' '
+       test_setup_6e &&
        (
                cd 6e &&
 
@@ -1552,7 +1572,7 @@ test_expect_success '6e-check: Add/add from one side' '
 #   Expected: y/d, CONFLICT(rename/rename for both z/b and z/c)
 #   NOTE: There's a rename of z/ here, y/ has more renames, so z/d -> y/d.
 
-test_expect_success '7a-setup: rename-dir vs. rename-dir (NOT split evenly) PLUS add-other-file' '
+test_setup_7a () {
        test_create_repo 7a &&
        (
                cd 7a &&
@@ -1583,9 +1603,10 @@ test_expect_success '7a-setup: rename-dir vs. rename-dir (NOT split evenly) PLUS
                test_tick &&
                git commit -m "B"
        )
-'
+}
 
-test_expect_success '7a-check: rename-dir vs. rename-dir (NOT split evenly) PLUS add-other-file' '
+test_expect_success '7a: rename-dir vs. rename-dir (NOT split evenly) PLUS add-other-file' '
+       test_setup_7a &&
        (
                cd 7a &&
 
@@ -1623,7 +1644,7 @@ test_expect_success '7a-check: rename-dir vs. rename-dir (NOT split evenly) PLUS
 #   Commit B: z/{b,c,d_1},        w/d_2
 #   Expected: y/{b,c}, CONFLICT(rename/rename(2to1): x/d_1, w/d_2 -> y_d)
 
-test_expect_success '7b-setup: rename/rename(2to1), but only due to transitive rename' '
+test_setup_7b () {
        test_create_repo 7b &&
        (
                cd 7b &&
@@ -1655,9 +1676,10 @@ test_expect_success '7b-setup: rename/rename(2to1), but only due to transitive r
                test_tick &&
                git commit -m "B"
        )
-'
+}
 
-test_expect_success '7b-check: rename/rename(2to1), but only due to transitive rename' '
+test_expect_success '7b: rename/rename(2to1), but only due to transitive rename' '
+       test_setup_7b &&
        (
                cd 7b &&
 
@@ -1702,7 +1724,7 @@ test_expect_success '7b-check: rename/rename(2to1), but only due to transitive r
 #         neither CONFLICT(x/d -> w/d vs. z/d)
 #         nor CONFLiCT x/d -> w/d vs. y/d vs. z/d)
 
-test_expect_success '7c-setup: rename/rename(1to...2or3); transitive rename may add complexity' '
+test_setup_7c () {
        test_create_repo 7c &&
        (
                cd 7c &&
@@ -1732,9 +1754,10 @@ test_expect_success '7c-setup: rename/rename(1to...2or3); transitive rename may
                test_tick &&
                git commit -m "B"
        )
-'
+}
 
-test_expect_success '7c-check: rename/rename(1to...2or3); transitive rename may add complexity' '
+test_expect_success '7c: rename/rename(1to...2or3); transitive rename may add complexity' '
+       test_setup_7c &&
        (
                cd 7c &&
 
@@ -1766,7 +1789,7 @@ test_expect_success '7c-check: rename/rename(1to...2or3); transitive rename may
 #   Expected: y/{b,c}, CONFLICT(delete x/d vs rename to y/d)
 #   NOTE: z->y so NOT CONFLICT(delete x/d vs rename to z/d)
 
-test_expect_success '7d-setup: transitive rename involved in rename/delete; how is it reported?' '
+test_setup_7d () {
        test_create_repo 7d &&
        (
                cd 7d &&
@@ -1796,9 +1819,10 @@ test_expect_success '7d-setup: transitive rename involved in rename/delete; how
                test_tick &&
                git commit -m "B"
        )
-'
+}
 
-test_expect_success '7d-check: transitive rename involved in rename/delete; how is it reported?' '
+test_expect_success '7d: transitive rename involved in rename/delete; how is it reported?' '
+       test_setup_7d &&
        (
                cd 7d &&
 
@@ -1851,7 +1875,7 @@ test_expect_success '7d-check: transitive rename involved in rename/delete; how
 #         see testcases 9c and 9d for further discussion of this issue and
 #         how it's resolved.
 
-test_expect_success '7e-setup: transitive rename in rename/delete AND dirs in the way' '
+test_setup_7e () {
        test_create_repo 7e &&
        (
                cd 7e &&
@@ -1886,9 +1910,10 @@ test_expect_success '7e-setup: transitive rename in rename/delete AND dirs in th
                test_tick &&
                git commit -m "B"
        )
-'
+}
 
-test_expect_success '7e-check: transitive rename in rename/delete AND dirs in the way' '
+test_expect_success '7e: transitive rename in rename/delete AND dirs in the way' '
+       test_setup_7e &&
        (
                cd 7e &&
 
@@ -1945,7 +1970,7 @@ test_expect_success '7e-check: transitive rename in rename/delete AND dirs in th
 # simple rule from section 5 prevents me from handling this as optimally as
 # we potentially could.
 
-test_expect_success '8a-setup: Dual-directory rename, one into the others way' '
+test_setup_8a () {
        test_create_repo 8a &&
        (
                cd 8a &&
@@ -1977,9 +2002,10 @@ test_expect_success '8a-setup: Dual-directory rename, one into the others way' '
                test_tick &&
                git commit -m "B"
        )
-'
+}
 
-test_expect_success '8a-check: Dual-directory rename, one into the others way' '
+test_expect_success '8a: Dual-directory rename, one into the others way' '
+       test_setup_8a &&
        (
                cd 8a &&
 
@@ -2023,7 +2049,7 @@ test_expect_success '8a-check: Dual-directory rename, one into the others way' '
 # making us fall back to pre-directory-rename-detection behavior for both
 # e_1 and e_2.
 
-test_expect_success '8b-setup: Dual-directory rename, one into the others way, with conflicting filenames' '
+test_setup_8b () {
        test_create_repo 8b &&
        (
                cd 8b &&
@@ -2055,9 +2081,10 @@ test_expect_success '8b-setup: Dual-directory rename, one into the others way, w
                test_tick &&
                git commit -m "B"
        )
-'
+}
 
-test_expect_success '8b-check: Dual-directory rename, one into the others way, with conflicting filenames' '
+test_expect_success '8b: Dual-directory rename, one into the others way, with conflicting filenames' '
+       test_setup_8b &&
        (
                cd 8b &&
 
@@ -2089,14 +2116,14 @@ test_expect_success '8b-check: Dual-directory rename, one into the others way, w
 #
 #   Note: It could easily be argued that the correct resolution here is
 #         y/{b,c,e}, CONFLICT(rename/delete: z/d -> y/d vs deleted)
-#         and that the modifed version of d should be present in y/ after
+#         and that the modified version of d should be present in y/ after
 #         the merge, just marked as conflicted.  Indeed, I previously did
 #         argue that.  But applying directory renames to the side of
 #         history where a file is merely modified results in spurious
 #         rename/rename(1to2) conflicts -- see testcase 9h.  See also
 #         notes in 8d.
 
-test_expect_success '8c-setup: modify/delete or rename+modify/delete?' '
+test_setup_8c () {
        test_create_repo 8c &&
        (
                cd 8c &&
@@ -2127,9 +2154,10 @@ test_expect_success '8c-setup: modify/delete or rename+modify/delete?' '
                test_tick &&
                git commit -m "B"
        )
-'
+}
 
-test_expect_success '8c-check: modify/delete or rename+modify/delete' '
+test_expect_success '8c: modify/delete or rename+modify/delete' '
+       test_setup_8c &&
        (
                cd 8c &&
 
@@ -2175,7 +2203,7 @@ test_expect_success '8c-check: modify/delete or rename+modify/delete' '
 #   during merging are supposed to be about opposite sides doing things
 #   differently.
 
-test_expect_success '8d-setup: rename/delete...or not?' '
+test_setup_8d () {
        test_create_repo 8d &&
        (
                cd 8d &&
@@ -2204,9 +2232,10 @@ test_expect_success '8d-setup: rename/delete...or not?' '
                test_tick &&
                git commit -m "B"
        )
-'
+}
 
-test_expect_success '8d-check: rename/delete...or not?' '
+test_expect_success '8d: rename/delete...or not?' '
+       test_setup_8d &&
        (
                cd 8d &&
 
@@ -2250,7 +2279,7 @@ test_expect_success '8d-check: rename/delete...or not?' '
 #        about the ramifications of doing that, I didn't know how to rule out
 #        that opening other weird edge and corner cases so I just punted.
 
-test_expect_success '8e-setup: Both sides rename, one side adds to original directory' '
+test_setup_8e () {
        test_create_repo 8e &&
        (
                cd 8e &&
@@ -2279,9 +2308,10 @@ test_expect_success '8e-setup: Both sides rename, one side adds to original dire
                test_tick &&
                git commit -m "B"
        )
-'
+}
 
-test_expect_success '8e-check: Both sides rename, one side adds to original directory' '
+test_expect_success '8e: Both sides rename, one side adds to original directory' '
+       test_setup_8e &&
        (
                cd 8e &&
 
@@ -2333,7 +2363,7 @@ test_expect_success '8e-check: Both sides rename, one side adds to original dire
 #         of which one had the most paths going to it.  A naive implementation
 #         of that could take the new file in commit B at z/i to x/w/i or x/i.
 
-test_expect_success '9a-setup: Inner renamed directory within outer renamed directory' '
+test_setup_9a () {
        test_create_repo 9a &&
        (
                cd 9a &&
@@ -2366,9 +2396,10 @@ test_expect_success '9a-setup: Inner renamed directory within outer renamed dire
                test_tick &&
                git commit -m "B"
        )
-'
+}
 
-test_expect_success '9a-check: Inner renamed directory within outer renamed directory' '
+test_expect_success '9a: Inner renamed directory within outer renamed directory' '
+       test_setup_9a &&
        (
                cd 9a &&
 
@@ -2404,7 +2435,7 @@ test_expect_success '9a-check: Inner renamed directory within outer renamed dire
 #   Commit B: z/{b,c,d_3}
 #   Expected: y/{b,c,d_merged}
 
-test_expect_success '9b-setup: Transitive rename with content merge' '
+test_setup_9b () {
        test_create_repo 9b &&
        (
                cd 9b &&
@@ -2436,9 +2467,10 @@ test_expect_success '9b-setup: Transitive rename with content merge' '
                test_tick &&
                git commit -m "B"
        )
-'
+}
 
-test_expect_success '9b-check: Transitive rename with content merge' '
+test_expect_success '9b: Transitive rename with content merge' '
+       test_setup_9b &&
        (
                cd 9b &&
 
@@ -2491,7 +2523,7 @@ test_expect_success '9b-check: Transitive rename with content merge' '
 #         away, then ignore that particular rename from the other side of
 #         history for any implicit directory renames.
 
-test_expect_success '9c-setup: Doubly transitive rename?' '
+test_setup_9c () {
        test_create_repo 9c &&
        (
                cd 9c &&
@@ -2526,9 +2558,10 @@ test_expect_success '9c-setup: Doubly transitive rename?' '
                test_tick &&
                git commit -m "B"
        )
-'
+}
 
-test_expect_success '9c-check: Doubly transitive rename?' '
+test_expect_success '9c: Doubly transitive rename?' '
+       test_setup_9c &&
        (
                cd 9c &&
 
@@ -2579,7 +2612,7 @@ test_expect_success '9c-check: Doubly transitive rename?' '
 #   simple rules that are consistent with what we need for all the other
 #   testcases and simplifies things for the user.
 
-test_expect_success '9d-setup: N-way transitive rename?' '
+test_setup_9d () {
        test_create_repo 9d &&
        (
                cd 9d &&
@@ -2614,9 +2647,10 @@ test_expect_success '9d-setup: N-way transitive rename?' '
                test_tick &&
                git commit -m "B"
        )
-'
+}
 
-test_expect_success '9d-check: N-way transitive rename?' '
+test_expect_success '9d: N-way transitive rename?' '
+       test_setup_9d &&
        (
                cd 9d &&
 
@@ -2653,7 +2687,7 @@ test_expect_success '9d-check: N-way transitive rename?' '
 #   Expected: combined/{a,b,c,d,e,f,g,h,i,j,k,l}, CONFLICT(Nto1) warnings,
 #             dir1/yo, dir2/yo, dir3/yo, dirN/yo
 
-test_expect_success '9e-setup: N-to-1 whammo' '
+test_setup_9e () {
        test_create_repo 9e &&
        (
                cd 9e &&
@@ -2696,9 +2730,10 @@ test_expect_success '9e-setup: N-to-1 whammo' '
                test_tick &&
                git commit -m "B"
        )
-'
+}
 
-test_expect_success C_LOCALE_OUTPUT '9e-check: N-to-1 whammo' '
+test_expect_success C_LOCALE_OUTPUT '9e: N-to-1 whammo' '
+       test_setup_9e &&
        (
                cd 9e &&
 
@@ -2745,7 +2780,7 @@ test_expect_success C_LOCALE_OUTPUT '9e-check: N-to-1 whammo' '
 #   Commit B: goal/{a,b}/$more_files, goal/c
 #   Expected: priority/{a,b}/$more_files, priority/c
 
-test_expect_success '9f-setup: Renamed directory that only contained immediate subdirs' '
+test_setup_9f () {
        test_create_repo 9f &&
        (
                cd 9f &&
@@ -2774,9 +2809,10 @@ test_expect_success '9f-setup: Renamed directory that only contained immediate s
                test_tick &&
                git commit -m "B"
        )
-'
+}
 
-test_expect_success '9f-check: Renamed directory that only contained immediate subdirs' '
+test_expect_success '9f: Renamed directory that only contained immediate subdirs' '
+       test_setup_9f &&
        (
                cd 9f &&
 
@@ -2809,7 +2845,7 @@ test_expect_success '9f-check: Renamed directory that only contained immediate s
 #   Commit B: goal/{a,b}/$more_files, goal/c
 #   Expected: priority/{alpha,bravo}/$more_files, priority/c
 
-test_expect_success '9g-setup: Renamed directory that only contained immediate subdirs, immediate subdirs renamed' '
+test_setup_9g () {
        test_create_repo 9g &&
        (
                cd 9g &&
@@ -2841,9 +2877,9 @@ test_expect_success '9g-setup: Renamed directory that only contained immediate s
                test_tick &&
                git commit -m "B"
        )
-'
+}
 
-test_expect_failure '9g-check: Renamed directory that only contained immediate subdirs, immediate subdirs renamed' '
+test_expect_failure '9g: Renamed directory that only contained immediate subdirs, immediate subdirs renamed' '
        (
                cd 9g &&
 
@@ -2877,7 +2913,7 @@ test_expect_failure '9g-check: Renamed directory that only contained immediate s
 #   Expected: y/{b,c}, x/d_2
 #   NOTE: If we applied the z/ -> y/ rename to z/d, then we'd end up with
 #         a rename/rename(1to2) conflict (z/d -> y/d vs. x/d)
-test_expect_success '9h-setup: Avoid dir rename on merely modified path' '
+test_setup_9h () {
        test_create_repo 9h &&
        (
                cd 9h &&
@@ -2910,9 +2946,10 @@ test_expect_success '9h-setup: Avoid dir rename on merely modified path' '
                test_tick &&
                git commit -m "B"
        )
-'
+}
 
-test_expect_success '9h-check: Avoid dir rename on merely modified path' '
+test_expect_success '9h: Avoid dir rename on merely modified path' '
+       test_setup_9h &&
        (
                cd 9h &&
 
@@ -2957,7 +2994,7 @@ test_expect_success '9h-check: Avoid dir rename on merely modified path' '
 #   Expected: Aborted Merge +
 #       ERROR_MSG(untracked working tree files would be overwritten by merge)
 
-test_expect_success '10a-setup: Overwrite untracked with normal rename/delete' '
+test_setup_10a () {
        test_create_repo 10a &&
        (
                cd 10a &&
@@ -2983,9 +3020,10 @@ test_expect_success '10a-setup: Overwrite untracked with normal rename/delete' '
                test_tick &&
                git commit -m "B"
        )
-'
+}
 
-test_expect_success '10a-check: Overwrite untracked with normal rename/delete' '
+test_expect_success '10a: Overwrite untracked with normal rename/delete' '
+       test_setup_10a &&
        (
                cd 10a &&
 
@@ -3021,7 +3059,7 @@ test_expect_success '10a-check: Overwrite untracked with normal rename/delete' '
 #             z/c_1 -> z/d_1 rename recorded at stage 3 for y/d +
 #       ERROR_MSG(refusing to lose untracked file at 'y/d')
 
-test_expect_success '10b-setup: Overwrite untracked with dir rename + delete' '
+test_setup_10b () {
        test_create_repo 10b &&
        (
                cd 10b &&
@@ -3050,9 +3088,10 @@ test_expect_success '10b-setup: Overwrite untracked with dir rename + delete' '
                test_tick &&
                git commit -m "B"
        )
-'
+}
 
-test_expect_success '10b-check: Overwrite untracked with dir rename + delete' '
+test_expect_success '10b: Overwrite untracked with dir rename + delete' '
+       test_setup_10b &&
        (
                cd 10b &&
 
@@ -3098,10 +3137,10 @@ test_expect_success '10b-check: Overwrite untracked with dir rename + delete' '
 #             y/c~B^0 +
 #             ERROR_MSG(Refusing to lose untracked file at y/c)
 
-test_expect_success '10c-setup: Overwrite untracked with dir rename/rename(1to2)' '
-       test_create_repo 10c &&
+test_setup_10c () {
+       test_create_repo 10c_$1 &&
        (
-               cd 10c &&
+               cd 10c_$1 &&
 
                mkdir z x &&
                echo a >z/a &&
@@ -3128,11 +3167,12 @@ test_expect_success '10c-setup: Overwrite untracked with dir rename/rename(1to2)
                test_tick &&
                git commit -m "B"
        )
-'
+}
 
-test_expect_success '10c-check: Overwrite untracked with dir rename/rename(1to2)' '
+test_expect_success '10c1: Overwrite untracked with dir rename/rename(1to2)' '
+       test_setup_10c 1 &&
        (
-               cd 10c &&
+               cd 10c_1 &&
 
                git checkout A^0 &&
                echo important >y/c &&
@@ -3163,9 +3203,10 @@ test_expect_success '10c-check: Overwrite untracked with dir rename/rename(1to2)
        )
 '
 
-test_expect_success '10c-check: Overwrite untracked with dir rename/rename(1to2), other direction' '
+test_expect_success '10c2: Overwrite untracked with dir rename/rename(1to2), other direction' '
+       test_setup_10c 2 &&
        (
-               cd 10c &&
+               cd 10c_2 &&
 
                git reset --hard &&
                git clean -fdqx &&
@@ -3208,7 +3249,7 @@ test_expect_success '10c-check: Overwrite untracked with dir rename/rename(1to2)
 #             CONFLICT(rename/rename) z/c_1 vs x/f_2 -> y/wham
 #             ERROR_MSG(Refusing to lose untracked file at y/wham)
 
-test_expect_success '10d-setup: Delete untracked with dir rename/rename(2to1)' '
+test_setup_10d () {
        test_create_repo 10d &&
        (
                cd 10d &&
@@ -3240,9 +3281,10 @@ test_expect_success '10d-setup: Delete untracked with dir rename/rename(2to1)' '
                test_tick &&
                git commit -m "B"
        )
-'
+}
 
-test_expect_success '10d-check: Delete untracked with dir rename/rename(2to1)' '
+test_expect_success '10d: Delete untracked with dir rename/rename(2to1)' '
+       test_setup_10d &&
        (
                cd 10d &&
 
@@ -3290,7 +3332,7 @@ test_expect_success '10d-check: Delete untracked with dir rename/rename(2to1)' '
 #   Commit B: z/{a,b,c}
 #   Expected: y/{a,b,c} + untracked z/c
 
-test_expect_success '10e-setup: Does git complain about untracked file that is not really in the way?' '
+test_setup_10e () {
        test_create_repo 10e &&
        (
                cd 10e &&
@@ -3317,9 +3359,9 @@ test_expect_success '10e-setup: Does git complain about untracked file that is n
                test_tick &&
                git commit -m "B"
        )
-'
+}
 
-test_expect_failure '10e-check: Does git complain about untracked file that is not really in the way?' '
+test_expect_failure '10e: Does git complain about untracked file that is not really in the way?' '
        (
                cd 10e &&
 
@@ -3371,7 +3413,7 @@ test_expect_failure '10e-check: Does git complain about untracked file that is n
 #             z/c~HEAD with contents of B:z/b_v2,
 #             z/c with uncommitted mods on top of A:z/c_v1
 
-test_expect_success '11a-setup: Avoid losing dirty contents with simple rename' '
+test_setup_11a () {
        test_create_repo 11a &&
        (
                cd 11a &&
@@ -3398,9 +3440,10 @@ test_expect_success '11a-setup: Avoid losing dirty contents with simple rename'
                test_tick &&
                git commit -m "B"
        )
-'
+}
 
-test_expect_success '11a-check: Avoid losing dirty contents with simple rename' '
+test_expect_success '11a: Avoid losing dirty contents with simple rename' '
+       test_setup_11a &&
        (
                cd 11a &&
 
@@ -3441,7 +3484,7 @@ test_expect_success '11a-check: Avoid losing dirty contents with simple rename'
 #             ERROR_MSG(Refusing to lose dirty file at z/c)
 
 
-test_expect_success '11b-setup: Avoid losing dirty file involved in directory rename' '
+test_setup_11b () {
        test_create_repo 11b &&
        (
                cd 11b &&
@@ -3470,9 +3513,10 @@ test_expect_success '11b-setup: Avoid losing dirty file involved in directory re
                test_tick &&
                git commit -m "B"
        )
-'
+}
 
-test_expect_success '11b-check: Avoid losing dirty file involved in directory rename' '
+test_expect_success '11b: Avoid losing dirty file involved in directory rename' '
+       test_setup_11b &&
        (
                cd 11b &&
 
@@ -3515,7 +3559,7 @@ test_expect_success '11b-check: Avoid losing dirty file involved in directory re
 #   Expected: Abort_msg("following files would be overwritten by merge") +
 #             y/c left untouched (still has uncommitted mods)
 
-test_expect_success '11c-setup: Avoid losing not-uptodate with rename + D/F conflict' '
+test_setup_11c () {
        test_create_repo 11c &&
        (
                cd 11c &&
@@ -3545,9 +3589,10 @@ test_expect_success '11c-setup: Avoid losing not-uptodate with rename + D/F conf
                test_tick &&
                git commit -m "B"
        )
-'
+}
 
-test_expect_success '11c-check: Avoid losing not-uptodate with rename + D/F conflict' '
+test_expect_success '11c: Avoid losing not-uptodate with rename + D/F conflict' '
+       test_setup_11c &&
        (
                cd 11c &&
 
@@ -3581,7 +3626,7 @@ test_expect_success '11c-check: Avoid losing not-uptodate with rename + D/F conf
 #             Warning_Msg("Refusing to lose dirty file at z/c) +
 #             y/{a,c~HEAD,c/d}, x/b, now-untracked z/c_v1 with uncommitted mods
 
-test_expect_success '11d-setup: Avoid losing not-uptodate with rename + D/F conflict' '
+test_setup_11d () {
        test_create_repo 11d &&
        (
                cd 11d &&
@@ -3612,9 +3657,10 @@ test_expect_success '11d-setup: Avoid losing not-uptodate with rename + D/F conf
                test_tick &&
                git commit -m "B"
        )
-'
+}
 
-test_expect_success '11d-check: Avoid losing not-uptodate with rename + D/F conflict' '
+test_expect_success '11d: Avoid losing not-uptodate with rename + D/F conflict' '
+       test_setup_11d &&
        (
                cd 11d &&
 
@@ -3659,7 +3705,7 @@ test_expect_success '11d-check: Avoid losing not-uptodate with rename + D/F conf
 #             y/c~HEAD has A:y/c_2 contents
 #             y/c has dirty file from before merge
 
-test_expect_success '11e-setup: Avoid deleting not-uptodate with dir rename/rename(1to2)/add' '
+test_setup_11e () {
        test_create_repo 11e &&
        (
                cd 11e &&
@@ -3691,9 +3737,10 @@ test_expect_success '11e-setup: Avoid deleting not-uptodate with dir rename/rena
                test_tick &&
                git commit -m "B"
        )
-'
+}
 
-test_expect_success '11e-check: Avoid deleting not-uptodate with dir rename/rename(1to2)/add' '
+test_expect_success '11e: Avoid deleting not-uptodate with dir rename/rename(1to2)/add' '
+       test_setup_11e &&
        (
                cd 11e &&
 
@@ -3744,7 +3791,7 @@ test_expect_success '11e-check: Avoid deleting not-uptodate with dir rename/rena
 #             CONFLICT(rename/rename) x/c vs x/d -> y/wham
 #             ERROR_MSG(Refusing to lose dirty file at y/wham)
 
-test_expect_success '11f-setup: Avoid deleting not-uptodate with dir rename/rename(2to1)' '
+test_setup_11f () {
        test_create_repo 11f &&
        (
                cd 11f &&
@@ -3773,9 +3820,10 @@ test_expect_success '11f-setup: Avoid deleting not-uptodate with dir rename/rena
                test_tick &&
                git commit -m "B"
        )
-'
+}
 
-test_expect_success '11f-check: Avoid deleting not-uptodate with dir rename/rename(2to1)' '
+test_expect_success '11f: Avoid deleting not-uptodate with dir rename/rename(2to1)' '
+       test_setup_11f &&
        (
                cd 11f &&
 
@@ -3832,7 +3880,7 @@ test_expect_success '11f-check: Avoid deleting not-uptodate with dir rename/rena
 #   Commit B: node1/{leaf1,leaf2,leaf5}, node2/{leaf3,leaf4,leaf6}
 #   Expected: node1/{leaf1,leaf2,leaf5,node2/{leaf3,leaf4,leaf6}}
 
-test_expect_success '12a-setup: Moving one directory hierarchy into another' '
+test_setup_12a () {
        test_create_repo 12a &&
        (
                cd 12a &&
@@ -3862,9 +3910,10 @@ test_expect_success '12a-setup: Moving one directory hierarchy into another' '
                test_tick &&
                git commit -m "B"
        )
-'
+}
 
-test_expect_success '12a-check: Moving one directory hierarchy into another' '
+test_expect_success '12a: Moving one directory hierarchy into another' '
+       test_setup_12a &&
        (
                cd 12a &&
 
@@ -3910,7 +3959,7 @@ test_expect_success '12a-check: Moving one directory hierarchy into another' '
 #         To which, I can do no more than shrug my shoulders and say that
 #         even simple rules give weird results when given weird inputs.
 
-test_expect_success '12b-setup: Moving two directory hierarchies into each other' '
+test_setup_12b () {
        test_create_repo 12b &&
        (
                cd 12b &&
@@ -3938,9 +3987,10 @@ test_expect_success '12b-setup: Moving two directory hierarchies into each other
                test_tick &&
                git commit -m "B"
        )
-'
+}
 
-test_expect_success '12b-check: Moving two directory hierarchies into each other' '
+test_expect_success '12b: Moving two directory hierarchies into each other' '
+       test_setup_12b &&
        (
                cd 12b &&
 
@@ -3976,7 +4026,7 @@ test_expect_success '12b-check: Moving two directory hierarchies into each other
 #   NOTE: This is *exactly* like 12c, except that every path is modified on
 #         each side of the merge.
 
-test_expect_success '12c-setup: Moving one directory hierarchy into another w/ content merge' '
+test_setup_12c () {
        test_create_repo 12c &&
        (
                cd 12c &&
@@ -4008,9 +4058,10 @@ test_expect_success '12c-setup: Moving one directory hierarchy into another w/ c
                test_tick &&
                git commit -m "B"
        )
-'
+}
 
-test_expect_success '12c-check: Moving one directory hierarchy into another w/ content merge' '
+test_expect_success '12c: Moving one directory hierarchy into another w/ content merge' '
+       test_setup_12c &&
        (
                cd 12c &&
 
@@ -4051,6 +4102,122 @@ test_expect_success '12c-check: Moving one directory hierarchy into another w/ c
        )
 '
 
+# Testcase 12d, Rename/merge of subdirectory into the root
+#   Commit O: a/b/subdir/foo
+#   Commit A: subdir/foo
+#   Commit B: a/b/subdir/foo, a/b/bar
+#   Expected: subdir/foo, bar
+
+test_setup_12d () {
+       test_create_repo 12d &&
+       (
+               cd 12d &&
+
+               mkdir -p a/b/subdir &&
+               test_commit a/b/subdir/foo &&
+
+               git branch O &&
+               git branch A &&
+               git branch B &&
+
+               git checkout A &&
+               mkdir subdir &&
+               git mv a/b/subdir/foo.t subdir/foo.t &&
+               test_tick &&
+               git commit -m "A" &&
+
+               git checkout B &&
+               test_commit a/b/bar
+       )
+}
+
+test_expect_success '12d: Rename/merge subdir into the root, variant 1' '
+       test_setup_12d &&
+       (
+               cd 12d &&
+
+               git checkout A^0 &&
+
+               git -c merge.directoryRenames=true merge -s recursive B^0 &&
+
+               git ls-files -s >out &&
+               test_line_count = 2 out &&
+
+               git rev-parse >actual \
+                       HEAD:subdir/foo.t   HEAD:bar.t &&
+               git rev-parse >expect \
+                       O:a/b/subdir/foo.t  B:a/b/bar.t &&
+               test_cmp expect actual &&
+
+               git hash-object bar.t >actual &&
+               git rev-parse B:a/b/bar.t >expect &&
+               test_cmp expect actual &&
+
+               test_must_fail git rev-parse HEAD:a/b/subdir/foo.t &&
+               test_must_fail git rev-parse HEAD:a/b/bar.t &&
+               test_path_is_missing a/ &&
+               test_path_is_file bar.t
+       )
+'
+
+# Testcase 12e, Rename/merge of subdirectory into the root
+#   Commit O: a/b/foo
+#   Commit A: foo
+#   Commit B: a/b/foo, a/b/bar
+#   Expected: foo, bar
+
+test_setup_12e () {
+       test_create_repo 12e &&
+       (
+               cd 12e &&
+
+               mkdir -p a/b &&
+               test_commit a/b/foo &&
+
+               git branch O &&
+               git branch A &&
+               git branch B &&
+
+               git checkout A &&
+               mkdir subdir &&
+               git mv a/b/foo.t foo.t &&
+               test_tick &&
+               git commit -m "A" &&
+
+               git checkout B &&
+               test_commit a/b/bar
+       )
+}
+
+test_expect_success '12e: Rename/merge subdir into the root, variant 2' '
+       test_setup_12e &&
+       (
+               cd 12e &&
+
+               git checkout A^0 &&
+
+               git -c merge.directoryRenames=true merge -s recursive B^0 &&
+
+               git ls-files -s >out &&
+               test_line_count = 2 out &&
+
+               git rev-parse >actual \
+                       HEAD:foo.t   HEAD:bar.t &&
+               git rev-parse >expect \
+                       O:a/b/foo.t  B:a/b/bar.t &&
+               test_cmp expect actual &&
+
+               git hash-object bar.t >actual &&
+               git rev-parse B:a/b/bar.t >expect &&
+               test_cmp expect actual &&
+
+               test_must_fail git rev-parse HEAD:a/b/foo.t &&
+               test_must_fail git rev-parse HEAD:a/b/bar.t &&
+               test_path_is_missing a/ &&
+               test_path_is_file bar.t
+       )
+'
+
 ###########################################################################
 # SECTION 13: Checking informational and conflict messages
 #
@@ -4068,10 +4235,10 @@ test_expect_success '12c-check: Moving one directory hierarchy into another w/ c
 #   Commit B: z/{b,c,d,e/f}
 #   Expected: y/{b,c,d,e/f}, with notices/conflicts for both y/d and y/e/f
 
-test_expect_success '13a-setup: messages for newly added files' '
-       test_create_repo 13a &&
+test_setup_13a () {
+       test_create_repo 13a_$1 &&
        (
-               cd 13a &&
+               cd 13a_$1 &&
 
                mkdir z &&
                echo b >z/b &&
@@ -4097,11 +4264,12 @@ test_expect_success '13a-setup: messages for newly added files' '
                test_tick &&
                git commit -m "B"
        )
-'
+}
 
-test_expect_success '13a-check(conflict): messages for newly added files' '
+test_expect_success '13a(conflict): messages for newly added files' '
+       test_setup_13a conflict &&
        (
-               cd 13a &&
+               cd 13a_conflict &&
 
                git checkout A^0 &&
 
@@ -4121,9 +4289,10 @@ test_expect_success '13a-check(conflict): messages for newly added files' '
        )
 '
 
-test_expect_success '13a-check(info): messages for newly added files' '
+test_expect_success '13a(info): messages for newly added files' '
+       test_setup_13a info &&
        (
-               cd 13a &&
+               cd 13a_info &&
 
                git reset --hard &&
                git checkout A^0 &&
@@ -4153,10 +4322,10 @@ test_expect_success '13a-check(info): messages for newly added files' '
 #   Expected: y/{b,c,d_merged}, with two conflict messages for y/d,
 #             one about content, and one about file location
 
-test_expect_success '13b-setup: messages for transitive rename with conflicted content' '
-       test_create_repo 13b &&
+test_setup_13b () {
+       test_create_repo 13b_$1 &&
        (
-               cd 13b &&
+               cd 13b_$1 &&
 
                mkdir x &&
                mkdir z &&
@@ -4185,11 +4354,12 @@ test_expect_success '13b-setup: messages for transitive rename with conflicted c
                test_tick &&
                git commit -m "B"
        )
-'
+}
 
-test_expect_success '13b-check(conflict): messages for transitive rename with conflicted content' '
+test_expect_success '13b(conflict): messages for transitive rename with conflicted content' '
+       test_setup_13b conflict &&
        (
-               cd 13b &&
+               cd 13b_conflict &&
 
                git checkout A^0 &&
 
@@ -4207,9 +4377,10 @@ test_expect_success '13b-check(conflict): messages for transitive rename with co
        )
 '
 
-test_expect_success '13b-check(info): messages for transitive rename with conflicted content' '
+test_expect_success '13b(info): messages for transitive rename with conflicted content' '
+       test_setup_13b info &&
        (
-               cd 13b &&
+               cd 13b_info &&
 
                git reset --hard &&
                git checkout A^0 &&
@@ -4238,10 +4409,10 @@ test_expect_success '13b-check(info): messages for transitive rename with confli
 #             d and B had full knowledge, but that's a slippery slope as
 #             shown in testcase 13d.
 
-test_expect_success '13c-setup: messages for rename/rename(1to1) via transitive rename' '
-       test_create_repo 13c &&
+test_setup_13c () {
+       test_create_repo 13c_$1 &&
        (
-               cd 13c &&
+               cd 13c_$1 &&
 
                mkdir x &&
                mkdir z &&
@@ -4269,11 +4440,12 @@ test_expect_success '13c-setup: messages for rename/rename(1to1) via transitive
                test_tick &&
                git commit -m "B"
        )
-'
+}
 
-test_expect_success '13c-check(conflict): messages for rename/rename(1to1) via transitive rename' '
+test_expect_success '13c(conflict): messages for rename/rename(1to1) via transitive rename' '
+       test_setup_13c conflict &&
        (
-               cd 13c &&
+               cd 13c_conflict &&
 
                git checkout A^0 &&
 
@@ -4290,9 +4462,10 @@ test_expect_success '13c-check(conflict): messages for rename/rename(1to1) via t
        )
 '
 
-test_expect_success '13c-check(info): messages for rename/rename(1to1) via transitive rename' '
+test_expect_success '13c(info): messages for rename/rename(1to1) via transitive rename' '
+       test_setup_13c info &&
        (
-               cd 13c &&
+               cd 13c_info &&
 
                git reset --hard &&
                git checkout A^0 &&
@@ -4324,10 +4497,10 @@ test_expect_success '13c-check(info): messages for rename/rename(1to1) via trans
 #               * B renames a/y to c/y, and A renames c/->d/ => a/y -> d/y
 #               No conflict in where a/y ends up, so put it in d/y.
 
-test_expect_success '13d-setup: messages for rename/rename(1to1) via dual transitive rename' '
-       test_create_repo 13d &&
+test_setup_13d () {
+       test_create_repo 13d_$1 &&
        (
-               cd 13d &&
+               cd 13d_$1 &&
 
                mkdir a &&
                mkdir b &&
@@ -4356,11 +4529,12 @@ test_expect_success '13d-setup: messages for rename/rename(1to1) via dual transi
                test_tick &&
                git commit -m "B"
        )
-'
+}
 
-test_expect_success '13d-check(conflict): messages for rename/rename(1to1) via dual transitive rename' '
+test_expect_success '13d(conflict): messages for rename/rename(1to1) via dual transitive rename' '
+       test_setup_13d conflict &&
        (
-               cd 13d &&
+               cd 13d_conflict &&
 
                git checkout A^0 &&
 
@@ -4380,9 +4554,10 @@ test_expect_success '13d-check(conflict): messages for rename/rename(1to1) via d
        )
 '
 
-test_expect_success '13d-check(info): messages for rename/rename(1to1) via dual transitive rename' '
+test_expect_success '13d(info): messages for rename/rename(1to1) via dual transitive rename' '
+       test_setup_13d info &&
        (
-               cd 13d &&
+               cd 13d_info &&
 
                git reset --hard &&
                git checkout A^0 &&
@@ -4448,7 +4623,7 @@ test_expect_success '13d-check(info): messages for rename/rename(1to1) via dual
 #          in the outer merge for this special kind of setup, but it at
 #          least avoids hitting a BUG().
 #
-test_expect_success '13e-setup: directory rename detection in recursive case' '
+test_setup_13e () {
        test_create_repo 13e &&
        (
                cd 13e &&
@@ -4493,9 +4668,10 @@ test_expect_success '13e-setup: directory rename detection in recursive case' '
                test_tick &&
                git commit -m "D"
        )
-'
+}
 
-test_expect_success '13e-check: directory rename detection in recursive case' '
+test_expect_success '13e: directory rename detection in recursive case' '
+       test_setup_13e &&
        (
                cd 13e &&
 
index 3a47623ed3160b9037cbf7fd63bf7638a8dd57dc..b7e46698321588f21b942fb6aac7316fafc2206f 100755 (executable)
@@ -36,10 +36,10 @@ test_description="merge cases"
 #   Commit B: b_3
 #   Expected: b_2
 
-test_expect_success '1a-setup: Modify(A)/Modify(B), change on B subset of A' '
-       test_create_repo 1a &&
+test_setup_1a () {
+       test_create_repo 1a_$1 &&
        (
-               cd 1a &&
+               cd 1a_$1 &&
 
                test_write_lines 1 2 3 4 5 6 7 8 9 10 >b &&
                git add b &&
@@ -62,13 +62,12 @@ test_expect_success '1a-setup: Modify(A)/Modify(B), change on B subset of A' '
                test_tick &&
                git commit -m "B"
        )
-'
+}
 
-test_expect_success '1a-check-L: Modify(A)/Modify(B), change on B subset of A' '
-       test_when_finished "git -C 1a reset --hard" &&
-       test_when_finished "git -C 1a clean -fd" &&
+test_expect_success '1a-L: Modify(A)/Modify(B), change on B subset of A' '
+       test_setup_1a L &&
        (
-               cd 1a &&
+               cd 1a_L &&
 
                git checkout A^0 &&
 
@@ -96,11 +95,10 @@ test_expect_success '1a-check-L: Modify(A)/Modify(B), change on B subset of A' '
        )
 '
 
-test_expect_success '1a-check-R: Modify(A)/Modify(B), change on B subset of A' '
-       test_when_finished "git -C 1a reset --hard" &&
-       test_when_finished "git -C 1a clean -fd" &&
+test_expect_success '1a-R: Modify(A)/Modify(B), change on B subset of A' '
+       test_setup_1a R &&
        (
-               cd 1a &&
+               cd 1a_R &&
 
                git checkout B^0 &&
 
@@ -133,10 +131,10 @@ test_expect_success '1a-check-R: Modify(A)/Modify(B), change on B subset of A' '
 #   Commit B: c_1
 #   Expected: c_2
 
-test_expect_success '2a-setup: Modify(A)/rename(B)' '
-       test_create_repo 2a &&
+test_setup_2a () {
+       test_create_repo 2a_$1 &&
        (
-               cd 2a &&
+               cd 2a_$1 &&
 
                test_seq 1 10 >b &&
                git add b &&
@@ -158,13 +156,12 @@ test_expect_success '2a-setup: Modify(A)/rename(B)' '
                test_tick &&
                git commit -m "B"
        )
-'
+}
 
-test_expect_success '2a-check-L: Modify/rename, merge into modify side' '
-       test_when_finished "git -C 2a reset --hard" &&
-       test_when_finished "git -C 2a clean -fd" &&
+test_expect_success '2a-L: Modify/rename, merge into modify side' '
+       test_setup_2a L &&
        (
-               cd 2a &&
+               cd 2a_L &&
 
                git checkout A^0 &&
 
@@ -189,11 +186,10 @@ test_expect_success '2a-check-L: Modify/rename, merge into modify side' '
        )
 '
 
-test_expect_success '2a-check-R: Modify/rename, merge into rename side' '
-       test_when_finished "git -C 2a reset --hard" &&
-       test_when_finished "git -C 2a clean -fd" &&
+test_expect_success '2a-R: Modify/rename, merge into rename side' '
+       test_setup_2a R &&
        (
-               cd 2a &&
+               cd 2a_R &&
 
                git checkout B^0 &&
 
@@ -224,10 +220,10 @@ test_expect_success '2a-check-R: Modify/rename, merge into rename side' '
 #   Commit B: b_3
 #   Expected: c_2
 
-test_expect_success '2b-setup: Rename+Mod(A)/Mod(B), B mods subset of A' '
-       test_create_repo 2b &&
+test_setup_2b () {
+       test_create_repo 2b_$1 &&
        (
-               cd 2b &&
+               cd 2b_$1 &&
 
                test_write_lines 1 2 3 4 5 6 7 8 9 10 >b &&
                git add b &&
@@ -251,13 +247,12 @@ test_expect_success '2b-setup: Rename+Mod(A)/Mod(B), B mods subset of A' '
                test_tick &&
                git commit -m "B"
        )
-'
+}
 
-test_expect_success '2b-check-L: Rename+Mod(A)/Mod(B), B mods subset of A' '
-       test_when_finished "git -C 2b reset --hard" &&
-       test_when_finished "git -C 2b clean -fd" &&
+test_expect_success '2b-L: Rename+Mod(A)/Mod(B), B mods subset of A' '
+       test_setup_2b L &&
        (
-               cd 2b &&
+               cd 2b_L &&
 
                git checkout A^0 &&
 
@@ -288,11 +283,10 @@ test_expect_success '2b-check-L: Rename+Mod(A)/Mod(B), B mods subset of A' '
        )
 '
 
-test_expect_success '2b-check-R: Rename+Mod(A)/Mod(B), B mods subset of A' '
-       test_when_finished "git -C 2b reset --hard" &&
-       test_when_finished "git -C 2b clean -fd" &&
+test_expect_success '2b-R: Rename+Mod(A)/Mod(B), B mods subset of A' '
+       test_setup_2b R &&
        (
-               cd 2b &&
+               cd 2b_R &&
 
                git checkout B^0 &&
 
@@ -332,7 +326,7 @@ test_expect_success '2b-check-R: Rename+Mod(A)/Mod(B), B mods subset of A' '
 #         skip the update, then we're in trouble.  This test verifies we do
 #         not make that particular mistake.
 
-test_expect_success '2c-setup: Modify b & add c VS rename b->c' '
+test_setup_2c () {
        test_create_repo 2c &&
        (
                cd 2c &&
@@ -358,9 +352,10 @@ test_expect_success '2c-setup: Modify b & add c VS rename b->c' '
                test_tick &&
                git commit -m "B"
        )
-'
+}
 
-test_expect_success '2c-check: Modify b & add c VS rename b->c' '
+test_expect_success '2c: Modify b & add c VS rename b->c' '
+       test_setup_2c &&
        (
                cd 2c &&
 
@@ -428,10 +423,10 @@ test_expect_success '2c-check: Modify b & add c VS rename b->c' '
 #   Commit B: bq_1, bar/whatever
 #   Expected: bar/{bq_2, whatever}
 
-test_expect_success '3a-setup: bq_1->foo/bq_2 on A, foo/->bar/ on B' '
-       test_create_repo 3a &&
+test_setup_3a () {
+       test_create_repo 3a_$1 &&
        (
-               cd 3a &&
+               cd 3a_$1 &&
 
                mkdir foo &&
                test_seq 1 10 >bq &&
@@ -456,13 +451,12 @@ test_expect_success '3a-setup: bq_1->foo/bq_2 on A, foo/->bar/ on B' '
                test_tick &&
                git commit -m "B"
        )
-'
+}
 
-test_expect_success '3a-check-L: bq_1->foo/bq_2 on A, foo/->bar/ on B' '
-       test_when_finished "git -C 3a reset --hard" &&
-       test_when_finished "git -C 3a clean -fd" &&
+test_expect_success '3a-L: bq_1->foo/bq_2 on A, foo/->bar/ on B' '
+       test_setup_3a L &&
        (
-               cd 3a &&
+               cd 3a_L &&
 
                git checkout A^0 &&
 
@@ -487,11 +481,10 @@ test_expect_success '3a-check-L: bq_1->foo/bq_2 on A, foo/->bar/ on B' '
        )
 '
 
-test_expect_success '3a-check-R: bq_1->foo/bq_2 on A, foo/->bar/ on B' '
-       test_when_finished "git -C 3a reset --hard" &&
-       test_when_finished "git -C 3a clean -fd" &&
+test_expect_success '3a-R: bq_1->foo/bq_2 on A, foo/->bar/ on B' '
+       test_setup_3a R &&
        (
-               cd 3a &&
+               cd 3a_R &&
 
                git checkout B^0 &&
 
@@ -522,10 +515,10 @@ test_expect_success '3a-check-R: bq_1->foo/bq_2 on A, foo/->bar/ on B' '
 #   Commit B: bq_2, bar/whatever
 #   Expected: bar/{bq_2, whatever}
 
-test_expect_success '3b-setup: bq_1->foo/bq_2 on A, foo/->bar/ on B' '
-       test_create_repo 3b &&
+test_setup_3b () {
+       test_create_repo 3b_$1 &&
        (
-               cd 3b &&
+               cd 3b_$1 &&
 
                mkdir foo &&
                test_seq 1 10 >bq &&
@@ -550,13 +543,12 @@ test_expect_success '3b-setup: bq_1->foo/bq_2 on A, foo/->bar/ on B' '
                test_tick &&
                git commit -m "B"
        )
-'
+}
 
-test_expect_success '3b-check-L: bq_1->foo/bq_2 on A, foo/->bar/ on B' '
-       test_when_finished "git -C 3b reset --hard" &&
-       test_when_finished "git -C 3b clean -fd" &&
+test_expect_success '3b-L: bq_1->foo/bq_2 on A, foo/->bar/ on B' '
+       test_setup_3b L &&
        (
-               cd 3b &&
+               cd 3b_L &&
 
                git checkout A^0 &&
 
@@ -581,11 +573,10 @@ test_expect_success '3b-check-L: bq_1->foo/bq_2 on A, foo/->bar/ on B' '
        )
 '
 
-test_expect_success '3b-check-R: bq_1->foo/bq_2 on A, foo/->bar/ on B' '
-       test_when_finished "git -C 3b reset --hard" &&
-       test_when_finished "git -C 3b clean -fd" &&
+test_expect_success '3b-R: bq_1->foo/bq_2 on A, foo/->bar/ on B' '
+       test_setup_3b R &&
        (
-               cd 3b &&
+               cd 3b_R &&
 
                git checkout B^0 &&
 
@@ -621,7 +612,7 @@ test_expect_success '3b-check-R: bq_1->foo/bq_2 on A, foo/->bar/ on B' '
 #   Working copy: b_4
 #   Expected: b_2 for merge, b_4 in working copy
 
-test_expect_success '4a-setup: Change on A, change on B subset of A, dirty mods present' '
+test_setup_4a () {
        test_create_repo 4a &&
        (
                cd 4a &&
@@ -647,7 +638,7 @@ test_expect_success '4a-setup: Change on A, change on B subset of A, dirty mods
                test_tick &&
                git commit -m "B"
        )
-'
+}
 
 # NOTE: For as long as we continue using unpack_trees() without index_only
 #   set to true, it will error out on a case like this claiming the the locally
@@ -655,9 +646,8 @@ test_expect_success '4a-setup: Change on A, change on B subset of A, dirty mods
 #   correct requires doing the merge in-memory first, then realizing that no
 #   updates to the file are necessary, and thus that we can just leave the path
 #   alone.
-test_expect_failure '4a-check: Change on A, change on B subset of A, dirty mods present' '
-       test_when_finished "git -C 4a reset --hard" &&
-       test_when_finished "git -C 4a clean -fd" &&
+test_expect_failure '4a: Change on A, change on B subset of A, dirty mods present' '
+       test_setup_4a &&
        (
                cd 4a &&
 
@@ -695,7 +685,7 @@ test_expect_failure '4a-check: Change on A, change on B subset of A, dirty mods
 #   Working copy: c_4
 #   Expected: c_2
 
-test_expect_success '4b-setup: Rename+Mod(A)/Mod(B), change on B subset of A, dirty mods present' '
+test_setup_4b () {
        test_create_repo 4b &&
        (
                cd 4b &&
@@ -722,11 +712,10 @@ test_expect_success '4b-setup: Rename+Mod(A)/Mod(B), change on B subset of A, di
                test_tick &&
                git commit -m "B"
        )
-'
+}
 
-test_expect_success '4b-check: Rename+Mod(A)/Mod(B), change on B subset of A, dirty mods present' '
-       test_when_finished "git -C 4b reset --hard" &&
-       test_when_finished "git -C 4b clean -fd" &&
+test_expect_success '4b: Rename+Mod(A)/Mod(B), change on B subset of A, dirty mods present' '
+       test_setup_4b &&
        (
                cd 4b &&
 
index 28611c978e6c0094a5e452bf8c8c8778da02ae6c..52cde097dd5c1c473df987cf74f51da714bd17b4 100755 (executable)
@@ -52,7 +52,7 @@ test_expect_success 'traverse unexpected non-commit parent (lone)' '
 '
 
 test_expect_success 'traverse unexpected non-commit parent (seen)' '
-       test_must_fail git rev-list --objects $commit $broken_commit \
+       test_must_fail git rev-list --objects $blob $broken_commit \
                >output 2>&1 &&
        test_i18ngrep "not a commit" output
 '
index 80eb13d94e2a27d160424c9919ee0052c8c773c0..4a09bea1d61178e659d95485353b42dfb49ce705 100755 (executable)
@@ -227,10 +227,10 @@ test_expect_success \
 test_expect_success \
        'trying to delete two tags, existing and not, should fail in the 2nd' '
        tag_exists mytag &&
-       ! tag_exists myhead &&
-       test_must_fail git tag -d mytag anothertag &&
+       ! tag_exists nonexistingtag &&
+       test_must_fail git tag -d mytag nonexistingtag &&
        ! tag_exists mytag &&
-       ! tag_exists myhead
+       ! tag_exists nonexistingtag
 '
 
 test_expect_success 'trying to delete an already deleted tag should fail' \
@@ -1420,7 +1420,7 @@ test_expect_success \
 get_tag_header reuse $commit commit $time >expect
 echo "An annotation to be reused" >> expect
 test_expect_success \
-       'overwriting an annoted tag should use its previous body' '
+       'overwriting an annotated tag should use its previous body' '
        git tag -a -m "An annotation to be reused" reuse &&
        GIT_EDITOR=true git tag -f -a reuse &&
        get_tag_msg reuse >actual &&
index 9d1abe50eff6772673df545d3e6715d4d2e6f2db..7476781979c3ff164ce9eeba1079d41f15bdaa5c 100755 (executable)
@@ -134,6 +134,21 @@ test_expect_success 'git-clean, dirty case' '
        test_i18ncmp expected result
 '
 
+test_expect_success '--ignore-skip-worktree-entries leaves worktree alone' '
+       test_commit keep-me &&
+       git update-index --skip-worktree keep-me.t &&
+       rm keep-me.t &&
+
+       : ignoring the worktree &&
+       git update-index --remove --ignore-skip-worktree-entries keep-me.t &&
+       git diff-index --cached --exit-code HEAD &&
+
+       : not ignoring the worktree, a deletion is staged &&
+       git update-index --remove keep-me.t &&
+       test_must_fail git diff-index --cached --exit-code HEAD \
+               --diff-filter=D -- keep-me.t
+'
+
 #TODO test_expect_failure 'git-apply adds file' false
 #TODO test_expect_failure 'git-apply updates file' false
 #TODO test_expect_failure 'git-apply removes file' false
index 041e319e79aacb74ea9ec8fc20a38cc222961b6e..8f077bea6095a6fea8d92ac8a83b11f5d865cdf6 100755 (executable)
@@ -44,8 +44,8 @@ test_expect_success GPG 'create signed tags' '
 test_expect_success GPGSM 'create signed tags x509 ' '
        test_config gpg.format x509 &&
        test_config user.signingkey $GIT_COMMITTER_EMAIL &&
-       echo 9 >file && test_tick && git commit -a -m "nineth gpgsm-signed" &&
-       git tag -s -m nineth nineth-signed-x509
+       echo 9 >file && test_tick && git commit -a -m "ninth gpgsm-signed" &&
+       git tag -s -m ninth ninth-signed-x509
 '
 
 test_expect_success GPG 'verify and show signatures' '
@@ -80,10 +80,10 @@ test_expect_success GPG 'verify and show signatures' '
 '
 
 test_expect_success GPGSM 'verify and show signatures x509' '
-       git verify-tag nineth-signed-x509 2>actual &&
+       git verify-tag ninth-signed-x509 2>actual &&
        grep "Good signature from" actual &&
        ! grep "BAD signature from" actual &&
-       echo nineth-signed-x509 OK
+       echo ninth-signed-x509 OK
 '
 
 test_expect_success GPG 'detect fudged signature' '
@@ -127,10 +127,10 @@ test_expect_success GPG 'verify signatures with --raw' '
 '
 
 test_expect_success GPGSM 'verify signatures with --raw x509' '
-       git verify-tag --raw nineth-signed-x509 2>actual &&
+       git verify-tag --raw ninth-signed-x509 2>actual &&
        grep "GOODSIG" actual &&
        ! grep "BADSIG" actual &&
-       echo nineth-signed-x509 OK
+       echo ninth-signed-x509 OK
 '
 
 test_expect_success GPG 'verify multiple tags' '
@@ -147,7 +147,7 @@ test_expect_success GPG 'verify multiple tags' '
 '
 
 test_expect_success GPGSM 'verify multiple tags x509' '
-       tags="seventh-signed nineth-signed-x509" &&
+       tags="seventh-signed ninth-signed-x509" &&
        for i in $tags
        do
                git verify-tag -v --raw $i || return 1
index 4e676cdce8d621c86b4f758bf9535efb17735f46..482ce3510edd1007118d52fbb36111a8b0d09247 100755 (executable)
@@ -1571,7 +1571,7 @@ test_expect_success '"status.showStash=true" weaker than "--no-show-stash"' '
        test_cmp expected_without_stash actual
 '
 
-test_expect_success 'no additionnal info if no stash entries' '
+test_expect_success 'no additional info if no stash entries' '
        git stash clear &&
        git -c status.showStash=true status >actual &&
        test_cmp expected_without_stash actual
index f19202b509899481d4a098de049f1e2c9cae9915..6602790b5f4c4aace1b101189258b647109ccdb0 100755 (executable)
@@ -1234,7 +1234,7 @@ test_expect_success 'with simple command' '
        test_cmp expected actual
 '
 
-test_expect_success 'with command using commiter information' '
+test_expect_success 'with command using committer information' '
        git config trailer.sign.ifExists "addIfDifferent" &&
        git config trailer.sign.command "echo \"\$GIT_COMMITTER_NAME <\$GIT_COMMITTER_EMAIL>\"" &&
        cat complex_message_body >expected &&
index 997d5fb349dfd7e5ee068d2c34b3e091f678f891..1e47ed2ca27a401bfc177e69ee0966c9b4ac1f09 100755 (executable)
@@ -294,7 +294,7 @@ do
        done
 done
 
-# test that splitting the index dosn't interfere
+# test that splitting the index doesn't interfere
 test_expect_success 'splitting the index results in the same state' '
        write_integration_script &&
        dirty_repo &&
index 5514edcf68be8020ca94e8c51b9c9f2b3102bd09..d8e7a1e5ba85c0a9e7736435c923adb18bb4549c 100755 (executable)
@@ -23,7 +23,8 @@ my ($version, $time) = @ARGV;
 
 if ($version == 1) {
        # convert nanoseconds to seconds
-       $time = int $time / 1000000000;
+       # subtract one second to make sure watchman will return all changes
+       $time = int ($time / 1000000000) - 1;
 } else {
        die "Unsupported query-fsmonitor hook version '$version'.\n" .
            "Falling back to scanning...\n";
@@ -54,18 +55,12 @@ sub launch_watchman {
        #
        # To accomplish this, we're using the "since" generator to use the
        # recency index to select candidate nodes and "fields" to limit the
-       # output to file names only. Then we're using the "expression" term to
-       # further constrain the results.
-       #
-       # The category of transient files that we want to ignore will have a
-       # creation clock (cclock) newer than $time_t value and will also not
-       # currently exist.
+       # output to file names only.
 
        my $query = <<" END";
                ["query", "$git_work_tree", {
                        "since": $time,
-                       "fields": ["name"],
-                       "expression": ["not", ["allof", ["since", $time, "cclock"], ["not", "exists"]]]
+                       "fields": ["name"]
                }]
        END
        
index 997f90b42b3e513328b23f718ac7215eb9a960b8..bd94779611b6fe862ae5fb449edf2f63e2b418a2 100755 (executable)
@@ -1260,7 +1260,7 @@ test_expect_success $PREREQ 'sendemail.identity: --no-identity clears previous i
        grep "To: default@example.com" stdout
 '
 
-test_expect_success $PREREQ 'sendemail.identity: bool identity variable existance overrides' '
+test_expect_success $PREREQ 'sendemail.identity: bool identity variable existence overrides' '
        git -c sendemail.identity=cloud \
                -c sendemail.xmailer=true \
                -c sendemail.cloud.xmailer=false \
index dadc70b7d5705d11437d40a701dd5079247e6357..ca223dca988832867909a65b69d4265289acaccb 100755 (executable)
@@ -275,7 +275,7 @@ $whitespace
     third note for first commit
 EXPECT_END
 
-test_expect_success 'add concatentation notes with M command' '
+test_expect_success 'add concatenation notes with M command' '
 
        git fast-import <input &&
        GIT_NOTES_REF=refs/notes/test git log | grep "^    " > actual &&
index 2e4e21481542779dab3354c0ee9b332f76ad2034..db084fe625a77d7d9c484b4acda6cccd5b5f4b77 100755 (executable)
@@ -541,7 +541,7 @@ test_expect_success 'tree_tag'        '
 
 # NEEDSWORK: not just check return status, but validate the output
 # Note that these tests DO NOTHING other than print a warning that
-# they are ommitting the one tag we asked them to export (because the
+# they are omitting the one tag we asked them to export (because the
 # tags resolve to a tree).  They exist just to make sure we do not
 # abort but instead just warn.
 test_expect_success 'tree_tag-obj'    'git fast-export tree_tag-obj'
index 0796a438bc7759292ba900427304ebf9c814c904..e38cbc97d38e41ae8bdf5c8d11695e06257dfa0b 100755 (executable)
@@ -188,8 +188,8 @@ test_expect_success 'forks: project_index lists all projects (incl. forks)' '
 '
 
 xss() {
-       echo >&2 "Checking $1..." &&
-       gitweb_run "$1" &&
+       echo >&2 "Checking $*..." &&
+       gitweb_run "$@" &&
        if grep "$TAG" gitweb.body; then
                echo >&2 "xss: $TAG should have been quoted in output"
                return 1
@@ -200,7 +200,8 @@ xss() {
 test_expect_success 'xss checks' '
        TAG="<magic-xss-tag>" &&
        xss "a=rss&p=$TAG" &&
-       xss "a=rss&p=foo.git&f=$TAG"
+       xss "a=rss&p=foo.git&f=$TAG" &&
+       xss "" "$TAG+"
 '
 
 test_done
index 3cff1fce1b7464826412ece52ef394a551dce65e..9c9710d8c7b8713f4dfb0cfacd0c07ce71a71226 100755 (executable)
@@ -407,7 +407,7 @@ test_expect_success 'reinit depot' '
 '
 
 #
-# What happens when two files of the same name are overlayed together?
+# What happens when two files of the same name are overlaid together?
 # The last-listed file should take preference.
 #
 # //depot
index 54f8ce18cb9e510edaf7d21f70ddb075a55304e3..ec3eccfd3d67c67dab10859918af960c5e452a38 100755 (executable)
@@ -378,7 +378,7 @@ test_expect_success '__gitdir - finds repo' '
 '
 
 
-test_expect_success '__gitdir - returns error when cant find repo' '
+test_expect_success '__gitdir - returns error when cannot find repo' '
        (
                __git_dir="non-existing" &&
                test_must_fail __gitdir >"$actual"
@@ -945,7 +945,7 @@ test_expect_success 'setup for filtering matching refs' '
        rm -f .git/FETCH_HEAD
 '
 
-test_expect_success '__git_refs - dont filter refs unless told so' '
+test_expect_success '__git_refs - do not filter refs unless told so' '
        cat >expected <<-EOF &&
        HEAD
        master
@@ -1257,7 +1257,7 @@ test_path_completion ()
                # In the following tests calling this function we only
                # care about how __git_complete_index_file() deals with
                # unusual characters in path names.  By requesting only
-               # untracked files we dont have to bother adding any
+               # untracked files we do not have to bother adding any
                # paths to the index in those tests.
                __git_complete_index_file --others &&
                print_comp
@@ -1548,7 +1548,10 @@ test_expect_success 'complete tree filename with metacharacters' '
 '
 
 test_expect_success PERL 'send-email' '
-       test_completion "git send-email --cov" "--cover-letter " &&
+       test_completion "git send-email --cov" <<-\EOF &&
+       --cover-from-description=Z
+       --cover-letter Z
+       EOF
        test_completion "git send-email ma" "master "
 '
 
index e06fa02a0eec2409ac78054d1ce30f9c3cf465b1..e8ae150d7577a63cb1460e39a3a295a96ce91c36 100644 (file)
@@ -404,9 +404,13 @@ unset VISUAL EMAIL LANGUAGE COLUMNS $("$PERL_PATH" -e '
 unset XDG_CACHE_HOME
 unset XDG_CONFIG_HOME
 unset GITPERLLIB
-GIT_AUTHOR_EMAIL=author@example.com
+TEST_AUTHOR_LOCALNAME=author
+TEST_AUTHOR_DOMAIN=example.com
+GIT_AUTHOR_EMAIL=${TEST_AUTHOR_LOCALNAME}@${TEST_AUTHOR_DOMAIN}
 GIT_AUTHOR_NAME='A U Thor'
-GIT_COMMITTER_EMAIL=committer@example.com
+TEST_COMMITTER_LOCALNAME=committer
+TEST_COMMITTER_DOMAIN=example.com
+GIT_COMMITTER_EMAIL=${TEST_COMMITTER_LOCALNAME}@${TEST_COMMITTER_DOMAIN}
 GIT_COMMITTER_NAME='C O Mitter'
 GIT_MERGE_VERBOSITY=5
 GIT_MERGE_AUTOEDIT=no
@@ -1000,6 +1004,12 @@ test_skip () {
                to_skip=t
                skipped_reason="GIT_SKIP_TESTS"
        fi
+       if test -z "$to_skip" && test -n "$run_list" &&
+          ! match_test_selector_list '--run' $test_count "$run_list"
+       then
+               to_skip=t
+               skipped_reason="--run"
+       fi
        if test -z "$to_skip" && test -n "$test_prereq" &&
           ! test_have_prereq "$test_prereq"
        then
@@ -1012,12 +1022,6 @@ test_skip () {
                fi
                skipped_reason="missing $missing_prereq${of_prereq}"
        fi
-       if test -z "$to_skip" && test -n "$run_list" &&
-               ! match_test_selector_list '--run' $test_count "$run_list"
-       then
-               to_skip=t
-               skipped_reason="--run"
-       fi
 
        case "$to_skip" in
        t)
diff --git a/tag.c b/tag.c
index bfa0e3143580f4f817fa0722a77224e81c58b615..71b544467efacd12f36e8c61efdc387db11d1bff 100644 (file)
--- a/tag.c
+++ b/tag.c
@@ -141,7 +141,16 @@ int parse_tag_buffer(struct repository *r, struct tag *item, const void *data, u
 
        if (item->object.parsed)
                return 0;
-       item->object.parsed = 1;
+
+       if (item->tag) {
+               /*
+                * Presumably left over from a previous failed parse;
+                * clear it out in preparation for re-parsing (we'll probably
+                * hit the same error, which lets us tell our current caller
+                * about the problem).
+                */
+               FREE_AND_NULL(item->tag);
+       }
 
        if (size < the_hash_algo->hexsz + 24)
                return -1;
@@ -167,10 +176,15 @@ int parse_tag_buffer(struct repository *r, struct tag *item, const void *data, u
        } else if (!strcmp(type, tag_type)) {
                item->tagged = (struct object *)lookup_tag(r, &oid);
        } else {
-               error("Unknown type %s", type);
-               item->tagged = NULL;
+               return error("unknown tag type '%s' in %s",
+                            type, oid_to_hex(&item->object.oid));
        }
 
+       if (!item->tagged)
+               return error("bad tag pointer to %s in %s",
+                            oid_to_hex(&oid),
+                            oid_to_hex(&item->object.oid));
+
        if (bufptr + 4 < tail && starts_with(bufptr, "tag "))
                ;               /* good */
        else
@@ -187,6 +201,7 @@ int parse_tag_buffer(struct repository *r, struct tag *item, const void *data, u
        else
                item->date = 0;
 
+       item->object.parsed = 1;
        return 0;
 }
 
diff --git a/tar.h b/tar.h
index 3467705e9b0e14a0230473186079e83a582e4345..6b258c4d4a3feb9f4a0319541f015baa41b122ae 100644 (file)
--- a/tar.h
+++ b/tar.h
@@ -1,3 +1,6 @@
+#ifndef TAR_H
+#define TAR_H
+
 #define TYPEFLAG_AUTO          '\0'
 #define TYPEFLAG_REG           '0'
 #define TYPEFLAG_LNK           '2'
@@ -23,3 +26,5 @@ struct ustar_header {
        char devminor[8];       /* 337 */
        char prefix[155];       /* 345 */
 };
+
+#endif /* TAR_H */
index e673bb3980f3c286291809e05f80873852bc3e9c..ef94fa293800b31e3982204f43b13785afebd9bd 100755 (executable)
@@ -22,7 +22,8 @@ my ($version, $time) = @ARGV;
 
 if ($version == 1) {
        # convert nanoseconds to seconds
-       $time = int $time / 1000000000;
+       # subtract one second to make sure watchman will return all changes
+       $time = int ($time / 1000000000) - 1;
 } else {
        die "Unsupported query-fsmonitor hook version '$version'.\n" .
            "Falling back to scanning...\n";
@@ -53,18 +54,12 @@ sub launch_watchman {
        #
        # To accomplish this, we're using the "since" generator to use the
        # recency index to select candidate nodes and "fields" to limit the
-       # output to file names only. Then we're using the "expression" term to
-       # further constrain the results.
-       #
-       # The category of transient files that we want to ignore will have a
-       # creation clock (cclock) newer than $time_t value and will also not
-       # currently exist.
+       # output to file names only.
 
        my $query = <<" END";
                ["query", "$git_work_tree", {
                        "since": $time,
-                       "fields": ["name"],
-                       "expression": ["not", ["allof", ["since", $time, "cclock"], ["not", "exists"]]]
+                       "fields": ["name"]
                }]
        END
 
index 6948fd41086f91e7b2439918666cad0320909a6a..dc6e75ef13151fe011c738f8e3dbe706e24c4773 100644 (file)
@@ -19,7 +19,7 @@ static int tr2sid_nr_git_parents;
  *    "H<first_8_chars_of_sha1_of_hostname>"
  *    "Localhost" when no hostname.
  *
- * where <process> is a 9 character string containing the least signifcant
+ * where <process> is a 9 character string containing the least significant
  * 32 bits in the process-id.
  *    "P<pid>"
  * (This is an abribrary choice.  On most systems pid_t is a 32 bit value,
index ffac8029ad77b66aa9f94612d896ccda9e31ade0..a8018f18cc87e869d63bc92abf0d4434c320134e 100644 (file)
@@ -26,12 +26,9 @@ static int tr2env_perf_be_brief;
 #define TR2FMT_PERF_REPO_WIDTH (3)
 #define TR2FMT_PERF_CATEGORY_WIDTH (12)
 
-#define TR2_DOTS_BUFFER_SIZE (100)
 #define TR2_INDENT (2)
 #define TR2_INDENT_LENGTH(ctx) (((ctx)->nr_open_regions - 1) * TR2_INDENT)
 
-static struct strbuf dots = STRBUF_INIT;
-
 static int fn_init(void)
 {
        int want = tr2_dst_trace_want(&tr2dst_perf);
@@ -41,8 +38,6 @@ static int fn_init(void)
        if (!want)
                return want;
 
-       strbuf_addchars(&dots, '.', TR2_DOTS_BUFFER_SIZE);
-
        brief = tr2_sysenv_get(TR2_SYSENV_PERF_BRIEF);
        if (brief && *brief &&
            ((want_brief = git_parse_maybe_bool(brief)) != -1))
@@ -54,8 +49,6 @@ static int fn_init(void)
 static void fn_term(void)
 {
        tr2_dst_trace_disable(&tr2dst_perf);
-
-       strbuf_release(&dots);
 }
 
 /*
@@ -138,14 +131,8 @@ static void perf_fmt_prepare(const char *event_name,
        strbuf_addf(buf, "%-*.*s | ", TR2FMT_PERF_CATEGORY_WIDTH,
                    TR2FMT_PERF_CATEGORY_WIDTH, (category ? category : ""));
 
-       if (ctx->nr_open_regions > 0) {
-               int len_indent = TR2_INDENT_LENGTH(ctx);
-               while (len_indent > dots.len) {
-                       strbuf_addbuf(buf, &dots);
-                       len_indent -= dots.len;
-               }
-               strbuf_addf(buf, "%.*s", len_indent, dots.buf);
-       }
+       if (ctx->nr_open_regions > 0)
+               strbuf_addchars(buf, '.', TR2_INDENT_LENGTH(ctx));
 }
 
 static void perf_io_write_fl(const char *file, int line, const char *event_name,
index 33ea7810d8cfbb819fcfc76ca8e71aa03c66f3be..28cbd19570a95815c5d2d40a4bb305e8be143d2a 100644 (file)
@@ -275,9 +275,9 @@ static int check_submodule_move_head(const struct cache_entry *ce,
 }
 
 /*
- * Preform the loading of the repository's gitmodules file.  This function is
+ * Perform the loading of the repository's gitmodules file.  This function is
  * used by 'check_update()' to perform loading of the gitmodules file in two
- * differnt situations:
+ * different situations:
  * (1) before removing entries from the working tree if the gitmodules file has
  *     been marked for removal.  This situation is specified by 'state' == NULL.
  * (2) before checking out entries to the working tree if the gitmodules file
diff --git a/url.c b/url.c
index e34e5e751737aeb10a6afecc0a2cdf0ec78fa5fc..e04bd60b6bead493e3236949b5b4a837c729c146 100644 (file)
--- a/url.c
+++ b/url.c
@@ -5,7 +5,7 @@ int is_urlschemechar(int first_flag, int ch)
 {
        /*
         * The set of valid URL schemes, as per STD66 (RFC3986) is
-        * '[A-Za-z][A-Za-z0-9+.-]*'. But use sightly looser check
+        * '[A-Za-z][A-Za-z0-9+.-]*'. But use slightly looser check
         * of '[A-Za-z0-9][A-Za-z0-9+.-]*' because earlier version
         * of check used '[A-Za-z0-9]+' so not to break any remote
         * helpers.
diff --git a/usage.c b/usage.c
index 2fdb20086bd695eaad57ef7574ce4e02655e96c1..58fb5fff5f245c33898e27c85296a4a47325c34e 100644 (file)
--- a/usage.c
+++ b/usage.c
@@ -9,14 +9,26 @@
 void vreportf(const char *prefix, const char *err, va_list params)
 {
        char msg[4096];
-       char *p;
+       char *p, *pend = msg + sizeof(msg);
+       size_t prefix_len = strlen(prefix);
 
-       vsnprintf(msg, sizeof(msg), err, params);
-       for (p = msg; *p; p++) {
+       if (sizeof(msg) <= prefix_len) {
+               fprintf(stderr, "BUG!!! too long a prefix '%s'\n", prefix);
+               abort();
+       }
+       memcpy(msg, prefix, prefix_len);
+       p = msg + prefix_len;
+       if (vsnprintf(p, pend - p, err, params) < 0)
+               *p = '\0'; /* vsnprintf() failed, clip at prefix */
+
+       for (; p != pend - 1 && *p; p++) {
                if (iscntrl(*p) && *p != '\t' && *p != '\n')
                        *p = '?';
        }
-       fprintf(stderr, "%s%s\n", prefix, msg);
+
+       *(p++) = '\n'; /* we no longer need a NUL */
+       fflush(stderr);
+       write_in_full(2, msg, p - msg);
 }
 
 static NORETURN void usage_builtin(const char *err, va_list params)
index e187d356f6fff746547f14129cbe7668e11b4adb..577053c10ab72b28b83b1518825a99a7f6016e8e 100644 (file)
@@ -32,6 +32,18 @@ PATTERNS("dts",
         /* Property names and math operators */
         "[a-zA-Z0-9,._+?#-]+"
         "|[-+*/%&^|!~]|>>|<<|&&|\\|\\|"),
+PATTERNS("elixir",
+        "^[ \t]*((def(macro|module|impl|protocol|p)?|test)[ \t].*)$",
+        /* Atoms, names, and module attributes */
+        "|[@:]?[a-zA-Z0-9@_?!]+"
+        /* Numbers with specific base */
+        "|[-+]?0[xob][0-9a-fA-F]+"
+        /* Numbers */
+        "|[-+]?[0-9][0-9_.]*([eE][-+]?[0-9_]+)?"
+        /* Operators and atoms that represent them */
+        "|:?(\\+\\+|--|\\.\\.|~~~|<>|\\^\\^\\^|<?\\|>|<<<?|>?>>|<<?~|~>?>|<~>|<=|>=|===?|!==?|=~|&&&?|\\|\\|\\|?|=>|<-|\\\\\\\\|->)"
+        /* Not real operators, but should be grouped */
+        "|:?%[A-Za-z0-9_.]\\{\\}?"),
 IPATTERN("fortran",
         "!^([C*]|[ \t]*!)\n"
         "!^[ \t]*MODULE[ \t]+PROCEDURE[ \t]\n"
diff --git a/utf8.c b/utf8.c
index 5c8f151f755960abadf210fcb9101ca6c6aec45a..5b39361ada0bbab3dc4df90e64cc6173bb465ff4 100644 (file)
--- a/utf8.c
+++ b/utf8.c
@@ -411,11 +411,10 @@ out:
  */
 static int same_utf_encoding(const char *src, const char *dst)
 {
-       if (istarts_with(src, "utf") && istarts_with(dst, "utf")) {
-               /* src[3] or dst[3] might be '\0' */
-               int i = (src[3] == '-' ? 4 : 3);
-               int j = (dst[3] == '-' ? 4 : 3);
-               return !strcasecmp(src+i, dst+j);
+       if (skip_iprefix(src, "utf", &src) && skip_iprefix(dst, "utf", &dst)) {
+               skip_prefix(src, "-", &src);
+               skip_prefix(dst, "-", &dst);
+               return !strcasecmp(src, dst);
        }
        return 0;
 }
index ede4246bbd3397086f90217539a2d07a35a4b986..93df26900c2bfa923f1e871924ef319a6786fa72 100644 (file)
@@ -44,7 +44,7 @@ void discard_hunk_line(void *priv,
  * Compare the strings l1 with l2 which are of size s1 and s2 respectively.
  * Returns 1 if the strings are deemed equal, 0 otherwise.
  * The `flags` given as XDF_WHITESPACE_FLAGS determine how white spaces
- * are treated for the comparision.
+ * are treated for the comparison.
  */
 int xdiff_compare_lines(const char *l1, long s1,
                        const char *l2, long s2, long flags);