Merge branch 'jc/dotdot-is-parent-directory' into maint-1.7.11
authorJunio C Hamano <gitster@pobox.com>
Wed, 12 Sep 2012 21:00:34 +0000 (14:00 -0700)
committerJunio C Hamano <gitster@pobox.com>
Wed, 12 Sep 2012 21:00:34 +0000 (14:00 -0700)
"git log .." errored out saying it is both rev range and a path when
there is no disambiguating "--" is on the command line.  Update the
command line parser to interpret ".." as a path in such a case.

* jc/dotdot-is-parent-directory:
  specifying ranges: we did not mean to make ".." an empty set

84 files changed:
Documentation/CodingGuidelines
Documentation/RelNotes/1.7.11.6.txt [new file with mode: 0644]
Documentation/asciidoc.conf
Documentation/git-checkout.txt
Documentation/git-config.txt
Documentation/git-describe.txt
Documentation/git-filter-branch.txt
Documentation/git-fsck.txt
Documentation/git-lost-found.txt
Documentation/git-pack-refs.txt
Documentation/git-pull.txt
Documentation/git-replace.txt
Documentation/git-submodule.txt
Documentation/git-tag.txt
Documentation/git.txt
Documentation/gitcli.txt
Documentation/rev-list-options.txt
Documentation/user-manual.conf
GIT-VERSION-GEN
Makefile
RelNotes
builtin.h
builtin/apply.c
builtin/blame.c
builtin/cat-file.c
builtin/checkout.c
builtin/commit.c
builtin/config.c
builtin/diff.c
builtin/for-each-ref.c
builtin/merge.c
builtin/receive-pack.c
cache.h
combine-diff.c
compat/terminal.c
contrib/ciabot/INSTALL [new file with mode: 0644]
contrib/ciabot/README
contrib/ciabot/ciabot.py
contrib/ciabot/ciabot.sh
diff-lib.c
diff-no-index.c
diff.c
diff.h
diffcore-rename.c
diffcore.h
fsck.c
git-mergetool.sh
git-rebase--interactive.sh
git-send-email.perl
git-sh-setup.sh
git-stash.sh
git-submodule.sh
gitweb/gitweb.perl
http.c
http.h
merge-recursive.c
notes-merge.c
patch-ids.c
read-cache.c
remote-curl.c
revision.c
run-command.c
setup.c
submodule.c
t/lib-httpd.sh
t/lib-httpd/apache.conf
t/perf/perf-lib.sh
t/t1450-fsck.sh
t/t2107-update-index-basic.sh
t/t3401-rebase-partial.sh
t/t4022-diff-rewrite.sh
t/t4054-diff-bogus-tree.sh [new file with mode: 0755]
t/t5400-send-pack.sh
t/t5540-http-push.sh
t/t5541-http-push.sh
t/t5550-http-fetch.sh
t/t5551-http-fetch.sh
t/t6300-for-each-ref.sh
t/t7400-submodule-basic.sh
t/t7406-submodule-update.sh
t/t7610-mergetool.sh
t/t9001-send-email.sh
t/test-lib-functions.sh
tree-diff.c

index 4557711..57da6aa 100644 (file)
@@ -76,11 +76,19 @@ For shell scripts specifically (not exhaustive):
 
  - We do not use Process Substitution <(list) or >(list).
 
+ - Do not write control structures on a single line with semicolon.
+   "then" should be on the next line for if statements, and "do"
+   should be on the next line for "while" and "for".
+
  - We prefer "test" over "[ ... ]".
 
  - We do not write the noiseword "function" in front of shell
    functions.
 
+ - We prefer a space between the function name and the parentheses. The
+   opening "{" should also be on the same line.
+   E.g.: my_function () {
+
  - As to use of grep, stick to a subset of BRE (namely, no \{m,n\},
    [::], [==], nor [..]) for portability.
 
diff --git a/Documentation/RelNotes/1.7.11.6.txt b/Documentation/RelNotes/1.7.11.6.txt
new file mode 100644 (file)
index 0000000..ba7d3c3
--- /dev/null
@@ -0,0 +1,84 @@
+Git v1.7.11.6 Release Notes
+===========================
+
+Fixes since v1.7.11.5
+---------------------
+
+ * "ciabot" script (in contrib/) has been updated with extensive
+   documentation.
+
+ * "git foo" errored out with "Not a directory" when the user had a
+   non-directory on $PATH, and worse yet it masked an alias "foo" from
+   running.
+
+ * When the user exports a non-default IFS without HT, scripts that
+   rely on being able to parse "ls-files -s | while read a b c..."
+   started to fail.  Protect them from such a misconfiguration.
+
+ * When the user gives an argument that can be taken as both a
+   revision name and a pathname without disambiguating with "--", we
+   used to give a help message "Use '--' to separate".  The message
+   has been clarified to show where that '--' goes on the command
+   line.
+
+ * Documentation for the configuration file format had a confusing
+   example.
+
+ * Older parts of the documentation described as if having a regular
+   file in .git/refs/ hierarchy were the only way to have branches and
+   tags, which is not true for quite some time.
+
+ * It was generally understood that "--long-option"s to many of our
+   subcommands can be abbreviated to the unique prefix, but it was not
+   easy to find it described for new readers of the documentation set.
+
+ * The "--topo-order", "--date-order" (and the lack of either means
+   the default order) options to "rev-list" and "log" family of
+   commands were poorly described in the documentation.
+
+ * "git commit --amend" let the user edit the log message and then
+   died when the human-readable committer name was given
+   insufficiently by getpwent(3).
+
+ * The exit status code from "git config" was way overspecified while
+   being incorrect.  The implementation has been updated to give the
+   documented status for a case that was documented, and introduce a
+   new code for "all other errors".
+
+ * The output from "git diff -B" for a file that ends with an
+   incomplete line did not put "\ No newline..." on a line of its own.
+
+ * "git diff" had a confusion between taking data from a path in the
+   working tree and taking data from an object that happens to have
+   name 0{40} recorded in a tree.
+
+ * The "--rebase" option to "git pull" can be abbreviated to "-r",
+   but we didn't document it.
+
+ * When "git push" triggered the automatic gc on the receiving end, a
+   message from "git prune" that said it was removing cruft leaked to
+   the standard output, breaking the communication protocol.
+
+ * The reflog entries left by "git rebase" and "git rebase -i" were
+   inconsistent (the interactive one gave an abbreviated object name).
+
+ * "git send-email" did not unquote encoded words that appear on the
+   header correctly, and lost "_" from strings.
+
+ * "git stash apply/pop" did not trigger "rerere" upon conflicts
+   unlike other mergy operations.
+
+ * "git submodule <cmd> path" did not error out when the path to the
+   submodule was misspelt.
+
+ * "git submodule update -f" did not update paths in the working tree
+   that has local changes.
+   (merge 01d4721 sz/submodule-force-update later to maint).
+
+ * "gitweb" when used with PATH_INFO failed to notice directories with
+   SP (and other characters that need URL-style quoting) in them.
+
+ * Fallback 'getpass' implementation made unportable use of stdio API.
+
+ * A utility shell function test_seq has been added as a replacement
+   for the 'seq' utility found on some platforms.
index a26d245..1273a85 100644 (file)
@@ -36,7 +36,7 @@ ifndef::git-asciidoc-no-roff[]
 # v1.72 breaks with this because it replaces dots not in roff requests.
 [listingblock]
 <example><title>{title}</title>
-<literallayout>
+<literallayout class="monospaced">
 ifdef::doctype-manpage[]
 &#10;.ft C&#10;
 endif::doctype-manpage[]
@@ -53,7 +53,7 @@ ifdef::doctype-manpage[]
 # The following two small workarounds insert a simple paragraph after screen
 [listingblock]
 <example><title>{title}</title>
-<literallayout>
+<literallayout class="monospaced">
 |
 </literallayout><simpara></simpara>
 {title#}</example>
index 63a2516..11cc7f0 100644 (file)
@@ -84,11 +84,11 @@ entries; instead, unmerged entries are ignored.
        When checking out paths from the index, check out stage #2
        ('ours') or #3 ('theirs') for unmerged paths.
 
--b::
+-b <new_branch>::
        Create a new branch named <new_branch> and start it at
        <start_point>; see linkgit:git-branch[1] for details.
 
--B::
+-B <new_branch>::
        Creates the branch <new_branch> and start it at <start_point>;
        if it already exists, then reset it to <start_point>. This is
        equivalent to running "git branch" with "-f"; see
@@ -124,7 +124,7 @@ explicitly give a name with '-b' in such a case.
        <commit> is not a branch name.  See the "DETACHED HEAD" section
        below for details.
 
---orphan::
+--orphan <new_branch>::
        Create a new 'orphan' branch, named <new_branch>, started from
        <start_point> and switch to it.  The first commit made on this
        new branch will have no parents and it will be the root of a new
index d9463cb..21b8f61 100644 (file)
@@ -54,16 +54,16 @@ configuration file by default, and options '--system', '--global',
 '--file <filename>' can be used to tell the command to write to
 that location (you can say '--local' but that is the default).
 
-This command will fail (with exit code ret) if:
+This command will fail with non-zero status upon error.  Some exit
+codes are:
 
 . The config file is invalid (ret=3),
 . can not write to the config file (ret=4),
 . no section or name was provided (ret=2),
 . the section or key is invalid (ret=1),
 . you try to unset an option which does not exist (ret=5),
-. you try to unset/set an option for which multiple lines match (ret=5),
-. you try to use an invalid regexp (ret=6), or
-. you use '--global' option without $HOME being properly set (ret=128).
+. you try to unset/set an option for which multiple lines match (ret=5), or
+. you try to use an invalid regexp (ret=6).
 
 On success, the command returns the exit code 0.
 
@@ -258,7 +258,7 @@ Given a .git/config like this:
 
        ; Proxy settings
        [core]
-               gitproxy="proxy-command" for kernel.org
+               gitproxy=proxy-command for kernel.org
                gitproxy=default-proxy ; for all the rest
 
 you can set the filemode to true with
@@ -333,7 +333,7 @@ To actually match only values with an exclamation mark, you have to
 To add a new proxy, without altering any of the existing ones, use
 
 ------------
-% git config core.gitproxy '"proxy-command" for example.com'
+% git config --add core.gitproxy '"proxy-command" for example.com'
 ------------
 
 An example to use customized color from the configuration in your
index 039cce2..72d6bb6 100644 (file)
@@ -36,12 +36,12 @@ OPTIONS
 
 --all::
        Instead of using only the annotated tags, use any ref
-       found in `.git/refs/`.  This option enables matching
+       found in `refs/` namespace.  This option enables matching
        any known branch, remote-tracking branch, or lightweight tag.
 
 --tags::
        Instead of using only the annotated tags, use any tag
-       found in `.git/refs/tags`.  This option enables matching
+       found in `refs/tags` namespace.  This option enables matching
        a lightweight (non-annotated) tag.
 
 --contains::
index 81f5823..15e7ac8 100644 (file)
@@ -32,7 +32,8 @@ changes, which would normally have no effect.  Nevertheless, this may be
 useful in the future for compensating for some git bugs or such,
 therefore such a usage is permitted.
 
-*NOTE*: This command honors `.git/info/grafts` and `.git/refs/replace/`.
+*NOTE*: This command honors `.git/info/grafts` file and refs in
+the `refs/replace/` namespace.
 If you have any grafts or replacement refs defined, running this command
 will make them permanent.
 
index bbb25da..da348fc 100644 (file)
@@ -23,8 +23,8 @@ OPTIONS
        An object to treat as the head of an unreachability trace.
 +
 If no objects are given, 'git fsck' defaults to using the
-index file, all SHA1 references in .git/refs/*, and all reflogs (unless
---no-reflogs is given) as heads.
+index file, all SHA1 references in `refs` namespace, and all reflogs
+(unless --no-reflogs is given) as heads.
 
 --unreachable::
        Print out objects that exist but that aren't reachable from any
index c406a11..d549328 100644 (file)
@@ -48,7 +48,8 @@ $ gitk $(cd .git/lost-found/commit && echo ??*)
 ------------
 
 After making sure you know which the object is the tag you are looking
-for, you can reconnect it to your regular .git/refs hierarchy.
+for, you can reconnect it to your regular `refs` hierarchy by using
+the `update-ref` command.
 
 ------------
 $ git cat-file -t 1ef2b196
index 10afd4e..f131677 100644 (file)
@@ -14,7 +14,8 @@ DESCRIPTION
 -----------
 
 Traditionally, tips of branches and tags (collectively known as
-'refs') were stored one file per ref under `$GIT_DIR/refs`
+'refs') were stored one file per ref in a (sub)directory
+under `$GIT_DIR/refs`
 directory.  While many branch tips tend to be updated often,
 most tags and some branch tips are never updated.  When a
 repository has hundreds or thousands of tags, this
@@ -22,13 +23,14 @@ one-file-per-ref format both wastes storage and hurts
 performance.
 
 This command is used to solve the storage and performance
-problem by stashing the refs in a single file,
+problem by storing the refs in a single file,
 `$GIT_DIR/packed-refs`.  When a ref is missing from the
-traditional `$GIT_DIR/refs` hierarchy, it is looked up in this
+traditional `$GIT_DIR/refs` directory hierarchy, it is looked
+up in this
 file and used if found.
 
 Subsequent updates to branches always create new files under
-`$GIT_DIR/refs` hierarchy.
+`$GIT_DIR/refs` directory hierarchy.
 
 A recommended practice to deal with a repository with too many
 refs is to pack its refs with `--all --prune` once, and
@@ -57,6 +59,15 @@ a repository with many branches of historical interests.
 The command usually removes loose refs under `$GIT_DIR/refs`
 hierarchy after packing them.  This option tells it not to.
 
+
+BUGS
+----
+
+Older documentation written before the packed-refs mechanism was
+introduced may still say things like ".git/refs/heads/<branch> file
+exists" when it means "branch <branch> exists".
+
+
 GIT
 ---
 Part of the linkgit:git[1] suite
index defb544..67fa5ee 100644 (file)
@@ -101,6 +101,7 @@ include::merge-options.txt[]
 
 :git-pull: 1
 
+-r::
 --rebase::
        Rebase the current branch on top of the upstream branch after
        fetching.  If there is a remote-tracking branch corresponding to
index 17df525..51131d0 100644 (file)
@@ -14,14 +14,13 @@ SYNOPSIS
 
 DESCRIPTION
 -----------
-Adds a 'replace' reference in `.git/refs/replace/`
+Adds a 'replace' reference in `refs/replace/` namespace.
 
 The name of the 'replace' reference is the SHA1 of the object that is
 replaced. The content of the 'replace' reference is the SHA1 of the
 replacement object.
 
-Unless `-f` is given, the 'replace' reference must not yet exist in
-`.git/refs/replace/` directory.
+Unless `-f` is given, the 'replace' reference must not yet exist.
 
 Replacement references will be used by default by all git commands
 except those doing reachability traversal (prune, pack transfer and
index fbbbcb2..2de7bf0 100644 (file)
@@ -149,6 +149,11 @@ submodule with the `--init` option.
 +
 If `--recursive` is specified, this command will recurse into the
 registered submodules, and update any nested submodules within.
++
+If `--force` is specified, the submodule will be checked out (using
+`git checkout --force` if appropriate), even if the commit specified in the
+index of the containing repository already matches the commit checked out in
+the submodule.
 
 summary::
        Show commit summary between the given commit (defaults to HEAD) and
@@ -210,7 +215,9 @@ OPTIONS
        This option is only valid for add and update commands.
        When running add, allow adding an otherwise ignored submodule path.
        When running update, throw away local changes in submodules when
-       switching to a different commit.
+       switching to a different commit; and always run a checkout operation
+       in the submodule, even if the commit listed in the index of the
+       containing repository matches the commit checked out in the submodule.
 
 --cached::
        This option is only valid for status and summary commands.  These
index e36a7c3..247534e 100644 (file)
@@ -20,11 +20,10 @@ SYNOPSIS
 DESCRIPTION
 -----------
 
-Add a tag reference in `.git/refs/tags/`, unless `-d/-l/-v` is given
+Add a tag reference in `refs/tags/`, unless `-d/-l/-v` is given
 to delete, list or verify tags.
 
-Unless `-f` is given, the tag to be created must not yet exist in the
-`.git/refs/tags/` directory.
+Unless `-f` is given, the named tag must not yet exist.
 
 If one of `-a`, `-s`, or `-u <key-id>` is passed, the command
 creates a 'tag' object, and requires a tag message.  Unless
index 677eb95..165d13a 100644 (file)
@@ -44,9 +44,10 @@ unreleased) version of git, that is available from 'master'
 branch of the `git.git` repository.
 Documentation for older releases are available here:
 
-* link:v1.7.11.5/git.html[documentation for release 1.7.11.5]
+* link:v1.7.11.6/git.html[documentation for release 1.7.11.6]
 
 * release notes for
+  link:RelNotes/1.7.11.6.txt[1.7.11.6],
   link:RelNotes/1.7.11.5.txt[1.7.11.5],
   link:RelNotes/1.7.11.4.txt[1.7.11.4],
   link:RelNotes/1.7.11.3.txt[1.7.11.3],
index ea17f7a..3e72a5d 100644 (file)
@@ -62,6 +62,14 @@ scripting git:
    `git log -1 HEAD` but write `git log -1 HEAD --`; the former will not work
    if you happen to have a file called `HEAD` in the work tree.
 
+ * many commands allow a long option "--option" to be abbreviated
+   only to their unique prefix (e.g. if there is no other option
+   whose name begins with "opt", you may be able to spell "--opt" to
+   invoke the "--option" flag), but you should fully spell them out
+   when writing your scripts; later versions of Git may introduce a
+   new option whose name shares the same prefix, e.g. "--optimize",
+   to make a short prefix that used to be unique no longer unique.
+
 
 ENHANCED OPTION PARSER
 ----------------------
index d9b2b5b..def1340 100644 (file)
@@ -578,16 +578,33 @@ Commit Ordering
 
 By default, the commits are shown in reverse chronological order.
 
---topo-order::
+--date-order::
+       Show no parents before all of its children are shown, but
+       otherwise show commits in the commit timestamp order.
 
-       This option makes them appear in topological order (i.e.
-       descendant commits are shown before their parents).
+--topo-order::
+       Show no parents before all of its children are shown, and
+       avoid showing commits on multiple lines of history
+       intermixed.
++
+For example, in a commit history like this:
++
+----------------------------------------------------------------
 
---date-order::
+    ---1----2----4----7
+       \              \
+        3----5----6----8---
 
-       This option is similar to '--topo-order' in the sense that no
-       parent comes before all of its children, but otherwise things
-       are still ordered in the commit timestamp order.
+----------------------------------------------------------------
++
+where the numbers denote the order of commit timestamps, `git
+rev-list` and friends with `--date-order` show the commits in the
+timestamp order: 8 7 6 5 4 3 2 1.
++
+With `--topo-order`, they would show 8 6 5 3 7 4 2 1 (or 8 7 4 2 6 5
+3 1); some older commits are shown before newer ones in order to
+avoid showing the commits from two parallel development track mixed
+together.
 
 --reverse::
 
index 339b309..d87294d 100644 (file)
@@ -14,7 +14,7 @@ ifdef::backend-docbook[]
 # "unbreak" docbook-xsl v1.68 for manpages. v1.69 works with or without this.
 [listingblock]
 <example><title>{title}</title>
-<literallayout>
+<literallayout class="monospaced">
 |
 </literallayout>
 {title#}</example>
index bab4056..754b94e 100755 (executable)
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 GVF=GIT-VERSION-FILE
-DEF_VER=v1.7.11.5
+DEF_VER=v1.7.11.6
 
 LF='
 '
index df58303..1f72c1c 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1005,6 +1005,7 @@ ifeq ($(uname_S),SunOS)
        NO_REGEX = YesPlease
        NO_FNMATCH_CASEFOLD = YesPlease
        NO_MSGFMT_EXTENDED_OPTIONS = YesPlease
+       HAVE_DEV_TTY = YesPlease
        ifeq ($(uname_R),5.6)
                SOCKLEN_T = int
                NO_HSTRERROR = YesPlease
index f6490be..d04f1ae 120000 (symlink)
--- a/RelNotes
+++ b/RelNotes
@@ -1 +1 @@
-Documentation/RelNotes/1.7.11.5.txt
\ No newline at end of file
+Documentation/RelNotes/1.7.11.6.txt
\ No newline at end of file
index e426de3..dffb34e 100644 (file)
--- a/builtin.h
+++ b/builtin.h
@@ -44,7 +44,7 @@ extern int check_pager_config(const char *cmd);
 struct diff_options;
 extern void setup_diff_pager(struct diff_options *);
 
-extern int textconv_object(const char *path, unsigned mode, const unsigned char *sha1, char **buf, unsigned long *buf_size);
+extern int textconv_object(const char *path, unsigned mode, const unsigned char *sha1, int sha1_valid, char **buf, unsigned long *buf_size);
 
 extern int cmd_add(int argc, const char **argv, const char *prefix);
 extern int cmd_annotate(int argc, const char **argv, const char *prefix);
@@ -85,7 +85,6 @@ extern int cmd_get_tar_commit_id(int argc, const char **argv, const char *prefix
 extern int cmd_grep(int argc, const char **argv, const char *prefix);
 extern int cmd_hash_object(int argc, const char **argv, const char *prefix);
 extern int cmd_help(int argc, const char **argv, const char *prefix);
-extern int cmd_http_fetch(int argc, const char **argv, const char *prefix);
 extern int cmd_index_pack(int argc, const char **argv, const char *prefix);
 extern int cmd_init_db(int argc, const char **argv, const char *prefix);
 extern int cmd_log(int argc, const char **argv, const char *prefix);
@@ -110,7 +109,6 @@ extern int cmd_notes(int argc, const char **argv, const char *prefix);
 extern int cmd_pack_objects(int argc, const char **argv, const char *prefix);
 extern int cmd_pack_redundant(int argc, const char **argv, const char *prefix);
 extern int cmd_patch_id(int argc, const char **argv, const char *prefix);
-extern int cmd_pickaxe(int argc, const char **argv, const char *prefix);
 extern int cmd_prune(int argc, const char **argv, const char *prefix);
 extern int cmd_prune_packed(int argc, const char **argv, const char *prefix);
 extern int cmd_push(int argc, const char **argv, const char *prefix);
@@ -143,7 +141,6 @@ extern int cmd_update_ref(int argc, const char **argv, const char *prefix);
 extern int cmd_update_server_info(int argc, const char **argv, const char *prefix);
 extern int cmd_upload_archive(int argc, const char **argv, const char *prefix);
 extern int cmd_upload_archive_writer(int argc, const char **argv, const char *prefix);
-extern int cmd_upload_tar(int argc, const char **argv, const char *prefix);
 extern int cmd_var(int argc, const char **argv, const char *prefix);
 extern int cmd_verify_tag(int argc, const char **argv, const char *prefix);
 extern int cmd_version(int argc, const char **argv, const char *prefix);
index b4428ea..ca54ff3 100644 (file)
@@ -184,7 +184,6 @@ struct patch {
        int is_new, is_delete;  /* -1 = unknown, 0 = false, 1 = true */
        int rejected;
        unsigned ws_rule;
-       unsigned long deflate_origlen;
        int lines_added, lines_deleted;
        int score;
        unsigned int is_toplevel_relative:1;
index 960c58d..cad4111 100644 (file)
@@ -110,6 +110,7 @@ static int diff_hunks(mmfile_t *file_a, mmfile_t *file_b, long ctxlen,
 int textconv_object(const char *path,
                    unsigned mode,
                    const unsigned char *sha1,
+                   int sha1_valid,
                    char **buf,
                    unsigned long *buf_size)
 {
@@ -117,7 +118,7 @@ int textconv_object(const char *path,
        struct userdiff_driver *textconv;
 
        df = alloc_filespec(path);
-       fill_filespec(df, sha1, mode);
+       fill_filespec(df, sha1, sha1_valid, mode);
        textconv = get_textconv(df);
        if (!textconv) {
                free_filespec(df);
@@ -142,7 +143,7 @@ static void fill_origin_blob(struct diff_options *opt,
 
                num_read_blob++;
                if (DIFF_OPT_TST(opt, ALLOW_TEXTCONV) &&
-                   textconv_object(o->path, o->mode, o->blob_sha1, &file->ptr, &file_size))
+                   textconv_object(o->path, o->mode, o->blob_sha1, 1, &file->ptr, &file_size))
                        ;
                else
                        file->ptr = read_sha1_file(o->blob_sha1, &type, &file_size);
@@ -406,8 +407,7 @@ static struct origin *find_origin(struct scoreboard *sb,
        paths[1] = NULL;
 
        diff_tree_setup_paths(paths, &diff_opts);
-       if (diff_setup_done(&diff_opts) < 0)
-               die("diff-setup");
+       diff_setup_done(&diff_opts);
 
        if (is_null_sha1(origin->commit->object.sha1))
                do_diff_cache(parent->tree->object.sha1, &diff_opts);
@@ -493,8 +493,7 @@ static struct origin *find_rename(struct scoreboard *sb,
        diff_opts.single_follow = origin->path;
        paths[0] = NULL;
        diff_tree_setup_paths(paths, &diff_opts);
-       if (diff_setup_done(&diff_opts) < 0)
-               die("diff-setup");
+       diff_setup_done(&diff_opts);
 
        if (is_null_sha1(origin->commit->object.sha1))
                do_diff_cache(parent->tree->object.sha1, &diff_opts);
@@ -1074,8 +1073,7 @@ static int find_copy_in_parent(struct scoreboard *sb,
 
        paths[0] = NULL;
        diff_tree_setup_paths(paths, &diff_opts);
-       if (diff_setup_done(&diff_opts) < 0)
-               die("diff-setup");
+       diff_setup_done(&diff_opts);
 
        /* Try "find copies harder" on new path if requested;
         * we do not want to use diffcore_rename() actually to
@@ -2123,7 +2121,7 @@ static struct commit *fake_working_tree_commit(struct diff_options *opt,
                switch (st.st_mode & S_IFMT) {
                case S_IFREG:
                        if (DIFF_OPT_TST(opt, ALLOW_TEXTCONV) &&
-                           textconv_object(read_from, mode, null_sha1, &buf_ptr, &buf_len))
+                           textconv_object(read_from, mode, null_sha1, 0, &buf_ptr, &buf_len))
                                strbuf_attach(&buf, buf_ptr, buf_len, buf_len + 1);
                        else if (strbuf_read_file(&buf, read_from, st.st_size) != st.st_size)
                                die_errno("cannot open or read '%s'", read_from);
@@ -2515,7 +2513,7 @@ parse_done:
                        die("no such path %s in %s", path, final_commit_name);
 
                if (DIFF_OPT_TST(&sb.revs->diffopt, ALLOW_TEXTCONV) &&
-                   textconv_object(path, o->mode, o->blob_sha1, (char **) &sb.final_buf,
+                   textconv_object(path, o->mode, o->blob_sha1, 1, (char **) &sb.final_buf,
                                    &sb.final_buf_size))
                        ;
                else
index 36a9104..60568f9 100644 (file)
@@ -146,7 +146,7 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name)
                        die("git cat-file --textconv %s: <object> must be <sha1:path>",
                            obj_name);
 
-               if (!textconv_object(obj_context.path, obj_context.mode, sha1, &buf, &size))
+               if (!textconv_object(obj_context.path, obj_context.mode, sha1, 1, &buf, &size))
                        die("git cat-file --textconv: unable to run textconv on %s",
                            obj_name);
                break;
index e060efb..45aa0a8 100644 (file)
@@ -315,8 +315,7 @@ static void show_local_changes(struct object *head, struct diff_options *opts)
        init_revisions(&rev, NULL);
        rev.diffopt.flags = opts->flags;
        rev.diffopt.output_format |= DIFF_FORMAT_NAME_STATUS;
-       if (diff_setup_done(&rev.diffopt) < 0)
-               die(_("diff_setup_done failed"));
+       diff_setup_done(&rev.diffopt);
        add_pending_object(&rev, head, NULL);
        run_diff_index(&rev, 0);
 }
index 95eeab1..20cef95 100644 (file)
@@ -725,7 +725,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
        strbuf_release(&sb);
 
        /* This checks if committer ident is explicitly given */
-       strbuf_addstr(&committer_ident, git_committer_info(0));
+       strbuf_addstr(&committer_ident, git_committer_info(IDENT_STRICT));
        if (use_editor && include_status) {
                char *ai_tmp, *ci_tmp;
                if (whence != FROM_COMMIT)
index 33c8820..b44277c 100644 (file)
@@ -160,7 +160,7 @@ static int show_config(const char *key_, const char *value_, void *cb)
 
 static int get_value(const char *key_, const char *regex_)
 {
-       int ret = -1;
+       int ret = CONFIG_GENERIC_ERROR;
        char *global = NULL, *repo_config = NULL;
        const char *system_wide = NULL, *local;
        struct config_include_data inc = CONFIG_INCLUDE_INIT;
@@ -198,11 +198,14 @@ static int get_value(const char *key_, const char *regex_)
                if (regcomp(key_regexp, key, REG_EXTENDED)) {
                        fprintf(stderr, "Invalid key pattern: %s\n", key_);
                        free(key);
+                       ret = CONFIG_INVALID_PATTERN;
                        goto free_strings;
                }
        } else {
-               if (git_config_parse_key(key_, &key, NULL))
+               if (git_config_parse_key(key_, &key, NULL)) {
+                       ret = CONFIG_INVALID_KEY;
                        goto free_strings;
+               }
        }
 
        if (regex_) {
@@ -214,6 +217,7 @@ static int get_value(const char *key_, const char *regex_)
                regexp = (regex_t*)xmalloc(sizeof(regex_t));
                if (regcomp(regexp, regex_, REG_EXTENDED)) {
                        fprintf(stderr, "Invalid pattern: %s\n", regex_);
+                       ret = CONFIG_INVALID_PATTERN;
                        goto free_strings;
                }
        }
index da8f6aa..9650be2 100644 (file)
@@ -29,6 +29,8 @@ static void stuff_change(struct diff_options *opt,
                         unsigned old_mode, unsigned new_mode,
                         const unsigned char *old_sha1,
                         const unsigned char *new_sha1,
+                        int old_sha1_valid,
+                        int new_sha1_valid,
                         const char *old_name,
                         const char *new_name)
 {
@@ -54,8 +56,8 @@ static void stuff_change(struct diff_options *opt,
 
        one = alloc_filespec(old_name);
        two = alloc_filespec(new_name);
-       fill_filespec(one, old_sha1, old_mode);
-       fill_filespec(two, new_sha1, new_mode);
+       fill_filespec(one, old_sha1, old_sha1_valid, old_mode);
+       fill_filespec(two, new_sha1, new_sha1_valid, new_mode);
 
        diff_queue(&diff_queued_diff, one, two);
 }
@@ -84,6 +86,7 @@ static int builtin_diff_b_f(struct rev_info *revs,
        stuff_change(&revs->diffopt,
                     blob[0].mode, canon_mode(st.st_mode),
                     blob[0].sha1, null_sha1,
+                    1, 0,
                     path, path);
        diffcore_std(&revs->diffopt);
        diff_flush(&revs->diffopt);
@@ -108,6 +111,7 @@ static int builtin_diff_blobs(struct rev_info *revs,
        stuff_change(&revs->diffopt,
                     blob[0].mode, blob[1].mode,
                     blob[0].sha1, blob[1].sha1,
+                    1, 1,
                     blob[0].name, blob[1].name);
        diffcore_std(&revs->diffopt);
        diff_flush(&revs->diffopt);
@@ -298,8 +302,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
        argc = setup_revisions(argc, argv, &rev, NULL);
        if (!rev.diffopt.output_format) {
                rev.diffopt.output_format = DIFF_FORMAT_PATCH;
-               if (diff_setup_done(&rev.diffopt) < 0)
-                       die(_("diff_setup_done failed"));
+               diff_setup_done(&rev.diffopt);
        }
 
        DIFF_OPT_SET(&rev.diffopt, RECURSIVE);
index b01d76a..0c5294e 100644 (file)
@@ -962,7 +962,9 @@ static int opt_parse_sort(const struct option *opt, const char *arg, int unset)
        if (!arg) /* should --no-sort void the list ? */
                return -1;
 
-       *sort_tail = s = xcalloc(1, sizeof(*s));
+       s = xcalloc(1, sizeof(*s));
+       s->next = *sort_tail;
+       *sort_tail = s;
 
        if (*arg == '-') {
                s->reverse = 1;
index dd50a0c..e81fde6 100644 (file)
@@ -404,8 +404,7 @@ static void finish(struct commit *head_commit,
                opts.output_format |=
                        DIFF_FORMAT_SUMMARY | DIFF_FORMAT_DIFFSTAT;
                opts.detect_rename = DIFF_DETECT_RENAME;
-               if (diff_setup_done(&opts) < 0)
-                       die(_("diff_setup_done failed"));
+               diff_setup_done(&opts);
                diff_tree_sha1(head, new_head, "", &opts);
                diffcore_std(&opts);
                diff_flush(&opts);
index 0afb8b2..3f05d97 100644 (file)
@@ -977,7 +977,8 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
                        const char *argv_gc_auto[] = {
                                "gc", "--auto", "--quiet", NULL,
                        };
-                       run_command_v_opt(argv_gc_auto, RUN_GIT_CMD);
+                       int opt = RUN_GIT_CMD | RUN_COMMAND_STDOUT_TO_STDERR;
+                       run_command_v_opt(argv_gc_auto, opt);
                }
                if (auto_update_server_info)
                        update_server_info(0);
diff --git a/cache.h b/cache.h
index 8958104..75b3bea 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -1103,6 +1103,7 @@ extern int update_server_info(int);
 #define CONFIG_NO_WRITE 4
 #define CONFIG_NOTHING_SET 5
 #define CONFIG_INVALID_PATTERN 6
+#define CONFIG_GENERIC_ERROR 7
 
 typedef int (*config_fn_t)(const char *, const char *, void *);
 extern int git_default_config(const char *, const char *, void *);
index 9786680..bb1cc96 100644 (file)
@@ -111,7 +111,7 @@ static char *grab_blob(const unsigned char *sha1, unsigned int mode,
                return xcalloc(1, 1);
        } else if (textconv) {
                struct diff_filespec *df = alloc_filespec(path);
-               fill_filespec(df, sha1, mode);
+               fill_filespec(df, sha1, 1, mode);
                *size = fill_textconv(textconv, df, &blob);
                free_filespec(df);
        } else {
@@ -823,7 +823,7 @@ static void show_patch_diff(struct combine_diff_path *elem, int num_parent,
                                                   &result_size, NULL, NULL);
                } else if (textconv) {
                        struct diff_filespec *df = alloc_filespec(elem->path);
-                       fill_filespec(df, null_sha1, st.st_mode);
+                       fill_filespec(df, null_sha1, 0, st.st_mode);
                        result_size = fill_textconv(textconv, df, &result);
                        free_filespec(df);
                } else if (0 <= (fd = open(elem->path, O_RDONLY))) {
index 6d16c8f..bbb038d 100644 (file)
@@ -59,6 +59,7 @@ char *git_terminal_prompt(const char *prompt, int echo)
 
        r = strbuf_getline(&buf, fh, '\n');
        if (!echo) {
+               fseek(fh, SEEK_CUR, 0);
                putc('\n', fh);
                fflush(fh);
        }
diff --git a/contrib/ciabot/INSTALL b/contrib/ciabot/INSTALL
new file mode 100644 (file)
index 0000000..7222961
--- /dev/null
@@ -0,0 +1,54 @@
+= Installation instructions =
+
+Two scripts are included.  The Python one (ciabot.py) is faster and
+more capable; the shell one (ciabot.sh) is a fallback in case Python
+gives your git hosting site indigestion. (I know of no such sites.)
+
+It is no longer necessary to modify the script in order to put it
+in place; in fact, this is now discouraged. It is entirely
+configurable with the following git config variables:
+
+ciabot.project = name of the project
+ciabot.repo = name of the project repo for gitweb/cgit purposes
+ciabot.xmlrpc  = if true, ship notifications via XML-RPC
+ciabot.revformat = format in which the revision is shown
+
+The revformat variable may have the following values
+raw -> full hex ID of commit
+short -> first 12 chars of hex ID
+describe -> describe relative to last tag, falling back to short
+
+ciabot.project defaults to the directory name of the repository toplevel.
+ciabot.repo defaults to ciabot.project lowercased.
+ciabot.xmlrpc defaults to True
+ciabot.revformat defaults to 'describe'.
+
+This means that in the normal case you need not do any configuration at all,
+however setting ciabot.project will allow the hook to run slightly faster.
+
+Once you've set these variables, try your script with -n to see the
+notification message dumped to stdout and verify that it looks sane.
+
+To live-test these scripts, your project needs to have been registered with
+the CIA site.  Here are the steps:
+
+1. Open an IRC window on irc://freenode/commits or your registered
+   project IRC channel.
+
+2. Run ciabot.py and/or ciabot.sh from any directory under git
+   control.
+
+You should see a notification on the channel for your most recent commit.
+
+After verifying correct function, install one of these scripts either
+in a post-commit hook or in an update hook.
+
+In post-commit, run it without arguments. It will query for
+current HEAD and the latest commit ID to get the information it
+needs.
+
+In update, call it with a refname followed by a list of commits:
+You want to reverse the order git rev-list emits because it lists
+from most recent to oldest.
+
+/path/to/ciabot.py ${refname} $(git rev-list ${oldhead}..${newhead} | tac)
index 3b916ac..2dfe1f9 100644 (file)
@@ -8,5 +8,4 @@ You probably want the Python version; it's faster, more capable, and
 better documented.  The shell version is maintained only as a fallback
 for use on hosting sites that don't permit Python hook scripts.
 
-You will find installation instructions for each script in its comment
-header.
+See the file INSTALL for installation instructions.
index 8ce04eb..bd24395 100755 (executable)
 # usage: ciabot.py [-V] [-n] [-p projectname]  [refname [commits...]]
 #
 # This script is meant to be run either in a post-commit hook or in an
-# update hook.  If there's nothing unusual about your hosting setup,
-# you can specify the project name and repo with config variables and
-# avoid having to modify this script.  Try it with -n to see the
-# notification mail dumped to stdout and verify that it looks
-# sane. With -V it dumps its version and exits.
+# update hook. Try it with -n to see the notification mail dumped to
+# stdout and verify that it looks sane. With -V it dumps its version
+# and exits.
 #
 # In post-commit, run it without arguments. It will query for
 # current HEAD and the latest commit ID to get the information it
 # /path/to/ciabot.py ${refname} $(git rev-list ${oldhead}..${newhead} | tac)
 #
 # Configuration variables affecting this script:
-# ciabot.project = name of the project (required)
+#
+# ciabot.project = name of the project
 # ciabot.repo = name of the project repo for gitweb/cgit purposes
 # ciabot.xmlrpc  = if true (default), ship notifications via XML-RPC
 # ciabot.revformat = format in which the revision is shown
 #
-# The ciabot.repo value defaults to ciabot.project lowercased.
+# ciabot.project defaults to the directory name of the repository toplevel.
+# ciabot.repo defaults to ciabot.project lowercased.
+#
+# This means that in the normal case you need not do any configuration at all,
+# but setting the project name will speed it up slightly.
 #
 # The revformat variable may have the following values
 # raw -> full hex ID of commit
@@ -102,7 +105,7 @@ toaddr = "cia@cia.vc"
 # Identify the generator script.
 # Should only change when the script itself gets a new home and maintainer.
 generator = "http://www.catb.org/~esr/ciabot.py"
-version = "3.5"
+version = "3.6"
 
 def do(command):
     return commands.getstatusoutput(command)[1]
@@ -192,10 +195,17 @@ if __name__ == "__main__":
             print "ciabot.py: version", version
             sys.exit(0)
 
-    # Cough and die if user has not specified a project
+    # The project variable defaults to the name of the repository toplevel.
     if not project:
-        sys.stderr.write("ciabot.py: no project specified, bailing out.\n")
-        sys.exit(1)
+        here = os.getcwd()
+        while True:
+            if os.path.exists(os.path.join(here, ".git")):
+                project = os.path.basename(here)
+                break
+            elif here == '/':
+                sys.stderr.write("ciabot.py: no .git below root!\n")
+                sys.exit(1)
+            here = os.path.dirname(here)
 
     if not repo:
         repo = project.lower()
index dde7400..3fbbc53 100755 (executable)
 # usage: ciabot.sh [-V] [-n] [-p projectname] [refname commit]
 #
 # This script is meant to be run either in a post-commit hook or in an
-# update hook.  If there's nothing unusual about your hosting setup,
-# you can specify the project name and repo with config variables and
-# avoid having to modify this script.  Try it with -n to see the
-# notification mail dumped to stdout and verify that it looks
-# sane. With -V it dumps its version and exits.
+# update hook. Try it with -n to see the notification mail dumped to
+# stdout and verify that it looks sane. With -V it dumps its version
+# and exits.
 #
 # In post-commit, run it without arguments. It will query for
 # current HEAD and the latest commit ID to get the information it
 # most recent to least - better to ship notifactions from oldest to newest.
 #
 # Configuration variables affecting this script:
-# ciabot.project = name of the project (makes -p option unnecessary)
+#
+# ciabot.project = name of the project
 # ciabot.repo = name of the project repo for gitweb/cgit purposes
 # ciabot.revformat = format in which the revision is shown
 #
-# The ciabot.repo defaults to ciabot.project lowercased.
+# ciabot.project defaults to the directory name of the repository toplevel.
+# ciabot.repo defaults to ciabot.project lowercased.
+#
+# This means that in the normal case you need not do any configuration at all,
+# but setting the project name will speed it up slightly.
 #
 # The revformat variable may have the following values
 # raw -> full hex ID of commit
 # shpped from an update in their actual order.)
 #
 
-# The project as known to CIA. You can also hardwire this or set it with a
-# -p option.
+# The project as known to CIA. You can set this with a -p option,
+# or let it default to the directory name of the repo toplevel.
 project=$(git config --get ciabot.project)
 
+if [ -z $project ]
+then
+    here=`pwd`;
+    while :; do
+       if [ -d $here/.git ]
+       then
+           project=`basename $here`
+           break
+       elif [ $here = '/' ]
+       then
+           echo "ciabot.sh: no .git below root!"
+           exit 1
+       fi
+       here=`dirname $here`
+    done
+fi
+
 # Name of the repo for gitweb/cgit purposes
 repo=$(git config --get ciabot.repo)
 [ -z $repo] && repo=$(echo "${project}" | tr '[A-Z]' '[a-z]')
@@ -100,7 +120,7 @@ urlprefix="http://${host}/cgi-bin/cgit.cgi/${repo}/commit/?id="
 # Identify the script. The 'generator' variable should change only
 # when the script itself gets a new home and maintainer.
 generator="http://www.catb.org/~esr/ciabot/ciabot.sh"
-version=3.4
+version=3.5
 
 # Addresses for the e-mail
 from="CIABOT-NOREPLY@${hostname}"
index fc0dff3..f35de0f 100644 (file)
@@ -206,7 +206,8 @@ int run_diff_files(struct rev_info *revs, unsigned int option)
                        if (silent_on_removed)
                                continue;
                        diff_addremove(&revs->diffopt, '-', ce->ce_mode,
-                                      ce->sha1, ce->name, 0);
+                                      ce->sha1, !is_null_sha1(ce->sha1),
+                                      ce->name, 0);
                        continue;
                }
                changed = match_stat_with_submodule(&revs->diffopt, ce, &st,
@@ -220,6 +221,7 @@ int run_diff_files(struct rev_info *revs, unsigned int option)
                newmode = ce_mode_from_stat(ce, st.st_mode);
                diff_change(&revs->diffopt, oldmode, newmode,
                            ce->sha1, (changed ? null_sha1 : ce->sha1),
+                           !is_null_sha1(ce->sha1), (changed ? 0 : !is_null_sha1(ce->sha1)),
                            ce->name, 0, dirty_submodule);
 
        }
@@ -236,11 +238,12 @@ int run_diff_files(struct rev_info *revs, unsigned int option)
 static void diff_index_show_file(struct rev_info *revs,
                                 const char *prefix,
                                 struct cache_entry *ce,
-                                const unsigned char *sha1, unsigned int mode,
+                                const unsigned char *sha1, int sha1_valid,
+                                unsigned int mode,
                                 unsigned dirty_submodule)
 {
        diff_addremove(&revs->diffopt, prefix[0], mode,
-                      sha1, ce->name, dirty_submodule);
+                      sha1, sha1_valid, ce->name, dirty_submodule);
 }
 
 static int get_stat_data(struct cache_entry *ce,
@@ -295,7 +298,7 @@ static void show_new_file(struct rev_info *revs,
            &dirty_submodule, &revs->diffopt) < 0)
                return;
 
-       diff_index_show_file(revs, "+", new, sha1, mode, dirty_submodule);
+       diff_index_show_file(revs, "+", new, sha1, !is_null_sha1(sha1), mode, dirty_submodule);
 }
 
 static int show_modified(struct rev_info *revs,
@@ -312,7 +315,7 @@ static int show_modified(struct rev_info *revs,
                          &dirty_submodule, &revs->diffopt) < 0) {
                if (report_missing)
                        diff_index_show_file(revs, "-", old,
-                                            old->sha1, old->ce_mode, 0);
+                                            old->sha1, 1, old->ce_mode, 0);
                return -1;
        }
 
@@ -347,7 +350,8 @@ static int show_modified(struct rev_info *revs,
                return 0;
 
        diff_change(&revs->diffopt, oldmode, mode,
-                   old->sha1, sha1, old->name, 0, dirty_submodule);
+                   old->sha1, sha1, 1, !is_null_sha1(sha1),
+                   old->name, 0, dirty_submodule);
        return 0;
 }
 
@@ -380,7 +384,7 @@ static void do_oneway_diff(struct unpack_trees_options *o,
                struct diff_filepair *pair;
                pair = diff_unmerge(&revs->diffopt, idx->name);
                if (tree)
-                       fill_filespec(pair->one, tree->sha1, tree->ce_mode);
+                       fill_filespec(pair->one, tree->sha1, 1, tree->ce_mode);
                return;
        }
 
@@ -396,7 +400,7 @@ static void do_oneway_diff(struct unpack_trees_options *o,
         * Something removed from the tree?
         */
        if (!idx) {
-               diff_index_show_file(revs, "-", tree, tree->sha1, tree->ce_mode, 0);
+               diff_index_show_file(revs, "-", tree, tree->sha1, 1, tree->ce_mode, 0);
                return;
        }
 
index 7d805a0..74da659 100644 (file)
@@ -82,7 +82,7 @@ static struct diff_filespec *noindex_filespec(const char *name, int mode)
        if (!name)
                name = "/dev/null";
        s = alloc_filespec(name);
-       fill_filespec(s, null_sha1, mode);
+       fill_filespec(s, null_sha1, 0, mode);
        if (name == file_from_standard_input)
                populate_from_stdin(s);
        return s;
@@ -258,8 +258,7 @@ void diff_no_index(struct rev_info *revs,
        DIFF_OPT_SET(&revs->diffopt, NO_INDEX);
 
        revs->max_count = -2;
-       if (diff_setup_done(&revs->diffopt) < 0)
-               die("diff_setup_done failed");
+       diff_setup_done(&revs->diffopt);
 
        setup_diff_pager(&revs->diffopt);
        DIFF_OPT_SET(&revs->diffopt, EXIT_WITH_STATUS);
diff --git a/diff.c b/diff.c
index 62cbe14..f1b0447 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -574,6 +574,7 @@ static void emit_rewrite_lines(struct emit_callback *ecb,
        if (!endp) {
                const char *plain = diff_get_color(ecb->color_diff,
                                                   DIFF_PLAIN);
+               putc('\n', ecb->opt->file);
                emit_line_0(ecb->opt, plain, reset, '\\',
                            nneof, strlen(nneof));
        }
@@ -2541,12 +2542,12 @@ void free_filespec(struct diff_filespec *spec)
 }
 
 void fill_filespec(struct diff_filespec *spec, const unsigned char *sha1,
-                  unsigned short mode)
+                  int sha1_valid, unsigned short mode)
 {
        if (mode) {
                spec->mode = canon_mode(mode);
                hashcpy(spec->sha1, sha1);
-               spec->sha1_valid = !is_null_sha1(sha1);
+               spec->sha1_valid = sha1_valid;
        }
 }
 
@@ -3187,7 +3188,7 @@ void diff_setup(struct diff_options *options)
        }
 }
 
-int diff_setup_done(struct diff_options *options)
+void diff_setup_done(struct diff_options *options)
 {
        int count = 0;
 
@@ -3286,8 +3287,6 @@ int diff_setup_done(struct diff_options *options)
                options->output_format = DIFF_FORMAT_NO_OUTPUT;
                DIFF_OPT_SET(options, EXIT_WITH_STATUS);
        }
-
-       return 0;
 }
 
 static int opt_arg(const char *arg, int arg_short, const char *arg_long, int *val)
@@ -4693,6 +4692,7 @@ static int is_submodule_ignored(const char *path, struct diff_options *options)
 void diff_addremove(struct diff_options *options,
                    int addremove, unsigned mode,
                    const unsigned char *sha1,
+                   int sha1_valid,
                    const char *concatpath, unsigned dirty_submodule)
 {
        struct diff_filespec *one, *two;
@@ -4724,9 +4724,9 @@ void diff_addremove(struct diff_options *options,
        two = alloc_filespec(concatpath);
 
        if (addremove != '+')
-               fill_filespec(one, sha1, mode);
+               fill_filespec(one, sha1, sha1_valid, mode);
        if (addremove != '-') {
-               fill_filespec(two, sha1, mode);
+               fill_filespec(two, sha1, sha1_valid, mode);
                two->dirty_submodule = dirty_submodule;
        }
 
@@ -4739,6 +4739,7 @@ void diff_change(struct diff_options *options,
                 unsigned old_mode, unsigned new_mode,
                 const unsigned char *old_sha1,
                 const unsigned char *new_sha1,
+                int old_sha1_valid, int new_sha1_valid,
                 const char *concatpath,
                 unsigned old_dirty_submodule, unsigned new_dirty_submodule)
 {
@@ -4753,6 +4754,8 @@ void diff_change(struct diff_options *options,
                const unsigned char *tmp_c;
                tmp = old_mode; old_mode = new_mode; new_mode = tmp;
                tmp_c = old_sha1; old_sha1 = new_sha1; new_sha1 = tmp_c;
+               tmp = old_sha1_valid; old_sha1_valid = new_sha1_valid;
+                       new_sha1_valid = tmp;
                tmp = old_dirty_submodule; old_dirty_submodule = new_dirty_submodule;
                        new_dirty_submodule = tmp;
        }
@@ -4763,8 +4766,8 @@ void diff_change(struct diff_options *options,
 
        one = alloc_filespec(concatpath);
        two = alloc_filespec(concatpath);
-       fill_filespec(one, old_sha1, old_mode);
-       fill_filespec(two, new_sha1, new_mode);
+       fill_filespec(one, old_sha1, old_sha1_valid, old_mode);
+       fill_filespec(two, new_sha1, new_sha1_valid, new_mode);
        one->dirty_submodule = old_dirty_submodule;
        two->dirty_submodule = new_dirty_submodule;
 
diff --git a/diff.h b/diff.h
index e027650..e25addb 100644 (file)
--- a/diff.h
+++ b/diff.h
@@ -19,12 +19,14 @@ typedef void (*change_fn_t)(struct diff_options *options,
                 unsigned old_mode, unsigned new_mode,
                 const unsigned char *old_sha1,
                 const unsigned char *new_sha1,
+                int old_sha1_valid, int new_sha1_valid,
                 const char *fullpath,
                 unsigned old_dirty_submodule, unsigned new_dirty_submodule);
 
 typedef void (*add_remove_fn_t)(struct diff_options *options,
                    int addremove, unsigned mode,
                    const unsigned char *sha1,
+                   int sha1_valid,
                    const char *fullpath, unsigned dirty_submodule);
 
 typedef void (*diff_format_fn_t)(struct diff_queue_struct *q,
@@ -214,12 +216,15 @@ extern void diff_addremove(struct diff_options *,
                           int addremove,
                           unsigned mode,
                           const unsigned char *sha1,
+                          int sha1_valid,
                           const char *fullpath, unsigned dirty_submodule);
 
 extern void diff_change(struct diff_options *,
                        unsigned mode1, unsigned mode2,
                        const unsigned char *sha1,
                        const unsigned char *sha2,
+                       int sha1_valid,
+                       int sha2_valid,
                        const char *fullpath,
                        unsigned dirty_submodule1, unsigned dirty_submodule2);
 
@@ -241,7 +246,7 @@ extern int git_diff_ui_config(const char *var, const char *value, void *cb);
 extern int diff_use_color_default;
 extern void diff_setup(struct diff_options *);
 extern int diff_opt_parse(struct diff_options *, const char **, int);
-extern int diff_setup_done(struct diff_options *);
+extern void diff_setup_done(struct diff_options *);
 
 #define DIFF_DETECT_RENAME     1
 #define DIFF_DETECT_COPY       2
index 216a7a4..512d0ac 100644 (file)
@@ -48,7 +48,7 @@ static struct diff_rename_dst *locate_rename_dst(struct diff_filespec *two,
                memmove(rename_dst + first + 1, rename_dst + first,
                        (rename_dst_nr - first - 1) * sizeof(*rename_dst));
        rename_dst[first].two = alloc_filespec(two->path);
-       fill_filespec(rename_dst[first].two, two->sha1, two->mode);
+       fill_filespec(rename_dst[first].two, two->sha1, two->sha1_valid, two->mode);
        rename_dst[first].pair = NULL;
        return &(rename_dst[first]);
 }
index be0739c..1c16c85 100644 (file)
@@ -55,7 +55,7 @@ struct diff_filespec {
 extern struct diff_filespec *alloc_filespec(const char *);
 extern void free_filespec(struct diff_filespec *);
 extern void fill_filespec(struct diff_filespec *, const unsigned char *,
-                         unsigned short);
+                         int, unsigned short);
 
 extern int diff_populate_filespec(struct diff_filespec *, int);
 extern void diff_free_filespec_data(struct diff_filespec *);
diff --git a/fsck.c b/fsck.c
index 4c63b2c..7395ef6 100644 (file)
--- a/fsck.c
+++ b/fsck.c
@@ -139,6 +139,7 @@ static int verify_ordered(unsigned mode1, const char *name1, unsigned mode2, con
 static int fsck_tree(struct tree *item, int strict, fsck_error error_func)
 {
        int retval;
+       int has_null_sha1 = 0;
        int has_full_path = 0;
        int has_empty_name = 0;
        int has_zero_pad = 0;
@@ -157,9 +158,12 @@ static int fsck_tree(struct tree *item, int strict, fsck_error error_func)
        while (desc.size) {
                unsigned mode;
                const char *name;
+               const unsigned char *sha1;
 
-               tree_entry_extract(&desc, &name, &mode);
+               sha1 = tree_entry_extract(&desc, &name, &mode);
 
+               if (is_null_sha1(sha1))
+                       has_null_sha1 = 1;
                if (strchr(name, '/'))
                        has_full_path = 1;
                if (!*name)
@@ -207,6 +211,8 @@ static int fsck_tree(struct tree *item, int strict, fsck_error error_func)
        }
 
        retval = 0;
+       if (has_null_sha1)
+               retval += error_func(&item->object, FSCK_WARN, "contains entries pointing to null sha1");
        if (has_full_path)
                retval += error_func(&item->object, FSCK_WARN, "contains full pathnames");
        if (has_empty_name)
index 0db0c44..c50e18a 100755 (executable)
@@ -18,270 +18,301 @@ require_work_tree
 
 # Returns true if the mode reflects a symlink
 is_symlink () {
-    test "$1" = 120000
+       test "$1" = 120000
 }
 
 is_submodule () {
-    test "$1" = 160000
+       test "$1" = 160000
 }
 
 local_present () {
-    test -n "$local_mode"
+       test -n "$local_mode"
 }
 
 remote_present () {
-    test -n "$remote_mode"
+       test -n "$remote_mode"
 }
 
 base_present () {
-    test -n "$base_mode"
+       test -n "$base_mode"
 }
 
 cleanup_temp_files () {
-    if test "$1" = --save-backup ; then
-       rm -rf -- "$MERGED.orig"
-       test -e "$BACKUP" && mv -- "$BACKUP" "$MERGED.orig"
-       rm -f -- "$LOCAL" "$REMOTE" "$BASE"
-    else
-       rm -f -- "$LOCAL" "$REMOTE" "$BASE" "$BACKUP"
-    fi
+       if test "$1" = --save-backup
+       then
+               rm -rf -- "$MERGED.orig"
+               test -e "$BACKUP" && mv -- "$BACKUP" "$MERGED.orig"
+               rm -f -- "$LOCAL" "$REMOTE" "$BASE"
+       else
+               rm -f -- "$LOCAL" "$REMOTE" "$BASE" "$BACKUP"
+       fi
 }
 
 describe_file () {
-    mode="$1"
-    branch="$2"
-    file="$3"
-
-    printf "  {%s}: " "$branch"
-    if test -z "$mode"; then
-       echo "deleted"
-    elif is_symlink "$mode" ; then
-       echo "a symbolic link -> '$(cat "$file")'"
-    elif is_submodule "$mode" ; then
-       echo "submodule commit $file"
-    else
-       if base_present; then
-           echo "modified file"
+       mode="$1"
+       branch="$2"
+       file="$3"
+
+       printf "  {%s}: " "$branch"
+       if test -z "$mode"
+       then
+               echo "deleted"
+       elif is_symlink "$mode"
+       then
+               echo "a symbolic link -> '$(cat "$file")'"
+       elif is_submodule "$mode"
+       then
+               echo "submodule commit $file"
+       elif base_present
+       then
+               echo "modified file"
        else
-           echo "created file"
+               echo "created file"
        fi
-    fi
 }
 
-
 resolve_symlink_merge () {
-    while true; do
-       printf "Use (l)ocal or (r)emote, or (a)bort? "
-       read ans || return 1
-       case "$ans" in
-           [lL]*)
-               git checkout-index -f --stage=2 -- "$MERGED"
-               git add -- "$MERGED"
-               cleanup_temp_files --save-backup
-               return 0
-               ;;
-           [rR]*)
-               git checkout-index -f --stage=3 -- "$MERGED"
-               git add -- "$MERGED"
-               cleanup_temp_files --save-backup
-               return 0
-               ;;
-           [aA]*)
-               return 1
-               ;;
-           esac
+       while true
+       do
+               printf "Use (l)ocal or (r)emote, or (a)bort? "
+               read ans || return 1
+               case "$ans" in
+               [lL]*)
+                       git checkout-index -f --stage=2 -- "$MERGED"
+                       git add -- "$MERGED"
+                       cleanup_temp_files --save-backup
+                       return 0
+                       ;;
+               [rR]*)
+                       git checkout-index -f --stage=3 -- "$MERGED"
+                       git add -- "$MERGED"
+                       cleanup_temp_files --save-backup
+                       return 0
+                       ;;
+               [aA]*)
+                       return 1
+                       ;;
+               esac
        done
 }
 
 resolve_deleted_merge () {
-    while true; do
-       if base_present; then
-           printf "Use (m)odified or (d)eleted file, or (a)bort? "
-       else
-           printf "Use (c)reated or (d)eleted file, or (a)bort? "
-       fi
-       read ans || return 1
-       case "$ans" in
-           [mMcC]*)
-               git add -- "$MERGED"
-               cleanup_temp_files --save-backup
-               return 0
-               ;;
-           [dD]*)
-               git rm -- "$MERGED" > /dev/null
-               cleanup_temp_files
-               return 0
-               ;;
-           [aA]*)
-               return 1
-               ;;
-           esac
+       while true
+       do
+               if base_present
+               then
+                       printf "Use (m)odified or (d)eleted file, or (a)bort? "
+               else
+                       printf "Use (c)reated or (d)eleted file, or (a)bort? "
+               fi
+               read ans || return 1
+               case "$ans" in
+               [mMcC]*)
+                       git add -- "$MERGED"
+                       cleanup_temp_files --save-backup
+                       return 0
+                       ;;
+               [dD]*)
+                       git rm -- "$MERGED" > /dev/null
+                       cleanup_temp_files
+                       return 0
+                       ;;
+               [aA]*)
+                       return 1
+                       ;;
+               esac
        done
 }
 
 resolve_submodule_merge () {
-    while true; do
-       printf "Use (l)ocal or (r)emote, or (a)bort? "
-       read ans || return 1
-       case "$ans" in
-           [lL]*)
-               if ! local_present; then
-                   if test -n "$(git ls-tree HEAD -- "$MERGED")"; then
-                       # Local isn't present, but it's a subdirectory
-                       git ls-tree --full-name -r HEAD -- "$MERGED" | git update-index --index-info || exit $?
-                   else
-                       test -e "$MERGED" && mv -- "$MERGED" "$BACKUP"
-                       git update-index --force-remove "$MERGED"
+       while true
+       do
+               printf "Use (l)ocal or (r)emote, or (a)bort? "
+               read ans || return 1
+               case "$ans" in
+               [lL]*)
+                       if ! local_present
+                       then
+                               if test -n "$(git ls-tree HEAD -- "$MERGED")"
+                               then
+                                       # Local isn't present, but it's a subdirectory
+                                       git ls-tree --full-name -r HEAD -- "$MERGED" |
+                                       git update-index --index-info || exit $?
+                               else
+                                       test -e "$MERGED" && mv -- "$MERGED" "$BACKUP"
+                                       git update-index --force-remove "$MERGED"
+                                       cleanup_temp_files --save-backup
+                               fi
+                       elif is_submodule "$local_mode"
+                       then
+                               stage_submodule "$MERGED" "$local_sha1"
+                       else
+                               git checkout-index -f --stage=2 -- "$MERGED"
+                               git add -- "$MERGED"
+                       fi
+                       return 0
+                       ;;
+               [rR]*)
+                       if ! remote_present
+                       then
+                               if test -n "$(git ls-tree MERGE_HEAD -- "$MERGED")"
+                               then
+                                       # Remote isn't present, but it's a subdirectory
+                                       git ls-tree --full-name -r MERGE_HEAD -- "$MERGED" |
+                                       git update-index --index-info || exit $?
+                               else
+                                       test -e "$MERGED" && mv -- "$MERGED" "$BACKUP"
+                                       git update-index --force-remove "$MERGED"
+                               fi
+                       elif is_submodule "$remote_mode"
+                       then
+                               ! is_submodule "$local_mode" &&
+                               test -e "$MERGED" &&
+                               mv -- "$MERGED" "$BACKUP"
+                               stage_submodule "$MERGED" "$remote_sha1"
+                       else
+                               test -e "$MERGED" && mv -- "$MERGED" "$BACKUP"
+                               git checkout-index -f --stage=3 -- "$MERGED"
+                               git add -- "$MERGED"
+                       fi
                        cleanup_temp_files --save-backup
-                   fi
-               elif is_submodule "$local_mode"; then
-                   stage_submodule "$MERGED" "$local_sha1"
-               else
-                   git checkout-index -f --stage=2 -- "$MERGED"
-                   git add -- "$MERGED"
-               fi
-               return 0
-               ;;
-           [rR]*)
-               if ! remote_present; then
-                   if test -n "$(git ls-tree MERGE_HEAD -- "$MERGED")"; then
-                       # Remote isn't present, but it's a subdirectory
-                       git ls-tree --full-name -r MERGE_HEAD -- "$MERGED" | git update-index --index-info || exit $?
-                   else
-                       test -e "$MERGED" && mv -- "$MERGED" "$BACKUP"
-                       git update-index --force-remove "$MERGED"
-                   fi
-               elif is_submodule "$remote_mode"; then
-                   ! is_submodule "$local_mode" && test -e "$MERGED" && mv -- "$MERGED" "$BACKUP"
-                   stage_submodule "$MERGED" "$remote_sha1"
-               else
-                   test -e "$MERGED" && mv -- "$MERGED" "$BACKUP"
-                   git checkout-index -f --stage=3 -- "$MERGED"
-                   git add -- "$MERGED"
-               fi
-               cleanup_temp_files --save-backup
-               return 0
-               ;;
-           [aA]*)
-               return 1
-               ;;
-           esac
+                       return 0
+                       ;;
+               [aA]*)
+                       return 1
+                       ;;
+               esac
        done
 }
 
 stage_submodule () {
-    path="$1"
-    submodule_sha1="$2"
-    mkdir -p "$path" || die "fatal: unable to create directory for module at $path"
-    # Find $path relative to work tree
-    work_tree_root=$(cd_to_toplevel && pwd)
-    work_rel_path=$(cd "$path" && GIT_WORK_TREE="${work_tree_root}" git rev-parse --show-prefix)
-    test -n "$work_rel_path" || die "fatal: unable to get path of module $path relative to work tree"
-    git update-index --add --replace --cacheinfo 160000 "$submodule_sha1" "${work_rel_path%/}" || die
+       path="$1"
+       submodule_sha1="$2"
+       mkdir -p "$path" ||
+       die "fatal: unable to create directory for module at $path"
+       # Find $path relative to work tree
+       work_tree_root=$(cd_to_toplevel && pwd)
+       work_rel_path=$(cd "$path" &&
+               GIT_WORK_TREE="${work_tree_root}" git rev-parse --show-prefix
+       )
+       test -n "$work_rel_path" ||
+       die "fatal: unable to get path of module $path relative to work tree"
+       git update-index --add --replace --cacheinfo 160000 "$submodule_sha1" "${work_rel_path%/}" || die
 }
 
 checkout_staged_file () {
-    tmpfile=$(expr \
-           "$(git checkout-index --temp --stage="$1" "$2" 2>/dev/null)" \
-           : '\([^     ]*\)    ')
-
-    if test $? -eq 0 -a -n "$tmpfile" ; then
-       mv -- "$(git rev-parse --show-cdup)$tmpfile" "$3"
-    else
-       >"$3"
-    fi
+       tmpfile=$(expr \
+               "$(git checkout-index --temp --stage="$1" "$2" 2>/dev/null)" \
+               : '\([^ ]*\)    ')
+
+       if test $? -eq 0 -a -n "$tmpfile"
+       then
+               mv -- "$(git rev-parse --show-cdup)$tmpfile" "$3"
+       else
+               >"$3"
+       fi
 }
 
 merge_file () {
-    MERGED="$1"
+       MERGED="$1"
 
-    f=$(git ls-files -u -- "$MERGED")
-    if test -z "$f" ; then
-       if test ! -f "$MERGED" ; then
-           echo "$MERGED: file not found"
-       else
-           echo "$MERGED: file does not need merging"
+       f=$(git ls-files -u -- "$MERGED")
+       if test -z "$f"
+       then
+               if test ! -f "$MERGED"
+               then
+                       echo "$MERGED: file not found"
+               else
+                       echo "$MERGED: file does not need merging"
+               fi
+               return 1
        fi
-       return 1
-    fi
-
-    ext="$$$(expr "$MERGED" : '.*\(\.[^/]*\)$')"
-    BACKUP="./$MERGED.BACKUP.$ext"
-    LOCAL="./$MERGED.LOCAL.$ext"
-    REMOTE="./$MERGED.REMOTE.$ext"
-    BASE="./$MERGED.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;}')
-
-    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
-       return
-    fi
-
-    mv -- "$MERGED" "$BACKUP"
-    cp -- "$BACKUP" "$MERGED"
-
-    checkout_staged_file 1 "$MERGED" "$BASE"
-    checkout_staged_file 2 "$MERGED" "$LOCAL"
-    checkout_staged_file 3 "$MERGED" "$REMOTE"
-
-    if test -z "$local_mode" -o -z "$remote_mode"; then
-       echo "Deleted merge conflict for '$MERGED':"
-       describe_file "$local_mode" "local" "$LOCAL"
-       describe_file "$remote_mode" "remote" "$REMOTE"
-       resolve_deleted_merge
-       return
-    fi
 
-    if is_symlink "$local_mode" || is_symlink "$remote_mode"; then
-       echo "Symbolic link merge conflict for '$MERGED':"
+       ext="$$$(expr "$MERGED" : '.*\(\.[^/]*\)$')"
+       BACKUP="./$MERGED.BACKUP.$ext"
+       LOCAL="./$MERGED.LOCAL.$ext"
+       REMOTE="./$MERGED.REMOTE.$ext"
+       BASE="./$MERGED.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;}')
+
+       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
+               return
+       fi
+
+       mv -- "$MERGED" "$BACKUP"
+       cp -- "$BACKUP" "$MERGED"
+
+       checkout_staged_file 1 "$MERGED" "$BASE"
+       checkout_staged_file 2 "$MERGED" "$LOCAL"
+       checkout_staged_file 3 "$MERGED" "$REMOTE"
+
+       if test -z "$local_mode" -o -z "$remote_mode"
+       then
+               echo "Deleted merge conflict for '$MERGED':"
+               describe_file "$local_mode" "local" "$LOCAL"
+               describe_file "$remote_mode" "remote" "$REMOTE"
+               resolve_deleted_merge
+               return
+       fi
+
+       if is_symlink "$local_mode" || is_symlink "$remote_mode"
+       then
+               echo "Symbolic link merge conflict for '$MERGED':"
+               describe_file "$local_mode" "local" "$LOCAL"
+               describe_file "$remote_mode" "remote" "$REMOTE"
+               resolve_symlink_merge
+               return
+       fi
+
+       echo "Normal merge conflict for '$MERGED':"
        describe_file "$local_mode" "local" "$LOCAL"
        describe_file "$remote_mode" "remote" "$REMOTE"
-       resolve_symlink_merge
-       return
-    fi
-
-    echo "Normal merge conflict for '$MERGED':"
-    describe_file "$local_mode" "local" "$LOCAL"
-    describe_file "$remote_mode" "remote" "$REMOTE"
-    if "$prompt" = true; then
-       printf "Hit return to start merge resolution tool (%s): " "$merge_tool"
-       read ans || return 1
-    fi
-
-    if base_present; then
-           present=true
-    else
-           present=false
-    fi
-
-    if ! run_merge_tool "$merge_tool" "$present"; then
-       echo "merge of $MERGED failed" 1>&2
-       mv -- "$BACKUP" "$MERGED"
-
-       if test "$merge_keep_temporaries" = "false"; then
-           cleanup_temp_files
+       if "$prompt" = true
+       then
+               printf "Hit return to start merge resolution tool (%s): " "$merge_tool"
+               read ans || return 1
        fi
 
-       return 1
-    fi
+       if base_present
+       then
+               present=true
+       else
+               present=false
+       fi
+
+       if ! run_merge_tool "$merge_tool" "$present"
+       then
+               echo "merge of $MERGED failed" 1>&2
+               mv -- "$BACKUP" "$MERGED"
+
+               if test "$merge_keep_temporaries" = "false"
+               then
+                       cleanup_temp_files
+               fi
 
-    if test "$merge_keep_backup" = "true"; then
-       mv -- "$BACKUP" "$MERGED.orig"
-    else
-       rm -- "$BACKUP"
-    fi
+               return 1
+       fi
 
-    git add -- "$MERGED"
-    cleanup_temp_files
-    return 0
+       if test "$merge_keep_backup" = "true"
+       then
+               mv -- "$BACKUP" "$MERGED.orig"
+       else
+               rm -- "$BACKUP"
+       fi
+
+       git add -- "$MERGED"
+       cleanup_temp_files
+       return 0
 }
 
 show_tool_help () {
@@ -325,61 +356,61 @@ prompt=$(git config --bool mergetool.prompt || echo true)
 
 while test $# != 0
 do
-    case "$1" in
+       case "$1" in
        --tool-help)
                show_tool_help
                ;;
        -t|--tool*)
-           case "$#,$1" in
+               case "$#,$1" in
                *,*=*)
-                   merge_tool=$(expr "z$1" : 'z-[^=]*=\(.*\)')
-                   ;;
+                       merge_tool=$(expr "z$1" : 'z-[^=]*=\(.*\)')
+                       ;;
                1,*)
-                   usage ;;
+                       usage ;;
                *)
-                   merge_tool="$2"
-                   shift ;;
-           esac
-           ;;
+                       merge_tool="$2"
+                       shift ;;
+               esac
+               ;;
        -y|--no-prompt)
-           prompt=false
-           ;;
+               prompt=false
+               ;;
        --prompt)
-           prompt=true
-           ;;
+               prompt=true
+               ;;
        --)
-           shift
-           break
-           ;;
+               shift
+               break
+               ;;
        -*)
-           usage
-           ;;
-       *)
-           break
-           ;;
-    esac
-    shift
-done
-
-prompt_after_failed_merge() {
-    while true; do
-       printf "Continue merging other unresolved paths (y/n) ? "
-       read ans || return 1
-       case "$ans" in
-
-           [yY]*)
-               return 0
+               usage
                ;;
-
-           [nN]*)
-               return 1
+       *)
+               break
                ;;
        esac
-    done
+       shift
+done
+
+prompt_after_failed_merge () {
+       while true
+       do
+               printf "Continue merging other unresolved paths (y/n) ? "
+               read ans || return 1
+               case "$ans" in
+               [yY]*)
+                       return 0
+                       ;;
+               [nN]*)
+                       return 1
+                       ;;
+               esac
+       done
 }
 
-if test -z "$merge_tool"; then
-    merge_tool=$(get_merge_tool "$merge_tool") || exit
+if test -z "$merge_tool"
+then
+       merge_tool=$(get_merge_tool "$merge_tool") || exit
 fi
 merge_keep_backup="$(git config --bool mergetool.keepBackup || echo true)"
 merge_keep_temporaries="$(git config --bool mergetool.keepTemporaries || echo false)"
@@ -388,22 +419,24 @@ last_status=0
 rollup_status=0
 files=
 
-if test $# -eq 0 ; then
-    cd_to_toplevel
+if test $# -eq 0
+then
+       cd_to_toplevel
 
-    if test -e "$GIT_DIR/MERGE_RR"
-    then
-       files=$(git rerere remaining)
-    else
-       files=$(git ls-files -u | sed -e 's/^[^ ]*      //' | sort -u)
-    fi
+       if test -e "$GIT_DIR/MERGE_RR"
+       then
+               files=$(git rerere remaining)
+       else
+               files=$(git ls-files -u | sed -e 's/^[^ ]*      //' | sort -u)
+       fi
 else
-    files=$(git ls-files -u -- "$@" | sed -e 's/^[^    ]*      //' | sort -u)
+       files=$(git ls-files -u -- "$@" | sed -e 's/^[^ ]*      //' | sort -u)
 fi
 
-if test -z "$files" ; then
-    echo "No files need merging"
-    exit 0
+if test -z "$files"
+then
+       echo "No files need merging"
+       exit 0
 fi
 
 printf "Merging:\n"
@@ -413,15 +446,17 @@ IFS='
 '
 for i in $files
 do
-    if test $last_status -ne 0; then
-       prompt_after_failed_merge || exit 1
-    fi
-    printf "\n"
-    merge_file "$i"
-    last_status=$?
-    if test $last_status -ne 0; then
-       rollup_status=1
-    fi
+       if test $last_status -ne 0
+       then
+               prompt_after_failed_merge || exit 1
+       fi
+       printf "\n"
+       merge_file "$i"
+       last_status=$?
+       if test $last_status -ne 0
+       then
+               rollup_status=1
+       fi
 done
 
 exit $rollup_status
index 0c19b7c..8492678 100644 (file)
@@ -548,11 +548,10 @@ do_next () {
        test -s "$todo" && return
 
        comment_for_reflog finish &&
-       shortonto=$(git rev-parse --short $onto) &&
        newhead=$(git rev-parse HEAD) &&
        case $head_name in
        refs/*)
-               message="$GIT_REFLOG_ACTION: $head_name onto $shortonto" &&
+               message="$GIT_REFLOG_ACTION: $head_name onto $onto" &&
                git update-ref -m "$message" $head_name $newhead $orig_head &&
                git symbolic-ref \
                  -m "$GIT_REFLOG_ACTION: returning to $head_name" \
index ef30c55..6647137 100755 (executable)
@@ -862,11 +862,13 @@ $time = time - scalar $#files;
 sub unquote_rfc2047 {
        local ($_) = @_;
        my $encoding;
-       if (s/=\?([^?]+)\?q\?(.*)\?=/$2/g) {
+       s{=\?([^?]+)\?q\?(.*?)\?=}{
                $encoding = $1;
-               s/_/ /g;
-               s/=([0-9A-F]{2})/chr(hex($1))/eg;
-       }
+               my $e = $2;
+               $e =~ s/_/ /g;
+               $e =~ s/=([0-9A-F]{2})/chr(hex($1))/eg;
+               $e;
+       }eg;
        return wantarray ? ($_, $encoding) : $_;
 }
 
index 770a86e..ee0e0bc 100644 (file)
@@ -9,8 +9,12 @@
 # you would cause "cd" to be taken to unexpected places.  If you
 # like CDPATH, define it for your interactive shell sessions without
 # exporting it.
+# But we protect ourselves from such a user mistake nevertheless.
 unset CDPATH
 
+# Similarly for IFS
+unset IFS
+
 git_broken_path_fix () {
        case ":$PATH:" in
        *:$1:*) : ok ;;
index 4e2c7f8..bbefdf6 100755 (executable)
@@ -469,6 +469,7 @@ apply_stash () {
        else
                # Merge conflict; keep the exit status from merge-recursive
                status=$?
+               git rerere
                if test -n "$INDEX_OPTION"
                then
                        gettextln "Index was not unstashed." >&2
index 30fa93a..e89b516 100755 (executable)
@@ -73,26 +73,48 @@ resolve_relative_url ()
 #
 module_list()
 {
-       git ls-files --error-unmatch --stage -- "$@" |
+       (
+               git ls-files --error-unmatch --stage -- "$@" ||
+               echo "unmatched pathspec exists"
+       ) |
        perl -e '
        my %unmerged = ();
        my ($null_sha1) = ("0" x 40);
+       my @out = ();
+       my $unmatched = 0;
        while (<STDIN>) {
+               if (/^unmatched pathspec/) {
+                       $unmatched = 1;
+                       next;
+               }
                chomp;
                my ($mode, $sha1, $stage, $path) =
                        /^([0-7]+) ([0-9a-f]{40}) ([0-3])\t(.*)$/;
                next unless $mode eq "160000";
                if ($stage ne "0") {
                        if (!$unmerged{$path}++) {
-                               print "$mode $null_sha1 U\t$path\n";
+                               push @out, "$mode $null_sha1 U\t$path\n";
                        }
                        next;
                }
-               print "$_\n";
+               push @out, "$_\n";
+       }
+       if ($unmatched) {
+               print "#unmatched\n";
+       } else {
+               print for (@out);
        }
        '
 }
 
+die_if_unmatched ()
+{
+       if test "$1" = "#unmatched"
+       then
+               exit 1
+       fi
+}
+
 #
 # Map submodule path to submodule name
 #
@@ -346,6 +368,7 @@ cmd_foreach()
        module_list |
        while read mode sha1 stage sm_path
        do
+               die_if_unmatched "$mode"
                if test -e "$sm_path"/.git
                then
                        say "$(eval_gettext "Entering '\$prefix\$sm_path'")"
@@ -398,6 +421,7 @@ cmd_init()
        module_list "$@" |
        while read mode sha1 stage sm_path
        do
+               die_if_unmatched "$mode"
                name=$(module_name "$sm_path") || exit
 
                # Copy url setting when it is not set yet
@@ -498,6 +522,7 @@ cmd_update()
        err=
        while read mode sha1 stage sm_path
        do
+               die_if_unmatched "$mode"
                if test "$stage" = U
                then
                        echo >&2 "Skipping unmerged submodule $sm_path"
@@ -539,7 +564,7 @@ Maybe you want to use 'update --init'?")"
                        die "$(eval_gettext "Unable to find current revision in submodule path '\$sm_path'")"
                fi
 
-               if test "$subsha1" != "$sha1"
+               if test "$subsha1" != "$sha1" -o -n "$force"
                then
                        subforce=$force
                        # If we don't already have a -f flag and the submodule has never been checked out
@@ -893,6 +918,7 @@ cmd_status()
        module_list "$@" |
        while read mode sha1 stage sm_path
        do
+               die_if_unmatched "$mode"
                name=$(module_name "$sm_path") || exit
                url=$(git config submodule."$name".url)
                displaypath="$prefix$sm_path"
@@ -961,6 +987,7 @@ cmd_sync()
        module_list "$@" |
        while read mode sha1 stage sm_path
        do
+               die_if_unmatched "$mode"
                name=$(module_name "$sm_path")
                url=$(git config -f .gitmodules --get submodule."$name".url)
 
index 55e0e9e..a40ed0c 100755 (executable)
@@ -54,6 +54,11 @@ sub evaluate_uri {
        # to build the base URL ourselves:
        our $path_info = decode_utf8($ENV{"PATH_INFO"});
        if ($path_info) {
+               # $path_info has already been URL-decoded by the web server, but
+               # $my_url and $my_uri have not. URL-decode them so we can properly
+               # strip $path_info.
+               $my_url = unescape($my_url);
+               $my_uri = unescape($my_uri);
                if ($my_url =~ s,\Q$path_info\E$,, &&
                    $my_uri =~ s,\Q$path_info\E$,, &&
                    defined $ENV{'SCRIPT_NAME'}) {
diff --git a/http.c b/http.c
index 5cb87f1..0a27b14 100644 (file)
--- a/http.c
+++ b/http.c
@@ -744,6 +744,33 @@ char *get_remote_object_url(const char *url, const char *hex,
        return strbuf_detach(&buf, NULL);
 }
 
+int handle_curl_result(struct active_request_slot *slot)
+{
+       struct slot_results *results = slot->results;
+
+       if (results->curl_result == CURLE_OK) {
+               credential_approve(&http_auth);
+               return HTTP_OK;
+       } else if (missing_target(results))
+               return HTTP_MISSING_TARGET;
+       else if (results->http_code == 401) {
+               if (http_auth.username && http_auth.password) {
+                       credential_reject(&http_auth);
+                       return HTTP_NOAUTH;
+               } else {
+                       credential_fill(&http_auth);
+                       init_curl_http_auth(slot->curl);
+                       return HTTP_REAUTH;
+               }
+       } else {
+               if (!curl_errorstr[0])
+                       strlcpy(curl_errorstr,
+                               curl_easy_strerror(results->curl_result),
+                               sizeof(curl_errorstr));
+               return HTTP_ERROR;
+       }
+}
+
 /* http_request() targets */
 #define HTTP_REQUEST_STRBUF    0
 #define HTTP_REQUEST_FILE      1
@@ -791,26 +818,7 @@ static int http_request(const char *url, void *result, int target, int options)
 
        if (start_active_slot(slot)) {
                run_active_slot(slot);
-               if (results.curl_result == CURLE_OK)
-                       ret = HTTP_OK;
-               else if (missing_target(&results))
-                       ret = HTTP_MISSING_TARGET;
-               else if (results.http_code == 401) {
-                       if (http_auth.username && http_auth.password) {
-                               credential_reject(&http_auth);
-                               ret = HTTP_NOAUTH;
-                       } else {
-                               credential_fill(&http_auth);
-                               init_curl_http_auth(slot->curl);
-                               ret = HTTP_REAUTH;
-                       }
-               } else {
-                       if (!curl_errorstr[0])
-                               strlcpy(curl_errorstr,
-                                       curl_easy_strerror(results.curl_result),
-                                       sizeof(curl_errorstr));
-                       ret = HTTP_ERROR;
-               }
+               ret = handle_curl_result(slot);
        } else {
                error("Unable to start HTTP request for %s", url);
                ret = HTTP_START_FAILED;
@@ -819,9 +827,6 @@ static int http_request(const char *url, void *result, int target, int options)
        curl_slist_free_all(headers);
        strbuf_release(&buf);
 
-       if (ret == HTTP_OK)
-               credential_approve(&http_auth);
-
        return ret;
 }
 
diff --git a/http.h b/http.h
index 915c286..12de255 100644 (file)
--- a/http.h
+++ b/http.h
@@ -78,6 +78,7 @@ extern int start_active_slot(struct active_request_slot *slot);
 extern void run_active_slot(struct active_request_slot *slot);
 extern void finish_active_slot(struct active_request_slot *slot);
 extern void finish_all_active_slots(void);
+extern int handle_curl_result(struct active_request_slot *slot);
 
 #ifdef USE_CURL_MULTI
 extern void fill_active_slots(void);
index 680937c..e02da35 100644 (file)
@@ -493,8 +493,7 @@ static struct string_list *get_renames(struct merge_options *o,
        opts.rename_score = o->rename_score;
        opts.show_rename_progress = o->show_rename_progress;
        opts.output_format = DIFF_FORMAT_NO_OUTPUT;
-       if (diff_setup_done(&opts) < 0)
-               die("diff setup failed");
+       diff_setup_done(&opts);
        diff_tree_sha1(o_tree->object.sha1, tree->object.sha1, "", &opts);
        diffcore_std(&opts);
        if (opts.needed_rename_limit > o->needed_rename_limit)
@@ -614,23 +613,6 @@ static char *unique_path(struct merge_options *o, const char *path, const char *
        return newpath;
 }
 
-static void flush_buffer(int fd, const char *buf, unsigned long size)
-{
-       while (size > 0) {
-               long ret = write_in_full(fd, buf, size);
-               if (ret < 0) {
-                       /* Ignore epipe */
-                       if (errno == EPIPE)
-                               break;
-                       die_errno("merge-recursive");
-               } else if (!ret) {
-                       die("merge-recursive: disk full?");
-               }
-               size -= ret;
-               buf += ret;
-       }
-}
-
 static int dir_in_way(const char *path, int check_working_copy)
 {
        int pos, pathlen = strlen(path);
@@ -789,7 +771,7 @@ static void update_file_flags(struct merge_options *o,
                        fd = open(path, O_WRONLY | O_TRUNC | O_CREAT, mode);
                        if (fd < 0)
                                die_errno("failed to open '%s'", path);
-                       flush_buffer(fd, buf, size);
+                       write_in_full(fd, buf, size);
                        close(fd);
                } else if (S_ISLNK(mode)) {
                        char *lnk = xmemdupz(buf, size);
index 74aa77c..7d62904 100644 (file)
@@ -126,8 +126,7 @@ static struct notes_merge_pair *diff_tree_remote(struct notes_merge_options *o,
        diff_setup(&opt);
        DIFF_OPT_SET(&opt, RECURSIVE);
        opt.output_format = DIFF_FORMAT_NO_OUTPUT;
-       if (diff_setup_done(&opt) < 0)
-               die("diff_setup_done failed");
+       diff_setup_done(&opt);
        diff_tree_sha1(base, remote, "", &opt);
        diffcore_std(&opt);
 
@@ -190,8 +189,7 @@ static void diff_tree_local(struct notes_merge_options *o,
        diff_setup(&opt);
        DIFF_OPT_SET(&opt, RECURSIVE);
        opt.output_format = DIFF_FORMAT_NO_OUTPUT;
-       if (diff_setup_done(&opt) < 0)
-               die("diff_setup_done failed");
+       diff_setup_done(&opt);
        diff_tree_sha1(base, local, "", &opt);
        diffcore_std(&opt);
 
index 5717257..bc8a28f 100644 (file)
@@ -39,8 +39,7 @@ int init_patch_ids(struct patch_ids *ids)
        memset(ids, 0, sizeof(*ids));
        diff_setup(&ids->diffopts);
        DIFF_OPT_SET(&ids->diffopts, RECURSIVE);
-       if (diff_setup_done(&ids->diffopts) < 0)
-               return error("diff_setup_done failed");
+       diff_setup_done(&ids->diffopts);
        return 0;
 }
 
index b645827..d8543e4 100644 (file)
@@ -1409,11 +1409,9 @@ int read_index_from(struct index_state *istate, const char *path)
        size_t mmap_size;
        struct strbuf previous_name_buf = STRBUF_INIT, *previous_name;
 
-       errno = EBUSY;
        if (istate->initialized)
                return istate->cache_nr;
 
-       errno = ENOENT;
        istate->timestamp.sec = 0;
        istate->timestamp.nsec = 0;
        fd = open(path, O_RDONLY);
@@ -1426,15 +1424,14 @@ int read_index_from(struct index_state *istate, const char *path)
        if (fstat(fd, &st))
                die_errno("cannot stat the open index");
 
-       errno = EINVAL;
        mmap_size = xsize_t(st.st_size);
        if (mmap_size < sizeof(struct cache_header) + 20)
                die("index file smaller than expected");
 
        mmap = xmmap(NULL, mmap_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
-       close(fd);
        if (mmap == MAP_FAILED)
                die_errno("unable to map index file");
+       close(fd);
 
        hdr = mmap;
        if (verify_hdr(hdr, mmap_size) < 0)
@@ -1490,7 +1487,6 @@ int read_index_from(struct index_state *istate, const char *path)
 
 unmap:
        munmap(mmap, mmap_size);
-       errno = EINVAL;
        die("index file corrupt");
 }
 
@@ -1790,6 +1786,8 @@ int write_index(struct index_state *istate, int newfd)
                        continue;
                if (!ce_uptodate(ce) && is_racy_timestamp(istate, ce))
                        ce_smudge_racily_clean_entry(ce);
+               if (is_null_sha1(ce->sha1))
+                       return error("cache entry has null sha1: %s", ce->name);
                if (ce_write_entry(&c, newfd, ce, previous_name) < 0)
                        return -1;
        }
index 04a9d62..3ec474f 100644 (file)
@@ -362,16 +362,17 @@ static size_t rpc_in(char *ptr, size_t eltsize,
 
 static int run_slot(struct active_request_slot *slot)
 {
-       int err = 0;
+       int err;
        struct slot_results results;
 
        slot->results = &results;
        slot->curl_result = curl_easy_perform(slot->curl);
        finish_active_slot(slot);
 
-       if (results.curl_result != CURLE_OK) {
-               err |= error("RPC failed; result=%d, HTTP code = %ld",
-                       results.curl_result, results.http_code);
+       err = handle_curl_result(slot);
+       if (err != HTTP_OK && err != HTTP_REAUTH) {
+               error("RPC failed; result=%d, HTTP code = %ld",
+                     results.curl_result, results.http_code);
        }
 
        return err;
@@ -436,9 +437,11 @@ static int post_rpc(struct rpc_state *rpc)
        }
 
        if (large_request) {
-               err = probe_rpc(rpc);
-               if (err)
-                       return err;
+               do {
+                       err = probe_rpc(rpc);
+               } while (err == HTTP_REAUTH);
+               if (err != HTTP_OK)
+                       return -1;
        }
 
        slot = get_active_slot();
@@ -525,7 +528,11 @@ static int post_rpc(struct rpc_state *rpc)
        curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, rpc_in);
        curl_easy_setopt(slot->curl, CURLOPT_FILE, rpc);
 
-       err = run_slot(slot);
+       do {
+               err = run_slot(slot);
+       } while (err == HTTP_REAUTH && !large_request && !use_gzip);
+       if (err != HTTP_OK)
+               err = -1;
 
        curl_slist_free_all(headers);
        free(gzip_body);
index 457868d..7210496 100644 (file)
@@ -345,6 +345,7 @@ static int tree_difference = REV_TREE_SAME;
 static void file_add_remove(struct diff_options *options,
                    int addremove, unsigned mode,
                    const unsigned char *sha1,
+                   int sha1_valid,
                    const char *fullpath, unsigned dirty_submodule)
 {
        int diff = addremove == '+' ? REV_TREE_NEW : REV_TREE_OLD;
@@ -358,6 +359,7 @@ static void file_change(struct diff_options *options,
                 unsigned old_mode, unsigned new_mode,
                 const unsigned char *old_sha1,
                 const unsigned char *new_sha1,
+                int old_sha1_valid, int new_sha1_valid,
                 const char *fullpath,
                 unsigned old_dirty_submodule, unsigned new_dirty_submodule)
 {
@@ -1865,8 +1867,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
        if (revs->combine_merges)
                revs->ignore_merges = 0;
        revs->diffopt.abbrev = revs->abbrev;
-       if (diff_setup_done(&revs->diffopt) < 0)
-               die("diff_setup_done failed");
+       diff_setup_done(&revs->diffopt);
 
        compile_grep_patterns(&revs->grep_filter);
 
index 606791d..f9922b9 100644 (file)
@@ -139,6 +139,8 @@ int sane_execvp(const char *file, char * const argv[])
         */
        if (errno == EACCES && !strchr(file, '/'))
                errno = exists_in_PATH(file) ? EACCES : ENOENT;
+       else if (errno == ENOTDIR && !strchr(file, '/'))
+               errno = ENOENT;
        return -1;
 }
 
diff --git a/setup.c b/setup.c
index e114977..7663a4c 100644 (file)
--- a/setup.c
+++ b/setup.c
@@ -82,7 +82,7 @@ static void NORETURN die_verify_filename(const char *prefix,
 
        if (!diagnose_misspelt_rev)
                die("%s: no such path in the working tree.\n"
-                   "Use '-- <path>...' to specify paths that do not exist locally.",
+                   "Use 'git <command> -- <path>...' to specify paths that do not exist locally.",
                    arg);
        /*
         * Saying "'(icase)foo' does not exist in the index" when the
@@ -96,7 +96,8 @@ static void NORETURN die_verify_filename(const char *prefix,
 
        /* ... or fall back the most general message. */
        die("ambiguous argument '%s': unknown revision or path not in the working tree.\n"
-           "Use '--' to separate paths from revisions", arg);
+           "Use '--' to separate paths from revisions, like this:\n"
+           "'git <command> [<revision>...] -- [<file>...]'", arg);
 
 }
 
@@ -145,7 +146,8 @@ void verify_non_filename(const char *prefix, const char *arg)
        if (!check_filename(prefix, arg))
                return;
        die("ambiguous argument '%s': both revision and filename\n"
-           "Use '--' to separate filenames from revisions", arg);
+           "Use '--' to separate paths from revisions, like this:\n"
+           "'git <command> [<revision>...] -- [<file>...]'", arg);
 }
 
 /*
index 959d349..19dc6a6 100644 (file)
@@ -574,8 +574,7 @@ static void calculate_changed_submodule_paths(void)
                        DIFF_OPT_SET(&diff_opts, RECURSIVE);
                        diff_opts.output_format |= DIFF_FORMAT_CALLBACK;
                        diff_opts.format_callback = submodule_collect_changed_cb;
-                       if (diff_setup_done(&diff_opts) < 0)
-                               die("diff_setup_done failed");
+                       diff_setup_done(&diff_opts);
                        diff_tree_sha1(parent->item->object.sha1, commit->object.sha1, "", &diff_opts);
                        diffcore_std(&diff_opts);
                        diff_flush(&diff_opts);
index 094d490..0f31ef9 100644 (file)
@@ -163,3 +163,42 @@ test_http_push_nonff() {
                test_i18ngrep "Updates were rejected because" output
        '
 }
+
+setup_askpass_helper() {
+       test_expect_success 'setup askpass helper' '
+               write_script "$TRASH_DIRECTORY/askpass" <<-\EOF &&
+               echo >>"$TRASH_DIRECTORY/askpass-query" "askpass: $*" &&
+               cat "$TRASH_DIRECTORY/askpass-response"
+               EOF
+               GIT_ASKPASS="$TRASH_DIRECTORY/askpass" &&
+               export GIT_ASKPASS &&
+               export TRASH_DIRECTORY
+       '
+}
+
+set_askpass() {
+       >"$TRASH_DIRECTORY/askpass-query" &&
+       echo "$*" >"$TRASH_DIRECTORY/askpass-response"
+}
+
+expect_askpass() {
+       dest=$HTTPD_DEST
+       {
+               case "$1" in
+               none)
+                       ;;
+               pass)
+                       echo "askpass: Password for 'http://$2@$dest': "
+                       ;;
+               both)
+                       echo "askpass: Username for 'http://$dest': "
+                       echo "askpass: Password for 'http://$2@$dest': "
+                       ;;
+               *)
+                       false
+                       ;;
+               esac
+       } >"$TRASH_DIRECTORY/askpass-expect" &&
+       test_cmp "$TRASH_DIRECTORY/askpass-expect" \
+                "$TRASH_DIRECTORY/askpass-query"
+}
index de3762e..ec8618d 100644 (file)
@@ -43,24 +43,22 @@ ErrorLog error.log
 </IfVersion>
 
 Alias /dumb/ www/
-Alias /auth/ www/auth/
+Alias /auth/dumb/ www/auth/dumb/
 
-<Location /smart/>
+<LocationMatch /smart/>
        SetEnv GIT_EXEC_PATH ${GIT_EXEC_PATH}
        SetEnv GIT_HTTP_EXPORT_ALL
-</Location>
-<Location /smart_noexport/>
+</LocationMatch>
+<LocationMatch /smart_noexport/>
        SetEnv GIT_EXEC_PATH ${GIT_EXEC_PATH}
-</Location>
-<Location /smart_custom_env/>
+</LocationMatch>
+<LocationMatch /smart_custom_env/>
        SetEnv GIT_EXEC_PATH ${GIT_EXEC_PATH}
        SetEnv GIT_HTTP_EXPORT_ALL
        SetEnv GIT_COMMITTER_NAME "Custom User"
        SetEnv GIT_COMMITTER_EMAIL custom@example.com
-</Location>
-ScriptAlias /smart/ ${GIT_EXEC_PATH}/git-http-backend/
-ScriptAlias /smart_noexport/ ${GIT_EXEC_PATH}/git-http-backend/
-ScriptAlias /smart_custom_env/ ${GIT_EXEC_PATH}/git-http-backend/
+</LocationMatch>
+ScriptAliasMatch /smart_*[^/]*/(.*) ${GIT_EXEC_PATH}/git-http-backend/$1
 <Directory ${GIT_EXEC_PATH}>
        Options None
 </Directory>
@@ -91,6 +89,13 @@ SSLEngine On
        Require valid-user
 </Location>
 
+<LocationMatch "^/auth-push/.*/git-receive-pack$">
+       AuthType Basic
+       AuthName "git-auth"
+       AuthUserFile passwd
+       Require valid-user
+</LocationMatch>
+
 <IfDefine DAV>
        LoadModule dav_module modules/mod_dav.so
        LoadModule dav_fs_module modules/mod_dav_fs.so
index 5580c22..a1361e5 100644 (file)
@@ -163,7 +163,7 @@ test_perf () {
                else
                        echo "perf $test_count - $1:"
                fi
-               for i in $(seq 1 $GIT_PERF_REPEAT_COUNT); do
+               for i in $(test_seq 1 $GIT_PERF_REPEAT_COUNT); do
                        say >&3 "running: $2"
                        if test_run_perf_ "$2"
                        then
index 5b79c51..bf7a2cd 100755 (executable)
@@ -213,4 +213,30 @@ test_expect_success 'rev-list --verify-objects with bad sha1' '
        grep -q "error: sha1 mismatch 63ffffffffffffffffffffffffffffffffffffff" out
 '
 
+_bz='\0'
+_bz5="$_bz$_bz$_bz$_bz$_bz"
+_bz20="$_bz5$_bz5$_bz5$_bz5"
+
+test_expect_success 'fsck notices blob entry pointing to null sha1' '
+       (git init null-blob &&
+        cd null-blob &&
+        sha=$(printf "100644 file$_bz$_bz20" |
+              git hash-object -w --stdin -t tree) &&
+         git fsck 2>out &&
+         cat out &&
+         grep "warning.*null sha1" out
+       )
+'
+
+test_expect_success 'fsck notices submodule entry pointing to null sha1' '
+       (git init null-commit &&
+        cd null-commit &&
+        sha=$(printf "160000 submodule$_bz$_bz20" |
+              git hash-object -w --stdin -t tree) &&
+         git fsck 2>out &&
+         cat out &&
+         grep "warning.*null sha1" out
+       )
+'
+
 test_done
index 809fafe..0dbbb00 100755 (executable)
@@ -29,4 +29,23 @@ test_expect_success 'update-index -h with corrupt index' '
        grep "[Uu]sage: git update-index" broken/usage
 '
 
+test_expect_success '--cacheinfo does not accept blob null sha1' '
+       echo content >file &&
+       git add file &&
+       git rev-parse :file >expect &&
+       test_must_fail git update-index --cacheinfo 100644 $_z40 file &&
+       git rev-parse :file >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success '--cacheinfo does not accept gitlink null sha1' '
+       git init submodule &&
+       (cd submodule && test_commit foo) &&
+       git add submodule &&
+       git rev-parse :submodule >expect &&
+       test_must_fail git update-index --cacheinfo 160000 $_z40 submodule &&
+       git rev-parse :submodule >actual &&
+       test_cmp expect actual
+'
+
 test_done
index 7f8693b..58f4823 100755 (executable)
@@ -47,7 +47,23 @@ test_expect_success 'rebase ignores empty commit' '
        git commit --allow-empty -m empty &&
        test_commit D &&
        git rebase C &&
-       test $(git log --format=%s C..) = "D"
+       test "$(git log --format=%s C..)" = "D"
+'
+
+test_expect_success 'rebase --keep-empty' '
+       git reset --hard D &&
+       git rebase --keep-empty C &&
+       test "$(git log --format=%s C..)" = "D
+empty"
+'
+
+test_expect_success 'rebase --keep-empty keeps empty even if already in upstream' '
+       git reset --hard A &&
+       git commit --allow-empty -m also-empty &&
+       git rebase --keep-empty D &&
+       test "$(git log --format=%s A..)" = "also-empty
+D
+empty"
 '
 
 test_done
index c00a94b..2d030a4 100755 (executable)
@@ -66,5 +66,35 @@ test_expect_success 'suppress deletion diff with -B -D' '
        grep -v "Linus Torvalds" actual
 '
 
+test_expect_success 'prepare a file that ends with an incomplete line' '
+       test_seq 1 99 >seq &&
+       printf 100 >>seq &&
+       git add seq &&
+       git commit seq -m seq
+'
+
+test_expect_success 'rewrite the middle 90% of sequence file and terminate with newline' '
+       test_seq 1 5 >seq &&
+       test_seq 9331 9420 >>seq &&
+       test_seq 96 100 >>seq
+'
+
+test_expect_success 'confirm that sequence file is considered a rewrite' '
+       git diff -B seq >res &&
+       grep "dissimilarity index" res
+'
+
+test_expect_success 'no newline at eof is on its own line without -B' '
+       git diff seq >res &&
+       grep "^\\\\ " res &&
+       ! grep "^..*\\\\ " res
+'
+
+test_expect_success 'no newline at eof is on its own line with -B' '
+       git diff -B seq >res &&
+       grep "^\\\\ " res &&
+       ! grep "^..*\\\\ " res
+'
+
 test_done
 
diff --git a/t/t4054-diff-bogus-tree.sh b/t/t4054-diff-bogus-tree.sh
new file mode 100755 (executable)
index 0000000..0843c87
--- /dev/null
@@ -0,0 +1,83 @@
+#!/bin/sh
+
+test_description='test diff with a bogus tree containing the null sha1'
+. ./test-lib.sh
+
+empty_tree=4b825dc642cb6eb9a060e54bf8d69288fbee4904
+
+test_expect_success 'create bogus tree' '
+       bogus_tree=$(
+               printf "100644 fooQQQQQQQQQQQQQQQQQQQQQ" |
+               q_to_nul |
+               git hash-object -w --stdin -t tree
+       )
+'
+
+test_expect_success 'create tree with matching file' '
+       echo bar >foo &&
+       git add foo &&
+       good_tree=$(git write-tree)
+       blob=$(git rev-parse :foo)
+'
+
+test_expect_success 'raw diff shows null sha1 (addition)' '
+       echo ":000000 100644 $_z40 $_z40 A      foo" >expect &&
+       git diff-tree $empty_tree $bogus_tree >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'raw diff shows null sha1 (removal)' '
+       echo ":100644 000000 $_z40 $_z40 D      foo" >expect &&
+       git diff-tree $bogus_tree $empty_tree >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'raw diff shows null sha1 (modification)' '
+       echo ":100644 100644 $blob $_z40 M      foo" >expect &&
+       git diff-tree $good_tree $bogus_tree >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'raw diff shows null sha1 (other direction)' '
+       echo ":100644 100644 $_z40 $blob M      foo" >expect &&
+       git diff-tree $bogus_tree $good_tree >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'raw diff shows null sha1 (reverse)' '
+       echo ":100644 100644 $_z40 $blob M      foo" >expect &&
+       git diff-tree -R $good_tree $bogus_tree >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'raw diff shows null sha1 (index)' '
+       echo ":100644 100644 $_z40 $blob M      foo" >expect &&
+       git diff-index $bogus_tree >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'patch fails due to bogus sha1 (addition)' '
+       test_must_fail git diff-tree -p $empty_tree $bogus_tree
+'
+
+test_expect_success 'patch fails due to bogus sha1 (removal)' '
+       test_must_fail git diff-tree -p $bogus_tree $empty_tree
+'
+
+test_expect_success 'patch fails due to bogus sha1 (modification)' '
+       test_must_fail git diff-tree -p $good_tree $bogus_tree
+'
+
+test_expect_success 'patch fails due to bogus sha1 (other direction)' '
+       test_must_fail git diff-tree -p $bogus_tree $good_tree
+'
+
+test_expect_success 'patch fails due to bogus sha1 (reverse)' '
+       test_must_fail git diff-tree -R -p $good_tree $bogus_tree
+'
+
+test_expect_success 'patch fails due to bogus sha1 (index)' '
+       test_must_fail git diff-index -p $bogus_tree
+'
+
+test_done
index 0eace37..250c720 100755 (executable)
@@ -145,6 +145,41 @@ test_expect_success 'push --all excludes remote-tracking hierarchy' '
        )
 '
 
+test_expect_success 'receive-pack runs auto-gc in remote repo' '
+       rm -rf parent child &&
+       git init parent &&
+       (
+           # Setup a repo with 2 packs
+           cd parent &&
+           echo "Some text" >file.txt &&
+           git add . &&
+           git commit -m "Initial commit" &&
+           git repack -adl &&
+           echo "Some more text" >>file.txt &&
+           git commit -a -m "Second commit" &&
+           git repack
+       ) &&
+       cp -a parent child &&
+       (
+           # Set the child to auto-pack if more than one pack exists
+           cd child &&
+           git config gc.autopacklimit 1 &&
+           git branch test_auto_gc &&
+           # And create a file that follows the temporary object naming
+           # convention for the auto-gc to remove
+           : >.git/objects/tmp_test_object &&
+           test-chmtime =-1209601 .git/objects/tmp_test_object
+       ) &&
+       (
+           cd parent &&
+           echo "Even more text" >>file.txt &&
+           git commit -a -m "Third commit" &&
+           git send-pack ../child HEAD:refs/heads/test_auto_gc >output 2>&1 &&
+           grep "Auto packing the repository for optimum performance." output
+       ) &&
+       test ! -e child/.git/objects/tmp_test_object
+'
+
 rewound_push_setup() {
        rm -rf parent child &&
        mkdir parent &&
index 1eea647..f141f2d 100755 (executable)
@@ -46,15 +46,7 @@ test_expect_success 'create password-protected repository' '
               "$HTTPD_DOCUMENT_ROOT_PATH/auth/dumb/test_repo.git"
 '
 
-test_expect_success 'setup askpass helper' '
-       cat >askpass <<-\EOF &&
-       #!/bin/sh
-       echo user@host
-       EOF
-       chmod +x askpass &&
-       GIT_ASKPASS="$PWD/askpass" &&
-       export GIT_ASKPASS
-'
+setup_askpass_helper
 
 test_expect_success 'clone remote repository' '
        cd "$ROOT_PATH" &&
@@ -162,6 +154,7 @@ test_http_push_nonff "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git \
 
 test_expect_success 'push to password-protected repository (user in URL)' '
        test_commit pw-user &&
+       set_askpass user@host &&
        git push "$HTTPD_URL_USER/auth/dumb/test_repo.git" HEAD &&
        git rev-parse --verify HEAD >expect &&
        git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/auth/dumb/test_repo.git" \
@@ -169,9 +162,15 @@ test_expect_success 'push to password-protected repository (user in URL)' '
        test_cmp expect actual
 '
 
+test_expect_failure 'user was prompted only once for password' '
+       expect_askpass pass user@host
+'
+
 test_expect_failure 'push to password-protected repository (no user in URL)' '
        test_commit pw-nouser &&
+       set_askpass user@host &&
        git push "$HTTPD_URL/auth/dumb/test_repo.git" HEAD &&
+       expect_askpass both user@host
        git rev-parse --verify HEAD >expect &&
        git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/auth/dumb/test_repo.git" \
                rev-parse --verify HEAD >actual &&
index 312e484..ef6d6b6 100755 (executable)
@@ -36,6 +36,8 @@ test_expect_success 'setup remote repository' '
        mv test_repo.git "$HTTPD_DOCUMENT_ROOT_PATH"
 '
 
+setup_askpass_helper
+
 cat >exp <<EOF
 GET  /smart/test_repo.git/info/refs?service=git-upload-pack HTTP/1.1 200
 POST /smart/test_repo.git/git-upload-pack HTTP/1.1 200
@@ -266,5 +268,29 @@ test_expect_success 'http push respects GIT_COMMITTER_* in reflog' '
        test_cmp expect actual
 '
 
+test_expect_success 'push over smart http with auth' '
+       cd "$ROOT_PATH/test_repo_clone" &&
+       echo push-auth-test >expect &&
+       test_commit push-auth-test &&
+       set_askpass user@host &&
+       git push "$HTTPD_URL"/auth/smart/test_repo.git &&
+       git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git" \
+               log -1 --format=%s >actual &&
+       expect_askpass both user@host &&
+       test_cmp expect actual
+'
+
+test_expect_success 'push to auth-only-for-push repo' '
+       cd "$ROOT_PATH/test_repo_clone" &&
+       echo push-half-auth >expect &&
+       test_commit push-half-auth &&
+       set_askpass user@host &&
+       git push "$HTTPD_URL"/auth-push/smart/test_repo.git &&
+       git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git" \
+               log -1 --format=%s >actual &&
+       expect_askpass both user@host &&
+       test_cmp expect actual
+'
+
 stop_httpd
 test_done
index b06f817..16ef041 100755 (executable)
@@ -41,68 +41,34 @@ test_expect_success 'clone http repository' '
 '
 
 test_expect_success 'create password-protected repository' '
-       mkdir "$HTTPD_DOCUMENT_ROOT_PATH/auth/" &&
+       mkdir -p "$HTTPD_DOCUMENT_ROOT_PATH/auth/dumb/" &&
        cp -Rf "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" \
-              "$HTTPD_DOCUMENT_ROOT_PATH/auth/repo.git"
-'
-
-test_expect_success 'setup askpass helpers' '
-       cat >askpass <<-EOF &&
-       #!/bin/sh
-       echo >>"$PWD/askpass-query" "askpass: \$*" &&
-       cat "$PWD/askpass-response"
-       EOF
-       chmod +x askpass &&
-       GIT_ASKPASS="$PWD/askpass" &&
-       export GIT_ASKPASS
-'
-
-expect_askpass() {
-       dest=$HTTPD_DEST
-       {
-               case "$1" in
-               none)
-                       ;;
-               pass)
-                       echo "askpass: Password for 'http://$2@$dest': "
-                       ;;
-               both)
-                       echo "askpass: Username for 'http://$dest': "
-                       echo "askpass: Password for 'http://$2@$dest': "
-                       ;;
-               *)
-                       false
-                       ;;
-               esac
-       } >askpass-expect &&
-       test_cmp askpass-expect askpass-query
-}
+              "$HTTPD_DOCUMENT_ROOT_PATH/auth/dumb/repo.git"
+'
+
+setup_askpass_helper
 
 test_expect_success 'cloning password-protected repository can fail' '
-       >askpass-query &&
-       echo wrong >askpass-response &&
-       test_must_fail git clone "$HTTPD_URL/auth/repo.git" clone-auth-fail &&
+       set_askpass wrong &&
+       test_must_fail git clone "$HTTPD_URL/auth/dumb/repo.git" clone-auth-fail &&
        expect_askpass both wrong
 '
 
 test_expect_success 'http auth can use user/pass in URL' '
-       >askpass-query &&
-       echo wrong >askpass-response &&
-       git clone "$HTTPD_URL_USER_PASS/auth/repo.git" clone-auth-none &&
+       set_askpass wrong &&
+       git clone "$HTTPD_URL_USER_PASS/auth/dumb/repo.git" clone-auth-none &&
        expect_askpass none
 '
 
 test_expect_success 'http auth can use just user in URL' '
-       >askpass-query &&
-       echo user@host >askpass-response &&
-       git clone "$HTTPD_URL_USER/auth/repo.git" clone-auth-pass &&
+       set_askpass user@host &&
+       git clone "$HTTPD_URL_USER/auth/dumb/repo.git" clone-auth-pass &&
        expect_askpass pass user@host
 '
 
 test_expect_success 'http auth can request both user and pass' '
-       >askpass-query &&
-       echo user@host >askpass-response &&
-       git clone "$HTTPD_URL/auth/repo.git" clone-auth-both &&
+       set_askpass user@host &&
+       git clone "$HTTPD_URL/auth/dumb/repo.git" clone-auth-both &&
        expect_askpass both user@host
 '
 
@@ -112,25 +78,22 @@ test_expect_success 'http auth respects credential helper config' '
                echo username=user@host
                echo password=user@host
        }; f" &&
-       >askpass-query &&
-       echo wrong >askpass-response &&
-       git clone "$HTTPD_URL/auth/repo.git" clone-auth-helper &&
+       set_askpass wrong &&
+       git clone "$HTTPD_URL/auth/dumb/repo.git" clone-auth-helper &&
        expect_askpass none
 '
 
 test_expect_success 'http auth can get username from config' '
        test_config_global "credential.$HTTPD_URL.username" user@host &&
-       >askpass-query &&
-       echo user@host >askpass-response &&
-       git clone "$HTTPD_URL/auth/repo.git" clone-auth-user &&
+       set_askpass user@host &&
+       git clone "$HTTPD_URL/auth/dumb/repo.git" clone-auth-user &&
        expect_askpass pass user@host
 '
 
 test_expect_success 'configured username does not override URL' '
        test_config_global "credential.$HTTPD_URL.username" wrong &&
-       >askpass-query &&
-       echo user@host >askpass-response &&
-       git clone "$HTTPD_URL_USER/auth/repo.git" clone-auth-user2 &&
+       set_askpass user@host &&
+       git clone "$HTTPD_URL_USER/auth/dumb/repo.git" clone-auth-user2 &&
        expect_askpass pass user@host
 '
 
index fadf2f2..2db5c35 100755 (executable)
@@ -27,6 +27,8 @@ test_expect_success 'create http-accessible bare repository' '
        git push public master:master
 '
 
+setup_askpass_helper
+
 cat >exp <<EOF
 > GET /smart/repo.git/info/refs?service=git-upload-pack HTTP/1.1
 > Accept: */*
@@ -109,12 +111,30 @@ test_expect_success 'follow redirects (302)' '
        git clone $HTTPD_URL/smart-redir-temp/repo.git --quiet repo-t
 '
 
+test_expect_success 'clone from password-protected repository' '
+       echo two >expect &&
+       set_askpass user@host &&
+       git clone --bare "$HTTPD_URL/auth/smart/repo.git" smart-auth &&
+       expect_askpass both user@host &&
+       git --git-dir=smart-auth log -1 --format=%s >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'clone from auth-only-for-push repository' '
+       echo two >expect &&
+       set_askpass wrong &&
+       git clone --bare "$HTTPD_URL/auth-push/smart/repo.git" smart-noauth &&
+       expect_askpass none &&
+       git --git-dir=smart-noauth log -1 --format=%s >actual &&
+       test_cmp expect actual
+'
+
 test -n "$GIT_TEST_LONG" && test_set_prereq EXPENSIVE
 
 test_expect_success EXPENSIVE 'create 50,000 tags in the repo' '
        (
        cd "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
-       for i in `seq 50000`
+       for i in `test_seq 50000`
        do
                echo "commit refs/heads/too-many-refs"
                echo "mark :$i"
index 1721784..752f5cb 100755 (executable)
@@ -456,4 +456,14 @@ test_atom refs/tags/signed-long contents "subject line
 body contents
 $sig"
 
+cat >expected <<\EOF
+408fe76d02a785a006c2e9c669b7be5589ede96d <committer@example.com> refs/tags/master
+90b5ebede4899eda64893bc2a4c8f1d6fb6dfc40 <committer@example.com> refs/tags/bogo
+EOF
+
+test_expect_success 'Verify sort with multiple keys' '
+       git for-each-ref --format="%(objectname) %(taggeremail) %(refname)" --sort=objectname --sort=taggeremail \
+               refs/tags/bogo refs/tags/master > actual &&
+       test_cmp expected actual
+'
 test_done
index 81827e6..0278f48 100755 (executable)
@@ -258,6 +258,27 @@ test_expect_success 'init should register submodule url in .git/config' '
        test_cmp expect url
 '
 
+test_failure_with_unknown_submodule () {
+       test_must_fail git submodule $1 no-such-submodule 2>output.err &&
+       grep "^error: .*no-such-submodule" output.err
+}
+
+test_expect_success 'init should fail with unknown submodule' '
+       test_failure_with_unknown_submodule init
+'
+
+test_expect_success 'update should fail with unknown submodule' '
+       test_failure_with_unknown_submodule update
+'
+
+test_expect_success 'status should fail with unknown submodule' '
+       test_failure_with_unknown_submodule status
+'
+
+test_expect_success 'sync should fail with unknown submodule' '
+       test_failure_with_unknown_submodule sync
+'
+
 test_expect_success 'update should fail when path is used by a file' '
        echo hello >expect &&
 
@@ -418,10 +439,7 @@ test_expect_success 'moving to a commit without submodule does not leave empty d
 '
 
 test_expect_success 'submodule <invalid-path> warns' '
-
-       git submodule no-such-submodule 2> output.err &&
-       grep "^error: .*no-such-submodule" output.err
-
+       test_failure_with_unknown_submodule
 '
 
 test_expect_success 'add submodules without specifying an explicit path' '
index ce61d4c..1542653 100755 (executable)
@@ -123,6 +123,18 @@ test_expect_success 'submodule update should throw away changes with --force ' '
        )
 '
 
+test_expect_success 'submodule update --force forcibly checks out submodules' '
+       (cd super &&
+        (cd submodule &&
+         rm -f file
+        ) &&
+        git submodule update --force submodule &&
+        (cd submodule &&
+         test "$(git status -s file)" = ""
+        )
+       )
+'
+
 test_expect_success 'submodule update --rebase staying on master' '
        (cd super/submodule &&
          git checkout master
@@ -367,7 +379,7 @@ test_expect_success 'submodule update continues after checkout error' '
         git submodule init &&
         git commit -am "new_submodule" &&
         (cd submodule2 &&
-         git rev-parse --max-count=1 HEAD > ../expect
+         git rev-parse --verify HEAD >../expect
         ) &&
         (cd submodule &&
          test_commit "update_submodule" file
@@ -384,7 +396,7 @@ test_expect_success 'submodule update continues after checkout error' '
         git checkout HEAD^ &&
         test_must_fail git submodule update &&
         (cd submodule2 &&
-         git rev-parse --max-count=1 HEAD > ../actual
+         git rev-parse --verify HEAD >../actual
         ) &&
         test_cmp expect actual
        )
@@ -413,7 +425,7 @@ test_expect_success 'submodule update continues after recursive checkout error'
          test_commit "update_submodule_again_again" file
         ) &&
         (cd submodule2 &&
-         git rev-parse --max-count=1 HEAD > ../expect &&
+         git rev-parse --verify HEAD >../expect &&
          test_commit "update_submodule2_again" file
         ) &&
         git add submodule &&
@@ -428,7 +440,7 @@ test_expect_success 'submodule update continues after recursive checkout error'
         ) &&
         test_must_fail git submodule update --recursive &&
         (cd submodule2 &&
-         git rev-parse --max-count=1 HEAD > ../actual
+         git rev-parse --verify HEAD >../actual
         ) &&
         test_cmp expect actual
        )
@@ -460,12 +472,12 @@ test_expect_success 'submodule update exit immediately in case of merge conflict
         ) &&
         git checkout HEAD^ &&
         (cd submodule2 &&
-         git rev-parse --max-count=1 HEAD > ../expect
+         git rev-parse --verify HEAD >../expect
         ) &&
         git config submodule.submodule.update merge &&
         test_must_fail git submodule update &&
         (cd submodule2 &&
-         git rev-parse --max-count=1 HEAD > ../actual
+         git rev-parse --verify HEAD >../actual
         ) &&
         test_cmp expect actual
        )
@@ -495,12 +507,12 @@ test_expect_success 'submodule update exit immediately after recursive rebase er
         ) &&
         git checkout HEAD^ &&
         (cd submodule2 &&
-         git rev-parse --max-count=1 HEAD > ../expect
+         git rev-parse --verify HEAD >../expect
         ) &&
         git config submodule.submodule.update rebase &&
         test_must_fail git submodule update &&
         (cd submodule2 &&
-         git rev-parse --max-count=1 HEAD > ../actual
+         git rev-parse --verify HEAD >../actual
         ) &&
         test_cmp expect actual
        )
index f5e16fc..6fa0c70 100755 (executable)
@@ -55,6 +55,16 @@ test_expect_success 'setup' '
     git rm file12 &&
     git commit -m "branch1 changes" &&
 
+    git checkout -b stash1 master &&
+    echo stash1 change file11 >file11 &&
+    git add file11 &&
+    git commit -m "stash1 changes" &&
+
+    git checkout -b stash2 master &&
+    echo stash2 change file11 >file11 &&
+    git add file11 &&
+    git commit -m "stash2 changes" &&
+
     git checkout master &&
     git submodule update -N &&
     echo master updated >file1 &&
@@ -193,7 +203,35 @@ test_expect_success 'mergetool skips resolved paths when rerere is active' '
     git reset --hard
 '
 
+test_expect_success 'conflicted stash sets up rerere'  '
+    git config rerere.enabled true &&
+    git checkout stash1 &&
+    echo "Conflicting stash content" >file11 &&
+    git stash &&
+
+    git checkout --detach stash2 &&
+    test_must_fail git stash apply &&
+
+    test -n "$(git ls-files -u)" &&
+    conflicts="$(git rerere remaining)" &&
+    test "$conflicts" = "file11" &&
+    output="$(git mergetool --no-prompt)" &&
+    test "$output" != "No files need merging" &&
+
+    git commit -am "save the stash resolution" &&
+
+    git reset --hard stash2 &&
+    test_must_fail git stash apply &&
+
+    test -n "$(git ls-files -u)" &&
+    conflicts="$(git rerere remaining)" &&
+    test -z "$conflicts" &&
+    output="$(git mergetool --no-prompt)" &&
+    test "$output" = "No files need merging"
+'
+
 test_expect_success 'mergetool takes partial path' '
+    git reset --hard
     git config rerere.enabled false &&
     git checkout -b test12 branch1 &&
     git submodule update -N &&
index 8c12c65..0351228 100755 (executable)
@@ -841,6 +841,19 @@ test_expect_success $PREREQ '--compose adds MIME for utf8 subject' '
        grep "^Subject: =?UTF-8?q?utf8-s=C3=BCbj=C3=ABct?=" msgtxt1
 '
 
+test_expect_success $PREREQ 'utf8 author is correctly passed on' '
+       clean_fake_sendmail &&
+       test_commit weird_author &&
+       test_when_finished "git reset --hard HEAD^" &&
+       git commit --amend --author "Füñný Nâmé <odd_?=mail@example.com>" &&
+       git format-patch --stdout -1 >funny_name.patch &&
+       git send-email --from="Example <nobody@example.com>" \
+         --to=nobody@example.com \
+         --smtp-server="$(pwd)/fake.sendmail" \
+         funny_name.patch &&
+       grep "^From: Füñný Nâmé <odd_?=mail@example.com>" msgtxt1
+'
+
 test_expect_success $PREREQ 'detects ambiguous reference/file conflict' '
        echo master > master &&
        git add master &&
index 80daaca..9096398 100644 (file)
@@ -530,6 +530,27 @@ test_cmp() {
        $GIT_TEST_CMP "$@"
 }
 
+# Print a sequence of numbers or letters in increasing order.  This is
+# similar to GNU seq(1), but the latter might not be available
+# everywhere (and does not do letters).  It may be used like:
+#
+#      for i in `test_seq 100`; do
+#              for j in `test_seq 10 20`; do
+#                      for k in `test_seq a z`; do
+#                              echo $i-$j-$k
+#                      done
+#              done
+#      done
+
+test_seq () {
+       case $# in
+       1)      set 1 "$@" ;;
+       2)      ;;
+       *)      error "bug in the test script: not 1 or 2 parameters to test_seq" ;;
+       esac
+       "$PERL_PATH" -le 'print for $ARGV[0]..$ARGV[1]' -- "$@"
+}
+
 # This function can be used to schedule some commands to be run
 # unconditionally at the end of the test to restore sanity:
 #
index 28ad6db..ba01563 100644 (file)
@@ -49,12 +49,12 @@ static int compare_tree_entry(struct tree_desc *t1, struct tree_desc *t2,
        if (DIFF_OPT_TST(opt, RECURSIVE) && S_ISDIR(mode1)) {
                if (DIFF_OPT_TST(opt, TREE_IN_RECURSIVE)) {
                        opt->change(opt, mode1, mode2,
-                                   sha1, sha2, base->buf, 0, 0);
+                                   sha1, sha2, 1, 1, base->buf, 0, 0);
                }
                strbuf_addch(base, '/');
                diff_tree_sha1(sha1, sha2, base->buf, opt);
        } else {
-               opt->change(opt, mode1, mode2, sha1, sha2, base->buf, 0, 0);
+               opt->change(opt, mode1, mode2, sha1, sha2, 1, 1, base->buf, 0, 0);
        }
        strbuf_setlen(base, old_baselen);
        return 0;
@@ -100,7 +100,7 @@ static void show_entry(struct diff_options *opt, const char *prefix,
                        die("corrupt tree sha %s", sha1_to_hex(sha1));
 
                if (DIFF_OPT_TST(opt, TREE_IN_RECURSIVE))
-                       opt->add_remove(opt, *prefix, mode, sha1, base->buf, 0);
+                       opt->add_remove(opt, *prefix, mode, sha1, 1, base->buf, 0);
 
                strbuf_addch(base, '/');
 
@@ -108,7 +108,7 @@ static void show_entry(struct diff_options *opt, const char *prefix,
                show_tree(opt, prefix, &inner, base);
                free(tree);
        } else
-               opt->add_remove(opt, prefix[0], mode, sha1, base->buf, 0);
+               opt->add_remove(opt, prefix[0], mode, sha1, 1, base->buf, 0);
 
        strbuf_setlen(base, old_baselen);
 }
@@ -212,8 +212,7 @@ static void try_to_follow_renames(struct tree_desc *t1, struct tree_desc *t2, co
        diff_opts.rename_score = opt->rename_score;
        paths[0] = NULL;
        diff_tree_setup_paths(paths, &diff_opts);
-       if (diff_setup_done(&diff_opts) < 0)
-               die("unable to set up diff options to follow renames");
+       diff_setup_done(&diff_opts);
        diff_tree(t1, t2, base, &diff_opts);
        diffcore_std(&diff_opts);
        diff_tree_release_paths(&diff_opts);