summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/Makefile1
-rw-r--r--Documentation/MyFirstContribution.txt53
-rw-r--r--Documentation/MyFirstObjectWalk.txt906
-rw-r--r--Documentation/RelNotes/2.25.0.txt68
-rw-r--r--Documentation/config/format.txt6
-rw-r--r--Documentation/fetch-options.txt4
-rw-r--r--Documentation/git-am.txt2
-rw-r--r--Documentation/git-bisect.txt2
-rw-r--r--Documentation/git-diff.txt2
-rw-r--r--Documentation/git-format-patch.txt22
-rw-r--r--Documentation/git-multi-pack-index.txt6
-rw-r--r--Documentation/git-notes.txt6
-rw-r--r--Documentation/git-rev-parse.txt7
-rw-r--r--Documentation/git-update-index.txt6
-rw-r--r--Documentation/gitrepository-layout.txt10
-rw-r--r--Documentation/gitsubmodules.txt2
-rw-r--r--Documentation/pretty-formats.txt6
-rw-r--r--Documentation/technical/api-config.txt319
-rw-r--r--Documentation/technical/api-grep.txt8
-rw-r--r--Documentation/technical/api-object-access.txt15
-rw-r--r--Documentation/technical/api-quote.txt10
-rw-r--r--Documentation/technical/api-xdiff-interface.txt7
-rwxr-xr-xGIT-VERSION-GEN2
l---------RelNotes2
-rw-r--r--apply.c2
-rw-r--r--builtin/blame.c32
-rw-r--r--builtin/clone.c8
-rw-r--r--builtin/commit-graph.c1
-rw-r--r--builtin/commit.c22
-rw-r--r--builtin/fetch.c12
-rw-r--r--builtin/fsck.c126
-rw-r--r--builtin/log.c125
-rw-r--r--builtin/multi-pack-index.c18
-rw-r--r--builtin/notes.c2
-rw-r--r--builtin/rebase.c7
-rw-r--r--builtin/repack.c9
-rw-r--r--builtin/rev-parse.c11
-rw-r--r--builtin/stash.c5
-rw-r--r--builtin/update-index.c6
-rw-r--r--builtin/worktree.c2
-rw-r--r--command-list.txt1
-rw-r--r--commit-graph.c3
-rw-r--r--commit.c57
-rw-r--r--config.h335
-rw-r--r--fetch-pack.c47
-rw-r--r--fsck.c386
-rw-r--r--fsck.h39
-rwxr-xr-xgit-legacy-stash.sh3
-rw-r--r--graph.c646
-rw-r--r--http.c18
-rw-r--r--merge-recursive.c104
-rw-r--r--midx.c71
-rw-r--r--midx.h10
-rw-r--r--path.c128
-rw-r--r--pretty.c9
-rw-r--r--sequencer.c24
-rw-r--r--sequencer.h3
-rw-r--r--t/lib-rebase.sh28
-rw-r--r--t/oid-info/hash-info9
-rwxr-xr-xt/t0060-path-utils.sh4
-rwxr-xr-xt/t1305-config-include.sh2
-rwxr-xr-xt/t1400-update-ref.sh18
-rwxr-xr-xt/t1450-fsck.sh2
-rwxr-xr-xt/t1500-rev-parse.sh15
-rwxr-xr-xt/t2400-worktree-add.sh24
-rwxr-xr-xt/t3301-notes.sh46
-rwxr-xr-xt/t3404-rebase-interactive.sh596
-rwxr-xr-xt/t3429-rebase-edit-todo.sh2
-rwxr-xr-xt/t3430-rebase-merges.sh2
-rwxr-xr-xt/t3903-stash.sh11
-rwxr-xr-xt/t4010-diff-pathspec.sh20
-rwxr-xr-xt/t4011-diff-symlink.sh40
-rwxr-xr-xt/t4014-format-patch.sh172
-rwxr-xr-xt/t4015-diff-whitespace.sh89
-rwxr-xr-xt/t4027-diff-submodule.sh16
-rwxr-xr-xt/t4034-diff-words.sh93
-rwxr-xr-xt/t4038-diff-combined.sh19
-rwxr-xr-xt/t4039-diff-assume-unchanged.sh3
-rwxr-xr-xt/t4044-diff-index-unique-abbrev.sh46
-rwxr-xr-xt/t4045-diff-relative.sh2
-rwxr-xr-xt/t4048-diff-combined-binary.sh58
-rwxr-xr-xt/t4108-apply-threeway.sh55
-rwxr-xr-xt/t4202-log.sh2
-rwxr-xr-xt/t4203-mailmap.sh122
-rwxr-xr-xt/t4214-log-graph-octopus.sh62
-rwxr-xr-xt/t4215-log-skewed-merges.sh243
-rwxr-xr-xt/t5318-commit-graph.sh2
-rwxr-xr-xt/t5319-multi-pack-index.sh69
-rwxr-xr-xt/t5616-partial-clone.sh8
-rwxr-xr-xt/t6006-rev-list-format.sh28
-rwxr-xr-xt/t6016-rev-list-graph-simplify-history.sh30
-rwxr-xr-xt/t6042-merge-rename-corner-cases.sh111
-rwxr-xr-xt/t6043-merge-rename-directories.sh568
-rwxr-xr-xt/t6046-merge-skip-unneeded-updates.sh135
-rwxr-xr-xt/t6102-rev-list-unexpected-objects.sh2
-rwxr-xr-xt/t7012-skip-worktree-writing.sh15
-rwxr-xr-xt/t7519/fsmonitor-watchman13
-rwxr-xr-xt/t9902-completion.sh5
-rw-r--r--t/test-lib.sh8
-rw-r--r--tag.c21
-rwxr-xr-xtemplates/hooks--fsmonitor-watchman.sample13
-rw-r--r--trace2/tr2_tgt_perf.c17
-rw-r--r--usage.c20
103 files changed, 4545 insertions, 2065 deletions
diff --git a/Documentation/Makefile b/Documentation/Makefile
index 06d85ad..8fe829c 100644
--- a/Documentation/Makefile
+++ b/Documentation/Makefile
@@ -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
diff --git a/Documentation/MyFirstContribution.txt b/Documentation/MyFirstContribution.txt
index 5e9b808..b55837e 100644
--- a/Documentation/MyFirstContribution.txt
+++ b/Documentation/MyFirstContribution.txt
@@ -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
index 0000000..4d24dae
--- /dev/null
+++ b/Documentation/MyFirstObjectWalk.txt
@@ -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
diff --git a/Documentation/RelNotes/2.25.0.txt b/Documentation/RelNotes/2.25.0.txt
new file mode 100644
index 0000000..b61b69f
--- /dev/null
+++ b/Documentation/RelNotes/2.25.0.txt
@@ -0,0 +1,68 @@
+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 '@'.
+
+
+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.
+
+
+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).
+
+ * 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).
diff --git a/Documentation/config/format.txt b/Documentation/config/format.txt
index 40cad92..513fcd8 100644
--- a/Documentation/config/format.txt
+++ b/Documentation/config/format.txt
@@ -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.
diff --git a/Documentation/fetch-options.txt b/Documentation/fetch-options.txt
index 43b9ff3..a2f7862 100644
--- a/Documentation/fetch-options.txt
+++ b/Documentation/fetch-options.txt
@@ -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
diff --git a/Documentation/git-am.txt b/Documentation/git-am.txt
index fc3b993..fc5750b 100644
--- a/Documentation/git-am.txt
+++ b/Documentation/git-am.txt
@@ -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
diff --git a/Documentation/git-bisect.txt b/Documentation/git-bisect.txt
index 4b45d83..7586c5a 100644
--- a/Documentation/git-bisect.txt
+++ b/Documentation/git-bisect.txt
@@ -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
diff --git a/Documentation/git-diff.txt b/Documentation/git-diff.txt
index 72179d9..37781cf 100644
--- a/Documentation/git-diff.txt
+++ b/Documentation/git-diff.txt
@@ -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>...]::
diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt
index 2035d4d..00bdf9b 100644
--- a/Documentation/git-format-patch.txt
+++ b/Documentation/git-format-patch.txt
@@ -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
------------
diff --git a/Documentation/git-multi-pack-index.txt b/Documentation/git-multi-pack-index.txt
index 233b2b7..642d9ac 100644
--- a/Documentation/git-multi-pack-index.txt
+++ b/Documentation/git-multi-pack-index.txt
@@ -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::
diff --git a/Documentation/git-notes.txt b/Documentation/git-notes.txt
index f56a5a9..ced2e82 100644
--- a/Documentation/git-notes.txt
+++ b/Documentation/git-notes.txt
@@ -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>`
diff --git a/Documentation/git-rev-parse.txt b/Documentation/git-rev-parse.txt
index e72d332..9985477 100644
--- a/Documentation/git-rev-parse.txt
+++ b/Documentation/git-rev-parse.txt
@@ -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
~~~~~~~~~~~~~
diff --git a/Documentation/git-update-index.txt b/Documentation/git-update-index.txt
index 1c4d146..0839344 100644
--- a/Documentation/git-update-index.txt
+++ b/Documentation/git-update-index.txt
@@ -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
diff --git a/Documentation/gitrepository-layout.txt b/Documentation/gitrepository-layout.txt
index d6388f1..1a2ef4c 100644
--- a/Documentation/gitrepository-layout.txt
+++ b/Documentation/gitrepository-layout.txt
@@ -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`.
diff --git a/Documentation/gitsubmodules.txt b/Documentation/gitsubmodules.txt
index 0a89020..c476f89 100644
--- a/Documentation/gitsubmodules.txt
+++ b/Documentation/gitsubmodules.txt
@@ -3,7 +3,7 @@ gitsubmodules(7)
NAME
----
-gitsubmodules - mounting one repository inside another
+gitsubmodules - Mounting one repository inside another
SYNOPSIS
--------
diff --git a/Documentation/pretty-formats.txt b/Documentation/pretty-formats.txt
index b87e2e8..31c6e8d 100644
--- a/Documentation/pretty-formats.txt
+++ b/Documentation/pretty-formats.txt
@@ -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
diff --git a/Documentation/technical/api-config.txt b/Documentation/technical/api-config.txt
deleted file mode 100644
index 7d20716..0000000
--- a/Documentation/technical/api-config.txt
+++ /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
index a69cc89..0000000
--- a/Documentation/technical/api-grep.txt
+++ /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
index 5b29622..0000000
--- a/Documentation/technical/api-object-access.txt
+++ /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
index e8a1bce..0000000
--- a/Documentation/technical/api-quote.txt
+++ /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)
diff --git a/Documentation/technical/api-xdiff-interface.txt b/Documentation/technical/api-xdiff-interface.txt
deleted file mode 100644
index 6296eca..0000000
--- a/Documentation/technical/api-xdiff-interface.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-xdiff interface API
-===================
-
-Talk about our calling convention to xdiff library, including
-xdiff_emit_consume_fn.
-
-(Dscho, JC)
diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN
index 5048d9b..22e8d83 100755
--- a/GIT-VERSION-GEN
+++ b/GIT-VERSION-GEN
@@ -1,7 +1,7 @@
#!/bin/sh
GVF=GIT-VERSION-FILE
-DEF_VER=v2.24.0
+DEF_VER=v2.24.GIT
LF='
'
diff --git a/RelNotes b/RelNotes
index fc657e7..091dd02 120000
--- 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/apply.c b/apply.c
index f8a046a..b9291f5 100644
--- 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)
diff --git a/builtin/blame.c b/builtin/blame.c
index e946ba6..bf1cecd 100644
--- a/builtin/blame.c
+++ b/builtin/blame.c
@@ -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")),
diff --git a/builtin/clone.c b/builtin/clone.c
index c46ee29..b24f04c 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -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);
@@ -981,7 +981,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 +992,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 +1020,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 {
diff --git a/builtin/commit-graph.c b/builtin/commit-graph.c
index addc8d4..ec0fc93 100644
--- a/builtin/commit-graph.c
+++ b/builtin/commit-graph.c
@@ -8,7 +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>"),
diff --git a/builtin/commit.c b/builtin/commit.c
index e588bc6..294dc57 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -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};
diff --git a/builtin/fetch.c b/builtin/fetch.c
index 863c858..bc750b6 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -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()
};
@@ -1411,7 +1414,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 +1602,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) {
@@ -1865,7 +1869,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));
diff --git a/builtin/fsck.c b/builtin/fsck.c
index 18403a9..8d13794 100644
--- a/builtin/fsck.c
+++ b/builtin/fsck.c
@@ -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)
diff --git a/builtin/log.c b/builtin/log.c
index 89873d2..a26f223 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -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",
diff --git a/builtin/multi-pack-index.c b/builtin/multi-pack-index.c
index b1ea1a6..5bf88cd 100644
--- a/builtin/multi-pack-index.c
+++ b/builtin/multi-pack-index.c
@@ -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]);
}
diff --git a/builtin/notes.c b/builtin/notes.c
index 02e97f5..95456f3 100644
--- a/builtin/notes.c
+++ b/builtin/notes.c
@@ -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);
}
diff --git a/builtin/rebase.c b/builtin/rebase.c
index 4a20582..e755087 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -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")),
diff --git a/builtin/repack.c b/builtin/repack.c
index 094c2f8..0781763 100644
--- a/builtin/repack.c
+++ b/builtin/repack.c
@@ -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);
diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c
index 308c67e..85ce209 100644
--- a/builtin/rev-parse.c
+++ b/builtin/rev-parse.c
@@ -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;
diff --git a/builtin/stash.c b/builtin/stash.c
index 4e80617..d913487 100644
--- a/builtin/stash.c
+++ b/builtin/stash.c
@@ -1088,8 +1088,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);
diff --git a/builtin/update-index.c b/builtin/update-index.c
index 49302d9..d527b8f 100644
--- a/builtin/update-index.c
+++ b/builtin/update-index.c
@@ -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,
diff --git a/builtin/worktree.c b/builtin/worktree.c
index 4de44f5..d6bc526 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -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;
diff --git a/command-list.txt b/command-list.txt
index a9ac72b..72e435c 100644
--- a/command-list.txt
+++ b/command-list.txt
@@ -203,6 +203,7 @@ gitmodules guide
gitnamespaces guide
gitrepository-layout guide
gitrevisions guide
+gitsubmodules guide
gittutorial-2 guide
gittutorial guide
gitworkflows guide
diff --git a/commit-graph.c b/commit-graph.c
index 0aea7b2..a0f8685 100644
--- a/commit-graph.c
+++ b/commit-graph.c
@@ -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;
diff --git a/commit.c b/commit.c
index 40890ae..434ec03 100644
--- 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;
+}
diff --git a/config.h b/config.h
index f0ed464..91fd4c5 100644
--- 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) \
diff --git a/fetch-pack.c b/fetch-pack.c
index 0130b44..f80e2d1 100644
--- a/fetch-pack.c
+++ b/fetch-pack.c
@@ -756,8 +756,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;
@@ -819,7 +844,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 {
@@ -873,6 +904,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;
}
@@ -1008,7 +1047,7 @@ 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:
@@ -1464,7 +1503,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;
diff --git a/fsck.c b/fsck.c
index cdb7d8d..ac4ba4c 100644
--- 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 b95595a..69cf715 100644
--- 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
diff --git a/git-legacy-stash.sh b/git-legacy-stash.sh
index 07ad4a5..53fa574 100755
--- a/git-legacy-stash.sh
+++ b/git-legacy-stash.sh
@@ -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"
) ) ||
diff --git a/graph.c b/graph.c
index f531354..e3fd0ea 100644
--- 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 {
@@ -175,9 +203,63 @@ struct git_graph {
*/
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
@@ -1148,20 +1237,25 @@ 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
diff --git a/http.c b/http.c
index 027a86d..5f34816 100644
--- 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/merge-recursive.c b/merge-recursive.c
index 42be7c9..11869ad 100644
--- a/merge-recursive.c
+++ b/merge-recursive.c
@@ -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 f29afc0..37ec286 100644
--- 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 f0ae656..e6fa356 100644
--- 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);
diff --git a/path.c b/path.c
index e3da1f3..0607156 100644
--- 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);
diff --git a/pretty.c b/pretty.c
index b32f036..93eb6e8 100644
--- 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;
diff --git a/sequencer.c b/sequencer.c
index 9d5964f..8952cfa 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -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);
diff --git a/sequencer.h b/sequencer.h
index 574260f..9f9ae29 100644
--- a/sequencer.h
+++ b/sequencer.h
@@ -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 */
diff --git a/t/lib-rebase.sh b/t/lib-rebase.sh
index 6d87961..b72c051 100644
--- a/t/lib-rebase.sh
+++ b/t/lib-rebase.sh
@@ -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
+}
diff --git a/t/oid-info/hash-info b/t/oid-info/hash-info
index ccdbfdf..d0736dd 100644
--- a/t/oid-info/hash-info
+++ b/t/oid-info/hash-info
@@ -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
diff --git a/t/t0060-path-utils.sh b/t/t0060-path-utils.sh
index c7b53e4..553e94b 100755
--- a/t/t0060-path-utils.sh
+++ b/t/t0060-path-utils.sh
@@ -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
diff --git a/t/t1305-config-include.sh b/t/t1305-config-include.sh
index d20b4d1..f1e1b28 100755
--- a/t/t1305-config-include.sh
+++ b/t/t1305-config-include.sh
@@ -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
'
diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh
index 1fbd940..69a7f27 100755
--- a/t/t1400-update-ref.sh
+++ b/t/t1400-update-ref.sh
@@ -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"
diff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh
index 50d28e6..7c7ff7e 100755
--- a/t/t1450-fsck.sh
+++ b/t/t1450-fsck.sh
@@ -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
)
'
diff --git a/t/t1500-rev-parse.sh b/t/t1500-rev-parse.sh
index 01abee5..0177fd8 100755
--- a/t/t1500-rev-parse.sh
+++ b/t/t1500-rev-parse.sh
@@ -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 &&
diff --git a/t/t2400-worktree-add.sh b/t/t2400-worktree-add.sh
index e819ba7..8a98314 100755
--- a/t/t2400-worktree-add.sh
+++ b/t/t2400-worktree-add.sh
@@ -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
diff --git a/t/t3301-notes.sh b/t/t3301-notes.sh
index d3fa298..d66a5f6 100755
--- a/t/t3301-notes.sh
+++ b/t/t3301-notes.sh
@@ -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^)"
@@ -901,11 +919,29 @@ test_expect_success 'allow overwrite with "git notes copy -f"' '
${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
+ Author: A U Thor <author@example.com>
+ Date: Thu Apr 7 15:23:13 2005 -0700
+
+ ${indent}11th
+
+ Notes:
${indent}yet another note
${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' '
diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh
index d2dfbe4..bf0dc75 100755
--- a/t/t3404-rebase-interactive.sh
+++ b/t/t3404-rebase-interactive.sh
@@ -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
diff --git a/t/t3429-rebase-edit-todo.sh b/t/t3429-rebase-edit-todo.sh
index 8739cb6..aaeac6e 100755
--- a/t/t3429-rebase-edit-todo.sh
+++ b/t/t3429-rebase-edit-todo.sh
@@ -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 &&
diff --git a/t/t3430-rebase-merges.sh b/t/t3430-rebase-merges.sh
index 9efcf48..a30d27e 100755
--- a/t/t3430-rebase-merges.sh
+++ b/t/t3430-rebase-merges.sh
@@ -408,7 +408,7 @@ test_expect_success 'octopus merges' '
| | * three
| * | two
| |/
- * | one
+ * / one
|/
o before-octopus
EOF
diff --git a/t/t3903-stash.sh b/t/t3903-stash.sh
index 580bfbd..a4da72f 100755
--- a/t/t3903-stash.sh
+++ b/t/t3903-stash.sh
@@ -1269,4 +1269,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
diff --git a/t/t4010-diff-pathspec.sh b/t/t4010-diff-pathspec.sh
index 281f8fa..e5ca359 100755
--- a/t/t4010-diff-pathspec.sh
+++ b/t/t4010-diff-pathspec.sh
@@ -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' \
diff --git a/t/t4011-diff-symlink.sh b/t/t4011-diff-symlink.sh
index 5ae19b9..717034b 100755
--- a/t/t4011-diff-symlink.sh
+++ b/t/t4011-diff-symlink.sh
@@ -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 @@
diff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh
index b896999..69267b1 100755
--- a/t/t4014-format-patch.sh
+++ b/t/t4014-format-patch.sh
@@ -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 &&
diff --git a/t/t4015-diff-whitespace.sh b/t/t4015-diff-whitespace.sh
index 6b087df..eadaf57 100755
--- a/t/t4015-diff-whitespace.sh
+++ b/t/t4015-diff-whitespace.sh
@@ -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>
diff --git a/t/t4027-diff-submodule.sh b/t/t4027-diff-submodule.sh
index 9aa8e2b..e29deaf 100755
--- a/t/t4027-diff-submodule.sh
+++ b/t/t4027-diff-submodule.sh
@@ -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
diff --git a/t/t4034-diff-words.sh b/t/t4034-diff-words.sh
index 9a93c2a..fb145aa 100755
--- a/t/t4034-diff-words.sh
+++ b/t/t4034-diff-words.sh
@@ -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
'
diff --git a/t/t4038-diff-combined.sh b/t/t4038-diff-combined.sh
index b9d876e..b5a5689 100755
--- a/t/t4038-diff-combined.sh
+++ b/t/t4038-diff-combined.sh
@@ -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
'
diff --git a/t/t4039-diff-assume-unchanged.sh b/t/t4039-diff-assume-unchanged.sh
index 53ac44b..0eb0314 100755
--- a/t/t4039-diff-assume-unchanged.sh
+++ b/t/t4039-diff-assume-unchanged.sh
@@ -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' '
diff --git a/t/t4044-diff-index-unique-abbrev.sh b/t/t4044-diff-index-unique-abbrev.sh
index 647905e..4701796 100755
--- a/t/t4044-diff-index-unique-abbrev.sh
+++ b/t/t4044-diff-index-unique-abbrev.sh
@@ -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' '
diff --git a/t/t4045-diff-relative.sh b/t/t4045-diff-relative.sh
index 36f8ed8..2588087 100755
--- a/t/t4045-diff-relative.sh
+++ b/t/t4045-diff-relative.sh
@@ -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 &&
diff --git a/t/t4048-diff-combined-binary.sh b/t/t4048-diff-combined-binary.sh
index 87a8949..7f9ad9f 100755
--- a/t/t4048-diff-combined-binary.sh
+++ b/t/t4048-diff-combined-binary.sh
@@ -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 @@@
diff --git a/t/t4108-apply-threeway.sh b/t/t4108-apply-threeway.sh
index fa5d4ef..d7349ce 100755
--- a/t/t4108-apply-threeway.sh
+++ b/t/t4108-apply-threeway.sh
@@ -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 &&
diff --git a/t/t4202-log.sh b/t/t4202-log.sh
index e803ba4..ab0d021 100755
--- a/t/t4202-log.sh
+++ b/t/t4202-log.sh
@@ -667,7 +667,7 @@ cat > expect <<\EOF
* | | fifth
* | | fourth
|/ /
-* | third
+* / third
|/
* second
* initial
diff --git a/t/t4203-mailmap.sh b/t/t4203-mailmap.sh
index 918ada6..586c3a8 100755
--- a/t/t4203-mailmap.sh
+++ b/t/t4203-mailmap.sh
@@ -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
diff --git a/t/t4214-log-graph-octopus.sh b/t/t4214-log-graph-octopus.sh
index 3ae8e51..40d27db 100755
--- a/t/t4214-log-graph-octopus.sh
+++ b/t/t4214-log-graph-octopus.sh
@@ -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
index 0000000..18709a7
--- /dev/null
+++ b/t/t4215-log-skewed-merges.sh
@@ -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
diff --git a/t/t5318-commit-graph.sh b/t/t5318-commit-graph.sh
index d42b3ef..127b404 100755
--- a/t/t5318-commit-graph.sh
+++ b/t/t5318-commit-graph.sh
@@ -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
)
'
diff --git a/t/t5319-multi-pack-index.sh b/t/t5319-multi-pack-index.sh
index c72ca04..cd2f87b 100755
--- a/t/t5319-multi-pack-index.sh
+++ b/t/t5319-multi-pack-index.sh
@@ -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 &&
diff --git a/t/t5616-partial-clone.sh b/t/t5616-partial-clone.sh
index 79f7b65..eaa33a8 100755
--- a/t/t5616-partial-clone.sh
+++ b/t/t5616-partial-clone.sh
@@ -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 &&
diff --git a/t/t6006-rev-list-format.sh b/t/t6006-rev-list-format.sh
index cfb74d0..ebdc49c 100755
--- a/t/t6006-rev-list-format.sh
+++ b/t/t6006-rev-list-format.sh
@@ -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
'
diff --git a/t/t6016-rev-list-graph-simplify-history.sh b/t/t6016-rev-list-graph-simplify-history.sh
index f7181d1..f5e6e92 100755
--- a/t/t6016-rev-list-graph-simplify-history.sh
+++ b/t/t6016-rev-list-graph-simplify-history.sh
@@ -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 &&
diff --git a/t/t6042-merge-rename-corner-cases.sh b/t/t6042-merge-rename-corner-cases.sh
index c5b57f4..b047cf1 100755
--- a/t/t6042-merge-rename-corner-cases.sh
+++ b/t/t6042-merge-rename-corner-cases.sh
@@ -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 &&
diff --git a/t/t6043-merge-rename-directories.sh b/t/t6043-merge-rename-directories.sh
index c966147..bd2f97b 100755
--- a/t/t6043-merge-rename-directories.sh
+++ b/t/t6043-merge-rename-directories.sh
@@ -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 &&
@@ -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 &&
@@ -948,7 +960,7 @@ test_expect_success '5a-check: Merge directories, other side adds files to origi
# 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 &&
@@ -2096,7 +2123,7 @@ test_expect_success '8b-check: Dual-directory rename, one into the others way, w
# 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 &&
diff --git a/t/t6046-merge-skip-unneeded-updates.sh b/t/t6046-merge-skip-unneeded-updates.sh
index 3a47623..b7e4669 100755
--- a/t/t6046-merge-skip-unneeded-updates.sh
+++ b/t/t6046-merge-skip-unneeded-updates.sh
@@ -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 &&
diff --git a/t/t6102-rev-list-unexpected-objects.sh b/t/t6102-rev-list-unexpected-objects.sh
index 28611c9..52cde09 100755
--- a/t/t6102-rev-list-unexpected-objects.sh
+++ b/t/t6102-rev-list-unexpected-objects.sh
@@ -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
'
diff --git a/t/t7012-skip-worktree-writing.sh b/t/t7012-skip-worktree-writing.sh
index 9d1abe5..7476781 100755
--- a/t/t7012-skip-worktree-writing.sh
+++ b/t/t7012-skip-worktree-writing.sh
@@ -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
diff --git a/t/t7519/fsmonitor-watchman b/t/t7519/fsmonitor-watchman
index 5514edc..d8e7a1e 100755
--- a/t/t7519/fsmonitor-watchman
+++ b/t/t7519/fsmonitor-watchman
@@ -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
diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh
index 54f8ce1..e90ac56 100755
--- a/t/t9902-completion.sh
+++ b/t/t9902-completion.sh
@@ -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 "
'
diff --git a/t/test-lib.sh b/t/test-lib.sh
index e06fa02..46c4440 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -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
diff --git a/tag.c b/tag.c
index bfa0e31..71b5444 100644
--- 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/templates/hooks--fsmonitor-watchman.sample b/templates/hooks--fsmonitor-watchman.sample
index e673bb3..ef94fa2 100755
--- a/templates/hooks--fsmonitor-watchman.sample
+++ b/templates/hooks--fsmonitor-watchman.sample
@@ -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
diff --git a/trace2/tr2_tgt_perf.c b/trace2/tr2_tgt_perf.c
index ffac802..a8018f1 100644
--- a/trace2/tr2_tgt_perf.c
+++ b/trace2/tr2_tgt_perf.c
@@ -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,
diff --git a/usage.c b/usage.c
index 2fdb200..58fb5ff 100644
--- 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)