Merge branch 'nd/fetch-multi-gc-once'
authorJunio C Hamano <gitster@pobox.com>
Tue, 9 Jul 2019 22:25:42 +0000 (15:25 -0700)
committerJunio C Hamano <gitster@pobox.com>
Tue, 9 Jul 2019 22:25:42 +0000 (15:25 -0700)
"git fetch" that grabs from a group of remotes learned to run the
auto-gc only once at the very end.

* nd/fetch-multi-gc-once:
fetch: only run 'gc' once when fetching multiple remotes

192 files changed:
.clang-format
.gitignore
Documentation/CodingGuidelines
Documentation/Makefile
Documentation/MyFirstContribution.txt [new file with mode: 0644]
Documentation/RelNotes/2.23.0.txt [new file with mode: 0644]
Documentation/config.txt
Documentation/config/alias.txt
Documentation/config/format.txt
Documentation/config/tag.txt
Documentation/git-branch.txt
Documentation/git-clone.txt
Documentation/git-fast-export.txt
Documentation/git-fast-import.txt
Documentation/git-for-each-ref.txt
Documentation/git-format-patch.txt
Documentation/git-hash-object.txt
Documentation/git-merge.txt
Documentation/git-rebase.txt
Documentation/git-rev-list.txt
Documentation/git-send-email.txt
Documentation/git-tag.txt
Documentation/git-update-server-info.txt
Documentation/git.txt
Documentation/gitattributes.txt
Documentation/gitignore.txt
Documentation/merge-options.txt
Documentation/rev-list-options.txt
Documentation/technical/api-trace2.txt
Documentation/technical/commit-graph.txt
GIT-VERSION-GEN
Makefile
RelNotes
apply.c
branch.c
branch.h
builtin/am.c
builtin/bisect--helper.c
builtin/branch.c
builtin/clone.c
builtin/column.c
builtin/commit-graph.c
builtin/commit.c
builtin/fast-export.c
builtin/fetch.c
builtin/gc.c
builtin/hash-object.c
builtin/index-pack.c
builtin/init-db.c
builtin/interpret-trailers.c
builtin/log.c
builtin/ls-files.c
builtin/merge.c
builtin/mktree.c
builtin/name-rev.c
builtin/pack-objects.c
builtin/patch-id.c
builtin/range-diff.c
builtin/read-tree.c
builtin/rebase.c
builtin/receive-pack.c
builtin/remote.c
builtin/repack.c
builtin/rev-list.c
builtin/rm.c
builtin/show-branch.c
builtin/stash.c
builtin/submodule--helper.c
builtin/tag.c
builtin/update-index.c
builtin/upload-pack.c
builtin/verify-commit.c
builtin/worktree.c
builtin/write-tree.c
bundle.c
cache.h
commit-graph.c
commit-graph.h
commit.c
compat/poll/poll.c
compat/winansi.c
config.c
configure.ac
contrib/coccinelle/array.cocci
contrib/completion/git-completion.bash
diff-lib.c
diff.c
diff.h
diffcore-rename.c
dir.c
entry.c
fast-import.c
fsmonitor.c
fsmonitor.h
git-add--interactive.perl
git-mergetool.sh
git-p4.py
git-rebase--am.sh [deleted file]
git-rebase--common.sh [deleted file]
git-rebase--preserve-merges.sh
git-request-pull.sh
git-send-email.perl
git.c
grep.c
help.c
kwset.c
list-objects-filter-options.c
list-objects-filter.c
ls-refs.c
name-hash.c
object-store.h
object.c
packfile.c
packfile.h
patch-ids.c
patch-ids.h
preload-index.c
pretty.c
read-cache.c
ref-filter.c
refs.c
refs.h
revision.c
sequencer.c
server-info.c
sh-i18n--envsubst.c
sha1-file.c
submodule.c
submodule.h
t/README
t/t0000-basic.sh
t/t0001-init.sh
t/t1301-shared-repo.sh
t/t1305-config-include.sh
t/t2400-worktree-add.sh
t/t3200-branch.sh
t/t3203-branch-output.sh
t/t3400-rebase.sh
t/t3404-rebase-interactive.sh
t/t3430-rebase-merges.sh
t/t3701-add-interactive.sh
t/t3903-stash.sh
t/t4014-format-patch.sh
t/t4018-diff-funcname.sh
t/t4018/matlab-class-definition [new file with mode: 0644]
t/t4018/matlab-function [new file with mode: 0644]
t/t4018/matlab-octave-section-1 [new file with mode: 0644]
t/t4018/matlab-octave-section-2 [new file with mode: 0644]
t/t4018/matlab-section [new file with mode: 0644]
t/t4018/rust-fn [new file with mode: 0644]
t/t4018/rust-impl [new file with mode: 0644]
t/t4018/rust-struct [new file with mode: 0644]
t/t4018/rust-trait [new file with mode: 0644]
t/t4202-log.sh
t/t4257-am-interactive.sh [new file with mode: 0755]
t/t5150-request-pull.sh
t/t5200-update-server-info.sh [new file with mode: 0755]
t/t5318-commit-graph.sh
t/t5509-fetch-push-namespaces.sh
t/t5601-clone.sh
t/t5607-clone-bundle.sh
t/t5616-partial-clone.sh
t/t5617-clone-submodules-remote.sh [new file with mode: 0755]
t/t5801-remote-helpers.sh
t/t5801/git-remote-testgit
t/t6000-rev-list-misc.sh
t/t6302-for-each-ref-filter.sh
t/t6500-gc.sh
t/t7004-tag.sh
t/t7405-submodule-merge.sh
t/t7513-interpret-trailers.sh
t/t7600-merge.sh
t/t7610-mergetool.sh
t/t7810-grep.sh
t/t9001-send-email.sh
t/t9300-fast-import.sh
t/t9350-fast-export.sh
t/t9350/broken-iso-8859-7-commit-message.txt [new file with mode: 0644]
t/t9350/simple-iso-8859-7-commit-message.txt [new file with mode: 0644]
t/t9801-git-p4-branch.sh
t/t9817-git-p4-exclude.sh
t/t9832-unshelve.sh
t/test-lib-functions.sh
t/test-lib.sh
transport-helper.c
unicode-width.h
upload-pack.c
url.c
userdiff.c
worktree.c
wrapper.c
wt-status.c
index 41d4cd23fd97f599053a19555a173894da71e560..c592dda681fecfaa6bf64fb3f539eafaf4123ed8 100644 (file)
@@ -148,8 +148,21 @@ SpacesInSquareBrackets: false
 Cpp11BracedListStyle: false
 
 # A list of macros that should be interpreted as foreach loops instead of as
-# function calls.
-ForEachMacros: ['for_each_string_list_item', 'for_each_wanted_builtin', 'for_each_builtin', 'for_each_ut']
+# function calls. Taken from:
+#   git grep -h '^#define [^[:space:]]*for_each[^[:space:]]*(' \
+#   | sed "s,^#define \([^[:space:]]*for_each[^[:space:]]*\)(.*$,  - '\1'," \
+#   | sort | uniq
+ForEachMacros:
+  - 'for_each_abbrev'
+  - 'for_each_builtin'
+  - 'for_each_string_list_item'
+  - 'for_each_ut'
+  - 'for_each_wanted_builtin'
+  - 'list_for_each'
+  - 'list_for_each_dir'
+  - 'list_for_each_prev'
+  - 'list_for_each_prev_safe'
+  - 'list_for_each_safe'
 
 # The maximum number of consecutive empty lines to keep.
 MaxEmptyLinesToKeep: 1
index 2374f77a1aae580ed4f71d14e1383bd2dabebd27..4470d7cfc0ae72991116f8d1b65fb03e35e88bdf 100644 (file)
 /git-range-diff
 /git-read-tree
 /git-rebase
-/git-rebase--am
-/git-rebase--common
-/git-rebase--interactive
 /git-rebase--preserve-merges
 /git-receive-pack
 /git-reflog
index 32210a4386c32972b7c4dbfa36b0cf977dd85de2..1169ff6c8eb87e6e372110a89f31273879bc862f 100644 (file)
@@ -412,6 +412,12 @@ For C programs:
    must be declared with "extern" in header files. However, function
    declarations should not use "extern", as that is already the default.
 
+ - You can launch gdb around your program using the shorthand GIT_DEBUGGER.
+   Run `GIT_DEBUGGER=1 ./bin-wrappers/git foo` to simply use gdb as is, or
+   run `GIT_DEBUGGER="<debugger> <debugger-args>" ./bin-wrappers/git foo` to
+   use your own debugger and arguments. Example: `GIT_DEBUGGER="ddd --gdb"
+   ./bin-wrappers/git log` (See `wrap-for-bin.sh`.)
+
 For Perl programs:
 
  - Most of the C guidelines above apply.
index dbf5a0f2762fba5c7d0fcf5f7ec322c413f2b779..76f2ecfc1b1a42c601542928847fd79890e4d96c 100644 (file)
@@ -76,6 +76,7 @@ SP_ARTICLES += howto/maintain-git
 API_DOCS = $(patsubst %.txt,%,$(filter-out technical/api-index-skel.txt technical/api-index.txt, $(wildcard technical/api-*.txt)))
 SP_ARTICLES += $(API_DOCS)
 
+TECH_DOCS += MyFirstContribution
 TECH_DOCS += SubmittingPatches
 TECH_DOCS += technical/hash-function-transition
 TECH_DOCS += technical/http-protocol
diff --git a/Documentation/MyFirstContribution.txt b/Documentation/MyFirstContribution.txt
new file mode 100644 (file)
index 0000000..895b7cf
--- /dev/null
@@ -0,0 +1,1132 @@
+My First Contribution to the Git Project
+========================================
+:sectanchors:
+
+[[summary]]
+== Summary
+
+This is a tutorial demonstrating the end-to-end workflow of creating a change to
+the Git tree, sending it for review, and making changes based on comments.
+
+[[prerequisites]]
+=== Prerequisites
+
+This tutorial assumes you're already fairly familiar with using Git to manage
+source code.  The Git workflow steps will largely remain unexplained.
+
+[[related-reading]]
+=== Related Reading
+
+This tutorial aims to summarize the following documents, but the reader may find
+useful additional context:
+
+- `Documentation/SubmittingPatches`
+- `Documentation/howto/new-command.txt`
+
+[[getting-started]]
+== Getting Started
+
+[[cloning]]
+=== Clone the Git Repository
+
+Git is mirrored in a number of locations. Clone the repository from one of them;
+https://git-scm.com/downloads suggests one of the best places to clone from is
+the mirror on GitHub.
+
+----
+$ git clone https://github.com/git/git git
+$ cd git
+----
+
+[[identify-problem]]
+=== Identify Problem to Solve
+
+////
+Use + to indicate fixed-width here; couldn't get ` to work nicely with the
+quotes around "Pony Saying 'Um, Hello'".
+////
+In this tutorial, we will add a new command, +git psuh+, short for ``Pony Saying
+`Um, Hello''' - a feature which has gone unimplemented despite a high frequency
+of invocation during users' typical daily workflow.
+
+(We've seen some other effort in this space with the implementation of popular
+commands such as `sl`.)
+
+[[setup-workspace]]
+=== Set Up Your Workspace
+
+Let's start by making a development branch to work on our changes. Per
+`Documentation/SubmittingPatches`, since a brand new command is a new feature,
+it's fine to base your work on `master`. However, in the future for bugfixes,
+etc., you should check that document and base it on the appropriate branch.
+
+For the purposes of this document, we will base all our work on the `master`
+branch of the upstream project. Create the `psuh` branch you will use for
+development like so:
+
+----
+$ git checkout -b psuh origin/master
+----
+
+We'll make a number of commits here in order to demonstrate how to send a topic
+with multiple patches up for review simultaneously.
+
+[[code-it-up]]
+== Code It Up!
+
+NOTE: A reference implementation can be found at
+https://github.com/nasamuffin/git/tree/psuh.
+
+[[add-new-command]]
+=== Adding a New Command
+
+Lots of the subcommands are written as builtins, which means they are
+implemented in C and compiled into the main `git` executable. Implementing the
+very simple `psuh` command as a built-in will demonstrate the structure of the
+codebase, the internal API, and the process of working together as a contributor
+with the reviewers and maintainer to integrate this change into the system.
+
+Built-in subcommands are typically implemented in a function named "cmd_"
+followed by the name of the subcommand, in a source file named after the
+subcommand and contained within `builtin/`. So it makes sense to implement your
+command in `builtin/psuh.c`. Create that file, and within it, write the entry
+point for your command in a function matching the style and signature:
+
+----
+int cmd_psuh(int argc, const char **argv, const char *prefix)
+----
+
+We'll also need to add the declaration of psuh; open up `builtin.h`, find the
+declaration for `cmd_push`, and add a new line for `psuh` immediately before it,
+in order to keep the declarations sorted:
+
+----
+int cmd_psuh(int argc, const char **argv, const char *prefix);
+----
+
+Be sure to `#include "builtin.h"` in your `psuh.c`.
+
+Go ahead and add some throwaway printf to that function. This is a decent
+starting point as we can now add build rules and register the command.
+
+NOTE: Your throwaway text, as well as much of the text you will be adding over
+the course of this tutorial, is user-facing. That means it needs to be
+localizable. Take a look at `po/README` under "Marking strings for translation".
+Throughout the tutorial, we will mark strings for translation as necessary; you
+should also do so when writing your user-facing commands in the future.
+
+----
+int cmd_psuh(int argc, const char **argv, const char *prefix)
+{
+       printf(_("Pony saying hello goes here.\n"));
+       return 0;
+}
+----
+
+Let's try to build it.  Open `Makefile`, find where `builtin/push.o` is added
+to `BUILTIN_OBJS`, and add `builtin/psuh.o` in the same way next to it in
+alphabetical order. Once you've done so, move to the top-level directory and
+build simply with `make`. Also add the `DEVELOPER=1` variable to turn on
+some additional warnings:
+
+----
+$ echo DEVELOPER=1 >config.mak
+$ make
+----
+
+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.
+
+The list of commands lives in `git.c`. We can register a new command by adding
+a `cmd_struct` to the `commands[]` array. `struct cmd_struct` takes a string
+with the command name, a function pointer to the command implementation, and a
+setup option flag. For now, let's keep mimicking `push`. Find the line where
+`cmd_push` is registered, copy it, and modify it for `cmd_psuh`, placing the new
+line in alphabetical order.
+
+The options are documented in `builtin.h` under "Adding a new built-in." Since
+we hope to print some data about the user's current workspace context later,
+we need a Git directory, so choose `RUN_SETUP` as your only option.
+
+Go ahead and build again. You should see a clean build, so let's kick the tires
+and see if it works. There's a binary you can use to test with in the
+`bin-wrappers` directory.
+
+----
+$ ./bin-wrappers/git psuh
+----
+
+Check it out! You've got a command! Nice work! Let's commit this.
+
+`git status` reveals modified `Makefile`, `builtin.h`, and `git.c` as well as
+untracked `builtin/psuh.c` and `git-psuh`. First, let's take care of the binary,
+which should be ignored. Open `.gitignore` in your editor, find `/git-push`, and
+add an entry for your new command in alphabetical order:
+
+----
+...
+/git-prune-packed
+/git-psuh
+/git-pull
+/git-push
+/git-quiltimport
+/git-range-diff
+...
+----
+
+Checking `git status` again should show that `git-psuh` has been removed from
+the untracked list and `.gitignore` has been added to the modified list. Now we
+can stage and commit:
+
+----
+$ git add Makefile builtin.h builtin/psuh.c git.c .gitignore
+$ git commit -s
+----
+
+You will be presented with your editor in order to write a commit message. Start
+the commit with a 50-column or less subject line, including the name of the
+component you're working on, followed by a blank line (always required) and then
+the body of your commit message, which should provide the bulk of the context.
+Remember to be explicit and provide the "Why" of your change, especially if it
+couldn't easily be understood from your diff. When editing your commit message,
+don't remove the Signed-off-by line which was added by `-s` above.
+
+----
+psuh: add a built-in by popular demand
+
+Internal metrics indicate this is a command many users expect to be
+present. So here's an implementation to help drive customer
+satisfaction and engagement: a pony which doubtfully greets the user,
+or, a Pony Saying "Um, Hello" (PSUH).
+
+This commit message is intentionally formatted to 72 columns per line,
+starts with a single line as "commit message subject" that is written as
+if to command the codebase to do something (add this, teach a command
+that). The body of the message is designed to add information about the
+commit that is not readily deduced from reading the associated diff,
+such as answering the question "why?".
+
+Signed-off-by: A U Thor <author@example.com>
+----
+
+Go ahead and inspect your new commit with `git show`. "psuh:" indicates you
+have modified mainly the `psuh` command. The subject line gives readers an idea
+of what you've changed. The sign-off line (`-s`) indicates that you agree to
+the Developer's Certificate of Origin 1.1 (see the
+`Documentation/SubmittingPatches` +++[[dco]]+++ header).
+
+For the remainder of the tutorial, the subject line only will be listed for the
+sake of brevity. However, fully-fleshed example commit messages are available
+on the reference implementation linked at the top of this document.
+
+[[implementation]]
+=== Implementation
+
+It's probably useful to do at least something besides printing out a string.
+Let's start by having a look at everything we get.
+
+Modify your `cmd_psuh` implementation to dump the args you're passed, keeping
+existing `printf()` calls in place:
+
+----
+       int i;
+
+       ...
+
+       printf(Q_("Your args (there is %d):\n",
+                 "Your args (there are %d):\n",
+                 argc),
+              argc);
+       for (i = 0; i < argc; i++)
+               printf("%d: %s\n", i, argv[i]);
+
+       printf(_("Your current working directory:\n<top-level>%s%s\n"),
+              prefix ? "/" : "", prefix ? prefix : "");
+
+----
+
+Build and try it. As you may expect, there's pretty much just whatever we give
+on the command line, including the name of our command. (If `prefix` is empty
+for you, try `cd Documentation/ && ../bin-wrappers/git psuh`). That's not so
+helpful. So what other context can we get?
+
+Add a line to `#include "config.h"`. Then, add the following bits to the
+function body:
+
+----
+       const char *cfg_name;
+
+...
+
+       git_config(git_default_config, NULL);
+       if (git_config_get_string_const("user.name", &cfg_name) > 0)
+               printf(_("No name is found in config\n"));
+       else
+               printf(_("Your name: %s\n"), cfg_name);
+----
+
+`git_config()` will grab the configuration from config files known to Git and
+apply standard precedence rules. `git_config_get_string_const()` will look up
+a specific key ("user.name") and give you the value. There are a number of
+single-key lookup functions like this one; you can see them all (and more info
+about how to use `git_config()`) in `Documentation/technical/api-config.txt`.
+
+You should see that the name printed matches the one you see when you run:
+
+----
+$ git config --get user.name
+----
+
+Great! Now we know how to check for values in the Git config. Let's commit this
+too, so we don't lose our progress.
+
+----
+$ git add builtin/psuh.c
+$ git commit -sm "psuh: show parameters & config opts"
+----
+
+NOTE: Again, the above is for sake of brevity in this tutorial. In a real change
+you should not use `-m` but instead use the editor to write a meaningful
+message.
+
+Still, it'd be nice to know what the user's working context is like. Let's see
+if we can print the name of the user's current branch. We can mimic the
+`git status` implementation; the printer is located in `wt-status.c` and we can
+see that the branch is held in a `struct wt_status`.
+
+`wt_status_print()` gets invoked by `cmd_status()` in `builtin/commit.c`.
+Looking at that implementation we see the status config being populated like so:
+
+----
+status_init_config(&s, git_status_config);
+----
+
+But as we drill down, we can find that `status_init_config()` wraps a call
+to `git_config()`. Let's modify the code we wrote in the previous commit.
+
+Be sure to include the header to allow you to use `struct wt_status`:
+----
+#include "wt-status.h"
+----
+
+Then modify your `cmd_psuh` implementation to declare your `struct wt_status`,
+prepare it, and print its contents:
+
+----
+       struct wt_status status;
+
+...
+
+       wt_status_prepare(the_repository, &status);
+       git_config(git_default_config, &status);
+
+...
+
+       printf(_("Your current branch: %s\n"), status.branch);
+----
+
+Run it again. Check it out - here's the (verbose) name of your current branch!
+
+Let's commit this as well.
+
+----
+$ git add builtin/psuh.c
+$ git commit -sm "psuh: print the current branch"
+----
+
+Now let's see if we can get some info about a specific commit.
+
+Luckily, there are some helpers for us here. `commit.h` has a function called
+`lookup_commit_reference_by_name` to which we can simply provide a hardcoded
+string; `pretty.h` has an extremely handy `pp_commit_easy()` call which doesn't
+require a full format object to be passed.
+
+Add the following includes:
+
+----
+#include "commit.h"
+#include "pretty.h"
+----
+
+Then, add the following lines within your implementation of `cmd_psuh()` near
+the declarations and the logic, respectively.
+
+----
+       struct commit *c = NULL;
+       struct strbuf commitline = STRBUF_INIT;
+
+...
+
+       c = lookup_commit_reference_by_name("origin/master");
+
+       if (c != NULL) {
+               pp_commit_easy(CMIT_FMT_ONELINE, c, &commitline);
+               printf(_("Current commit: %s\n"), commitline.buf);
+       }
+----
+
+The `struct strbuf` provides some safety belts to your basic `char*`, one of
+which is a length member to prevent buffer overruns. It needs to be initialized
+nicely with `STRBUF_INIT`. Keep it in mind when you need to pass around `char*`.
+
+`lookup_commit_reference_by_name` resolves the name you pass it, so you can play
+with the value there and see what kind of things you can come up with.
+
+`pp_commit_easy` is a convenience wrapper in `pretty.h` that takes a single
+format enum shorthand, rather than an entire format struct. It then
+pretty-prints the commit according to that shorthand. These are similar to the
+formats available with `--pretty=FOO` in many Git commands.
+
+Build it and run, and if you're using the same name in the example, you should
+see the subject line of the most recent commit in `origin/master` that you know
+about. Neat! Let's commit that as well.
+
+----
+$ git add builtin/psuh.c
+$ git commit -sm "psuh: display the top of origin/master"
+----
+
+[[add-documentation]]
+=== Adding Documentation
+
+Awesome! You've got a fantastic new command that you're ready to share with the
+community. But hang on just a minute - this isn't very user-friendly. Run the
+following:
+
+----
+$ ./bin-wrappers/git help psuh
+----
+
+Your new command is undocumented! Let's fix that.
+
+Take a look at `Documentation/git-*.txt`. These are the manpages for the
+subcommands that Git knows about. You can open these up and take a look to get
+acquainted with the format, but then go ahead and make a new file
+`Documentation/git-psuh.txt`. Like with most of the documentation in the Git
+project, help pages are written with AsciiDoc (see CodingGuidelines, "Writing
+Documentation" section). Use the following template to fill out your own
+manpage:
+
+// Surprisingly difficult to embed AsciiDoc source within AsciiDoc.
+[listing]
+....
+git-psuh(1)
+===========
+
+NAME
+----
+git-psuh - Delight users' typo with a shy horse
+
+
+SYNOPSIS
+--------
+[verse]
+'git-psuh'
+
+DESCRIPTION
+-----------
+...
+
+OPTIONS[[OPTIONS]]
+------------------
+...
+
+OUTPUT
+------
+...
+
+GIT
+---
+Part of the linkgit:git[1] suite
+....
+
+The most important pieces of this to note are the file header, underlined by =,
+the NAME section, and the SYNOPSIS, which would normally contain the grammar if
+your command took arguments. Try to use well-established manpage headers so your
+documentation is consistent with other Git and UNIX manpages; this makes life
+easier for your user, who can skip to the section they know contains the
+information they need.
+
+Now that you've written your manpage, you'll need to build it explicitly. We
+convert your AsciiDoc to troff which is man-readable like so:
+
+----
+$ make all doc
+$ man Documentation/git-psuh.1
+----
+
+or
+
+----
+$ make -C Documentation/ git-psuh.1
+$ man Documentation/git-psuh.1
+----
+
+NOTE: You may need to install the package `asciidoc` to get this to work.
+
+While this isn't as satisfying as running through `git help`, you can at least
+check that your help page looks right.
+
+You can also check that the documentation coverage is good (that is, the project
+sees that your command has been implemented as well as documented) by running
+`make check-docs` from the top-level.
+
+Go ahead and commit your new documentation change.
+
+[[add-usage]]
+=== Adding Usage Text
+
+Try and run `./bin-wrappers/git psuh -h`. Your command should crash at the end.
+That's because `-h` is a special case which your command should handle by
+printing usage.
+
+Take a look at `Documentation/technical/api-parse-options.txt`. This is a handy
+tool for pulling out options you need to be able to handle, and it takes a
+usage string.
+
+In order to use it, we'll need to prepare a NULL-terminated usage string and a
+`builtin_psuh_options` array. Add a line to `#include "parse-options.h"`.
+
+At global scope, add your usage:
+
+----
+static const char * const psuh_usage[] = {
+       N_("git psuh"),
+       NULL,
+};
+----
+
+Then, within your `cmd_psuh()` implementation, we can declare and populate our
+`option` struct. Ours is pretty boring but you can add more to it if you want to
+explore `parse_options()` in more detail:
+
+----
+       struct option options[] = {
+               OPT_END()
+       };
+----
+
+Finally, before you print your args and prefix, add the call to
+`parse-options()`:
+
+----
+       argc = parse_options(argc, argv, prefix, options, psuh_usage, 0);
+----
+
+This call will modify your `argv` parameter. It will strip the options you
+specified in `options` from `argv` and the locations pointed to from `options`
+entries will be updated. Be sure to replace your `argc` with the result from
+`parse_options()`, or you will be confused if you try to parse `argv` later.
+
+It's worth noting the special argument `--`. As you may be aware, many Unix
+commands use `--` to indicate "end of named parameters" - all parameters after
+the `--` are interpreted merely as positional arguments. (This can be handy if
+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.
+
+Build again. Now, when you run with `-h`, you should see your usage printed and
+your command terminated before anything else interesting happens. Great!
+
+Go ahead and commit this one, too.
+
+[[testing]]
+== Testing
+
+It's important to test your code - even for a little toy command like this one.
+Moreover, your patch won't be accepted into the Git tree without tests. Your
+tests should:
+
+* Illustrate the current behavior of the feature
+* Prove the current behavior matches the expected behavior
+* Ensure the externally-visible behavior isn't broken in later changes
+
+So let's write some tests.
+
+Related reading: `t/README`
+
+[[overview-test-structure]]
+=== Overview of Testing Structure
+
+The tests in Git live in `t/` and are named with a 4-digit decimal number using
+the schema shown in the Naming Tests section of `t/README`.
+
+[[write-new-test]]
+=== Writing Your Test
+
+Since this a toy command, let's go ahead and name the test with t9999. However,
+as many of the family/subcmd combinations are full, best practice seems to be
+to find a command close enough to the one you've added and share its naming
+space.
+
+Create a new file `t/t9999-psuh-tutorial.sh`. Begin with the header as so (see
+"Writing Tests" and "Source 'test-lib.sh'" in `t/README`):
+
+----
+#!/bin/sh
+
+test_description='git-psuh test
+
+This test runs git-psuh and makes sure it does not crash.'
+
+. ./test-lib.sh
+----
+
+Tests are framed inside of a `test_expect_success` in order to output TAP
+formatted results. Let's make sure that `git psuh` doesn't exit poorly and does
+mention the right animal somewhere:
+
+----
+test_expect_success 'runs correctly with no args and good output' '
+       git psuh >actual &&
+       test_i18ngrep Pony actual
+'
+----
+
+Indicate that you've run everything you wanted by adding the following at the
+bottom of your script:
+
+----
+test_done
+----
+
+Make sure you mark your test script executable:
+
+----
+$ chmod +x t/t9999-psuh-tutorial.sh
+----
+
+You can get an idea of whether you created your new test script successfully
+by running `make -C t test-lint`, which will check for things like test number
+uniqueness, executable bit, and so on.
+
+[[local-test]]
+=== Running Locally
+
+Let's try and run locally:
+
+----
+$ make
+$ cd t/ && prove t9999-psuh-tutorial.sh
+----
+
+You can run the full test suite and ensure `git-psuh` didn't break anything:
+
+----
+$ cd t/
+$ prove -j$(nproc) --shuffle t[0-9]*.sh
+----
+
+NOTE: You can also do this with `make test` or use any testing harness which can
+speak TAP. `prove` can run concurrently. `shuffle` randomizes the order the
+tests are run in, which makes them resilient against unwanted inter-test
+dependencies. `prove` also makes the output nicer.
+
+Go ahead and commit this change, as well.
+
+[[ready-to-share]]
+== Getting Ready to Share
+
+You may have noticed already that the Git project performs its code reviews via
+emailed patches, which are then applied by the maintainer when they are ready
+and approved by the community. The Git project does not accept patches from
+pull requests, and the patches emailed for review need to be formatted a
+specific way. At this point the tutorial diverges, in order to demonstrate two
+different methods of formatting your patchset and getting it reviewed.
+
+The first method to be covered is GitGitGadget, which is useful for those
+already familiar with GitHub's common pull request workflow. This method
+requires a GitHub account.
+
+The second method to be covered is `git send-email`, which can give slightly
+more fine-grained control over the emails to be sent. This method requires some
+setup which can change depending on your system and will not be covered in this
+tutorial.
+
+Regardless of which method you choose, your engagement with reviewers will be
+the same; the review process will be covered after the sections on GitGitGadget
+and `git send-email`.
+
+[[howto-ggg]]
+== Sending Patches via GitGitGadget
+
+One option for sending patches is to follow a typical pull request workflow and
+send your patches out via GitGitGadget. GitGitGadget is a tool created by
+Johannes Schindelin to make life as a Git contributor easier for those used to
+the GitHub PR workflow. It allows contributors to open pull requests against its
+mirror of the Git project, and does some magic to turn the PR into a set of
+emails and send them out for you. It also runs the Git continuous integration
+suite for you. It's documented at http://gitgitgadget.github.io.
+
+[[create-fork]]
+=== Forking `git/git` on GitHub
+
+Before you can send your patch off to be reviewed using GitGitGadget, you will
+need to fork the Git project and upload your changes. First thing - make sure
+you have a GitHub account.
+
+Head to the https://github.com/git/git[GitHub mirror] and look for the Fork
+button. Place your fork wherever you deem appropriate and create it.
+
+[[upload-to-fork]]
+=== Uploading to Your Own Fork
+
+To upload your branch to your own fork, you'll need to add the new fork as a
+remote. You can use `git remote -v` to show the remotes you have added already.
+From your new fork's page on GitHub, you can press "Clone or download" to get
+the URL; then you need to run the following to add, replacing your own URL and
+remote name for the examples provided:
+
+----
+$ git remote add remotename git@github.com:remotename/git.git
+----
+
+or to use the HTTPS URL:
+
+----
+$ git remote add remotename https://github.com/remotename/git/.git
+----
+
+Run `git remote -v` again and you should see the new remote showing up.
+`git fetch remotename` (with the real name of your remote replaced) in order to
+get ready to push.
+
+Next, double-check that you've been doing all your development in a new branch
+by running `git branch`. If you didn't, now is a good time to move your new
+commits to their own branch.
+
+As mentioned briefly at the beginning of this document, we are basing our work
+on `master`, so go ahead and update as shown below, or using your preferred
+workflow.
+
+----
+$ git checkout master
+$ git pull -r
+$ git rebase master psuh
+----
+
+Finally, you're ready to push your new topic branch! (Due to our branch and
+command name choices, be careful when you type the command below.)
+
+----
+$ git push remotename psuh
+----
+
+Now you should be able to go and check out your newly created branch on GitHub.
+
+[[send-pr-ggg]]
+=== Sending a PR to GitGitGadget
+
+In order to have your code tested and formatted for review, you need to start by
+opening a Pull Request against `gitgitgadget/git`. Head to
+https://github.com/gitgitgadget/git and open a PR either with the "New pull
+request" button or the convenient "Compare & pull request" button that may
+appear with the name of your newly pushed branch.
+
+Review the PR's title and description, as it's used by GitGitGadget as the cover
+letter for your change. When you're happy, submit your pull request.
+
+[[run-ci-ggg]]
+=== Running CI and Getting Ready to Send
+
+If it's your first time using GitGitGadget (which is likely, as you're using
+this tutorial) then someone will need to give you permission to use the tool.
+As mentioned in the GitGitGadget documentation, you just need someone who
+already uses it to comment on your PR with `/allow <username>`. GitGitGadget
+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.
+
+If the CI fails, you can update your changes with `git rebase -i` and push your
+branch again:
+
+----
+$ git push -f remotename psuh
+----
+
+In fact, you should continue to make changes this way up until the point when
+your patch is accepted into `next`.
+
+////
+TODO https://github.com/gitgitgadget/gitgitgadget/issues/83
+It'd be nice to be able to verify that the patch looks good before sending it
+to everyone on Git mailing list.
+[[check-work-ggg]]
+=== Check Your Work
+////
+
+[[send-mail-ggg]]
+=== Sending Your Patches
+
+Now that your CI is passing and someone has granted you permission to use
+GitGitGadget with the `/allow` command, sending out for review is as simple as
+commenting on your PR with `/submit`.
+
+[[responding-ggg]]
+=== Updating With Comments
+
+Skip ahead to <<reviewing,Responding to Reviews>> for information on how to
+reply to review comments you will receive on the mailing list.
+
+Once you have your branch again in the shape you want following all review
+comments, you can submit again:
+
+----
+$ git push -f remotename psuh
+----
+
+Next, go look at your pull request against GitGitGadget; you should see the CI
+has been kicked off again. Now while the CI is running is a good time for you
+to modify your description at the top of the pull request thread; it will be
+used again as the cover letter. You should use this space to describe what
+has changed since your previous version, so that your reviewers have some idea
+of what they're looking at. When the CI is done running, you can comment once
+more with `/submit` - GitGitGadget will automatically add a v2 mark to your
+changes.
+
+[[howto-git-send-email]]
+== Sending Patches with `git send-email`
+
+If you don't want to use GitGitGadget, you can also use Git itself to mail your
+patches. Some benefits of using Git this way include finer grained control of
+subject line (for example, being able to use the tag [RFC PATCH] in the subject)
+and being able to send a ``dry run'' mail to yourself to ensure it all looks
+good before going out to the list.
+
+[[setup-git-send-email]]
+=== Prerequisite: Setting Up `git send-email`
+
+Configuration for `send-email` can vary based on your operating system and email
+provider, and so will not be covered in this tutorial, beyond stating that in
+many distributions of Linux, `git-send-email` is not packaged alongside the
+typical `git` install. You may need to install this additional package; there
+are a number of resources online to help you do so. You will also need to
+determine the right way to configure it to use your SMTP server; again, as this
+configuration can change significantly based on your system and email setup, it
+is out of scope for the context of this tutorial.
+
+[[format-patch]]
+=== Preparing Initial Patchset
+
+Sending emails with Git is a two-part process; before you can prepare the emails
+themselves, you'll need to prepare the patches. Luckily, this is pretty simple:
+
+----
+$ git format-patch --cover-letter -o psuh/ master..psuh
+----
+
+The `--cover-letter` parameter tells `format-patch` to create a cover letter
+template for you. You will need to fill in the template before you're ready
+to send - but for now, the template will be next to your other patches.
+
+The `-o psuh/` parameter tells `format-patch` to place the patch files into a
+directory. This is useful because `git send-email` can take a directory and
+send out all the patches from there.
+
+`master..psuh` tells `format-patch` to generate patches for the difference
+between `master` and `psuh`. It will make one patch file per commit. After you
+run, you can go have a look at each of the patches with your favorite text
+editor and make sure everything looks alright; however, it's not recommended to
+make code fixups via the patch file. It's a better idea to make the change the
+normal way using `git rebase -i` or by adding a new commit than by modifying a
+patch.
+
+NOTE: Optionally, you can also use the `--rfc` flag to prefix your patch subject
+with ``[RFC PATCH]'' instead of ``[PATCH]''. RFC stands for ``request for
+comments'' and indicates that while your code isn't quite ready for submission,
+you'd like to begin the code review process. This can also be used when your
+patch is a proposal, but you aren't sure whether the community wants to solve
+the problem with that approach or not - to conduct a sort of design review. You
+may also see on the list patches marked ``WIP'' - this means they are incomplete
+but want reviewers to look at what they have so far. You can add this flag with
+`--subject-prefix=WIP`.
+
+Check and make sure that your patches and cover letter template exist in the
+directory you specified - you're nearly ready to send out your review!
+
+[[cover-letter]]
+=== Preparing Email
+
+In addition to an email per patch, the Git community also expects your patches
+to come with a cover letter, typically with a subject line [PATCH 0/x] (where
+x is the number of patches you're sending). Since you invoked `format-patch`
+with `--cover-letter`, you've already got a template ready. Open it up in your
+favorite editor.
+
+You should see a number of headers present already. Check that your `From:`
+header is correct. Then modify your `Subject:` to something which succinctly
+covers the purpose of your entire topic branch, for example:
+
+----
+Subject: [PATCH 0/7] adding the 'psuh' command
+----
+
+Make sure you retain the ``[PATCH 0/X]'' part; that's what indicates to the Git
+community that this email is the beginning of a review, and many reviewers
+filter their email for this type of flag.
+
+You'll need to add some extra parameters when you invoke `git send-email` to add
+the cover letter.
+
+Next you'll have to fill out the body of your cover letter. This is an important
+component of change submission as it explains to the community from a high level
+what you're trying to do, and why, in a way that's more apparent than just
+looking at your diff. Be sure to explain anything your diff doesn't make clear
+on its own.
+
+Here's an example body for `psuh`:
+
+----
+Our internal metrics indicate widespread interest in the command
+git-psuh - that is, many users are trying to use it, but finding it is
+unavailable, using some unknown workaround instead.
+
+The following handful of patches add the psuh command and implement some
+handy features on top of it.
+
+This patchset is part of the MyFirstContribution tutorial and should not
+be merged.
+----
+
+The template created by `git format-patch --cover-letter` includes a diffstat.
+This gives reviewers a summary of what they're in for when reviewing your topic.
+The one generated for `psuh` from the sample implementation looks like this:
+
+----
+ Documentation/git-psuh.txt | 40 +++++++++++++++++++++
+ Makefile                   |  1 +
+ builtin.h                  |  1 +
+ builtin/psuh.c             | 73 ++++++++++++++++++++++++++++++++++++++
+ git.c                      |  1 +
+ t/t9999-psuh-tutorial.sh   | 12 +++++++
+ 6 files changed, 128 insertions(+)
+ create mode 100644 Documentation/git-psuh.txt
+ create mode 100644 builtin/psuh.c
+ create mode 100755 t/t9999-psuh-tutorial.sh
+----
+
+Finally, the letter will include the version of Git used to generate the
+patches. You can leave that string alone.
+
+[[sending-git-send-email]]
+=== Sending Email
+
+At this point you should have a directory `psuh/` which is filled with your
+patches and a cover letter. Time to mail it out! You can send it like this:
+
+----
+$ git send-email --to=target@example.com psuh/*.patch
+----
+
+NOTE: Check `git help send-email` for some other options which you may find
+valuable, such as changing the Reply-to address or adding more CC and BCC lines.
+
+NOTE: When you are sending a real patch, it will go to git@vger.kernel.org - but
+please don't send your patchset from the tutorial to the real mailing list! For
+now, you can send it to yourself, to make sure you understand how it will look.
+
+After you run the command above, you will be presented with an interactive
+prompt for each patch that's about to go out. This gives you one last chance to
+edit or quit sending something (but again, don't edit code this way). Once you
+press `y` or `a` at these prompts your emails will be sent! Congratulations!
+
+Awesome, now the community will drop everything and review your changes. (Just
+kidding - be patient!)
+
+[[v2-git-send-email]]
+=== Sending v2
+
+Skip ahead to <<reviewing,Responding to Reviews>> for information on how to
+handle comments from reviewers. Continue this section when your topic branch is
+shaped the way you want it to look for your patchset v2.
+
+When you're ready with the next iteration of your patch, the process is fairly
+similar.
+
+First, generate your v2 patches again:
+
+----
+$ git format-patch -v2 --cover-letter -o psuh/ master..psuh
+----
+
+This will add your v2 patches, all named like `v2-000n-my-commit-subject.patch`,
+to the `psuh/` directory. You may notice that they are sitting alongside the v1
+patches; that's fine, but be careful when you are ready to send them.
+
+Edit your cover letter again. Now is a good time to mention what's different
+between your last version and now, if it's something significant. You do not
+need the exact same body in your second cover letter; focus on explaining to
+reviewers the changes you've made that may not be as visible.
+
+You will also need to go and find the Message-Id of your previous cover letter.
+You can either note it when you send the first series, from the output of `git
+send-email`, or you can look it up on the
+https://public-inbox.org/git[mailing list]. Find your cover letter in the
+archives, click on it, then click "permalink" or "raw" to reveal the Message-Id
+header. It should match:
+
+----
+Message-Id: <foo.12345.author@example.com>
+----
+
+Your Message-Id is `<foo.12345.author@example.com>`. This example will be used
+below as well; make sure to replace it with the correct Message-Id for your
+**previous cover letter** - that is, if you're sending v2, use the Message-Id
+from v1; if you're sending v3, use the Message-Id from v2.
+
+While you're looking at the email, you should also note who is CC'd, as it's
+common practice in the mailing list to keep all CCs on a thread. You can add
+these CC lines directly to your cover letter with a line like so in the header
+(before the Subject line):
+
+----
+CC: author@example.com, Othe R <other@example.com>
+----
+
+Now send the emails again, paying close attention to which messages you pass in
+to the command:
+
+----
+$ git send-email --to=target@example.com
+                --in-reply-to="<foo.12345.author@example.com>"
+                psuh/v2*
+----
+
+[[single-patch]]
+=== Bonus Chapter: One-Patch Changes
+
+In some cases, your very small change may consist of only one patch. When that
+happens, you only need to send one email. Your commit message should already be
+meaningful and explain at a high level the purpose (what is happening and why)
+of your patch, but if you need to supply even more context, you can do so below
+the `---` in your patch. Take the example below, which was generated with `git
+format-patch` on a single commit, and then edited to add the content between
+the `---` and the diffstat.
+
+----
+From 1345bbb3f7ac74abde040c12e737204689a72723 Mon Sep 17 00:00:00 2001
+From: A U Thor <author@example.com>
+Date: Thu, 18 Apr 2019 15:11:02 -0700
+Subject: [PATCH] README: change the grammar
+
+I think it looks better this way. This part of the commit message will
+end up in the commit-log.
+
+Signed-off-by: A U Thor <author@example.com>
+---
+Let's have a wild discussion about grammar on the mailing list. This
+part of my email will never end up in the commit log. Here is where I
+can add additional context to the mailing list about my intent, outside
+of the context of the commit log. This section was added after `git
+format-patch` was run, by editing the patch file in a text editor.
+
+ README.md | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/README.md b/README.md
+index 88f126184c..38da593a60 100644
+--- a/README.md
++++ b/README.md
+@@ -3,7 +3,7 @@
+ Git - fast, scalable, distributed revision control system
+ =========================================================
+
+-Git is a fast, scalable, distributed revision control system with an
++Git is a fast, scalable, and distributed revision control system with an
+ unusually rich command set that provides both high-level operations
+ and full access to internals.
+
+--
+2.21.0.392.gf8f6787159e-goog
+----
+
+[[now-what]]
+== My Patch Got Emailed - Now What?
+
+[[reviewing]]
+=== Responding to Reviews
+
+After a few days, you will hopefully receive a reply to your patchset with some
+comments. Woohoo! Now you can get back to work.
+
+It's good manners to reply to each comment, notifying the reviewer that you have
+made the change requested, feel the original is better, or that the comment
+inspired you to do something a new way which is superior to both the original
+and the suggested change. This way reviewers don't need to inspect your v2 to
+figure out whether you implemented their comment or not.
+
+If you are going to push back on a comment, be polite and explain why you feel
+your original is better; be prepared that the reviewer may still disagree with
+you, and the rest of the community may weigh in on one side or the other. As
+with all code reviews, it's important to keep an open mind to doing something a
+different way than you originally planned; other reviewers have a different
+perspective on the project than you do, and may be thinking of a valid side
+effect which had not occurred to you. It is always okay to ask for clarification
+if you aren't sure why a change was suggested, or what the reviewer is asking
+you to do.
+
+Make sure your email client has a plaintext email mode and it is turned on; the
+Git list rejects HTML email. Please also follow the mailing list etiquette
+outlined in the
+https://kernel.googlesource.com/pub/scm/git/git/+/todo/MaintNotes[Maintainer's
+Note], which are similar to etiquette rules in most open source communities
+surrounding bottom-posting and inline replies.
+
+When you're making changes to your code, it is cleanest - that is, the resulting
+commits are easiest to look at - if you use `git rebase -i` (interactive
+rebase). Take a look at this
+https://www.oreilly.com/library/view/git-pocket-guide/9781449327507/ch10.html[overview]
+from O'Reilly. The general idea is to modify each commit which requires changes;
+this way, instead of having a patch A with a mistake, a patch B which was fine
+and required no upstream reviews in v1, and a patch C which fixes patch A for
+v2, you can just ship a v2 with a correct patch A and correct patch B. This is
+changing history, but since it's local history which you haven't shared with
+anyone, that is okay for now! (Later, it may not make sense to do this; take a
+look at the section below this one for some context.)
+
+[[after-approval]]
+=== After Review Approval
+
+The Git project has four integration branches: `pu`, `next`, `master`, and
+`maint`. Your change will be placed into `pu` fairly early on by the maintainer
+while it is still in the review process; from there, when it is ready for wider
+testing, it will be merged into `next`. Plenty of early testers use `next` and
+may report issues. Eventually, changes in `next` will make it to `master`,
+which is typically considered stable. Finally, when a new release is cut,
+`maint` is used to base bugfixes onto. As mentioned at the beginning of this
+document, you can read `Documents/SubmittingPatches` for some more info about
+the use of the various integration branches.
+
+Back to now: your code has been lauded by the upstream reviewers. It is perfect.
+It is ready to be accepted. You don't need to do anything else; the maintainer
+will merge your topic branch to `next` and life is good.
+
+However, if you discover it isn't so perfect after this point, you may need to
+take some special steps depending on where you are in the process.
+
+If the maintainer has announced in the "What's cooking in git.git" email that
+your topic is marked for `next` - that is, that they plan to merge it to `next`
+but have not yet done so - you should send an email asking the maintainer to
+wait a little longer: "I've sent v4 of my series and you marked it for `next`,
+but I need to change this and that - please wait for v5 before you merge it."
+
+If the topic has already been merged to `next`, rather than modifying your
+patches with `git rebase -i`, you should make further changes incrementally -
+that is, with another commit, based on top of the maintainer's topic branch as
+detailed in https://github.com/gitster/git. Your work is still in the same topic
+but is now incremental, rather than a wholesale rewrite of the topic branch.
+
+The topic branches in the maintainer's GitHub are mirrored in GitGitGadget, so
+if you're sending your reviews out that way, you should be sure to open your PR
+against the appropriate GitGitGadget/Git branch.
+
+If you're using `git send-email`, you can use it the same way as before, but you
+should generate your diffs from `<topic>..<mybranch>` and base your work on
+`<topic>` instead of `master`.
diff --git a/Documentation/RelNotes/2.23.0.txt b/Documentation/RelNotes/2.23.0.txt
new file mode 100644 (file)
index 0000000..a6cd592
--- /dev/null
@@ -0,0 +1,183 @@
+Git 2.23 Release Notes
+======================
+
+Updates since v2.22
+-------------------
+
+Backward compatibility note
+
+ * The "--base" option of "format-patch" computed the patch-ids for
+   prerequisite patches in an unstable way, which has been updated to
+   compute in a way that is compatible with "git patch-id --stable".
+
+
+UI, Workflows & Features
+
+ * The "git fast-export/import" pair has been taught to handle commits
+   with log messages in encoding other than UTF-8 better.
+
+ * In recent versions of Git, per-worktree refs are exposed in
+   refs/worktrees/<wtname>/ hierarchy, which means that worktree names
+   must be a valid refname component.  The code now sanitizes the names
+   given to worktrees, to make sure these refs are well-formed.
+
+ * "git merge" learned "--quit" option that cleans up the in-progress
+   merge while leaving the working tree and the index still in a mess.
+
+ * "git format-patch" learns a configuration to set the default for
+   its --notes=<ref> option.
+
+ * The code to show args with potential typo that cannot be
+   interpreted as a commit-ish has been improved.
+
+ * "git clone --recurse-submodules" learned to set up the submodules
+   to ignore commit object names recorded in the superproject gitlink
+   and instead use the commits that happen to be at the tip of the
+   remote-tracking branches from the get-go, by passing the new
+   "--remote-submodules" option.
+
+ * The pattern "git diff/grep" use to extract funcname and words
+   boundary for Matlab has been extend to cover Octave, which is more
+   or less equivalent.
+
+ * "git help git" was hard to discover (well, at least for some
+   people).
+
+ * The pattern "git diff/grep" use to extract funcname and words
+   boundary for Rust has been added.
+
+
+Performance, Internal Implementation, Development Support etc.
+
+ * Update supporting parts of "git rebase" to remove code that should
+   no longer be used.
+
+ * Developer support to emulate unsatisfied prerequisites in tests to
+   ensure that the remainer of the tests still succeeds when tests
+   with prerequisites are skipped.
+
+ * "git update-server-info" learned not to rewrite the file with the
+   same contents.
+
+ * The way of specifying the path to find dynamic libraries at runtime
+   has been simplified.  The old default to pass -R/path/to/dir has been
+   replaced with the new default to pass -Wl,-rpath,/path/to/dir,
+   which is the more recent GCC uses.  Those who need to build with an
+   old GCC can still use "CC_LD_DYNPATH=-R"
+
+ * Prepare use of reachability index in topological walker that works
+   on a range (A..B).
+
+ * A new tutorial targetting specifically aspiring git-core
+   developers has been added.
+
+
+
+Fixes since v2.22
+-----------------
+
+ * A relative pathname given to "git init --template=<path> <repo>"
+   ought to be relative to the directory "git init" gets invoked in,
+   but it instead was made relative to the repository, which has been
+   corrected.
+   (merge e1df7fe43f nd/init-relative-template-fix later to maint).
+
+ * "git worktree add" used to fail when another worktree connected to
+   the same repository was corrupt, which has been corrected.
+   (merge 105df73e71 nd/corrupt-worktrees later to maint).
+
+ * The ownership rule for the file descriptor to fast-import remote
+   backend was mixed up, leading to unrelated file descriptor getting
+   closed, which has been fixed.
+   (merge 3203566a71 mh/import-transport-fd-fix later to maint).
+
+ * A "merge -c" instruction during "git rebase --rebase-merges" should
+   give the user a chance to edit the log message, even when there is
+   otherwise no need to create a new merge and replace the existing
+   one (i.e. fast-forward instead), but did not.  Which has been
+   corrected.
+
+ * Code cleanup and futureproof.
+   (merge 31f5256c82 ds/object-info-for-prefetch-fix later to maint).
+
+ * More parameter validation.
+   (merge de99eb0c24 es/grep-require-name-when-needed later to maint).
+
+ * "git update-server-info" used to leave stale packfiles in its
+   output, which has been corrected.
+   (merge e941c48d49 ew/server-info-remove-crufts later to maint).
+
+ * The server side support for "git fetch" used to show incorrect
+   value for the HEAD symbolic ref when the namespace feature is in
+   use, which has been corrected.
+   (merge 533e088250 jk/HEAD-symref-in-xfer-namespaces later to maint).
+
+ * "git am -i --resolved" segfaulted after trying to see a commit as
+   if it were a tree, which has been corrected.
+   (merge 7663e438c5 jk/am-i-resolved-fix later to maint).
+
+ * "git bundle verify" needs to see if prerequisite objects exist in
+   the receiving repository, but the command did not check if we are
+   in a repository upfront, which has been corrected.
+   (merge 3bbbe467f2 js/bundle-verify-require-object-store later to maint).
+
+ * "git merge --squash" is designed to update the working tree and the
+   index without creating the commit, and this cannot be countermanded
+   by adding the "--commit" option; the command now refuses to work
+   when both options are given.
+   (merge 1d14d0c994 vv/merge-squash-with-explicit-commit later to maint).
+
+ * The data collected by fsmonitor was not properly written back to
+   the on-disk index file, breaking t7519 tests occasionally, which
+   has been corrected.
+   (merge b5a8169752 js/fsmonitor-unflake later to maint).
+
+ * Update to Unicode 12.1 width table.
+   (merge 5817f9caa3 bb/unicode-12.1-reiwa later to maint).
+
+ * The command line to invoke a "git cat-file" command from inside
+   "git p4" was not properly quoted to protect a caret and running a
+   broken command on Windows, which has been corrected.
+   (merge c3f2358de3 mm/p4-unshelve-windows-fix later to maint).
+
+ * "git request-pull" learned to warn when the ref we ask them to pull
+   from in the local repository and in the published repository are
+   different.
+   (merge 0454220d66 pb/request-pull-verify-remote-ref later to maint).
+
+ * When creating a partial clone, the object filtering criteria is
+   recorded for the origin of the clone, but this incorrectly used a
+   hardcoded name "origin" to name that remote; it has been corrected
+   to honor the "--origin <name>" option.
+   (merge 1c4a9f9114 xl/record-partial-clone-origin later to maint).
+
+ * "git fetch" into a lazy clone forgot to fetch base objects that are
+   necessary to complete delta in a thin packfile, which has been
+   corrected.
+   (merge 810e19322d jt/partial-clone-missing-ref-delta-base later to maint).
+
+ * The filter_data used in the list-objects-filter (which manages a
+   lazily sparse clone repository) did not use the dynamic array API
+   correctly---'nr' is supposed to point at one past the last element
+   of the array in use.  This has been corrected.
+   (merge 7140600e2e md/list-objects-filter-memfix later to maint).
+
+ * The description about slashes in gitignore patterns (used to
+   indicate things like "anchored to this level only" and "only
+   matches directories") has been revamped.
+   (merge 1a58bad014 an/ignore-doc-update later to maint).
+
+ * The URL decoding code has been updated to avoid going past the end
+   of the string while parsing %-<hex>-<hex> sequence.
+   (merge d37dc239a4 md/url-parse-harden later to maint).
+
+ * The list of for-each like macros used by clang-format has been
+   updated.
+   (merge fc7e03aace mo/clang-format-for-each-update later to maint).
+
+ * Other code cleanup, docfix, build fix, etc.
+   (merge f547101b26 es/git-debugger-doc later to maint).
+   (merge 7877ac3d7b js/bisect-helper-check-get-oid-return-value later to maint).
+   (merge 0108f47eb3 sw/git-p4-unshelve-branched-files later to maint).
+   (merge 9df8f734fd cm/send-email-document-req-modules later to maint).
+   (merge afc3bf6eb1 ab/hash-object-doc later to maint).
index 7e2a6f61f593921450c311b05269a3555baa97ad..e3f5bc3396d0c7502f16eed989220c8e2010bcc1 100644 (file)
@@ -144,6 +144,20 @@ refer to linkgit:gitignore[5] for details. For convenience:
        This is the same as `gitdir` except that matching is done
        case-insensitively (e.g. on case-insensitive file sytems)
 
+`onbranch`::
+       The data that follows the keyword `onbranch:` is taken to be a
+       pattern with standard globbing wildcards and two additional
+       ones, `**/` and `/**`, that can match multiple path components.
+       If we are in a worktree where the name of the branch that is
+       currently checked out matches the pattern, the include condition
+       is met.
++
+If the pattern ends with `/`, `**` will be automatically added. For
+example, the pattern `foo/` becomes `foo/**`. In other words, it matches
+all branches that begin with `foo/`. This is useful if your branches are
+organized hierarchically and you would like to apply a configuration to
+all the branches in that hierarchy.
+
 A few more notes on matching via `gitdir` and `gitdir/i`:
 
  * Symlinks in `$GIT_DIR` are not resolved before matching.
@@ -206,6 +220,11 @@ Example
        [includeIf "gitdir:/path/to/group/"]
                path = foo.inc
 
+       ; include only if we are in a worktree where foo-branch is
+       ; currently checked out
+       [includeIf "onbranch:foo-branch"]
+               path = foo.inc
+
 Values
 ~~~~~~
 
index 0b14178314251a9b43c62bc5f85a4c1cc445fd1a..f1ca739d574293fd001322a2cc272e2cc0510344 100644 (file)
@@ -1,18 +1,28 @@
 alias.*::
        Command aliases for the linkgit:git[1] command wrapper - e.g.
-       after defining "alias.last = cat-file commit HEAD", the invocation
-       "git last" is equivalent to "git cat-file commit HEAD". To avoid
+       after defining `alias.last = cat-file commit HEAD`, the invocation
+       `git last` is equivalent to `git cat-file commit HEAD`. To avoid
        confusion and troubles with script usage, aliases that
        hide existing Git commands are ignored. Arguments are split by
        spaces, the usual shell quoting and escaping is supported.
        A quote pair or a backslash can be used to quote them.
 +
+Note that the first word of an alias does not necessarily have to be a
+command. It can be a command-line option that will be passed into the
+invocation of `git`. In particular, this is useful when used with `-c`
+to pass in one-time configurations or `-p` to force pagination. For example,
+`loud-rebase = -c commit.verbose=true rebase` can be defined such that
+running `git loud-rebase` would be equivalent to
+`git -c commit.verbose=true rebase`. Also, `ps = -p status` would be a
+helpful alias since `git ps` would paginate the output of `git status`
+where the original command does not.
++
 If the alias expansion is prefixed with an exclamation point,
 it will be treated as a shell command.  For example, defining
-"alias.new = !gitk --all --not ORIG_HEAD", the invocation
-"git new" is equivalent to running the shell command
-"gitk --all --not ORIG_HEAD".  Note that shell commands will be
+`alias.new = !gitk --all --not ORIG_HEAD`, the invocation
+`git new` is equivalent to running the shell command
+`gitk --all --not ORIG_HEAD`.  Note that shell commands will be
 executed from the top-level directory of a repository, which may
 not necessarily be the current directory.
-`GIT_PREFIX` is set as returned by running 'git rev-parse --show-prefix'
+`GIT_PREFIX` is set as returned by running `git rev-parse --show-prefix`
 from the original current directory. See linkgit:git-rev-parse[1].
index dc77941c4807518fab3f9137f202dd27c7f5c5d0..414a5a8a9d7de5bbcefd928ada93aed725427b33 100644 (file)
@@ -85,3 +85,18 @@ format.outputDirectory::
 format.useAutoBase::
        A boolean value which lets you enable the `--base=auto` option of
        format-patch by default.
+
+format.notes::
+       Provides the default value for the `--notes` option to
+       format-patch. Accepts a boolean value, or a ref which specifies
+       where to get notes. If false, format-patch defaults to
+       `--no-notes`. If true, format-patch defaults to `--notes`. If
+       set to a non-boolean value, format-patch defaults to
+       `--notes=<ref>`, where `ref` is the non-boolean value. Defaults
+       to false.
++
+If one wishes to use the ref `ref/notes/true`, please use that literal
+instead.
++
+This configuration can be specified multiple times in order to allow
+multiple notes refs to be included.
index 663663bdecfcbc9187432b07648abb31182520e3..ef5adb3f420df087132a15a9d0e825e911407758 100644 (file)
@@ -8,6 +8,14 @@ tag.sort::
        linkgit:git-tag[1]. Without the "--sort=<value>" option provided, the
        value of this variable will be used as the default.
 
+tag.gpgSign::
+       A boolean to specify whether all tags should be GPG signed.
+       Use of this option when running in an automated script can
+       result in a large number of tags being signed. It is therefore
+       convenient to use an agent to avoid typing your gpg passphrase
+       several times. Note that this option doesn't affects tag signing
+       behavior enabled by "-u <keyid>" or "--local-user=<keyid>" options.
+
 tar.umask::
        This variable can be used to restrict the permission bits of
        tar archive entries.  The default is 0002, which turns off the
index 6ebd512b4f3344c2f166f5bb09f0b5c6eb96ab03..16e14c62827f66afca2690a3be2cf1b69c69afd3 100644 (file)
@@ -8,12 +8,14 @@ git-branch - List, create, or delete branches
 SYNOPSIS
 --------
 [verse]
-'git branch' [--color[=<when>] | --no-color] [-r | -a]
-       [--list] [--show-current] [-v [--abbrev=<length> | --no-abbrev]]
+'git branch' [--color[=<when>] | --no-color] [--show-current]
+       [-v [--abbrev=<length> | --no-abbrev]]
        [--column[=<options>] | --no-column] [--sort=<key>]
        [(--merged | --no-merged) [<commit>]]
        [--contains [<commit]] [--no-contains [<commit>]]
-       [--points-at <object>] [--format=<format>] [<pattern>...]
+       [--points-at <object>] [--format=<format>]
+       [(-r | --remotes) | (-a | --all)]
+       [--list] [<pattern>...]
 'git branch' [--track | --no-track] [-f] <branchname> [<start-point>]
 'git branch' (--set-upstream-to=<upstream> | -u <upstream>) [<branchname>]
 'git branch' --unset-upstream [<branchname>]
@@ -26,13 +28,19 @@ DESCRIPTION
 -----------
 
 If `--list` is given, or if there are no non-option arguments, existing
-branches are listed; the current branch will be highlighted with an
-asterisk.  Option `-r` causes the remote-tracking branches to be listed,
-and option `-a` shows both local and remote branches. If a `<pattern>`
+branches are listed; the current branch will be highlighted in green and
+marked with an asterisk.  Any branches checked out in linked worktrees will
+be highlighted in cyan and marked with a plus sign. Option `-r` causes the
+remote-tracking branches to be listed,
+and option `-a` shows both local and remote branches.
+
+If a `<pattern>`
 is given, it is used as a shell wildcard to restrict the output to
 matching branches. If multiple patterns are given, a branch is shown if
-it matches any of the patterns.  Note that when providing a
-`<pattern>`, you must use `--list`; otherwise the command is interpreted
+it matches any of the patterns.
+
+Note that when providing a
+`<pattern>`, you must use `--list`; otherwise the command may be interpreted
 as branch creation.
 
 With `--contains`, shows only the branches that contain the named commit
@@ -153,10 +161,12 @@ This option is only applicable in non-verbose mode.
 -r::
 --remotes::
        List or delete (if used with -d) the remote-tracking branches.
+       Combine with `--list` to match the optional pattern(s).
 
 -a::
 --all::
        List both remote-tracking branches and local branches.
+       Combine with `--list` to match optional pattern(s).
 
 -l::
 --list::
@@ -174,8 +184,10 @@ This option is only applicable in non-verbose mode.
        When in list mode,
        show sha1 and commit subject line for each head, along with
        relationship to upstream branch (if any). If given twice, print
-       the name of the upstream branch, as well (see also `git remote
-       show <remote>`).
+       the path of the linked worktree (if any) and the name of the upstream
+       branch, as well (see also `git remote show <remote>`).  Note that the
+       current worktree's HEAD will not have its path printed (it will always
+       be your current directory).
 
 -q::
 --quiet::
@@ -322,6 +334,18 @@ $ git branch -D test                                    <2>
 <2> Delete the "test" branch even if the "master" branch (or whichever branch
     is currently checked out) does not have all commits from the test branch.
 
+Listing branches from a specific remote::
++
+------------
+$ git branch -r -l '<remote>/<pattern>'                 <1>
+$ git for-each-ref 'refs/remotes/<remote>/<pattern>'    <2>
+------------
++
+<1> Using `-a` would conflate <remote> with any local branches you happen to
+    have been prefixed with the same <remote> pattern.
+<2> `for-each-ref` can take a wide range of options. See linkgit:git-for-each-ref[1]
+
+Patterns will normally need quoting.
 
 NOTES
 -----
index a0f14b51f2654ced2fb7f76078d95050d2730a7d..5fc97f14de4debac113d490e5a8256cde67dae1b 100644 (file)
@@ -15,7 +15,8 @@ SYNOPSIS
          [--dissociate] [--separate-git-dir <git dir>]
          [--depth <depth>] [--[no-]single-branch] [--no-tags]
          [--recurse-submodules[=<pathspec>]] [--[no-]shallow-submodules]
-         [--jobs <n>] [--] <repository> [<directory>]
+         [--[no-]remote-submodules] [--jobs <n>] [--] <repository>
+         [<directory>]
 
 DESCRIPTION
 -----------
@@ -260,6 +261,12 @@ or `--mirror` is given)
 --[no-]shallow-submodules::
        All submodules which are cloned will be shallow with a depth of 1.
 
+--[no-]remote-submodules::
+       All submodules which are cloned will use the status of the submodule’s
+       remote-tracking branch to update the submodule, rather than the
+       superproject’s recorded SHA-1. Equivalent to passing `--remote` to
+       `git submodule update`.
+
 --separate-git-dir=<git dir>::
        Instead of placing the cloned repository where it is supposed
        to be, place the cloned repository at the specified directory,
index 64c01ba91884df1ec8e49ddc8fe852f1fb2a9425..11427acdde68e659ca5d443beff037df8c9f3ebb 100644 (file)
@@ -129,6 +129,13 @@ marks the same across runs.
        for intermediary filters (e.g. for rewriting commit messages
        which refer to older commits, or for stripping blobs by id).
 
+--reencode=(yes|no|abort)::
+       Specify how to handle `encoding` header in commit objects.  When
+       asking to 'abort' (which is the default), this program will die
+       when encountering such a commit object.  With 'yes', the commit
+       message will be reencoded into UTF-8.  With 'no', the original
+       encoding will be preserved.
+
 --refspec::
        Apply the specified refspec to each ref exported. Multiple of them can
        be specified.
index d65cdb3d08fd745bd4996d0e45259077ea8eb000..7baf9e47b5e61391fbfd5fe44acbfa9943bbe397 100644 (file)
@@ -388,6 +388,7 @@ change to the project.
        original-oid?
        ('author' (SP <name>)? SP LT <email> GT SP <when> LF)?
        'committer' (SP <name>)? SP LT <email> GT SP <when> LF
+       ('encoding' SP <encoding>)?
        data
        ('from' SP <commit-ish> LF)?
        ('merge' SP <commit-ish> LF)?
@@ -455,6 +456,12 @@ that was selected by the --date-format=<fmt> command-line option.
 See ``Date Formats'' above for the set of supported formats, and
 their syntax.
 
+`encoding`
+^^^^^^^^^^
+The optional `encoding` command indicates the encoding of the commit
+message.  Most commits are UTF-8 and the encoding is omitted, but this
+allows importing commit messages into git without first reencoding them.
+
 `from`
 ^^^^^^
 The `from` command is used to specify the commit to initialize
index 774cecc7ede787d22da5b656fe5299e4830d1d2e..6dcd39f6f63dca79f7bc324874e3a4a6172bb688 100644 (file)
@@ -214,6 +214,11 @@ symref::
        `:lstrip` and `:rstrip` options in the same way as `refname`
        above.
 
+worktreepath::
+       The absolute path to the worktree in which the ref is checked
+       out, if it is checked out in any linked worktree. Empty string
+       otherwise.
+
 In addition to the above, for commit and tag objects, the header
 field names (`tree`, `parent`, `object`, `type`, and `tag`) can
 be used to specify the value in the header field.
index 1af85d404f5191c18a8c587df06e7c0620066236..9ce5b8aaee345f07d3bc9eb3eb0c7513c6989900 100644 (file)
@@ -22,7 +22,8 @@ SYNOPSIS
                   [--rfc] [--subject-prefix=Subject-Prefix]
                   [(--reroll-count|-v) <n>]
                   [--to=<email>] [--cc=<email>]
-                  [--[no-]cover-letter] [--quiet] [--notes[=<ref>]]
+                  [--[no-]cover-letter] [--quiet]
+                  [--no-notes | --notes[=<ref>]]
                   [--interdiff=<previous>]
                   [--range-diff=<previous> [--creation-factor=<percent>]]
                   [--progress]
@@ -263,6 +264,7 @@ material (this may change in the future).
        for details.
 
 --notes[=<ref>]::
+--no-notes::
        Append the notes (see linkgit:git-notes[1]) for the commit
        after the three-dash line.
 +
@@ -273,6 +275,9 @@ these explanations after `format-patch` has run but before sending,
 keeping them as Git notes allows them to be maintained between versions
 of the patch series (but see the discussion of the `notes.rewrite`
 configuration options in linkgit:git-notes[1] to use this workflow).
++
+The default is `--no-notes`, unless the `format.notes` configuration is
+set.
 
 --[no-]signature=<signature>::
        Add a signature to each message produced. Per RFC 3676 the signature
index 814e74406ae4fb6ac68213df1f7e8e0192d1dbaf..df9e2c58bdbc5f31edaf25577df744868e16f3de 100644 (file)
@@ -18,9 +18,7 @@ Computes the object ID value for an object with specified type
 with the contents of the named file (which can be outside of the
 work tree), and optionally writes the resulting object into the
 object database.  Reports its object ID to its standard output.
-This is used by 'git cvsimport' to update the index
-without modifying files in the work tree.  When <type> is not
-specified, it defaults to "blob".
+When <type> is not specified, it defaults to "blob".
 
 OPTIONS
 -------
index 6294dbc09d213815fb3a31328de87af52fcf606a..3ff0393507c5ab25f06195b1fdca4bd7bd59770a 100644 (file)
@@ -13,8 +13,7 @@ SYNOPSIS
        [-s <strategy>] [-X <strategy-option>] [-S[<keyid>]]
        [--[no-]allow-unrelated-histories]
        [--[no-]rerere-autoupdate] [-m <msg>] [-F <file>] [<commit>...]
-'git merge' --abort
-'git merge' --continue
+'git merge' (--continue | --abort | --quit)
 
 DESCRIPTION
 -----------
@@ -100,6 +99,10 @@ commit or stash your changes before running 'git merge'.
 'git merge --abort' is equivalent to 'git reset --merge' when
 `MERGE_HEAD` is present.
 
+--quit::
+       Forget about the current merge in progress. Leave the index
+       and the working tree as-is.
+
 --continue::
        After a 'git merge' stops due to conflicts you can conclude the
        merge by running 'git merge --continue' (see "HOW TO RESOLVE
index 5e4e9276479c94e8a73ea4cd72819d50ec7b5e91..057c21d8ee5de7757c788e9a51abe1e7d2504a04 100644 (file)
@@ -12,7 +12,7 @@ SYNOPSIS
        [<upstream> [<branch>]]
 'git rebase' [-i | --interactive] [<options>] [--exec <cmd>] [--onto <newbase>]
        --root [<branch>]
-'git rebase' --continue | --skip | --abort | --quit | --edit-todo | --show-current-patch
+'git rebase' (--continue | --skip | --abort | --quit | --edit-todo | --show-current-patch)
 
 DESCRIPTION
 -----------
index 88609ff4351bb7793ffa0ce9f21efb39a8157da4..9392760b25411c889d53114fd55886cc7e3fb679 100644 (file)
@@ -48,6 +48,7 @@ SYNOPSIS
             [ --date=<format>]
             [ [ --objects | --objects-edge | --objects-edge-aggressive ]
               [ --unpacked ]
+              [ --object-names | --no-object-names ]
               [ --filter=<filter-spec> [ --filter-print-omitted ] ] ]
             [ --missing=<missing-action> ]
             [ --pretty | --header ]
index 1afe9fc858ea7dcd05ae5f77c994af8f17f5bf52..d93e5d0f58f0602e0cd0da7bfd6ce11f49b110d6 100644 (file)
@@ -278,6 +278,14 @@ must be used for each option.
 Automating
 ~~~~~~~~~~
 
+--no-[to|cc|bcc]::
+       Clears any list of "To:", "Cc:", "Bcc:" addresses previously
+       set via config.
+
+--no-identity::
+       Clears the previously read value of `sendemail.identity` set
+       via config, if any.
+
 --to-cmd=<command>::
        Specify a command to execute once per patch file which
        should generate patch file specific "To:" entries.
@@ -500,8 +508,12 @@ app-specific or your regular password as appropriate.  If you have credential
 helper configured (see linkgit:git-credential[1]), the password will be saved in
 the credential store so you won't have to type it the next time.
 
-Note: the following perl modules are required
-      Net::SMTP::SSL, MIME::Base64 and Authen::SASL
+Note: the following core Perl modules that may be installed with your
+distribution of Perl are required:
+MIME::Base64, MIME::QuotedPrint, Net::Domain and Net::SMTP.
+These additional Perl modules are also required:
+Authen::SASL and Mail::Address.
+
 
 SEE ALSO
 --------
index a74e7b926d030f6f385e49be68714133f6e3c06e..2e5599a67f86ad16969a5ea2697bdeb50ca801a6 100644 (file)
@@ -64,6 +64,13 @@ OPTIONS
 -s::
 --sign::
        Make a GPG-signed tag, using the default e-mail address's key.
+       The default behavior of tag GPG-signing is controlled by `tag.gpgSign`
+       configuration variable if it exists, or disabled oder otherwise.
+       See linkgit:git-config[1].
+
+--no-sign::
+       Override `tag.gpgSign` configuration variable that is
+       set to force each and every tag to be signed.
 
 -u <keyid>::
 --local-user=<keyid>::
index bd0e36492fa0f7b8a8a4707c2ac7db461fcc74c8..969bb2e15f1070ddc116b408b1eba9c61d496606 100644 (file)
@@ -9,7 +9,7 @@ git-update-server-info - Update auxiliary info file to help dumb servers
 SYNOPSIS
 --------
 [verse]
-'git update-server-info' [--force]
+'git update-server-info'
 
 DESCRIPTION
 -----------
@@ -19,15 +19,6 @@ $GIT_OBJECT_DIRECTORY/info directories to help clients discover
 what references and packs the server has.  This command
 generates such auxiliary files.
 
-
-OPTIONS
--------
-
--f::
---force::
-       Update the info files from scratch.
-
-
 OUTPUT
 ------
 
index 6ddc1e2ca6830ac165543dad4a57afd6d4b50c49..f9b09db89b83b9be12c4fdce856b4be822db23f0 100644 (file)
@@ -33,7 +33,8 @@ individual Git commands with "git help command".  linkgit:gitcli[7]
 manual page gives you an overview of the command-line command syntax.
 
 A formatted and hyperlinked copy of the latest Git documentation
-can be viewed at `https://git.github.io/htmldocs/git.html`.
+can be viewed at https://git.github.io/htmldocs/git.html
+or https://git-scm.com/docs.
 
 
 OPTIONS
index 4fb20cd0e963b6cf52c07c6ae7492adbf35f6cbf..2796dfc83bb15174c2657f90ded09b6d6af80e41 100644 (file)
@@ -819,7 +819,7 @@ patterns are available:
 
 - `java` suitable for source code in the Java language.
 
-- `matlab` suitable for source code in the MATLAB language.
+- `matlab` suitable for source code in the MATLAB and Octave languages.
 
 - `objc` suitable for source code in the Objective-C language.
 
@@ -833,6 +833,8 @@ patterns are available:
 
 - `ruby` suitable for source code in the Ruby language.
 
+- `rust` suitable for source code in the Rust language.
+
 - `tex` suitable for source code for LaTeX documents.
 
 
index b5bc9dbff05b293caae7c42e865b19bc067c4d82..d47b1ae29637269859bc7f41d0fc49732c8f39aa 100644 (file)
@@ -89,28 +89,28 @@ PATTERN FORMAT
    Put a backslash ("`\`") in front of the first "`!`" for patterns
    that begin with a literal "`!`", for example, "`\!important!.txt`".
 
- - If the pattern ends with a slash, it is removed for the
-   purpose of the following description, but it would only find
-   a match with a directory.  In other words, `foo/` will match a
-   directory `foo` and paths underneath it, but will not match a
-   regular file or a symbolic link `foo` (this is consistent
-   with the way how pathspec works in general in Git).
-
- - If the pattern does not contain a slash '/', Git treats it as
-   a shell glob pattern and checks for a match against the
-   pathname relative to the location of the `.gitignore` file
-   (relative to the toplevel of the work tree if not from a
-   `.gitignore` file).
-
- - Otherwise, Git treats the pattern as a shell glob: "`*`" matches
-   anything except "`/`", "`?`" matches any one character except "`/`"
-   and "`[]`" matches one character in a selected range. See
-   fnmatch(3) and the FNM_PATHNAME flag for a more detailed
  description.
-
- - A leading slash matches the beginning of the pathname.
-   For example, "/{asterisk}.c" matches "cat-file.c" but not
-   "mozilla-sha1/sha1.c".
+ - The slash '/' is used as the directory separator. Separators may
+   occur at the beginning, middle or end of the `.gitignore` search pattern.
+
+ - If there is a separator at the beginning or middle (or both) of the
+   pattern, then the pattern is relative to the directory level of the
+   particular `.gitignore` file itself. Otherwise the pattern may also
+   match at any level below the `.gitignore` level.
+
+ - If there is a separator at the end of the pattern then the pattern
+   will only match directories, otherwise the pattern can match both
+   files and directories.
+
+ - For example, a pattern `doc/frotz/` matches `doc/frotz` directory,
+   but not `a/doc/frotz` directory; however `frotz/` matches `frotz`
+   and `a/frotz` that is a directory (all paths are relative from
+   the `.gitignore` file).
+
- An asterisk "`*`" matches anything except a slash.
+   The character "`?`" matches any one character except "`/`".
+   The range notation, e.g. `[a-zA-Z]`, can be used to match
+   one of the characters in a range. See fnmatch(3) and the
+   FNM_PATHNAME flag for a more detailed description.
 
 Two consecutive asterisks ("`**`") in patterns matched against
 full pathname may have special meaning:
@@ -152,6 +152,28 @@ To stop tracking a file that is currently tracked, use
 EXAMPLES
 --------
 
+ - The pattern `hello.*` matches any file or folder
+   whose name begins with `hello`. If one wants to restrict
+   this only to the directory and not in its subdirectories,
+   one can prepend the pattern with a slash, i.e. `/hello.*`;
+   the pattern now matches `hello.txt`, `hello.c` but not
+   `a/hello.java`.
+
+ - The pattern `foo/` will match a directory `foo` and
+   paths underneath it, but will not match a regular file
+   or a symbolic link `foo` (this is consistent with the
+   way how pathspec works in general in Git)
+
+ - The pattern `doc/frotz` and `/doc/frotz` have the same effect
+   in any `.gitignore` file. In other words, a leading slash
+   is not relevant  if there is already a middle slash in
+   the pattern.
+
+ - The pattern "foo/*", matches "foo/test.json"
+   (a regular file), "foo/bar" (a directory), but it does not match
+   "foo/bar/hello.c" (a regular file), as the asterisk in the
+   pattern does not match "bar/hello.c" which has a slash in it.
+
 --------------------------------------------------------------
     $ git status
     [...]
index 61876dbc335e240b8842aba3dd4e3fd8a54e574a..79a00d2a4abd6f7191f2639185e199fa85f6289b 100644 (file)
@@ -102,6 +102,8 @@ merge.
 +
 With --no-squash perform the merge and commit the result. This
 option can be used to override --squash.
++
+With --squash, --commit is not allowed, and will fail.
 
 -s <strategy>::
 --strategy=<strategy>::
index 71a1fcc0939f791fe7f0423f15faf458dd0598f5..286fc163f14256f4f7ff4be7fba57cef963d2f75 100644 (file)
@@ -708,6 +708,16 @@ ifdef::git-rev-list[]
        Only useful with `--objects`; print the object IDs that are not
        in packs.
 
+--object-names::
+       Only useful with `--objects`; print the names of the object IDs
+       that are found. This is the default behavior.
+
+--no-object-names::
+       Only useful with `--objects`; does not print the names of the object
+       IDs that are found. This inverts `--object-names`. This flag allows
+       the output to be more easily parsed by commands such as
+       linkgit:git-cat-file[1].
+
 --filter=<filter-spec>::
        Only useful with one of the `--objects*`; omits objects (usually
        blobs) from the list of printed objects.  The '<filter-spec>'
index 23c3cc7a3727ed88513c1e735a41654a75334363..f7ffe7d5998c6ec6f7ed4ec420c938d7f129c94f 100644 (file)
@@ -35,7 +35,7 @@ Format details are given in a later section.
 === The Normal Format Target
 
 The normal format target is a tradition printf format and similar
-to GIT_TRACE format.  This format is enabled with the `GIT_TR`
+to GIT_TRACE format.  This format is enabled with the `GIT_TRACE2`
 environment variable or the `trace2.normalTarget` system or global
 config setting.
 
index 7805b0968c828fda1601fe00c2b5b9d9359b38e2..fb53341d5ee3116361d599b5aca2e5ea3a228589 100644 (file)
@@ -127,23 +127,6 @@ Design Details
   helpful for these clones, anyway. The commit-graph will not be read or
   written when shallow commits are present.
 
-Future Work
------------
-
-- After computing and storing generation numbers, we must make graph
-  walks aware of generation numbers to gain the performance benefits they
-  enable. This will mostly be accomplished by swapping a commit-date-ordered
-  priority queue with one ordered by generation number. The following
-  operations are important candidates:
-
-    - 'log --topo-order'
-    - 'tag --merged'
-
-- A server could provide a commit-graph file as part of the network protocol
-  to avoid extra calculations by clients. This feature is only of benefit if
-  the user is willing to trust the file, because verifying the file is correct
-  is as hard as computing it from scratch.
-
 Related Links
 -------------
 [0] https://bugs.chromium.org/p/git/issues/detail?id=8
index 67f86b3e678b20ed2558ae6350b3d8138c88d28d..122f6479ef9f772f575ecb673e0f960900526fc1 100755 (executable)
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 GVF=GIT-VERSION-FILE
-DEF_VER=v2.22.0
+DEF_VER=v2.22.GIT
 
 LF='
 '
index 8a7e2353520ddd7e0c8074d2b32d0441d97c1597..f58bf14c7bf3d9b29fcd0a97bc14e253f3eb675d 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -265,10 +265,6 @@ all::
 #
 # Define NO_DEFLATE_BOUND if your zlib does not have deflateBound.
 #
-# Define NO_R_TO_GCC_LINKER if your gcc does not like "-R/path/lib"
-# that tells runtime paths to dynamic libraries;
-# "-Wl,-rpath=/path/lib" is used instead.
-#
 # Define NO_NORETURN if using buggy versions of gcc 4.6+ and profile feedback,
 # as the compiler can crash (http://gcc.gnu.org/bugzilla/show_bug.cgi?id=49299)
 #
@@ -624,8 +620,6 @@ SCRIPT_SH += git-web--browse.sh
 
 SCRIPT_LIB += git-mergetool--lib
 SCRIPT_LIB += git-parse-remote
-SCRIPT_LIB += git-rebase--am
-SCRIPT_LIB += git-rebase--common
 SCRIPT_LIB += git-rebase--preserve-merges
 SCRIPT_LIB += git-sh-setup
 SCRIPT_LIB += git-sh-i18n
@@ -1160,6 +1154,7 @@ endif
 # which'll override these defaults.
 CFLAGS = -g -O2 -Wall
 LDFLAGS =
+CC_LD_DYNPATH = -Wl,-rpath,
 BASIC_CFLAGS = -I.
 BASIC_LDFLAGS =
 
@@ -1290,16 +1285,6 @@ ifeq ($(uname_S),Darwin)
        PTHREAD_LIBS =
 endif
 
-ifndef CC_LD_DYNPATH
-       ifdef NO_R_TO_GCC_LINKER
-               # Some gcc does not accept and pass -R to the linker to specify
-               # the runtime dynamic library path.
-               CC_LD_DYNPATH = -Wl,-rpath,
-       else
-               CC_LD_DYNPATH = -R
-       endif
-endif
-
 ifdef NO_LIBGEN_H
        COMPAT_CFLAGS += -DNO_LIBGEN_H
        COMPAT_OBJS += compat/basename.o
index 0b6d9fdbcf0cc440914446e494fde666c893562f..248d137c43b1d1e8858a863fdb4a811739ef3eca 120000 (symlink)
--- a/RelNotes
+++ b/RelNotes
@@ -1 +1 @@
-Documentation/RelNotes/2.22.0.txt
\ No newline at end of file
+Documentation/RelNotes/2.23.0.txt
\ No newline at end of file
diff --git a/apply.c b/apply.c
index f15afa9f6af98ae41af60eb2296ee35d38ccc7fd..4992eca416c9c2b5d21de42460cafa839df25f4e 100644 (file)
--- a/apply.c
+++ b/apply.c
@@ -4310,7 +4310,7 @@ static int add_index_file(struct apply_state *state,
                                                     "created file '%s'"),
                                                   path);
                        }
-                       fill_stat_cache_info(ce, &st);
+                       fill_stat_cache_info(state->repo->index, ce, &st);
                }
                if (write_object_file(buf, size, blob_type, &ce->oid) < 0) {
                        discard_cache_entry(ce);
index a594cc23e25458250885244f477a5d4879df2537..e70838fb872f98820a641ccaccc06fd4c0c381f7 100644 (file)
--- a/branch.c
+++ b/branch.c
@@ -338,14 +338,19 @@ void create_branch(struct repository *r,
        free(real_ref);
 }
 
-void remove_branch_state(struct repository *r)
+void remove_merge_branch_state(struct repository *r)
 {
-       sequencer_post_commit_cleanup(r);
        unlink(git_path_merge_head(r));
        unlink(git_path_merge_rr(r));
        unlink(git_path_merge_msg(r));
        unlink(git_path_merge_mode(r));
+}
+
+void remove_branch_state(struct repository *r)
+{
+       sequencer_post_commit_cleanup(r);
        unlink(git_path_squash_msg(r));
+       remove_merge_branch_state(r);
 }
 
 void die_if_checked_out(const char *branch, int ignore_current_worktree)
index 6f38db14e9c496c55e204791e5ab0a5243186118..064ee576f29764637dfdc62141973e935f8fd488 100644 (file)
--- a/branch.h
+++ b/branch.h
@@ -60,6 +60,12 @@ int validate_branchname(const char *name, struct strbuf *ref);
  */
 int validate_new_branchname(const char *name, struct strbuf *ref, int force);
 
+/*
+ * Remove information about the merge state on the current
+ * branch. (E.g., MERGE_HEAD)
+ */
+void remove_merge_branch_state(struct repository *r);
+
 /*
  * Remove information about the state of working on the current
  * branch. (E.g., MERGE_HEAD)
index 912d9821b18acfa0a4c1e3bed8e6c9e7b43dcdf8..252e37ddf08e45eceb98628fe598df3ebcafb1fd 100644 (file)
@@ -1339,9 +1339,10 @@ static void write_index_patch(const struct am_state *state)
        struct rev_info rev_info;
        FILE *fp;
 
-       if (!get_oid_tree("HEAD", &head))
-               tree = lookup_tree(the_repository, &head);
-       else
+       if (!get_oid("HEAD", &head)) {
+               struct commit *commit = lookup_commit_or_die(&head, "HEAD");
+               tree = get_commit_tree(commit);
+       } else
                tree = lookup_tree(the_repository,
                                   the_repository->hash_algo->empty_tree);
 
@@ -1643,11 +1644,8 @@ static int do_interactive(struct am_state *state)
 {
        assert(state->msg);
 
-       if (!isatty(0))
-               die(_("cannot be interactive without stdin connected to a terminal."));
-
        for (;;) {
-               const char *reply;
+               char reply[64];
 
                puts(_("Commit Body is:"));
                puts("--------------------------");
@@ -1659,11 +1657,11 @@ static int do_interactive(struct am_state *state)
                 * in your translation. The program will only accept English
                 * input at this point.
                 */
-               reply = git_prompt(_("Apply? [y]es/[n]o/[e]dit/[v]iew patch/[a]ccept all: "), PROMPT_ECHO);
+               printf(_("Apply? [y]es/[n]o/[e]dit/[v]iew patch/[a]ccept all: "));
+               if (!fgets(reply, sizeof(reply), stdin))
+                       die("unable to read from stdin; aborting");
 
-               if (!reply) {
-                       continue;
-               } else if (*reply == 'y' || *reply == 'Y') {
+               if (*reply == 'y' || *reply == 'Y') {
                        return 0;
                } else if (*reply == 'a' || *reply == 'A') {
                        state->interactive = 0;
@@ -1803,7 +1801,7 @@ static void am_run(struct am_state *state, int resume)
         */
        if (!state->rebasing) {
                am_destroy(state);
-               close_all_packs(the_repository->objects);
+               close_object_store(the_repository->objects);
                run_command_v_opt(argv_gc_auto, RUN_GIT_CMD);
        }
 }
@@ -2334,6 +2332,9 @@ int cmd_am(int argc, const char **argv, const char *prefix)
                                argv_array_push(&paths, mkpath("%s/%s", prefix, argv[i]));
                }
 
+               if (state.interactive && !paths.argc)
+                       die(_("interactive mode requires patches on the command line"));
+
                am_setup(&state, patch_format, paths.argv, keep_cr);
 
                argv_array_clear(&paths);
index e7325fe37f6148fd1ccd6fc5842bbc8f5022c306..1fbe156e67a4c75694de7e606a21cf959dd19f51 100644 (file)
@@ -570,7 +570,10 @@ static int bisect_start(struct bisect_terms *terms, int no_checkout,
        write_file(git_path_bisect_start(), "%s\n", start_head.buf);
 
        if (no_checkout) {
-               get_oid(start_head.buf, &oid);
+               if (get_oid(start_head.buf, &oid) < 0) {
+                       retval = error(_("invalid ref: '%s'"), start_head.buf);
+                       goto finish;
+               }
                if (update_ref(NULL, "BISECT_HEAD", &oid, NULL, 0,
                               UPDATE_REFS_MSG_ON_ERR)) {
                        retval = -1;
index d4359b33ac0fb27b6b8be7109a2a862bc9cd2d77..2ef214632f025b0da7bf5f118aebe3c68d5af2f8 100644 (file)
@@ -47,6 +47,7 @@ static char branch_colors[][COLOR_MAXLEN] = {
        GIT_COLOR_NORMAL,       /* LOCAL */
        GIT_COLOR_GREEN,        /* CURRENT */
        GIT_COLOR_BLUE,         /* UPSTREAM */
+       GIT_COLOR_CYAN,         /* WORKTREE */
 };
 enum color_branch {
        BRANCH_COLOR_RESET = 0,
@@ -54,7 +55,8 @@ enum color_branch {
        BRANCH_COLOR_REMOTE = 2,
        BRANCH_COLOR_LOCAL = 3,
        BRANCH_COLOR_CURRENT = 4,
-       BRANCH_COLOR_UPSTREAM = 5
+       BRANCH_COLOR_UPSTREAM = 5,
+       BRANCH_COLOR_WORKTREE = 6
 };
 
 static const char *color_branch_slots[] = {
@@ -64,6 +66,7 @@ static const char *color_branch_slots[] = {
        [BRANCH_COLOR_LOCAL]    = "local",
        [BRANCH_COLOR_CURRENT]  = "current",
        [BRANCH_COLOR_UPSTREAM] = "upstream",
+       [BRANCH_COLOR_WORKTREE] = "worktree",
 };
 
 static struct string_list output = STRING_LIST_INIT_DUP;
@@ -342,9 +345,10 @@ static char *build_format(struct ref_filter *filter, int maxwidth, const char *r
        struct strbuf local = STRBUF_INIT;
        struct strbuf remote = STRBUF_INIT;
 
-       strbuf_addf(&local, "%%(if)%%(HEAD)%%(then)* %s%%(else)  %s%%(end)",
-                   branch_get_color(BRANCH_COLOR_CURRENT),
-                   branch_get_color(BRANCH_COLOR_LOCAL));
+       strbuf_addf(&local, "%%(if)%%(HEAD)%%(then)* %s%%(else)%%(if)%%(worktreepath)%%(then)+ %s%%(else)  %s%%(end)%%(end)",
+                       branch_get_color(BRANCH_COLOR_CURRENT),
+                       branch_get_color(BRANCH_COLOR_WORKTREE),
+                       branch_get_color(BRANCH_COLOR_LOCAL));
        strbuf_addf(&remote, "  %s",
                    branch_get_color(BRANCH_COLOR_REMOTE));
 
@@ -363,9 +367,13 @@ static char *build_format(struct ref_filter *filter, int maxwidth, const char *r
                strbuf_addf(&local, " %s ", obname.buf);
 
                if (filter->verbose > 1)
+               {
+                       strbuf_addf(&local, "%%(if:notequals=*)%%(HEAD)%%(then)%%(if)%%(worktreepath)%%(then)(%s%%(worktreepath)%s) %%(end)%%(end)",
+                                   branch_get_color(BRANCH_COLOR_WORKTREE), branch_get_color(BRANCH_COLOR_RESET));
                        strbuf_addf(&local, "%%(if)%%(upstream)%%(then)[%s%%(upstream:short)%s%%(if)%%(upstream:track)"
                                    "%%(then): %%(upstream:track,nobracket)%%(end)] %%(end)%%(contents:subject)",
                                    branch_get_color(BRANCH_COLOR_UPSTREAM), branch_get_color(BRANCH_COLOR_RESET));
+               }
                else
                        strbuf_addf(&local, "%%(if)%%(upstream:track)%%(then)%%(upstream:track) %%(end)%%(contents:subject)");
 
@@ -830,7 +838,8 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
                strbuf_release(&buf);
        } else if (argc > 0 && argc <= 2) {
                if (filter.kind != FILTER_REFS_BRANCHES)
-                       die(_("-a and -r options to 'git branch' do not make sense with a branch name"));
+                       die(_("The -a, and -r, options to 'git branch' do not take a branch name.\n"
+                                 "Did you mean to use: -a|-r --list <pattern>?"));
 
                if (track == BRANCH_TRACK_OVERRIDE)
                        die(_("the '--set-upstream' option is no longer supported. Please use '--track' or '--set-upstream-to' instead."));
index 85b0d3155de0d9ee9257ac44acc699c004022836..189ea1c2a0d2767534cb244c5780ad4a0d74a814 100644 (file)
@@ -67,6 +67,7 @@ static int max_jobs = -1;
 static struct string_list option_recurse_submodules = STRING_LIST_INIT_NODUP;
 static struct list_objects_filter_options filter_options;
 static struct string_list server_options = STRING_LIST_INIT_NODUP;
+static int option_remote_submodules;
 
 static int recurse_submodules_cb(const struct option *opt,
                                 const char *arg, int unset)
@@ -142,6 +143,8 @@ static struct option builtin_clone_options[] = {
        OPT_SET_INT('6', "ipv6", &family, N_("use IPv6 addresses only"),
                        TRANSPORT_FAMILY_IPV6),
        OPT_PARSE_LIST_OBJECTS_FILTER(&filter_options),
+       OPT_BOOL(0, "remote-submodules", &option_remote_submodules,
+                   N_("any cloned submodules will use their remote-tracking branch")),
        OPT_END()
 };
 
@@ -354,8 +357,7 @@ static void setup_reference(void)
                             add_one_reference, &required);
 }
 
-static void copy_alternates(struct strbuf *src, struct strbuf *dst,
-                           const char *src_repo)
+static void copy_alternates(struct strbuf *src, const char *src_repo)
 {
        /*
         * Read from the source objects/info/alternates file
@@ -436,7 +438,7 @@ static void copy_or_link_directory(struct strbuf *src, struct strbuf *dest,
 
                /* Files that cannot be copied bit-for-bit... */
                if (!strcmp(src->buf + src_baselen, "/info/alternates")) {
-                       copy_alternates(src, dest, src_repo);
+                       copy_alternates(src, src_repo);
                        continue;
                }
 
@@ -791,6 +793,11 @@ static int checkout(int submodule_progress)
                if (option_verbosity < 0)
                        argv_array_push(&args, "--quiet");
 
+               if (option_remote_submodules) {
+                       argv_array_push(&args, "--remote");
+                       argv_array_push(&args, "--no-fetch");
+               }
+
                err = run_command_v_opt(args.argv, RUN_GIT_CMD);
                argv_array_clear(&args);
        }
@@ -1220,7 +1227,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
                        remote_head_points_at, &branch_top);
 
        if (filter_options.choice)
-               partial_clone_register("origin", &filter_options);
+               partial_clone_register(option_origin, &filter_options);
 
        if (is_local)
                clone_local(path, git_dir);
@@ -1245,7 +1252,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
        transport_disconnect(transport);
 
        if (option_dissociate) {
-               close_all_packs(the_repository->objects);
+               close_object_store(the_repository->objects);
                dissociate_from_references();
        }
 
index 5228ccf37a5c8f568091ebef6df86fda40aa93dc..e815e148aa18364c198eef0ee5c2ed4e34ae77e9 100644 (file)
@@ -43,7 +43,7 @@ int cmd_column(int argc, const char **argv, const char *prefix)
 
        memset(&copts, 0, sizeof(copts));
        copts.padding = 1;
-       argc = parse_options(argc, argv, "", options, builtin_column_usage, 0);
+       argc = parse_options(argc, argv, prefix, options, builtin_column_usage, 0);
        if (argc)
                usage_with_options(builtin_column_usage, options);
        if (real_command || command) {
index 537fdfd0f0759e8cce2303af5a197ae77b2b879b..d8efa5bab276a816bef48e9cb3891ccc0cb81aad 100644 (file)
@@ -141,6 +141,8 @@ static int graph_write(int argc, const char **argv)
        struct string_list *pack_indexes = NULL;
        struct string_list *commit_hex = NULL;
        struct string_list lines;
+       int result = 0;
+       unsigned int flags = COMMIT_GRAPH_PROGRESS;
 
        static struct option builtin_commit_graph_write_options[] = {
                OPT_STRING(0, "object-dir", &opts.obj_dir,
@@ -165,13 +167,13 @@ static int graph_write(int argc, const char **argv)
                die(_("use at most one of --reachable, --stdin-commits, or --stdin-packs"));
        if (!opts.obj_dir)
                opts.obj_dir = get_object_directory();
+       if (opts.append)
+               flags |= COMMIT_GRAPH_APPEND;
 
        read_replace_refs = 0;
 
-       if (opts.reachable) {
-               write_commit_graph_reachable(opts.obj_dir, opts.append, 1);
-               return 0;
-       }
+       if (opts.reachable)
+               return write_commit_graph_reachable(opts.obj_dir, flags);
 
        string_list_init(&lines, 0);
        if (opts.stdin_packs || opts.stdin_commits) {
@@ -188,14 +190,14 @@ static int graph_write(int argc, const char **argv)
                UNLEAK(buf);
        }
 
-       write_commit_graph(opts.obj_dir,
-                          pack_indexes,
-                          commit_hex,
-                          opts.append,
-                          1);
+       if (write_commit_graph(opts.obj_dir,
+                              pack_indexes,
+                              commit_hex,
+                              flags))
+               result = 1;
 
        UNLEAK(lines);
-       return 0;
+       return result;
 }
 
 int cmd_commit_graph(int argc, const char **argv, const char *prefix)
index 1c9e8e2228c7ce58375bc247c4ad5850a1bd7d2d..192140111747cc0683c1ae19a785000110808ff3 100644 (file)
@@ -1669,8 +1669,9 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
                      "new_index file. Check that disk is not full and quota is\n"
                      "not exceeded, and then \"git reset HEAD\" to recover."));
 
-       if (git_env_bool(GIT_TEST_COMMIT_GRAPH, 0))
-               write_commit_graph_reachable(get_object_directory(), 0, 0);
+       if (git_env_bool(GIT_TEST_COMMIT_GRAPH, 0) &&
+           write_commit_graph_reachable(get_object_directory(), 0))
+               return 1;
 
        repo_rerere(the_repository, 0);
        run_command_v_opt(argv_gc_auto, RUN_GIT_CMD);
index 9e283482efcfa6de0376cc9306061cea149b12df..c22cef3b2faff945148029197a32253540c24f73 100644 (file)
@@ -33,6 +33,7 @@ static const char *fast_export_usage[] = {
 static int progress;
 static enum { SIGNED_TAG_ABORT, VERBATIM, WARN, WARN_STRIP, STRIP } signed_tag_mode = SIGNED_TAG_ABORT;
 static enum { TAG_FILTERING_ABORT, DROP, REWRITE } tag_of_filtered_mode = TAG_FILTERING_ABORT;
+static enum { REENCODE_ABORT, REENCODE_YES, REENCODE_NO } reencode_mode = REENCODE_ABORT;
 static int fake_missing_tagger;
 static int use_done_feature;
 static int no_data;
@@ -77,6 +78,31 @@ static int parse_opt_tag_of_filtered_mode(const struct option *opt,
        return 0;
 }
 
+static int parse_opt_reencode_mode(const struct option *opt,
+                                  const char *arg, int unset)
+{
+       if (unset) {
+               reencode_mode = REENCODE_ABORT;
+               return 0;
+       }
+
+       switch (git_parse_maybe_bool(arg)) {
+       case 0:
+               reencode_mode = REENCODE_NO;
+               break;
+       case 1:
+               reencode_mode = REENCODE_YES;
+               break;
+       default:
+               if (!strcasecmp(arg, "abort"))
+                       reencode_mode = REENCODE_ABORT;
+               else
+                       return error("Unknown reencoding mode: %s", arg);
+       }
+
+       return 0;
+}
+
 static struct decoration idnums;
 static uint32_t last_idnum;
 
@@ -453,7 +479,7 @@ static const char *find_encoding(const char *begin, const char *end)
        bol = memmem(begin, end ? end - begin : strlen(begin),
                     needle, strlen(needle));
        if (!bol)
-               return git_commit_encoding;
+               return NULL;
        bol += strlen(needle);
        eol = strchrnul(bol, '\n');
        *eol = '\0';
@@ -633,18 +659,32 @@ static void handle_commit(struct commit *commit, struct rev_info *rev,
        }
 
        mark_next_object(&commit->object);
-       if (anonymize)
+       if (anonymize) {
                reencoded = anonymize_commit_message(message);
-       else if (!is_encoding_utf8(encoding))
-               reencoded = reencode_string(message, "UTF-8", encoding);
+       } else if (encoding) {
+               switch(reencode_mode) {
+               case REENCODE_YES:
+                       reencoded = reencode_string(message, "UTF-8", encoding);
+                       break;
+               case REENCODE_NO:
+                       break;
+               case REENCODE_ABORT:
+                       die("Encountered commit-specific encoding %s in commit "
+                           "%s; use --reencode=[yes|no] to handle it",
+                           encoding, oid_to_hex(&commit->object.oid));
+               }
+       }
        if (!commit->parents)
                printf("reset %s\n", refname);
        printf("commit %s\nmark :%"PRIu32"\n", refname, last_idnum);
        if (show_original_ids)
                printf("original-oid %s\n", oid_to_hex(&commit->object.oid));
-       printf("%.*s\n%.*s\ndata %u\n%s",
+       printf("%.*s\n%.*s\n",
               (int)(author_end - author), author,
-              (int)(committer_end - committer), committer,
+              (int)(committer_end - committer), committer);
+       if (!reencoded && encoding)
+               printf("encoding %s\n", encoding);
+       printf("data %u\n%s",
               (unsigned)(reencoded
                          ? strlen(reencoded) : message
                          ? strlen(message) : 0),
@@ -1088,6 +1128,9 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
                OPT_CALLBACK(0, "tag-of-filtered-object", &tag_of_filtered_mode, N_("mode"),
                             N_("select handling of tags that tag filtered objects"),
                             parse_opt_tag_of_filtered_mode),
+               OPT_CALLBACK(0, "reencode", &reencode_mode, N_("mode"),
+                            N_("select handling of commit messages in an alternate encoding"),
+                            parse_opt_reencode_mode),
                OPT_STRING(0, "export-marks", &export_filename, N_("file"),
                             N_("Dump marks to this file")),
                OPT_STRING(0, "import-marks", &import_filename, N_("file"),
index 6a3c5078974d553447c6468032408f99e76cc57d..667f2cec7bdcc1bd5bf6f0f99d0dc39ff4071b98 100644 (file)
@@ -242,6 +242,7 @@ static int will_fetch(struct ref **head, const unsigned char *sha1)
 struct refname_hash_entry {
        struct hashmap_entry ent; /* must be the first member */
        struct object_id oid;
+       int ignore;
        char refname[FLEX_ARRAY];
 };
 
@@ -290,6 +291,11 @@ static int refname_hash_exists(struct hashmap *map, const char *refname)
        return !!hashmap_get_from_hash(map, strhash(refname), refname);
 }
 
+static void clear_item(struct refname_hash_entry *item)
+{
+       item->ignore = 1;
+}
+
 static void find_non_local_tags(const struct ref *refs,
                                struct ref **head,
                                struct ref ***tail)
@@ -322,7 +328,7 @@ static void find_non_local_tags(const struct ref *refs,
                            !will_fetch(head, ref->old_oid.hash) &&
                            !has_object_file_with_flags(&item->oid, OBJECT_INFO_QUICK) &&
                            !will_fetch(head, item->oid.hash))
-                               oidclr(&item->oid);
+                               clear_item(item);
                        item = NULL;
                        continue;
                }
@@ -336,7 +342,7 @@ static void find_non_local_tags(const struct ref *refs,
                if (item &&
                    !has_object_file_with_flags(&item->oid, OBJECT_INFO_QUICK) &&
                    !will_fetch(head, item->oid.hash))
-                       oidclr(&item->oid);
+                       clear_item(item);
 
                item = NULL;
 
@@ -357,7 +363,7 @@ static void find_non_local_tags(const struct ref *refs,
        if (item &&
            !has_object_file_with_flags(&item->oid, OBJECT_INFO_QUICK) &&
            !will_fetch(head, item->oid.hash))
-               oidclr(&item->oid);
+               clear_item(item);
 
        /*
         * For all the tags in the remote_refs_list,
@@ -365,19 +371,21 @@ static void find_non_local_tags(const struct ref *refs,
         */
        for_each_string_list_item(remote_ref_item, &remote_refs_list) {
                const char *refname = remote_ref_item->string;
+               struct ref *rm;
 
                item = hashmap_get_from_hash(&remote_refs, strhash(refname), refname);
                if (!item)
                        BUG("unseen remote ref?");
 
                /* Unless we have already decided to ignore this item... */
-               if (!is_null_oid(&item->oid)) {
-                       struct ref *rm = alloc_ref(item->refname);
-                       rm->peer_ref = alloc_ref(item->refname);
-                       oidcpy(&rm->old_oid, &item->oid);
-                       **tail = rm;
-                       *tail = &rm->next;
-               }
+               if (item->ignore)
+                       continue;
+
+               rm = alloc_ref(item->refname);
+               rm->peer_ref = alloc_ref(item->refname);
+               oidcpy(&rm->old_oid, &item->oid);
+               **tail = rm;
+               *tail = &rm->next;
        }
        hashmap_free(&remote_refs, 1);
        string_list_clear(&remote_refs_list, 0);
@@ -1675,7 +1683,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
 
        string_list_clear(&list, 0);
 
-       close_all_packs(the_repository->objects);
+       close_object_store(the_repository->objects);
 
        if (enable_auto_gc) {
                argv_array_pushl(&argv_gc_auto, "gc", "--auto", NULL);
index 8943bcc300d4a2ce6786857908e189c374373906..be8e0bfcbe0a428f72c5762ff8a066baaefd2533 100644 (file)
@@ -653,7 +653,7 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
        gc_before_repack();
 
        if (!repository_format_precious_objects) {
-               close_all_packs(the_repository->objects);
+               close_object_store(the_repository->objects);
                if (run_command_v_opt(repack.argv, RUN_GIT_CMD))
                        die(FAILED_RUN, repack.argv[0]);
 
@@ -681,13 +681,14 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
        report_garbage = report_pack_garbage;
        reprepare_packed_git(the_repository);
        if (pack_garbage.nr > 0) {
-               close_all_packs(the_repository->objects);
+               close_object_store(the_repository->objects);
                clean_pack_garbage();
        }
 
-       if (gc_write_commit_graph)
-               write_commit_graph_reachable(get_object_directory(), 0,
-                                            !quiet && !daemonized);
+       if (gc_write_commit_graph &&
+           write_commit_graph_reachable(get_object_directory(),
+                                        !quiet && !daemonized ? COMMIT_GRAPH_PROGRESS : 0))
+               return 1;
 
        if (auto_gc && too_many_loose_objects())
                warning(_("There are too many unreachable loose objects; "
index e055c1110310bc3b6ccef4fa83dae84316fcc75f..640ef4ded595a3a21579a5630e04c3e6a412420d 100644 (file)
@@ -108,7 +108,7 @@ int cmd_hash_object(int argc, const char **argv, const char *prefix)
        int i;
        const char *errstr = NULL;
 
-       argc = parse_options(argc, argv, NULL, hash_object_options,
+       argc = parse_options(argc, argv, prefix, hash_object_options,
                             hash_object_usage, 0);
 
        if (flags & HASH_WRITE_OBJECT)
index ccf4eb7e9b3361ee7eb209180d98ce8df757d92d..0d55f73b0b443b60dccc31096675d40691b3eb56 100644 (file)
@@ -14,6 +14,7 @@
 #include "thread-utils.h"
 #include "packfile.h"
 #include "object-store.h"
+#include "fetch-object.h"
 
 static const char index_pack_usage[] =
 "git index-pack [-v] [-o <index-file>] [--keep | --keep=<msg>] [--verify] [--strict] (<pack-file> | --stdin [--fix-thin] [<pack-file>])";
@@ -1351,6 +1352,25 @@ static void fix_unresolved_deltas(struct hashfile *f)
                sorted_by_pos[i] = &ref_deltas[i];
        QSORT(sorted_by_pos, nr_ref_deltas, delta_pos_compare);
 
+       if (repository_format_partial_clone) {
+               /*
+                * Prefetch the delta bases.
+                */
+               struct oid_array to_fetch = OID_ARRAY_INIT;
+               for (i = 0; i < nr_ref_deltas; i++) {
+                       struct ref_delta_entry *d = sorted_by_pos[i];
+                       if (!oid_object_info_extended(the_repository, &d->oid,
+                                                     NULL,
+                                                     OBJECT_INFO_FOR_PREFETCH))
+                               continue;
+                       oid_array_append(&to_fetch, &d->oid);
+               }
+               if (to_fetch.nr)
+                       fetch_objects(repository_format_partial_clone,
+                                     to_fetch.oid, to_fetch.nr);
+               oid_array_clear(&to_fetch);
+       }
+
        for (i = 0; i < nr_ref_deltas; i++) {
                struct ref_delta_entry *d = sorted_by_pos[i];
                enum object_type type;
@@ -1650,8 +1670,10 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
        int report_end_of_input = 0;
 
        /*
-        * index-pack never needs to fetch missing objects, since it only
-        * accesses the repo to do hash collision checks
+        * index-pack never needs to fetch missing objects except when
+        * REF_DELTA bases are missing (which are explicitly handled). It only
+        * accesses the repo to do hash collision checks and to check which
+        * REF_DELTA bases need to be fetched.
         */
        fetch_if_missing = 0;
 
index 6ca002893f47516ee8963db09d60b3716b60a205..944ec77fe1032775ff595f0b1eb99a1fb22d059f 100644 (file)
@@ -502,6 +502,9 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
        if (real_git_dir && !is_absolute_path(real_git_dir))
                real_git_dir = real_pathdup(real_git_dir, 1);
 
+       if (template_dir && *template_dir && !is_absolute_path(template_dir))
+               template_dir = absolute_pathdup(template_dir);
+
        if (argc == 1) {
                int mkdir_tried = 0;
        retry:
index 8ae40dec4746571cf80d2f76bbad067c33a972fa..f101d092b883e6554cce193c66626b5707252c3f 100644 (file)
@@ -10,6 +10,7 @@
 #include "parse-options.h"
 #include "string-list.h"
 #include "trailer.h"
+#include "config.h"
 
 static const char * const git_interpret_trailers_usage[] = {
        N_("git interpret-trailers [--in-place] [--trim-empty] [(--trailer <token>[(=|:)<value>])...] [<file>...]"),
@@ -112,6 +113,8 @@ int cmd_interpret_trailers(int argc, const char **argv, const char *prefix)
                OPT_END()
        };
 
+       git_config(git_default_config, NULL);
+
        argc = parse_options(argc, argv, prefix, options,
                             git_interpret_trailers_usage, 0);
 
index e43ee12fb1dd33c669056ae2b02c12ecc0b73d67..7c8767d3bc781e132fdcb54edbfab2c856380e62 100644 (file)
@@ -779,6 +779,8 @@ enum {
 
 static int git_format_config(const char *var, const char *value, void *cb)
 {
+       struct rev_info *rev = cb;
+
        if (!strcmp(var, "format.headers")) {
                if (!value)
                        die(_("format.headers without value"));
@@ -864,6 +866,22 @@ static int git_format_config(const char *var, const char *value, void *cb)
                        from = NULL;
                return 0;
        }
+       if (!strcmp(var, "format.notes")) {
+               struct strbuf buf = STRBUF_INIT;
+               int b = git_parse_maybe_bool(value);
+               if (!b)
+                       return 0;
+               rev->show_notes = 1;
+               if (b < 0) {
+                       strbuf_addstr(&buf, value);
+                       expand_notes_ref(&buf);
+                       string_list_append(&rev->notes_opt.extra_notes_refs,
+                                       strbuf_detach(&buf, NULL));
+               } else {
+                       rev->notes_opt.use_default_notes = 1;
+               }
+               return 0;
+       }
 
        return git_log_config(var, value, cb);
 }
@@ -1435,7 +1453,7 @@ static void prepare_bases(struct base_tree_info *bases,
                struct object_id *patch_id;
                if (*commit_base_at(&commit_base, commit))
                        continue;
-               if (commit_patch_id(commit, &diffopt, &oid, 0))
+               if (commit_patch_id(commit, &diffopt, &oid, 0, 1))
                        die(_("cannot get patch id"));
                ALLOC_GROW(bases->patch_id, bases->nr_patch_id + 1, bases->alloc_patch_id);
                patch_id = bases->patch_id + bases->nr_patch_id;
@@ -1617,8 +1635,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
        extra_to.strdup_strings = 1;
        extra_cc.strdup_strings = 1;
        init_log_defaults();
-       git_config(git_format_config, NULL);
        repo_init_revisions(the_repository, &rev, prefix);
+       git_config(git_format_config, &rev);
        rev.commit_format = CMIT_FMT_EMAIL;
        rev.expand_tabs_in_log_default = 0;
        rev.verbose_header = 1;
index 7f83c9a6f26bd92e5e3832a8ea8d6ec44d5a5f5a..670e8fb93c9320686410cabb8938a106be8e8158 100644 (file)
@@ -373,7 +373,7 @@ static void prune_index(struct index_state *istate,
        first = pos;
        last = istate->cache_nr;
        while (last > first) {
-               int next = (last + first) >> 1;
+               int next = first + ((last - first) >> 1);
                const struct cache_entry *ce = istate->cache[next];
                if (!strncmp(ce->name, prefix, prefixlen)) {
                        first = next+1;
index e96f72af8044769df6b4ef42f0525b572b0c39e2..aad5a9504c8546db0adfd36c59cb1ad1918a56e9 100644 (file)
@@ -37,6 +37,7 @@
 #include "packfile.h"
 #include "tag.h"
 #include "alias.h"
+#include "branch.h"
 #include "commit-reach.h"
 #include "wt-status.h"
 
@@ -58,7 +59,7 @@ static const char * const builtin_merge_usage[] = {
 };
 
 static int show_diffstat = 1, shortlog_len = -1, squash;
-static int option_commit = 1;
+static int option_commit = -1;
 static int option_edit = -1;
 static int allow_trivial = 1, have_message, verify_signatures;
 static int overwrite_ignore = 1;
@@ -73,6 +74,7 @@ static int option_renormalize;
 static int verbosity;
 static int allow_rerere_auto;
 static int abort_current_merge;
+static int quit_current_merge;
 static int continue_current_merge;
 static int allow_unrelated_histories;
 static int show_progress = -1;
@@ -274,6 +276,8 @@ static struct option builtin_merge_options[] = {
        OPT__VERBOSITY(&verbosity),
        OPT_BOOL(0, "abort", &abort_current_merge,
                N_("abort the current in-progress merge")),
+       OPT_BOOL(0, "quit", &quit_current_merge,
+               N_("--abort but leave index and working tree alone")),
        OPT_BOOL(0, "continue", &continue_current_merge,
                N_("continue the current in-progress merge")),
        OPT_BOOL(0, "allow-unrelated-histories", &allow_unrelated_histories,
@@ -287,14 +291,6 @@ static struct option builtin_merge_options[] = {
        OPT_END()
 };
 
-/* Cleans up metadata that is uninteresting after a succeeded merge. */
-static void drop_save(void)
-{
-       unlink(git_path_merge_head(the_repository));
-       unlink(git_path_merge_msg(the_repository));
-       unlink(git_path_merge_mode(the_repository));
-}
-
 static int save_state(struct object_id *stash)
 {
        int len;
@@ -388,7 +384,7 @@ static void finish_up_to_date(const char *msg)
 {
        if (verbosity >= 0)
                printf("%s%s\n", squash ? _(" (nothing to squash)") : "", msg);
-       drop_save();
+       remove_merge_branch_state(the_repository);
 }
 
 static void squash_message(struct commit *commit, struct commit_list *remoteheads)
@@ -457,7 +453,7 @@ static void finish(struct commit *head_commit,
                         * We ignore errors in 'gc --auto', since the
                         * user should see them.
                         */
-                       close_all_packs(the_repository->objects);
+                       close_object_store(the_repository->objects);
                        run_command_v_opt(argv_gc_auto, RUN_GIT_CMD);
                }
        }
@@ -881,7 +877,7 @@ static int merge_trivial(struct commit *head, struct commit_list *remoteheads)
                        &result_commit, NULL, sign_commit))
                die(_("failed to write commit object"));
        finish(head, remoteheads, &result_commit, "In-index merge");
-       drop_save();
+       remove_merge_branch_state(the_repository);
        return 0;
 }
 
@@ -907,7 +903,7 @@ static int finish_automerge(struct commit *head,
        strbuf_addf(&buf, "Merge made by the '%s' strategy.", wt_strategy);
        finish(head, remoteheads, &result_commit, buf.buf);
        strbuf_release(&buf);
-       drop_save();
+       remove_merge_branch_state(the_repository);
        return 0;
 }
 
@@ -1289,6 +1285,16 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                goto done;
        }
 
+       if (quit_current_merge) {
+               if (orig_argc != 2)
+                       usage_msg_opt(_("--quit expects no arguments"),
+                                     builtin_merge_usage,
+                                     builtin_merge_options);
+
+               remove_merge_branch_state(the_repository);
+               goto done;
+       }
+
        if (continue_current_merge) {
                int nargc = 1;
                const char *nargv[] = {"commit", NULL};
@@ -1339,9 +1345,19 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
        if (squash) {
                if (fast_forward == FF_NO)
                        die(_("You cannot combine --squash with --no-ff."));
+               if (option_commit > 0)
+                       die(_("You cannot combine --squash with --commit."));
+               /*
+                * squash can now silently disable option_commit - this is not
+                * a problem as it is only overriding the default, not a user
+                * supplied option.
+                */
                option_commit = 0;
        }
 
+       if (option_commit < 0)
+               option_commit = 1;
+
        if (!argc) {
                if (default_to_upstream)
                        argc = setup_with_upstream(&argv);
@@ -1495,7 +1511,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                }
 
                finish(head_commit, remoteheads, &commit->object.oid, msg.buf);
-               drop_save();
+               remove_merge_branch_state(the_repository);
                goto done;
        } else if (!remoteheads->next && common->next)
                ;
index 94e82b8504f7da5bed50d81771cefe3445b98ece..891991b00d673457ecdc6d82d1aa400a9eff03e9 100644 (file)
@@ -67,7 +67,7 @@ static const char *mktree_usage[] = {
        NULL
 };
 
-static void mktree_line(char *buf, size_t len, int nul_term_line, int allow_missing)
+static void mktree_line(char *buf, int nul_term_line, int allow_missing)
 {
        char *ptr, *ntr;
        const char *p;
@@ -172,7 +172,7 @@ int cmd_mktree(int ac, const char **av, const char *prefix)
                                        break;
                                die("input format error: (blank line only valid in batch mode)");
                        }
-                       mktree_line(sb.buf, sb.len, nul_term_line, allow_missing);
+                       mktree_line(sb.buf, nul_term_line, allow_missing);
                }
                if (is_batch_mode && got_eof && used < 1) {
                        /*
index 05ccf53e003144d07e319abb8930fa31623c6632..16df43473aaf61fe9b6f95fa449068ccf0eacafb 100644 (file)
@@ -40,9 +40,7 @@ static void set_commit_rev_name(struct commit *commit, struct rev_name *name)
 }
 
 static int is_better_name(struct rev_name *name,
-                         const char *tip_name,
                          timestamp_t taggerdate,
-                         int generation,
                          int distance,
                          int from_tag)
 {
@@ -103,8 +101,7 @@ static void name_rev(struct commit *commit,
                name = xmalloc(sizeof(rev_name));
                set_commit_rev_name(commit, name);
                goto copy_data;
-       } else if (is_better_name(name, tip_name, taggerdate,
-                                 generation, distance, from_tag)) {
+       } else if (is_better_name(name, taggerdate, distance, from_tag)) {
 copy_data:
                name->tip_name = tip_name;
                name->taggerdate = taggerdate;
index 41d7fc59830c090d8b0422980528e4ea429c9e6b..b2be8869c236378ca70517d2c7431a6993952a3c 100644 (file)
@@ -2899,7 +2899,7 @@ static int ofscmp(const void *a_, const void *b_)
                return oidcmp(&a->object->oid, &b->object->oid);
 }
 
-static void add_objects_in_unpacked_packs(struct rev_info *revs)
+static void add_objects_in_unpacked_packs(void)
 {
        struct packed_git *p;
        struct in_pack in_pack;
@@ -3011,7 +3011,7 @@ static int loosened_object_can_be_discarded(const struct object_id *oid,
        return 1;
 }
 
-static void loosen_unused_packed_objects(struct rev_info *revs)
+static void loosen_unused_packed_objects(void)
 {
        struct packed_git *p;
        uint32_t i;
@@ -3158,11 +3158,11 @@ static void get_object_list(int ac, const char **av)
        }
 
        if (keep_unreachable)
-               add_objects_in_unpacked_packs(&revs);
+               add_objects_in_unpacked_packs();
        if (pack_loose_unreachable)
                add_unreachable_loose_objects();
        if (unpack_unreachable)
-               loosen_unused_packed_objects(&revs);
+               loosen_unused_packed_objects();
 
        oid_array_clear(&recent_objects);
 }
index 970d0d30b4f4107e667f3a75d172cc2d25b04b8f..bd28b80b2d0f3cd5e9f0b451678279198c52d24d 100644 (file)
@@ -1,5 +1,6 @@
 #include "builtin.h"
 #include "config.h"
+#include "diff.h"
 
 static void flush_current_id(int patchlen, struct object_id *id, struct object_id *result)
 {
@@ -54,22 +55,6 @@ static int scan_hunk_header(const char *p, int *p_before, int *p_after)
        return 1;
 }
 
-static void flush_one_hunk(struct object_id *result, git_SHA_CTX *ctx)
-{
-       unsigned char hash[GIT_MAX_RAWSZ];
-       unsigned short carry = 0;
-       int i;
-
-       git_SHA1_Final(hash, ctx);
-       git_SHA1_Init(ctx);
-       /* 20-byte sum, with carry */
-       for (i = 0; i < GIT_SHA1_RAWSZ; ++i) {
-               carry += result->hash[i] + hash[i];
-               result->hash[i] = carry;
-               carry >>= 8;
-       }
-}
-
 static int get_one_patchid(struct object_id *next_oid, struct object_id *result,
                           struct strbuf *line_buf, int stable)
 {
index 784bd193219e46dff7a9773614036344b23f7039..9202e75544761f274d3e360a3dc609fff294d0bb 100644 (file)
@@ -32,7 +32,7 @@ int cmd_range_diff(int argc, const char **argv, const char *prefix)
        repo_diff_setup(the_repository, &diffopt);
 
        options = parse_options_concat(range_diff_options, diffopt.parseopts);
-       argc = parse_options(argc, argv, NULL, options,
+       argc = parse_options(argc, argv, prefix, options,
                             builtin_range_diff_usage, 0);
 
        diff_setup_done(&diffopt);
index 5c9c0825957532dc5e59668889adaf15708007ac..ca5e655d2f8b4243284eacf640230349e0d11d88 100644 (file)
@@ -111,7 +111,7 @@ static int git_read_tree_config(const char *var, const char *value, void *cb)
        return git_default_config(var, value, cb);
 }
 
-int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)
+int cmd_read_tree(int argc, const char **argv, const char *cmd_prefix)
 {
        int i, stage = 0;
        struct object_id oid;
@@ -165,7 +165,7 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)
 
        git_config(git_read_tree_config, NULL);
 
-       argc = parse_options(argc, argv, unused_prefix, read_tree_options,
+       argc = parse_options(argc, argv, cmd_prefix, read_tree_options,
                             read_tree_usage, 0);
 
        hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
index db6ca9bd7d4c4e9cdf58eb47fbfce2dda5b58497..789bf0e5f92badf1dadd5327990609bebcc4f6e1 100644 (file)
@@ -508,7 +508,7 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)
        if (argc == 1)
                usage_with_options(builtin_rebase_interactive_usage, options);
 
-       argc = parse_options(argc, argv, NULL, options,
+       argc = parse_options(argc, argv, prefix, options,
                        builtin_rebase_interactive_usage, PARSE_OPT_KEEP_ARGV0);
 
        if (!is_null_oid(&squash_onto))
@@ -738,20 +738,30 @@ static int finish_rebase(struct rebase_options *opts)
 {
        struct strbuf dir = STRBUF_INIT;
        const char *argv_gc_auto[] = { "gc", "--auto", NULL };
+       int ret = 0;
 
        delete_ref(NULL, "REBASE_HEAD", NULL, REF_NO_DEREF);
        apply_autostash(opts);
-       close_all_packs(the_repository->objects);
+       close_object_store(the_repository->objects);
        /*
         * We ignore errors in 'gc --auto', since the
         * user should see them.
         */
        run_command_v_opt(argv_gc_auto, RUN_GIT_CMD);
-       strbuf_addstr(&dir, opts->state_dir);
-       remove_dir_recursively(&dir, 0);
-       strbuf_release(&dir);
+       if (opts->type == REBASE_INTERACTIVE) {
+               struct replay_opts replay = REPLAY_OPTS_INIT;
 
-       return 0;
+               replay.action = REPLAY_INTERACTIVE_REBASE;
+               ret = sequencer_remove_state(&replay);
+       } else {
+               strbuf_addstr(&dir, opts->state_dir);
+               if (remove_dir_recursively(&dir, 0))
+                       ret = error(_("could not remove '%s'"),
+                                   opts->state_dir);
+               strbuf_release(&dir);
+       }
+
+       return ret;
 }
 
 static struct commit *peel_committish(const char *name)
@@ -1153,10 +1163,6 @@ static int run_specific_rebase(struct rebase_options *opts, enum action action)
        }
 
        switch (opts->type) {
-       case REBASE_AM:
-               backend = "git-rebase--am";
-               backend_func = "git_rebase__am";
-               break;
        case REBASE_PRESERVE_MERGES:
                backend = "git-rebase--preserve-merges";
                backend_func = "git_rebase__preserve_merges";
@@ -1167,8 +1173,7 @@ static int run_specific_rebase(struct rebase_options *opts, enum action action)
        }
 
        strbuf_addf(&script_snippet,
-                   ". git-sh-setup && . git-rebase--common &&"
-                   " . %s && %s", backend, backend_func);
+                   ". git-sh-setup && . %s && %s", backend, backend_func);
        argv[0] = script_snippet.buf;
 
        status = run_command_v_opt(argv, RUN_USING_SHELL);
@@ -1626,15 +1631,23 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
                        die(_("could not move back to %s"),
                            oid_to_hex(&options.orig_head));
                remove_branch_state(the_repository);
-               ret = finish_rebase(&options);
+               ret = !!finish_rebase(&options);
                goto cleanup;
        }
        case ACTION_QUIT: {
-               strbuf_reset(&buf);
-               strbuf_addstr(&buf, options.state_dir);
-               ret = !!remove_dir_recursively(&buf, 0);
-               if (ret)
-                       die(_("could not remove '%s'"), options.state_dir);
+               if (options.type == REBASE_INTERACTIVE) {
+                       struct replay_opts replay = REPLAY_OPTS_INIT;
+
+                       replay.action = REPLAY_INTERACTIVE_REBASE;
+                       ret = !!sequencer_remove_state(&replay);
+               } else {
+                       strbuf_reset(&buf);
+                       strbuf_addstr(&buf, options.state_dir);
+                       ret = !!remove_dir_recursively(&buf, 0);
+                       if (ret)
+                               error(_("could not remove '%s'"),
+                                      options.state_dir);
+               }
                goto cleanup;
        }
        case ACTION_EDIT_TODO:
@@ -2146,6 +2159,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
        ret = !!run_specific_rebase(&options, action);
 
 cleanup:
+       strbuf_release(&buf);
        strbuf_release(&revisions);
        free(options.head_name);
        free(options.gpg_sign_opt);
index 29f165d8bd3513a6f85667b070e8f157994364bb..610eadf5f092a651fe3d04b07528f40f6adddafe 100644 (file)
@@ -1809,8 +1809,7 @@ static const char *unpack_with_sideband(struct shallow_info *si)
        return ret;
 }
 
-static void prepare_shallow_update(struct command *commands,
-                                  struct shallow_info *si)
+static void prepare_shallow_update(struct shallow_info *si)
 {
        int i, j, k, bitmap_size = DIV_ROUND_UP(si->ref->nr, 32);
 
@@ -1876,7 +1875,7 @@ static void update_shallow_info(struct command *commands,
        si->ref = ref;
 
        if (shallow_update) {
-               prepare_shallow_update(commands, si);
+               prepare_shallow_update(si);
                return;
        }
 
@@ -2043,7 +2042,7 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
                        proc.git_cmd = 1;
                        proc.argv = argv_gc_auto;
 
-                       close_all_packs(the_repository->objects);
+                       close_object_store(the_repository->objects);
                        if (!start_command(&proc)) {
                                if (use_sideband)
                                        copy_to_sideband(proc.err, -1, NULL);
index f7edf7f2cb1f5880e5674f0bf5d41672602e52e4..5591cef775432d2bd01b866183e789952ad36915 100644 (file)
@@ -1407,7 +1407,7 @@ static int update(int argc, const char **argv)
        return retval;
 }
 
-static int remove_all_fetch_refspecs(const char *remote, const char *key)
+static int remove_all_fetch_refspecs(const char *key)
 {
        return git_config_set_multivar_gently(key, NULL, NULL, 1);
 }
@@ -1437,7 +1437,7 @@ static int set_remote_branches(const char *remotename, const char **branches,
        if (!remote_is_configured(remote, 1))
                die(_("No such remote '%s'"), remotename);
 
-       if (!add_mode && remove_all_fetch_refspecs(remotename, key.buf)) {
+       if (!add_mode && remove_all_fetch_refspecs(key.buf)) {
                strbuf_release(&key);
                return 1;
        }
index caca11392713eb92816d1e503f286bfe0d9be78a..f834b5551b1ffe003b943c18cd35397e9196e730 100644 (file)
@@ -422,7 +422,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
        if (!names.nr && !po_args.quiet)
                printf_ln(_("Nothing new to pack."));
 
-       close_all_packs(the_repository->objects);
+       close_object_store(the_repository->objects);
 
        /*
         * Ok we have prepared all new packfiles.
index 9f31837d303f1f302266fdd03298ddce73867ba0..301ccb970bb36340bc6b4a136a3029811f252aa2 100644 (file)
@@ -49,6 +49,7 @@ static const char rev_list_usage[] =
 "    --objects | --objects-edge\n"
 "    --unpacked\n"
 "    --header | --pretty\n"
+"    --[no-]object-names\n"
 "    --abbrev=<n> | --no-abbrev\n"
 "    --abbrev-commit\n"
 "    --left-right\n"
@@ -75,9 +76,12 @@ enum missing_action {
 };
 static enum missing_action arg_missing_action;
 
+/* display only the oid of each object encountered */
+static int arg_show_object_names = 1;
+
 #define DEFAULT_OIDSET_SIZE     (16*1024)
 
-static void finish_commit(struct commit *commit, void *data);
+static void finish_commit(struct commit *commit);
 static void show_commit(struct commit *commit, void *data)
 {
        struct rev_list_info *info = data;
@@ -86,7 +90,7 @@ static void show_commit(struct commit *commit, void *data)
        display_progress(progress, ++progress_counter);
 
        if (info->flags & REV_LIST_QUIET) {
-               finish_commit(commit, data);
+               finish_commit(commit);
                return;
        }
 
@@ -99,7 +103,7 @@ static void show_commit(struct commit *commit, void *data)
                        revs->count_left++;
                else
                        revs->count_right++;
-               finish_commit(commit, data);
+               finish_commit(commit);
                return;
        }
 
@@ -188,10 +192,10 @@ static void show_commit(struct commit *commit, void *data)
                        putchar('\n');
        }
        maybe_flush_or_die(stdout, "stdout");
-       finish_commit(commit, data);
+       finish_commit(commit);
 }
 
-static void finish_commit(struct commit *commit, void *data)
+static void finish_commit(struct commit *commit)
 {
        if (commit->parents) {
                free_commit_list(commit->parents);
@@ -255,7 +259,10 @@ static void show_object(struct object *obj, const char *name, void *cb_data)
        display_progress(progress, ++progress_counter);
        if (info->flags & REV_LIST_QUIET)
                return;
-       show_object_with_name(stdout, obj, name);
+       if (arg_show_object_names)
+               show_object_with_name(stdout, obj, name);
+       else
+               printf("%s\n", oid_to_hex(&obj->oid));
 }
 
 static void show_edge(struct commit *commit)
@@ -484,6 +491,16 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
                if (skip_prefix(arg, "--missing=", &arg))
                        continue; /* already handled above */
 
+               if (!strcmp(arg, ("--no-object-names"))) {
+                       arg_show_object_names = 0;
+                       continue;
+               }
+
+               if (!strcmp(arg, ("--object-names"))) {
+                       arg_show_object_names = 1;
+                       continue;
+               }
+
                usage(rev_list_usage);
 
        }
index 90cbe896c99188130a36a18aa18a57cd080be591..be8edc6d1e1185fa0224b516c0e4f73ceb590aa3 100644 (file)
@@ -61,7 +61,7 @@ static void print_error_files(struct string_list *files_list,
        }
 }
 
-static void submodules_absorb_gitdir_if_needed(const char *prefix)
+static void submodules_absorb_gitdir_if_needed(void)
 {
        int i;
        for (i = 0; i < list.nr; i++) {
@@ -83,7 +83,7 @@ static void submodules_absorb_gitdir_if_needed(const char *prefix)
                        continue;
 
                if (!submodule_uses_gitfile(name))
-                       absorb_git_dir_into_superproject(prefix, name,
+                       absorb_git_dir_into_superproject(name,
                                ABSORB_GITDIR_RECURSE_SUBMODULES);
        }
 }
@@ -313,7 +313,7 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
        }
 
        if (!index_only)
-               submodules_absorb_gitdir_if_needed(prefix);
+               submodules_absorb_gitdir_if_needed();
 
        /*
         * If not forced, the file, the index and the HEAD (if exists)
index 082daeac329d1c9f8f94f91210f854468377ad7a..35d7f51c236dc0b9a734809a1cee9b04a5f12397 100644 (file)
@@ -514,7 +514,6 @@ static int show_merge_base(struct commit_list *seen, int num_rev)
 
 static int show_independent(struct commit **rev,
                            int num_rev,
-                           char **ref_name,
                            unsigned int *rev_mask)
 {
        int i;
@@ -862,7 +861,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
                return show_merge_base(seen, num_rev);
 
        if (independent)
-               return show_independent(rev, num_rev, ref_name, rev_mask);
+               return show_independent(rev, num_rev, rev_mask);
 
        /* Show list; --more=-1 means list-only */
        if (1 < num_rev || extra < 0) {
index 2a8e6d09b406345519201ef64f572197a409e7a4..fde6397caa16326912087215383506026d00ab79 100644 (file)
@@ -713,11 +713,11 @@ static int git_stash_config(const char *var, const char *value, void *cb)
 static int show_stash(int argc, const char **argv, const char *prefix)
 {
        int i;
-       int opts = 0;
        int ret = 0;
        struct stash_info info;
        struct rev_info rev;
        struct argv_array stash_args = ARGV_ARRAY_INIT;
+       struct argv_array revision_args = ARGV_ARRAY_INIT;
        struct option options[] = {
                OPT_END()
        };
@@ -726,11 +726,12 @@ static int show_stash(int argc, const char **argv, const char *prefix)
        git_config(git_diff_ui_config, NULL);
        init_revisions(&rev, prefix);
 
+       argv_array_push(&revision_args, argv[0]);
        for (i = 1; i < argc; i++) {
                if (argv[i][0] != '-')
                        argv_array_push(&stash_args, argv[i]);
                else
-                       opts++;
+                       argv_array_push(&revision_args, argv[i]);
        }
 
        ret = get_stash_info(&info, stash_args.argc, stash_args.argv);
@@ -742,7 +743,7 @@ static int show_stash(int argc, const char **argv, const char *prefix)
         * The config settings are applied only if there are not passed
         * any options.
         */
-       if (!opts) {
+       if (revision_args.argc == 1) {
                git_config(git_stash_config, NULL);
                if (show_stat)
                        rev.diffopt.output_format = DIFF_FORMAT_DIFFSTAT;
@@ -756,7 +757,7 @@ static int show_stash(int argc, const char **argv, const char *prefix)
                }
        }
 
-       argc = setup_revisions(argc, argv, &rev, NULL);
+       argc = setup_revisions(revision_args.argc, revision_args.argv, &rev, NULL);
        if (argc > 1) {
                free_stash_info(&info);
                usage_with_options(git_stash_show_usage, options);
index 0bf4aa088e0ca4bf6378d121cfc23e0f42ac3af4..13da32d3b736f00e2e41aa4729f1f2422c94439e 100644 (file)
@@ -2107,8 +2107,7 @@ static int absorb_git_dirs(int argc, const char **argv, const char *prefix)
                return 1;
 
        for (i = 0; i < list.nr; i++)
-               absorb_git_dir_into_superproject(prefix,
-                               list.entries[i]->name, flags);
+               absorb_git_dir_into_superproject(list.entries[i]->name, flags);
 
        return 0;
 }
index ef37dccf864932a31c019775cdad6d33207941a1..e0a4c25382846f801c43cd1093ffb0ffa63f975f 100644 (file)
@@ -33,6 +33,7 @@ static const char * const git_tag_usage[] = {
 
 static unsigned int colopts;
 static int force_sign_annotate;
+static int config_sign_tag = -1; /* unspecified */
 
 static int list_tags(struct ref_filter *filter, struct ref_sorting *sorting,
                     struct ref_format *format)
@@ -144,6 +145,11 @@ static int git_tag_config(const char *var, const char *value, void *cb)
        int status;
        struct ref_sorting **sorting_tail = (struct ref_sorting **)cb;
 
+       if (!strcmp(var, "tag.gpgsign")) {
+               config_sign_tag = git_config_bool(var, value);
+               return 0;
+       }
+
        if (!strcmp(var, "tag.sort")) {
                if (!value)
                        return config_error_nonbool(var);
@@ -442,15 +448,10 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
        memset(&opt, 0, sizeof(opt));
        memset(&filter, 0, sizeof(filter));
        filter.lines = -1;
+       opt.sign = -1;
 
        argc = parse_options(argc, argv, prefix, options, git_tag_usage, 0);
 
-       if (keyid) {
-               opt.sign = 1;
-               set_signing_key(keyid);
-       }
-       create_tag_object = (opt.sign || annotate || msg.given || msgfile);
-
        if (!cmdmode) {
                if (argc == 0)
                        cmdmode = 'l';
@@ -463,6 +464,15 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
        if (cmdmode == 'l')
                setup_auto_pager("tag", 1);
 
+       if (opt.sign == -1)
+               opt.sign = cmdmode ? 0 : config_sign_tag > 0;
+
+       if (keyid) {
+               opt.sign = 1;
+               set_signing_key(keyid);
+       }
+       create_tag_object = (opt.sign || annotate || msg.given || msgfile);
+
        if ((create_tag_object || force) && (cmdmode != 0))
                usage_with_options(git_tag_usage, options);
 
index 27db0928bf052b0c518bee57ffa7cdd6c03d5d85..3f8cc6ccb47c2f8927e91076a8844d2cfd8a0bb2 100644 (file)
@@ -280,7 +280,7 @@ static int add_one_path(const struct cache_entry *old, const char *path, int len
        memcpy(ce->name, path, len);
        ce->ce_flags = create_ce_flags(0);
        ce->ce_namelen = len;
-       fill_stat_cache_info(ce, st);
+       fill_stat_cache_info(&the_index, ce, st);
        ce->ce_mode = ce_mode_from_stat(old, st->st_mode);
 
        if (index_path(&the_index, &ce->oid, path, st,
index 42dc4da5a1fc047baa9f71288670c3b981cb3b7e..6da8fa2607c6b831c69a76c5bb2ec5caec444d54 100644 (file)
@@ -33,7 +33,7 @@ int cmd_upload_pack(int argc, const char **argv, const char *prefix)
        packet_trace_identity("upload-pack");
        read_replace_refs = 0;
 
-       argc = parse_options(argc, argv, NULL, options, upload_pack_usage, 0);
+       argc = parse_options(argc, argv, prefix, options, upload_pack_usage, 0);
 
        if (argc != 1)
                usage_with_options(upload_pack_usage, options);
index 7772c07ed7a1e13e3b6ecabc4642cefd6d16b333..4b9e823f8f9656b686f22ac4bb531f7d63f325ee 100644 (file)
@@ -21,15 +21,14 @@ static const char * const verify_commit_usage[] = {
                NULL
 };
 
-static int run_gpg_verify(const struct object_id *oid, const char *buf, unsigned long size, unsigned flags)
+static int run_gpg_verify(struct commit *commit, unsigned flags)
 {
        struct signature_check signature_check;
        int ret;
 
        memset(&signature_check, 0, sizeof(signature_check));
 
-       ret = check_commit_signature(lookup_commit(the_repository, oid),
-                                    &signature_check);
+       ret = check_commit_signature(commit, &signature_check);
        print_signature_buffer(&signature_check, flags);
 
        signature_check_clear(&signature_check);
@@ -38,26 +37,20 @@ static int run_gpg_verify(const struct object_id *oid, const char *buf, unsigned
 
 static int verify_commit(const char *name, unsigned flags)
 {
-       enum object_type type;
        struct object_id oid;
-       char *buf;
-       unsigned long size;
-       int ret;
+       struct object *obj;
 
        if (get_oid(name, &oid))
                return error("commit '%s' not found.", name);
 
-       buf = read_object_file(&oid, &type, &size);
-       if (!buf)
+       obj = parse_object(the_repository, &oid);
+       if (!obj)
                return error("%s: unable to read file.", name);
-       if (type != OBJ_COMMIT)
+       if (obj->type != OBJ_COMMIT)
                return error("%s: cannot verify a non-commit object of type %s.",
-                               name, type_name(type));
-
-       ret = run_gpg_verify(&oid, buf, size, flags);
+                               name, type_name(obj->type));
 
-       free(buf);
-       return ret;
+       return run_gpg_verify((struct commit *)obj, flags);
 }
 
 static int git_verify_commit_config(const char *var, const char *value, void *cb)
index d2a7e2f3f18ba411d065a52ac868db612de5beaa..a5bb02b2076a27a78947fda25c1e16dafd637622 100644 (file)
@@ -275,6 +275,7 @@ static int add_worktree(const char *path, const char *refname,
        struct strbuf symref = STRBUF_INIT;
        struct commit *commit = NULL;
        int is_branch = 0;
+       struct strbuf sb_name = STRBUF_INIT;
 
        validate_worktree_add(path, opts);
 
@@ -290,7 +291,13 @@ static int add_worktree(const char *path, const char *refname,
                die(_("invalid reference: %s"), refname);
 
        name = worktree_basename(path, &len);
-       git_path_buf(&sb_repo, "worktrees/%.*s", (int)(path + len - name), name);
+       strbuf_add(&sb, name, path + len - name);
+       sanitize_refname_component(sb.buf, &sb_name);
+       if (!sb_name.len)
+               BUG("How come '%s' becomes empty after sanitization?", sb.buf);
+       strbuf_reset(&sb);
+       name = sb_name.buf;
+       git_path_buf(&sb_repo, "worktrees/%s", name);
        len = sb_repo.len;
        if (safe_create_leading_directories_const(sb_repo.buf))
                die_errno(_("could not create leading directories of '%s'"),
@@ -418,6 +425,7 @@ static int add_worktree(const char *path, const char *refname,
        strbuf_release(&symref);
        strbuf_release(&sb_repo);
        strbuf_release(&sb_git);
+       strbuf_release(&sb_name);
        return ret;
 }
 
index 3d46d22ee5261d2cf1233913b7b544e812c26122..45d61707e7d11e60e5ba2e6da90017fb38b30dc3 100644 (file)
@@ -16,16 +16,16 @@ static const char * const write_tree_usage[] = {
        NULL
 };
 
-int cmd_write_tree(int argc, const char **argv, const char *unused_prefix)
+int cmd_write_tree(int argc, const char **argv, const char *cmd_prefix)
 {
        int flags = 0, ret;
-       const char *prefix = NULL;
+       const char *tree_prefix = NULL;
        struct object_id oid;
        const char *me = "git-write-tree";
        struct option write_tree_options[] = {
                OPT_BIT(0, "missing-ok", &flags, N_("allow missing objects"),
                        WRITE_TREE_MISSING_OK),
-               OPT_STRING(0, "prefix", &prefix, N_("<prefix>/"),
+               OPT_STRING(0, "prefix", &tree_prefix, N_("<prefix>/"),
                           N_("write tree object for a subdirectory <prefix>")),
                { OPTION_BIT, 0, "ignore-cache-tree", &flags, NULL,
                  N_("only useful for debugging"),
@@ -35,10 +35,10 @@ int cmd_write_tree(int argc, const char **argv, const char *unused_prefix)
        };
 
        git_config(git_default_config, NULL);
-       argc = parse_options(argc, argv, unused_prefix, write_tree_options,
+       argc = parse_options(argc, argv, cmd_prefix, write_tree_options,
                             write_tree_usage, 0);
 
-       ret = write_cache_as_tree(&oid, flags, prefix);
+       ret = write_cache_as_tree(&oid, flags, tree_prefix);
        switch (ret) {
        case 0:
                printf("%s\n", oid_to_hex(&oid));
@@ -50,7 +50,7 @@ int cmd_write_tree(int argc, const char **argv, const char *unused_prefix)
                die("%s: error building trees", me);
                break;
        case WRITE_TREE_PREFIX_ERROR:
-               die("%s: prefix %s not found", me, prefix);
+               die("%s: prefix %s not found", me, tree_prefix);
                break;
        }
        return ret;
index b45666c49b1c19a63d816527a761d0177d9633ce..b5d21cd80f1cd009a8113ceb34c9a834ddbdbe3a 100644 (file)
--- a/bundle.c
+++ b/bundle.c
@@ -142,6 +142,9 @@ int verify_bundle(struct repository *r,
        int i, ret = 0, req_nr;
        const char *message = _("Repository lacks these prerequisite commits:");
 
+       if (!r || !r->objects || !r->objects->odb)
+               return error(_("need a repository to verify a bundle"));
+
        repo_init_revisions(r, &revs, NULL);
        for (i = 0; i < p->nr; i++) {
                struct ref_list_entry *e = p->list + i;
diff --git a/cache.h b/cache.h
index b4bb2e2c11adff0061065fa975874c7c4a2a318b..bf20337ef435234acf171f76137e683e66fb22d2 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -826,7 +826,7 @@ int match_stat_data(const struct stat_data *sd, struct stat *st);
 int match_stat_data_racy(const struct index_state *istate,
                         const struct stat_data *sd, struct stat *st);
 
-void fill_stat_cache_info(struct cache_entry *ce, struct stat *st);
+void fill_stat_cache_info(struct index_state *istate, struct cache_entry *ce, struct stat *st);
 
 #define REFRESH_REALLY         0x0001  /* ignore_valid */
 #define REFRESH_UNMERGED       0x0002  /* allow unmerged */
index 7c5e54875fdacdf77235a077c9928f7d3bf0d001..1752341098c328da1e9f6bce230e6b04730ffa11 100644 (file)
@@ -361,10 +361,10 @@ int generation_numbers_enabled(struct repository *r)
        return !!first_generation;
 }
 
-void close_commit_graph(struct repository *r)
+void close_commit_graph(struct raw_object_store *o)
 {
-       free_commit_graph(r->objects->commit_graph);
-       r->objects->commit_graph = NULL;
+       free_commit_graph(o->commit_graph);
+       o->commit_graph = NULL;
 }
 
 static int bsearch_graph(struct commit_graph *g, struct object_id *oid, uint32_t *pos)
@@ -525,14 +525,38 @@ struct tree *get_commit_tree_in_graph(struct repository *r, const struct commit
        return get_commit_tree_in_graph_one(r, r->objects->commit_graph, c);
 }
 
+struct packed_commit_list {
+       struct commit **list;
+       int nr;
+       int alloc;
+};
+
+struct packed_oid_list {
+       struct object_id *list;
+       int nr;
+       int alloc;
+};
+
+struct write_commit_graph_context {
+       struct repository *r;
+       const char *obj_dir;
+       char *graph_name;
+       struct packed_oid_list oids;
+       struct packed_commit_list commits;
+       int num_extra_edges;
+       unsigned long approx_nr_objects;
+       struct progress *progress;
+       int progress_done;
+       uint64_t progress_cnt;
+       unsigned append:1,
+                report_progress:1;
+};
+
 static void write_graph_chunk_fanout(struct hashfile *f,
-                                    struct commit **commits,
-                                    int nr_commits,
-                                    struct progress *progress,
-                                    uint64_t *progress_cnt)
+                                    struct write_commit_graph_context *ctx)
 {
        int i, count = 0;
-       struct commit **list = commits;
+       struct commit **list = ctx->commits.list;
 
        /*
         * Write the first-level table (the list is sorted,
@@ -540,10 +564,10 @@ static void write_graph_chunk_fanout(struct hashfile *f,
         * having to do eight extra binary search iterations).
         */
        for (i = 0; i < 256; i++) {
-               while (count < nr_commits) {
+               while (count < ctx->commits.nr) {
                        if ((*list)->object.oid.hash[0] != i)
                                break;
-                       display_progress(progress, ++*progress_cnt);
+                       display_progress(ctx->progress, ++ctx->progress_cnt);
                        count++;
                        list++;
                }
@@ -553,14 +577,12 @@ static void write_graph_chunk_fanout(struct hashfile *f,
 }
 
 static void write_graph_chunk_oids(struct hashfile *f, int hash_len,
-                                  struct commit **commits, int nr_commits,
-                                  struct progress *progress,
-                                  uint64_t *progress_cnt)
+                                  struct write_commit_graph_context *ctx)
 {
-       struct commit **list = commits;
+       struct commit **list = ctx->commits.list;
        int count;
-       for (count = 0; count < nr_commits; count++, list++) {
-               display_progress(progress, ++*progress_cnt);
+       for (count = 0; count < ctx->commits.nr; count++, list++) {
+               display_progress(ctx->progress, ++ctx->progress_cnt);
                hashwrite(f, (*list)->object.oid.hash, (int)hash_len);
        }
 }
@@ -572,19 +594,17 @@ static const unsigned char *commit_to_sha1(size_t index, void *table)
 }
 
 static void write_graph_chunk_data(struct hashfile *f, int hash_len,
-                                  struct commit **commits, int nr_commits,
-                                  struct progress *progress,
-                                  uint64_t *progress_cnt)
+                                  struct write_commit_graph_context *ctx)
 {
-       struct commit **list = commits;
-       struct commit **last = commits + nr_commits;
+       struct commit **list = ctx->commits.list;
+       struct commit **last = ctx->commits.list + ctx->commits.nr;
        uint32_t num_extra_edges = 0;
 
        while (list < last) {
                struct commit_list *parent;
                int edge_value;
                uint32_t packedDate[2];
-               display_progress(progress, ++*progress_cnt);
+               display_progress(ctx->progress, ++ctx->progress_cnt);
 
                parse_commit_no_graph(*list);
                hashwrite(f, get_commit_tree_oid(*list)->hash, hash_len);
@@ -595,8 +615,8 @@ static void write_graph_chunk_data(struct hashfile *f, int hash_len,
                        edge_value = GRAPH_PARENT_NONE;
                else {
                        edge_value = sha1_pos(parent->item->object.oid.hash,
-                                             commits,
-                                             nr_commits,
+                                             ctx->commits.list,
+                                             ctx->commits.nr,
                                              commit_to_sha1);
 
                        if (edge_value < 0)
@@ -616,8 +636,8 @@ static void write_graph_chunk_data(struct hashfile *f, int hash_len,
                        edge_value = GRAPH_EXTRA_EDGES_NEEDED | num_extra_edges;
                else {
                        edge_value = sha1_pos(parent->item->object.oid.hash,
-                                             commits,
-                                             nr_commits,
+                                             ctx->commits.list,
+                                             ctx->commits.nr,
                                              commit_to_sha1);
                        if (edge_value < 0)
                                BUG("missing parent %s for commit %s",
@@ -649,19 +669,16 @@ static void write_graph_chunk_data(struct hashfile *f, int hash_len,
 }
 
 static void write_graph_chunk_extra_edges(struct hashfile *f,
-                                         struct commit **commits,
-                                         int nr_commits,
-                                         struct progress *progress,
-                                         uint64_t *progress_cnt)
+                                         struct write_commit_graph_context *ctx)
 {
-       struct commit **list = commits;
-       struct commit **last = commits + nr_commits;
+       struct commit **list = ctx->commits.list;
+       struct commit **last = ctx->commits.list + ctx->commits.nr;
        struct commit_list *parent;
 
        while (list < last) {
                int num_parents = 0;
 
-               display_progress(progress, ++*progress_cnt);
+               display_progress(ctx->progress, ++ctx->progress_cnt);
 
                for (parent = (*list)->parents; num_parents < 3 && parent;
                     parent = parent->next)
@@ -675,8 +692,8 @@ static void write_graph_chunk_extra_edges(struct hashfile *f,
                /* Since num_parents > 2, this initializer is safe. */
                for (parent = (*list)->parents->next; parent; parent = parent->next) {
                        int edge_value = sha1_pos(parent->item->object.oid.hash,
-                                                 commits,
-                                                 nr_commits,
+                                                 ctx->commits.list,
+                                                 ctx->commits.nr,
                                                  commit_to_sha1);
 
                        if (edge_value < 0)
@@ -700,125 +717,111 @@ static int commit_compare(const void *_a, const void *_b)
        return oidcmp(a, b);
 }
 
-struct packed_commit_list {
-       struct commit **list;
-       int nr;
-       int alloc;
-};
-
-struct packed_oid_list {
-       struct object_id *list;
-       int nr;
-       int alloc;
-       struct progress *progress;
-       int progress_done;
-};
-
 static int add_packed_commits(const struct object_id *oid,
                              struct packed_git *pack,
                              uint32_t pos,
                              void *data)
 {
-       struct packed_oid_list *list = (struct packed_oid_list*)data;
+       struct write_commit_graph_context *ctx = (struct write_commit_graph_context*)data;
        enum object_type type;
        off_t offset = nth_packed_object_offset(pack, pos);
        struct object_info oi = OBJECT_INFO_INIT;
 
-       if (list->progress)
-               display_progress(list->progress, ++list->progress_done);
+       if (ctx->progress)
+               display_progress(ctx->progress, ++ctx->progress_done);
 
        oi.typep = &type;
-       if (packed_object_info(the_repository, pack, offset, &oi) < 0)
+       if (packed_object_info(ctx->r, pack, offset, &oi) < 0)
                die(_("unable to get type of object %s"), oid_to_hex(oid));
 
        if (type != OBJ_COMMIT)
                return 0;
 
-       ALLOC_GROW(list->list, list->nr + 1, list->alloc);
-       oidcpy(&(list->list[list->nr]), oid);
-       list->nr++;
+       ALLOC_GROW(ctx->oids.list, ctx->oids.nr + 1, ctx->oids.alloc);
+       oidcpy(&(ctx->oids.list[ctx->oids.nr]), oid);
+       ctx->oids.nr++;
 
        return 0;
 }
 
-static void add_missing_parents(struct packed_oid_list *oids, struct commit *commit)
+static void add_missing_parents(struct write_commit_graph_context *ctx, struct commit *commit)
 {
        struct commit_list *parent;
        for (parent = commit->parents; parent; parent = parent->next) {
                if (!(parent->item->object.flags & UNINTERESTING)) {
-                       ALLOC_GROW(oids->list, oids->nr + 1, oids->alloc);
-                       oidcpy(&oids->list[oids->nr], &(parent->item->object.oid));
-                       oids->nr++;
+                       ALLOC_GROW(ctx->oids.list, ctx->oids.nr + 1, ctx->oids.alloc);
+                       oidcpy(&ctx->oids.list[ctx->oids.nr], &(parent->item->object.oid));
+                       ctx->oids.nr++;
                        parent->item->object.flags |= UNINTERESTING;
                }
        }
 }
 
-static void close_reachable(struct packed_oid_list *oids, int report_progress)
+static void close_reachable(struct write_commit_graph_context *ctx)
 {
        int i;
        struct commit *commit;
-       struct progress *progress = NULL;
 
-       if (report_progress)
-               progress = start_delayed_progress(
-                       _("Loading known commits in commit graph"), oids->nr);
-       for (i = 0; i < oids->nr; i++) {
-               display_progress(progress, i + 1);
-               commit = lookup_commit(the_repository, &oids->list[i]);
+       if (ctx->report_progress)
+               ctx->progress = start_delayed_progress(
+                                       _("Loading known commits in commit graph"),
+                                       ctx->oids.nr);
+       for (i = 0; i < ctx->oids.nr; i++) {
+               display_progress(ctx->progress, i + 1);
+               commit = lookup_commit(ctx->r, &ctx->oids.list[i]);
                if (commit)
                        commit->object.flags |= UNINTERESTING;
        }
-       stop_progress(&progress);
+       stop_progress(&ctx->progress);
 
        /*
-        * As this loop runs, oids->nr may grow, but not more
+        * As this loop runs, ctx->oids.nr may grow, but not more
         * than the number of missing commits in the reachable
         * closure.
         */
-       if (report_progress)
-               progress = start_delayed_progress(
-                       _("Expanding reachable commits in commit graph"), oids->nr);
-       for (i = 0; i < oids->nr; i++) {
-               display_progress(progress, i + 1);
-               commit = lookup_commit(the_repository, &oids->list[i]);
+       if (ctx->report_progress)
+               ctx->progress = start_delayed_progress(
+                                       _("Expanding reachable commits in commit graph"),
+                                       ctx->oids.nr);
+       for (i = 0; i < ctx->oids.nr; i++) {
+               display_progress(ctx->progress, i + 1);
+               commit = lookup_commit(ctx->r, &ctx->oids.list[i]);
 
                if (commit && !parse_commit_no_graph(commit))
-                       add_missing_parents(oids, commit);
+                       add_missing_parents(ctx, commit);
        }
-       stop_progress(&progress);
+       stop_progress(&ctx->progress);
 
-       if (report_progress)
-               progress = start_delayed_progress(
-                       _("Clearing commit marks in commit graph"), oids->nr);
-       for (i = 0; i < oids->nr; i++) {
-               display_progress(progress, i + 1);
-               commit = lookup_commit(the_repository, &oids->list[i]);
+       if (ctx->report_progress)
+               ctx->progress = start_delayed_progress(
+                                       _("Clearing commit marks in commit graph"),
+                                       ctx->oids.nr);
+       for (i = 0; i < ctx->oids.nr; i++) {
+               display_progress(ctx->progress, i + 1);
+               commit = lookup_commit(ctx->r, &ctx->oids.list[i]);
 
                if (commit)
                        commit->object.flags &= ~UNINTERESTING;
        }
-       stop_progress(&progress);
+       stop_progress(&ctx->progress);
 }
 
-static void compute_generation_numbers(struct packed_commit_list* commits,
-                                      int report_progress)
+static void compute_generation_numbers(struct write_commit_graph_context *ctx)
 {
        int i;
        struct commit_list *list = NULL;
-       struct progress *progress = NULL;
 
-       if (report_progress)
-               progress = start_progress(
-                       _("Computing commit graph generation numbers"),
-                       commits->nr);
-       for (i = 0; i < commits->nr; i++) {
-               display_progress(progress, i + 1);
-               if (commits->list[i]->generation != GENERATION_NUMBER_INFINITY &&
-                   commits->list[i]->generation != GENERATION_NUMBER_ZERO)
+       if (ctx->report_progress)
+               ctx->progress = start_progress(
+                                       _("Computing commit graph generation numbers"),
+                                       ctx->commits.nr);
+       for (i = 0; i < ctx->commits.nr; i++) {
+               display_progress(ctx->progress, i + 1);
+               if (ctx->commits.list[i]->generation != GENERATION_NUMBER_INFINITY &&
+                   ctx->commits.list[i]->generation != GENERATION_NUMBER_ZERO)
                        continue;
 
-               commit_list_insert(commits->list[i], &list);
+               commit_list_insert(ctx->commits.list[i], &list);
                while (list) {
                        struct commit *current = list->item;
                        struct commit_list *parent;
@@ -845,7 +848,7 @@ static void compute_generation_numbers(struct packed_commit_list* commits,
                        }
                }
        }
-       stop_progress(&progress);
+       stop_progress(&ctx->progress);
 }
 
 static int add_ref_to_list(const char *refname,
@@ -858,207 +861,187 @@ static int add_ref_to_list(const char *refname,
        return 0;
 }
 
-void write_commit_graph_reachable(const char *obj_dir, int append,
-                                 int report_progress)
+int write_commit_graph_reachable(const char *obj_dir, unsigned int flags)
 {
        struct string_list list = STRING_LIST_INIT_DUP;
+       int result;
 
        for_each_ref(add_ref_to_list, &list);
-       write_commit_graph(obj_dir, NULL, &list, append, report_progress);
+       result = write_commit_graph(obj_dir, NULL, &list,
+                                   flags);
 
        string_list_clear(&list, 0);
+       return result;
 }
 
-void write_commit_graph(const char *obj_dir,
-                       struct string_list *pack_indexes,
-                       struct string_list *commit_hex,
-                       int append, int report_progress)
+static int fill_oids_from_packs(struct write_commit_graph_context *ctx,
+                               struct string_list *pack_indexes)
 {
-       struct packed_oid_list oids;
-       struct packed_commit_list commits;
-       struct hashfile *f;
-       uint32_t i, count_distinct = 0;
-       char *graph_name;
-       struct lock_file lk = LOCK_INIT;
-       uint32_t chunk_ids[5];
-       uint64_t chunk_offsets[5];
-       int num_chunks;
-       int num_extra_edges;
-       struct commit_list *parent;
-       struct progress *progress = NULL;
-       const unsigned hashsz = the_hash_algo->rawsz;
-       uint64_t progress_cnt = 0;
+       uint32_t i;
        struct strbuf progress_title = STRBUF_INIT;
-       unsigned long approx_nr_objects;
-
-       if (!commit_graph_compatible(the_repository))
-               return;
-
-       oids.nr = 0;
-       approx_nr_objects = approximate_object_count();
-       oids.alloc = approx_nr_objects / 32;
-       oids.progress = NULL;
-       oids.progress_done = 0;
+       struct strbuf packname = STRBUF_INIT;
+       int dirlen;
 
-       if (append) {
-               prepare_commit_graph_one(the_repository, obj_dir);
-               if (the_repository->objects->commit_graph)
-                       oids.alloc += the_repository->objects->commit_graph->num_commits;
+       strbuf_addf(&packname, "%s/pack/", ctx->obj_dir);
+       dirlen = packname.len;
+       if (ctx->report_progress) {
+               strbuf_addf(&progress_title,
+                           Q_("Finding commits for commit graph in %d pack",
+                              "Finding commits for commit graph in %d packs",
+                              pack_indexes->nr),
+                           pack_indexes->nr);
+               ctx->progress = start_delayed_progress(progress_title.buf, 0);
+               ctx->progress_done = 0;
        }
-
-       if (oids.alloc < 1024)
-               oids.alloc = 1024;
-       ALLOC_ARRAY(oids.list, oids.alloc);
-
-       if (append && the_repository->objects->commit_graph) {
-               struct commit_graph *commit_graph =
-                       the_repository->objects->commit_graph;
-               for (i = 0; i < commit_graph->num_commits; i++) {
-                       const unsigned char *hash = commit_graph->chunk_oid_lookup +
-                               commit_graph->hash_len * i;
-                       hashcpy(oids.list[oids.nr++].hash, hash);
+       for (i = 0; i < pack_indexes->nr; i++) {
+               struct packed_git *p;
+               strbuf_setlen(&packname, dirlen);
+               strbuf_addstr(&packname, pack_indexes->items[i].string);
+               p = add_packed_git(packname.buf, packname.len, 1);
+               if (!p) {
+                       error(_("error adding pack %s"), packname.buf);
+                       return -1;
+               }
+               if (open_pack_index(p)) {
+                       error(_("error opening index for %s"), packname.buf);
+                       return -1;
                }
+               for_each_object_in_pack(p, add_packed_commits, ctx,
+                                       FOR_EACH_OBJECT_PACK_ORDER);
+               close_pack(p);
+               free(p);
        }
 
-       if (pack_indexes) {
-               struct strbuf packname = STRBUF_INIT;
-               int dirlen;
-               strbuf_addf(&packname, "%s/pack/", obj_dir);
-               dirlen = packname.len;
-               if (report_progress) {
-                       strbuf_addf(&progress_title,
-                                   Q_("Finding commits for commit graph in %d pack",
-                                      "Finding commits for commit graph in %d packs",
-                                      pack_indexes->nr),
-                                   pack_indexes->nr);
-                       oids.progress = start_delayed_progress(progress_title.buf, 0);
-                       oids.progress_done = 0;
-               }
-               for (i = 0; i < pack_indexes->nr; i++) {
-                       struct packed_git *p;
-                       strbuf_setlen(&packname, dirlen);
-                       strbuf_addstr(&packname, pack_indexes->items[i].string);
-                       p = add_packed_git(packname.buf, packname.len, 1);
-                       if (!p)
-                               die(_("error adding pack %s"), packname.buf);
-                       if (open_pack_index(p))
-                               die(_("error opening index for %s"), packname.buf);
-                       for_each_object_in_pack(p, add_packed_commits, &oids,
-                                               FOR_EACH_OBJECT_PACK_ORDER);
-                       close_pack(p);
-                       free(p);
-               }
-               stop_progress(&oids.progress);
-               strbuf_reset(&progress_title);
-               strbuf_release(&packname);
+       stop_progress(&ctx->progress);
+       strbuf_reset(&progress_title);
+       strbuf_release(&packname);
+
+       return 0;
+}
+
+static void fill_oids_from_commit_hex(struct write_commit_graph_context *ctx,
+                                     struct string_list *commit_hex)
+{
+       uint32_t i;
+       struct strbuf progress_title = STRBUF_INIT;
+
+       if (ctx->report_progress) {
+               strbuf_addf(&progress_title,
+                           Q_("Finding commits for commit graph from %d ref",
+                              "Finding commits for commit graph from %d refs",
+                              commit_hex->nr),
+                           commit_hex->nr);
+               ctx->progress = start_delayed_progress(
+                                       progress_title.buf,
+                                       commit_hex->nr);
        }
+       for (i = 0; i < commit_hex->nr; i++) {
+               const char *end;
+               struct object_id oid;
+               struct commit *result;
+
+               display_progress(ctx->progress, i + 1);
+               if (commit_hex->items[i].string &&
+                   parse_oid_hex(commit_hex->items[i].string, &oid, &end))
+                       continue;
 
-       if (commit_hex) {
-               if (report_progress) {
-                       strbuf_addf(&progress_title,
-                                   Q_("Finding commits for commit graph from %d ref",
-                                      "Finding commits for commit graph from %d refs",
-                                      commit_hex->nr),
-                                   commit_hex->nr);
-                       progress = start_delayed_progress(progress_title.buf,
-                                                         commit_hex->nr);
-               }
-               for (i = 0; i < commit_hex->nr; i++) {
-                       const char *end;
-                       struct object_id oid;
-                       struct commit *result;
-
-                       display_progress(progress, i + 1);
-                       if (commit_hex->items[i].string &&
-                           parse_oid_hex(commit_hex->items[i].string, &oid, &end))
-                               continue;
-
-                       result = lookup_commit_reference_gently(the_repository, &oid, 1);
-
-                       if (result) {
-                               ALLOC_GROW(oids.list, oids.nr + 1, oids.alloc);
-                               oidcpy(&oids.list[oids.nr], &(result->object.oid));
-                               oids.nr++;
-                       }
+               result = lookup_commit_reference_gently(ctx->r, &oid, 1);
+
+               if (result) {
+                       ALLOC_GROW(ctx->oids.list, ctx->oids.nr + 1, ctx->oids.alloc);
+                       oidcpy(&ctx->oids.list[ctx->oids.nr], &(result->object.oid));
+                       ctx->oids.nr++;
                }
-               stop_progress(&progress);
-               strbuf_reset(&progress_title);
        }
+       stop_progress(&ctx->progress);
+       strbuf_release(&progress_title);
+}
 
-       if (!pack_indexes && !commit_hex) {
-               if (report_progress)
-                       oids.progress = start_delayed_progress(
-                               _("Finding commits for commit graph among packed objects"),
-                               approx_nr_objects);
-               for_each_packed_object(add_packed_commits, &oids,
-                                      FOR_EACH_OBJECT_PACK_ORDER);
-               if (oids.progress_done < approx_nr_objects)
-                       display_progress(oids.progress, approx_nr_objects);
-               stop_progress(&oids.progress);
-       }
+static void fill_oids_from_all_packs(struct write_commit_graph_context *ctx)
+{
+       if (ctx->report_progress)
+               ctx->progress = start_delayed_progress(
+                       _("Finding commits for commit graph among packed objects"),
+                       ctx->approx_nr_objects);
+       for_each_packed_object(add_packed_commits, ctx,
+                              FOR_EACH_OBJECT_PACK_ORDER);
+       if (ctx->progress_done < ctx->approx_nr_objects)
+               display_progress(ctx->progress, ctx->approx_nr_objects);
+       stop_progress(&ctx->progress);
+}
 
-       close_reachable(&oids, report_progress);
+static uint32_t count_distinct_commits(struct write_commit_graph_context *ctx)
+{
+       uint32_t i, count_distinct = 1;
 
-       if (report_progress)
-               progress = start_delayed_progress(
+       if (ctx->report_progress)
+               ctx->progress = start_delayed_progress(
                        _("Counting distinct commits in commit graph"),
-                       oids.nr);
-       display_progress(progress, 0); /* TODO: Measure QSORT() progress */
-       QSORT(oids.list, oids.nr, commit_compare);
-       count_distinct = 1;
-       for (i = 1; i < oids.nr; i++) {
-               display_progress(progress, i + 1);
-               if (!oideq(&oids.list[i - 1], &oids.list[i]))
+                       ctx->oids.nr);
+       display_progress(ctx->progress, 0); /* TODO: Measure QSORT() progress */
+       QSORT(ctx->oids.list, ctx->oids.nr, commit_compare);
+
+       for (i = 1; i < ctx->oids.nr; i++) {
+               display_progress(ctx->progress, i + 1);
+               if (!oideq(&ctx->oids.list[i - 1], &ctx->oids.list[i]))
                        count_distinct++;
        }
-       stop_progress(&progress);
+       stop_progress(&ctx->progress);
 
-       if (count_distinct >= GRAPH_EDGE_LAST_MASK)
-               die(_("the commit graph format cannot write %d commits"), count_distinct);
+       return count_distinct;
+}
 
-       commits.nr = 0;
-       commits.alloc = count_distinct;
-       ALLOC_ARRAY(commits.list, commits.alloc);
+static void copy_oids_to_commits(struct write_commit_graph_context *ctx)
+{
+       uint32_t i;
+       struct commit_list *parent;
 
-       num_extra_edges = 0;
-       if (report_progress)
-               progress = start_delayed_progress(
+       ctx->num_extra_edges = 0;
+       if (ctx->report_progress)
+               ctx->progress = start_delayed_progress(
                        _("Finding extra edges in commit graph"),
-                       oids.nr);
-       for (i = 0; i < oids.nr; i++) {
+                       ctx->oids.nr);
+       for (i = 0; i < ctx->oids.nr; i++) {
                int num_parents = 0;
-               display_progress(progress, i + 1);
-               if (i > 0 && oideq(&oids.list[i - 1], &oids.list[i]))
+               display_progress(ctx->progress, i + 1);
+               if (i > 0 && oideq(&ctx->oids.list[i - 1], &ctx->oids.list[i]))
                        continue;
 
-               commits.list[commits.nr] = lookup_commit(the_repository, &oids.list[i]);
-               parse_commit_no_graph(commits.list[commits.nr]);
+               ctx->commits.list[ctx->commits.nr] = lookup_commit(ctx->r, &ctx->oids.list[i]);
+               parse_commit_no_graph(ctx->commits.list[ctx->commits.nr]);
 
-               for (parent = commits.list[commits.nr]->parents;
+               for (parent = ctx->commits.list[ctx->commits.nr]->parents;
                     parent; parent = parent->next)
                        num_parents++;
 
                if (num_parents > 2)
-                       num_extra_edges += num_parents - 1;
+                       ctx->num_extra_edges += num_parents - 1;
 
-               commits.nr++;
+               ctx->commits.nr++;
        }
-       num_chunks = num_extra_edges ? 4 : 3;
-       stop_progress(&progress);
-
-       if (commits.nr >= GRAPH_EDGE_LAST_MASK)
-               die(_("too many commits to write graph"));
-
-       compute_generation_numbers(&commits, report_progress);
+       stop_progress(&ctx->progress);
+}
 
-       graph_name = get_commit_graph_filename(obj_dir);
-       if (safe_create_leading_directories(graph_name)) {
-               UNLEAK(graph_name);
-               die_errno(_("unable to create leading directories of %s"),
-                         graph_name);
+static int write_commit_graph_file(struct write_commit_graph_context *ctx)
+{
+       uint32_t i;
+       struct hashfile *f;
+       struct lock_file lk = LOCK_INIT;
+       uint32_t chunk_ids[5];
+       uint64_t chunk_offsets[5];
+       const unsigned hashsz = the_hash_algo->rawsz;
+       struct strbuf progress_title = STRBUF_INIT;
+       int num_chunks = ctx->num_extra_edges ? 4 : 3;
+
+       ctx->graph_name = get_commit_graph_filename(ctx->obj_dir);
+       if (safe_create_leading_directories(ctx->graph_name)) {
+               UNLEAK(ctx->graph_name);
+               error(_("unable to create leading directories of %s"),
+                       ctx->graph_name);
+               return -1;
        }
 
-       hold_lock_file_for_update(&lk, graph_name, LOCK_DIE_ON_ERROR);
+       hold_lock_file_for_update(&lk, ctx->graph_name, LOCK_DIE_ON_ERROR);
        f = hashfd(lk.tempfile->fd, lk.tempfile->filename.buf);
 
        hashwrite_be32(f, GRAPH_SIGNATURE);
@@ -1071,7 +1054,7 @@ void write_commit_graph(const char *obj_dir,
        chunk_ids[0] = GRAPH_CHUNKID_OIDFANOUT;
        chunk_ids[1] = GRAPH_CHUNKID_OIDLOOKUP;
        chunk_ids[2] = GRAPH_CHUNKID_DATA;
-       if (num_extra_edges)
+       if (ctx->num_extra_edges)
                chunk_ids[3] = GRAPH_CHUNKID_EXTRAEDGES;
        else
                chunk_ids[3] = 0;
@@ -1079,9 +1062,9 @@ void write_commit_graph(const char *obj_dir,
 
        chunk_offsets[0] = 8 + (num_chunks + 1) * GRAPH_CHUNKLOOKUP_WIDTH;
        chunk_offsets[1] = chunk_offsets[0] + GRAPH_FANOUT_SIZE;
-       chunk_offsets[2] = chunk_offsets[1] + hashsz * commits.nr;
-       chunk_offsets[3] = chunk_offsets[2] + (hashsz + 16) * commits.nr;
-       chunk_offsets[4] = chunk_offsets[3] + 4 * num_extra_edges;
+       chunk_offsets[2] = chunk_offsets[1] + hashsz * ctx->commits.nr;
+       chunk_offsets[3] = chunk_offsets[2] + (hashsz + 16) * ctx->commits.nr;
+       chunk_offsets[4] = chunk_offsets[3] + 4 * ctx->num_extra_edges;
 
        for (i = 0; i <= num_chunks; i++) {
                uint32_t chunk_write[3];
@@ -1092,31 +1075,113 @@ void write_commit_graph(const char *obj_dir,
                hashwrite(f, chunk_write, 12);
        }
 
-       if (report_progress) {
+       if (ctx->report_progress) {
                strbuf_addf(&progress_title,
                            Q_("Writing out commit graph in %d pass",
                               "Writing out commit graph in %d passes",
                               num_chunks),
                            num_chunks);
-               progress = start_delayed_progress(
+               ctx->progress = start_delayed_progress(
                        progress_title.buf,
-                       num_chunks * commits.nr);
+                       num_chunks * ctx->commits.nr);
        }
-       write_graph_chunk_fanout(f, commits.list, commits.nr, progress, &progress_cnt);
-       write_graph_chunk_oids(f, hashsz, commits.list, commits.nr, progress, &progress_cnt);
-       write_graph_chunk_data(f, hashsz, commits.list, commits.nr, progress, &progress_cnt);
-       if (num_extra_edges)
-               write_graph_chunk_extra_edges(f, commits.list, commits.nr, progress, &progress_cnt);
-       stop_progress(&progress);
+       write_graph_chunk_fanout(f, ctx);
+       write_graph_chunk_oids(f, hashsz, ctx);
+       write_graph_chunk_data(f, hashsz, ctx);
+       if (ctx->num_extra_edges)
+               write_graph_chunk_extra_edges(f, ctx);
+       stop_progress(&ctx->progress);
        strbuf_release(&progress_title);
 
-       close_commit_graph(the_repository);
+       close_commit_graph(ctx->r->objects);
        finalize_hashfile(f, NULL, CSUM_HASH_IN_STREAM | CSUM_FSYNC);
        commit_lock_file(&lk);
 
-       free(graph_name);
-       free(commits.list);
-       free(oids.list);
+       return 0;
+}
+
+int write_commit_graph(const char *obj_dir,
+                      struct string_list *pack_indexes,
+                      struct string_list *commit_hex,
+                      unsigned int flags)
+{
+       struct write_commit_graph_context *ctx;
+       uint32_t i, count_distinct = 0;
+       int res = 0;
+
+       if (!commit_graph_compatible(the_repository))
+               return 0;
+
+       ctx = xcalloc(1, sizeof(struct write_commit_graph_context));
+       ctx->r = the_repository;
+       ctx->obj_dir = obj_dir;
+       ctx->append = flags & COMMIT_GRAPH_APPEND ? 1 : 0;
+       ctx->report_progress = flags & COMMIT_GRAPH_PROGRESS ? 1 : 0;
+
+       ctx->approx_nr_objects = approximate_object_count();
+       ctx->oids.alloc = ctx->approx_nr_objects / 32;
+
+       if (ctx->append) {
+               prepare_commit_graph_one(ctx->r, ctx->obj_dir);
+               if (ctx->r->objects->commit_graph)
+                       ctx->oids.alloc += ctx->r->objects->commit_graph->num_commits;
+       }
+
+       if (ctx->oids.alloc < 1024)
+               ctx->oids.alloc = 1024;
+       ALLOC_ARRAY(ctx->oids.list, ctx->oids.alloc);
+
+       if (ctx->append && ctx->r->objects->commit_graph) {
+               struct commit_graph *g = ctx->r->objects->commit_graph;
+               for (i = 0; i < g->num_commits; i++) {
+                       const unsigned char *hash = g->chunk_oid_lookup + g->hash_len * i;
+                       hashcpy(ctx->oids.list[ctx->oids.nr++].hash, hash);
+               }
+       }
+
+       if (pack_indexes) {
+               if ((res = fill_oids_from_packs(ctx, pack_indexes)))
+                       goto cleanup;
+       }
+
+       if (commit_hex)
+               fill_oids_from_commit_hex(ctx, commit_hex);
+
+       if (!pack_indexes && !commit_hex)
+               fill_oids_from_all_packs(ctx);
+
+       close_reachable(ctx);
+
+       count_distinct = count_distinct_commits(ctx);
+
+       if (count_distinct >= GRAPH_EDGE_LAST_MASK) {
+               error(_("the commit graph format cannot write %d commits"), count_distinct);
+               res = -1;
+               goto cleanup;
+       }
+
+       ctx->commits.alloc = count_distinct;
+       ALLOC_ARRAY(ctx->commits.list, ctx->commits.alloc);
+
+       copy_oids_to_commits(ctx);
+
+       if (ctx->commits.nr >= GRAPH_EDGE_LAST_MASK) {
+               error(_("too many commits to write graph"));
+               res = -1;
+               goto cleanup;
+       }
+
+       compute_generation_numbers(ctx);
+
+       res = write_commit_graph_file(ctx);
+
+cleanup:
+       free(ctx->graph_name);
+       free(ctx->commits.list);
+       free(ctx->oids.list);
+       free(ctx);
+
+       return res;
 }
 
 #define VERIFY_COMMIT_GRAPH_ERROR_HASH 2
index 7dfb8c896fc35f633c73221ec639ca9c425338ab..390c7f696104fbe772151b91cb04889d0f682401 100644 (file)
@@ -65,16 +65,24 @@ struct commit_graph *parse_commit_graph(void *graph_map, int fd,
  */
 int generation_numbers_enabled(struct repository *r);
 
-void write_commit_graph_reachable(const char *obj_dir, int append,
-                                 int report_progress);
-void write_commit_graph(const char *obj_dir,
-                       struct string_list *pack_indexes,
-                       struct string_list *commit_hex,
-                       int append, int report_progress);
+#define COMMIT_GRAPH_APPEND     (1 << 0)
+#define COMMIT_GRAPH_PROGRESS   (1 << 1)
+
+/*
+ * The write_commit_graph* methods return zero on success
+ * and a negative value on failure. Note that if the repository
+ * is not compatible with the commit-graph feature, then the
+ * methods will return 0 without writing a commit-graph.
+ */
+int write_commit_graph_reachable(const char *obj_dir, unsigned int flags);
+int write_commit_graph(const char *obj_dir,
+                      struct string_list *pack_indexes,
+                      struct string_list *commit_hex,
+                      unsigned int flags);
 
 int verify_commit_graph(struct repository *r, struct commit_graph *g);
 
-void close_commit_graph(struct repository *);
+void close_commit_graph(struct raw_object_store *);
 void free_commit_graph(struct commit_graph *);
 
 #endif
index 8fa1883c61c580578a755cdf2da009203d8d386e..26ce0770f688eb94d5be5975d756752dbc0337a8 100644 (file)
--- a/commit.c
+++ b/commit.c
@@ -449,7 +449,7 @@ int parse_commit_buffer(struct repository *r, struct commit *item, const void *b
        item->date = parse_commit_date(bufptr, tail);
 
        if (check_graph)
-               load_commit_graph_info(the_repository, item);
+               load_commit_graph_info(r, item);
 
        return 0;
 }
index 4459408c7d0ba762b1dd4184a4c970778ba097a5..8b07edb0feca434cce2ff7766c30d7529febf996 100644 (file)
@@ -149,7 +149,7 @@ win32_compute_revents (HANDLE h, int *p_sought)
     case FILE_TYPE_PIPE:
       if (!once_only)
        {
-         NtQueryInformationFile = (PNtQueryInformationFile)
+         NtQueryInformationFile = (PNtQueryInformationFile)(void (*)(void))
            GetProcAddress (GetModuleHandle ("ntdll.dll"),
                            "NtQueryInformationFile");
          once_only = TRUE;
index f4f08237f9ed513e0dd3b3bfd9494f19944e3239..a29d34ef44864f02bd89c8490d38bca0efe2a8e6 100644 (file)
@@ -7,6 +7,7 @@
 #include <wingdi.h>
 #include <winreg.h>
 #include "win32.h"
+#include "win32/lazyload.h"
 
 static int fd_is_interactive[3] = { 0, 0, 0 };
 #define FD_CONSOLE 0x1
@@ -41,26 +42,21 @@ typedef struct _CONSOLE_FONT_INFOEX {
 #endif
 #endif
 
-typedef BOOL (WINAPI *PGETCURRENTCONSOLEFONTEX)(HANDLE, BOOL,
-               PCONSOLE_FONT_INFOEX);
-
 static void warn_if_raster_font(void)
 {
        DWORD fontFamily = 0;
-       PGETCURRENTCONSOLEFONTEX pGetCurrentConsoleFontEx;
+       DECLARE_PROC_ADDR(kernel32.dll, BOOL, GetCurrentConsoleFontEx,
+                       HANDLE, BOOL, PCONSOLE_FONT_INFOEX);
 
        /* don't bother if output was ascii only */
        if (!non_ascii_used)
                return;
 
        /* GetCurrentConsoleFontEx is available since Vista */
-       pGetCurrentConsoleFontEx = (PGETCURRENTCONSOLEFONTEX) GetProcAddress(
-                       GetModuleHandle("kernel32.dll"),
-                       "GetCurrentConsoleFontEx");
-       if (pGetCurrentConsoleFontEx) {
+       if (INIT_PROC_ADDR(GetCurrentConsoleFontEx)) {
                CONSOLE_FONT_INFOEX cfi;
                cfi.cbSize = sizeof(cfi);
-               if (pGetCurrentConsoleFontEx(console, 0, &cfi))
+               if (GetCurrentConsoleFontEx(console, 0, &cfi))
                        fontFamily = cfi.FontFamily;
        } else {
                /* pre-Vista: check default console font in registry */
index 296a6d9cc4110bd7fcef542ac9cc9cfe04d4f4d4..faa57e436cf4fc6d84580f3940bafdb4896c7658 100644 (file)
--- a/config.c
+++ b/config.c
@@ -19,6 +19,7 @@
 #include "utf8.h"
 #include "dir.h"
 #include "color.h"
+#include "refs.h"
 
 struct config_source {
        struct config_source *prev;
@@ -170,6 +171,12 @@ static int handle_path_include(const char *path, struct config_include_data *inc
        return ret;
 }
 
+static void add_trailing_starstar_for_dir(struct strbuf *pat)
+{
+       if (pat->len && is_dir_sep(pat->buf[pat->len - 1]))
+               strbuf_addstr(pat, "**");
+}
+
 static int prepare_include_condition_pattern(struct strbuf *pat)
 {
        struct strbuf path = STRBUF_INIT;
@@ -199,8 +206,7 @@ static int prepare_include_condition_pattern(struct strbuf *pat)
        } else if (!is_absolute_path(pat->buf))
                strbuf_insert(pat, 0, "**/", 3);
 
-       if (pat->len && is_dir_sep(pat->buf[pat->len - 1]))
-               strbuf_addstr(pat, "**");
+       add_trailing_starstar_for_dir(pat);
 
        strbuf_release(&path);
        return prefix;
@@ -264,6 +270,25 @@ static int include_by_gitdir(const struct config_options *opts,
        return ret;
 }
 
+static int include_by_branch(const char *cond, size_t cond_len)
+{
+       int flags;
+       int ret;
+       struct strbuf pattern = STRBUF_INIT;
+       const char *refname = resolve_ref_unsafe("HEAD", 0, NULL, &flags);
+       const char *shortname;
+
+       if (!refname || !(flags & REF_ISSYMREF) ||
+                       !skip_prefix(refname, "refs/heads/", &shortname))
+               return 0;
+
+       strbuf_add(&pattern, cond, cond_len);
+       add_trailing_starstar_for_dir(&pattern);
+       ret = !wildmatch(pattern.buf, shortname, WM_PATHNAME);
+       strbuf_release(&pattern);
+       return ret;
+}
+
 static int include_condition_is_true(const struct config_options *opts,
                                     const char *cond, size_t cond_len)
 {
@@ -272,6 +297,8 @@ static int include_condition_is_true(const struct config_options *opts,
                return include_by_gitdir(opts, cond, cond_len, 0);
        else if (skip_prefix_mem(cond, cond_len, "gitdir/i:", &cond, &cond_len))
                return include_by_gitdir(opts, cond, cond_len, 1);
+       else if (skip_prefix_mem(cond, cond_len, "onbranch:", &cond, &cond_len))
+               return include_by_branch(cond, cond_len);
 
        /* unknown conditionals are always false */
        return 0;
@@ -834,22 +861,16 @@ static int git_parse_source(config_fn_t fn, void *data,
        return error_return;
 }
 
-static int parse_unit_factor(const char *end, uintmax_t *val)
+static uintmax_t get_unit_factor(const char *end)
 {
        if (!*end)
                return 1;
-       else if (!strcasecmp(end, "k")) {
-               *val *= 1024;
-               return 1;
-       }
-       else if (!strcasecmp(end, "m")) {
-               *val *= 1024 * 1024;
-               return 1;
-       }
-       else if (!strcasecmp(end, "g")) {
-               *val *= 1024 * 1024 * 1024;
-               return 1;
-       }
+       else if (!strcasecmp(end, "k"))
+               return 1024;
+       else if (!strcasecmp(end, "m"))
+               return 1024 * 1024;
+       else if (!strcasecmp(end, "g"))
+               return 1024 * 1024 * 1024;
        return 0;
 }
 
@@ -859,19 +880,20 @@ static int git_parse_signed(const char *value, intmax_t *ret, intmax_t max)
                char *end;
                intmax_t val;
                uintmax_t uval;
-               uintmax_t factor = 1;
+               uintmax_t factor;
 
                errno = 0;
                val = strtoimax(value, &end, 0);
                if (errno == ERANGE)
                        return 0;
-               if (!parse_unit_factor(end, &factor)) {
+               factor = get_unit_factor(end);
+               if (!factor) {
                        errno = EINVAL;
                        return 0;
                }
-               uval = labs(val);
-               uval *= factor;
-               if (uval > max || labs(val) > uval) {
+               uval = val < 0 ? -val : val;
+               if (unsigned_mult_overflows(factor, uval) ||
+                   factor * uval > max) {
                        errno = ERANGE;
                        return 0;
                }
@@ -888,21 +910,23 @@ static int git_parse_unsigned(const char *value, uintmax_t *ret, uintmax_t max)
        if (value && *value) {
                char *end;
                uintmax_t val;
-               uintmax_t oldval;
+               uintmax_t factor;
 
                errno = 0;
                val = strtoumax(value, &end, 0);
                if (errno == ERANGE)
                        return 0;
-               oldval = val;
-               if (!parse_unit_factor(end, &val)) {
+               factor = get_unit_factor(end);
+               if (!factor) {
                        errno = EINVAL;
                        return 0;
                }
-               if (val > max || oldval > val) {
+               if (unsigned_mult_overflows(factor, val) ||
+                   factor * val > max) {
                        errno = ERANGE;
                        return 0;
                }
+               val *= factor;
                *ret = val;
                return 1;
        }
index be3b55f1cc2b7776cdb601acdc7cffd51ae79401..a43b4764023315695c8d691832a39aed8b861ce6 100644 (file)
@@ -475,8 +475,18 @@ else
       if test "$git_cv_ld_rpath" = "yes"; then
          CC_LD_DYNPATH=-rpath
       else
-         CC_LD_DYNPATH=
-         AC_MSG_WARN([linker does not support runtime path to dynamic libraries])
+         AC_CACHE_CHECK([if linker supports -Wl,+b,], git_cv_ld_wl_b, [
+            SAVE_LDFLAGS="${LDFLAGS}"
+            LDFLAGS="${SAVE_LDFLAGS} -Wl,+b,/"
+            AC_LINK_IFELSE([AC_LANG_PROGRAM([], [])], [git_cv_ld_wl_b=yes], [git_cv_ld_wl_b=no])
+            LDFLAGS="${SAVE_LDFLAGS}"
+         ])
+         if test "$git_cv_ld_wl_b" = "yes"; then
+            CC_LD_DYNPATH=-Wl,+b,
+          else
+             CC_LD_DYNPATH=
+             AC_MSG_WARN([linker does not support runtime path to dynamic libraries])
+          fi
       fi
    fi
 fi
index 01586821dc7c15be2a94c30bfbf4f3b4c94ac5f3..46b8d2ee11151b97e4e0cfd56afcf059247d1c54 100644 (file)
@@ -1,29 +1,60 @@
 @@
-type T;
-T *dst;
-T *src;
-expression n;
+expression dst, src, n, E;
 @@
-- memcpy(dst, src, (n) * sizeof(*dst));
-+ COPY_ARRAY(dst, src, n);
+  memcpy(dst, src, n * sizeof(
+- E[...]
++ *(E)
+  ))
 
 @@
 type T;
-T *dst;
-T *src;
-expression n;
+T *ptr;
+T[] arr;
+expression E, n;
 @@
-- memcpy(dst, src, (n) * sizeof(*src));
-+ COPY_ARRAY(dst, src, n);
+(
+  memcpy(ptr, E,
+- n * sizeof(*(ptr))
++ n * sizeof(T)
+  )
+|
+  memcpy(arr, E,
+- n * sizeof(*(arr))
++ n * sizeof(T)
+  )
+|
+  memcpy(E, ptr,
+- n * sizeof(*(ptr))
++ n * sizeof(T)
+  )
+|
+  memcpy(E, arr,
+- n * sizeof(*(arr))
++ n * sizeof(T)
+  )
+)
 
 @@
 type T;
-T *dst;
-T *src;
+T *dst_ptr;
+T *src_ptr;
+T[] dst_arr;
+T[] src_arr;
 expression n;
 @@
-- memcpy(dst, src, (n) * sizeof(T));
-+ COPY_ARRAY(dst, src, n);
+(
+- memcpy(dst_ptr, src_ptr, (n) * sizeof(T))
++ COPY_ARRAY(dst_ptr, src_ptr, n)
+|
+- memcpy(dst_ptr, src_arr, (n) * sizeof(T))
++ COPY_ARRAY(dst_ptr, src_arr, n)
+|
+- memcpy(dst_arr, src_ptr, (n) * sizeof(T))
++ COPY_ARRAY(dst_arr, src_ptr, n)
+|
+- memcpy(dst_arr, src_arr, (n) * sizeof(T))
++ COPY_ARRAY(dst_arr, src_arr, n)
+)
 
 @@
 type T;
index 9f71bcde967bc50915b90eaef72061f8c6a56315..8c6b610a2447cd1ec7c2dff0f9dbd09f6a024f57 100644 (file)
@@ -400,7 +400,8 @@ __gitcomp_builtin ()
        if [ -z "$options" ]; then
                # leading and trailing spaces are significant to make
                # option removal work correctly.
-               options=" $incl $(__git ${cmd/_/ } --git-completion-helper) "
+               options=" $incl $(__git ${cmd/_/ } --git-completion-helper) " || return
+
                for i in $excl; do
                        options="${options/ $i / }"
                done
index a838c219ec044b9fb78b4bda058d078e66d7aaf8..61812f48c2737a4702e604962e97f5c00d394b16 100644 (file)
@@ -232,7 +232,7 @@ int run_diff_files(struct rev_info *revs, unsigned int option)
 
                if (!changed && !dirty_submodule) {
                        ce_mark_uptodate(ce);
-                       mark_fsmonitor_valid(ce);
+                       mark_fsmonitor_valid(istate, ce);
                        if (!revs->diffopt.flags.find_copies_harder)
                                continue;
                }
diff --git a/diff.c b/diff.c
index a654d46f6a93de96d85706c7373ef0d617081a21..1ee04e321b1b5cc4e3f6bde37f49b7d3c0694aa6 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -5990,6 +5990,22 @@ static int remove_space(char *line, int len)
        return dst - line;
 }
 
+void flush_one_hunk(struct object_id *result, git_SHA_CTX *ctx)
+{
+       unsigned char hash[GIT_MAX_RAWSZ];
+       unsigned short carry = 0;
+       int i;
+
+       git_SHA1_Final(hash, ctx);
+       git_SHA1_Init(ctx);
+       /* 20-byte sum, with carry */
+       for (i = 0; i < GIT_SHA1_RAWSZ; ++i) {
+               carry += result->hash[i] + hash[i];
+               result->hash[i] = carry;
+               carry >>= 8;
+       }
+}
+
 static void patch_id_consume(void *priv, char *line, unsigned long len)
 {
        struct patch_id_t *data = priv;
@@ -6014,8 +6030,8 @@ static void patch_id_add_mode(git_SHA_CTX *ctx, unsigned mode)
        git_SHA1_Update(ctx, buf, len);
 }
 
-/* returns 0 upon success, and writes result into sha1 */
-static int diff_get_patch_id(struct diff_options *options, struct object_id *oid, int diff_header_only)
+/* returns 0 upon success, and writes result into oid */
+static int diff_get_patch_id(struct diff_options *options, struct object_id *oid, int diff_header_only, int stable)
 {
        struct diff_queue_struct *q = &diff_queued_diff;
        int i;
@@ -6025,6 +6041,7 @@ static int diff_get_patch_id(struct diff_options *options, struct object_id *oid
        git_SHA1_Init(&ctx);
        memset(&data, 0, sizeof(struct patch_id_t));
        data.ctx = &ctx;
+       oidclr(oid);
 
        for (i = 0; i < q->nr; i++) {
                xpparam_t xpp;
@@ -6100,17 +6117,22 @@ static int diff_get_patch_id(struct diff_options *options, struct object_id *oid
                                  patch_id_consume, &data, &xpp, &xecfg))
                        return error("unable to generate patch-id diff for %s",
                                     p->one->path);
+
+               if (stable)
+                       flush_one_hunk(oid, &ctx);
        }
 
-       git_SHA1_Final(oid->hash, &ctx);
+       if (!stable)
+               git_SHA1_Final(oid->hash, &ctx);
+
        return 0;
 }
 
-int diff_flush_patch_id(struct diff_options *options, struct object_id *oid, int diff_header_only)
+int diff_flush_patch_id(struct diff_options *options, struct object_id *oid, int diff_header_only, int stable)
 {
        struct diff_queue_struct *q = &diff_queued_diff;
        int i;
-       int result = diff_get_patch_id(options, oid, diff_header_only);
+       int result = diff_get_patch_id(options, oid, diff_header_only, stable);
 
        for (i = 0; i < q->nr; i++)
                diff_free_filepair(q->queue[i]);
diff --git a/diff.h b/diff.h
index d5e44baa9640d2917c69ad3463be0228288b98e4..b680b377b2f5871ecb3a27856f8d15fe2b1c8621 100644 (file)
--- a/diff.h
+++ b/diff.h
@@ -436,7 +436,8 @@ int run_diff_files(struct rev_info *revs, unsigned int option);
 int run_diff_index(struct rev_info *revs, int cached);
 
 int do_diff_cache(const struct object_id *, struct diff_options *);
-int diff_flush_patch_id(struct diff_options *, struct object_id *, int);
+int diff_flush_patch_id(struct diff_options *, struct object_id *, int, int);
+void flush_one_hunk(struct object_id *, git_SHA_CTX *);
 
 int diff_result_code(struct diff_options *, int);
 
index 07bd34b63145e1dc179afebcc7af351c34c2ab07..6af92d5eba6584416d331c2f6e63bd7d403ca773 100644 (file)
@@ -23,7 +23,7 @@ static int find_rename_dst(struct diff_filespec *two)
        first = 0;
        last = rename_dst_nr;
        while (last > first) {
-               int next = (last + first) >> 1;
+               int next = first + ((last - first) >> 1);
                struct diff_rename_dst *dst = &(rename_dst[next]);
                int cmp = strcmp(two->path, dst->two->path);
                if (!cmp)
@@ -83,7 +83,7 @@ static struct diff_rename_src *register_rename_src(struct diff_filepair *p)
        first = 0;
        last = rename_src_nr;
        while (last > first) {
-               int next = (last + first) >> 1;
+               int next = first + ((last - first) >> 1);
                struct diff_rename_src *src = &(rename_src[next]);
                int cmp = strcmp(one->path, src->p->one->path);
                if (!cmp)
diff --git a/dir.c b/dir.c
index ba4a51c296efcad9861ebfb4b318fbd5cb3025a4..d021c908e5d162cfd4d961ab11d16b1b9eb7124a 100644 (file)
--- a/dir.c
+++ b/dir.c
@@ -701,7 +701,7 @@ static struct untracked_cache_dir *lookup_untracked(struct untracked_cache *uc,
        first = 0;
        last = dir->dirs_nr;
        while (last > first) {
-               int cmp, next = (last + first) >> 1;
+               int cmp, next = first + ((last - first) >> 1);
                d = dir->dirs[next];
                cmp = strncmp(name, d->name, len);
                if (!cmp && strlen(d->name) > len)
diff --git a/entry.c b/entry.c
index 0e4f2f29101f913d0db2c401851f279eac496f02..53380bb614c19e82edfb45049f1703b4a3b9c8a3 100644 (file)
--- a/entry.c
+++ b/entry.c
@@ -373,7 +373,7 @@ static int write_entry(struct cache_entry *ce,
                        if (lstat(ce->name, &st) < 0)
                                return error_errno("unable to stat just-written file %s",
                                                   ce->name);
-               fill_stat_cache_info(ce, &st);
+               fill_stat_cache_info(state->istate, ce, &st);
                ce->ce_flags |= CE_UPDATE_IN_BASE;
                mark_fsmonitor_invalid(state->istate, ce);
                state->istate->cache_changed |= CE_ENTRY_CHANGED;
index f38d04fa58510bb7ab35caf4c43d5b2d954cc292..6dfdd6801c720f3b693ca6b09f8b7b3cf0f0c764 100644 (file)
@@ -644,7 +644,7 @@ static struct tree_content *grow_tree_content(
        struct tree_content *r = new_tree_content(t->entry_count + amt);
        r->entry_count = t->entry_count;
        r->delta_depth = t->delta_depth;
-       memcpy(r->entries,t->entries,t->entry_count*sizeof(t->entries[0]));
+       COPY_ARRAY(r->entries, t->entries, t->entry_count);
        release_tree_content(t);
        return r;
 }
@@ -2585,6 +2585,7 @@ static void parse_new_commit(const char *arg)
        struct branch *b;
        char *author = NULL;
        char *committer = NULL;
+       const char *encoding = NULL;
        struct hash_list *merge_list = NULL;
        unsigned int merge_count;
        unsigned char prev_fanout, new_fanout;
@@ -2607,6 +2608,8 @@ static void parse_new_commit(const char *arg)
        }
        if (!committer)
                die("Expected committer but didn't get one");
+       if (skip_prefix(command_buf.buf, "encoding ", &encoding))
+               read_next_command();
        parse_data(&msg, 0, NULL);
        read_next_command();
        parse_from(b);
@@ -2670,9 +2673,13 @@ static void parse_new_commit(const char *arg)
        }
        strbuf_addf(&new_data,
                "author %s\n"
-               "committer %s\n"
-               "\n",
+               "committer %s\n",
                author ? author : committer, committer);
+       if (encoding)
+               strbuf_addf(&new_data,
+                       "encoding %s\n",
+                       encoding);
+       strbuf_addch(&new_data, '\n');
        strbuf_addbuf(&new_data, &msg);
        free(author);
        free(committer);
index 1dee0aded1c433c7cfb90195328f270559c9b421..231e83a94db58e4efb2f4d7a1dbe90220bd2f317 100644 (file)
@@ -56,7 +56,7 @@ int read_fsmonitor_extension(struct index_state *istate, const void *data,
 
 void fill_fsmonitor_bitmap(struct index_state *istate)
 {
-       int i;
+       unsigned int i;
        istate->fsmonitor_dirty = ewah_new();
        for (i = 0; i < istate->cache_nr; i++)
                if (!(istate->cache[i]->ce_flags & CE_FSMONITOR_VALID))
@@ -134,7 +134,7 @@ void refresh_fsmonitor(struct index_state *istate)
        size_t bol; /* beginning of line */
        uint64_t last_update;
        char *buf;
-       int i;
+       unsigned int i;
 
        if (!core_fsmonitor || istate->fsmonitor_has_run_once)
                return;
@@ -192,7 +192,7 @@ void refresh_fsmonitor(struct index_state *istate)
 
 void add_fsmonitor(struct index_state *istate)
 {
-       int i;
+       unsigned int i;
 
        if (!istate->fsmonitor_last_update) {
                trace_printf_key(&trace_fsmonitor, "add fsmonitor");
@@ -225,7 +225,7 @@ void remove_fsmonitor(struct index_state *istate)
 
 void tweak_fsmonitor(struct index_state *istate)
 {
-       int i;
+       unsigned int i;
        int fsmonitor_enabled = git_config_get_fsmonitor();
 
        if (istate->fsmonitor_dirty) {
index 8489fa3244976bb3e489c030e726cf06f0e4e9b3..739318ab6d1060e3f5ef9eba0ab93aa560c1d246 100644 (file)
@@ -49,9 +49,10 @@ void refresh_fsmonitor(struct index_state *istate);
  * called any time the cache entry has been updated to reflect the
  * current state of the file on disk.
  */
-static inline void mark_fsmonitor_valid(struct cache_entry *ce)
+static inline void mark_fsmonitor_valid(struct index_state *istate, struct cache_entry *ce)
 {
-       if (core_fsmonitor) {
+       if (core_fsmonitor && !(ce->ce_flags & CE_FSMONITOR_VALID)) {
+               istate->cache_changed = 1;
                ce->ce_flags |= CE_FSMONITOR_VALID;
                trace_printf_key(&trace_fsmonitor, "mark_fsmonitor_clean '%s'", ce->name);
        }
index 20eb81cc92f947d872b31a179d43d97772ff25e4..da5b4ec4bc50e4e3adaba0f25820262b6a7436a0 100755 (executable)
@@ -972,7 +972,11 @@ sub coalesce_overlapping_hunks {
                        next;
                }
                if ($ofs_delta) {
-                       $n_ofs += $ofs_delta;
+                       if ($patch_mode_flavour{IS_REVERSE}) {
+                               $o_ofs -= $ofs_delta;
+                       } else {
+                               $n_ofs += $ofs_delta;
+                       }
                        $_->{TEXT}->[0] = format_hunk_header($o_ofs, $o_cnt,
                                                             $n_ofs, $n_cnt);
                }
index 88fa6a914a172877991ae9890cae1a42ca51fb89..e3f6d543fb5bb0777483081fadaa7b66035ab5fa 100755 (executable)
@@ -228,9 +228,8 @@ stage_submodule () {
 }
 
 checkout_staged_file () {
-       tmpfile=$(expr \
-               "$(git checkout-index --temp --stage="$1" "$2" 2>/dev/null)" \
-               : '\([^ ]*\)    ')
+       tmpfile="$(git checkout-index --temp --stage="$1" "$2" 2>/dev/null)" &&
+       tmpfile=${tmpfile%%'    '*}
 
        if test $? -eq 0 && test -n "$tmpfile"
        then
@@ -255,13 +254,16 @@ merge_file () {
                return 1
        fi
 
-       if BASE=$(expr "$MERGED" : '\(.*\)\.[^/]*$')
-       then
-               ext=$(expr "$MERGED" : '.*\(\.[^/]*\)$')
-       else
+       # extract file extension from the last path component
+       case "${MERGED##*/}" in
+       *.*)
+               ext=.${MERGED##*.}
+               BASE=${MERGED%"$ext"}
+               ;;
+       *)
                BASE=$MERGED
                ext=
-       fi
+       esac
 
        mergetool_tmpdir_init
 
@@ -277,15 +279,30 @@ merge_file () {
        REMOTE="$MERGETOOL_TMPDIR/${BASE}_REMOTE_$$$ext"
        BASE="$MERGETOOL_TMPDIR/${BASE}_BASE_$$$ext"
 
-       base_mode=$(git ls-files -u -- "$MERGED" | awk '{if ($3==1) print $1;}')
-       local_mode=$(git ls-files -u -- "$MERGED" | awk '{if ($3==2) print $1;}')
-       remote_mode=$(git ls-files -u -- "$MERGED" | awk '{if ($3==3) print $1;}')
+       base_mode= local_mode= remote_mode=
+
+       # here, $IFS is just a LF
+       for line in $f
+       do
+               mode=${line%% *}                # 1st word
+               sha1=${line#"$mode "}
+               sha1=${sha1%% *}                # 2nd word
+               case "${line#$mode $sha1 }" in  # remainder
+               '1      '*)
+                       base_mode=$mode
+                       ;;
+               '2      '*)
+                       local_mode=$mode local_sha1=$sha1
+                       ;;
+               '3      '*)
+                       remote_mode=$mode remote_sha1=$sha1
+                       ;;
+               esac
+       done
 
        if is_submodule "$local_mode" || is_submodule "$remote_mode"
        then
                echo "Submodule merge conflict for '$MERGED':"
-               local_sha1=$(git ls-files -u -- "$MERGED" | awk '{if ($3==2) print $2;}')
-               remote_sha1=$(git ls-files -u -- "$MERGED" | awk '{if ($3==3) print $2;}')
                describe_file "$local_mode" "local" "$local_sha1"
                describe_file "$remote_mode" "remote" "$remote_sha1"
                resolve_submodule_merge
@@ -406,7 +423,7 @@ main () {
                -t|--tool*)
                        case "$#,$1" in
                        *,*=*)
-                               merge_tool=$(expr "z$1" : 'z-[^=]*=\(.*\)')
+                               merge_tool=${1#*=}
                                ;;
                        1,*)
                                usage ;;
index 5b79920f46a972437ff05966d042a84d44f050d9..3991e7d1a7fc4d7206f8786a0d92ae5961ef0ef4 100755 (executable)
--- a/git-p4.py
+++ b/git-p4.py
@@ -737,7 +737,7 @@ def extractLogMessageFromGitCommit(commit):
 
     ## fixme: title is first line of commit, not 1st paragraph.
     foundTitle = False
-    for log in read_pipe_lines("git cat-file commit %s" % commit):
+    for log in read_pipe_lines(["git", "cat-file", "commit", commit]):
        if not foundTitle:
            if len(log) == 1:
                foundTitle = True
@@ -1309,14 +1309,14 @@ def processContent(self, git_mode, relPath, contents):
 
 class Command:
     delete_actions = ( "delete", "move/delete", "purge" )
-    add_actions = ( "add", "move/add" )
+    add_actions = ( "add", "branch", "move/add" )
 
     def __init__(self):
         self.usage = "usage: %prog [options]"
         self.needsGit = True
         self.verbose = False
 
-    # This is required for the "append" cloneExclude action
+    # This is required for the "append" update_shelve action
     def ensure_value(self, attr, value):
         if not hasattr(self, attr) or getattr(self, attr) is None:
             setattr(self, attr, value)
@@ -2530,6 +2530,11 @@ def map_in_client(self, depot_path):
         die( "Error: %s is not found in client spec path" % depot_path )
         return ""
 
+def cloneExcludeCallback(option, opt_str, value, parser):
+    # prepend "/" because the first "/" was consumed as part of the option itself.
+    # ("-//depot/A/..." becomes "/depot/A/..." after option parsing)
+    parser.values.cloneExclude += ["/" + re.sub(r"\.\.\.$", "", value)]
+
 class P4Sync(Command, P4UserMap):
 
     def __init__(self):
@@ -2553,7 +2558,7 @@ def __init__(self):
                 optparse.make_option("--use-client-spec", dest="useClientSpec", action='store_true',
                                      help="Only sync files that are included in the Perforce Client Spec"),
                 optparse.make_option("-/", dest="cloneExclude",
-                                     action="append", type="string",
+                                     action="callback", callback=cloneExcludeCallback, type="string",
                                      help="exclude depot path"),
         ]
         self.description = """Imports from Perforce into a git repository.\n
@@ -2618,20 +2623,25 @@ def checkpoint(self):
         if self.verbose:
             print("checkpoint finished: " + out)
 
+    def isPathWanted(self, path):
+        for p in self.cloneExclude:
+            if p.endswith("/"):
+                if p4PathStartsWith(path, p):
+                    return False
+            # "-//depot/file1" without a trailing "/" should only exclude "file1", but not "file111" or "file1_dir/file2"
+            elif path.lower() == p.lower():
+                return False
+        for p in self.depotPaths:
+            if p4PathStartsWith(path, p):
+                return True
+        return False
+
     def extractFilesFromCommit(self, commit, shelved=False, shelved_cl = 0):
-        self.cloneExclude = [re.sub(r"\.\.\.$", "", path)
-                             for path in self.cloneExclude]
         files = []
         fnum = 0
         while "depotFile%s" % fnum in commit:
             path =  commit["depotFile%s" % fnum]
-
-            if [p for p in self.cloneExclude
-                if p4PathStartsWith(path, p)]:
-                found = False
-            else:
-                found = [p for p in self.depotPaths
-                         if p4PathStartsWith(path, p)]
+            found = self.isPathWanted(path)
             if not found:
                 fnum = fnum + 1
                 continue
@@ -2668,7 +2678,7 @@ def stripRepoPath(self, path, prefixes):
             path = self.clientSpecDirs.map_in_client(path)
             if self.detectBranches:
                 for b in self.knownBranches:
-                    if path.startswith(b + "/"):
+                    if p4PathStartsWith(path, b + "/"):
                         path = path[len(b)+1:]
 
         elif self.keepRepoPath:
@@ -2700,8 +2710,7 @@ def splitFilesIntoBranches(self, commit):
         fnum = 0
         while "depotFile%s" % fnum in commit:
             path =  commit["depotFile%s" % fnum]
-            found = [p for p in self.depotPaths
-                     if p4PathStartsWith(path, p)]
+            found = self.isPathWanted(path)
             if not found:
                 fnum = fnum + 1
                 continue
@@ -2723,7 +2732,7 @@ def splitFilesIntoBranches(self, commit):
             for branch in self.knownBranches.keys():
                 # add a trailing slash so that a commit into qt/4.2foo
                 # doesn't end up in qt/4.2, e.g.
-                if relPath.startswith(branch + "/"):
+                if p4PathStartsWith(relPath, branch + "/"):
                     if branch not in branches:
                         branches[branch] = []
                     branches[branch].append(file)
@@ -3325,7 +3334,9 @@ def gitCommitByP4Change(self, ref, change):
             if currentChange < change:
                 earliestCommit = "^%s" % next
             else:
-                latestCommit = "%s" % next
+                if next == latestCommit:
+                    die("Infinite loop while looking in ref %s for change %s. Check your branch mappings" % (ref, change))
+                latestCommit = "%s^@" % next
 
         return ""
 
@@ -3888,7 +3899,6 @@ def run(self, args):
             self.cloneDestination = depotPaths[-1]
             depotPaths = depotPaths[:-1]
 
-        self.cloneExclude = ["/"+p for p in self.cloneExclude]
         for p in depotPaths:
             if not p.startswith("//"):
                 sys.stderr.write('Depot paths must start with "//": %s\n' % p)
diff --git a/git-rebase--am.sh b/git-rebase--am.sh
deleted file mode 100644 (file)
index 6416716..0000000
+++ /dev/null
@@ -1,85 +0,0 @@
-# This shell script fragment is sourced by git-rebase to implement
-# its default, fast, patch-based, non-interactive mode.
-#
-# Copyright (c) 2010 Junio C Hamano.
-#
-
-git_rebase__am () {
-
-case "$action" in
-continue)
-       git am --resolved --resolvemsg="$resolvemsg" \
-               ${gpg_sign_opt:+"$gpg_sign_opt"} &&
-       move_to_original_branch
-       return
-       ;;
-skip)
-       git am --skip --resolvemsg="$resolvemsg" &&
-       move_to_original_branch
-       return
-       ;;
-show-current-patch)
-       exec git am --show-current-patch
-       ;;
-esac
-
-if test -z "$rebase_root"
-       # this is now equivalent to ! -z "$upstream"
-then
-       revisions=$upstream...$orig_head
-else
-       revisions=$onto...$orig_head
-fi
-
-ret=0
-rm -f "$GIT_DIR/rebased-patches"
-
-git format-patch -k --stdout --full-index --cherry-pick --right-only \
-       --src-prefix=a/ --dst-prefix=b/ --no-renames --no-cover-letter \
-       --pretty=mboxrd --topo-order \
-       $git_format_patch_opt \
-       "$revisions" ${restrict_revision+^$restrict_revision} \
-       >"$GIT_DIR/rebased-patches"
-ret=$?
-
-if test 0 != $ret
-then
-       rm -f "$GIT_DIR/rebased-patches"
-       case "$head_name" in
-       refs/heads/*)
-               git checkout -q "$head_name"
-               ;;
-       *)
-               git checkout -q "$orig_head"
-               ;;
-       esac
-
-       cat >&2 <<-EOF
-
-       git encountered an error while preparing the patches to replay
-       these revisions:
-
-           $revisions
-
-       As a result, git cannot rebase them.
-       EOF
-       return $ret
-fi
-
-git am $git_am_opt --rebasing --resolvemsg="$resolvemsg" \
-       --patch-format=mboxrd \
-       $allow_rerere_autoupdate \
-       ${gpg_sign_opt:+"$gpg_sign_opt"} <"$GIT_DIR/rebased-patches"
-ret=$?
-
-rm -f "$GIT_DIR/rebased-patches"
-
-if test 0 != $ret
-then
-       test -d "$state_dir" && write_basic_state
-       return $ret
-fi
-
-move_to_original_branch
-
-}
diff --git a/git-rebase--common.sh b/git-rebase--common.sh
deleted file mode 100644 (file)
index f00e13e..0000000
+++ /dev/null
@@ -1,69 +0,0 @@
-
-resolvemsg="
-$(gettext 'Resolve all conflicts manually, mark them as resolved with
-"git add/rm <conflicted_files>", then run "git rebase --continue".
-You can instead skip this commit: run "git rebase --skip".
-To abort and get back to the state before "git rebase", run "git rebase --abort".')
-"
-
-write_basic_state () {
-       echo "$head_name" > "$state_dir"/head-name &&
-       echo "$onto" > "$state_dir"/onto &&
-       echo "$orig_head" > "$state_dir"/orig-head &&
-       test t = "$GIT_QUIET" && : > "$state_dir"/quiet
-       test t = "$verbose" && : > "$state_dir"/verbose
-       test -n "$strategy" && echo "$strategy" > "$state_dir"/strategy
-       test -n "$strategy_opts" && echo "$strategy_opts" > \
-               "$state_dir"/strategy_opts
-       test -n "$allow_rerere_autoupdate" && echo "$allow_rerere_autoupdate" > \
-               "$state_dir"/allow_rerere_autoupdate
-       test -n "$gpg_sign_opt" && echo "$gpg_sign_opt" > "$state_dir"/gpg_sign_opt
-       test -n "$signoff" && echo "$signoff" >"$state_dir"/signoff
-       test -n "$reschedule_failed_exec" && : > "$state_dir"/reschedule-failed-exec
-}
-
-apply_autostash () {
-       if test -f "$state_dir/autostash"
-       then
-               stash_sha1=$(cat "$state_dir/autostash")
-               if git stash apply $stash_sha1 >/dev/null 2>&1
-               then
-                       echo "$(gettext 'Applied autostash.')" >&2
-               else
-                       git stash store -m "autostash" -q $stash_sha1 ||
-                       die "$(eval_gettext "Cannot store \$stash_sha1")"
-                       gettext 'Applying autostash resulted in conflicts.
-Your changes are safe in the stash.
-You can run "git stash pop" or "git stash drop" at any time.
-' >&2
-               fi
-       fi
-}
-
-move_to_original_branch () {
-       case "$head_name" in
-       refs/*)
-               message="rebase finished: $head_name onto $onto"
-               git update-ref -m "$message" \
-                       $head_name $(git rev-parse HEAD) $orig_head &&
-               git symbolic-ref \
-                       -m "rebase finished: returning to $head_name" \
-                       HEAD $head_name ||
-               die "$(eval_gettext "Could not move back to \$head_name")"
-               ;;
-       esac
-}
-
-output () {
-       case "$verbose" in
-       '')
-               output=$("$@" 2>&1 )
-               status=$?
-               test $status != 0 && printf "%s\n" "$output"
-               return $status
-               ;;
-       *)
-               "$@"
-               ;;
-       esac
-}
index afbb65765d46102339068e7e9aa397fcf88ee6a5..dec90e9af67785e23bbe7cc553a1c23a5f0a1fd3 100644 (file)
@@ -77,6 +77,61 @@ rewritten_pending="$state_dir"/rewritten-pending
 # and leaves CR at the end instead.
 cr=$(printf "\015")
 
+resolvemsg="
+$(gettext 'Resolve all conflicts manually, mark them as resolved with
+"git add/rm <conflicted_files>", then run "git rebase --continue".
+You can instead skip this commit: run "git rebase --skip".
+To abort and get back to the state before "git rebase", run "git rebase --abort".')
+"
+
+write_basic_state () {
+       echo "$head_name" > "$state_dir"/head-name &&
+       echo "$onto" > "$state_dir"/onto &&
+       echo "$orig_head" > "$state_dir"/orig-head &&
+       test t = "$GIT_QUIET" && : > "$state_dir"/quiet
+       test t = "$verbose" && : > "$state_dir"/verbose
+       test -n "$strategy" && echo "$strategy" > "$state_dir"/strategy
+       test -n "$strategy_opts" && echo "$strategy_opts" > \
+               "$state_dir"/strategy_opts
+       test -n "$allow_rerere_autoupdate" && echo "$allow_rerere_autoupdate" > \
+               "$state_dir"/allow_rerere_autoupdate
+       test -n "$gpg_sign_opt" && echo "$gpg_sign_opt" > "$state_dir"/gpg_sign_opt
+       test -n "$signoff" && echo "$signoff" >"$state_dir"/signoff
+       test -n "$reschedule_failed_exec" && : > "$state_dir"/reschedule-failed-exec
+}
+
+apply_autostash () {
+       if test -f "$state_dir/autostash"
+       then
+               stash_sha1=$(cat "$state_dir/autostash")
+               if git stash apply $stash_sha1 >/dev/null 2>&1
+               then
+                       echo "$(gettext 'Applied autostash.')" >&2
+               else
+                       git stash store -m "autostash" -q $stash_sha1 ||
+                       die "$(eval_gettext "Cannot store \$stash_sha1")"
+                       gettext 'Applying autostash resulted in conflicts.
+Your changes are safe in the stash.
+You can run "git stash pop" or "git stash drop" at any time.
+' >&2
+               fi
+       fi
+}
+
+output () {
+       case "$verbose" in
+       '')
+               output=$("$@" 2>&1 )
+               status=$?
+               test $status != 0 && printf "%s\n" "$output"
+               return $status
+               ;;
+       *)
+               "$@"
+               ;;
+       esac
+}
+
 strategy_args=${strategy:+--strategy=$strategy}
 test -n "$strategy_opts" &&
 eval '
index 13c172bd94fc5d4a9658b27fae9733f73a272376..2d0e44656cc6ca4cc0cd8b579a1ebd8798e90b5f 100755 (executable)
@@ -65,6 +65,8 @@ test -z "$head" && die "fatal: Not a valid revision: $local"
 headrev=$(git rev-parse --verify --quiet "$head"^0)
 test -z "$headrev" && die "fatal: Ambiguous revision: $local"
 
+local_sha1=$(git rev-parse --verify --quiet "$head")
+
 # Was it a branch with a description?
 branch_name=${head#refs/heads/}
 if test "z$branch_name" = "z$headref" ||
@@ -77,43 +79,53 @@ merge_base=$(git merge-base $baserev $headrev) ||
 die "fatal: No commits in common between $base and $head"
 
 # $head is the refname from the command line.
-# If a ref with the same name as $head exists at the remote
-# and their values match, use that.
-#
-# Otherwise find a random ref that matches $headrev.
+# Find a ref with the same name as $head that exists at the remote
+# and points to the same commit as the local object.
 find_matching_ref='
        my ($head,$headrev) = (@ARGV);
-       my ($found);
+       my $pattern = qr{/\Q$head\E$};
+       my ($remote_sha1, $found);
 
        while (<STDIN>) {
                chomp;
                my ($sha1, $ref, $deref) = /^(\S+)\s+([^^]+)(\S*)$/;
-               my ($pattern);
-               next unless ($sha1 eq $headrev);
 
-               $pattern="/$head\$";
-               if ($ref eq $head) {
-                       $found = $ref;
-               }
-               if ($ref =~ /$pattern/) {
-                       $found = $ref;
-               }
                if ($sha1 eq $head) {
-                       $found = $sha1;
+                       $found = $remote_sha1 = $sha1;
+                       break;
+               }
+
+               if ($ref eq $head || $ref =~ $pattern) {
+                       if ($deref eq "") {
+                               # Remember the matching object on the remote side
+                               $remote_sha1 = $sha1;
+                       }
+                       if ($sha1 eq $headrev) {
+                               $found = $ref;
+                               break;
+                       }
                }
        }
        if ($found) {
-               print "$found\n";
+               $remote_sha1 = $headrev if ! defined $remote_sha1;
+               print "$remote_sha1 $found\n";
        }
 '
 
-ref=$(git ls-remote "$url" | @@PERL@@ -e "$find_matching_ref" "${remote:-HEAD}" "$headrev")
+set fnord $(git ls-remote "$url" | @@PERL@@ -e "$find_matching_ref" "${remote:-HEAD}" "$headrev")
+remote_sha1=$2
+ref=$3
 
 if test -z "$ref"
 then
        echo "warn: No match for commit $headrev found at $url" >&2
        echo "warn: Are you sure you pushed '${remote:-HEAD}' there?" >&2
        status=1
+elif test "$local_sha1" != "$remote_sha1"
+then
+       echo "warn: $head found at $url but points to a different object" >&2
+       echo "warn: Are you sure you pushed '${remote:-HEAD}' there?" >&2
+       status=1
 fi
 
 # Special case: turn "for_linus" to "tags/for_linus" when it is correct
index 24859a7bc37b05909cf16554c102cadc842c13ff..5f92c89c1c1be5ba2fadedd1d7a8d013fb7577fd 100755 (executable)
@@ -177,11 +177,15 @@ sub format_2822_time {
 my $re_encoded_word = qr/=\?($re_token)\?($re_token)\?($re_encoded_text)\?=/;
 
 # Variables we fill in automatically, or via prompting:
-my (@to,$no_to,@initial_to,@cc,$no_cc,@initial_cc,@bcclist,$no_bcc,@xh,
+my (@to,@cc,@xh,$envelope_sender,
        $initial_in_reply_to,$reply_to,$initial_subject,@files,
-       $author,$sender,$smtp_authpass,$annotate,$use_xmailer,$compose,$time);
-
-my $envelope_sender;
+       $author,$sender,$smtp_authpass,$annotate,$compose,$time);
+# Things we either get from config, *or* are overridden on the
+# command-line.
+my ($no_cc, $no_to, $no_bcc, $no_identity);
+my (@config_to, @getopt_to);
+my (@config_cc, @getopt_cc);
+my (@config_bcc, @getopt_bcc);
 
 # Example reply to:
 #$initial_in_reply_to = ''; #<20050203173208.GA23964@foobar.com>';
@@ -228,33 +232,37 @@ sub do_edit {
 }
 
 # Variables with corresponding config settings
-my ($thread, $chain_reply_to, $suppress_from, $signed_off_by_cc);
+my ($suppress_from, $signed_off_by_cc);
 my ($cover_cc, $cover_to);
 my ($to_cmd, $cc_cmd);
 my ($smtp_server, $smtp_server_port, @smtp_server_options);
 my ($smtp_authuser, $smtp_encryption, $smtp_ssl_cert_path);
 my ($batch_size, $relogin_delay);
 my ($identity, $aliasfiletype, @alias_files, $smtp_domain, $smtp_auth);
-my ($validate, $confirm);
+my ($confirm);
 my (@suppress_cc);
 my ($auto_8bit_encoding);
 my ($compose_encoding);
-my $target_xfer_encoding = 'auto';
-
+# Variables with corresponding config settings & hardcoded defaults
 my ($debug_net_smtp) = 0;              # Net::SMTP, see send_message()
+my $thread = 1;
+my $chain_reply_to = 0;
+my $use_xmailer = 1;
+my $validate = 1;
+my $target_xfer_encoding = 'auto';
 
 my %config_bool_settings = (
-    "thread" => [\$thread, 1],
-    "chainreplyto" => [\$chain_reply_to, 0],
-    "suppressfrom" => [\$suppress_from, undef],
-    "signedoffbycc" => [\$signed_off_by_cc, undef],
-    "cccover" => [\$cover_cc, undef],
-    "tocover" => [\$cover_to, undef],
-    "signedoffcc" => [\$signed_off_by_cc, undef],      # Deprecated
-    "validate" => [\$validate, 1],
-    "multiedit" => [\$multiedit, undef],
-    "annotate" => [\$annotate, undef],
-    "xmailer" => [\$use_xmailer, 1]
+    "thread" => \$thread,
+    "chainreplyto" => \$chain_reply_to,
+    "suppressfrom" => \$suppress_from,
+    "signedoffbycc" => \$signed_off_by_cc,
+    "cccover" => \$cover_cc,
+    "tocover" => \$cover_to,
+    "signedoffcc" => \$signed_off_by_cc,
+    "validate" => \$validate,
+    "multiedit" => \$multiedit,
+    "annotate" => \$annotate,
+    "xmailer" => \$use_xmailer,
 );
 
 my %config_settings = (
@@ -267,12 +275,12 @@ sub do_edit {
     "smtpauth" => \$smtp_auth,
     "smtpbatchsize" => \$batch_size,
     "smtprelogindelay" => \$relogin_delay,
-    "to" => \@initial_to,
+    "to" => \@config_to,
     "tocmd" => \$to_cmd,
-    "cc" => \@initial_cc,
+    "cc" => \@config_cc,
     "cccmd" => \$cc_cmd,
     "aliasfiletype" => \$aliasfiletype,
-    "bcc" => \@bcclist,
+    "bcc" => \@config_bcc,
     "suppresscc" => \@suppress_cc,
     "envelopesender" => \$envelope_sender,
     "confirm"   => \$confirm,
@@ -315,13 +323,87 @@ sub signal_handler {
 $SIG{TERM} = \&signal_handler;
 $SIG{INT}  = \&signal_handler;
 
+# Read our sendemail.* config
+sub read_config {
+       my ($configured, $prefix) = @_;
+
+       foreach my $setting (keys %config_bool_settings) {
+               my $target = $config_bool_settings{$setting};
+               my $v = Git::config_bool(@repo, "$prefix.$setting");
+               next unless defined $v;
+               next if $configured->{$setting}++;
+               $$target = $v;
+       }
+
+       foreach my $setting (keys %config_path_settings) {
+               my $target = $config_path_settings{$setting};
+               if (ref($target) eq "ARRAY") {
+                       my @values = Git::config_path(@repo, "$prefix.$setting");
+                       next unless @values;
+                       next if $configured->{$setting}++;
+                       @$target = @values;
+               }
+               else {
+                       my $v = Git::config_path(@repo, "$prefix.$setting");
+                       next unless defined $v;
+                       next if $configured->{$setting}++;
+                       $$target = $v;
+               }
+       }
+
+       foreach my $setting (keys %config_settings) {
+               my $target = $config_settings{$setting};
+               if (ref($target) eq "ARRAY") {
+                       my @values = Git::config(@repo, "$prefix.$setting");
+                       next unless @values;
+                       next if $configured->{$setting}++;
+                       @$target = @values;
+               }
+               else {
+                       my $v = Git::config(@repo, "$prefix.$setting");
+                       next unless defined $v;
+                       next if $configured->{$setting}++;
+                       $$target = $v;
+               }
+       }
+
+       if (!defined $smtp_encryption) {
+               my $setting = "$prefix.smtpencryption";
+               my $enc = Git::config(@repo, $setting);
+               return unless defined $enc;
+               return if $configured->{$setting}++;
+               if (defined $enc) {
+                       $smtp_encryption = $enc;
+               } elsif (Git::config_bool(@repo, "$prefix.smtpssl")) {
+                       $smtp_encryption = 'ssl';
+               }
+       }
+}
+
+# sendemail.identity yields to --identity. We must parse this
+# special-case first before the rest of the config is read.
+$identity = Git::config(@repo, "sendemail.identity");
+my $rc = GetOptions(
+       "identity=s" => \$identity,
+       "no-identity" => \$no_identity,
+);
+usage() unless $rc;
+undef $identity if $no_identity;
+
+# Now we know enough to read the config
+{
+    my %configured;
+    read_config(\%configured, "sendemail.$identity") if defined $identity;
+    read_config(\%configured, "sendemail");
+}
+
 # Begin by accumulating all the variables (defined above), that we will end up
 # needing, first, from the command line:
 
 my $help;
 my $git_completion_helper;
-my $rc = GetOptions("h" => \$help,
-                    "dump-aliases" => \$dump_aliases);
+$rc = GetOptions("h" => \$help,
+                 "dump-aliases" => \$dump_aliases);
 usage() unless $rc;
 die __("--dump-aliases incompatible with other options\n")
     if !$help and $dump_aliases and @ARGV;
@@ -330,12 +412,12 @@ sub signal_handler {
                     "in-reply-to=s" => \$initial_in_reply_to,
                    "reply-to=s" => \$reply_to,
                    "subject=s" => \$initial_subject,
-                   "to=s" => \@initial_to,
+                   "to=s" => \@getopt_to,
                    "to-cmd=s" => \$to_cmd,
                    "no-to" => \$no_to,
-                   "cc=s" => \@initial_cc,
+                   "cc=s" => \@getopt_cc,
                    "no-cc" => \$no_cc,
-                   "bcc=s" => \@bcclist,
+                   "bcc=s" => \@getopt_bcc,
                    "no-bcc" => \$no_bcc,
                    "chain-reply-to!" => \$chain_reply_to,
                    "no-chain-reply-to" => sub {$chain_reply_to = 0},
@@ -351,7 +433,6 @@ sub signal_handler {
                    "smtp-domain:s" => \$smtp_domain,
                    "smtp-auth=s" => \$smtp_auth,
                    "no-smtp-auth" => sub {$smtp_auth = 'none'},
-                   "identity=s" => \$identity,
                    "annotate!" => \$annotate,
                    "no-annotate" => sub {$annotate = 0},
                    "compose" => \$compose,
@@ -386,6 +467,11 @@ sub signal_handler {
                    "git-completion-helper" => \$git_completion_helper,
         );
 
+# Munge any "either config or getopt, not both" variables
+my @initial_to = @getopt_to ? @getopt_to : ($no_to ? () : @config_to);
+my @initial_cc = @getopt_cc ? @getopt_cc : ($no_cc ? () : @config_cc);
+my @initial_bcc = @getopt_bcc ? @getopt_bcc : ($no_bcc ? () : @config_bcc);
+
 usage() if $help;
 completion_helper() if $git_completion_helper;
 unless ($rc) {
@@ -399,65 +485,6 @@ sub signal_handler {
        "(via command-line or configuration option)\n")
        if defined $relogin_delay and not defined $batch_size;
 
-# Now, let's fill any that aren't set in with defaults:
-
-sub read_config {
-       my ($prefix) = @_;
-
-       foreach my $setting (keys %config_bool_settings) {
-               my $target = $config_bool_settings{$setting}->[0];
-               $$target = Git::config_bool(@repo, "$prefix.$setting") unless (defined $$target);
-       }
-
-       foreach my $setting (keys %config_path_settings) {
-               my $target = $config_path_settings{$setting};
-               if (ref($target) eq "ARRAY") {
-                       unless (@$target) {
-                               my @values = Git::config_path(@repo, "$prefix.$setting");
-                               @$target = @values if (@values && defined $values[0]);
-                       }
-               }
-               else {
-                       $$target = Git::config_path(@repo, "$prefix.$setting") unless (defined $$target);
-               }
-       }
-
-       foreach my $setting (keys %config_settings) {
-               my $target = $config_settings{$setting};
-               next if $setting eq "to" and defined $no_to;
-               next if $setting eq "cc" and defined $no_cc;
-               next if $setting eq "bcc" and defined $no_bcc;
-               if (ref($target) eq "ARRAY") {
-                       unless (@$target) {
-                               my @values = Git::config(@repo, "$prefix.$setting");
-                               @$target = @values if (@values && defined $values[0]);
-                       }
-               }
-               else {
-                       $$target = Git::config(@repo, "$prefix.$setting") unless (defined $$target);
-               }
-       }
-
-       if (!defined $smtp_encryption) {
-               my $enc = Git::config(@repo, "$prefix.smtpencryption");
-               if (defined $enc) {
-                       $smtp_encryption = $enc;
-               } elsif (Git::config_bool(@repo, "$prefix.smtpssl")) {
-                       $smtp_encryption = 'ssl';
-               }
-       }
-}
-
-# read configuration from [sendemail "$identity"], fall back on [sendemail]
-$identity = Git::config(@repo, "sendemail.identity") unless (defined $identity);
-read_config("sendemail.$identity") if (defined $identity);
-read_config("sendemail");
-
-# fall back on builtin bool defaults
-foreach my $setting (values %config_bool_settings) {
-       ${$setting->[0]} = $setting->[1] unless (defined (${$setting->[0]}));
-}
-
 # 'default' encryption is none -- this only prevents a warning
 $smtp_encryption = '' unless (defined $smtp_encryption);
 
@@ -941,7 +968,7 @@ sub expand_one_alias {
 
 @initial_to = process_address_list(@initial_to);
 @initial_cc = process_address_list(@initial_cc);
-@bcclist = process_address_list(@bcclist);
+@initial_bcc = process_address_list(@initial_bcc);
 
 if ($thread && !defined $initial_in_reply_to && $prompting) {
        $initial_in_reply_to = ask(
@@ -1364,7 +1391,7 @@ sub send_message {
                    }
               @cc);
        my $to = join (",\n\t", @recipients);
-       @recipients = unique_email_list(@recipients,@cc,@bcclist);
+       @recipients = unique_email_list(@recipients,@cc,@initial_bcc);
        @recipients = (map { extract_valid_address_or_die($_) } @recipients);
        my $date = format_2822_time($time++);
        my $gitversion = '@@GIT_VERSION@@';
diff --git a/git.c b/git.c
index 1bf9c94550d9c232de2f89ef0f2b5a661e43cd79..c2eec470c956c597aef77ff7d3632994986db00f 100644 (file)
--- a/git.c
+++ b/git.c
@@ -33,7 +33,8 @@ const char git_usage_string[] =
 const char git_more_info_string[] =
        N_("'git help -a' and 'git help -g' list available subcommands and some\n"
           "concept guides. See 'git help <command>' or 'git help <concept>'\n"
-          "to read about a specific subcommand or concept.");
+          "to read about a specific subcommand or concept.\n"
+          "See 'git help git' for an overview of the system.");
 
 static int use_pager = -1;
 
diff --git a/grep.c b/grep.c
index 0d50598acda6f1c41951d1d656ea701114473239..f7c3a5803e8ea0bf2c609e9eeec6764217641cfc 100644 (file)
--- a/grep.c
+++ b/grep.c
@@ -1780,6 +1780,10 @@ static int grep_source_1(struct grep_opt *opt, struct grep_source *gs, int colle
        enum grep_context ctx = GREP_CONTEXT_HEAD;
        xdemitconf_t xecfg;
 
+       if (!opt->status_only && gs->name == NULL)
+               BUG("grep call which could print a name requires "
+                   "grep_source.name be non-NULL");
+
        if (!opt->output)
                opt->output = std_output;
 
diff --git a/help.c b/help.c
index a9e451f2ee7a165eecb17746db9cc1a2f7e2cfbb..5261d83ecf15042d8babccf9779f0b96fac94e92 100644 (file)
--- a/help.c
+++ b/help.c
@@ -754,19 +754,19 @@ static int append_similar_ref(const char *refname, const struct object_id *oid,
 {
        struct similar_ref_cb *cb = (struct similar_ref_cb *)(cb_data);
        char *branch = strrchr(refname, '/') + 1;
-       const char *remote;
 
        /* A remote branch of the same name is deemed similar */
-       if (skip_prefix(refname, "refs/remotes/", &remote) &&
+       if (starts_with(refname, "refs/remotes/") &&
            !strcmp(branch, cb->base_ref))
-               string_list_append(cb->similar_refs, remote);
+               string_list_append_nodup(cb->similar_refs,
+                                        shorten_unambiguous_ref(refname, 1));
        return 0;
 }
 
 static struct string_list guess_refs(const char *ref)
 {
        struct similar_ref_cb ref_cb;
-       struct string_list similar_refs = STRING_LIST_INIT_NODUP;
+       struct string_list similar_refs = STRING_LIST_INIT_DUP;
 
        ref_cb.base_ref = ref;
        ref_cb.similar_refs = &similar_refs;
diff --git a/kwset.c b/kwset.c
index 4fb6455acaf1293c4f47f27eb6e47be4d39633e2..fc439e0667f137f3449635a37a32f8418d5041f0 100644 (file)
--- a/kwset.c
+++ b/kwset.c
 #include "compat/obstack.h"
 
 #define NCHAR (UCHAR_MAX + 1)
-#define obstack_chunk_alloc xmalloc
+/* adapter for `xmalloc()`, which takes `size_t`, not `long` */
+static void *obstack_chunk_alloc(long size)
+{
+       if (size < 0)
+               BUG("Cannot allocate a negative amount: %ld", size);
+       return xmalloc(size);
+}
 #define obstack_chunk_free free
 
 #define U(c) ((unsigned char) (c))
@@ -475,7 +481,7 @@ kwsprep (kwset_t kws)
        for (i = 0; i < NCHAR; ++i)
          kwset->next[i] = next[U(trans[i])];
       else
-       memcpy(kwset->next, next, NCHAR * sizeof(struct trie *));
+       COPY_ARRAY(kwset->next, next, NCHAR);
     }
 
   /* Fix things up for any translation table. */
index a15d0f782923f6bbb0aa9534c80f53c2942e7a79..1cb20c659c82b151a652da0528d0673ac629cc6c 100644 (file)
@@ -91,7 +91,7 @@ static int gently_parse_list_objects_filter(
         */
 
        if (errbuf)
-               strbuf_addf(errbuf, "invalid filter-spec '%s'", arg);
+               strbuf_addf(errbuf, _("invalid filter-spec '%s'"), arg);
 
        memset(filter_options, 0, sizeof(*filter_options));
        return 1;
index 53f90442c5da992808f09f6d764fcefc44d70ee0..36e1f774bcfc50d0475ad835464cec092314eb79 100644 (file)
@@ -356,13 +356,13 @@ static enum list_objects_filter_result filter_sparse(
                                            filename, &dtype, &filter_data->el,
                                            r->index);
                if (val < 0)
-                       val = filter_data->array_frame[filter_data->nr].defval;
+                       val = filter_data->array_frame[filter_data->nr - 1].defval;
 
                ALLOC_GROW(filter_data->array_frame, filter_data->nr + 1,
                           filter_data->alloc);
-               filter_data->nr++;
                filter_data->array_frame[filter_data->nr].defval = val;
                filter_data->array_frame[filter_data->nr].child_prov_omit = 0;
+               filter_data->nr++;
 
                /*
                 * A directory with this tree OID may appear in multiple
@@ -387,16 +387,15 @@ static enum list_objects_filter_result filter_sparse(
 
        case LOFS_END_TREE:
                assert(obj->type == OBJ_TREE);
-               assert(filter_data->nr > 0);
+               assert(filter_data->nr > 1);
 
-               frame = &filter_data->array_frame[filter_data->nr];
-               filter_data->nr--;
+               frame = &filter_data->array_frame[--filter_data->nr];
 
                /*
                 * Tell our parent directory if any of our children were
                 * provisionally omitted.
                 */
-               filter_data->array_frame[filter_data->nr].child_prov_omit |=
+               filter_data->array_frame[filter_data->nr - 1].child_prov_omit |=
                        frame->child_prov_omit;
 
                /*
@@ -412,7 +411,7 @@ static enum list_objects_filter_result filter_sparse(
                assert(obj->type == OBJ_BLOB);
                assert((obj->flags & SEEN) == 0);
 
-               frame = &filter_data->array_frame[filter_data->nr];
+               frame = &filter_data->array_frame[filter_data->nr - 1];
 
                dtype = DT_REG;
                val = is_excluded_from_list(pathname, strlen(pathname),
@@ -453,7 +452,7 @@ static enum list_objects_filter_result filter_sparse(
 static void filter_sparse_free(void *filter_data)
 {
        struct filter_sparse_data *d = filter_data;
-       /* TODO free contents of 'd' */
+       free(d->array_frame);
        free(d);
 }
 
@@ -472,6 +471,7 @@ static void *filter_sparse_oid__init(
        ALLOC_GROW(d->array_frame, d->nr + 1, d->alloc);
        d->array_frame[d->nr].defval = 0; /* default to include */
        d->array_frame[d->nr].child_prov_omit = 0;
+       d->nr++;
 
        *filter_fn = filter_sparse;
        *filter_free_fn = filter_sparse_free;
index 0a7dbc6442fad37fd9e2ce72866b04b134818181..818aef70a09e7cedc2f241f235302e69969b2e3b 100644 (file)
--- a/ls-refs.c
+++ b/ls-refs.c
@@ -57,7 +57,8 @@ static int send_ref(const char *refname, const struct object_id *oid,
                if (!symref_target)
                        die("'%s' is a symref but it is not?", refname);
 
-               strbuf_addf(&refline, " symref-target:%s", symref_target);
+               strbuf_addf(&refline, " symref-target:%s",
+                           strip_namespace(symref_target));
        }
 
        if (data->peel) {
index b4861bc7b02a93bd5f3659098764f535bb1f51c1..695908609f40f9cbb3960162a311af5d452610e2 100644 (file)
@@ -345,8 +345,9 @@ static int handle_range_dir(
        else {
                int begin = k_start;
                int end = k_end;
+               assert(begin >= 0);
                while (begin < end) {
-                       int mid = (begin + end) >> 1;
+                       int mid = begin + ((end - begin) >> 1);
                        int cmp = strncmp(istate->cache[mid]->name, prefix->buf, prefix->len);
                        if (cmp == 0) /* mid has same prefix; look in second part */
                                begin = mid + 1;
index 272e01e452b97bd8598a76d6facc5a6f803b579a..49f56ab8d9608919808ecebe71b9deb30c53980b 100644 (file)
@@ -277,10 +277,14 @@ struct object_info {
 #define OBJECT_INFO_IGNORE_LOOSE 16
 /*
  * Do not attempt to fetch the object if missing (even if fetch_is_missing is
- * nonzero). This is meant for bulk prefetching of missing blobs in a partial
- * clone. Implies OBJECT_INFO_QUICK.
+ * nonzero).
  */
-#define OBJECT_INFO_FOR_PREFETCH (32 + OBJECT_INFO_QUICK)
+#define OBJECT_INFO_SKIP_FETCH_OBJECT 32
+/*
+ * This is meant for bulk prefetching of missing blobs in a partial
+ * clone. Implies OBJECT_INFO_SKIP_FETCH_OBJECT and OBJECT_INFO_QUICK
+ */
+#define OBJECT_INFO_FOR_PREFETCH (OBJECT_INFO_SKIP_FETCH_OBJECT | OBJECT_INFO_QUICK)
 
 int oid_object_info_extended(struct repository *r,
                             const struct object_id *,
index e81d47a79cd6eb42fae7c104be0e9ac1e5173e79..cf1a2b708612a0e335893fadee0952e620400c5f 100644 (file)
--- a/object.c
+++ b/object.c
@@ -517,7 +517,7 @@ void raw_object_store_clear(struct raw_object_store *o)
        o->loaded_alternates = 0;
 
        INIT_LIST_HEAD(&o->packed_git_mru);
-       close_all_packs(o);
+       close_object_store(o);
        o->packed_git = NULL;
 }
 
index 49c8544ff4591e71e1845cd1b275d5fdb898b400..c0d83fdfed973de8574224e46fb0afd4be0a98c9 100644 (file)
@@ -16,6 +16,7 @@
 #include "tree.h"
 #include "object-store.h"
 #include "midx.h"
+#include "commit-graph.h"
 
 char *odb_pack_name(struct strbuf *buf,
                    const unsigned char *sha1,
@@ -336,7 +337,7 @@ void close_pack(struct packed_git *p)
        close_pack_index(p);
 }
 
-void close_all_packs(struct raw_object_store *o)
+void close_object_store(struct raw_object_store *o)
 {
        struct packed_git *p;
 
@@ -350,6 +351,8 @@ void close_all_packs(struct raw_object_store *o)
                close_midx(o->multi_pack_index);
                o->multi_pack_index = NULL;
        }
+
+       close_commit_graph(o);
 }
 
 /*
@@ -640,7 +643,7 @@ unsigned char *use_pack(struct packed_git *p,
                        while (packed_git_limit < pack_mapped
                                && unuse_one_window(p))
                                ; /* nothing */
-                       win->base = xmmap(NULL, win->len,
+                       win->base = xmmap_gently(NULL, win->len,
                                PROT_READ, MAP_PRIVATE,
                                p->pack_fd, win->offset);
                        if (win->base == MAP_FAILED)
@@ -1269,7 +1272,7 @@ static enum object_type packed_to_object_type(struct repository *r,
                if (poi_stack_nr >= poi_stack_alloc && poi_stack == small_poi_stack) {
                        poi_stack_alloc = alloc_nr(poi_stack_nr);
                        ALLOC_ARRAY(poi_stack, poi_stack_alloc);
-                       memcpy(poi_stack, small_poi_stack, sizeof(off_t)*poi_stack_nr);
+                       COPY_ARRAY(poi_stack, small_poi_stack, poi_stack_nr);
                } else {
                        ALLOC_GROW(poi_stack, poi_stack_nr+1, poi_stack_alloc);
                }
@@ -1679,8 +1682,8 @@ void *unpack_entry(struct repository *r, struct packed_git *p, off_t obj_offset,
                    && delta_stack == small_delta_stack) {
                        delta_stack_alloc = alloc_nr(delta_stack_nr);
                        ALLOC_ARRAY(delta_stack, delta_stack_alloc);
-                       memcpy(delta_stack, small_delta_stack,
-                              sizeof(*delta_stack)*delta_stack_nr);
+                       COPY_ARRAY(delta_stack, small_delta_stack,
+                                  delta_stack_nr);
                } else {
                        ALLOC_GROW(delta_stack, delta_stack_nr+1, delta_stack_alloc);
                }
index b678d35c0b6df11623f2d29f7b7ea7dfe0e1bea1..81e868d55a9b1f1aeaafa4925a72ae5c53af86e9 100644 (file)
@@ -90,7 +90,7 @@ uint32_t get_pack_fanout(struct packed_git *p, uint32_t value);
 unsigned char *use_pack(struct packed_git *, struct pack_window **, off_t, unsigned long *);
 void close_pack_windows(struct packed_git *);
 void close_pack(struct packed_git *);
-void close_all_packs(struct raw_object_store *o);
+void close_object_store(struct raw_object_store *o);
 void unuse_pack(struct pack_window **);
 void clear_delta_base_cache(void);
 struct packed_git *add_packed_git(const char *path, size_t path_len, int local);
index c262e1be9c9c912cc12c5b60a08e69f8cf9a30c9..f70d3966542d0d5b9b0e749bbe4b61968a30bcac 100644 (file)
@@ -11,7 +11,7 @@ static int patch_id_defined(struct commit *commit)
 }
 
 int commit_patch_id(struct commit *commit, struct diff_options *options,
-                   struct object_id *oid, int diff_header_only)
+                   struct object_id *oid, int diff_header_only, int stable)
 {
        if (!patch_id_defined(commit))
                return -1;
@@ -22,7 +22,7 @@ int commit_patch_id(struct commit *commit, struct diff_options *options,
        else
                diff_root_tree_oid(&commit->object.oid, "", options);
        diffcore_std(options);
-       return diff_flush_patch_id(options, oid, diff_header_only);
+       return diff_flush_patch_id(options, oid, diff_header_only, stable);
 }
 
 /*
@@ -46,11 +46,11 @@ static int patch_id_neq(const void *cmpfn_data,
        struct patch_id *b = (void *)entry_or_key;
 
        if (is_null_oid(&a->patch_id) &&
-           commit_patch_id(a->commit, opt, &a->patch_id, 0))
+           commit_patch_id(a->commit, opt, &a->patch_id, 0, 0))
                return error("Could not get patch ID for %s",
                        oid_to_hex(&a->commit->object.oid));
        if (is_null_oid(&b->patch_id) &&
-           commit_patch_id(b->commit, opt, &b->patch_id, 0))
+           commit_patch_id(b->commit, opt, &b->patch_id, 0, 0))
                return error("Could not get patch ID for %s",
                        oid_to_hex(&b->commit->object.oid));
        return !oideq(&a->patch_id, &b->patch_id);
@@ -80,7 +80,7 @@ static int init_patch_id_entry(struct patch_id *patch,
        struct object_id header_only_patch_id;
 
        patch->commit = commit;
-       if (commit_patch_id(commit, &ids->diffopts, &header_only_patch_id, 1))
+       if (commit_patch_id(commit, &ids->diffopts, &header_only_patch_id, 1, 0))
                return -1;
 
        hashmap_entry_init(patch, sha1hash(header_only_patch_id.hash));
index 82a12b66f8891e5a9a45c2a63845dd28de7eeda7..03bb04e7071f5f65a3b09f138aa4473fa0a0a655 100644 (file)
@@ -20,7 +20,7 @@ struct patch_ids {
 };
 
 int commit_patch_id(struct commit *commit, struct diff_options *options,
-                   struct object_id *oid, int);
+                   struct object_id *oid, int, int);
 int init_patch_ids(struct repository *, struct patch_ids *);
 int free_patch_ids(struct patch_ids *);
 struct patch_id *add_commit_patch_id(struct commit *, struct patch_ids *);
index e73600ee7841a299e060369ca9eecf46d5a7fb0f..ed6eaa47388af8d33398e7c9652c622030e75147 100644 (file)
@@ -78,7 +78,7 @@ static void *preload_thread(void *_data)
                if (ie_match_stat(index, ce, &st, CE_MATCH_RACY_IS_DIRTY|CE_MATCH_IGNORE_FSMONITOR))
                        continue;
                ce_mark_uptodate(ce);
-               mark_fsmonitor_valid(ce);
+               mark_fsmonitor_valid(index, ce);
        } while (--nr > 0);
        if (p->progress) {
                struct progress_data *pd = p->progress;
index ced0485257d3190f0c3ad8b89f15fa47c8660fad..e4ed14effe1aabdef50155b932a96e6648ff4707 100644 (file)
--- a/pretty.c
+++ b/pretty.c
@@ -106,8 +106,8 @@ static void setup_commit_formats(void)
        commit_formats_len = ARRAY_SIZE(builtin_formats);
        builtin_formats_len = commit_formats_len;
        ALLOC_GROW(commit_formats, commit_formats_len, commit_formats_alloc);
-       memcpy(commit_formats, builtin_formats,
-              sizeof(*builtin_formats)*ARRAY_SIZE(builtin_formats));
+       COPY_ARRAY(commit_formats, builtin_formats,
+                  ARRAY_SIZE(builtin_formats));
 
        git_config(git_pretty_formats_config, NULL);
 }
index 22e7b9944e35d257b144fe06dce2073c91d4819f..c701f7f8b81378104e2732e8bb132144ff82e8f5 100644 (file)
@@ -195,7 +195,7 @@ int match_stat_data(const struct stat_data *sd, struct stat *st)
  * cache, ie the parts that aren't tracked by GIT, and only used
  * to validate the cache.
  */
-void fill_stat_cache_info(struct cache_entry *ce, struct stat *st)
+void fill_stat_cache_info(struct index_state *istate, struct cache_entry *ce, struct stat *st)
 {
        fill_stat_data(&ce->ce_stat_data, st);
 
@@ -204,7 +204,7 @@ void fill_stat_cache_info(struct cache_entry *ce, struct stat *st)
 
        if (S_ISREG(st->st_mode)) {
                ce_mark_uptodate(ce);
-               mark_fsmonitor_valid(ce);
+               mark_fsmonitor_valid(istate, ce);
        }
 }
 
@@ -549,7 +549,7 @@ static int index_name_stage_pos(const struct index_state *istate, const char *na
        first = 0;
        last = istate->cache_nr;
        while (last > first) {
-               int next = (last + first) >> 1;
+               int next = first + ((last - first) >> 1);
                struct cache_entry *ce = istate->cache[next];
                int cmp = cache_name_stage_compare(name, namelen, stage, ce->name, ce_namelen(ce), ce_stage(ce));
                if (!cmp)
@@ -728,7 +728,7 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st,
        memcpy(ce->name, path, namelen);
        ce->ce_namelen = namelen;
        if (!intent_only)
-               fill_stat_cache_info(ce, st);
+               fill_stat_cache_info(istate, ce, st);
        else
                ce->ce_flags |= CE_INTENT_TO_ADD;
 
@@ -1432,7 +1432,7 @@ static struct cache_entry *refresh_cache_ent(struct index_state *istate,
                         */
                        if (!S_ISGITLINK(ce->ce_mode)) {
                                ce_mark_uptodate(ce);
-                               mark_fsmonitor_valid(ce);
+                               mark_fsmonitor_valid(istate, ce);
                        }
                        return ce;
                }
@@ -1447,7 +1447,7 @@ static struct cache_entry *refresh_cache_ent(struct index_state *istate,
        updated = make_empty_cache_entry(istate, ce_namelen(ce));
        copy_cache_entry(updated, ce);
        memcpy(updated->name, ce->name, ce->ce_namelen + 1);
-       fill_stat_cache_info(updated, &st);
+       fill_stat_cache_info(istate, updated, &st);
        /*
         * If ignore_valid is not set, we should leave CE_VALID bit
         * alone.  Otherwise, paths marked with --no-assume-unchanged
@@ -2037,7 +2037,7 @@ static void *load_cache_entries_thread(void *_data)
 }
 
 static unsigned long load_cache_entries_threaded(struct index_state *istate, const char *mmap, size_t mmap_size,
-                       unsigned long src_offset, int nr_threads, struct index_entry_offset_table *ieot)
+                                                int nr_threads, struct index_entry_offset_table *ieot)
 {
        int i, offset, ieot_blocks, ieot_start, err;
        struct load_cache_entries_thread_data *data;
@@ -2198,7 +2198,7 @@ int do_read_index(struct index_state *istate, const char *path, int must_exist)
                ieot = read_ieot_extension(mmap, mmap_size, extension_offset);
 
        if (ieot) {
-               src_offset += load_cache_entries_threaded(istate, mmap, mmap_size, src_offset, nr_threads, ieot);
+               src_offset += load_cache_entries_threaded(istate, mmap, mmap_size, nr_threads, ieot);
                free(ieot);
        } else {
                src_offset += load_all_cache_entries(istate, mmap, mmap_size, src_offset);
index 8500671bc60957432568443de42b548612106f3c..8fb25c1412b1324c10e2b0a518350488383aa669 100644 (file)
@@ -20,6 +20,8 @@
 #include "commit-slab.h"
 #include "commit-graph.h"
 #include "commit-reach.h"
+#include "worktree.h"
+#include "hashmap.h"
 
 static struct ref_msg {
        const char *gone;
@@ -75,6 +77,27 @@ static struct expand_data {
        struct object_info info;
 } oi, oi_deref;
 
+struct ref_to_worktree_entry {
+       struct hashmap_entry ent; /* must be the first member! */
+       struct worktree *wt; /* key is wt->head_ref */
+};
+
+static int ref_to_worktree_map_cmpfnc(const void *unused_lookupdata,
+                                     const void *existing_hashmap_entry_to_test,
+                                     const void *key,
+                                     const void *keydata_aka_refname)
+{
+       const struct ref_to_worktree_entry *e = existing_hashmap_entry_to_test;
+       const struct ref_to_worktree_entry *k = key;
+       return strcmp(e->wt->head_ref,
+               keydata_aka_refname ? keydata_aka_refname : k->wt->head_ref);
+}
+
+static struct ref_to_worktree_map {
+       struct hashmap map;
+       struct worktree **worktrees;
+} ref_to_worktree_map;
+
 /*
  * An atom is a valid field atom listed below, possibly prefixed with
  * a "*" to denote deref_tag().
@@ -480,6 +503,7 @@ static struct {
        { "flag", SOURCE_NONE },
        { "HEAD", SOURCE_NONE, FIELD_STR, head_atom_parser },
        { "color", SOURCE_NONE, FIELD_STR, color_atom_parser },
+       { "worktreepath", SOURCE_NONE },
        { "align", SOURCE_NONE, FIELD_STR, align_atom_parser },
        { "end", SOURCE_NONE },
        { "if", SOURCE_NONE, FIELD_STR, if_atom_parser },
@@ -1531,6 +1555,48 @@ static int get_object(struct ref_array_item *ref, int deref, struct object **obj
        return 0;
 }
 
+static void populate_worktree_map(struct hashmap *map, struct worktree **worktrees)
+{
+       int i;
+
+       for (i = 0; worktrees[i]; i++) {
+               if (worktrees[i]->head_ref) {
+                       struct ref_to_worktree_entry *entry;
+                       entry = xmalloc(sizeof(*entry));
+                       entry->wt = worktrees[i];
+                       hashmap_entry_init(entry, strhash(worktrees[i]->head_ref));
+
+                       hashmap_add(map, entry);
+               }
+       }
+}
+
+static void lazy_init_worktree_map(void)
+{
+       if (ref_to_worktree_map.worktrees)
+               return;
+
+       ref_to_worktree_map.worktrees = get_worktrees(0);
+       hashmap_init(&(ref_to_worktree_map.map), ref_to_worktree_map_cmpfnc, NULL, 0);
+       populate_worktree_map(&(ref_to_worktree_map.map), ref_to_worktree_map.worktrees);
+}
+
+static char *get_worktree_path(const struct used_atom *atom, const struct ref_array_item *ref)
+{
+       struct hashmap_entry entry;
+       struct ref_to_worktree_entry *lookup_result;
+
+       lazy_init_worktree_map();
+
+       hashmap_entry_init(&entry, strhash(ref->refname));
+       lookup_result = hashmap_get(&(ref_to_worktree_map.map), &entry, ref->refname);
+
+       if (lookup_result)
+               return xstrdup(lookup_result->wt->path);
+       else
+               return xstrdup("");
+}
+
 /*
  * Parse the object referred by ref, and grab needed value.
  */
@@ -1568,6 +1634,13 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err)
 
                if (starts_with(name, "refname"))
                        refname = get_refname(atom, ref);
+               else if (!strcmp(name, "worktreepath")) {
+                       if (ref->kind == FILTER_REFS_BRANCHES)
+                               v->s = get_worktree_path(atom, ref);
+                       else
+                               v->s = xstrdup("");
+                       continue;
+               }
                else if (starts_with(name, "symref"))
                        refname = get_symref(atom, ref);
                else if (starts_with(name, "upstream")) {
@@ -2051,6 +2124,11 @@ void ref_array_clear(struct ref_array *array)
                free_array_item(array->items[i]);
        FREE_AND_NULL(array->items);
        array->nr = array->alloc = 0;
+       if (ref_to_worktree_map.worktrees) {
+               hashmap_free(&(ref_to_worktree_map.map), 1);
+               free_worktrees(ref_to_worktree_map.worktrees);
+               ref_to_worktree_map.worktrees = NULL;
+       }
 }
 
 static void do_merge_filter(struct ref_filter_cbdata *ref_cbdata)
diff --git a/refs.c b/refs.c
index 92d1f6dbdd0d313cdf109e02f31a51bb55cfc601..b8a8430c963831d9f761fbd8dad099370286987f 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -63,7 +63,7 @@ static unsigned char refname_disposition[256] = {
  * not legal.  It is legal if it is something reasonable to have under
  * ".git/refs/"; We do not like it if:
  *
- * - any path component of it begins with ".", or
+ * - it begins with ".", or
  * - it has double dots "..", or
  * - it has ASCII control characters, or
  * - it has ":", "?", "[", "\", "^", "~", SP, or TAB anywhere, or
@@ -71,31 +71,63 @@ static unsigned char refname_disposition[256] = {
  * - it ends with a "/", or
  * - it ends with ".lock", or
  * - it contains a "@{" portion
+ *
+ * When sanitized is not NULL, instead of rejecting the input refname
+ * as an error, try to come up with a usable replacement for the input
+ * refname in it.
  */
-static int check_refname_component(const char *refname, int *flags)
+static int check_refname_component(const char *refname, int *flags,
+                                  struct strbuf *sanitized)
 {
        const char *cp;
        char last = '\0';
+       size_t component_start = 0; /* garbage - not a reasonable initial value */
+
+       if (sanitized)
+               component_start = sanitized->len;
 
        for (cp = refname; ; cp++) {
                int ch = *cp & 255;
                unsigned char disp = refname_disposition[ch];
+
+               if (sanitized && disp != 1)
+                       strbuf_addch(sanitized, ch);
+
                switch (disp) {
                case 1:
                        goto out;
                case 2:
-                       if (last == '.')
-                               return -1; /* Refname contains "..". */
+                       if (last == '.') { /* Refname contains "..". */
+                               if (sanitized)
+                                       /* collapse ".." to single "." */
+                                       strbuf_setlen(sanitized, sanitized->len - 1);
+                               else
+                                       return -1;
+                       }
                        break;
                case 3:
-                       if (last == '@')
-                               return -1; /* Refname contains "@{". */
+                       if (last == '@') { /* Refname contains "@{". */
+                               if (sanitized)
+                                       sanitized->buf[sanitized->len-1] = '-';
+                               else
+                                       return -1;
+                       }
                        break;
                case 4:
-                       return -1;
+                       /* forbidden char */
+                       if (sanitized)
+                               sanitized->buf[sanitized->len-1] = '-';
+                       else
+                               return -1;
+                       break;
                case 5:
-                       if (!(*flags & REFNAME_REFSPEC_PATTERN))
-                               return -1; /* refspec can't be a pattern */
+                       if (!(*flags & REFNAME_REFSPEC_PATTERN)) {
+                               /* refspec can't be a pattern */
+                               if (sanitized)
+                                       sanitized->buf[sanitized->len-1] = '-';
+                               else
+                                       return -1;
+                       }
 
                        /*
                         * Unset the pattern flag so that we only accept
@@ -109,26 +141,48 @@ static int check_refname_component(const char *refname, int *flags)
 out:
        if (cp == refname)
                return 0; /* Component has zero length. */
-       if (refname[0] == '.')
-               return -1; /* Component starts with '.'. */
+
+       if (refname[0] == '.') { /* Component starts with '.'. */
+               if (sanitized)
+                       sanitized->buf[component_start] = '-';
+               else
+                       return -1;
+       }
        if (cp - refname >= LOCK_SUFFIX_LEN &&
-           !memcmp(cp - LOCK_SUFFIX_LEN, LOCK_SUFFIX, LOCK_SUFFIX_LEN))
-               return -1; /* Refname ends with ".lock". */
+           !memcmp(cp - LOCK_SUFFIX_LEN, LOCK_SUFFIX, LOCK_SUFFIX_LEN)) {
+               if (!sanitized)
+                       return -1;
+               /* Refname ends with ".lock". */
+               while (strbuf_strip_suffix(sanitized, LOCK_SUFFIX)) {
+                       /* try again in case we have .lock.lock */
+               }
+       }
        return cp - refname;
 }
 
-int check_refname_format(const char *refname, int flags)
+static int check_or_sanitize_refname(const char *refname, int flags,
+                                    struct strbuf *sanitized)
 {
        int component_len, component_count = 0;
 
-       if (!strcmp(refname, "@"))
+       if (!strcmp(refname, "@")) {
                /* Refname is a single character '@'. */
-               return -1;
+               if (sanitized)
+                       strbuf_addch(sanitized, '-');
+               else
+                       return -1;
+       }
 
        while (1) {
+               if (sanitized && sanitized->len)
+                       strbuf_complete(sanitized, '/');
+
                /* We are at the start of a path component. */
-               component_len = check_refname_component(refname, &flags);
-               if (component_len <= 0)
+               component_len = check_refname_component(refname, &flags,
+                                                       sanitized);
+               if (sanitized && component_len == 0)
+                       ; /* OK, omit empty component */
+               else if (component_len <= 0)
                        return -1;
 
                component_count++;
@@ -138,13 +192,29 @@ int check_refname_format(const char *refname, int flags)
                refname += component_len + 1;
        }
 
-       if (refname[component_len - 1] == '.')
-               return -1; /* Refname ends with '.'. */
+       if (refname[component_len - 1] == '.') {
+               /* Refname ends with '.'. */
+               if (sanitized)
+                       ; /* omit ending dot */
+               else
+                       return -1;
+       }
        if (!(flags & REFNAME_ALLOW_ONELEVEL) && component_count < 2)
                return -1; /* Refname has only one component. */
        return 0;
 }
 
+int check_refname_format(const char *refname, int flags)
+{
+       return check_or_sanitize_refname(refname, flags, NULL);
+}
+
+void sanitize_refname_component(const char *refname, struct strbuf *out)
+{
+       if (check_or_sanitize_refname(refname, REFNAME_ALLOW_ONELEVEL, out))
+               BUG("sanitizing refname '%s' check returned error", refname);
+}
+
 int refname_is_safe(const char *refname)
 {
        const char *rest;
diff --git a/refs.h b/refs.h
index 2727405b61c4e26538c3e76b528df37078337376..730d05ad91a6ac8961c58d599942a5721960d7d7 100644 (file)
--- a/refs.h
+++ b/refs.h
@@ -463,6 +463,12 @@ int for_each_reflog(each_ref_fn fn, void *cb_data);
  */
 int check_refname_format(const char *refname, int flags);
 
+/*
+ * Apply the rules from check_refname_format, but mutate the result until it
+ * is acceptable, and place the result in "out".
+ */
+void sanitize_refname_component(const char *refname, struct strbuf *out);
+
 const char *prettify_refname(const char *refname);
 
 char *refs_shorten_unambiguous_ref(struct ref_store *refs,
index d4aaf0ef257943029923f741b4ae383bb00d7f5f..621feb9df716400f32d016e1d36fc368b6a884fb 100644 (file)
@@ -436,7 +436,9 @@ static struct commit *handle_commit(struct rev_info *revs,
                        die("unable to parse commit %s", name);
                if (flags & UNINTERESTING) {
                        mark_parents_uninteresting(commit);
-                       revs->limited = 1;
+
+                       if (!revs->topo_order || !generation_numbers_enabled(the_repository))
+                               revs->limited = 1;
                }
                if (revs->sources) {
                        char **slot = revision_sources_at(revs->sources, commit);
@@ -3263,6 +3265,9 @@ static void expand_topo_walk(struct rev_info *revs, struct commit *commit)
                struct commit *parent = p->item;
                int *pi;
 
+               if (parent->object.flags & UNINTERESTING)
+                       continue;
+
                if (parse_commit_gently(parent, 1) < 0)
                        continue;
 
index f88a97fb10a322c21062ec8c102482677cfd0feb..4a9b7f1c3fc45dd16e08ef0d4cd6abdfa1e69747 100644 (file)
@@ -279,7 +279,7 @@ static const char *gpg_sign_opt_quoted(struct replay_opts *opts)
 int sequencer_remove_state(struct replay_opts *opts)
 {
        struct strbuf buf = STRBUF_INIT;
-       int i;
+       int i, ret = 0;
 
        if (is_rebase_i(opts) &&
            strbuf_read_file(&buf, rebase_path_refs_to_delete(), 0) > 0) {
@@ -288,8 +288,10 @@ int sequencer_remove_state(struct replay_opts *opts)
                        char *eol = strchr(p, '\n');
                        if (eol)
                                *eol = '\0';
-                       if (delete_ref("(rebase -i) cleanup", p, NULL, 0) < 0)
+                       if (delete_ref("(rebase -i) cleanup", p, NULL, 0) < 0) {
                                warning(_("could not delete '%s'"), p);
+                               ret = -1;
+                       }
                        if (!eol)
                                break;
                        p = eol + 1;
@@ -305,10 +307,11 @@ int sequencer_remove_state(struct replay_opts *opts)
 
        strbuf_reset(&buf);
        strbuf_addstr(&buf, get_dir(opts));
-       remove_dir_recursively(&buf, 0);
+       if (remove_dir_recursively(&buf, 0))
+               ret = error(_("could not remove '%s'"), buf.buf);
        strbuf_release(&buf);
 
-       return 0;
+       return ret;
 }
 
 static const char *action_name(const struct replay_opts *opts)
@@ -767,7 +770,7 @@ static int parse_key_value_squoted(char *buf, struct string_list *list)
  *     GIT_AUTHOR_DATE='$author_date'
  *
  * where $author_name, $author_email and $author_date are quoted. We are strict
- * with our parsing, as the file was meant to be eval'd in the old
+ * with our parsing, as the file was meant to be eval'd in the now-removed
  * git-am.sh/git-rebase--interactive.sh scripts, and thus if the file differs
  * from what this function expects, it is better to bail out than to do
  * something that the user does not expect.
@@ -3401,6 +3404,10 @@ static int do_merge(struct repository *r,
                rollback_lock_file(&lock);
                ret = fast_forward_to(r, &commit->object.oid,
                                      &head_commit->object.oid, 0, opts);
+               if (flags & TODO_EDIT_MERGE_MSG) {
+                       run_commit_flags |= AMEND_MSG;
+                       goto fast_forward_edit;
+               }
                goto leave_merge;
        }
 
@@ -3504,6 +3511,7 @@ static int do_merge(struct repository *r,
                 * value (a negative one would indicate that the `merge`
                 * command needs to be rescheduled).
                 */
+       fast_forward_edit:
                ret = !!run_git_commit(r, git_path_merge_msg(r), opts,
                                       run_commit_flags);
 
index 41274d098bc3b07b7f0130e7b9efcc20873cf46d..4d8199b1d916f70f136b767132916fdebb73318e 100644 (file)
@@ -1,4 +1,5 @@
 #include "cache.h"
+#include "dir.h"
 #include "repository.h"
 #include "refs.h"
 #include "object.h"
 #include "tag.h"
 #include "packfile.h"
 #include "object-store.h"
+#include "strbuf.h"
+
+struct update_info_ctx {
+       FILE *cur_fp;
+       FILE *old_fp; /* becomes NULL if it differs from cur_fp */
+       struct strbuf cur_sb;
+       struct strbuf old_sb;
+};
+
+static void uic_mark_stale(struct update_info_ctx *uic)
+{
+       fclose(uic->old_fp);
+       uic->old_fp = NULL;
+}
+
+static int uic_is_stale(const struct update_info_ctx *uic)
+{
+       return uic->old_fp == NULL;
+}
+
+static int uic_printf(struct update_info_ctx *uic, const char *fmt, ...)
+{
+       va_list ap;
+       int ret = -1;
+
+       va_start(ap, fmt);
+
+       if (uic_is_stale(uic)) {
+               ret = vfprintf(uic->cur_fp, fmt, ap);
+       } else {
+               ssize_t r;
+               struct strbuf *cur = &uic->cur_sb;
+               struct strbuf *old = &uic->old_sb;
+
+               strbuf_reset(cur);
+               strbuf_vinsertf(cur, 0, fmt, ap);
+
+               strbuf_reset(old);
+               strbuf_grow(old, cur->len);
+               r = fread(old->buf, 1, cur->len, uic->old_fp);
+               if (r != cur->len || memcmp(old->buf, cur->buf, r))
+                       uic_mark_stale(uic);
+
+               if (fwrite(cur->buf, 1, cur->len, uic->cur_fp) == cur->len)
+                       ret = 0;
+       }
+
+       va_end(ap);
+
+       return ret;
+}
 
 /*
  * Create the file "path" by writing to a temporary file and renaming
  * it into place. The contents of the file come from "generate", which
  * should return non-zero if it encounters an error.
  */
-static int update_info_file(char *path, int (*generate)(FILE *))
+static int update_info_file(char *path,
+                       int (*generate)(struct update_info_ctx *),
+                       int force)
 {
        char *tmp = mkpathdup("%s_XXXXXX", path);
        int ret = -1;
        int fd = -1;
-       FILE *fp = NULL, *to_close;
+       FILE *to_close;
+       struct update_info_ctx uic = {
+               .cur_fp = NULL,
+               .old_fp = NULL,
+               .cur_sb = STRBUF_INIT,
+               .old_sb = STRBUF_INIT
+       };
 
        safe_create_leading_directories(path);
        fd = git_mkstemp_mode(tmp, 0666);
        if (fd < 0)
                goto out;
-       to_close = fp = fdopen(fd, "w");
-       if (!fp)
+       to_close = uic.cur_fp = fdopen(fd, "w");
+       if (!uic.cur_fp)
                goto out;
        fd = -1;
-       ret = generate(fp);
+
+       /* no problem on ENOENT and old_fp == NULL, it's stale, now */
+       if (!force)
+               uic.old_fp = fopen_or_warn(path, "r");
+
+       /*
+        * uic_printf will compare incremental comparison aginst old_fp
+        * and mark uic as stale if needed
+        */
+       ret = generate(&uic);
        if (ret)
                goto out;
-       fp = NULL;
+
+       /* new file may be shorter than the old one, check here */
+       if (!uic_is_stale(&uic)) {
+               struct stat st;
+               long new_len = ftell(uic.cur_fp);
+               int old_fd = fileno(uic.old_fp);
+
+               if (new_len < 0) {
+                       ret = -1;
+                       goto out;
+               }
+               if (fstat(old_fd, &st) || (st.st_size != (size_t)new_len))
+                       uic_mark_stale(&uic);
+       }
+
+       uic.cur_fp = NULL;
        if (fclose(to_close))
                goto out;
-       if (adjust_shared_perm(tmp) < 0)
-               goto out;
-       if (rename(tmp, path) < 0)
-               goto out;
+
+       if (uic_is_stale(&uic)) {
+               if (adjust_shared_perm(tmp) < 0)
+                       goto out;
+               if (rename(tmp, path) < 0)
+                       goto out;
+       } else {
+               unlink(tmp);
+       }
        ret = 0;
 
 out:
        if (ret) {
                error_errno("unable to update %s", path);
-               if (fp)
-                       fclose(fp);
+               if (uic.cur_fp)
+                       fclose(uic.cur_fp);
                else if (fd >= 0)
                        close(fd);
                unlink(tmp);
        }
        free(tmp);
+       if (uic.old_fp)
+               fclose(uic.old_fp);
+       strbuf_release(&uic.old_sb);
+       strbuf_release(&uic.cur_sb);
        return ret;
 }
 
 static int add_info_ref(const char *path, const struct object_id *oid,
                        int flag, void *cb_data)
 {
-       FILE *fp = cb_data;
+       struct update_info_ctx *uic = cb_data;
        struct object *o = parse_object(the_repository, oid);
        if (!o)
                return -1;
 
-       if (fprintf(fp, "%s     %s\n", oid_to_hex(oid), path) < 0)
+       if (uic_printf(uic, "%s %s\n", oid_to_hex(oid), path) < 0)
                return -1;
 
        if (o->type == OBJ_TAG) {
                o = deref_tag(the_repository, o, path, 0);
                if (o)
-                       if (fprintf(fp, "%s     %s^{}\n",
+                       if (uic_printf(uic, "%s %s^{}\n",
                                oid_to_hex(&o->oid), path) < 0)
                                return -1;
        }
        return 0;
 }
 
-static int generate_info_refs(FILE *fp)
+static int generate_info_refs(struct update_info_ctx *uic)
 {
-       return for_each_ref(add_info_ref, fp);
+       return for_each_ref(add_info_ref, uic);
 }
 
-static int update_info_refs(void)
+static int update_info_refs(int force)
 {
        char *path = git_pathdup("info/refs");
-       int ret = update_info_file(path, generate_info_refs);
+       int ret = update_info_file(path, generate_info_refs, force);
        free(path);
        return ret;
 }
@@ -191,26 +284,21 @@ static void init_pack_info(const char *infofile, int force)
 {
        struct packed_git *p;
        int stale;
-       int i = 0;
+       int i;
+       size_t alloc = 0;
 
        for (p = get_all_packs(the_repository); p; p = p->next) {
                /* we ignore things on alternate path since they are
                 * not available to the pullers in general.
                 */
-               if (!p->pack_local)
-                       continue;
-               i++;
-       }
-       num_pack = i;
-       info = xcalloc(num_pack, sizeof(struct pack_info *));
-       for (i = 0, p = get_all_packs(the_repository); p; p = p->next) {
-               if (!p->pack_local)
+               if (!p->pack_local || !file_exists(p->pack_name))
                        continue;
-               assert(i < num_pack);
+
+               i = num_pack++;
+               ALLOC_GROW(info, num_pack, alloc);
                info[i] = xcalloc(1, sizeof(struct pack_info));
                info[i]->p = p;
                info[i]->old_num = -1;
-               i++;
        }
 
        if (infofile && !force)
@@ -236,14 +324,14 @@ static void free_pack_info(void)
        free(info);
 }
 
-static int write_pack_info_file(FILE *fp)
+static int write_pack_info_file(struct update_info_ctx *uic)
 {
        int i;
        for (i = 0; i < num_pack; i++) {
-               if (fprintf(fp, "P %s\n", pack_basename(info[i]->p)) < 0)
+               if (uic_printf(uic, "P %s\n", pack_basename(info[i]->p)) < 0)
                        return -1;
        }
-       if (fputc('\n', fp) == EOF)
+       if (uic_printf(uic, "\n") < 0)
                return -1;
        return 0;
 }
@@ -254,7 +342,7 @@ static int update_info_packs(int force)
        int ret;
 
        init_pack_info(infofile, force);
-       ret = update_info_file(infofile, write_pack_info_file);
+       ret = update_info_file(infofile, write_pack_info_file, force);
        free_pack_info();
        free(infofile);
        return ret;
@@ -269,7 +357,7 @@ int update_server_info(int force)
         */
        int errs = 0;
 
-       errs = errs | update_info_refs();
+       errs = errs | update_info_refs(force);
        errs = errs | update_info_packs(force);
 
        /* remove leftover rev-cache file if there is any */
index cecfdd36c7e696dce6188aea3398fbe8857d798b..e7430b9aa8ecf136a7d625ee059822e1e1a74b26 100644 (file)
@@ -249,7 +249,7 @@ sorted_string_list_member (const string_list_ty *slp, const char *s)
        {
          /* Here we know that if s is in the list, it is at an index j
             with j1 <= j < j2.  */
-         size_t j = (j1 + j2) >> 1;
+         size_t j = j1 + ((j2 - j1) >> 1);
          int result = strcmp (slp->item[j], s);
 
          if (result > 0)
index ed5c50dac427f2f3aa2b9b7b203bf2355049b163..888b6024d5de050753765e37b65ea0521d91f9fd 100644 (file)
@@ -1379,7 +1379,7 @@ int oid_object_info_extended(struct repository *r, const struct object_id *oid,
                /* Check if it is a missing object */
                if (fetch_if_missing && repository_format_partial_clone &&
                    !already_retried && r == the_repository &&
-                   !(flags & OBJECT_INFO_FOR_PREFETCH)) {
+                   !(flags & OBJECT_INFO_SKIP_FETCH_OBJECT)) {
                        /*
                         * TODO Investigate having fetch_object() return
                         * TODO error/success and stopping the music here.
index 2cfaba059993e356e11eeabfaf5f67e9e26a3519..0f199c51378dc93458904abcaca4f58875952313 100644 (file)
@@ -1910,7 +1910,7 @@ int submodule_move_head(const char *path,
        if (!(flags & SUBMODULE_MOVE_HEAD_DRY_RUN)) {
                if (old_head) {
                        if (!submodule_uses_gitfile(path))
-                               absorb_git_dir_into_superproject("", path,
+                               absorb_git_dir_into_superproject(path,
                                        ABSORB_GITDIR_RECURSE_SUBMODULES);
                } else {
                        char *gitdir = xstrfmt("%s/modules/%s",
@@ -1997,8 +1997,7 @@ int submodule_move_head(const char *path,
  * Embeds a single submodules git directory into the superprojects git dir,
  * non recursively.
  */
-static void relocate_single_git_dir_into_superproject(const char *prefix,
-                                                     const char *path)
+static void relocate_single_git_dir_into_superproject(const char *path)
 {
        char *old_git_dir = NULL, *real_old_git_dir = NULL, *real_new_git_dir = NULL;
        const char *new_git_dir;
@@ -2040,8 +2039,7 @@ static void relocate_single_git_dir_into_superproject(const char *prefix,
  * having its git directory within the working tree to the git dir nested
  * in its superprojects git dir under modules/.
  */
-void absorb_git_dir_into_superproject(const char *prefix,
-                                     const char *path,
+void absorb_git_dir_into_superproject(const char *path,
                                      unsigned flags)
 {
        int err_code;
@@ -2082,7 +2080,7 @@ void absorb_git_dir_into_superproject(const char *prefix,
                char *real_common_git_dir = real_pathdup(get_git_common_dir(), 1);
 
                if (!starts_with(real_sub_git_dir, real_common_git_dir))
-                       relocate_single_git_dir_into_superproject(prefix, path);
+                       relocate_single_git_dir_into_superproject(path);
 
                free(real_sub_git_dir);
                free(real_common_git_dir);
index 9e18e9b80760ad1562682125bc90d08fb6e1c0d3..8072e6d6ddfd847ca8f739e9d8af9b815c43e21d 100644 (file)
@@ -141,8 +141,7 @@ void submodule_unset_core_worktree(const struct submodule *sub);
 void prepare_submodule_repo_env(struct argv_array *out);
 
 #define ABSORB_GITDIR_RECURSE_SUBMODULES (1<<0)
-void absorb_git_dir_into_superproject(const char *prefix,
-                                     const char *path,
+void absorb_git_dir_into_superproject(const char *path,
                                      unsigned flags);
 
 /*
index 6404f33e19421c47999518138ed6f46a066921c0..9747971d58e1a5efaa85a8ce00772c82d7c5aba7 100644 (file)
--- a/t/README
+++ b/t/README
@@ -334,6 +334,15 @@ that cannot be easily covered by a few specific test cases. These
 could be enabled by running the test suite with correct GIT_TEST_
 environment set.
 
+GIT_TEST_FAIL_PREREQS<non-empty?> fails all prerequisites. This is
+useful for discovering issues with the tests where say a later test
+implicitly depends on an optional earlier test.
+
+There's a "FAIL_PREREQS" prerequisite that can be used to test for
+whether this mode is active, and e.g. skip some tests that are hard to
+refactor to deal with it. The "SYMLINKS" prerequisite is currently
+excluded as so much relies on it, but this might change in the future.
+
 GIT_TEST_GETTEXT_POISON=<non-empty?> turns all strings marked for
 translation into gibberish if non-empty (think "test -n"). Used for
 spotting those tests that need to be marked with a C_LOCALE_OUTPUT
index c03054c538a0f9220cb268ed4fa24ae963d06e84..31de7e90f3b38d304e952bee68119a0100bbbfb6 100755 (executable)
@@ -726,7 +726,7 @@ donthaveit=yes
 test_expect_success DONTHAVEIT 'unmet prerequisite causes test to be skipped' '
        donthaveit=no
 '
-if test $haveit$donthaveit != yesyes
+if test -z "$GIT_TEST_FAIL_PREREQS" -a $haveit$donthaveit != yesyes
 then
        say "bug in test framework: prerequisite tags do not work reliably"
        exit 1
@@ -747,7 +747,7 @@ donthaveiteither=yes
 test_expect_success DONTHAVEIT,HAVEIT 'unmet prerequisites causes test to be skipped' '
        donthaveiteither=no
 '
-if test $haveit$donthaveit$donthaveiteither != yesyesyes
+if test -z "$GIT_TEST_FAIL_PREREQS" -a $haveit$donthaveit$donthaveiteither != yesyesyes
 then
        say "bug in test framework: multiple prerequisite tags do not work reliably"
        exit 1
@@ -763,7 +763,7 @@ test_expect_success !LAZY_TRUE 'missing lazy prereqs skip tests' '
        donthavetrue=no
 '
 
-if test "$havetrue$donthavetrue" != yesyes
+if test -z "$GIT_TEST_FAIL_PREREQS" -a "$havetrue$donthavetrue" != yesyes
 then
        say 'bug in test framework: lazy prerequisites do not work'
        exit 1
@@ -779,7 +779,7 @@ test_expect_success LAZY_FALSE 'missing negative lazy prereqs will skip' '
        havefalse=no
 '
 
-if test "$nothavefalse$havefalse" != yesyes
+if test -z "$GIT_TEST_FAIL_PREREQS" -a "$nothavefalse$havefalse" != yesyes
 then
        say 'bug in test framework: negative lazy prerequisites do not work'
        exit 1
@@ -790,7 +790,7 @@ test_expect_success 'tests clean up after themselves' '
        test_when_finished clean=yes
 '
 
-if test $clean != yes
+if test -z "$GIT_TEST_FAIL_PREREQS" -a $clean != yes
 then
        say "bug in test framework: basic cleanup command does not work reliably"
        exit 1
index 77a224aafb8c63a237f868bff23e6d3cd418bb44..0276d14a0ba66d4af6ad7af14c6b7e98305a1b84 100755 (executable)
@@ -175,7 +175,7 @@ test_expect_success 'reinit' '
 test_expect_success 'init with --template' '
        mkdir template-source &&
        echo content >template-source/file &&
-       git init --template=../template-source template-custom &&
+       git init --template=template-source template-custom &&
        test_cmp template-source/file template-custom/.git/file
 '
 
index dfece751b572821464c390ee68f26d91624e2a33..2dc853d1be5f0fe012d40b5d020fcbfd7638ccb7 100755 (executable)
@@ -136,7 +136,7 @@ test_expect_success POSIXPERM 'forced modes' '
        (
                cd new &&
                umask 002 &&
-               git init --shared=0660 --template=../templates &&
+               git init --shared=0660 --template=templates &&
                >frotz &&
                git add frotz &&
                git commit -a -m initial &&
@@ -192,7 +192,7 @@ test_expect_success POSIXPERM 're-init respects core.sharedrepository (remote)'
        umask 0022 &&
        git init --bare --shared=0666 child.git &&
        test_path_is_missing child.git/foo &&
-       git init --bare --template=../templates child.git &&
+       git init --bare --template=templates child.git &&
        echo "-rw-rw-rw-" >expect &&
        test_modebits child.git/foo >actual &&
        test_cmp expect actual
@@ -203,7 +203,7 @@ test_expect_success POSIXPERM 'template can set core.sharedrepository' '
        umask 0022 &&
        git config core.sharedrepository 0666 &&
        cp .git/config templates/config &&
-       git init --bare --template=../templates child.git &&
+       git init --bare --template=templates child.git &&
        echo "-rw-rw-rw-" >expect &&
        test_modebits child.git/HEAD >actual &&
        test_cmp expect actual
index 579a86b7f8f6f876434bc1107e051304bc45afc6..9571e366f801ea347845eef9736c92197fbdba08 100755 (executable)
@@ -309,6 +309,45 @@ test_expect_success SYMLINKS 'conditional include, gitdir matching symlink, icas
        )
 '
 
+test_expect_success 'conditional include, onbranch' '
+       echo "[includeIf \"onbranch:foo-branch\"]path=bar9" >>.git/config &&
+       echo "[test]nine=9" >.git/bar9 &&
+       git checkout -b master &&
+       test_must_fail git config test.nine &&
+       git checkout -b foo-branch &&
+       echo 9 >expect &&
+       git config test.nine >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'conditional include, onbranch, wildcard' '
+       echo "[includeIf \"onbranch:?oo-*/**\"]path=bar10" >>.git/config &&
+       echo "[test]ten=10" >.git/bar10 &&
+       git checkout -b not-foo-branch/a &&
+       test_must_fail git config test.ten &&
+
+       echo 10 >expect &&
+       git checkout -b foo-branch/a/b/c &&
+       git config test.ten >actual &&
+       test_cmp expect actual &&
+
+       git checkout -b moo-bar/a &&
+       git config test.ten >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'conditional include, onbranch, implicit /** for /' '
+       echo "[includeIf \"onbranch:foo-dir/\"]path=bar11" >>.git/config &&
+       echo "[test]eleven=11" >.git/bar11 &&
+       git checkout -b not-foo-dir/a &&
+       test_must_fail git config test.eleven &&
+
+       echo 11 >expect &&
+       git checkout -b foo-dir/a/b/c &&
+       git config test.eleven >actual &&
+       test_cmp expect actual
+'
+
 test_expect_success 'include cycles are detected' '
        cat >.gitconfig <<-\EOF &&
        [test]value = gitconfig
index 286bba35d8ae06f97cbf59263cd472975036deaa..e819ba741ec96018755a08ef956998edfdd1dcc4 100755 (executable)
@@ -570,4 +570,21 @@ test_expect_success '"add" an existing locked but missing worktree' '
        git worktree add --force --force --detach gnoo
 '
 
+test_expect_success FUNNYNAMES 'sanitize generated worktree name' '
+       git worktree add --detach ".  weird*..?.lock.lock" &&
+       test -d .git/worktrees/---weird-.-
+'
+
+test_expect_success '"add" should not fail because of another bad worktree' '
+       git init add-fail &&
+       (
+               cd add-fail &&
+               test_commit first &&
+               mkdir sub &&
+               git worktree add sub/to-be-deleted &&
+               rm -rf sub &&
+               git worktree add second
+       )
+'
+
 test_done
index e9d7084d19c9d650f43f97d1c389c7fbc4cc51d2..411a70b0ce966f196b516053f2b1ea35ad03bef2 100755 (executable)
@@ -206,18 +206,22 @@ test_expect_success 'git branch -M baz bam should succeed when baz is checked ou
        git worktree add -f bazdir2 baz &&
        git branch -M baz bam &&
        test $(git -C bazdir rev-parse --abbrev-ref HEAD) = bam &&
-       test $(git -C bazdir2 rev-parse --abbrev-ref HEAD) = bam
+       test $(git -C bazdir2 rev-parse --abbrev-ref HEAD) = bam &&
+       rm -r bazdir bazdir2 &&
+       git worktree prune
 '
 
 test_expect_success 'git branch -M baz bam should succeed within a worktree in which baz is checked out' '
        git checkout -b baz &&
-       git worktree add -f bazdir3 baz &&
+       git worktree add -f bazdir baz &&
        (
-               cd bazdir3 &&
+               cd bazdir &&
                git branch -M baz bam &&
                test $(git rev-parse --abbrev-ref HEAD) = bam
        ) &&
-       test $(git rev-parse --abbrev-ref HEAD) = bam
+       test $(git rev-parse --abbrev-ref HEAD) = bam &&
+       rm -r bazdir &&
+       git worktree prune
 '
 
 test_expect_success 'git branch -M master should work when master is checked out' '
@@ -804,7 +808,9 @@ test_expect_success 'test deleting branch without config' '
 test_expect_success 'deleting currently checked out branch fails' '
        git worktree add -b my7 my7 &&
        test_must_fail git -C my7 branch -d my7 &&
-       test_must_fail git branch -d my7
+       test_must_fail git branch -d my7 &&
+       rm -r my7 &&
+       git worktree prune
 '
 
 test_expect_success 'test --track without .fetch entries' '
index be5514893030313bbe08b9041b9e3d85f7fbde2b..71818b90f00d3727cb00e24da181fc9dec420f08 100755 (executable)
@@ -136,10 +136,13 @@ test_expect_success 'git branch `--show-current` works properly with worktrees'
        branch-two
        EOF
        git checkout branch-one &&
-       git worktree add worktree branch-two &&
+       test_when_finished "
+               git worktree remove worktree_dir
+       " &&
+       git worktree add worktree_dir branch-two &&
        {
                git branch --show-current &&
-               git -C worktree branch --show-current
+               git -C worktree_dir branch --show-current
        } >actual &&
        test_cmp expect actual
 '
@@ -284,6 +287,24 @@ test_expect_success 'git branch --format option' '
        test_i18ncmp expect actual
 '
 
+test_expect_success 'worktree colors correct' '
+       cat >expect <<-EOF &&
+       * <GREEN>(HEAD detached from fromtag)<RESET>
+         ambiguous<RESET>
+         branch-one<RESET>
+       + <CYAN>branch-two<RESET>
+         master<RESET>
+         ref-to-branch<RESET> -> branch-one
+         ref-to-remote<RESET> -> origin/branch-one
+       EOF
+       git worktree add worktree_dir branch-two &&
+       git branch --color >actual.raw &&
+       rm -r worktree_dir &&
+       git worktree prune &&
+       test_decode_color <actual.raw >actual &&
+       test_i18ncmp expect actual
+'
+
 test_expect_success "set up color tests" '
        echo "<RED>master<RESET>" >expect.color &&
        echo "master" >expect.bare &&
@@ -308,4 +329,23 @@ test_expect_success '--color overrides auto-color' '
        test_cmp expect.color actual
 '
 
+test_expect_success 'verbose output lists worktree path' '
+       one=$(git rev-parse --short HEAD) &&
+       two=$(git rev-parse --short master) &&
+       cat >expect <<-EOF &&
+       * (HEAD detached from fromtag) $one one
+         ambiguous                    $one one
+         branch-one                   $two two
+       + branch-two                   $one ($(pwd)/worktree_dir) one
+         master                       $two two
+         ref-to-branch                $two two
+         ref-to-remote                $two two
+       EOF
+       git worktree add worktree_dir branch-two &&
+       git branch -vv >actual &&
+       rm -r worktree_dir &&
+       git worktree prune &&
+       test_i18ncmp expect actual
+'
+
 test_done
index 42f147858d7c2e87d6920cdd77744e99bf77001b..80b23fd3269c7828660469207728f67087c82467 100755 (executable)
@@ -285,7 +285,7 @@ EOF
        test_cmp From_.msg out
 '
 
-test_expect_success 'rebase--am.sh and --show-current-patch' '
+test_expect_success 'rebase --am and --show-current-patch' '
        test_create_repo conflict-apply &&
        (
                cd conflict-apply &&
index 1723e1a858585d9e83d7662326a6f337db7e1fa6..46d971b4efe647d48cb8c771b07e794065992b0b 100755 (executable)
@@ -1031,7 +1031,7 @@ test_expect_success 'rebase -i --root reword root commit' '
        test -z "$(git show -s --format=%p HEAD^)"
 '
 
-test_expect_success 'rebase -i --root when root has untracked file confilct' '
+test_expect_success 'rebase -i --root when root has untracked file conflict' '
        test_when_finished "reset_rebase" &&
        git checkout -b failing-root-pick A &&
        echo x >file2 &&
index 42ba5b9f0981b2b7ce98e1c5c55012ef61757bab..7b6c4847ad6b0993bc192f53dec4671edfdf7f91 100755 (executable)
@@ -164,6 +164,19 @@ test_expect_success 'failed `merge <branch>` does not crash' '
        grep "^Merge branch ${SQ}G${SQ}$" .git/rebase-merge/message
 '
 
+test_expect_success 'fast-forward merge -c still rewords' '
+       git checkout -b fast-forward-merge-c H &&
+       (
+               set_fake_editor &&
+               FAKE_COMMIT_MESSAGE=edited \
+                       GIT_SEQUENCE_EDITOR="echo merge -c H G >" \
+                       git rebase -ir @^
+       ) &&
+       echo edited >expected &&
+       git log --pretty=format:%B -1 >actual &&
+       test_cmp expected actual
+'
+
 test_expect_success 'with a branch tip that was cherry-picked already' '
        git checkout -b already-upstream master &&
        base="$(git rev-parse --verify HEAD)" &&
@@ -224,8 +237,24 @@ test_expect_success 'refs/rewritten/* is worktree-local' '
        test_cmp_rev HEAD "$(cat wt/b)"
 '
 
+test_expect_success '--abort cleans up refs/rewritten' '
+       git checkout -b abort-cleans-refs-rewritten H &&
+       GIT_SEQUENCE_EDITOR="echo break >>" git rebase -ir @^ &&
+       git rev-parse --verify refs/rewritten/onto &&
+       git rebase --abort &&
+       test_must_fail git rev-parse --verify refs/rewritten/onto
+'
+
+test_expect_success '--quit cleans up refs/rewritten' '
+       git checkout -b quit-cleans-refs-rewritten H &&
+       GIT_SEQUENCE_EDITOR="echo break >>" git rebase -ir @^ &&
+       git rev-parse --verify refs/rewritten/onto &&
+       git rebase --quit &&
+       test_must_fail git rev-parse --verify refs/rewritten/onto
+'
+
 test_expect_success 'post-rewrite hook and fixups work for merges' '
-       git checkout -b post-rewrite &&
+       git checkout -b post-rewrite &&
        test_commit same1 &&
        git reset --hard HEAD^ &&
        test_commit same2 &&
index 65dfbc033a027df1a590cbfaf75ae47f4ed9c547..69991a3168f354b8bfefcd396a968e653bb352bd 100755 (executable)
@@ -639,4 +639,12 @@ test_expect_success 'add -p patch editing works with pathological context lines'
        test_cmp expected-2 actual
 '
 
+test_expect_success 'checkout -p works with pathological context lines' '
+       test_write_lines a a a a a a >a &&
+       git add a &&
+       test_write_lines a b a b a b a b a b a > a&&
+       test_write_lines s n n y q | git checkout -p &&
+       test_write_lines a b a b a a b a b a >expect &&
+       test_cmp expect a
+'
 test_done
index ea30d5f6a0f228971d29e257a89fca6ea594cecb..b22e671608119d37dd73bf6b0df41ce62e1a26f9 100755 (executable)
@@ -708,6 +708,24 @@ test_expect_success 'invalid ref of the form "n", n >= N' '
        git stash drop
 '
 
+test_expect_success 'valid ref of the form "n", n < N' '
+       git stash clear &&
+       echo bar5 >file &&
+       echo bar6 >file2 &&
+       git add file2 &&
+       git stash &&
+       git stash show 0 &&
+       git stash branch tmp 0 &&
+       git checkout master &&
+       git stash &&
+       git stash apply 0 &&
+       git reset --hard &&
+       git stash pop 0 &&
+       git stash &&
+       git stash drop 0 &&
+       test_must_fail git stash drop
+'
+
 test_expect_success 'branch: do not drop the stash if the branch exists' '
        git stash clear &&
        echo foo >file &&
index b6e2fdbc4410f1b8b04586f12bb8806e85a10e8d..ca7debf1d4c0b5f73301b2f8a35490b77c167a61 100755 (executable)
@@ -36,8 +36,27 @@ test_expect_success setup '
        git checkout master &&
        git diff-tree -p C2 | git apply --index &&
        test_tick &&
-       git commit -m "Master accepts moral equivalent of #2"
+       git commit -m "Master accepts moral equivalent of #2" &&
 
+       git checkout side &&
+       git checkout -b patchid &&
+       for i in 5 6 1 2 3 A 4 B C 7 8 9 10 D E F; do echo "$i"; done >file2 &&
+       for i in 1 2 3 A 4 B C 7 8 9 10 D E F 5 6; do echo "$i"; done >file3 &&
+       for i in 8 9 10; do echo "$i"; done >file &&
+       git add file file2 file3 &&
+       test_tick &&
+       git commit -m "patchid 1" &&
+       for i in 4 A B 7 8 9 10; do echo "$i"; done >file2 &&
+       for i in 8 9 10 5 6; do echo "$i"; done >file3 &&
+       git add file2 file3 &&
+       test_tick &&
+       git commit -m "patchid 2" &&
+       for i in 10 5 6; do echo "$i"; done >file &&
+       git add file &&
+       test_tick &&
+       git commit -m "patchid 3" &&
+
+       git checkout master
 '
 
 test_expect_success "format-patch --ignore-if-in-upstream" '
@@ -738,6 +757,76 @@ test_expect_success 'format-patch --notes --signoff' '
        sed "1,/^---$/d" out | grep "test message"
 '
 
+test_expect_success 'format-patch notes output control' '
+       git notes add -m "notes config message" HEAD &&
+       test_when_finished git notes remove HEAD &&
+
+       git format-patch -1 --stdout >out &&
+       ! grep "notes config message" out &&
+       git format-patch -1 --stdout --notes >out &&
+       grep "notes config message" out &&
+       git format-patch -1 --stdout --no-notes >out &&
+       ! grep "notes config message" out &&
+       git format-patch -1 --stdout --notes --no-notes >out &&
+       ! grep "notes config message" out &&
+       git format-patch -1 --stdout --no-notes --notes >out &&
+       grep "notes config message" out &&
+
+       test_config format.notes true &&
+       git format-patch -1 --stdout >out &&
+       grep "notes config message" out &&
+       git format-patch -1 --stdout --notes >out &&
+       grep "notes config message" out &&
+       git format-patch -1 --stdout --no-notes >out &&
+       ! grep "notes config message" out &&
+       git format-patch -1 --stdout --notes --no-notes >out &&
+       ! grep "notes config message" out &&
+       git format-patch -1 --stdout --no-notes --notes >out &&
+       grep "notes config message" out
+'
+
+test_expect_success 'format-patch with multiple notes refs' '
+       git notes --ref note1 add -m "this is note 1" HEAD &&
+       test_when_finished git notes --ref note1 remove HEAD &&
+       git notes --ref note2 add -m "this is note 2" HEAD &&
+       test_when_finished git notes --ref note2 remove HEAD &&
+
+       git format-patch -1 --stdout >out &&
+       ! grep "this is note 1" out &&
+       ! grep "this is note 2" out &&
+       git format-patch -1 --stdout --notes=note1 >out &&
+       grep "this is note 1" out &&
+       ! grep "this is note 2" out &&
+       git format-patch -1 --stdout --notes=note2 >out &&
+       ! grep "this is note 1" out &&
+       grep "this is note 2" out &&
+       git format-patch -1 --stdout --notes=note1 --notes=note2 >out &&
+       grep "this is note 1" out &&
+       grep "this is note 2" out &&
+
+       test_config format.notes note1 &&
+       git format-patch -1 --stdout >out &&
+       grep "this is note 1" out &&
+       ! grep "this is note 2" out &&
+       git format-patch -1 --stdout --no-notes >out &&
+       ! grep "this is note 1" out &&
+       ! grep "this is note 2" out &&
+       git format-patch -1 --stdout --notes=note2 >out &&
+       grep "this is note 1" out &&
+       grep "this is note 2" out &&
+       git format-patch -1 --stdout --no-notes --notes=note2 >out &&
+       ! grep "this is note 1" out &&
+       grep "this is note 2" out &&
+
+       git config --add format.notes note2 &&
+       git format-patch -1 --stdout >out &&
+       grep "this is note 1" out &&
+       grep "this is note 2" out &&
+       git format-patch -1 --stdout --no-notes >out &&
+       ! grep "this is note 1" out &&
+       ! grep "this is note 2" out
+'
+
 echo "fatal: --name-only does not make sense" > expect.name-only
 echo "fatal: --name-status does not make sense" > expect.name-status
 echo "fatal: --check does not make sense" > expect.check
@@ -1559,7 +1648,7 @@ test_expect_success 'format-patch -o overrides format.outputDirectory' '
 '
 
 test_expect_success 'format-patch --base' '
-       git checkout side &&
+       git checkout patchid &&
        git format-patch --stdout --base=HEAD~3 -1 | tail -n 7 >actual1 &&
        git format-patch --stdout --base=HEAD~3 HEAD~.. | tail -n 7 >actual2 &&
        echo >expected &&
@@ -1568,7 +1657,14 @@ test_expect_success 'format-patch --base' '
        echo "prerequisite-patch-id: $(git show --patch HEAD~1 | git patch-id --stable | awk "{print \$1}")" >>expected &&
        signature >> expected &&
        test_cmp expected actual1 &&
-       test_cmp expected actual2
+       test_cmp expected actual2 &&
+       echo >fail &&
+       echo "base-commit: $(git rev-parse HEAD~3)" >>fail &&
+       echo "prerequisite-patch-id: $(git show --patch HEAD~2 | git patch-id --unstable | awk "{print \$1}")" >>fail &&
+       echo "prerequisite-patch-id: $(git show --patch HEAD~1 | git patch-id --unstable | awk "{print \$1}")" >>fail &&
+       signature >> fail &&
+       ! test_cmp fail actual1 &&
+       ! test_cmp fail actual2
 '
 
 test_expect_success 'format-patch --base errors out when base commit is in revision list' '
index 22f9f88f0afc54f1dfeebbea623a4c41fde709f6..9261d6d3a0000e9891e1af58265349522ca40b84 100755 (executable)
@@ -43,6 +43,7 @@ diffpatterns="
        php
        python
        ruby
+       rust
        tex
        custom1
        custom2
diff --git a/t/t4018/matlab-class-definition b/t/t4018/matlab-class-definition
new file mode 100644 (file)
index 0000000..84daedf
--- /dev/null
@@ -0,0 +1,5 @@
+classdef RIGHT
+    properties
+        ChangeMe
+    end
+end
diff --git a/t/t4018/matlab-function b/t/t4018/matlab-function
new file mode 100644 (file)
index 0000000..897a9b1
--- /dev/null
@@ -0,0 +1,4 @@
+function y = RIGHT()
+x = 5;
+y = ChangeMe + x;
+end
diff --git a/t/t4018/matlab-octave-section-1 b/t/t4018/matlab-octave-section-1
new file mode 100644 (file)
index 0000000..3bb6c46
--- /dev/null
@@ -0,0 +1,3 @@
+%%% RIGHT section
+# this is octave script
+ChangeMe = 1;
diff --git a/t/t4018/matlab-octave-section-2 b/t/t4018/matlab-octave-section-2
new file mode 100644 (file)
index 0000000..ab2980f
--- /dev/null
@@ -0,0 +1,3 @@
+## RIGHT section
+# this is octave script
+ChangeMe = 1;
diff --git a/t/t4018/matlab-section b/t/t4018/matlab-section
new file mode 100644 (file)
index 0000000..5ea59a5
--- /dev/null
@@ -0,0 +1,3 @@
+%% RIGHT section
+% this is understood by both matlab and octave
+ChangeMe = 1;
diff --git a/t/t4018/rust-fn b/t/t4018/rust-fn
new file mode 100644 (file)
index 0000000..cbe0215
--- /dev/null
@@ -0,0 +1,5 @@
+pub(self) fn RIGHT<T>(x: &[T]) where T: Debug {
+    let _ = x;
+    // a comment
+    let a = ChangeMe;
+}
diff --git a/t/t4018/rust-impl b/t/t4018/rust-impl
new file mode 100644 (file)
index 0000000..09df3cd
--- /dev/null
@@ -0,0 +1,5 @@
+impl<'a, T: AsRef<[u8]>>  std::RIGHT for Git<'a> {
+
+    pub fn ChangeMe(&self) -> () {
+    }
+}
diff --git a/t/t4018/rust-struct b/t/t4018/rust-struct
new file mode 100644 (file)
index 0000000..76aff1c
--- /dev/null
@@ -0,0 +1,5 @@
+#[derive(Debug)]
+pub(super) struct RIGHT<'a> {
+    name: &'a str,
+    age: ChangeMe,
+}
diff --git a/t/t4018/rust-trait b/t/t4018/rust-trait
new file mode 100644 (file)
index 0000000..ea397f0
--- /dev/null
@@ -0,0 +1,5 @@
+unsafe trait RIGHT<T> {
+    fn len(&self) -> u32;
+    fn ChangeMe(&self, n: u32) -> T;
+    fn iter<F>(&self, f: F) where F: Fn(T);
+}
index 819c24d10eaa3cb4a58b72ae8a8e96151b5cabe9..c20209324c8e71c677b70ce2217cf6439385024b 100755 (executable)
@@ -352,7 +352,7 @@ test_expect_success 'log with grep.patternType configuration and command line' '
        test_cmp expect actual
 '
 
-test_expect_success 'log with various grep.patternType configurations & command-lines' '
+test_expect_success !FAIL_PREREQS 'log with various grep.patternType configurations & command-lines' '
        git init pattern-type &&
        (
                cd pattern-type &&
diff --git a/t/t4257-am-interactive.sh b/t/t4257-am-interactive.sh
new file mode 100755 (executable)
index 0000000..5344bd2
--- /dev/null
@@ -0,0 +1,52 @@
+#!/bin/sh
+
+test_description='am --interactive tests'
+. ./test-lib.sh
+
+test_expect_success 'set up patches to apply' '
+       test_commit unrelated &&
+       test_commit no-conflict &&
+       test_commit conflict-patch file patch &&
+       git format-patch --stdout -2 >mbox &&
+
+       git reset --hard unrelated &&
+       test_commit conflict-master file master base
+'
+
+# Sanity check our setup.
+test_expect_success 'applying all patches generates conflict' '
+       test_must_fail git am mbox &&
+       echo resolved >file &&
+       git add -u &&
+       git am --resolved
+'
+
+test_expect_success 'interactive am can apply a single patch' '
+       git reset --hard base &&
+       # apply the first, but not the second
+       test_write_lines y n | git am -i mbox &&
+
+       echo no-conflict >expect &&
+       git log -1 --format=%s >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'interactive am can resolve conflict' '
+       git reset --hard base &&
+       # apply both; the second one will conflict
+       test_write_lines y y | test_must_fail git am -i mbox &&
+       echo resolved >file &&
+       git add -u &&
+       # interactive "--resolved" will ask us if we want to apply the result
+       echo y | git am -i --resolved &&
+
+       echo conflict-patch >expect &&
+       git log -1 --format=%s >actual &&
+       test_cmp expect actual &&
+
+       echo resolved >expect &&
+       git cat-file blob HEAD:file >actual &&
+       test_cmp expect actual
+'
+
+test_done
index fca001eb9bb5767b1aff6439dbc98e49ed3aaef8..852dcd913f1e746ba4a65763ae31ee96064f3fd2 100755 (executable)
@@ -246,4 +246,57 @@ test_expect_success 'request-pull ignores OPTIONS_KEEPDASHDASH poison' '
 
 '
 
+test_expect_success 'request-pull quotes regex metacharacters properly' '
+
+       rm -fr downstream.git &&
+       git init --bare downstream.git &&
+       (
+               cd local &&
+               git checkout initial &&
+               git merge --ff-only master &&
+               git tag -mrelease v2.0 &&
+               git push origin refs/tags/v2.0:refs/tags/v2-0 &&
+               test_must_fail git request-pull initial "$downstream_url" tags/v2.0 \
+                       2>../err
+       ) &&
+       grep "No match for commit .*" err &&
+       grep "Are you sure you pushed" err
+
+'
+
+test_expect_success 'pull request with mismatched object' '
+
+       rm -fr downstream.git &&
+       git init --bare downstream.git &&
+       (
+               cd local &&
+               git checkout initial &&
+               git merge --ff-only master &&
+               git push origin HEAD:refs/tags/full &&
+               test_must_fail git request-pull initial "$downstream_url" tags/full \
+                       2>../err
+       ) &&
+       grep "points to a different object" err &&
+       grep "Are you sure you pushed" err
+
+'
+
+test_expect_success 'pull request with stale object' '
+
+       rm -fr downstream.git &&
+       git init --bare downstream.git &&
+       (
+               cd local &&
+               git checkout initial &&
+               git merge --ff-only master &&
+               git push origin refs/tags/full &&
+               git tag -f -m"Thirty-one days" full &&
+               test_must_fail git request-pull initial "$downstream_url" tags/full \
+                       2>../err
+       ) &&
+       grep "points to a different object" err &&
+       grep "Are you sure you pushed" err
+
+'
+
 test_done
diff --git a/t/t5200-update-server-info.sh b/t/t5200-update-server-info.sh
new file mode 100755 (executable)
index 0000000..21a58ee
--- /dev/null
@@ -0,0 +1,41 @@
+#!/bin/sh
+
+test_description='Test git update-server-info'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' 'test_commit file'
+
+test_expect_success 'create info/refs' '
+       git update-server-info &&
+       test_path_is_file .git/info/refs
+'
+
+test_expect_success 'modify and store mtime' '
+       test-tool chmtime =0 .git/info/refs &&
+       test-tool chmtime --get .git/info/refs >a
+'
+
+test_expect_success 'info/refs is not needlessly overwritten' '
+       git update-server-info &&
+       test-tool chmtime --get .git/info/refs >b &&
+       test_cmp a b
+'
+
+test_expect_success 'info/refs can be forced to update' '
+       git update-server-info -f &&
+       test-tool chmtime --get .git/info/refs >b &&
+       ! test_cmp a b
+'
+
+test_expect_success 'info/refs updates when changes are made' '
+       test-tool chmtime =0 .git/info/refs &&
+       test-tool chmtime --get .git/info/refs >b &&
+       test_cmp a b &&
+       git update-ref refs/heads/foo HEAD &&
+       git update-server-info &&
+       test-tool chmtime --get .git/info/refs >b &&
+       ! test_cmp a b
+'
+
+test_done
index 840ad4d8accbfef59e2c9ade82105cc717f6866c..5267c4be20e709bb1632c5b9786ffdb418bcb5c9 100755 (executable)
@@ -23,6 +23,14 @@ test_expect_success 'write graph with no packs' '
        test_path_is_file info/commit-graph
 '
 
+test_expect_success 'close with correct error on bad input' '
+       cd "$TRASH_DIRECTORY/full" &&
+       echo doesnotexist >in &&
+       { git commit-graph write --stdin-packs <in 2>stderr; ret=$?; } &&
+       test "$ret" = 1 &&
+       test_i18ngrep "error adding pack" stderr
+'
+
 test_expect_success 'create commits and repack' '
        cd "$TRASH_DIRECTORY/full" &&
        for i in $(test_seq 3)
index c88df78c0bffaae8f09fc2db79427d85a6f8ff48..75cbfcc392c8cd71e20d901a7a28bf7f6f73451a 100755 (executable)
@@ -124,4 +124,32 @@ test_expect_success 'try to update a hidden full ref' '
        test_must_fail git -C original push pushee-namespaced master
 '
 
+test_expect_success 'set up ambiguous HEAD' '
+       git init ambiguous &&
+       (
+               cd ambiguous &&
+               git commit --allow-empty -m foo &&
+               git update-ref refs/namespaces/ns/refs/heads/one HEAD &&
+               git update-ref refs/namespaces/ns/refs/heads/two HEAD &&
+               git symbolic-ref refs/namespaces/ns/HEAD \
+                       refs/namespaces/ns/refs/heads/two
+       )
+'
+
+test_expect_success 'clone chooses correct HEAD (v0)' '
+       GIT_NAMESPACE=ns git -c protocol.version=0 \
+               clone ambiguous ambiguous-v0 &&
+       echo refs/heads/two >expect &&
+       git -C ambiguous-v0 symbolic-ref HEAD >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'clone chooses correct HEAD (v2)' '
+       GIT_NAMESPACE=ns git -c protocol.version=2 \
+               clone ambiguous ambiguous-v2 &&
+       echo refs/heads/two >expect &&
+       git -C ambiguous-v2 symbolic-ref HEAD >actual &&
+       test_cmp expect actual
+'
+
 test_done
index de9d99cf88a5b72a3aa6e190124dff36e67818b6..37d76808d4a74b101f0606fc7a114fed6a8afa91 100755 (executable)
@@ -630,9 +630,8 @@ test_expect_success CASE_INSENSITIVE_FS 'colliding file detection' '
        test_i18ngrep "the following paths have collided" icasefs/warning
 '
 
-partial_clone () {
+partial_clone_server () {
               SERVER="$1" &&
-              URL="$2" &&
 
        rm -rf "$SERVER" client &&
        test_create_repo "$SERVER" &&
@@ -642,8 +641,14 @@ partial_clone () {
        test_commit -C "$SERVER" two &&
        HASH2=$(git hash-object "$SERVER/two.t") &&
        test_config -C "$SERVER" uploadpack.allowfilter 1 &&
-       test_config -C "$SERVER" uploadpack.allowanysha1inwant 1 &&
+       test_config -C "$SERVER" uploadpack.allowanysha1inwant 1
+}
 
+partial_clone () {
+              SERVER="$1" &&
+              URL="$2" &&
+
+       partial_clone_server "${SERVER}" &&
        git clone --filter=blob:limit=0 "$URL" client &&
 
        git -C client fsck &&
@@ -660,6 +665,11 @@ test_expect_success 'partial clone' '
        partial_clone server "file://$(pwd)/server"
 '
 
+test_expect_success 'partial clone with -o' '
+       partial_clone_server server &&
+       git clone -o blah --filter=blob:limit=0 "file://$(pwd)/server" client
+'
+
 test_expect_success 'partial clone: warn if server does not support object filtering' '
        rm -rf server client &&
        test_create_repo server &&
index cf39e9e2437f06b4725b61ad2194dd65b4857f82..2a0fb15cf175251df0a140aa4f5c56fd7885292d 100755 (executable)
@@ -14,6 +14,12 @@ test_expect_success 'setup' '
        git tag -d third
 '
 
+test_expect_success '"verify" needs a worktree' '
+       git bundle create tip.bundle -1 master &&
+       test_must_fail nongit git bundle verify ../tip.bundle 2>err &&
+       test_i18ngrep "need a repository" err
+'
+
 test_expect_success 'annotated tags can be excluded by rev-list options' '
        git bundle create bundle --all --since=7.Apr.2005.15:14:00.-0700 &&
        git ls-remote bundle > output &&
index 9a8f9886b3e2d82b5c10bf749d485f05bd364db6..b91ef548f86b0e250b8fdcd1b8b764c780218931 100755 (executable)
@@ -244,11 +244,25 @@ test_expect_success 'fetch what is specified on CLI even if already promised' '
 . "$TEST_DIRECTORY"/lib-httpd.sh
 start_httpd
 
-# Converts bytes into a form suitable for inclusion in a sed command. For
-# example, "printf 'ab\r\n' | hex_unpack" results in '\x61\x62\x0d\x0a'.
-sed_escape () {
-       perl -e '$/ = undef; $input = <>; print unpack("H2" x length($input), $input)' |
-               sed 's/\(..\)/\\x\1/g'
+# Converts bytes into their hexadecimal representation. For example,
+# "printf 'ab\r\n' | hex_unpack" results in '61620d0a'.
+hex_unpack () {
+       perl -e '$/ = undef; $input = <>; print unpack("H2" x length($input), $input)'
+}
+
+# Inserts $1 at the start of the string and every 2 characters thereafter.
+intersperse () {
+       sed 's/\(..\)/'$1'\1/g'
+}
+
+# Create a one-time-sed command to replace the existing packfile with $1.
+replace_packfile () {
+       # The protocol requires that the packfile be sent in sideband 1, hence
+       # the extra \x01 byte at the beginning.
+       printf "1,/packfile/!c %04x\\\\x01%s0000" \
+               "$(($(wc -c <$1) + 5))" \
+               "$(hex_unpack <$1 | intersperse '\\x')" \
+               >"$HTTPD_ROOT_PATH/one-time-sed"
 }
 
 test_expect_success 'upon cloning, check that all refs point to objects' '
@@ -270,10 +284,7 @@ test_expect_success 'upon cloning, check that all refs point to objects' '
        # Replace the existing packfile with the crafted one. The protocol
        # requires that the packfile be sent in sideband 1, hence the extra
        # \x01 byte at the beginning.
-       printf "1,/packfile/!c %04x\\\\x01%s0000" \
-               "$(($(wc -c <incomplete.pack) + 5))" \
-               "$(sed_escape <incomplete.pack)" \
-               >"$HTTPD_ROOT_PATH/one-time-sed" &&
+       replace_packfile incomplete.pack &&
 
        # Use protocol v2 because the sed command looks for the "packfile"
        # section header.
@@ -313,10 +324,7 @@ test_expect_success 'when partial cloning, tolerate server not sending target of
        # Replace the existing packfile with the crafted one. The protocol
        # requires that the packfile be sent in sideband 1, hence the extra
        # \x01 byte at the beginning.
-       printf "1,/packfile/!c %04x\\\\x01%s0000" \
-               "$(($(wc -c <incomplete.pack) + 5))" \
-               "$(sed_escape <incomplete.pack)" \
-               >"$HTTPD_ROOT_PATH/one-time-sed" &&
+       replace_packfile incomplete.pack &&
 
        # Use protocol v2 because the sed command looks for the "packfile"
        # section header.
@@ -331,4 +339,82 @@ test_expect_success 'when partial cloning, tolerate server not sending target of
        ! test -e "$HTTPD_ROOT_PATH/one-time-sed"
 '
 
+test_expect_success 'tolerate server sending REF_DELTA against missing promisor objects' '
+       SERVER="$HTTPD_DOCUMENT_ROOT_PATH/server" &&
+       rm -rf "$SERVER" repo &&
+       test_create_repo "$SERVER" &&
+       test_config -C "$SERVER" uploadpack.allowfilter 1 &&
+       test_config -C "$SERVER" uploadpack.allowanysha1inwant 1 &&
+
+       # Create a commit with 2 blobs to be used as delta bases.
+       for i in $(test_seq 10)
+       do
+               echo "this is a line" >>"$SERVER/foo.txt" &&
+               echo "this is another line" >>"$SERVER/have.txt"
+       done &&
+       git -C "$SERVER" add foo.txt have.txt &&
+       git -C "$SERVER" commit -m bar &&
+       git -C "$SERVER" rev-parse HEAD:foo.txt >deltabase_missing &&
+       git -C "$SERVER" rev-parse HEAD:have.txt >deltabase_have &&
+
+       # Clone. The client has deltabase_have but not deltabase_missing.
+       git -c protocol.version=2 clone --no-checkout \
+               --filter=blob:none $HTTPD_URL/one_time_sed/server repo &&
+       git -C repo hash-object -w -- "$SERVER/have.txt" &&
+
+       # Sanity check to ensure that the client does not have
+       # deltabase_missing.
+       git -C repo rev-list --objects --ignore-missing \
+               -- $(cat deltabase_missing) >objlist &&
+       test_line_count = 0 objlist &&
+
+       # Another commit. This commit will be fetched by the client.
+       echo "abcdefghijklmnopqrstuvwxyz" >>"$SERVER/foo.txt" &&
+       echo "abcdefghijklmnopqrstuvwxyz" >>"$SERVER/have.txt" &&
+       git -C "$SERVER" add foo.txt have.txt &&
+       git -C "$SERVER" commit -m baz &&
+
+       # Pack a thin pack containing, among other things, HEAD:foo.txt
+       # delta-ed against HEAD^:foo.txt and HEAD:have.txt delta-ed against
+       # HEAD^:have.txt.
+       printf "%s\n--not\n%s\n" \
+               $(git -C "$SERVER" rev-parse HEAD) \
+               $(git -C "$SERVER" rev-parse HEAD^) |
+               git -C "$SERVER" pack-objects --thin --stdout >thin.pack &&
+
+       # Ensure that the pack contains one delta against HEAD^:foo.txt. Since
+       # the delta contains at least 26 novel characters, the size cannot be
+       # contained in 4 bits, so the object header will take up 2 bytes. The
+       # most significant nybble of the first byte is 0b1111 (0b1 to indicate
+       # that the header continues, and 0b111 to indicate REF_DELTA), followed
+       # by any 3 nybbles, then the OID of the delta base.
+       printf "f.,..%s" $(intersperse "," <deltabase_missing) >want &&
+       hex_unpack <thin.pack | intersperse "," >have &&
+       grep $(cat want) have &&
+
+       # Ensure that the pack contains one delta against HEAD^:have.txt,
+       # similar to the above.
+       printf "f.,..%s" $(intersperse "," <deltabase_have) >want &&
+       hex_unpack <thin.pack | intersperse "," >have &&
+       grep $(cat want) have &&
+
+       replace_packfile thin.pack &&
+
+       # Use protocol v2 because the sed command looks for the "packfile"
+       # section header.
+       test_config -C "$SERVER" protocol.version 2 &&
+
+       # Fetch the thin pack and ensure that index-pack is able to handle the
+       # REF_DELTA object with a missing promisor delta base.
+       GIT_TRACE_PACKET="$(pwd)/trace" git -C repo -c protocol.version=2 fetch &&
+
+       # Ensure that the missing delta base was directly fetched, but not the
+       # one that the client has.
+       grep "want $(cat deltabase_missing)" trace &&
+       ! grep "want $(cat deltabase_have)" trace &&
+
+       # Ensure that the one-time-sed script was used.
+       ! test -e "$HTTPD_ROOT_PATH/one-time-sed"
+'
+
 test_done
diff --git a/t/t5617-clone-submodules-remote.sh b/t/t5617-clone-submodules-remote.sh
new file mode 100755 (executable)
index 0000000..37fcce9
--- /dev/null
@@ -0,0 +1,54 @@
+#!/bin/sh
+
+test_description='Test cloning repos with submodules using remote-tracking branches'
+
+. ./test-lib.sh
+
+pwd=$(pwd)
+
+test_expect_success 'setup' '
+       git checkout -b master &&
+       test_commit commit1 &&
+       mkdir sub &&
+       (
+               cd sub &&
+               git init &&
+               test_commit subcommit1 &&
+               git tag sub_when_added_to_super
+       ) &&
+       git submodule add "file://$pwd/sub" sub &&
+       git commit -m "add submodule" &&
+       (
+               cd sub &&
+               test_commit subcommit2
+       )
+'
+
+test_expect_success 'clone with --no-remote-submodules' '
+       test_when_finished "rm -rf super_clone" &&
+       git clone --recurse-submodules --no-remote-submodules "file://$pwd/." super_clone &&
+       (
+               cd super_clone/sub &&
+               git diff --exit-code sub_when_added_to_super
+       )
+'
+
+test_expect_success 'clone with --remote-submodules' '
+       test_when_finished "rm -rf super_clone" &&
+       git clone --recurse-submodules --remote-submodules "file://$pwd/." super_clone &&
+       (
+               cd super_clone/sub &&
+               git diff --exit-code remotes/origin/master
+       )
+'
+
+test_expect_success 'check the default is --no-remote-submodules' '
+       test_when_finished "rm -rf super_clone" &&
+       git clone --recurse-submodules "file://$pwd/." super_clone &&
+       (
+               cd super_clone/sub &&
+               git diff --exit-code sub_when_added_to_super
+       )
+'
+
+test_done
index d04f8007e0e34fa938bf4fccb91eff384743e616..2d6c4a281edb3f0678a600fd3e780825536da437 100755 (executable)
@@ -126,7 +126,7 @@ test_expect_success 'forced push' '
 '
 
 test_expect_success 'cloning without refspec' '
-       GIT_REMOTE_TESTGIT_REFSPEC="" \
+       GIT_REMOTE_TESTGIT_NOREFSPEC=1 \
        git clone "testgit::${PWD}/server" local2 2>error &&
        test_i18ngrep "this remote helper should implement refspec capability" error &&
        compare_refs local2 HEAD server HEAD
@@ -135,7 +135,7 @@ test_expect_success 'cloning without refspec' '
 test_expect_success 'pulling without refspecs' '
        (cd local2 &&
        git reset --hard &&
-       GIT_REMOTE_TESTGIT_REFSPEC="" git pull 2>../error) &&
+       GIT_REMOTE_TESTGIT_NOREFSPEC=1 git pull 2>../error) &&
        test_i18ngrep "this remote helper should implement refspec capability" error &&
        compare_refs local2 HEAD server HEAD
 '
@@ -145,8 +145,8 @@ test_expect_success 'pushing without refspecs' '
        (cd local2 &&
        echo content >>file &&
        git commit -a -m ten &&
-       GIT_REMOTE_TESTGIT_REFSPEC="" &&
-       export GIT_REMOTE_TESTGIT_REFSPEC &&
+       GIT_REMOTE_TESTGIT_NOREFSPEC=1 &&
+       export GIT_REMOTE_TESTGIT_NOREFSPEC &&
        test_must_fail git push 2>../error) &&
        test_i18ngrep "remote-helper doesn.t support push; refspec needed" error
 '
@@ -303,4 +303,14 @@ test_expect_success 'fetch url' '
        compare_refs server HEAD local FETCH_HEAD
 '
 
+test_expect_success 'fetch tag' '
+       (cd server &&
+        git tag v1.0
+       ) &&
+       (cd local &&
+        git fetch
+       ) &&
+       compare_refs local v1.0 server v1.0
+'
+
 test_done
index 752c763eb666e197304efbc7ea006325a36ff870..6b9f0b5dc79cf0239daf4f6a210baaccf8612d74 100755 (executable)
@@ -11,13 +11,15 @@ fi
 url=$2
 
 dir="$GIT_DIR/testgit/$alias"
-prefix="refs/testgit/$alias"
 
-default_refspec="refs/heads/*:${prefix}/heads/*"
+h_refspec="refs/heads/*:refs/testgit/$alias/heads/*"
+t_refspec="refs/tags/*:refs/testgit/$alias/tags/*"
 
-refspec="${GIT_REMOTE_TESTGIT_REFSPEC-$default_refspec}"
-
-test -z "$refspec" && prefix="refs"
+if test -n "$GIT_REMOTE_TESTGIT_NOREFSPEC"
+then
+       h_refspec=""
+       t_refspec=""
+fi
 
 GIT_DIR="$url/.git"
 export GIT_DIR
@@ -40,7 +42,8 @@ do
        capabilities)
                echo 'import'
                echo 'export'
-               test -n "$refspec" && echo "refspec $refspec"
+               test -n "$h_refspec" && echo "refspec $h_refspec"
+               test -n "$t_refspec" && echo "refspec $t_refspec"
                if test -n "$gitmarks"
                then
                        echo "*import-marks $gitmarks"
@@ -52,7 +55,7 @@ do
                echo
                ;;
        list)
-               git for-each-ref --format='? %(refname)' 'refs/heads/'
+               git for-each-ref --format='? %(refname)' 'refs/heads/' 'refs/tags/'
                head=$(git symbolic-ref HEAD)
                echo "@$head HEAD"
                echo
@@ -81,10 +84,11 @@ do
 
                echo "feature done"
                git fast-export \
+                       ${h_refspec:+"--refspec=$h_refspec"} \
+                       ${t_refspec:+"--refspec=$t_refspec"} \
                        ${testgitmarks:+"--import-marks=$testgitmarks"} \
                        ${testgitmarks:+"--export-marks=$testgitmarks"} \
-                       $refs |
-               sed -e "s#refs/heads/#${prefix}/heads/#g"
+                       $refs
                echo "done"
                ;;
        export)
index 05079997291fe295aacc80be1f26fd7002f0a898..52a9e38d66f3222f4c02f7d067a9509db685f875 100755 (executable)
@@ -48,6 +48,26 @@ test_expect_success 'rev-list --objects with pathspecs and copied files' '
        ! grep one output
 '
 
+test_expect_success 'rev-list --objects --no-object-names has no space/names' '
+       git rev-list --objects --no-object-names HEAD >output &&
+       ! grep wanted_file output &&
+       ! grep unwanted_file output &&
+       ! grep " " output
+'
+
+test_expect_success 'rev-list --objects --no-object-names works with cat-file' '
+       git rev-list --objects --no-object-names --all >list-output &&
+       git cat-file --batch-check <list-output >cat-output &&
+       ! grep missing cat-output
+'
+
+test_expect_success '--no-object-names and --object-names are last-one-wins' '
+       git rev-list --objects --no-object-names --object-names --all >output &&
+       grep wanted_file output &&
+       git rev-list --objects --object-names --no-object-names --all >output &&
+       ! grep wanted_file output
+'
+
 test_expect_success 'rev-list A..B and rev-list ^A B are the same' '
        git commit --allow-empty -m another &&
        git tag -a -m "annotated" v1.0 &&
index fc067ed6723bc0aaaddd2473e4d61f13087b9e15..35408d53fd8af5d627326dd2a47e532d64a4c304 100755 (executable)
@@ -441,4 +441,17 @@ test_expect_success '--merged is incompatible with --no-merged' '
        test_must_fail git for-each-ref --merged HEAD --no-merged HEAD
 '
 
+test_expect_success 'validate worktree atom' '
+       cat >expect <<-EOF &&
+       master: $(pwd)
+       master_worktree: $(pwd)/worktree_dir
+       side: not checked out
+       EOF
+       git worktree add -b master_worktree worktree_dir master &&
+       git for-each-ref --format="%(refname:short): %(if)%(worktreepath)%(then)%(worktreepath)%(else)not checked out%(end)" refs/heads/ >actual &&
+       rm -r worktree_dir &&
+       git worktree prune &&
+       test_cmp expect actual
+'
+
 test_done
index 515c6735e9cb076c9f4b5a252c48216765b5076a..c0f04dc6b0e149173691e13c0194ffbd53be6ef8 100755 (executable)
@@ -71,6 +71,8 @@ test_expect_success 'gc --keep-largest-pack' '
                git gc --keep-largest-pack &&
                ( cd .git/objects/pack && ls *.pack ) >pack-list &&
                test_line_count = 2 pack-list &&
+               awk "/^P /{print \$2}" <.git/objects/info/packs >pack-info &&
+               test_line_count = 2 pack-info &&
                test_path_is_file $BASE_PACK &&
                git fsck
        )
index 6aeeb279a0a03614151a37f1bed8f22f4af0c4f5..80eb13d94e2a27d160424c9919ee0052c8c773c0 100755 (executable)
@@ -932,6 +932,27 @@ test_expect_success GPG \
        test_cmp expect actual
 '
 
+get_tag_header gpgsign-enabled $commit commit $time >expect
+echo "A message" >>expect
+echo '-----BEGIN PGP SIGNATURE-----' >>expect
+test_expect_success GPG \
+       'git tag configured tag.gpgsign enables GPG sign' \
+       'test_config tag.gpgsign true &&
+       git tag -m "A message" gpgsign-enabled &&
+       get_tag_msg gpgsign-enabled>actual &&
+       test_cmp expect actual
+'
+
+get_tag_header no-sign $commit commit $time >expect
+echo "A message" >>expect
+test_expect_success GPG \
+       'git tag --no-sign configured tag.gpgsign skip GPG sign' \
+       'test_config tag.gpgsign true &&
+       git tag -a --no-sign -m "A message" no-sign &&
+       get_tag_msg no-sign>actual &&
+       test_cmp expect actual
+'
+
 test_expect_success GPG \
        'trying to create a signed tag with non-existing -F file should fail' '
        ! test -f nonexistingfile &&
index 7855bd8648f84b074e8a028a50aef49495c75431..aa33978ed2868e1585b4acdf87cdba2907597f45 100755 (executable)
@@ -417,7 +417,7 @@ test_expect_failure 'directory/submodule conflict; keep submodule clean' '
        )
 '
 
-test_expect_failure 'directory/submodule conflict; should not treat submodule files as untracked or in the way' '
+test_expect_failure !FAIL_PREREQS 'directory/submodule conflict; should not treat submodule files as untracked or in the way' '
        test_when_finished "git -C directory-submodule/path reset --hard" &&
        test_when_finished "git -C directory-submodule reset --hard" &&
        (
index c44186133147838d7f17c4d42f8cb96a5df73b28..f19202b509899481d4a098de049f1e2c9cae9915 100755 (executable)
@@ -538,33 +538,50 @@ test_expect_success 'with 2 files arguments' '
        test_cmp expected actual
 '
 
-test_expect_success 'with message that has comments' '
-       cat basic_message >message_with_comments &&
-       sed -e "s/ Z\$/ /" >>message_with_comments <<-\EOF &&
-               # comment
-
-               # other comment
-               Cc: Z
-               # yet another comment
-               Reviewed-by: Johan
-               Reviewed-by: Z
-               # last comment
-
-       EOF
-       cat basic_patch >>message_with_comments &&
-       cat basic_message >expected &&
-       cat >>expected <<-\EOF &&
-               # comment
-
-               Reviewed-by: Johan
-               Cc: Peff
-               # last comment
-
-       EOF
-       cat basic_patch >>expected &&
-       git interpret-trailers --trim-empty --trailer "Cc: Peff" message_with_comments >actual &&
-       test_cmp expected actual
-'
+# Cover multiple comment characters with the same test input.
+for char in "#" ";"
+do
+       case "$char" in
+       "#")
+               # This is the default, so let's explicitly _not_
+               # set any config to make sure it behaves as we expect.
+               ;;
+       *)
+               config="-c core.commentChar=$char"
+               ;;
+       esac
+
+       test_expect_success "with message that has comments ($char)" '
+               cat basic_message >message_with_comments &&
+               sed -e "s/ Z\$/ /" \
+                   -e "s/#/$char/g" >>message_with_comments <<-EOF &&
+                       # comment
+
+                       # other comment
+                       Cc: Z
+                       # yet another comment
+                       Reviewed-by: Johan
+                       Reviewed-by: Z
+                       # last comment
+
+               EOF
+               cat basic_patch >>message_with_comments &&
+               cat basic_message >expected &&
+               sed -e "s/#/$char/g" >>expected <<-\EOF &&
+                       # comment
+
+                       Reviewed-by: Johan
+                       Cc: Peff
+                       # last comment
+
+               EOF
+               cat basic_patch >>expected &&
+               git $config interpret-trailers \
+                       --trim-empty --trailer "Cc: Peff" \
+                       message_with_comments >actual &&
+               test_cmp expected actual
+       '
+done
 
 test_expect_success 'with message that has an old style conflict block' '
        cat basic_message >message_with_comments &&
index 7f9c68cbe75a688357e6c71486c3897861421681..132608879ad3c93011b8429d7f6f0f8ab1ed45cf 100755 (executable)
@@ -570,6 +570,12 @@ test_expect_success 'combining --squash and --no-ff is refused' '
        test_must_fail git merge --no-ff --squash c1
 '
 
+test_expect_success 'combining --squash and --commit is refused' '
+       git reset --hard c0 &&
+       test_must_fail git merge --squash --commit c1 &&
+       test_must_fail git merge --commit --squash c1
+'
+
 test_expect_success 'option --ff-only overwrites --no-ff' '
        git merge --no-ff --ff-only c1 &&
        test_must_fail git merge --no-ff --ff-only c2
@@ -867,4 +873,50 @@ test_expect_success EXECKEEPSPID 'killed merge can be completed with --continue'
        verify_parents $c0 $c1
 '
 
+test_expect_success 'merge --quit' '
+       git init merge-quit &&
+       (
+               cd merge-quit &&
+               test_commit base &&
+               echo one >>base.t &&
+               git commit -am one &&
+               git branch one &&
+               git checkout base &&
+               echo two >>base.t &&
+               git commit -am two &&
+               test_must_fail git -c rerere.enabled=true merge one &&
+               test_path_is_file .git/MERGE_HEAD &&
+               test_path_is_file .git/MERGE_MODE &&
+               test_path_is_file .git/MERGE_MSG &&
+               git rerere status >rerere.before &&
+               git merge --quit &&
+               test_path_is_missing .git/MERGE_HEAD &&
+               test_path_is_missing .git/MERGE_MODE &&
+               test_path_is_missing .git/MERGE_MSG &&
+               git rerere status >rerere.after &&
+               test_must_be_empty rerere.after &&
+               ! test_cmp rerere.after rerere.before
+       )
+'
+
+test_expect_success 'merge suggests matching remote refname' '
+       git commit --allow-empty -m not-local &&
+       git update-ref refs/remotes/origin/not-local HEAD &&
+       git reset --hard HEAD^ &&
+
+       # This is white-box testing hackery; we happen to know
+       # that reading packed refs is more picky about the memory
+       # ownership of strings we pass to for_each_ref() callbacks.
+       git pack-refs --all --prune &&
+
+       test_must_fail git merge not-local 2>stderr &&
+       grep origin/not-local stderr
+'
+
+test_expect_success 'suggested names are not ambiguous' '
+       git update-ref refs/heads/origin/not-local HEAD &&
+       test_must_fail git merge not-local 2>stderr &&
+       grep remotes/origin/not-local stderr
+'
+
 test_done
index 5b61c10a9c5402bfca8473f663076f9062a2e2a4..ad288ddc695f7cce0990e3f2bab696f3aff09eb1 100755 (executable)
@@ -131,17 +131,21 @@ test_expect_success 'custom mergetool' '
        git checkout -b test$test_count branch1 &&
        git submodule update -N &&
        test_must_fail git merge master &&
-       ( yes "" | git mergetool both ) &&
-       ( yes "" | git mergetool file1 file1 ) &&
-       ( yes "" | git mergetool file2 "spaced name" ) &&
-       ( yes "" | git mergetool subdir/file3 ) &&
-       ( yes "d" | git mergetool file11 ) &&
-       ( yes "d" | git mergetool file12 ) &&
-       ( yes "l" | git mergetool submod ) &&
-       test "$(cat file1)" = "master updated" &&
-       test "$(cat file2)" = "master new" &&
-       test "$(cat subdir/file3)" = "master new sub" &&
-       test "$(cat submod/bar)" = "branch1 submodule" &&
+       yes "" | git mergetool both &&
+       yes "" | git mergetool file1 file1 &&
+       yes "" | git mergetool file2 "spaced name" &&
+       yes "" | git mergetool subdir/file3 &&
+       yes "d" | git mergetool file11 &&
+       yes "d" | git mergetool file12 &&
+       yes "l" | git mergetool submod &&
+       echo "master updated" >expect &&
+       test_cmp expect file1 &&
+       echo "master new" >expect &&
+       test_cmp expect file2 &&
+       echo "master new sub" >expect &&
+       test_cmp expect subdir/file3 &&
+       echo "branch1 submodule" >expect &&
+       test_cmp expect submod/bar &&
        git commit -m "branch1 resolved with mergetool"
 '
 
@@ -153,17 +157,21 @@ test_expect_success 'gui mergetool' '
        git checkout -b test$test_count branch1 &&
        git submodule update -N &&
        test_must_fail git merge master &&
-       ( yes "" | git mergetool --gui both ) &&
-       ( yes "" | git mergetool -g file1 file1 ) &&
-       ( yes "" | git mergetool --gui file2 "spaced name" ) &&
-       ( yes "" | git mergetool --gui subdir/file3 ) &&
-       ( yes "d" | git mergetool --gui file11 ) &&
-       ( yes "d" | git mergetool --gui file12 ) &&
-       ( yes "l" | git mergetool --gui submod ) &&
-       test "$(cat file1)" = "gui master updated" &&
-       test "$(cat file2)" = "gui master new" &&
-       test "$(cat subdir/file3)" = "gui master new sub" &&
-       test "$(cat submod/bar)" = "branch1 submodule" &&
+       yes "" | git mergetool --gui both &&
+       yes "" | git mergetool -g file1 file1 &&
+       yes "" | git mergetool --gui file2 "spaced name" &&
+       yes "" | git mergetool --gui subdir/file3 &&
+       yes "d" | git mergetool --gui file11 &&
+       yes "d" | git mergetool --gui file12 &&
+       yes "l" | git mergetool --gui submod &&
+       echo "gui master updated" >expect &&
+       test_cmp expect file1 &&
+       echo "gui master new" >expect &&
+       test_cmp expect file2 &&
+       echo "gui master new sub" >expect &&
+       test_cmp expect subdir/file3 &&
+       echo "branch1 submodule" >expect &&
+       test_cmp expect submod/bar &&
        git commit -m "branch1 resolved with mergetool"
 '
 
@@ -172,17 +180,21 @@ test_expect_success 'gui mergetool without merge.guitool set falls back to merge
        git checkout -b test$test_count branch1 &&
        git submodule update -N &&
        test_must_fail git merge master &&
-       ( yes "" | git mergetool --gui both ) &&
-       ( yes "" | git mergetool -g file1 file1 ) &&
-       ( yes "" | git mergetool --gui file2 "spaced name" ) &&
-       ( yes "" | git mergetool --gui subdir/file3 ) &&
-       ( yes "d" | git mergetool --gui file11 ) &&
-       ( yes "d" | git mergetool --gui file12 ) &&
-       ( yes "l" | git mergetool --gui submod ) &&
-       test "$(cat file1)" = "master updated" &&
-       test "$(cat file2)" = "master new" &&
-       test "$(cat subdir/file3)" = "master new sub" &&
-       test "$(cat submod/bar)" = "branch1 submodule" &&
+       yes "" | git mergetool --gui both &&
+       yes "" | git mergetool -g file1 file1 &&
+       yes "" | git mergetool --gui file2 "spaced name" &&
+       yes "" | git mergetool --gui subdir/file3 &&
+       yes "d" | git mergetool --gui file11 &&
+       yes "d" | git mergetool --gui file12 &&
+       yes "l" | git mergetool --gui submod &&
+       echo "master updated" >expect &&
+       test_cmp expect file1 &&
+       echo "master new" >expect &&
+       test_cmp expect file2 &&
+       echo "master new sub" >expect &&
+       test_cmp expect subdir/file3 &&
+       echo "branch1 submodule" >expect &&
+       test_cmp expect submod/bar &&
        git commit -m "branch1 resolved with mergetool"
 '
 
@@ -195,19 +207,20 @@ test_expect_success 'mergetool crlf' '
        test_config core.autocrlf true &&
        git checkout -b test$test_count branch1 &&
        test_must_fail git merge master &&
-       ( yes "" | git mergetool file1 ) &&
-       ( yes "" | git mergetool file2 ) &&
-       ( yes "" | git mergetool "spaced name" ) &&
-       ( yes "" | git mergetool both ) &&
-       ( yes "" | git mergetool subdir/file3 ) &&
-       ( yes "d" | git mergetool file11 ) &&
-       ( yes "d" | git mergetool file12 ) &&
-       ( yes "r" | git mergetool submod ) &&
+       yes "" | git mergetool file1 &&
+       yes "" | git mergetool file2 &&
+       yes "" | git mergetool "spaced name" &&
+       yes "" | git mergetool both &&
+       yes "" | git mergetool subdir/file3 &&
+       yes "d" | git mergetool file11 &&
+       yes "d" | git mergetool file12 &&
+       yes "r" | git mergetool submod &&
        test "$(printf x | cat file1 -)" = "$(printf "master updated\r\nx")" &&
        test "$(printf x | cat file2 -)" = "$(printf "master new\r\nx")" &&
        test "$(printf x | cat subdir/file3 -)" = "$(printf "master new sub\r\nx")" &&
        git submodule update -N &&
-       test "$(cat submod/bar)" = "master submodule" &&
+       echo "master submodule" >expect &&
+       test_cmp expect submod/bar &&
        git commit -m "branch1 resolved with mergetool - autocrlf"
 '
 
@@ -218,8 +231,9 @@ test_expect_success 'mergetool in subdir' '
        (
                cd subdir &&
                test_must_fail git merge master &&
-               ( yes "" | git mergetool file3 ) &&
-               test "$(cat file3)" = "master new sub"
+               yes "" | git mergetool file3 &&
+               echo "master new sub" >expect &&
+               test_cmp expect file3
        )
 '
 
@@ -230,16 +244,19 @@ test_expect_success 'mergetool on file in parent dir' '
        (
                cd subdir &&
                test_must_fail git merge master &&
-               ( yes "" | git mergetool file3 ) &&
-               ( yes "" | git mergetool ../file1 ) &&
-               ( yes "" | git mergetool ../file2 ../spaced\ name ) &&
-               ( yes "" | git mergetool ../both ) &&
-               ( yes "d" | git mergetool ../file11 ) &&
-               ( yes "d" | git mergetool ../file12 ) &&
-               ( yes "l" | git mergetool ../submod ) &&
-               test "$(cat ../file1)" = "master updated" &&
-               test "$(cat ../file2)" = "master new" &&
-               test "$(cat ../submod/bar)" = "branch1 submodule" &&
+               yes "" | git mergetool file3 &&
+               yes "" | git mergetool ../file1 &&
+               yes "" | git mergetool ../file2 ../spaced\ name &&
+               yes "" | git mergetool ../both &&
+               yes "d" | git mergetool ../file11 &&
+               yes "d" | git mergetool ../file12 &&
+               yes "l" | git mergetool ../submod &&
+               echo "master updated" >expect &&
+               test_cmp expect ../file1 &&
+               echo "master new" >expect &&
+               test_cmp expect ../file2 &&
+               echo "branch1 submodule" >expect &&
+               test_cmp expect ../submod/bar &&
                git commit -m "branch1 resolved with mergetool - subdir"
        )
 '
@@ -250,9 +267,9 @@ test_expect_success 'mergetool skips autoresolved' '
        git submodule update -N &&
        test_must_fail git merge master &&
        test -n "$(git ls-files -u)" &&
-       ( yes "d" | git mergetool file11 ) &&
-       ( yes "d" | git mergetool file12 ) &&
-       ( yes "l" | git mergetool submod ) &&
+       yes "d" | git mergetool file11 &&
+       yes "d" | git mergetool file12 &&
+       yes "l" | git mergetool submod &&
        output="$(git mergetool --no-prompt)" &&
        test "$output" = "No files need merging"
 '
@@ -264,13 +281,17 @@ test_expect_success 'mergetool merges all from subdir (rerere disabled)' '
        (
                cd subdir &&
                test_must_fail git merge master &&
-               ( yes "r" | git mergetool ../submod ) &&
-               ( yes "d" "d" | git mergetool --no-prompt ) &&
-               test "$(cat ../file1)" = "master updated" &&
-               test "$(cat ../file2)" = "master new" &&
-               test "$(cat file3)" = "master new sub" &&
+               yes "r" | git mergetool ../submod &&
+               yes "d" "d" | git mergetool --no-prompt &&
+               echo "master updated" >expect &&
+               test_cmp expect ../file1 &&
+               echo "master new" >expect &&
+               test_cmp expect ../file2 &&
+               echo "master new sub" >expect &&
+               test_cmp expect file3 &&
                ( cd .. && git submodule update -N ) &&
-               test "$(cat ../submod/bar)" = "master submodule" &&
+               echo "master submodule" >expect &&
+               test_cmp expect ../submod/bar &&
                git commit -m "branch2 resolved by mergetool from subdir"
        )
 '
@@ -283,13 +304,17 @@ test_expect_success 'mergetool merges all from subdir (rerere enabled)' '
        (
                cd subdir &&
                test_must_fail git merge master &&
-               ( yes "r" | git mergetool ../submod ) &&
-               ( yes "d" "d" | git mergetool --no-prompt ) &&
-               test "$(cat ../file1)" = "master updated" &&
-               test "$(cat ../file2)" = "master new" &&
-               test "$(cat file3)" = "master new sub" &&
+               yes "r" | git mergetool ../submod &&
+               yes "d" "d" | git mergetool --no-prompt &&
+               echo "master updated" >expect &&
+               test_cmp expect ../file1 &&
+               echo "master new" >expect &&
+               test_cmp expect ../file2 &&
+               echo "master new sub" >expect &&
+               test_cmp expect file3 &&
                ( cd .. && git submodule update -N ) &&
-               test "$(cat ../submod/bar)" = "master submodule" &&
+               echo "master submodule" >expect &&
+               test_cmp expect ../submod/bar &&
                git commit -m "branch2 resolved by mergetool from subdir"
        )
 '
@@ -301,8 +326,8 @@ test_expect_success 'mergetool skips resolved paths when rerere is active' '
        git checkout -b test$test_count branch1 &&
        git submodule update -N &&
        test_must_fail git merge master &&
-       ( yes "l" | git mergetool --no-prompt submod ) &&
-       ( yes "d" "d" | git mergetool --no-prompt ) &&
+       yes "l" | git mergetool --no-prompt submod &&
+       yes "d" "d" | git mergetool --no-prompt &&
        git submodule update -N &&
        output="$(yes "n" | git mergetool --no-prompt)" &&
        test "$output" = "No files need merging"
@@ -343,9 +368,10 @@ test_expect_success 'mergetool takes partial path' '
        git submodule update -N &&
        test_must_fail git merge master &&
 
-       ( yes "" | git mergetool subdir ) &&
+       yes "" | git mergetool subdir &&
 
-       test "$(cat subdir/file3)" = "master new sub"
+       echo "master new sub" >expect &&
+       test_cmp expect subdir/file3
 '
 
 test_expect_success 'mergetool delete/delete conflict' '
@@ -410,14 +436,16 @@ test_expect_success 'deleted vs modified submodule' '
        git checkout -b test$test_count.a test$test_count &&
        test_must_fail git merge master &&
        test -n "$(git ls-files -u)" &&
-       ( yes "" | git mergetool file1 file2 spaced\ name subdir/file3 ) &&
-       ( yes "" | git mergetool both ) &&
-       ( yes "d" | git mergetool file11 file12 ) &&
-       ( yes "r" | git mergetool submod ) &&
+       yes "" | git mergetool file1 file2 spaced\ name subdir/file3 &&
+       yes "" | git mergetool both &&
+       yes "d" | git mergetool file11 file12 &&
+       yes "r" | git mergetool submod &&
        rmdir submod && mv submod-movedaside submod &&
-       test "$(cat submod/bar)" = "branch1 submodule" &&
+       echo "branch1 submodule" >expect &&
+       test_cmp expect submod/bar &&
        git submodule update -N &&
-       test "$(cat submod/bar)" = "master submodule" &&
+       echo "master submodule" >expect &&
+       test_cmp expect submod/bar &&
        output="$(git mergetool --no-prompt)" &&
        test "$output" = "No files need merging" &&
        git commit -m "Merge resolved by keeping module" &&
@@ -427,10 +455,10 @@ test_expect_success 'deleted vs modified submodule' '
        git submodule update -N &&
        test_must_fail git merge master &&
        test -n "$(git ls-files -u)" &&
-       ( yes "" | git mergetool file1 file2 spaced\ name subdir/file3 ) &&
-       ( yes "" | git mergetool both ) &&
-       ( yes "d" | git mergetool file11 file12 ) &&
-       ( yes "l" | git mergetool submod ) &&
+       yes "" | git mergetool file1 file2 spaced\ name subdir/file3 &&
+       yes "" | git mergetool both &&
+       yes "d" | git mergetool file11 file12 &&
+       yes "l" | git mergetool submod &&
        test ! -e submod &&
        output="$(git mergetool --no-prompt)" &&
        test "$output" = "No files need merging" &&
@@ -441,10 +469,10 @@ test_expect_success 'deleted vs modified submodule' '
        git submodule update -N &&
        test_must_fail git merge test$test_count &&
        test -n "$(git ls-files -u)" &&
-       ( yes "" | git mergetool file1 file2 spaced\ name subdir/file3 ) &&
-       ( yes "" | git mergetool both ) &&
-       ( yes "d" | git mergetool file11 file12 ) &&
-       ( yes "r" | git mergetool submod ) &&
+       yes "" | git mergetool file1 file2 spaced\ name subdir/file3 &&
+       yes "" | git mergetool both &&
+       yes "d" | git mergetool file11 file12 &&
+       yes "r" | git mergetool submod &&
        test ! -e submod &&
        test -d submod.orig &&
        git submodule update -N &&
@@ -457,13 +485,15 @@ test_expect_success 'deleted vs modified submodule' '
        git submodule update -N &&
        test_must_fail git merge test$test_count &&
        test -n "$(git ls-files -u)" &&
-       ( yes "" | git mergetool file1 file2 spaced\ name subdir/file3 ) &&
-       ( yes "" | git mergetool both ) &&
-       ( yes "d" | git mergetool file11 file12 ) &&
-       ( yes "l" | git mergetool submod ) &&
-       test "$(cat submod/bar)" = "master submodule" &&
-       git submodule update -N &&
-       test "$(cat submod/bar)" = "master submodule" &&
+       yes "" | git mergetool file1 file2 spaced\ name subdir/file3 &&
+       yes "" | git mergetool both &&
+       yes "d" | git mergetool file11 file12 &&
+       yes "l" | git mergetool submod &&
+       echo "master submodule" >expect &&
+       test_cmp expect submod/bar &&
+       git submodule update -N &&
+       echo "master submodule" >expect &&
+       test_cmp expect submod/bar &&
        output="$(git mergetool --no-prompt)" &&
        test "$output" = "No files need merging" &&
        git commit -m "Merge resolved by keeping module"
@@ -481,14 +511,16 @@ test_expect_success 'file vs modified submodule' '
        git checkout -b test$test_count.a branch1 &&
        test_must_fail git merge master &&
        test -n "$(git ls-files -u)" &&
-       ( yes "" | git mergetool file1 file2 spaced\ name subdir/file3 ) &&
-       ( yes "" | git mergetool both ) &&
-       ( yes "d" | git mergetool file11 file12 ) &&
-       ( yes "r" | git mergetool submod ) &&
+       yes "" | git mergetool file1 file2 spaced\ name subdir/file3 &&
+       yes "" | git mergetool both &&
+       yes "d" | git mergetool file11 file12 &&
+       yes "r" | git mergetool submod &&
        rmdir submod && mv submod-movedaside submod &&
-       test "$(cat submod/bar)" = "branch1 submodule" &&
+       echo "branch1 submodule" >expect &&
+       test_cmp expect submod/bar &&
        git submodule update -N &&
-       test "$(cat submod/bar)" = "master submodule" &&
+       echo "master submodule" >expect &&
+       test_cmp expect submod/bar &&
        output="$(git mergetool --no-prompt)" &&
        test "$output" = "No files need merging" &&
        git commit -m "Merge resolved by keeping module" &&
@@ -497,12 +529,13 @@ test_expect_success 'file vs modified submodule' '
        git checkout -b test$test_count.b test$test_count &&
        test_must_fail git merge master &&
        test -n "$(git ls-files -u)" &&
-       ( yes "" | git mergetool file1 file2 spaced\ name subdir/file3 ) &&
-       ( yes "" | git mergetool both ) &&
-       ( yes "d" | git mergetool file11 file12 ) &&
-       ( yes "l" | git mergetool submod ) &&
+       yes "" | git mergetool file1 file2 spaced\ name subdir/file3 &&
+       yes "" | git mergetool both &&
+       yes "d" | git mergetool file11 file12 &&
+       yes "l" | git mergetool submod &&
        git submodule update -N &&
-       test "$(cat submod)" = "not a submodule" &&
+       echo "not a submodule" >expect &&
+       test_cmp expect submod &&
        output="$(git mergetool --no-prompt)" &&
        test "$output" = "No files need merging" &&
        git commit -m "Merge resolved by keeping file" &&
@@ -513,13 +546,14 @@ test_expect_success 'file vs modified submodule' '
        git submodule update -N &&
        test_must_fail git merge test$test_count &&
        test -n "$(git ls-files -u)" &&
-       ( yes "" | git mergetool file1 file2 spaced\ name subdir/file3 ) &&
-       ( yes "" | git mergetool both ) &&
-       ( yes "d" | git mergetool file11 file12 ) &&
-       ( yes "r" | git mergetool submod ) &&
+       yes "" | git mergetool file1 file2 spaced\ name subdir/file3 &&
+       yes "" | git mergetool both &&
+       yes "d" | git mergetool file11 file12 &&
+       yes "r" | git mergetool submod &&
        test -d submod.orig &&
        git submodule update -N &&
-       test "$(cat submod)" = "not a submodule" &&
+       echo "not a submodule" >expect &&
+       test_cmp expect submod &&
        output="$(git mergetool --no-prompt)" &&
        test "$output" = "No files need merging" &&
        git commit -m "Merge resolved by keeping file" &&
@@ -529,13 +563,15 @@ test_expect_success 'file vs modified submodule' '
        git submodule update -N &&
        test_must_fail git merge test$test_count &&
        test -n "$(git ls-files -u)" &&
-       ( yes "" | git mergetool file1 file2 spaced\ name subdir/file3 ) &&
-       ( yes "" | git mergetool both ) &&
-       ( yes "d" | git mergetool file11 file12 ) &&
-       ( yes "l" | git mergetool submod ) &&
-       test "$(cat submod/bar)" = "master submodule" &&
-       git submodule update -N &&
-       test "$(cat submod/bar)" = "master submodule" &&
+       yes "" | git mergetool file1 file2 spaced\ name subdir/file3 &&
+       yes "" | git mergetool both &&
+       yes "d" | git mergetool file11 file12 &&
+       yes "l" | git mergetool submod &&
+       echo "master submodule" >expect &&
+       test_cmp expect submod/bar &&
+       git submodule update -N &&
+       echo "master submodule" >expect &&
+       test_cmp expect submod/bar &&
        output="$(git mergetool --no-prompt)" &&
        test "$output" = "No files need merging" &&
        git commit -m "Merge resolved by keeping module"
@@ -587,19 +623,23 @@ test_expect_success 'submodule in subdirectory' '
        test_must_fail git merge test$test_count.a &&
        (
                cd subdir &&
-               ( yes "l" | git mergetool subdir_module )
+               yes "l" | git mergetool subdir_module
        ) &&
-       test "$(cat subdir/subdir_module/file15)" = "test$test_count.b" &&
+       echo "test$test_count.b" >expect &&
+       test_cmp expect subdir/subdir_module/file15 &&
        git submodule update -N &&
-       test "$(cat subdir/subdir_module/file15)" = "test$test_count.b" &&
+       echo "test$test_count.b" >expect &&
+       test_cmp expect subdir/subdir_module/file15 &&
        git reset --hard &&
        git submodule update -N &&
 
        test_must_fail git merge test$test_count.a &&
-       ( yes "r" | git mergetool subdir/subdir_module ) &&
-       test "$(cat subdir/subdir_module/file15)" = "test$test_count.b" &&
+       yes "r" | git mergetool subdir/subdir_module &&
+       echo "test$test_count.b" >expect &&
+       test_cmp expect subdir/subdir_module/file15 &&
        git submodule update -N &&
-       test "$(cat subdir/subdir_module/file15)" = "test$test_count.a" &&
+       echo "test$test_count.a" >expect &&
+       test_cmp expect subdir/subdir_module/file15 &&
        git commit -m "branch1 resolved with mergetool"
 '
 
@@ -615,22 +655,25 @@ test_expect_success 'directory vs modified submodule' '
 
        test_must_fail git merge master &&
        test -n "$(git ls-files -u)" &&
-       ( yes "l" | git mergetool submod ) &&
-       test "$(cat submod/file16)" = "not a submodule" &&
+       yes "l" | git mergetool submod &&
+       echo "not a submodule" >expect &&
+       test_cmp expect submod/file16 &&
        rm -rf submod.orig &&
 
        git reset --hard &&
        test_must_fail git merge master &&
        test -n "$(git ls-files -u)" &&
        test ! -e submod.orig &&
-       ( yes "r" | git mergetool submod ) &&
+       yes "r" | git mergetool submod &&
        test -d submod.orig &&
-       test "$(cat submod.orig/file16)" = "not a submodule" &&
+       echo "not a submodule" >expect &&
+       test_cmp expect submod.orig/file16 &&
        rm -r submod.orig &&
        mv submod-movedaside/.git submod &&
        ( cd submod && git clean -f && git reset --hard ) &&
        git submodule update -N &&
-       test "$(cat submod/bar)" = "master submodule" &&
+       echo "master submodule" >expect &&
+       test_cmp expect submod/bar &&
        git reset --hard &&
        rm -rf submod-movedaside &&
 
@@ -638,17 +681,19 @@ test_expect_success 'directory vs modified submodule' '
        git submodule update -N &&
        test_must_fail git merge test$test_count &&
        test -n "$(git ls-files -u)" &&
-       ( yes "l" | git mergetool submod ) &&
+       yes "l" | git mergetool submod &&
        git submodule update -N &&
-       test "$(cat submod/bar)" = "master submodule" &&
+       echo "master submodule" >expect &&
+       test_cmp expect submod/bar &&
 
        git reset --hard &&
        git submodule update -N &&
        test_must_fail git merge test$test_count &&
        test -n "$(git ls-files -u)" &&
        test ! -e submod.orig &&
-       ( yes "r" | git mergetool submod ) &&
-       test "$(cat submod/file16)" = "not a submodule" &&
+       yes "r" | git mergetool submod &&
+       echo "not a submodule" >expect &&
+       test_cmp expect submod/file16 &&
 
        git reset --hard master &&
        ( cd submod && git clean -f && git reset --hard ) &&
index 2e1bb61b41f9278c7463d310e62242697d83a782..7d7b396c2370f0f7b9b0f2d48035d7260e6b464b 100755 (executable)
@@ -412,7 +412,7 @@ do
                test_cmp expected actual
        '
 
-       test_expect_success !PCRE "grep $L with grep.patterntype=perl errors without PCRE" '
+       test_expect_success !FAIL_PREREQS,!PCRE "grep $L with grep.patterntype=perl errors without PCRE" '
                test_must_fail git -c grep.patterntype=perl grep "foo.*bar"
        '
 
@@ -1234,7 +1234,7 @@ test_expect_success PCRE 'grep --perl-regexp pattern' '
        test_cmp expected actual
 '
 
-test_expect_success !PCRE 'grep --perl-regexp pattern errors without PCRE' '
+test_expect_success !FAIL_PREREQS,!PCRE 'grep --perl-regexp pattern errors without PCRE' '
        test_must_fail git grep --perl-regexp "foo.*bar"
 '
 
@@ -1249,7 +1249,7 @@ test_expect_success LIBPCRE2 "grep -P with (*NO_JIT) doesn't error out" '
 
 '
 
-test_expect_success !PCRE 'grep -P pattern errors without PCRE' '
+test_expect_success !FAIL_PREREQS,!PCRE 'grep -P pattern errors without PCRE' '
        test_must_fail git grep -P "foo.*bar"
 '
 
index 1e3ac3c3846342ac84baa096ab00ed602e4cdf27..997f90b42b3e513328b23f718ac7215eb9a960b8 100755 (executable)
@@ -1204,7 +1204,7 @@ test_expect_success $PREREQ 'no in-reply-to and no threading' '
                --from="Example <nobody@example.com>" \
                --to=nobody@example.com \
                --no-thread \
-               $patches $patches >stdout &&
+               $patches >stdout &&
        ! grep "In-Reply-To: " stdout
 '
 
@@ -1224,17 +1224,72 @@ test_expect_success $PREREQ 'sendemail.to works' '
        git send-email \
                --dry-run \
                --from="Example <nobody@example.com>" \
-               $patches $patches >stdout &&
+               $patches >stdout &&
        grep "To: Somebody <somebody@ex.com>" stdout
 '
 
+test_expect_success $PREREQ 'setup sendemail.identity' '
+       git config --replace-all sendemail.to "default@example.com" &&
+       git config --replace-all sendemail.isp.to "isp@example.com" &&
+       git config --replace-all sendemail.cloud.to "cloud@example.com"
+'
+
+test_expect_success $PREREQ 'sendemail.identity: reads the correct identity config' '
+       git -c sendemail.identity=cloud send-email \
+               --dry-run \
+               --from="nobody@example.com" \
+               $patches >stdout &&
+       grep "To: cloud@example.com" stdout
+'
+
+test_expect_success $PREREQ 'sendemail.identity: identity overrides sendemail.identity' '
+       git -c sendemail.identity=cloud send-email \
+               --identity=isp \
+               --dry-run \
+               --from="nobody@example.com" \
+               $patches >stdout &&
+       grep "To: isp@example.com" stdout
+'
+
+test_expect_success $PREREQ 'sendemail.identity: --no-identity clears previous identity' '
+       git -c sendemail.identity=cloud send-email \
+               --no-identity \
+               --dry-run \
+               --from="nobody@example.com" \
+               $patches >stdout &&
+       grep "To: default@example.com" stdout
+'
+
+test_expect_success $PREREQ 'sendemail.identity: bool identity variable existance overrides' '
+       git -c sendemail.identity=cloud \
+               -c sendemail.xmailer=true \
+               -c sendemail.cloud.xmailer=false \
+               send-email \
+               --dry-run \
+               --from="nobody@example.com" \
+               $patches >stdout &&
+       grep "To: cloud@example.com" stdout &&
+       ! grep "X-Mailer" stdout
+'
+
+test_expect_success $PREREQ 'sendemail.identity: bool variable fallback' '
+       git -c sendemail.identity=cloud \
+               -c sendemail.xmailer=false \
+               send-email \
+               --dry-run \
+               --from="nobody@example.com" \
+               $patches >stdout &&
+       grep "To: cloud@example.com" stdout &&
+       ! grep "X-Mailer" stdout
+'
+
 test_expect_success $PREREQ '--no-to overrides sendemail.to' '
        git send-email \
                --dry-run \
                --from="Example <nobody@example.com>" \
                --no-to \
                --to=nobody@example.com \
-               $patches $patches >stdout &&
+               $patches >stdout &&
        grep "To: nobody@example.com" stdout &&
        ! grep "To: Somebody <somebody@ex.com>" stdout
 '
@@ -1245,7 +1300,7 @@ test_expect_success $PREREQ 'sendemail.cc works' '
                --dry-run \
                --from="Example <nobody@example.com>" \
                --to=nobody@example.com \
-               $patches $patches >stdout &&
+               $patches >stdout &&
        grep "Cc: Somebody <somebody@ex.com>" stdout
 '
 
@@ -1256,7 +1311,7 @@ test_expect_success $PREREQ '--no-cc overrides sendemail.cc' '
                --no-cc \
                --cc=bodies@example.com \
                --to=nobody@example.com \
-               $patches $patches >stdout &&
+               $patches >stdout &&
        grep "Cc: bodies@example.com" stdout &&
        ! grep "Cc: Somebody <somebody@ex.com>" stdout
 '
@@ -1268,7 +1323,7 @@ test_expect_success $PREREQ 'sendemail.bcc works' '
                --from="Example <nobody@example.com>" \
                --to=nobody@example.com \
                --smtp-server relay.example.com \
-               $patches $patches >stdout &&
+               $patches >stdout &&
        grep "RCPT TO:<other@ex.com>" stdout
 '
 
@@ -1280,7 +1335,7 @@ test_expect_success $PREREQ '--no-bcc overrides sendemail.bcc' '
                --bcc=bodies@example.com \
                --to=nobody@example.com \
                --smtp-server relay.example.com \
-               $patches $patches >stdout &&
+               $patches >stdout &&
        grep "RCPT TO:<bodies@example.com>" stdout &&
        ! grep "RCPT TO:<other@ex.com>" stdout
 '
@@ -1437,10 +1492,10 @@ test_expect_success $PREREQ 'setup expect' '
        EOF
 '
 
-test_expect_success $PREREQ 'sendemail.transferencoding=7bit fails on 8bit data' '
+test_expect_success $PREREQ '--transfer-encoding overrides sendemail.transferEncoding' '
        clean_fake_sendmail &&
-       git config sendemail.transferEncoding 7bit &&
-       test_must_fail git send-email \
+       test_must_fail git -c sendemail.transferEncoding=8bit \
+               send-email \
                --transfer-encoding=7bit \
                --smtp-server="$(pwd)/fake.sendmail" \
                email-using-8bit \
@@ -1449,11 +1504,10 @@ test_expect_success $PREREQ 'sendemail.transferencoding=7bit fails on 8bit data'
        test -z "$(ls msgtxt*)"
 '
 
-test_expect_success $PREREQ '--transfer-encoding overrides sendemail.transferEncoding' '
+test_expect_success $PREREQ 'sendemail.transferEncoding via config' '
        clean_fake_sendmail &&
-       git config sendemail.transferEncoding 8bit &&
-       test_must_fail git send-email \
-               --transfer-encoding=7bit \
+       test_must_fail git -c sendemail.transferEncoding=7bit \
+               send-email \
                --smtp-server="$(pwd)/fake.sendmail" \
                email-using-8bit \
                2>errors >out &&
@@ -1461,16 +1515,15 @@ test_expect_success $PREREQ '--transfer-encoding overrides sendemail.transferEnc
        test -z "$(ls msgtxt*)"
 '
 
-test_expect_success $PREREQ 'sendemail.transferencoding=8bit' '
+test_expect_success $PREREQ 'sendemail.transferEncoding via cli' '
        clean_fake_sendmail &&
-       git send-email \
-               --transfer-encoding=8bit \
+       test_must_fail git send-email \
+               --transfer-encoding=7bit \
                --smtp-server="$(pwd)/fake.sendmail" \
                email-using-8bit \
                2>errors >out &&
-       sed '1,/^$/d' msgtxt1 >actual &&
-       sed '1,/^$/d' email-using-8bit >expected &&
-       test_cmp expected actual
+       grep "cannot send message as 7bit" errors &&
+       test -z "$(ls msgtxt*)"
 '
 
 test_expect_success $PREREQ 'setup expect' '
@@ -1787,6 +1840,15 @@ test_expect_success '--dump-aliases must be used alone' '
        test_must_fail git send-email --dump-aliases --to=janice@example.com -1 refs/heads/accounting
 '
 
+test_expect_success $PREREQ 'aliases and sendemail.identity' '
+       test_must_fail git \
+               -c sendemail.identity=cloud \
+               -c sendemail.aliasesfile=default-aliases \
+               -c sendemail.cloud.aliasesfile=cloud-aliases \
+               send-email -1 2>stderr &&
+       test_i18ngrep "cloud-aliases" stderr
+'
+
 test_sendmail_aliases () {
        msg="$1" && shift &&
        expect="$@" &&
index 3668263c4046d96fdc79ea3ebe0c28bcb1f2de24..141b7fa35e74b860d91ea7cdabf48730442ed635 100755 (executable)
@@ -3299,4 +3299,24 @@ test_expect_success !MINGW 'W: get-mark & empty orphan commit with erroneous thi
        sed -e s/LFs/LLL/ W-input | tr L "\n" | test_must_fail git fast-import
 '
 
+###
+### series X (other new features)
+###
+
+test_expect_success 'X: handling encoding' '
+       test_tick &&
+       cat >input <<-INPUT_END &&
+       commit refs/heads/encoding
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+       encoding iso-8859-7
+       data <<COMMIT
+       INPUT_END
+
+       printf "Pi: \360\nCOMMIT\n" >>input &&
+
+       git fast-import <input &&
+       git cat-file -p encoding | grep $(printf "\360") &&
+       git log -1 --format=%B encoding | grep $(printf "\317\200")
+'
+
 test_done
index 5690fe28106624f3d666ccdda1dbcdd58673fb13..b4004e05c2a72c4daf95c4ce74b2145a9486efc4 100755 (executable)
@@ -94,22 +94,83 @@ test_expect_success 'fast-export --show-original-ids | git fast-import' '
        test $MUSS = $(git rev-parse --verify refs/tags/muss)
 '
 
-test_expect_success 'iso-8859-1' '
+test_expect_success 'reencoding iso-8859-7' '
 
-       git config i18n.commitencoding ISO8859-1 &&
-       # use author and committer name in ISO-8859-1 to match it.
-       . "$TEST_DIRECTORY"/t3901/8859-1.txt &&
+       test_when_finished "git reset --hard HEAD~1" &&
+       test_config i18n.commitencoding iso-8859-7 &&
        test_tick &&
        echo rosten >file &&
-       git commit -s -m den file &&
-       git fast-export wer^..wer >iso8859-1.fi &&
-       sed "s/wer/i18n/" iso8859-1.fi |
+       git commit -s -F "$TEST_DIRECTORY/t9350/simple-iso-8859-7-commit-message.txt" file &&
+       git fast-export --reencode=yes wer^..wer >iso-8859-7.fi &&
+       sed "s/wer/i18n/" iso-8859-7.fi |
                (cd new &&
                 git fast-import &&
+                # The commit object, if not re-encoded, would be 240 bytes.
+                # Removing the "encoding iso-8859-7\n" header drops 20 bytes.
+                # Re-encoding the Pi character from \xF0 (\360) in iso-8859-7
+                # to \xCF\x80 (\317\200) in UTF-8 adds a byte.  Check for
+                # the expected size.
+                test 221 -eq "$(git cat-file -s i18n)" &&
+                # ...and for the expected translation of bytes.
                 git cat-file commit i18n >actual &&
-                grep "Áéí óú" actual)
+                grep $(printf "\317\200") actual &&
+                # Also make sure the commit does not have the "encoding" header
+                ! grep ^encoding actual)
+'
+
+test_expect_success 'aborting on iso-8859-7' '
 
+       test_when_finished "git reset --hard HEAD~1" &&
+       test_config i18n.commitencoding iso-8859-7 &&
+       echo rosten >file &&
+       git commit -s -F "$TEST_DIRECTORY/t9350/simple-iso-8859-7-commit-message.txt" file &&
+       test_must_fail git fast-export --reencode=abort wer^..wer >iso-8859-7.fi
 '
+
+test_expect_success 'preserving iso-8859-7' '
+
+       test_when_finished "git reset --hard HEAD~1" &&
+       test_config i18n.commitencoding iso-8859-7 &&
+       echo rosten >file &&
+       git commit -s -F "$TEST_DIRECTORY/t9350/simple-iso-8859-7-commit-message.txt" file &&
+       git fast-export --reencode=no wer^..wer >iso-8859-7.fi &&
+       sed "s/wer/i18n-no-recoding/" iso-8859-7.fi |
+               (cd new &&
+                git fast-import &&
+                # The commit object, if not re-encoded, is 240 bytes.
+                # Removing the "encoding iso-8859-7\n" header would drops 20
+                # bytes.  Re-encoding the Pi character from \xF0 (\360) in
+                # iso-8859-7 to \xCF\x80 (\317\200) in UTF-8 adds a byte.
+                # Check for the expected size...
+                test 240 -eq "$(git cat-file -s i18n-no-recoding)" &&
+                # ...as well as the expected byte.
+                git cat-file commit i18n-no-recoding >actual &&
+                grep $(printf "\360") actual &&
+                # Also make sure the commit has the "encoding" header
+                grep ^encoding actual)
+'
+
+test_expect_success 'encoding preserved if reencoding fails' '
+
+       test_when_finished "git reset --hard HEAD~1" &&
+       test_config i18n.commitencoding iso-8859-7 &&
+       echo rosten >file &&
+       git commit -s -F "$TEST_DIRECTORY/t9350/broken-iso-8859-7-commit-message.txt" file &&
+       git fast-export --reencode=yes wer^..wer >iso-8859-7.fi &&
+       sed "s/wer/i18n-invalid/" iso-8859-7.fi |
+               (cd new &&
+                git fast-import &&
+                git cat-file commit i18n-invalid >actual &&
+                # Make sure the commit still has the encoding header
+                grep ^encoding actual &&
+                # Verify that the commit has the expected size; i.e.
+                # that no bytes were re-encoded to a different encoding.
+                test 252 -eq "$(git cat-file -s i18n-invalid)" &&
+                # ...and check for the original special bytes
+                grep $(printf "\360") actual &&
+                grep $(printf "\377") actual)
+'
+
 test_expect_success 'import/export-marks' '
 
        git checkout -b marks master &&
@@ -224,7 +285,6 @@ GIT_COMMITTER_NAME='C O Mitter'; export GIT_COMMITTER_NAME
 
 test_expect_success 'setup copies' '
 
-       git config --unset i18n.commitencoding &&
        git checkout -b copy rein &&
        git mv file file3 &&
        git commit -m move1 &&
diff --git a/t/t9350/broken-iso-8859-7-commit-message.txt b/t/t9350/broken-iso-8859-7-commit-message.txt
new file mode 100644 (file)
index 0000000..d06ad75
--- /dev/null
@@ -0,0 +1 @@
+Pi: ð; Invalid: ÿ
\ No newline at end of file
diff --git a/t/t9350/simple-iso-8859-7-commit-message.txt b/t/t9350/simple-iso-8859-7-commit-message.txt
new file mode 100644 (file)
index 0000000..8b3f0c3
--- /dev/null
@@ -0,0 +1 @@
+Pi: ð
\ No newline at end of file
index 38d6b9043b2039d97cdb634ac86658c45ec36abd..67ff2711f5f5fd9ab95cbf05df8f38868e24bbec 100755 (executable)
@@ -411,6 +411,46 @@ test_expect_failure 'git p4 clone file subset branch' '
        )
 '
 
+# Check that excluded files are omitted during import
+test_expect_success 'git p4 clone complex branches with excluded files' '
+       test_when_finished cleanup_git &&
+       test_create_repo "$git" &&
+       (
+               cd "$git" &&
+               git config git-p4.branchList branch1:branch2 &&
+               git config --add git-p4.branchList branch1:branch3 &&
+               git config --add git-p4.branchList branch1:branch4 &&
+               git config --add git-p4.branchList branch1:branch5 &&
+               git config --add git-p4.branchList branch1:branch6 &&
+               git p4 clone --dest=. --detect-branches -//depot/branch1/file2 -//depot/branch2/file2 -//depot/branch3/file2 -//depot/branch4/file2 -//depot/branch5/file2 -//depot/branch6/file2 //depot@all &&
+               git log --all --graph --decorate --stat &&
+               git reset --hard p4/depot/branch1 &&
+               test_path_is_file file1 &&
+               test_path_is_missing file2 &&
+               test_path_is_file file3 &&
+               git reset --hard p4/depot/branch2 &&
+               test_path_is_file file1 &&
+               test_path_is_missing file2 &&
+               test_path_is_missing file3 &&
+               git reset --hard p4/depot/branch3 &&
+               test_path_is_file file1 &&
+               test_path_is_missing file2 &&
+               test_path_is_missing file3 &&
+               git reset --hard p4/depot/branch4 &&
+               test_path_is_file file1 &&
+               test_path_is_missing file2 &&
+               test_path_is_file file3 &&
+               git reset --hard p4/depot/branch5 &&
+               test_path_is_file file1 &&
+               test_path_is_missing file2 &&
+               test_path_is_file file3 &&
+               git reset --hard p4/depot/branch6 &&
+               test_path_is_file file1 &&
+               test_path_is_missing file2 &&
+               test_path_is_missing file3
+       )
+'
+
 # From a report in http://stackoverflow.com/questions/11893688
 # where --use-client-spec caused branch prefixes not to be removed;
 # every file in git appeared into a subdirectory of the branch name.
@@ -610,4 +650,96 @@ test_expect_success 'Update a file in git side and submit to P4 using client vie
        )
 '
 
+test_expect_success 'restart p4d (case folding enabled)' '
+       stop_and_cleanup_p4d &&
+       start_p4d -C1
+'
+
+#
+# 1: //depot/main/mf1
+# 2: integrate //depot/main/... -> //depot/branch1/...
+# 3: //depot/main/mf2
+# 4: //depot/BRANCH1/B1f3
+# 5: //depot/branch1/b1f4
+#
+test_expect_success !CASE_INSENSITIVE_FS 'basic p4 branches for case folding' '
+       (
+               cd "$cli" &&
+               mkdir -p main &&
+
+               echo mf1 >main/mf1 &&
+               p4 add main/mf1 &&
+               p4 submit -d "main/mf1" &&
+
+               p4 integrate //depot/main/... //depot/branch1/... &&
+               p4 submit -d "integrate main to branch1" &&
+
+               echo mf2 >main/mf2 &&
+               p4 add main/mf2 &&
+               p4 submit -d "main/mf2" &&
+
+               mkdir BRANCH1 &&
+               echo B1f3 >BRANCH1/B1f3 &&
+               p4 add BRANCH1/B1f3 &&
+               p4 submit -d "BRANCH1/B1f3" &&
+
+               echo b1f4 >branch1/b1f4 &&
+               p4 add branch1/b1f4 &&
+               p4 submit -d "branch1/b1f4"
+       )
+'
+
+# Check that files are properly split across branches when ignorecase is set
+test_expect_success !CASE_INSENSITIVE_FS 'git p4 clone, branchList branch definition, ignorecase' '
+       test_when_finished cleanup_git &&
+       test_create_repo "$git" &&
+       (
+               cd "$git" &&
+               git config git-p4.branchList main:branch1 &&
+               git config --type=bool core.ignoreCase true &&
+               git p4 clone --dest=. --detect-branches //depot@all &&
+
+               git log --all --graph --decorate --stat &&
+
+               git reset --hard p4/master &&
+               test_path_is_file mf1 &&
+               test_path_is_file mf2 &&
+               test_path_is_missing B1f3 &&
+               test_path_is_missing b1f4 &&
+
+               git reset --hard p4/depot/branch1 &&
+               test_path_is_file mf1 &&
+               test_path_is_missing mf2 &&
+               test_path_is_file B1f3 &&
+               test_path_is_file b1f4
+       )
+'
+
+# Check that files are properly split across branches when ignorecase is set, use-client-spec case
+test_expect_success !CASE_INSENSITIVE_FS 'git p4 clone with client-spec, branchList branch definition, ignorecase' '
+       client_view "//depot/... //client/..." &&
+       test_when_finished cleanup_git &&
+       test_create_repo "$git" &&
+       (
+               cd "$git" &&
+               git config git-p4.branchList main:branch1 &&
+               git config --type=bool core.ignoreCase true &&
+               git p4 clone --dest=. --use-client-spec --detect-branches //depot@all &&
+
+               git log --all --graph --decorate --stat &&
+
+               git reset --hard p4/master &&
+               test_path_is_file mf1 &&
+               test_path_is_file mf2 &&
+               test_path_is_missing B1f3 &&
+               test_path_is_missing b1f4 &&
+
+               git reset --hard p4/depot/branch1 &&
+               test_path_is_file mf1 &&
+               test_path_is_missing mf2 &&
+               test_path_is_file B1f3 &&
+               test_path_is_file b1f4
+       )
+'
+
 test_done
index 96d25f0c02ccfb04391487b550a702751bf22a11..ec3d937c6a73bb8d89de51839af4bba4c3a73910 100755 (executable)
@@ -22,7 +22,9 @@ test_expect_success 'create exclude repo' '
                mkdir -p wanted discard &&
                echo wanted >wanted/foo &&
                echo discard >discard/foo &&
-               p4 add wanted/foo discard/foo &&
+               echo discard_file >discard_file &&
+               echo discard_file_not >discard_file_not &&
+               p4 add wanted/foo discard/foo discard_file discard_file_not &&
                p4 submit -d "initial revision"
        )
 '
@@ -33,7 +35,9 @@ test_expect_success 'check the repo was created correctly' '
        (
                cd "$git" &&
                test_path_is_file wanted/foo &&
-               test_path_is_file discard/foo
+               test_path_is_file discard/foo &&
+               test_path_is_file discard_file &&
+               test_path_is_file discard_file_not
        )
 '
 
@@ -43,7 +47,21 @@ test_expect_success 'clone, excluding part of repo' '
        (
                cd "$git" &&
                test_path_is_file wanted/foo &&
-               test_path_is_missing discard/foo
+               test_path_is_missing discard/foo &&
+               test_path_is_file discard_file &&
+               test_path_is_file discard_file_not
+       )
+'
+
+test_expect_success 'clone, excluding single file, no trailing /' '
+       test_when_finished cleanup_git &&
+       git p4 clone -//depot/discard_file --dest="$git" //depot/...@all &&
+       (
+               cd "$git" &&
+               test_path_is_file wanted/foo &&
+               test_path_is_file discard/foo &&
+               test_path_is_missing discard_file &&
+               test_path_is_file discard_file_not
        )
 '
 
@@ -52,15 +70,38 @@ test_expect_success 'clone, then sync with exclude' '
        git p4 clone -//depot/discard/... --dest="$git" //depot/...@all &&
        (
                cd "$cli" &&
-               p4 edit wanted/foo discard/foo &&
+               p4 edit wanted/foo discard/foo discard_file_not &&
                date >>wanted/foo &&
                date >>discard/foo &&
+               date >>discard_file_not &&
                p4 submit -d "updating" &&
 
                cd "$git" &&
                git p4 sync -//depot/discard/... &&
                test_path_is_file wanted/foo &&
-               test_path_is_missing discard/foo
+               test_path_is_missing discard/foo &&
+               test_path_is_file discard_file &&
+               test_path_is_file discard_file_not
+       )
+'
+
+test_expect_success 'clone, then sync with exclude, no trailing /' '
+       test_when_finished cleanup_git &&
+       git p4 clone -//depot/discard/... -//depot/discard_file --dest="$git" //depot/...@all &&
+       (
+               cd "$cli" &&
+               p4 edit wanted/foo discard/foo discard_file_not &&
+               date >>wanted/foo &&
+               date >>discard/foo &&
+               date >>discard_file_not &&
+               p4 submit -d "updating" &&
+
+               cd "$git" &&
+               git p4 sync -//depot/discard/... -//depot/discard_file &&
+               test_path_is_file wanted/foo &&
+               test_path_is_missing discard/foo &&
+               test_path_is_missing discard_file &&
+               test_path_is_file discard_file_not
        )
 '
 
index 1286a5b824b77d71061fb890d9f528b46ecc087d..e9276c48f4a2e795899a03681a737c70635ab39c 100755 (executable)
@@ -22,7 +22,10 @@ test_expect_success 'init depot' '
                : >file_to_move &&
                p4 add file_to_delete &&
                p4 add file_to_move &&
-               p4 submit -d "add files to delete"
+               p4 submit -d "add files to delete" &&
+               echo file_to_integrate >file_to_integrate &&
+               p4 add file_to_integrate &&
+               p4 submit -d "add file to integrate"
        )
 '
 
@@ -40,6 +43,7 @@ test_expect_success 'create shelved changelist' '
                p4 delete file_to_delete &&
                p4 edit file_to_move &&
                p4 move file_to_move moved_file &&
+               p4 integrate file_to_integrate integrated_file &&
                p4 opened &&
                p4 shelve -i <<EOF
 Change: new
@@ -53,6 +57,7 @@ Files:
        //depot/file_to_delete
        //depot/file_to_move
        //depot/moved_file
+       //depot/integrated_file
 EOF
 
        ) &&
@@ -65,6 +70,7 @@ EOF
                test_path_is_file file2 &&
                test_cmp file1 "$cli"/file1 &&
                test_cmp file2 "$cli"/file2 &&
+               test_cmp file_to_integrate "$cli"/integrated_file &&
                test_path_is_missing file_to_delete &&
                test_path_is_missing file_to_move &&
                test_path_is_file moved_file
index 8270de74beafb931f09f296557406c0d158d48de..0367cec5fde0514274fbd322bce0a58e1438b3e8 100644 (file)
@@ -309,6 +309,26 @@ test_unset_prereq () {
 }
 
 test_set_prereq () {
+       if test -n "$GIT_TEST_FAIL_PREREQS"
+       then
+               case "$1" in
+               # The "!" case is handled below with
+               # test_unset_prereq()
+               !*)
+                       ;;
+               # (Temporary?) whitelist of things we can't easily
+               # pretend not to support
+               SYMLINKS)
+                       ;;
+               # Inspecting whether GIT_TEST_FAIL_PREREQS is on
+               # should be unaffected.
+               FAIL_PREREQS)
+                       ;;
+               *)
+                       return
+               esac
+       fi
+
        case "$1" in
        !*)
                test_unset_prereq "${1#!}"
index 599fd70e141c7b7252b65ae6603486691864974b..d1ba33745a24c92411baca54604c853373b9bc39 100644 (file)
@@ -386,7 +386,6 @@ unset VISUAL EMAIL LANGUAGE COLUMNS $("$PERL_PATH" -e '
        my @env = keys %ENV;
        my $ok = join("|", qw(
                TRACE
-               TR2_
                DEBUG
                TEST
                .*_TEST
@@ -1607,3 +1606,7 @@ test_lazy_prereq SHA1 '
 test_lazy_prereq REBASE_P '
        test -z "$GIT_TEST_SKIP_REBASE_P"
 '
+
+test_lazy_prereq FAIL_PREREQS '
+       test -n "$GIT_TEST_FAIL_PREREQS"
+'
index cec83bd663d0f27589bd09176e38b084270f56c4..c7e17ec9cb61e6782bc255e6b90381054255c269 100644 (file)
@@ -423,7 +423,7 @@ static int get_importer(struct transport *transport, struct child_process *fasti
        struct helper_data *data = transport->data;
        int cat_blob_fd, code;
        child_process_init(fastimport);
-       fastimport->in = helper->out;
+       fastimport->in = xdup(helper->out);
        argv_array_push(&fastimport->args, "fast-import");
        argv_array_push(&fastimport->args, debug ? "--stats" : "--quiet");
 
index 93a48f3bf8429ebb9b40f6a079e6ef0c6d0edcd8..c9d027625d1f671a0807930480c9a3e4ddd1040d 100644 (file)
@@ -380,8 +380,7 @@ static const struct interval double_width[] = {
 { 0x31C0, 0x31E3 },
 { 0x31F0, 0x321E },
 { 0x3220, 0x3247 },
-{ 0x3250, 0x32FE },
-{ 0x3300, 0x4DBF },
+{ 0x3250, 0x4DBF },
 { 0x4E00, 0xA48C },
 { 0xA490, 0xA4C6 },
 { 0xA960, 0xA97C },
index 24298913c0d7932e20f4677d3b8a4ea62ed69f8d..b2a9f368ecd6ab159cae99deac0b9a9b5ee08f6a 100644 (file)
@@ -722,7 +722,7 @@ static void deepen_by_rev_list(struct packet_writer *writer, int ac,
 {
        struct commit_list *result;
 
-       close_commit_graph(the_repository);
+       close_commit_graph(the_repository->objects);
        result = get_shallow_commits_by_rev_list(ac, av, SHALLOW, NOT_SHALLOW);
        send_shallow(writer, result);
        free_commit_list(result);
@@ -1037,8 +1037,8 @@ static int find_symref(const char *refname, const struct object_id *oid,
        symref_target = resolve_ref_unsafe(refname, 0, NULL, &flag);
        if (!symref_target || (flag & REF_ISSYMREF) == 0)
                die("'%s' is a symref but it is not?", refname);
-       item = string_list_append(cb_data, refname);
-       item->util = xstrdup(symref_target);
+       item = string_list_append(cb_data, strip_namespace(refname));
+       item->util = xstrdup(strip_namespace(symref_target));
        return 0;
 }
 
diff --git a/url.c b/url.c
index 25576c390baa79cb0a203d7f682e8f3442f91a60..1b8ef78ceab03784ad48f8411b20669e2ea1ea1f 100644 (file)
--- a/url.c
+++ b/url.c
@@ -46,9 +46,9 @@ static char *url_decode_internal(const char **query, int len,
                        break;
                }
 
-               if (c == '%') {
+               if (c == '%' && (len < 0 || len >= 3)) {
                        int val = hex2chr(q + 1);
-                       if (0 <= val) {
+                       if (0 < val) {
                                strbuf_addch(out, val);
                                q += 3;
                                len -= 3;
index 3a78fbf5044fbc5d5624ce94e54eb4ab325e2cf1..e74a6d402255b0eaf1022863ba30305930d29b6f 100644 (file)
@@ -58,7 +58,12 @@ PATTERNS("java",
         "|[-+*/<>%&^|=!]="
         "|--|\\+\\+|<<=?|>>>?=?|&&|\\|\\|"),
 PATTERNS("matlab",
-        "^[[:space:]]*((classdef|function)[[:space:]].*)$|^%%[[:space:]].*$",
+        /*
+         * Octave pattern is mostly the same as matlab, except that '%%%' and
+         * '##' can also be used to begin code sections, in addition to '%%'
+         * that is understood by both.
+         */
+        "^[[:space:]]*((classdef|function)[[:space:]].*)$|^(%%%?|##)[[:space:]].*$",
         "[a-zA-Z_][a-zA-Z0-9_]*|[-+0-9.e]+|[=~<>]=|\\.[*/\\^']|\\|\\||&&"),
 PATTERNS("objc",
         /* Negate C statements that can look like functions */
@@ -130,6 +135,12 @@ PATTERNS("ruby", "^[ \t]*((class|module|def)[ \t].*)$",
         "(@|@@|\\$)?[a-zA-Z_][a-zA-Z0-9_]*"
         "|[-+0-9.e]+|0[xXbB]?[0-9a-fA-F]+|\\?(\\\\C-)?(\\\\M-)?."
         "|//=?|[-+*/<>%&^|=!]=|<<=?|>>=?|===|\\.{1,3}|::|[!=]~"),
+PATTERNS("rust",
+        "^[\t ]*((pub(\\([^\\)]+\\))?[\t ]+)?((async|const|unsafe|extern([\t ]+\"[^\"]+\"))[\t ]+)?(struct|enum|union|mod|trait|fn|impl)[< \t]+[^;]*)$",
+        /* -- */
+        "[a-zA-Z_][a-zA-Z0-9_]*"
+        "|[0-9][0-9_a-fA-Fiosuxz]*(\\.([0-9]*[eE][+-]?)?[0-9_fF]*)?"
+        "|[-+*\\/<>%&^|=!:]=|<<=?|>>=?|&&|\\|\\||->|=>|\\.{2}=|\\.{3}|::"),
 PATTERNS("bibtex", "(@[a-zA-Z]{1,}[ \t]*\\{{0,1}[ \t]*[^ \t\"@',\\#}{~%]*).*$",
         "[={}\"]|[^={}\" \t]+"),
 PATTERNS("tex", "^(\\\\((sub)*section|chapter|part)\\*{0,1}\\{.*)$",
index 4f66cd9ce178d855245712b42d681ff8bcda0636..5b4793caa34e3ab981268662efdc1d840ac53390 100644 (file)
@@ -228,9 +228,12 @@ struct worktree *find_worktree(struct worktree **list,
                free(to_free);
                return NULL;
        }
-       for (; *list; list++)
-               if (!fspathcmp(path, real_path((*list)->path)))
+       for (; *list; list++) {
+               const char *wt_path = real_path_if_valid((*list)->path);
+
+               if (wt_path && !fspathcmp(path, wt_path))
                        break;
+       }
        free(path);
        free(to_free);
        return *list;
index ea3cf64d4c399ae84c156b850002459a8ffde72c..1e45ab7b92749b653484f522701f5d86521b03c7 100644 (file)
--- a/wrapper.c
+++ b/wrapper.c
@@ -502,7 +502,7 @@ int git_mkstemps_mode(char *pattern, int suffix_len, int mode)
         * Try TMP_MAX different filenames.
         */
        gettimeofday(&tv, NULL);
-       value = ((size_t)(tv.tv_usec << 16)) ^ tv.tv_sec ^ getpid();
+       value = ((uint64_t)tv.tv_usec << 16) ^ tv.tv_sec ^ getpid();
        filename_template = &pattern[len - 6 - suffix_len];
        for (count = 0; count < TMP_MAX; ++count) {
                uint64_t v = value;
index d2a1bec226ba0dfef9e3ddd96c0b3a83ea6884bb..0bccef542fccee5deaa6ee3c740f60879aa97fb3 100644 (file)
@@ -2076,9 +2076,7 @@ static void wt_porcelain_v2_submodule_state(
 /*
  * Fix-up changed entries before we print them.
  */
-static void wt_porcelain_v2_fix_up_changed(
-       struct string_list_item *it,
-       struct wt_status *s)
+static void wt_porcelain_v2_fix_up_changed(struct string_list_item *it)
 {
        struct wt_status_change_data *d = it->util;
 
@@ -2138,7 +2136,7 @@ static void wt_porcelain_v2_print_changed_entry(
        char submodule_token[5];
        char sep_char, eol_char;
 
-       wt_porcelain_v2_fix_up_changed(it, s);
+       wt_porcelain_v2_fix_up_changed(it);
        wt_porcelain_v2_submodule_state(d, submodule_token);
 
        key[0] = d->index_status ? d->index_status : '.';