Merge branch 'rj/config-cygwin'
authorJunio C Hamano <gitster@pobox.com>
Wed, 13 Jul 2011 21:31:36 +0000 (14:31 -0700)
committerJunio C Hamano <gitster@pobox.com>
Wed, 13 Jul 2011 21:31:36 +0000 (14:31 -0700)
* rj/config-cygwin:
config.c: Make git_config() work correctly when called recursively
t1301-*.sh: Fix the 'forced modes' test on cygwin
help.c: Fix detection of custom merge strategy on cygwin

86 files changed:
Documentation/RelNotes/1.7.6.txt
Documentation/RelNotes/1.7.7.txt [new file with mode: 0644]
Documentation/config.txt
Documentation/diff-options.txt
Documentation/git-grep.txt
Documentation/git-instaweb.txt
Documentation/git-status.txt
Documentation/git.txt
Documentation/technical/pack-protocol.txt
GIT-VERSION-GEN
Makefile
RelNotes
abspath.c
builtin/branch.c
builtin/cat-file.c
builtin/checkout.c
builtin/diff.c
builtin/gc.c
builtin/grep.c
builtin/update-index.c
builtin/verify-pack.c
cache.h
combine-diff.c
compat/mingw.c
compat/mingw.h
contrib/completion/git-completion.bash
convert.c
convert.h [new file with mode: 0644]
diff-lib.c
diff.c
diff.h
entry.c
git-am.sh
git-bisect.sh
git-compat-util.h
git-instaweb.sh
git-mergetool--lib.sh
git-pull.sh
git-sh-setup.sh
git-stash.sh
git-submodule.sh
git-svn.perl
git.c
gitweb/gitweb.perl
gitweb/static/js/blame_incremental.js
grep.c
grep.h
http.c
read-cache.c
refs.c
refs.h
remote-curl.c
setup.c
sh-i18n--envsubst.c
sha1_file.c
streaming.c [new file with mode: 0644]
streaming.h [new file with mode: 0644]
t/Makefile
t/gitweb-lib.sh
t/t0021-conversion.sh
t/t1020-subdirectory.sh
t/t2018-checkout-branch.sh
t/t3903-stash.sh
t/t4048-diff-combined-binary.sh [new file with mode: 0755]
t/t4049-diff-stat-count.sh [new file with mode: 0755]
t/t4150-am.sh
t/t4151-am-abort.sh
t/t4203-mailmap.sh
t/t4205-log-pretty-formats.sh
t/t5506-remote-groups.sh
t/t5516-fetch-push.sh
t/t7400-submodule-basic.sh
t/t7401-submodule-summary.sh
t/t7406-submodule-update.sh
t/t7407-submodule-foreach.sh
t/t7503-pre-commit-hook.sh
t/t7508-status.sh
t/t7810-grep.sh
t/t9159-git-svn-no-parent-mergeinfo.sh [new file with mode: 0755]
t/t9300-fast-import.sh
t/test-lib.sh
unpack-trees.c
unpack-trees.h
userdiff.c
userdiff.h
wt-status.c
index d1b78e569ff402842f3e0fa8f61779268a006e18..9ec498ea39d969a60c74192bf0863ef9fabe4bc1 100644 (file)
@@ -1,4 +1,4 @@
-Git v1.7.6 Release Notes (draft)
+Git v1.7.6 Release Notes
 ========================
 
 Updates since v1.7.5
@@ -134,9 +134,3 @@ included in this release.
 
  * "git status -z" did not default to --porcelain output format.
    (merge bc/maint-status-z-to-use-porcelain later)
-
----
-exec >/var/tmp/1
-echo O=$(git describe master)
-O=v1.7.6-rc1
-git shortlog --no-merges ^maint ^$O master
diff --git a/Documentation/RelNotes/1.7.7.txt b/Documentation/RelNotes/1.7.7.txt
new file mode 100644 (file)
index 0000000..c5a5441
--- /dev/null
@@ -0,0 +1,36 @@
+Git v1.7.7 Release Notes
+========================
+
+Updates since v1.7.6
+--------------------
+
+ * The scripting part of the codebase is getting prepared for i18n/l10n.
+
+ * "git checkout" (both the code to update the files upon checking out a
+   different branch, the code to checkout specific set of files) learned
+   to stream the data from object store when possible, without having to
+   read the entire contents of a file in memory first.
+
+ * "git diff --cc" learned to correctly ignore binary files.
+
+ * "git diff --stat" learned --stat-count option to limit the output of
+   diffstat report.
+
+ * "git grep" learned --break and --heading options, to let users mimic
+   output format of "ack".
+
+Also contains various documentation updates and minor miscellaneous
+changes.
+
+
+Fixes since v1.7.6
+------------------
+
+Unless otherwise noted, all the fixes in 1.7.6.X maintenance track are
+included in this release.
+
+--
+exec >/var/tmp/1
+echo O=$(git describe master)
+O=v1.7.6-133-g395f65d
+git shortlog --no-merges ^maint ^$O master
index 6b937771994f5b0a532b6f2cc522a9d3f35c9c09..c631d1cbf9096b8973322366e2a3fe199286b9da 100644 (file)
@@ -1196,6 +1196,14 @@ http.proxy::
        environment variable (see linkgit:curl[1]).  This can be overridden
        on a per-remote basis; see remote.<name>.proxy
 
+http.cookiefile::
+       File containing previously stored cookie lines which should be used
+       in the git http session, if they match the server. The file format
+       of the file to read cookies from should be plain HTTP headers or
+       the Netscape/Mozilla cookie file format (see linkgit:curl[1]).
+       NOTE that the file specified with http.cookiefile is only used as
+       input. No cookies will be stored in the file.
+
 http.sslVerify::
        Whether to verify the SSL certificate when fetching or pushing
        over HTTPS. Can be overridden by the 'GIT_SSL_NO_VERIFY' environment
index c7ed94635786789baca1de41d482cd6334d1181a..b620b3afeca14166840fb76b4cefe1e4038fb47e 100644 (file)
@@ -48,11 +48,17 @@ endif::git-format-patch[]
 --patience::
        Generate a diff using the "patience diff" algorithm.
 
---stat[=<width>[,<name-width>]]::
+--stat[=<width>[,<name-width>[,<count>]]]::
        Generate a diffstat.  You can override the default
        output width for 80-column terminal by `--stat=<width>`.
        The width of the filename part can be controlled by
        giving another width to it separated by a comma.
+       By giving a third parameter `<count>`, you can limit the
+       output to the first `<count>` lines, followed by
+       `...` if there are more.
++
+These parameters can also be set individually with `--stat-width=<width>`,
+`--stat-name-width=<name-width>` and `--stat-count=<count>`.
 
 --numstat::
        Similar to `\--stat`, but shows number of added and
@@ -224,10 +230,14 @@ endif::git-format-patch[]
 
 ifndef::git-format-patch[]
 --check::
-       Warn if changes introduce trailing whitespace
-       or an indent that uses a space before a tab. Exits with
-       non-zero status if problems are found. Not compatible with
-       --exit-code.
+       Warn if changes introduce whitespace errors.  What are
+       considered whitespace errors is controlled by `core.whitespace`
+       configuration.  By default, trailing whitespaces (including
+       lines that solely consist of whitespaces) and a space character
+       that is immediately followed by a tab character inside the
+       initial indent of the line are considered whitespace errors.
+       Exits with non-zero status if problems are found. Not compatible
+       with --exit-code.
 endif::git-format-patch[]
 
 --full-index::
@@ -412,6 +422,17 @@ endif::git-format-patch[]
 --no-ext-diff::
        Disallow external diff drivers.
 
+--textconv::
+--no-textconv::
+       Allow (or disallow) external text conversion filters to be run
+       when comparing binary files. See linkgit:gitattributes[5] for
+       details. Because textconv filters are typically a one-way
+       conversion, the resulting diff is suitable for human
+       consumption, but cannot be applied. For this reason, textconv
+       filters are enabled by default only for linkgit:git-diff[1] and
+       linkgit:git-log[1], but not for linkgit:git-format-patch[1] or
+       diff plumbing commands.
+
 --ignore-submodules[=<when>]::
        Ignore changes to submodules in the diff generation. <when> can be
        either "none", "untracked", "dirty" or "all", which is the default
index e150c77cffb5d082822bd65c7bfbeb1f431fc42f..07b3c6a0866c5b4ff0b4653b462b332ea0d348c9 100644 (file)
@@ -148,6 +148,13 @@ OPTIONS
        gives the default to color output.
        Same as `--color=never`.
 
+--break::
+       Print an empty line between matches from different files.
+
+--heading::
+       Show the filename above the matches in that file instead of
+       at the start of each shown line.
+
 -[ABC] <context>::
        Show `context` trailing (`A` -- after), or leading (`B`
        -- before), or both (`C` -- context) lines, and place a
index 08f85ba046598070432e9d9cd45052ae20484402..ea95c90460b976ee187833c248366af579065f83 100644 (file)
@@ -51,8 +51,8 @@ OPTIONS
 
 start::
 --start::
-       Start the httpd instance and exit.  This does not generate
-       any of the configuration files for spawning a new instance.
+       Start the httpd instance and exit.  Regenerate configuration files
+       as necessary for spawning a new instance.
 
 stop::
 --stop::
@@ -62,8 +62,8 @@ stop::
 
 restart::
 --restart::
-       Restart the httpd instance and exit.  This does not generate
-       any of the configuration files for spawning a new instance.
+       Restart the httpd instance and exit.  Regenerate configuration files
+       as necessary for spawning a new instance.
 
 CONFIGURATION
 -------------
index 38cb741f180e0869a91a61a5f7706eb664b86d91..8ecc99d995fb86e4327c018467ed52d5345e6930 100644 (file)
@@ -69,6 +69,9 @@ configuration variable documented in linkgit:git-config[1].
        (and suppresses the output of submodule summaries when the config option
        `status.submodulesummary` is set).
 
+--ignored::
+       Show ignored files as well.
+
 -z::
        Terminate entries with NUL, instead of LF.  This implies
        the `--porcelain` output format if no other format is given.
@@ -119,7 +122,8 @@ codes can be interpreted as follows:
 * 'C' = copied
 * 'U' = updated but unmerged
 
-Ignored files are not listed.
+Ignored files are not listed, unless `--ignored` option is in effect,
+in which case `XY` are `!!`.
 
     X          Y     Meaning
     -------------------------------------------------
@@ -142,6 +146,7 @@ Ignored files are not listed.
     U           U    unmerged, both modified
     -------------------------------------------------
     ?           ?    untracked
+    !           !    ignored
     -------------------------------------------------
 
 If -b is used the short-format status is preceded by a line
index 5c45446a4ac955630c8709e2184e52163185b8d4..3c7a832343ea983f7122c5d33429a4129fdba70a 100644 (file)
@@ -44,6 +44,11 @@ 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.6/git.html[documentation for release 1.7.6]
+
+* release notes for
+  link:RelNotes/1.7.6.txt[1.7.6].
+
 * link:v1.7.5.4/git.html[documentation for release 1.7.5.4]
 
 * release notes for
index 369f91d3b949b23682c4deda8234f13513f15732..a7004c63e7ff5a4fe237267121a5b7c76a4c5496 100644 (file)
@@ -179,34 +179,36 @@ and descriptions.
 
 Packfile Negotiation
 --------------------
-After reference and capabilities discovery, the client can decide
-to terminate the connection by sending a flush-pkt, telling the
-server it can now gracefully terminate (as happens with the ls-remote
-command) or it can enter the negotiation phase, where the client and
-server determine what the minimal packfile necessary for transport is.
-
-Once the client has the initial list of references that the server
-has, as well as the list of capabilities, it will begin telling the
-server what objects it wants and what objects it has, so the server
-can make a packfile that only contains the objects that the client needs.
-The client will also send a list of the capabilities it wants to be in
-effect, out of what the server said it could do with the first 'want' line.
+After reference and capabilities discovery, the client can decide to
+terminate the connection by sending a flush-pkt, telling the server it can
+now gracefully terminate, and disconnect, when it does not need any pack
+data. This can happen with the ls-remote command, and also can happen when
+the client already is up-to-date.
+
+Otherwise, it enters the negotiation phase, where the client and
+server determine what the minimal packfile necessary for transport is,
+by telling the server what objects it wants, its shallow objects
+(if any), and the maximum commit depth it wants (if any).  The client
+will also send a list of the capabilities it wants to be in effect,
+out of what the server said it could do with the first 'want' line.
 
 ----
   upload-request    =  want-list
-                      have-list
-                      compute-end
+                      *shallow-line
+                      *1depth-request
+                      flush-pkt
 
   want-list         =  first-want
                       *additional-want
-                      flush-pkt
+
+  shallow-line      =  PKT_LINE("shallow" SP obj-id)
+
+  depth-request     =  PKT_LINE("deepen" SP depth)
 
   first-want        =  PKT-LINE("want" SP obj-id SP capability-list LF)
   additional-want   =  PKT-LINE("want" SP obj-id LF)
 
-  have-list         =  *have-line
-  have-line         =  PKT-LINE("have" SP obj-id LF)
-  compute-end       =  flush-pkt / PKT-LINE("done")
+  depth             =  1*DIGIT
 ----
 
 Clients MUST send all the obj-ids it wants from the reference
@@ -215,21 +217,64 @@ discovery phase as 'want' lines. Clients MUST send at least one
 obj-id in a 'want' command which did not appear in the response
 obtained through ref discovery.
 
-If client is requesting a shallow clone, it will now send a 'deepen'
-line with the depth it is requesting.
+The client MUST write all obj-ids which it only has shallow copies
+of (meaning that it does not have the parents of a commit) as
+'shallow' lines so that the server is aware of the limitations of
+the client's history. Clients MUST NOT mention an obj-id which
+it does not know exists on the server.
+
+The client now sends the maximum commit history depth it wants for
+this transaction, which is the number of commits it wants from the
+tip of the history, if any, as a 'deepen' line.  A depth of 0 is the
+same as not making a depth request. The client does not want to receive
+any commits beyond this depth, nor objects needed only to complete
+those commits. Commits whose parents are not received as a result are
+defined as shallow and marked as such in the server. This information
+is sent back to the client in the next step.
+
+Once all the 'want's and 'shallow's (and optional 'deepen') are
+transferred, clients MUST send a flush-pkt, to tell the server side
+that it is done sending the list.
+
+Otherwise, if the client sent a positive depth request, the server
+will determine which commits will and will not be shallow and
+send this information to the client. If the client did not request
+a positive depth, this step is skipped.
 
-Once all the "want"s (and optional 'deepen') are transferred,
-clients MUST send a flush-pkt. If the client has all the references
-on the server, client flushes and disconnects.
+----
+  shallow-update   =  *shallow-line
+                     *unshallow-line
+                     flush-pkt
 
-TODO: shallow/unshallow response and document the deepen command in the ABNF.
+  shallow-line     =  PKT-LINE("shallow" SP obj-id)
+
+  unshallow-line   =  PKT-LINE("unshallow" SP obj-id)
+----
+
+If the client has requested a positive depth, the server will compute
+the set of commits which are no deeper than the desired depth, starting
+at the client's wants. The server writes 'shallow' lines for each
+commit whose parents will not be sent as a result. The server writes
+an 'unshallow' line for each commit which the client has indicated is
+shallow, but is no longer shallow at the currently requested depth
+(that is, its parents will now be sent). The server MUST NOT mark
+as unshallow anything which the client has not indicated was shallow.
 
 Now the client will send a list of the obj-ids it has using 'have'
-lines.  In multi_ack mode, the canonical implementation will send up
-to 32 of these at a time, then will send a flush-pkt.  The canonical
-implementation will skip ahead and send the next 32 immediately,
-so that there is always a block of 32 "in-flight on the wire" at a
-time.
+lines, so the server can make a packfile that only contains the objects
+that the client needs. In multi_ack mode, the canonical implementation
+will send up to 32 of these at a time, then will send a flush-pkt. The
+canonical implementation will skip ahead and send the next 32 immediately,
+so that there is always a block of 32 "in-flight on the wire" at a time.
+
+----
+  upload-haves      =  have-list
+                      compute-end
+
+  have-list         =  *have-line
+  have-line         =  PKT-LINE("have" SP obj-id LF)
+  compute-end       =  flush-pkt / PKT-LINE("done")
+----
 
 If the server reads 'have' lines, it then will respond by ACKing any
 of the obj-ids the client said it had that the server also has. The
index 8bc844d7e30cda1a7c0b288655947c888e142800..8270abb5f982463119309423eded333a8153317e 100755 (executable)
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 GVF=GIT-VERSION-FILE
-DEF_VER=v1.7.6-rc1
+DEF_VER=v1.7.6.GIT
 
 LF='
 '
index e40ac0c7f5ec2f304b88d92f47ff94272f5ce2a4..8d8f61e4f031e242ac5056adaccc9f57d2d1efba 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -556,6 +556,7 @@ LIB_H += sha1-lookup.h
 LIB_H += sideband.h
 LIB_H += sigchain.h
 LIB_H += strbuf.h
+LIB_H += streaming.h
 LIB_H += string-list.h
 LIB_H += submodule.h
 LIB_H += tag.h
@@ -662,6 +663,7 @@ LIB_OBJS += shallow.o
 LIB_OBJS += sideband.o
 LIB_OBJS += sigchain.o
 LIB_OBJS += strbuf.o
+LIB_OBJS += streaming.o
 LIB_OBJS += string-list.o
 LIB_OBJS += submodule.o
 LIB_OBJS += symlinks.o
@@ -1124,8 +1126,6 @@ endif
        X = .exe
 endif
 ifeq ($(uname_S),Interix)
-       NO_SYS_POLL_H = YesPlease
-       NO_INTTYPES_H = YesPlease
        NO_INITGROUPS = YesPlease
        NO_IPV6 = YesPlease
        NO_MEMMEM = YesPlease
@@ -1136,10 +1136,14 @@ ifeq ($(uname_S),Interix)
        ifeq ($(uname_R),3.5)
                NO_INET_NTOP = YesPlease
                NO_INET_PTON = YesPlease
+               NO_SOCKADDR_STORAGE = YesPlease
+               NO_FNMATCH_CASEFOLD = YesPlease
        endif
        ifeq ($(uname_R),5.2)
                NO_INET_NTOP = YesPlease
                NO_INET_PTON = YesPlease
+               NO_SOCKADDR_STORAGE = YesPlease
+               NO_FNMATCH_CASEFOLD = YesPlease
        endif
 endif
 ifneq (,$(findstring MINGW,$(uname_S)))
index 5fcc4ef74770e8587a1469fff5e3f5610dabf0e1..6372427426d80b01b03437fd1a55fd412fb8f75d 120000 (symlink)
--- a/RelNotes
+++ b/RelNotes
@@ -1 +1 @@
-Documentation/RelNotes/1.7.6.txt
\ No newline at end of file
+Documentation/RelNotes/1.7.7.txt
\ No newline at end of file
index 3005aedde68b48297aacd2082797b2e2f249b094..01858eb7bca277a125c414ef4bf1904fd0ea9059 100644 (file)
--- a/abspath.c
+++ b/abspath.c
@@ -40,7 +40,7 @@ const char *real_path(const char *path)
 
        while (depth--) {
                if (!is_directory(buf)) {
-                       char *last_slash = strrchr(buf, '/');
+                       char *last_slash = find_last_dir_sep(buf);
                        if (last_slash) {
                                *last_slash = '\0';
                                last_elem = xstrdup(last_slash + 1);
@@ -65,7 +65,7 @@ const char *real_path(const char *path)
                        if (len + strlen(last_elem) + 2 > PATH_MAX)
                                die ("Too long path name: '%s/%s'",
                                                buf, last_elem);
-                       if (len && buf[len-1] != '/')
+                       if (len && !is_dir_sep(buf[len-1]))
                                buf[len++] = '/';
                        strcpy(buf + len, last_elem);
                        free(last_elem);
index d6ab93bfbb369965e76e384e6984a29b667cdfbe..3142daa57a6fa1c8a7d21095946bf5d26443d0e0 100644 (file)
@@ -19,7 +19,7 @@
 static const char * const builtin_branch_usage[] = {
        "git branch [options] [-r | -a] [--merged | --no-merged]",
        "git branch [options] [-l] [-f] <branchname> [<start-point>]",
-       "git branch [options] [-r] (-d | -D) <branchname>",
+       "git branch [options] [-r] (-d | -D) <branchname>...",
        "git branch [options] (-m | -M) [<oldbranch>] <newbranch>",
        NULL
 };
index 94632dbdb400f9a2986d10f06dd16119ce1b4e54..07bd984084fbbfbb826ef5b784fc68069675e73c 100644 (file)
@@ -187,6 +187,8 @@ static int batch_one_object(const char *obj_name, int print_contents)
        if (type <= 0) {
                printf("%s missing\n", obj_name);
                fflush(stdout);
+               if (print_contents == BATCH)
+                       free(contents);
                return 0;
        }
 
index 28cdc51b85e7d433dca085c0080f964d19a391b4..f152adf9ab71287e02e9e85c0b84aae096e5bec7 100644 (file)
@@ -657,24 +657,25 @@ static void suggest_reattach(struct commit *commit, struct rev_info *revs)
                "Warning: you are leaving %d commit behind, "
                "not connected to\n"
                "any of your branches:\n\n"
-               "%s\n"
-               "If you want to keep it by creating a new branch, "
-               "this may be a good time\nto do so with:\n\n"
-               " git branch new_branch_name %s\n\n",
+               "%s\n",
                /* The plural version */
                "Warning: you are leaving %d commits behind, "
                "not connected to\n"
                "any of your branches:\n\n"
-               "%s\n"
-               "If you want to keep them by creating a new branch, "
-               "this may be a good time\nto do so with:\n\n"
-               " git branch new_branch_name %s\n\n",
+               "%s\n",
                /* Give ngettext() the count */
                lost),
                lost,
-               sb.buf,
-               sha1_to_hex(commit->object.sha1));
+               sb.buf);
        strbuf_release(&sb);
+
+       if (advice_detached_head)
+               fprintf(stderr,
+                       _(
+                       "If you want to keep them by creating a new branch, "
+                       "this may be a good time\nto do so with:\n\n"
+                       " git branch new_branch_name %s\n\n"),
+                       sha1_to_hex(commit->object.sha1));
 }
 
 /*
@@ -1071,7 +1072,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
                if (strbuf_check_branch_ref(&buf, opts.new_branch))
                        die(_("git checkout: we do not like '%s' as a branch name."),
                            opts.new_branch);
-               if (!get_sha1(buf.buf, rev)) {
+               if (ref_exists(buf.buf)) {
                        opts.branch_exists = 1;
                        if (!opts.new_branch_force)
                                die(_("git checkout: branch %s already exists"),
index 14bd14fce0fa6b9c9b047142d23d4a2237b57a36..69cd5eed78cb402839813e7eca65b5598afa4a90 100644 (file)
@@ -182,6 +182,7 @@ static int builtin_diff_combined(struct rev_info *revs,
                hashcpy((unsigned char *)(parent + i), ent[i].item->sha1);
        diff_tree_combined(parent[0], parent + 1, ents - 1,
                           revs->dense_combined_merges, revs);
+       free(parent);
        return 0;
 }
 
index ff5f73ba87b23fc7e361a1751c2d9a64e0267163..0498094711d1addd40f526f0c76dd8ddb76ef550 100644 (file)
@@ -225,7 +225,7 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
                        fprintf(stderr,
                                        _("Auto packing the repository for optimum performance. You may also\n"
                                        "run \"git gc\" manually. See "
-                                       "\"git help gc\" for more information."));
+                                       "\"git help gc\" for more information.\n"));
        } else
                append_option(argv_repack,
                              prune_expire && !strcmp(prune_expire, "now")
index 871afaa3c76ea34b67671a6e4a46957a08a946e5..cccf8da6d2a600154536ea642250699d9356f148 100644 (file)
@@ -93,8 +93,7 @@ static pthread_cond_t cond_write;
 /* Signalled when we are finished with everything. */
 static pthread_cond_t cond_result;
 
-static int print_hunk_marks_between_files;
-static int printed_something;
+static int skip_first_line;
 
 static void add_work(enum work_type type, char *name, void *id)
 {
@@ -160,10 +159,20 @@ static void work_done(struct work_item *w)
            todo_done = (todo_done+1) % ARRAY_SIZE(todo)) {
                w = &todo[todo_done];
                if (w->out.len) {
-                       if (print_hunk_marks_between_files && printed_something)
-                               write_or_die(1, "--\n", 3);
-                       write_or_die(1, w->out.buf, w->out.len);
-                       printed_something = 1;
+                       const char *p = w->out.buf;
+                       size_t len = w->out.len;
+
+                       /* Skip the leading hunk mark of the first file. */
+                       if (skip_first_line) {
+                               while (len) {
+                                       len--;
+                                       if (*p++ == '\n')
+                                               break;
+                               }
+                               skip_first_line = 0;
+                       }
+
+                       write_or_die(1, p, len);
                }
                free(w->name);
                free(w->identifier);
@@ -813,6 +822,10 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
                OPT_BOOLEAN('c', "count", &opt.count,
                        "show the number of matches instead of matching lines"),
                OPT__COLOR(&opt.color, "highlight matches"),
+               OPT_BOOLEAN(0, "break", &opt.file_break,
+                       "print empty line between matches from different files"),
+               OPT_BOOLEAN(0, "heading", &opt.heading,
+                       "show filename only once above matches from same file"),
                OPT_GROUP(""),
                OPT_CALLBACK('C', NULL, &opt, "n",
                        "show <n> context lines before and after matches",
@@ -967,8 +980,8 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
                use_threads = 0;
 
        if (use_threads) {
-               if (opt.pre_context || opt.post_context)
-                       print_hunk_marks_between_files = 1;
+               if (opt.pre_context || opt.post_context || opt.file_break)
+                       skip_first_line = 1;
                start_threads(&opt);
        }
 #else
index f14bc908309c0bb01ec251d71f26b490b294622c..a6a23fa1f3c7782566d7fcfe470dd424b876e4a5 100644 (file)
@@ -100,8 +100,10 @@ static int add_one_path(struct cache_entry *old, const char *path, int len, stru
        ce->ce_mode = ce_mode_from_stat(old, st->st_mode);
 
        if (index_path(ce->sha1, path, st,
-                      info_only ? 0 : HASH_WRITE_OBJECT))
+                      info_only ? 0 : HASH_WRITE_OBJECT)) {
+               free(ce);
                return -1;
+       }
        option = allow_add ? ADD_CACHE_OK_TO_ADD : 0;
        option |= allow_replace ? ADD_CACHE_OK_TO_REPLACE : 0;
        if (add_cache_entry(ce, option))
index b6079ae6cb03c7f3112c6eebc8c9a012d690a125..3a919b170726a95b19c16f984ade250d4fc24c07 100644 (file)
@@ -33,9 +33,9 @@ static void show_pack_info(struct packed_git *p, unsigned int flags)
                if (!sha1)
                        die("internal error pack-check nth-packed-object");
                offset = nth_packed_object_offset(p, i);
-               type = packed_object_info_detail(p, offset, &size, &store_size,
+               type = typename(packed_object_info_detail(p, offset, &size, &store_size,
                                                 &delta_chain_length,
-                                                base_sha1);
+                                                base_sha1));
                if (!stat_only)
                        printf("%s ", sha1_to_hex(sha1));
                if (!delta_chain_length) {
diff --git a/cache.h b/cache.h
index e11cf6ab1c73ac97c94e76e8c8699d55af95b978..bc9e5eb15e23c23b41b76e1f353e078ea3858344 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -6,6 +6,7 @@
 #include "hash.h"
 #include "advice.h"
 #include "gettext.h"
+#include "convert.h"
 
 #include SHA1_HEADER
 #ifndef git_SHA_CTX
@@ -582,35 +583,6 @@ extern int fsync_object_files;
 extern int core_preload_index;
 extern int core_apply_sparse_checkout;
 
-enum safe_crlf {
-       SAFE_CRLF_FALSE = 0,
-       SAFE_CRLF_FAIL = 1,
-       SAFE_CRLF_WARN = 2
-};
-
-extern enum safe_crlf safe_crlf;
-
-enum auto_crlf {
-       AUTO_CRLF_FALSE = 0,
-       AUTO_CRLF_TRUE = 1,
-       AUTO_CRLF_INPUT = -1
-};
-
-extern enum auto_crlf auto_crlf;
-
-enum eol {
-       EOL_UNSET,
-       EOL_CRLF,
-       EOL_LF,
-#ifdef NATIVE_CRLF
-       EOL_NATIVE = EOL_CRLF
-#else
-       EOL_NATIVE = EOL_LF
-#endif
-};
-
-extern enum eol core_eol;
-
 enum branch_track {
        BRANCH_TRACK_UNSPECIFIED = -1,
        BRANCH_TRACK_NEVER = 0,
@@ -747,7 +719,7 @@ extern char *expand_user_path(const char *path);
 char *enter_repo(char *path, int strict);
 static inline int is_absolute_path(const char *path)
 {
-       return path[0] == '/' || has_dos_drive_prefix(path);
+       return is_dir_sep(path[0]) || has_dos_drive_prefix(path);
 }
 int is_directory(const char *);
 const char *real_path(const char *path);
@@ -780,6 +752,9 @@ extern int hash_sha1_file(const void *buf, unsigned long len, const char *type,
 extern int write_sha1_file(const void *buf, unsigned long len, const char *type, unsigned char *return_sha1);
 extern int pretend_sha1_file(void *, unsigned long, enum object_type, unsigned char *);
 extern int force_object_loose(const unsigned char *sha1, time_t mtime);
+extern void *map_sha1_file(const unsigned char *sha1, unsigned long *size);
+extern int unpack_sha1_header(z_stream *stream, unsigned char *map, unsigned long mapsize, void *buffer, unsigned long bufsiz);
+extern int parse_sha1_header(const char *hdr, unsigned long *sizep);
 
 /* global flag to enable extra checks when accessing packed objects */
 extern int do_check_packed_object_crc;
@@ -1021,7 +996,37 @@ extern off_t find_pack_entry_one(const unsigned char *, struct packed_git *);
 extern void *unpack_entry(struct packed_git *, off_t, enum object_type *, unsigned long *);
 extern unsigned long unpack_object_header_buffer(const unsigned char *buf, unsigned long len, enum object_type *type, unsigned long *sizep);
 extern unsigned long get_size_from_delta(struct packed_git *, struct pack_window **, off_t);
-extern const char *packed_object_info_detail(struct packed_git *, off_t, unsigned long *, unsigned long *, unsigned int *, unsigned char *);
+extern int packed_object_info_detail(struct packed_git *, off_t, unsigned long *, unsigned long *, unsigned int *, unsigned char *);
+extern int unpack_object_header(struct packed_git *, struct pack_window **, off_t *, unsigned long *);
+
+struct object_info {
+       /* Request */
+       unsigned long *sizep;
+
+       /* Response */
+       enum {
+               OI_CACHED,
+               OI_LOOSE,
+               OI_PACKED,
+               OI_DBCACHED
+       } whence;
+       union {
+               /*
+                * struct {
+                *      ... Nothing to expose in this case
+                * } cached;
+                * struct {
+                *      ... Nothing to expose in this case
+                * } loose;
+                */
+               struct {
+                       struct packed_git *pack;
+                       off_t offset;
+                       unsigned int is_delta;
+               } packed;
+       } u;
+};
+extern int sha1_object_info_extended(const unsigned char *, struct object_info *);
 
 /* Dumb servers support */
 extern int update_server_info(int);
@@ -1129,13 +1134,6 @@ extern void trace_strbuf(const char *key, const struct strbuf *buf);
 
 void packet_trace_identity(const char *prog);
 
-/* convert.c */
-/* returns 1 if *dst was used */
-extern int convert_to_git(const char *path, const char *src, size_t len,
-                          struct strbuf *dst, enum safe_crlf checksafe);
-extern int convert_to_working_tree(const char *path, const char *src, size_t len, struct strbuf *dst);
-extern int renormalize_buffer(const char *path, const char *src, size_t len, struct strbuf *dst);
-
 /* add */
 /*
  * return 0 if success, 1 - if addition of a file failed and
index 655fa89d8a7ffe3c3823080f5af3e5e385e95440..be67cfcd45469497f81e0c36ed87a11fd409daee 100644 (file)
@@ -7,6 +7,7 @@
 #include "xdiff-interface.h"
 #include "log-tree.h"
 #include "refs.h"
+#include "userdiff.h"
 
 static struct combine_diff_path *intersect_paths(struct combine_diff_path *curr, int n, int num_parent)
 {
@@ -92,7 +93,9 @@ struct sline {
        unsigned long *p_lno;
 };
 
-static char *grab_blob(const unsigned char *sha1, unsigned int mode, unsigned long *size)
+static char *grab_blob(const unsigned char *sha1, unsigned int mode,
+                      unsigned long *size, struct userdiff_driver *textconv,
+                      const char *path)
 {
        char *blob;
        enum object_type type;
@@ -105,6 +108,11 @@ static char *grab_blob(const unsigned char *sha1, unsigned int mode, unsigned lo
                /* deleted blob */
                *size = 0;
                return xcalloc(1, 1);
+       } else if (textconv) {
+               struct diff_filespec *df = alloc_filespec(path);
+               fill_filespec(df, sha1, mode);
+               *size = fill_textconv(textconv, df, &blob);
+               free_filespec(df);
        } else {
                blob = read_sha1_file(sha1, &type, size);
                if (type != OBJ_BLOB)
@@ -204,7 +212,9 @@ static void consume_line(void *state_, char *line, unsigned long len)
 static void combine_diff(const unsigned char *parent, unsigned int mode,
                         mmfile_t *result_file,
                         struct sline *sline, unsigned int cnt, int n,
-                        int num_parent, int result_deleted)
+                        int num_parent, int result_deleted,
+                        struct userdiff_driver *textconv,
+                        const char *path)
 {
        unsigned int p_lno, lno;
        unsigned long nmask = (1UL << n);
@@ -217,7 +227,7 @@ static void combine_diff(const unsigned char *parent, unsigned int mode,
        if (result_deleted)
                return; /* result deleted */
 
-       parent_file.ptr = grab_blob(parent, mode, &sz);
+       parent_file.ptr = grab_blob(parent, mode, &sz, textconv, path);
        parent_file.size = sz;
        memset(&xpp, 0, sizeof(xpp));
        xpp.flags = 0;
@@ -681,6 +691,82 @@ static void dump_quoted_path(const char *head,
        puts(buf.buf);
 }
 
+static void show_combined_header(struct combine_diff_path *elem,
+                                int num_parent,
+                                int dense,
+                                struct rev_info *rev,
+                                int mode_differs,
+                                int show_file_header)
+{
+       struct diff_options *opt = &rev->diffopt;
+       int abbrev = DIFF_OPT_TST(opt, FULL_INDEX) ? 40 : DEFAULT_ABBREV;
+       const char *a_prefix = opt->a_prefix ? opt->a_prefix : "a/";
+       const char *b_prefix = opt->b_prefix ? opt->b_prefix : "b/";
+       int use_color = DIFF_OPT_TST(opt, COLOR_DIFF);
+       const char *c_meta = diff_get_color(use_color, DIFF_METAINFO);
+       const char *c_reset = diff_get_color(use_color, DIFF_RESET);
+       const char *abb;
+       int added = 0;
+       int deleted = 0;
+       int i;
+
+       if (rev->loginfo && !rev->no_commit_id)
+               show_log(rev);
+
+       dump_quoted_path(dense ? "diff --cc " : "diff --combined ",
+                        "", elem->path, c_meta, c_reset);
+       printf("%sindex ", c_meta);
+       for (i = 0; i < num_parent; i++) {
+               abb = find_unique_abbrev(elem->parent[i].sha1,
+                                        abbrev);
+               printf("%s%s", i ? "," : "", abb);
+       }
+       abb = find_unique_abbrev(elem->sha1, abbrev);
+       printf("..%s%s\n", abb, c_reset);
+
+       if (mode_differs) {
+               deleted = !elem->mode;
+
+               /* We say it was added if nobody had it */
+               added = !deleted;
+               for (i = 0; added && i < num_parent; i++)
+                       if (elem->parent[i].status !=
+                           DIFF_STATUS_ADDED)
+                               added = 0;
+               if (added)
+                       printf("%snew file mode %06o",
+                              c_meta, elem->mode);
+               else {
+                       if (deleted)
+                               printf("%sdeleted file ", c_meta);
+                       printf("mode ");
+                       for (i = 0; i < num_parent; i++) {
+                               printf("%s%06o", i ? "," : "",
+                                      elem->parent[i].mode);
+                       }
+                       if (elem->mode)
+                               printf("..%06o", elem->mode);
+               }
+               printf("%s\n", c_reset);
+       }
+
+       if (!show_file_header)
+               return;
+
+       if (added)
+               dump_quoted_path("--- ", "", "/dev/null",
+                                c_meta, c_reset);
+       else
+               dump_quoted_path("--- ", a_prefix, elem->path,
+                                c_meta, c_reset);
+       if (deleted)
+               dump_quoted_path("+++ ", "", "/dev/null",
+                                c_meta, c_reset);
+       else
+               dump_quoted_path("+++ ", b_prefix, elem->path,
+                                c_meta, c_reset);
+}
+
 static void show_patch_diff(struct combine_diff_path *elem, int num_parent,
                            int dense, struct rev_info *rev)
 {
@@ -692,17 +778,22 @@ static void show_patch_diff(struct combine_diff_path *elem, int num_parent,
        int mode_differs = 0;
        int i, show_hunks;
        int working_tree_file = is_null_sha1(elem->sha1);
-       int abbrev = DIFF_OPT_TST(opt, FULL_INDEX) ? 40 : DEFAULT_ABBREV;
-       const char *a_prefix, *b_prefix;
        mmfile_t result_file;
+       struct userdiff_driver *userdiff;
+       struct userdiff_driver *textconv = NULL;
+       int is_binary;
 
        context = opt->context;
-       a_prefix = opt->a_prefix ? opt->a_prefix : "a/";
-       b_prefix = opt->b_prefix ? opt->b_prefix : "b/";
+       userdiff = userdiff_find_by_path(elem->path);
+       if (!userdiff)
+               userdiff = userdiff_find_by_name("default");
+       if (DIFF_OPT_TST(opt, ALLOW_TEXTCONV))
+               textconv = userdiff_get_textconv(userdiff);
 
        /* Read the result of merge first */
        if (!working_tree_file)
-               result = grab_blob(elem->sha1, elem->mode, &result_size);
+               result = grab_blob(elem->sha1, elem->mode, &result_size,
+                                  textconv, elem->path);
        else {
                /* Used by diff-tree to read from the working tree */
                struct stat st;
@@ -725,9 +816,16 @@ static void show_patch_diff(struct combine_diff_path *elem, int num_parent,
                } else if (S_ISDIR(st.st_mode)) {
                        unsigned char sha1[20];
                        if (resolve_gitlink_ref(elem->path, "HEAD", sha1) < 0)
-                               result = grab_blob(elem->sha1, elem->mode, &result_size);
+                               result = grab_blob(elem->sha1, elem->mode,
+                                                  &result_size, NULL, NULL);
                        else
-                               result = grab_blob(sha1, elem->mode, &result_size);
+                               result = grab_blob(sha1, elem->mode,
+                                                  &result_size, NULL, NULL);
+               } else if (textconv) {
+                       struct diff_filespec *df = alloc_filespec(elem->path);
+                       fill_filespec(df, null_sha1, st.st_mode);
+                       result_size = fill_textconv(textconv, df, &result);
+                       free_filespec(df);
                } else if (0 <= (fd = open(elem->path, O_RDONLY))) {
                        size_t len = xsize_t(st.st_size);
                        ssize_t done;
@@ -777,6 +875,38 @@ static void show_patch_diff(struct combine_diff_path *elem, int num_parent,
                        close(fd);
        }
 
+       for (i = 0; i < num_parent; i++) {
+               if (elem->parent[i].mode != elem->mode) {
+                       mode_differs = 1;
+                       break;
+               }
+       }
+
+       if (textconv)
+               is_binary = 0;
+       else if (userdiff->binary != -1)
+               is_binary = userdiff->binary;
+       else {
+               is_binary = buffer_is_binary(result, result_size);
+               for (i = 0; !is_binary && i < num_parent; i++) {
+                       char *buf;
+                       unsigned long size;
+                       buf = grab_blob(elem->parent[i].sha1,
+                                       elem->parent[i].mode,
+                                       &size, NULL, NULL);
+                       if (buffer_is_binary(buf, size))
+                               is_binary = 1;
+                       free(buf);
+               }
+       }
+       if (is_binary) {
+               show_combined_header(elem, num_parent, dense, rev,
+                                    mode_differs, 0);
+               printf("Binary files differ\n");
+               free(result);
+               return;
+       }
+
        for (cnt = 0, cp = result; cp < result + result_size; cp++) {
                if (*cp == '\n')
                        cnt++;
@@ -824,71 +954,15 @@ static void show_patch_diff(struct combine_diff_path *elem, int num_parent,
                        combine_diff(elem->parent[i].sha1,
                                     elem->parent[i].mode,
                                     &result_file, sline,
-                                    cnt, i, num_parent, result_deleted);
-               if (elem->parent[i].mode != elem->mode)
-                       mode_differs = 1;
+                                    cnt, i, num_parent, result_deleted,
+                                    textconv, elem->path);
        }
 
        show_hunks = make_hunks(sline, cnt, num_parent, dense);
 
        if (show_hunks || mode_differs || working_tree_file) {
-               const char *abb;
-               int use_color = DIFF_OPT_TST(opt, COLOR_DIFF);
-               const char *c_meta = diff_get_color(use_color, DIFF_METAINFO);
-               const char *c_reset = diff_get_color(use_color, DIFF_RESET);
-               int added = 0;
-               int deleted = 0;
-
-               if (rev->loginfo && !rev->no_commit_id)
-                       show_log(rev);
-               dump_quoted_path(dense ? "diff --cc " : "diff --combined ",
-                                "", elem->path, c_meta, c_reset);
-               printf("%sindex ", c_meta);
-               for (i = 0; i < num_parent; i++) {
-                       abb = find_unique_abbrev(elem->parent[i].sha1,
-                                                abbrev);
-                       printf("%s%s", i ? "," : "", abb);
-               }
-               abb = find_unique_abbrev(elem->sha1, abbrev);
-               printf("..%s%s\n", abb, c_reset);
-
-               if (mode_differs) {
-                       deleted = !elem->mode;
-
-                       /* We say it was added if nobody had it */
-                       added = !deleted;
-                       for (i = 0; added && i < num_parent; i++)
-                               if (elem->parent[i].status !=
-                                   DIFF_STATUS_ADDED)
-                                       added = 0;
-                       if (added)
-                               printf("%snew file mode %06o",
-                                      c_meta, elem->mode);
-                       else {
-                               if (deleted)
-                                       printf("%sdeleted file ", c_meta);
-                               printf("mode ");
-                               for (i = 0; i < num_parent; i++) {
-                                       printf("%s%06o", i ? "," : "",
-                                              elem->parent[i].mode);
-                               }
-                               if (elem->mode)
-                                       printf("..%06o", elem->mode);
-                       }
-                       printf("%s\n", c_reset);
-               }
-               if (added)
-                       dump_quoted_path("--- ", "", "/dev/null",
-                                        c_meta, c_reset);
-               else
-                       dump_quoted_path("--- ", a_prefix, elem->path,
-                                        c_meta, c_reset);
-               if (deleted)
-                       dump_quoted_path("+++ ", "", "/dev/null",
-                                        c_meta, c_reset);
-               else
-                       dump_quoted_path("+++ ", b_prefix, elem->path,
-                                        c_meta, c_reset);
+               show_combined_header(elem, num_parent, dense, rev,
+                                    mode_differs, 1);
                dump_sline(sline, cnt, num_parent,
                           DIFF_OPT_TST(opt, COLOR_DIFF), result_deleted);
        }
index f6e9ff7762356e099d2fe20fd31359bc0a1f68a2..6ef0cc4f99becd772a6fed1cfe2484a67b3a9000 100644 (file)
@@ -178,7 +178,7 @@ static int ask_yes_no_if_possible(const char *format, ...)
        vsnprintf(question, sizeof(question), format, args);
        va_end(args);
 
-       if ((retry_hook[0] = getenv("GIT_ASK_YESNO"))) {
+       if ((retry_hook[0] = mingw_getenv("GIT_ASK_YESNO"))) {
                retry_hook[1] = question;
                return !run_command_v_opt(retry_hook, 0);
        }
@@ -599,19 +599,6 @@ char *mingw_getcwd(char *pointer, int len)
        return ret;
 }
 
-#undef getenv
-char *mingw_getenv(const char *name)
-{
-       char *result = getenv(name);
-       if (!result && !strcmp(name, "TMPDIR")) {
-               /* on Windows it is TMP and TEMP */
-               result = getenv("TMP");
-               if (!result)
-                       result = getenv("TEMP");
-       }
-       return result;
-}
-
 /*
  * See http://msdn2.microsoft.com/en-us/library/17w5ykft(vs.71).aspx
  * (Parsing C++ Command-Line Arguments)
@@ -711,7 +698,7 @@ static const char *parse_interpreter(const char *cmd)
  */
 static char **get_path_split(void)
 {
-       char *p, **path, *envpath = getenv("PATH");
+       char *p, **path, *envpath = mingw_getenv("PATH");
        int i, n = 0;
 
        if (!envpath || !*envpath)
@@ -1128,6 +1115,36 @@ char **make_augmented_environ(const char *const *vars)
        return env;
 }
 
+#undef getenv
+
+/*
+ * The system's getenv looks up the name in a case-insensitive manner.
+ * This version tries a case-sensitive lookup and falls back to
+ * case-insensitive if nothing was found.  This is necessary because,
+ * as a prominent example, CMD sets 'Path', but not 'PATH'.
+ * Warning: not thread-safe.
+ */
+static char *getenv_cs(const char *name)
+{
+       size_t len = strlen(name);
+       int i = lookup_env(environ, name, len);
+       if (i >= 0)
+               return environ[i] + len + 1;    /* skip past name and '=' */
+       return getenv(name);
+}
+
+char *mingw_getenv(const char *name)
+{
+       char *result = getenv_cs(name);
+       if (!result && !strcmp(name, "TMPDIR")) {
+               /* on Windows it is TMP and TEMP */
+               result = getenv_cs("TMP");
+               if (!result)
+                       result = getenv_cs("TEMP");
+       }
+       return result;
+}
+
 /*
  * Note, this isn't a complete replacement for getaddrinfo. It assumes
  * that service contains a numerical port, or that it is null. It
index 547568b9181d61d50f06cf5b4b0ab43af78e5aa2..ce9dd980eb211c0301c4008249c61fb0b09f1280 100644 (file)
@@ -300,6 +300,15 @@ int winansi_fprintf(FILE *stream, const char *format, ...) __attribute__((format
 
 #define has_dos_drive_prefix(path) (isalpha(*(path)) && (path)[1] == ':')
 #define is_dir_sep(c) ((c) == '/' || (c) == '\\')
+static inline char *mingw_find_last_dir_sep(const char *path)
+{
+       char *ret = NULL;
+       for (; *path; ++path)
+               if (is_dir_sep(*path))
+                       ret = (char *)path;
+       return ret;
+}
+#define find_last_dir_sep mingw_find_last_dir_sep
 #define PATH_SEP ';'
 #define PRIuMAX "I64u"
 
index b36290fa6015d9b66dc6a5891ff12f85f279a1c3..5a8309076dc633f431797c4bd59876490b0d7408 100755 (executable)
@@ -2058,7 +2058,7 @@ _git_config ()
                color.ui
                commit.status
                commit.template
-               core.abbrevguard
+               core.abbrev
                core.askpass
                core.attributesfile
                core.autocrlf
index efc7e07d475c66f7835dc6cbbd3bc358f01c41c3..85939c29be67d4179a2afb9069b575afed4d8bb2 100644 (file)
--- a/convert.c
+++ b/convert.c
@@ -813,3 +813,400 @@ int renormalize_buffer(const char *path, const char *src, size_t len, struct str
        }
        return ret | convert_to_git(path, src, len, dst, 0);
 }
+
+/*****************************************************************
+ *
+ * Streaming converison support
+ *
+ *****************************************************************/
+
+typedef int (*filter_fn)(struct stream_filter *,
+                        const char *input, size_t *isize_p,
+                        char *output, size_t *osize_p);
+typedef void (*free_fn)(struct stream_filter *);
+
+struct stream_filter_vtbl {
+       filter_fn filter;
+       free_fn free;
+};
+
+struct stream_filter {
+       struct stream_filter_vtbl *vtbl;
+};
+
+static int null_filter_fn(struct stream_filter *filter,
+                         const char *input, size_t *isize_p,
+                         char *output, size_t *osize_p)
+{
+       size_t count;
+
+       if (!input)
+               return 0; /* we do not keep any states */
+       count = *isize_p;
+       if (*osize_p < count)
+               count = *osize_p;
+       if (count) {
+               memmove(output, input, count);
+               *isize_p -= count;
+               *osize_p -= count;
+       }
+       return 0;
+}
+
+static void null_free_fn(struct stream_filter *filter)
+{
+       ; /* nothing -- null instances are shared */
+}
+
+static struct stream_filter_vtbl null_vtbl = {
+       null_filter_fn,
+       null_free_fn,
+};
+
+static struct stream_filter null_filter_singleton = {
+       &null_vtbl,
+};
+
+int is_null_stream_filter(struct stream_filter *filter)
+{
+       return filter == &null_filter_singleton;
+}
+
+
+/*
+ * LF-to-CRLF filter
+ */
+static int lf_to_crlf_filter_fn(struct stream_filter *filter,
+                               const char *input, size_t *isize_p,
+                               char *output, size_t *osize_p)
+{
+       size_t count;
+
+       if (!input)
+               return 0; /* we do not keep any states */
+       count = *isize_p;
+       if (count) {
+               size_t i, o;
+               for (i = o = 0; o < *osize_p && i < count; i++) {
+                       char ch = input[i];
+                       if (ch == '\n') {
+                               if (o + 1 < *osize_p)
+                                       output[o++] = '\r';
+                               else
+                                       break;
+                       }
+                       output[o++] = ch;
+               }
+
+               *osize_p -= o;
+               *isize_p -= i;
+       }
+       return 0;
+}
+
+static struct stream_filter_vtbl lf_to_crlf_vtbl = {
+       lf_to_crlf_filter_fn,
+       null_free_fn,
+};
+
+static struct stream_filter lf_to_crlf_filter_singleton = {
+       &lf_to_crlf_vtbl,
+};
+
+
+/*
+ * Cascade filter
+ */
+#define FILTER_BUFFER 1024
+struct cascade_filter {
+       struct stream_filter filter;
+       struct stream_filter *one;
+       struct stream_filter *two;
+       char buf[FILTER_BUFFER];
+       int end, ptr;
+};
+
+static int cascade_filter_fn(struct stream_filter *filter,
+                            const char *input, size_t *isize_p,
+                            char *output, size_t *osize_p)
+{
+       struct cascade_filter *cas = (struct cascade_filter *) filter;
+       size_t filled = 0;
+       size_t sz = *osize_p;
+       size_t to_feed, remaining;
+
+       /*
+        * input -- (one) --> buf -- (two) --> output
+        */
+       while (filled < sz) {
+               remaining = sz - filled;
+
+               /* do we already have something to feed two with? */
+               if (cas->ptr < cas->end) {
+                       to_feed = cas->end - cas->ptr;
+                       if (stream_filter(cas->two,
+                                         cas->buf + cas->ptr, &to_feed,
+                                         output + filled, &remaining))
+                               return -1;
+                       cas->ptr += (cas->end - cas->ptr) - to_feed;
+                       filled = sz - remaining;
+                       continue;
+               }
+
+               /* feed one from upstream and have it emit into our buffer */
+               to_feed = input ? *isize_p : 0;
+               if (input && !to_feed)
+                       break;
+               remaining = sizeof(cas->buf);
+               if (stream_filter(cas->one,
+                                 input, &to_feed,
+                                 cas->buf, &remaining))
+                       return -1;
+               cas->end = sizeof(cas->buf) - remaining;
+               cas->ptr = 0;
+               if (input) {
+                       size_t fed = *isize_p - to_feed;
+                       *isize_p -= fed;
+                       input += fed;
+               }
+
+               /* do we know that we drained one completely? */
+               if (input || cas->end)
+                       continue;
+
+               /* tell two to drain; we have nothing more to give it */
+               to_feed = 0;
+               remaining = sz - filled;
+               if (stream_filter(cas->two,
+                                 NULL, &to_feed,
+                                 output + filled, &remaining))
+                       return -1;
+               if (remaining == (sz - filled))
+                       break; /* completely drained two */
+               filled = sz - remaining;
+       }
+       *osize_p -= filled;
+       return 0;
+}
+
+static void cascade_free_fn(struct stream_filter *filter)
+{
+       struct cascade_filter *cas = (struct cascade_filter *)filter;
+       free_stream_filter(cas->one);
+       free_stream_filter(cas->two);
+       free(filter);
+}
+
+static struct stream_filter_vtbl cascade_vtbl = {
+       cascade_filter_fn,
+       cascade_free_fn,
+};
+
+static struct stream_filter *cascade_filter(struct stream_filter *one,
+                                           struct stream_filter *two)
+{
+       struct cascade_filter *cascade;
+
+       if (!one || is_null_stream_filter(one))
+               return two;
+       if (!two || is_null_stream_filter(two))
+               return one;
+
+       cascade = xmalloc(sizeof(*cascade));
+       cascade->one = one;
+       cascade->two = two;
+       cascade->end = cascade->ptr = 0;
+       cascade->filter.vtbl = &cascade_vtbl;
+       return (struct stream_filter *)cascade;
+}
+
+/*
+ * ident filter
+ */
+#define IDENT_DRAINING (-1)
+#define IDENT_SKIPPING (-2)
+struct ident_filter {
+       struct stream_filter filter;
+       struct strbuf left;
+       int state;
+       char ident[45]; /* ": x40 $" */
+};
+
+static int is_foreign_ident(const char *str)
+{
+       int i;
+
+       if (prefixcmp(str, "$Id: "))
+               return 0;
+       for (i = 5; str[i]; i++) {
+               if (isspace(str[i]) && str[i+1] != '$')
+                       return 1;
+       }
+       return 0;
+}
+
+static void ident_drain(struct ident_filter *ident, char **output_p, size_t *osize_p)
+{
+       size_t to_drain = ident->left.len;
+
+       if (*osize_p < to_drain)
+               to_drain = *osize_p;
+       if (to_drain) {
+               memcpy(*output_p, ident->left.buf, to_drain);
+               strbuf_remove(&ident->left, 0, to_drain);
+               *output_p += to_drain;
+               *osize_p -= to_drain;
+       }
+       if (!ident->left.len)
+               ident->state = 0;
+}
+
+static int ident_filter_fn(struct stream_filter *filter,
+                          const char *input, size_t *isize_p,
+                          char *output, size_t *osize_p)
+{
+       struct ident_filter *ident = (struct ident_filter *)filter;
+       static const char head[] = "$Id";
+
+       if (!input) {
+               /* drain upon eof */
+               switch (ident->state) {
+               default:
+                       strbuf_add(&ident->left, head, ident->state);
+               case IDENT_SKIPPING:
+                       /* fallthru */
+               case IDENT_DRAINING:
+                       ident_drain(ident, &output, osize_p);
+               }
+               return 0;
+       }
+
+       while (*isize_p || (ident->state == IDENT_DRAINING)) {
+               int ch;
+
+               if (ident->state == IDENT_DRAINING) {
+                       ident_drain(ident, &output, osize_p);
+                       if (!*osize_p)
+                               break;
+                       continue;
+               }
+
+               ch = *(input++);
+               (*isize_p)--;
+
+               if (ident->state == IDENT_SKIPPING) {
+                       /*
+                        * Skipping until '$' or LF, but keeping them
+                        * in case it is a foreign ident.
+                        */
+                       strbuf_addch(&ident->left, ch);
+                       if (ch != '\n' && ch != '$')
+                               continue;
+                       if (ch == '$' && !is_foreign_ident(ident->left.buf)) {
+                               strbuf_setlen(&ident->left, sizeof(head) - 1);
+                               strbuf_addstr(&ident->left, ident->ident);
+                       }
+                       ident->state = IDENT_DRAINING;
+                       continue;
+               }
+
+               if (ident->state < sizeof(head) &&
+                   head[ident->state] == ch) {
+                       ident->state++;
+                       continue;
+               }
+
+               if (ident->state)
+                       strbuf_add(&ident->left, head, ident->state);
+               if (ident->state == sizeof(head) - 1) {
+                       if (ch != ':' && ch != '$') {
+                               strbuf_addch(&ident->left, ch);
+                               ident->state = 0;
+                               continue;
+                       }
+
+                       if (ch == ':') {
+                               strbuf_addch(&ident->left, ch);
+                               ident->state = IDENT_SKIPPING;
+                       } else {
+                               strbuf_addstr(&ident->left, ident->ident);
+                               ident->state = IDENT_DRAINING;
+                       }
+                       continue;
+               }
+
+               strbuf_addch(&ident->left, ch);
+               ident->state = IDENT_DRAINING;
+       }
+       return 0;
+}
+
+static void ident_free_fn(struct stream_filter *filter)
+{
+       struct ident_filter *ident = (struct ident_filter *)filter;
+       strbuf_release(&ident->left);
+       free(filter);
+}
+
+static struct stream_filter_vtbl ident_vtbl = {
+       ident_filter_fn,
+       ident_free_fn,
+};
+
+static struct stream_filter *ident_filter(const unsigned char *sha1)
+{
+       struct ident_filter *ident = xmalloc(sizeof(*ident));
+
+       sprintf(ident->ident, ": %s $", sha1_to_hex(sha1));
+       strbuf_init(&ident->left, 0);
+       ident->filter.vtbl = &ident_vtbl;
+       ident->state = 0;
+       return (struct stream_filter *)ident;
+}
+
+/*
+ * Return an appropriately constructed filter for the path, or NULL if
+ * the contents cannot be filtered without reading the whole thing
+ * in-core.
+ *
+ * Note that you would be crazy to set CRLF, smuge/clean or ident to a
+ * large binary blob you would want us not to slurp into the memory!
+ */
+struct stream_filter *get_stream_filter(const char *path, const unsigned char *sha1)
+{
+       struct conv_attrs ca;
+       enum crlf_action crlf_action;
+       struct stream_filter *filter = NULL;
+
+       convert_attrs(&ca, path);
+
+       if (ca.drv && (ca.drv->smudge || ca.drv->clean))
+               return filter;
+
+       if (ca.ident)
+               filter = ident_filter(sha1);
+
+       crlf_action = input_crlf_action(ca.crlf_action, ca.eol_attr);
+
+       if ((crlf_action == CRLF_BINARY) || (crlf_action == CRLF_INPUT) ||
+           (crlf_action == CRLF_GUESS && auto_crlf == AUTO_CRLF_FALSE))
+               filter = cascade_filter(filter, &null_filter_singleton);
+
+       else if (output_eol(crlf_action) == EOL_CRLF &&
+                !(crlf_action == CRLF_AUTO || crlf_action == CRLF_GUESS))
+               filter = cascade_filter(filter, &lf_to_crlf_filter_singleton);
+
+       return filter;
+}
+
+void free_stream_filter(struct stream_filter *filter)
+{
+       filter->vtbl->free(filter);
+}
+
+int stream_filter(struct stream_filter *filter,
+                 const char *input, size_t *isize_p,
+                 char *output, size_t *osize_p)
+{
+       return filter->vtbl->filter(filter, input, isize_p, output, osize_p);
+}
diff --git a/convert.h b/convert.h
new file mode 100644 (file)
index 0000000..d799a16
--- /dev/null
+++ b/convert.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2011, Google Inc.
+ */
+#ifndef CONVERT_H
+#define CONVERT_H
+
+enum safe_crlf {
+       SAFE_CRLF_FALSE = 0,
+       SAFE_CRLF_FAIL = 1,
+       SAFE_CRLF_WARN = 2
+};
+
+extern enum safe_crlf safe_crlf;
+
+enum auto_crlf {
+       AUTO_CRLF_FALSE = 0,
+       AUTO_CRLF_TRUE = 1,
+       AUTO_CRLF_INPUT = -1
+};
+
+extern enum auto_crlf auto_crlf;
+
+enum eol {
+       EOL_UNSET,
+       EOL_CRLF,
+       EOL_LF,
+#ifdef NATIVE_CRLF
+       EOL_NATIVE = EOL_CRLF
+#else
+       EOL_NATIVE = EOL_LF
+#endif
+};
+
+extern enum eol core_eol;
+
+/* returns 1 if *dst was used */
+extern int convert_to_git(const char *path, const char *src, size_t len,
+                         struct strbuf *dst, enum safe_crlf checksafe);
+extern int convert_to_working_tree(const char *path, const char *src,
+                                  size_t len, struct strbuf *dst);
+extern int renormalize_buffer(const char *path, const char *src, size_t len,
+                             struct strbuf *dst);
+
+/*****************************************************************
+ *
+ * Streaming converison support
+ *
+ *****************************************************************/
+
+struct stream_filter; /* opaque */
+
+extern struct stream_filter *get_stream_filter(const char *path, const unsigned char *);
+extern void free_stream_filter(struct stream_filter *);
+extern int is_null_stream_filter(struct stream_filter *);
+
+/*
+ * Use as much input up to *isize_p and fill output up to *osize_p;
+ * update isize_p and osize_p to indicate how much buffer space was
+ * consumed and filled. Return 0 on success, non-zero on error.
+ *
+ * Some filters may need to buffer the input and look-ahead inside it
+ * to decide what to output, and they may consume more than zero bytes
+ * of input and still not produce any output. After feeding all the
+ * input, pass NULL as input and keep calling this function, to let
+ * such filters know there is no more input coming and it is time for
+ * them to produce the remaining output based on the buffered input.
+ */
+extern int stream_filter(struct stream_filter *,
+                        const char *input, size_t *isize_p,
+                        char *output, size_t *osize_p);
+
+#endif /* CONVERT_H */
index 9c29293bbc05d175ba13338813e8532c7ad677cf..2e09500c8210e22d6c81484aa198f1d811a9933d 100644 (file)
@@ -433,8 +433,13 @@ static int oneway_diff(struct cache_entry **src, struct unpack_trees_options *o)
        if (tree == o->df_conflict_entry)
                tree = NULL;
 
-       if (ce_path_match(idx ? idx : tree, &revs->prune_data))
+       if (ce_path_match(idx ? idx : tree, &revs->prune_data)) {
                do_oneway_diff(o, idx, tree);
+               if (diff_can_quit_early(&revs->diffopt)) {
+                       o->exiting_early = 1;
+                       return -1;
+               }
+       }
 
        return 0;
 }
diff --git a/diff.c b/diff.c
index 61bedaed57216cadaf2716ef7aa0d63889571f8a..918c70ca457bcdca54b9d524ff14b60d88b5a631 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -1316,9 +1316,10 @@ static void show_stats(struct diffstat_t *data, struct diff_options *options)
        int i, len, add, del, adds = 0, dels = 0;
        uintmax_t max_change = 0, max_len = 0;
        int total_files = data->nr;
-       int width, name_width;
+       int width, name_width, count;
        const char *reset, *add_c, *del_c;
        const char *line_prefix = "";
+       int extra_shown = 0;
        struct strbuf *msg = NULL;
 
        if (data->nr == 0)
@@ -1331,6 +1332,7 @@ static void show_stats(struct diffstat_t *data, struct diff_options *options)
 
        width = options->stat_width ? options->stat_width : 80;
        name_width = options->stat_name_width ? options->stat_name_width : 50;
+       count = options->stat_count ? options->stat_count : data->nr;
 
        /* Sanity: give at least 5 columns to the graph,
         * but leave at least 10 columns for the name.
@@ -1347,9 +1349,14 @@ static void show_stats(struct diffstat_t *data, struct diff_options *options)
        add_c = diff_get_color_opt(options, DIFF_FILE_NEW);
        del_c = diff_get_color_opt(options, DIFF_FILE_OLD);
 
-       for (i = 0; i < data->nr; i++) {
+       for (i = 0; (i < count) && (i < data->nr); i++) {
                struct diffstat_file *file = data->files[i];
                uintmax_t change = file->added + file->deleted;
+               if (!data->files[i]->is_renamed &&
+                        (change == 0)) {
+                       count++; /* not shown == room for one more */
+                       continue;
+               }
                fill_print_name(file);
                len = strlen(file->print_name);
                if (max_len < len)
@@ -1360,6 +1367,7 @@ static void show_stats(struct diffstat_t *data, struct diff_options *options)
                if (max_change < change)
                        max_change = change;
        }
+       count = i; /* min(count, data->nr) */
 
        /* Compute the width of the graph part;
         * 10 is for one blank at the beginning of the line plus
@@ -1374,13 +1382,18 @@ static void show_stats(struct diffstat_t *data, struct diff_options *options)
        else
                width = max_change;
 
-       for (i = 0; i < data->nr; i++) {
+       for (i = 0; i < count; i++) {
                const char *prefix = "";
                char *name = data->files[i]->print_name;
                uintmax_t added = data->files[i]->added;
                uintmax_t deleted = data->files[i]->deleted;
                int name_len;
 
+               if (!data->files[i]->is_renamed &&
+                        (added + deleted == 0)) {
+                       total_files--;
+                       continue;
+               }
                /*
                 * "scale" the filename
                 */
@@ -1415,11 +1428,6 @@ static void show_stats(struct diffstat_t *data, struct diff_options *options)
                        fprintf(options->file, "  Unmerged\n");
                        continue;
                }
-               else if (!data->files[i]->is_renamed &&
-                        (added + deleted == 0)) {
-                       total_files--;
-                       continue;
-               }
 
                /*
                 * scale the add/delete
@@ -1441,6 +1449,20 @@ static void show_stats(struct diffstat_t *data, struct diff_options *options)
                show_graph(options->file, '-', del, del_c, reset);
                fprintf(options->file, "\n");
        }
+       for (i = count; i < data->nr; i++) {
+               uintmax_t added = data->files[i]->added;
+               uintmax_t deleted = data->files[i]->deleted;
+               if (!data->files[i]->is_renamed &&
+                        (added + deleted == 0)) {
+                       total_files--;
+                       continue;
+               }
+               adds += added;
+               dels += deleted;
+               if (!extra_shown)
+                       fprintf(options->file, "%s ...\n", line_prefix);
+               extra_shown = 1;
+       }
        fprintf(options->file, "%s", line_prefix);
        fprintf(options->file,
               " %d files changed, %d insertions(+), %d deletions(-)\n",
@@ -1984,19 +2006,7 @@ struct userdiff_driver *get_textconv(struct diff_filespec *one)
                return NULL;
 
        diff_filespec_load_driver(one);
-       if (!one->driver->textconv)
-               return NULL;
-
-       if (one->driver->textconv_want_cache && !one->driver->textconv_cache) {
-               struct notes_cache *c = xmalloc(sizeof(*c));
-               struct strbuf name = STRBUF_INIT;
-
-               strbuf_addf(&name, "textconv/%s", one->driver->name);
-               notes_cache_init(c, name.buf, one->driver->textconv);
-               one->driver->textconv_cache = c;
-       }
-
-       return one->driver;
+       return userdiff_get_textconv(one->driver);
 }
 
 static void builtin_diff(const char *name_a,
@@ -3220,6 +3230,7 @@ static int stat_opt(struct diff_options *options, const char **av)
        char *end;
        int width = options->stat_width;
        int name_width = options->stat_name_width;
+       int count = options->stat_count;
        int argcount = 1;
 
        arg += strlen("--stat");
@@ -3247,12 +3258,24 @@ static int stat_opt(struct diff_options *options, const char **av)
                                name_width = strtoul(av[1], &end, 10);
                                argcount = 2;
                        }
+               } else if (!prefixcmp(arg, "-count")) {
+                       arg += strlen("-count");
+                       if (*arg == '=')
+                               count = strtoul(arg + 1, &end, 10);
+                       else if (!*arg && !av[1])
+                               die("Option '--stat-count' requires a value");
+                       else if (!*arg) {
+                               count = strtoul(av[1], &end, 10);
+                               argcount = 2;
+                       }
                }
                break;
        case '=':
                width = strtoul(arg+1, &end, 10);
                if (*end == ',')
                        name_width = strtoul(end+1, &end, 10);
+               if (*end == ',')
+                       count = strtoul(end+1, &end, 10);
        }
 
        /* Important! This checks all the error cases! */
@@ -3261,6 +3284,7 @@ static int stat_opt(struct diff_options *options, const char **av)
        options->output_format |= DIFF_FORMAT_DIFFSTAT;
        options->stat_name_width = name_width;
        options->stat_width = width;
+       options->stat_count = count;
        return argcount;
 }
 
@@ -3325,7 +3349,7 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
        else if (!strcmp(arg, "-s"))
                options->output_format |= DIFF_FORMAT_NO_OUTPUT;
        else if (!prefixcmp(arg, "--stat"))
-               /* --stat, --stat-width, or --stat-name-width */
+               /* --stat, --stat-width, --stat-name-width, or --stat-count */
                return stat_opt(options, av);
 
        /* renames options */
diff --git a/diff.h b/diff.h
index 6d303c1d50aa799ec90aeb64b2a1b1f55811dd46..b920a20f80936ab66f5fe88c950dd35e38e1e856 100644 (file)
--- a/diff.h
+++ b/diff.h
@@ -125,6 +125,7 @@ struct diff_options {
 
        int stat_width;
        int stat_name_width;
+       int stat_count;
        const char *word_regex;
        enum diff_words_type word_diff;
 
diff --git a/entry.c b/entry.c
index b017167f2015623fb9c721e91d0a940abd1d5196..852fea13955475c1e2fda9cfc25a63a54a1f61c7 100644 (file)
--- a/entry.c
+++ b/entry.c
@@ -1,6 +1,7 @@
 #include "cache.h"
 #include "blob.h"
 #include "dir.h"
+#include "streaming.h"
 
 static void create_directories(const char *path, int path_len,
                               const struct checkout *state)
@@ -91,6 +92,91 @@ static void *read_blob_entry(struct cache_entry *ce, unsigned long *size)
        return NULL;
 }
 
+static int open_output_fd(char *path, struct cache_entry *ce, int to_tempfile)
+{
+       int symlink = (ce->ce_mode & S_IFMT) != S_IFREG;
+       if (to_tempfile) {
+               strcpy(path, symlink
+                      ? ".merge_link_XXXXXX" : ".merge_file_XXXXXX");
+               return mkstemp(path);
+       } else {
+               return create_file(path, !symlink ? ce->ce_mode : 0666);
+       }
+}
+
+static int fstat_output(int fd, const struct checkout *state, struct stat *st)
+{
+       /* use fstat() only when path == ce->name */
+       if (fstat_is_reliable() &&
+           state->refresh_cache && !state->base_dir_len) {
+               fstat(fd, st);
+               return 1;
+       }
+       return 0;
+}
+
+static int streaming_write_entry(struct cache_entry *ce, char *path,
+                                struct stream_filter *filter,
+                                const struct checkout *state, int to_tempfile,
+                                int *fstat_done, struct stat *statbuf)
+{
+       struct git_istream *st;
+       enum object_type type;
+       unsigned long sz;
+       int result = -1;
+       ssize_t kept = 0;
+       int fd = -1;
+
+       st = open_istream(ce->sha1, &type, &sz, filter);
+       if (!st)
+               return -1;
+       if (type != OBJ_BLOB)
+               goto close_and_exit;
+
+       fd = open_output_fd(path, ce, to_tempfile);
+       if (fd < 0)
+               goto close_and_exit;
+
+       for (;;) {
+               char buf[1024 * 16];
+               ssize_t wrote, holeto;
+               ssize_t readlen = read_istream(st, buf, sizeof(buf));
+
+               if (!readlen)
+                       break;
+               if (sizeof(buf) == readlen) {
+                       for (holeto = 0; holeto < readlen; holeto++)
+                               if (buf[holeto])
+                                       break;
+                       if (readlen == holeto) {
+                               kept += holeto;
+                               continue;
+                       }
+               }
+
+               if (kept && lseek(fd, kept, SEEK_CUR) == (off_t) -1)
+                       goto close_and_exit;
+               else
+                       kept = 0;
+               wrote = write_in_full(fd, buf, readlen);
+
+               if (wrote != readlen)
+                       goto close_and_exit;
+       }
+       if (kept && (lseek(fd, kept - 1, SEEK_CUR) == (off_t) -1 ||
+                    write(fd, "", 1) != 1))
+               goto close_and_exit;
+       *fstat_done = fstat_output(fd, state, statbuf);
+
+close_and_exit:
+       close_istream(st);
+       if (0 <= fd)
+               result = close(fd);
+       if (result && 0 <= fd)
+               unlink(path);
+       return result;
+}
+
 static int write_entry(struct cache_entry *ce, char *path, const struct checkout *state, int to_tempfile)
 {
        unsigned int ce_mode_s_ifmt = ce->ce_mode & S_IFMT;
@@ -101,6 +187,15 @@ static int write_entry(struct cache_entry *ce, char *path, const struct checkout
        size_t wrote, newsize = 0;
        struct stat st;
 
+       if (ce_mode_s_ifmt == S_IFREG) {
+               struct stream_filter *filter = get_stream_filter(path, ce->sha1);
+               if (filter &&
+                   !streaming_write_entry(ce, path, filter,
+                                          state, to_tempfile,
+                                          &fstat_done, &st))
+                       goto finish;
+       }
+
        switch (ce_mode_s_ifmt) {
        case S_IFREG:
        case S_IFLNK:
@@ -128,17 +223,7 @@ static int write_entry(struct cache_entry *ce, char *path, const struct checkout
                        size = newsize;
                }
 
-               if (to_tempfile) {
-                       if (ce_mode_s_ifmt == S_IFREG)
-                               strcpy(path, ".merge_file_XXXXXX");
-                       else
-                               strcpy(path, ".merge_link_XXXXXX");
-                       fd = mkstemp(path);
-               } else if (ce_mode_s_ifmt == S_IFREG) {
-                       fd = create_file(path, ce->ce_mode);
-               } else {
-                       fd = create_file(path, 0666);
-               }
+               fd = open_output_fd(path, ce, to_tempfile);
                if (fd < 0) {
                        free(new);
                        return error("unable to create file %s (%s)",
@@ -146,12 +231,8 @@ static int write_entry(struct cache_entry *ce, char *path, const struct checkout
                }
 
                wrote = write_in_full(fd, new, size);
-               /* use fstat() only when path == ce->name */
-               if (fstat_is_reliable() &&
-                   state->refresh_cache && !to_tempfile && !state->base_dir_len) {
-                       fstat(fd, &st);
-                       fstat_done = 1;
-               }
+               if (!to_tempfile)
+                       fstat_done = fstat_output(fd, state, &st);
                close(fd);
                free(new);
                if (wrote != size)
@@ -167,6 +248,7 @@ static int write_entry(struct cache_entry *ce, char *path, const struct checkout
                return error("unknown file mode for %s in index", path);
        }
 
+finish:
        if (state->refresh_cache) {
                if (!fstat_done)
                        lstat(ce->name, &st);
index 6cdd5910db50c96df3d149fba172750cb10c09cb..463c741dfc259927b4429f19972ced21d87c215e 100755 (executable)
--- a/git-am.sh
+++ b/git-am.sh
@@ -37,13 +37,14 @@ rerere-autoupdate update the index with reused conflict resolution if possible
 rebasing*       (internal use for git-rebase)"
 
 . git-sh-setup
+. git-sh-i18n
 prefix=$(git rev-parse --show-prefix)
 set_reflog_action am
 require_work_tree
 cd_to_toplevel
 
 git var GIT_COMMITTER_IDENT >/dev/null ||
-       die "You need to set your committer info first"
+       die "$(gettext "You need to set your committer info first")"
 
 if git rev-parse --verify -q HEAD >/dev/null
 then
@@ -88,8 +89,11 @@ safe_to_abort () {
        then
                return 0
        fi
-       echo >&2 "You seem to have moved HEAD since the last 'am' failure."
-       echo >&2 "Not rewinding to ORIG_HEAD"
+       (
+               gettext "You seem to have moved HEAD since the last 'am' failure.
+Not rewinding to ORIG_HEAD" &&
+               echo
+       ) >&2
        return 1
 }
 
@@ -98,9 +102,9 @@ stop_here_user_resolve () {
            printf '%s\n' "$resolvemsg"
            stop_here $1
     fi
-    echo "When you have resolved this problem run \"$cmdline --resolved\"."
-    echo "If you would prefer to skip this patch, instead run \"$cmdline --skip\"."
-    echo "To restore the original branch and stop patching run \"$cmdline --abort\"."
+    eval_gettext "When you have resolved this problem run \"\$cmdline --resolved\".
+If you would prefer to skip this patch, instead run \"\$cmdline --skip\".
+To restore the original branch and stop patching run \"\$cmdline --abort\"."; echo
 
     stop_here $1
 }
@@ -114,7 +118,7 @@ go_next () {
 
 cannot_fallback () {
        echo "$1"
-       echo "Cannot fall back to three-way merge."
+       gettext "Cannot fall back to three-way merge."; echo
        exit 1
 }
 
@@ -129,7 +133,7 @@ fall_back_3way () {
        "$dotest/patch" &&
     GIT_INDEX_FILE="$dotest/patch-merge-tmp-index" \
     git write-tree >"$dotest/patch-merge-base+" ||
-    cannot_fallback "Repository lacks necessary blobs to fall back on 3-way merge."
+    cannot_fallback "$(gettext "Repository lacks necessary blobs to fall back on 3-way merge.")"
 
     say Using index info to reconstruct a base tree...
     if GIT_INDEX_FILE="$dotest/patch-merge-tmp-index" \
@@ -138,8 +142,8 @@ fall_back_3way () {
        mv "$dotest/patch-merge-base+" "$dotest/patch-merge-base"
        mv "$dotest/patch-merge-tmp-index" "$dotest/patch-merge-index"
     else
-        cannot_fallback "Did you hand edit your patch?
-It does not apply to blobs recorded in its index."
+       cannot_fallback "$(gettext "Did you hand edit your patch?
+It does not apply to blobs recorded in its index.")"
     fi
 
     test -f "$dotest/patch-merge-index" &&
@@ -147,7 +151,7 @@ It does not apply to blobs recorded in its index."
     orig_tree=$(cat "$dotest/patch-merge-base") &&
     rm -fr "$dotest"/patch-merge-* || exit 1
 
-    say Falling back to patching base and 3-way merge...
+    say "$(gettext "Falling back to patching base and 3-way merge...")"
 
     # This is not so wrong.  Depending on which base we picked,
     # orig_tree may be wildly different from ours, but his_tree
@@ -254,7 +258,7 @@ split_patches () {
        stgit-series)
                if test $# -ne 1
                then
-                       clean_abort "Only one StGIT patch series can be applied at once"
+                       clean_abort "$(gettext "Only one StGIT patch series can be applied at once")"
                fi
                series_dir=`dirname "$1"`
                series_file="$1"
@@ -306,9 +310,9 @@ split_patches () {
                ;;
        *)
                if test -n "$parse_patch" ; then
-                       clean_abort "Patch format $patch_format is not supported."
+                       clean_abort "$(eval_gettext "Patch format \$patch_format is not supported.")"
                else
-                       clean_abort "Patch format detection failed."
+                       clean_abort "$(gettext "Patch format detection failed.")"
                fi
                ;;
        esac
@@ -358,7 +362,7 @@ do
        --rebasing)
                rebasing=t threeway=t keep=t scissors=f no_inbody_headers=t ;;
        -d|--dotest)
-               die "-d option is no longer supported.  Do not use."
+               die "$(gettext "-d option is no longer supported.  Do not use.")"
                ;;
        --resolvemsg)
                shift; resolvemsg=$1 ;;
@@ -421,12 +425,12 @@ then
                false
                ;;
        esac ||
-       die "previous rebase directory $dotest still exists but mbox given."
+       die "$(eval_gettext "previous rebase directory \$dotest still exists but mbox given.")"
        resume=yes
 
        case "$skip,$abort" in
        t,t)
-               die "Please make up your mind. --skip or --abort?"
+               die "$(gettext "Please make up your mind. --skip or --abort?")"
                ;;
        t,)
                git rerere clear
@@ -453,7 +457,7 @@ then
 else
        # Make sure we are not given --skip, --resolved, nor --abort
        test "$skip$resolved$abort" = "" ||
-               die "Resolve operation not in progress, we are not resuming."
+               die "$(gettext "Resolve operation not in progress, we are not resuming.")"
 
        # Start afresh.
        mkdir -p "$dotest" || exit
@@ -518,7 +522,7 @@ case "$resolved" in
        if test "$files"
        then
                test -n "$HAS_HEAD" && : >"$dotest/dirtyindex"
-               die "Dirty index: cannot apply patches (dirty: $files)"
+               die "$(eval_gettext "Dirty index: cannot apply patches (dirty: \$files)")"
        fi
 esac
 
@@ -607,9 +611,9 @@ do
                        go_next && continue
 
                test -s "$dotest/patch" || {
-                       echo "Patch is empty.  Was it split wrong?"
-                       echo "If you would prefer to skip this patch, instead run \"$cmdline --skip\"."
-                       echo "To restore the original branch and stop patching run \"$cmdline --abort\"."
+                       eval_gettext "Patch is empty.  Was it split wrong?
+If you would prefer to skip this patch, instead run \"\$cmdline --skip\".
+To restore the original branch and stop patching run \"\$cmdline --abort\"."; echo
                        stop_here $this
                }
                rm -f "$dotest/original-commit" "$dotest/author-script"
@@ -644,7 +648,7 @@ do
 
        if test -z "$GIT_AUTHOR_EMAIL"
        then
-               echo "Patch does not have a valid e-mail address."
+               gettext "Patch does not have a valid e-mail address."; echo
                stop_here $this
        fi
 
@@ -691,15 +695,18 @@ do
        if test "$interactive" = t
        then
            test -t 0 ||
-           die "cannot be interactive without stdin connected to a terminal."
+           die "$(gettext "cannot be interactive without stdin connected to a terminal.")"
            action=again
            while test "$action" = again
            do
-               echo "Commit Body is:"
+               gettext "Commit Body is:"; echo
                echo "--------------------------"
                cat "$dotest/final-commit"
                echo "--------------------------"
-               printf "Apply? [y]es/[n]o/[e]dit/[v]iew patch/[a]ccept all "
+               # TRANSLATORS: Make sure to include [y], [n], [e], [v] and [a]
+               # in your translation. The program will only accept English
+               # input at this point.
+               gettext "Apply? [y]es/[n]o/[e]dit/[v]iew patch/[a]ccept all "
                read reply
                case "$reply" in
                [yY]*) action=yes ;;
@@ -735,7 +742,7 @@ do
                stop_here $this
        fi
 
-       say "Applying: $FIRSTLINE"
+       say "$(eval_gettext "Applying: \$FIRSTLINE")"
 
        case "$resolved" in
        '')
@@ -756,16 +763,16 @@ do
                # working tree.
                resolved=
                git diff-index --quiet --cached HEAD -- && {
-                       echo "No changes - did you forget to use 'git add'?"
-                       echo "If there is nothing left to stage, chances are that something else"
-                       echo "already introduced the same changes; you might want to skip this patch."
+                       gettext "No changes - did you forget to use 'git add'?
+If there is nothing left to stage, chances are that something else
+already introduced the same changes; you might want to skip this patch."; echo
                        stop_here_user_resolve $this
                }
                unmerged=$(git ls-files -u)
                if test -n "$unmerged"
                then
-                       echo "You still have unmerged paths in your index"
-                       echo "did you forget to use 'git add'?"
+                       gettext "You still have unmerged paths in your index
+did you forget to use 'git add'?"; echo
                        stop_here_user_resolve $this
                fi
                apply_status=0
@@ -780,7 +787,7 @@ do
                    # Applying the patch to an earlier tree and merging the
                    # result may have produced the same tree as ours.
                    git diff-index --quiet --cached HEAD -- && {
-                       say No changes -- Patch already applied.
+                       say "$(gettext "No changes -- Patch already applied.")"
                        go_next
                        continue
                    }
@@ -790,7 +797,7 @@ do
        fi
        if test $apply_status != 0
        then
-               printf 'Patch failed at %s %s\n' "$msgnum" "$FIRSTLINE"
+               eval_gettext 'Patch failed at $msgnum $FIRSTLINE'; echo
                stop_here_user_resolve $this
        fi
 
@@ -806,7 +813,7 @@ do
                        GIT_AUTHOR_DATE=
                fi
                parent=$(git rev-parse --verify -q HEAD) ||
-               say >&2 "applying to an empty history"
+               say >&2 "$(gettext "applying to an empty history")"
 
                if test -n "$committer_date_is_author_date"
                then
index 415a8d04ccc4f313eb111a3bbfbf2af2382beebd..b2186a86279e8919214bb205400a05f32a316c3b 100755 (executable)
@@ -28,6 +28,7 @@ Please use "git help bisect" to get the full man page.'
 
 OPTIONS_SPEC=
 . git-sh-setup
+. git-sh-i18n
 require_work_tree
 
 _x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
@@ -35,10 +36,16 @@ _x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40"
 
 bisect_autostart() {
        test -s "$GIT_DIR/BISECT_START" || {
-               echo >&2 'You need to start by "git bisect start"'
+               (
+                       gettext "You need to start by \"git bisect start\"" &&
+                       echo
+               ) >&2
                if test -t 0
                then
-                       echo >&2 -n 'Do you want me to do it for you [Y/n]? '
+                       # TRANSLATORS: Make sure to include [Y] and [n] in your
+                       # translation. The program will only accept English input
+                       # at this point.
+           gettext "Do you want me to do it for you [Y/n]? " >&2
                        read yesno
                        case "$yesno" in
                        [Nn]*)
@@ -57,7 +64,7 @@ bisect_start() {
        #
        head=$(GIT_DIR="$GIT_DIR" git symbolic-ref -q HEAD) ||
        head=$(GIT_DIR="$GIT_DIR" git rev-parse --verify HEAD) ||
-       die "Bad HEAD - I need a HEAD"
+       die "$(gettext "Bad HEAD - I need a HEAD")"
 
        #
        # Check if we are bisecting.
@@ -76,11 +83,11 @@ bisect_start() {
                        # cogito usage, and cogito users should understand
                        # it relates to cg-seek.
                        [ -s "$GIT_DIR/head-name" ] &&
-                               die "won't bisect on seeked tree"
+                               die "$(gettext "won't bisect on seeked tree")"
                        start_head="${head#refs/heads/}"
                        ;;
                *)
-                       die "Bad HEAD - strange symbolic ref"
+                       die "$(gettext "Bad HEAD - strange symbolic ref")"
                        ;;
                esac
        fi
@@ -110,7 +117,7 @@ bisect_start() {
            *)
                rev=$(git rev-parse -q --verify "$arg^{commit}") || {
                    test $has_double_dash -eq 1 &&
-                       die "'$arg' does not appear to be a valid revision"
+                       die "$(eval_gettext "'\$arg' does not appear to be a valid revision")"
                    break
                }
                case $bad_seen in
@@ -155,7 +162,7 @@ bisect_write() {
        case "$state" in
                bad)            tag="$state" ;;
                good|skip)      tag="$state"-"$rev" ;;
-               *)              die "Bad bisect_write argument: $state" ;;
+               *)              die "$(eval_gettext "Bad bisect_write argument: \$state")" ;;
        esac
        git update-ref "refs/bisect/$tag" "$rev" || exit
        echo "# $state: $(git show-branch $rev)" >>"$GIT_DIR/BISECT_LOG"
@@ -183,7 +190,7 @@ bisect_skip() {
        do
            case "$arg" in
             *..*)
-                revs=$(git rev-list "$arg") || die "Bad rev input: $arg" ;;
+               revs=$(git rev-list "$arg") || die "$(eval_gettext "Bad rev input: \$arg")" ;;
             *)
                 revs=$(git rev-parse --sq-quote "$arg") ;;
            esac
@@ -197,10 +204,10 @@ bisect_state() {
        state=$1
        case "$#,$state" in
        0,*)
-               die "Please call 'bisect_state' with at least one argument." ;;
+               die "$(gettext "Please call 'bisect_state' with at least one argument.")" ;;
        1,bad|1,good|1,skip)
                rev=$(git rev-parse --verify HEAD) ||
-                       die "Bad rev input: HEAD"
+                       die "$(gettext "Bad rev input: HEAD")"
                bisect_write "$state" "$rev"
                check_expected_revs "$rev" ;;
        2,bad|*,good|*,skip)
@@ -209,13 +216,13 @@ bisect_state() {
                for rev in "$@"
                do
                        sha=$(git rev-parse --verify "$rev^{commit}") ||
-                               die "Bad rev input: $rev"
+                               die "$(eval_gettext "Bad rev input: \$rev")"
                        eval="$eval bisect_write '$state' '$sha'; "
                done
                eval "$eval"
                check_expected_revs "$@" ;;
        *,bad)
-               die "'git bisect bad' can take only one argument." ;;
+               die "$(gettext "'git bisect bad' can take only one argument.")" ;;
        *)
                usage ;;
        esac
@@ -238,25 +245,38 @@ bisect_next_check() {
        t,,good)
                # have bad but not good.  we could bisect although
                # this is less optimum.
-               echo >&2 'Warning: bisecting only with a bad commit.'
+               (
+                       gettext "Warning: bisecting only with a bad commit." &&
+                       echo
+               ) >&2
                if test -t 0
                then
-                       printf >&2 'Are you sure [Y/n]? '
+                       # TRANSLATORS: Make sure to include [Y] and [n] in your
+                       # translation. The program will only accept English input
+                       # at this point.
+                       gettext "Are you sure [Y/n]? " >&2
                        read yesno
                        case "$yesno" in [Nn]*) exit 1 ;; esac
                fi
                : bisect without good...
                ;;
        *)
-               THEN=''
-               test -s "$GIT_DIR/BISECT_START" || {
-                       echo >&2 'You need to start by "git bisect start".'
-                       THEN='then '
-               }
-               echo >&2 'You '$THEN'need to give me at least one good' \
-                       'and one bad revisions.'
-               echo >&2 '(You can use "git bisect bad" and' \
-                       '"git bisect good" for that.)'
+
+               if test -s "$GIT_DIR/BISECT_START"
+               then
+                       (
+                               gettext "You need to give me at least one good and one bad revisions.
+(You can use \"git bisect bad\" and \"git bisect good\" for that.)" &&
+                               echo
+                       ) >&2
+               else
+                       (
+                               gettext "You need to start by \"git bisect start\".
+You then need to give me at least one good and one bad revisions.
+(You can use \"git bisect bad\" and \"git bisect good\" for that.)" &&
+                               echo
+                       ) >&2
+               fi
                exit 1 ;;
        esac
 }
@@ -307,13 +327,15 @@ bisect_visualize() {
 
 bisect_reset() {
        test -s "$GIT_DIR/BISECT_START" || {
-               echo "We are not bisecting."
+               gettext "We are not bisecting."; echo
                return
        }
        case "$#" in
        0) branch=$(cat "$GIT_DIR/BISECT_START") ;;
-       1) git rev-parse --quiet --verify "$1^{commit}" > /dev/null ||
-              die "'$1' is not a valid commit"
+       1) git rev-parse --quiet --verify "$1^{commit}" > /dev/null || {
+              invalid="$1"
+              die "$(eval_gettext "'\$invalid' is not a valid commit")"
+          }
           branch="$1" ;;
        *)
            usage ;;
@@ -321,8 +343,8 @@ bisect_reset() {
        if git checkout "$branch" -- ; then
                bisect_clean_state
        else
-               die "Could not check out original HEAD '$branch'." \
-                               "Try 'git bisect reset <commit>'."
+               die "$(eval_gettext "Could not check out original HEAD '\$branch'.
+Try 'git bisect reset <commit>'.")"
        fi
 }
 
@@ -345,8 +367,9 @@ bisect_clean_state() {
 }
 
 bisect_replay () {
-       test "$#" -eq 1 || die "No logfile given"
-       test -r "$1" || die "cannot read $1 for replaying"
+       file="$1"
+       test "$#" -eq 1 || die "$(gettext "No logfile given")"
+       test -r "$file" || die "$(eval_gettext "cannot read \$file for replaying")"
        bisect_reset
        while read git bisect command rev
        do
@@ -362,9 +385,9 @@ bisect_replay () {
                good|bad|skip)
                        bisect_write "$command" "$rev" ;;
                *)
-                       die "?? what are you talking about?" ;;
+                       die "$(gettext "?? what are you talking about?")" ;;
                esac
-       done <"$1"
+       done <"$file"
        bisect_auto_next
 }
 
@@ -373,14 +396,18 @@ bisect_run () {
 
     while true
     do
-      echo "running $@"
+      command="$@"
+      eval_gettext "running \$command"; echo
       "$@"
       res=$?
 
       # Check for really bad run error.
       if [ $res -lt 0 -o $res -ge 128 ]; then
-         echo >&2 "bisect run failed:"
-         echo >&2 "exit code $res from '$@' is < 0 or >= 128"
+         (
+           eval_gettext "bisect run failed:
+exit code \$res from '\$command' is < 0 or >= 128" &&
+           echo
+         ) >&2
          exit $res
       fi
 
@@ -402,18 +429,24 @@ bisect_run () {
 
       if sane_grep "first bad commit could be any of" "$GIT_DIR/BISECT_RUN" \
                > /dev/null; then
-         echo >&2 "bisect run cannot continue any more"
+         (
+             gettext "bisect run cannot continue any more" &&
+             echo
+         ) >&2
          exit $res
       fi
 
       if [ $res -ne 0 ]; then
-         echo >&2 "bisect run failed:"
-         echo >&2 "'bisect_state $state' exited with error code $res"
+         (
+             eval_gettext "bisect run failed:
+'bisect_state \$state' exited with error code \$res" &&
+             echo
+         ) >&2
          exit $res
       fi
 
       if sane_grep "is the first bad commit" "$GIT_DIR/BISECT_RUN" > /dev/null; then
-         echo "bisect run success"
+         gettext "bisect run success"; echo
          exit 0;
       fi
 
@@ -421,7 +454,7 @@ bisect_run () {
 }
 
 bisect_log () {
-       test -s "$GIT_DIR/BISECT_LOG" || die "We are not bisecting."
+       test -s "$GIT_DIR/BISECT_LOG" || die "$(gettext "We are not bisecting.")"
        cat "$GIT_DIR/BISECT_LOG"
 }
 
index e0bb81ed8d0bd89f18b31b1c03d3e23744aea5a1..a75530df7b7865059a51f84eb6257962c4f863cd 100644 (file)
@@ -215,6 +215,10 @@ extern char *gitbasename(char *);
 #define is_dir_sep(c) ((c) == '/')
 #endif
 
+#ifndef find_last_dir_sep
+#define find_last_dir_sep(path) strrchr(path, '/')
+#endif
+
 #if __HP_cc >= 61000
 #define NORETURN __attribute__((noreturn))
 #define NORETURN_PTR
index 8bfa8a055ccd0c344d595f112b571bb3d6e21c28..01a1b05e6bdcd12f82f70282975780d3a19d910d 100755 (executable)
@@ -27,6 +27,7 @@ httpd="$(git config --get instaweb.httpd)"
 root="$(git config --get instaweb.gitwebdir)"
 port=$(git config --get instaweb.port)
 module_path="$(git config --get instaweb.modulepath)"
+action="browse"
 
 conf="$GIT_DIR/gitweb/httpd.conf"
 
@@ -98,12 +99,18 @@ start_httpd () {
 
        # here $httpd should have a meaningful value
        resolve_full_httpd
+       mkdir -p "$fqgitdir/gitweb/$httpd_only"
+       conf="$fqgitdir/gitweb/$httpd_only.conf"
+
+       # generate correct config file if it doesn't exist
+       test -f "$conf" || configure_httpd
+       test -f "$fqgitdir/gitweb/gitweb_config.perl" || gitweb_conf
 
        # don't quote $full_httpd, there can be arguments to it (-f)
        case "$httpd" in
        *mongoose*|*plackup*)
                #These servers don't have a daemon mode so we'll have to fork it
-               $full_httpd "$fqgitdir/gitweb/httpd.conf" &
+               $full_httpd "$conf" &
                #Save the pid before doing anything else (we'll print it later)
                pid=$!
 
@@ -117,7 +124,7 @@ $pid
 EOF
                ;;
        *)
-               $full_httpd "$fqgitdir/gitweb/httpd.conf"
+               $full_httpd "$conf"
                if test $? != 0; then
                        echo "Could not execute http daemon $httpd."
                        exit 1
@@ -148,17 +155,13 @@ while test $# != 0
 do
        case "$1" in
        --stop|stop)
-               stop_httpd
-               exit 0
+               action="stop"
                ;;
        --start|start)
-               start_httpd
-               exit 0
+               action="start"
                ;;
        --restart|restart)
-               stop_httpd
-               start_httpd
-               exit 0
+               action="restart"
                ;;
        -l|--local)
                local=true
@@ -587,33 +590,54 @@ our \$projects_list = \$projectroot;
 EOF
 }
 
-gitweb_conf
-
-resolve_full_httpd
-mkdir -p "$fqgitdir/gitweb/$httpd_only"
+configure_httpd() {
+       case "$httpd" in
+       *lighttpd*)
+               lighttpd_conf
+               ;;
+       *apache2*|*httpd*)
+               apache2_conf
+               ;;
+       webrick)
+               webrick_conf
+               ;;
+       *mongoose*)
+               mongoose_conf
+               ;;
+       *plackup*)
+               plackup_conf
+               ;;
+       *)
+               echo "Unknown httpd specified: $httpd"
+               exit 1
+               ;;
+       esac
+}
 
-case "$httpd" in
-*lighttpd*)
-       lighttpd_conf
-       ;;
-*apache2*|*httpd*)
-       apache2_conf
-       ;;
-webrick)
-       webrick_conf
+case "$action" in
+stop)
+       stop_httpd
+       exit 0
        ;;
-*mongoose*)
-       mongoose_conf
+start)
+       start_httpd
+       exit 0
        ;;
-*plackup*)
-       plackup_conf
-       ;;
-*)
-       echo "Unknown httpd specified: $httpd"
-       exit 1
+restart)
+       stop_httpd
+       start_httpd
+       exit 0
        ;;
 esac
 
+gitweb_conf
+
+resolve_full_httpd
+mkdir -p "$fqgitdir/gitweb/$httpd_only"
+conf="$fqgitdir/gitweb/$httpd_only.conf"
+
+configure_httpd
+
 start_httpd
 url=http://127.0.0.1:$port
 
index 4db9212331259664732f031438b7b87b7a10244f..91f90acfba2a710644f4a076d27d50e0fbb33c2d 100644 (file)
@@ -86,6 +86,11 @@ get_merge_tool_cmd () {
 }
 
 run_merge_tool () {
+       # If GIT_PREFIX is empty then we cannot use it in tools
+       # that expect to be able to chdir() to its value.
+       GIT_PREFIX=${GIT_PREFIX:-.}
+       export GIT_PREFIX
+
        merge_tool_path="$(get_merge_tool_path "$1")" || exit
        base_present="$2"
        status=0
@@ -188,6 +193,7 @@ run_merge_tool () {
                        check_unchanged
                else
                        "$merge_tool_path" -R -f -d -c "wincmd l" \
+                               -c 'cd $GIT_PREFIX' \
                                "$LOCAL" "$REMOTE"
                fi
                ;;
@@ -199,6 +205,7 @@ run_merge_tool () {
                        check_unchanged
                else
                        "$merge_tool_path" -R -f -d -c "wincmd l" \
+                               -c 'cd $GIT_PREFIX' \
                                "$LOCAL" "$REMOTE"
                fi
                ;;
index fb9e2df9312e96ecf8ad5ab2bde4b74b979fe02e..a10b1290bc9218942fcec54b9e930f072ca11bce 100755 (executable)
@@ -9,6 +9,7 @@ LONG_USAGE='Fetch one or more remote refs and merge it/them into the current HEA
 SUBDIRECTORY_OK=Yes
 OPTIONS_SPEC=
 . git-sh-setup
+. git-sh-i18n
 set_reflog_action "pull $*"
 require_work_tree
 cd_to_toplevel
@@ -17,20 +18,20 @@ cd_to_toplevel
 die_conflict () {
     git diff-index --cached --name-status -r --ignore-submodules HEAD --
     if [ $(git config --bool --get advice.resolveConflict || echo true) = "true" ]; then
-       die "Pull is not possible because you have unmerged files.
+       die "$(gettext "Pull is not possible because you have unmerged files.
 Please, fix them up in the work tree, and then use 'git add/rm <file>'
-as appropriate to mark resolution, or use 'git commit -a'."
+as appropriate to mark resolution, or use 'git commit -a'.")"
     else
-       die "Pull is not possible because you have unmerged files."
+       die "$(gettext "Pull is not possible because you have unmerged files.")"
     fi
 }
 
 die_merge () {
     if [ $(git config --bool --get advice.resolveConflict || echo true) = "true" ]; then
-       die "You have not concluded your merge (MERGE_HEAD exists).
-Please, commit your changes before you can merge."
+       die "$(gettext "You have not concluded your merge (MERGE_HEAD exists).
+Please, commit your changes before you can merge.")"
     else
-       die "You have not concluded your merge (MERGE_HEAD exists)."
+       die "$(gettext "You have not concluded your merge (MERGE_HEAD exists).")"
     fi
 }
 
@@ -185,7 +186,7 @@ test true = "$rebase" && {
                # On an unborn branch
                if test -f "$GIT_DIR/index"
                then
-                       die "updating an unborn branch with changes added to the index"
+                       die "$(gettext "updating an unborn branch with changes added to the index")"
                fi
        else
                require_clean_work_tree "pull with rebase" "Please commit or stash them."
@@ -216,17 +217,20 @@ then
        # $orig_head commit, but we are merging into $curr_head.
        # First update the working tree to match $curr_head.
 
-       echo >&2 "Warning: fetch updated the current branch head."
-       echo >&2 "Warning: fast-forwarding your working tree from"
-       echo >&2 "Warning: commit $orig_head."
+       (
+               eval_gettext "Warning: fetch updated the current branch head.
+Warning: fast-forwarding your working tree from
+Warning: commit \$orig_head." &&
+               echo
+       ) >&2
        git update-index -q --refresh
        git read-tree -u -m "$orig_head" "$curr_head" ||
-               die 'Cannot fast-forward your working tree.
+               die "$(eval_gettext "Cannot fast-forward your working tree.
 After making sure that you saved anything precious from
-$ git diff '$orig_head'
+$ git diff \$orig_head
 output, run
 $ git reset --hard
-to recover.'
+to recover.")"
 
 fi
 
@@ -241,11 +245,11 @@ case "$merge_head" in
 ?*' '?*)
        if test -z "$orig_head"
        then
-               die "Cannot merge multiple branches into empty head"
+               die "$(gettext "Cannot merge multiple branches into empty head")"
        fi
        if test true = "$rebase"
        then
-               die "Cannot rebase onto multiple branches"
+               die "$(gettext "Cannot rebase onto multiple branches")"
        fi
        ;;
 esac
index 94e26ed5e8dcf84c4f238c76b6c508dc84d0b7ea..8e427dab31c27bd78d9eac1ad15bc1f5dcc044c4 100644 (file)
@@ -39,9 +39,15 @@ git_broken_path_fix () {
 
 # @@BROKEN_PATH_FIX@@
 
-die() {
-       echo >&2 "$@"
-       exit 1
+die () {
+       die_with_status 1 "$@"
+}
+
+die_with_status () {
+       status=$1
+       shift
+       echo >&2 "$*"
+       exit "$status"
 }
 
 GIT_QUIET=
index 0a9403653d7dbbb6927973dcfcc41bfd9a904e05..5619da5c10f968a7eaeeaafc497ed03e763c9439 100755 (executable)
@@ -14,6 +14,7 @@ SUBDIRECTORY_OK=Yes
 OPTIONS_SPEC=
 START_DIR=`pwd`
 . git-sh-setup
+. git-sh-i18n
 require_work_tree
 cd_to_toplevel
 
@@ -39,7 +40,7 @@ no_changes () {
 clear_stash () {
        if test $# != 0
        then
-               die "git stash clear with parameters is unimplemented"
+               die "$(gettext "git stash clear with parameters is unimplemented")"
        fi
        if current=$(git rev-parse --verify $ref_stash 2>/dev/null)
        then
@@ -61,7 +62,7 @@ create_stash () {
        then
                head=$(git rev-list --oneline -n 1 HEAD --)
        else
-               die "You do not have the initial commit yet"
+               die "$(gettext "You do not have the initial commit yet")"
        fi
 
        if branch=$(git symbolic-ref -q HEAD)
@@ -76,7 +77,7 @@ create_stash () {
        i_tree=$(git write-tree) &&
        i_commit=$(printf 'index on %s\n' "$msg" |
                git commit-tree $i_tree -p $b_commit) ||
-               die "Cannot save the current index state"
+               die "$(gettext "Cannot save the current index state")"
 
        if test -z "$patch_mode"
        then
@@ -90,7 +91,7 @@ create_stash () {
                        git write-tree &&
                        rm -f "$TMPindex"
                ) ) ||
-                       die "Cannot save the current worktree state"
+                       die "$(gettext "Cannot save the current worktree state")"
 
        else
 
@@ -103,14 +104,14 @@ create_stash () {
 
                # state of the working tree
                w_tree=$(GIT_INDEX_FILE="$TMP-index" git write-tree) ||
-               die "Cannot save the current worktree state"
+               die "$(gettext "Cannot save the current worktree state")"
 
                git diff-tree -p HEAD $w_tree > "$TMP-patch" &&
                test -s "$TMP-patch" ||
-               die "No changes selected"
+               die "$(gettext "No changes selected")"
 
                rm -f "$TMP-index" ||
-               die "Cannot remove temporary index (can't happen)"
+               die "$(gettext "Cannot remove temporary index (can't happen)")"
 
        fi
 
@@ -123,7 +124,7 @@ create_stash () {
        fi
        w_commit=$(printf '%s\n' "$stash_msg" |
                git commit-tree $w_tree -p $b_commit -p $i_commit) ||
-               die "Cannot record working tree state"
+               die "$(gettext "Cannot record working tree state")"
 }
 
 save_stash () {
@@ -151,8 +152,19 @@ save_stash () {
                        break
                        ;;
                -*)
-                       echo "error: unknown option for 'stash save': $1"
-                       echo "       To provide a message, use git stash save -- '$1'"
+                       option="$1"
+                       # TRANSLATORS: $option is an invalid option, like
+                       # `--blah-blah'. The 7 spaces at the beginning of the
+                       # second line correspond to "error: ". So you should line
+                       # up the second line with however many characters the
+                       # translation of "error: " takes in your language. E.g. in
+                       # English this is:
+                       #
+                       #    $ git stash save --blah-blah 2>&1 | head -n 2
+                       #    error: unknown option for 'stash save': --blah-blah
+                       #           To provide a message, use git stash save -- '--blah-blah'
+                       eval_gettext "$("error: unknown option for 'stash save': \$option
+       To provide a message, use git stash save -- '\$option'")"; echo
                        usage
                        ;;
                *)
@@ -167,11 +179,11 @@ save_stash () {
        git update-index -q --refresh
        if no_changes
        then
-               say 'No local changes to save'
+               say "$(gettext "No local changes to save")"
                exit 0
        fi
        test -f "$GIT_DIR/logs/$ref_stash" ||
-               clear_stash || die "Cannot initialize stash"
+               clear_stash || die "$(gettext "Cannot initialize stash")"
 
        create_stash "$stash_msg"
 
@@ -179,7 +191,7 @@ save_stash () {
        : >>"$GIT_DIR/logs/$ref_stash"
 
        git update-ref -m "$stash_msg" $ref_stash $w_commit ||
-               die "Cannot save the current status"
+               die "$(gettext "Cannot save the current status")"
        say Saved working directory and index state "$stash_msg"
 
        if test -z "$patch_mode"
@@ -192,7 +204,7 @@ save_stash () {
                fi
        else
                git apply -R < "$TMP-patch" ||
-               die "Cannot remove worktree changes"
+               die "$(gettext "Cannot remove worktree changes")"
 
                if test "$keep_index" != "t"
                then
@@ -287,18 +299,21 @@ parse_flags_and_rev()
 
        case $# in
                0)
-                       have_stash || die "No stash found."
+                       have_stash || die "$(gettext "No stash found.")"
                        set -- ${ref_stash}@{0}
                ;;
                1)
                        :
                ;;
                *)
-                       die "Too many revisions specified: $REV"
+                       die "$(eval_gettext "Too many revisions specified: \$REV")"
                ;;
        esac
 
-       REV=$(git rev-parse --quiet --symbolic --verify $1 2>/dev/null) || die "$1 is not valid reference"
+       REV=$(git rev-parse --quiet --symbolic --verify $1 2>/dev/null) || {
+               reference="$1"
+               die "$(eval_gettext "\$reference is not valid reference")"
+       }
 
        i_commit=$(git rev-parse --quiet --verify $REV^2 2>/dev/null) &&
        set -- $(git rev-parse $REV $REV^1 $REV: $REV^1: $REV^2: 2>/dev/null) &&
@@ -320,7 +335,10 @@ is_stash_like()
 }
 
 assert_stash_like() {
-       is_stash_like "$@" || die "'$*' is not a stash-like commit"
+       is_stash_like "$@" || {
+               args="$*"
+               die "$(eval_gettext "'\$args' is not a stash-like commit")"
+       }
 }
 
 is_stash_ref() {
@@ -328,18 +346,21 @@ is_stash_ref() {
 }
 
 assert_stash_ref() {
-       is_stash_ref "$@" || die "'$*' is not a stash reference"
+       is_stash_ref "$@" || {
+               args="$*"
+               die "$(eval_gettext "'\$args' is not a stash reference")"
+       }
 }
 
 apply_stash () {
 
        assert_stash_like "$@"
 
-       git update-index -q --refresh || die 'unable to refresh index'
+       git update-index -q --refresh || die "$(gettext "unable to refresh index")"
 
        # current index state
        c_tree=$(git write-tree) ||
-               die 'Cannot apply a stash in the middle of a merge'
+               die "$(gettext "Cannot apply a stash in the middle of a merge")"
 
        unstashed_index_tree=
        if test -n "$INDEX_OPTION" && test "$b_tree" != "$i_tree" &&
@@ -347,9 +368,9 @@ apply_stash () {
        then
                git diff-tree --binary $s^2^..$s^2 | git apply --cached
                test $? -ne 0 &&
-                       die 'Conflicts in index. Try without --index.'
+                       die "$(gettext "Conflicts in index. Try without --index.")"
                unstashed_index_tree=$(git write-tree) ||
-                       die 'Could not save index tree'
+                       die "$(gettext "Could not save index tree")"
                git reset
        fi
 
@@ -375,7 +396,7 @@ apply_stash () {
                        git diff-index --cached --name-only --diff-filter=A $c_tree >"$a" &&
                        git read-tree --reset $c_tree &&
                        git update-index --add --stdin <"$a" ||
-                               die "Cannot unstage modified files"
+                               die "$(gettext "Cannot unstage modified files")"
                        rm -f "$a"
                fi
                squelch=
@@ -389,7 +410,10 @@ apply_stash () {
                status=$?
                if test -n "$INDEX_OPTION"
                then
-                       echo >&2 'Index was not unstashed.'
+                       (
+                               gettext "Index was not unstashed." &&
+                               echo
+                       ) >&2
                fi
                exit $status
        fi
@@ -406,14 +430,15 @@ drop_stash () {
        assert_stash_ref "$@"
 
        git reflog delete --updateref --rewrite "${REV}" &&
-               say "Dropped ${REV} ($s)" || die "${REV}: Could not drop stash entry"
+               say "$(eval_gettext "Dropped \${REV} (\$s)")" ||
+               die "$(eval_gettext "\${REV}: Could not drop stash entry")"
 
        # clear_stash if we just dropped the last stash entry
        git rev-parse --verify "$ref_stash@{0}" > /dev/null 2>&1 || clear_stash
 }
 
 apply_to_branch () {
-       test -n "$1" || die 'No branch name specified'
+       test -n "$1" || die "$(gettext "No branch name specified")"
        branch=$1
        shift 1
 
@@ -484,7 +509,7 @@ branch)
        case $# in
        0)
                save_stash &&
-               say '(To restore them type "git stash apply")'
+               say "$(gettext "(To restore them type \"git stash apply\")")"
                ;;
        *)
                usage
index d189a24c71c44806a9c1381e2a8e5993269e568a..cfd5aa60491a88534aeacb7fe70141aa391675a9 100755 (executable)
@@ -14,6 +14,7 @@ USAGE="[--quiet] add [-b branch] [-f|--force] [--reference <repository>] [--] <r
    or: $dashless [--quiet] sync [--] [<path>...]"
 OPTIONS_SPEC=
 . git-sh-setup
+. git-sh-i18n
 . git-parse-remote
 require_work_tree
 
@@ -34,7 +35,7 @@ resolve_relative_url ()
 {
        remote=$(get_default_remote)
        remoteurl=$(git config "remote.$remote.url") ||
-               die "remote ($remote) does not have a url defined in .git/config"
+               die "$(eval_gettext "remote (\$remote) does not have a url defined in .git/config")"
        url="$1"
        remoteurl=${remoteurl%/}
        sep=/
@@ -52,7 +53,7 @@ resolve_relative_url ()
                                sep=:
                                ;;
                        *)
-                               die "cannot strip one component off url '$remoteurl'"
+                               die "$(eval_gettext "cannot strip one component off url '\$remoteurl'")"
                                ;;
                        esac
                        ;;
@@ -104,7 +105,7 @@ module_name()
        name=$( git config -f .gitmodules --get-regexp '^submodule\..*\.path$' |
                sed -n -e 's|^submodule\.\(.*\)\.path '"$re"'$|\1|p' )
        test -z "$name" &&
-       die "No submodule mapping found in .gitmodules for path '$path'"
+       die "$(eval_gettext "No submodule mapping found in .gitmodules for path '\$path'")"
        echo "$name"
 }
 
@@ -128,7 +129,7 @@ module_clone()
        else
                git-clone -n "$url" "$path"
        fi ||
-       die "Clone of '$url' into submodule path '$path' failed"
+       die "$(eval_gettext "Clone of '\$url' into submodule path '\$path' failed")"
 }
 
 #
@@ -201,7 +202,7 @@ cmd_add()
                realrepo=$repo
                ;;
        *)
-               die "repo URL: '$repo' must be absolute or begin with ./|../"
+               die "$(eval_gettext "repo URL: '\$repo' must be absolute or begin with ./|../")"
        ;;
        esac
 
@@ -218,13 +219,16 @@ cmd_add()
                        s|/*$||
                ')
        git ls-files --error-unmatch "$path" > /dev/null 2>&1 &&
-       die "'$path' already exists in the index"
+       die "$(eval_gettext "'\$path' already exists in the index")"
 
        if test -z "$force" && ! git add --dry-run --ignore-missing "$path" > /dev/null 2>&1
        then
-               echo >&2 "The following path is ignored by one of your .gitignore files:" &&
-               echo >&2 $path &&
-               echo >&2 "Use -f if you really want to add it."
+               (
+                       eval_gettext "The following path is ignored by one of your .gitignore files:
+\$path
+Use -f if you really want to add it." &&
+                       echo
+               ) >&2
                exit 1
        fi
 
@@ -233,9 +237,9 @@ cmd_add()
        then
                if test -d "$path"/.git -o -f "$path"/.git
                then
-                       echo "Adding existing repo at '$path' to the index"
+                       eval_gettext "Adding existing repo at '\$path' to the index"; echo
                else
-                       die "'$path' already exists and is not a valid git repo"
+                       die "$(eval_gettext "'\$path' already exists and is not a valid git repo")"
                fi
 
                case "$repo" in
@@ -258,16 +262,16 @@ cmd_add()
                        '') git checkout -f -q ;;
                        ?*) git checkout -f -q -B "$branch" "origin/$branch" ;;
                        esac
-               ) || die "Unable to checkout submodule '$path'"
+               ) || die "$(eval_gettext "Unable to checkout submodule '\$path'")"
        fi
 
        git add $force "$path" ||
-       die "Failed to add submodule '$path'"
+       die "$(eval_gettext "Failed to add submodule '\$path'")"
 
        git config -f .gitmodules submodule."$path".path "$path" &&
        git config -f .gitmodules submodule."$path".url "$repo" &&
        git add --force .gitmodules ||
-       die "Failed to register submodule '$path'"
+       die "$(eval_gettext "Failed to register submodule '\$path'")"
 }
 
 #
@@ -305,7 +309,7 @@ cmd_foreach()
        do
                if test -e "$path"/.git
                then
-                       say "Entering '$prefix$path'"
+                       say "$(eval_gettext "Entering '\$prefix\$path'")"
                        name=$(module_name "$path")
                        (
                                prefix="$prefix$path/"
@@ -317,7 +321,7 @@ cmd_foreach()
                                        cmd_foreach "--recursive" "$@"
                                fi
                        ) ||
-                       die "Stopping at '$path'; script returned non-zero status."
+                       die "$(eval_gettext "Stopping at '\$path'; script returned non-zero status.")"
                fi
        done
 }
@@ -360,7 +364,7 @@ cmd_init()
 
                url=$(git config -f .gitmodules submodule."$name".url)
                test -z "$url" &&
-               die "No url found for submodule path '$path' in .gitmodules"
+               die "$(eval_gettext "No url found for submodule path '\$path' in .gitmodules")"
 
                # Possibly a url relative to parent
                case "$url" in
@@ -370,14 +374,14 @@ cmd_init()
                esac
 
                git config submodule."$name".url "$url" ||
-               die "Failed to register url for submodule path '$path'"
+               die "$(eval_gettext "Failed to register url for submodule path '\$path'")"
 
                upd="$(git config -f .gitmodules submodule."$name".update)"
                test -z "$upd" ||
                git config submodule."$name".update "$upd" ||
-               die "Failed to register update mode for submodule path '$path'"
+               die "$(eval_gettext "Failed to register update mode for submodule path '\$path'")"
 
-               say "Submodule '$name' ($url) registered for path '$path'"
+               say "$(eval_gettext "Submodule '\$name' (\$url) registered for path '\$path'")"
        done
 }
 
@@ -444,7 +448,8 @@ cmd_update()
        fi
 
        cloned_modules=
-       module_list "$@" |
+       module_list "$@" | {
+       err=
        while read mode sha1 stage path
        do
                if test "$stage" = U
@@ -460,8 +465,8 @@ cmd_update()
                        # Only mention uninitialized submodules when its
                        # path have been specified
                        test "$#" != "0" &&
-                       say "Submodule path '$path' not initialized" &&
-                       say "Maybe you want to use 'update --init'?"
+                       say "$(eval_gettext "Submodule path '\$path' not initialized
+Maybe you want to use 'update --init'?")"
                        continue
                fi
 
@@ -473,7 +478,7 @@ cmd_update()
                else
                        subsha1=$(clear_local_git_env; cd "$path" &&
                                git rev-parse --verify HEAD) ||
-                       die "Unable to find current revision in submodule path '$path'"
+                       die "$(eval_gettext "Unable to find current revision in submodule path '\$path'")"
                fi
 
                if ! test -z "$update"
@@ -497,7 +502,7 @@ cmd_update()
                                (clear_local_git_env; cd "$path" &&
                                        ( (rev=$(git rev-list -n 1 $sha1 --not --all 2>/dev/null) &&
                                         test -z "$rev") || git-fetch)) ||
-                               die "Unable to fetch in submodule path '$path'"
+                               die "$(eval_gettext "Unable to fetch in submodule path '\$path'")"
                        fi
 
                        # Is this something we just cloned?
@@ -507,35 +512,72 @@ cmd_update()
                                update_module= ;;
                        esac
 
+                       must_die_on_failure=
                        case "$update_module" in
                        rebase)
                                command="git rebase"
-                               action="rebase"
-                               msg="rebased onto"
+                               die_msg="$(eval_gettext "Unable to rebase '\$sha1' in submodule path '\$path'")"
+                               say_msg="$(eval_gettext "Submodule path '\$path': rebased into '\$sha1'")"
+                               must_die_on_failure=yes
                                ;;
                        merge)
                                command="git merge"
-                               action="merge"
-                               msg="merged in"
+                               die_msg="$(eval_gettext "Unable to merge '\$sha1' in submodule path '\$path'")"
+                               say_msg="$(eval_gettext "Submodule path '\$path': merged in '\$sha1'")"
+                               must_die_on_failure=yes
                                ;;
                        *)
                                command="git checkout $subforce -q"
-                               action="checkout"
-                               msg="checked out"
+                               die_msg="$(eval_gettext "Unable to checkout '\$sha1' in submodule path '\$path'")"
+                               say_msg="$(eval_gettext "Submodule path '\$path': checked out '\$sha1'")"
                                ;;
                        esac
 
-                       (clear_local_git_env; cd "$path" && $command "$sha1") ||
-                       die "Unable to $action '$sha1' in submodule path '$path'"
-                       say "Submodule path '$path': $msg '$sha1'"
+                       if (clear_local_git_env; cd "$path" && $command "$sha1")
+                       then
+                               say "$say_msg"
+                       elif test -n "$must_die_on_failure"
+                       then
+                               die_with_status 2 "$die_msg"
+                       else
+                               err="${err};$die_msg"
+                               continue
+                       fi
                fi
 
                if test -n "$recursive"
                then
-                       (clear_local_git_env; cd "$path" && eval cmd_update "$orig_flags") ||
-                       die "Failed to recurse into submodule path '$path'"
+                       (clear_local_git_env; cd "$path" && eval cmd_update "$orig_flags")
+                       res=$?
+                       if test $res -gt 0
+                       then
+                               die_msg="$(eval_gettext "Failed to recurse into submodule path '\$path'")"
+                               if test $res -eq 1
+                               then
+                                       err="${err};$die_msg"
+                                       continue
+                               else
+                                       die_with_status $res "$die_msg"
+                               fi
+                       fi
                fi
        done
+
+       if test -n "$err"
+       then
+               OIFS=$IFS
+               IFS=';'
+               for e in $err
+               do
+                       if test -n "$e"
+                       then
+                               echo >&2 "$e"
+                       fi
+               done
+               IFS=$OIFS
+               exit 1
+       fi
+       }
 }
 
 set_name_rev () {
@@ -617,7 +659,7 @@ cmd_summary() {
        if [ -n "$files" ]
        then
                test -n "$cached" &&
-               die "--cached cannot be used with --files"
+               die "$(gettext -- "--cached cannot be used with --files")"
                diff_cmd=diff-files
                head=
        fi
@@ -657,7 +699,10 @@ cmd_summary() {
                                ;; # removed
                        *)
                                # unexpected type
-                               echo >&2 "unexpected mode $mod_dst"
+                               (
+                                       eval_gettext "unexpected mode \$mod_dst" &&
+                                       echo
+                               ) >&2
                                continue ;;
                        esac
                fi
@@ -675,13 +720,13 @@ cmd_summary() {
                total_commits=
                case "$missing_src,$missing_dst" in
                t,)
-                       errmsg="  Warn: $name doesn't contain commit $sha1_src"
+                       errmsg="$(eval_gettext "  Warn: \$name doesn't contain commit \$sha1_src")"
                        ;;
                ,t)
-                       errmsg="  Warn: $name doesn't contain commit $sha1_dst"
+                       errmsg="$(eval_gettext "  Warn: \$name doesn't contain commit \$sha1_dst")"
                        ;;
                t,t)
-                       errmsg="  Warn: $name doesn't contain commits $sha1_src and $sha1_dst"
+                       errmsg="$(eval_gettext "  Warn: \$name doesn't contain commits \$sha1_src and \$sha1_dst")"
                        ;;
                *)
                        errmsg=
@@ -706,11 +751,13 @@ cmd_summary() {
                sha1_abbr_dst=$(echo $sha1_dst | cut -c1-7)
                if test $status = T
                then
+                       blob="$(gettext "blob")"
+                       submodule="$(gettext "submodule")"
                        if test $mod_dst = 160000
                        then
-                               echo "* $name $sha1_abbr_src(blob)->$sha1_abbr_dst(submodule)$total_commits:"
+                               echo "* $name $sha1_abbr_src($blob)->$sha1_abbr_dst($submodule)$total_commits:"
                        else
-                               echo "* $name $sha1_abbr_src(submodule)->$sha1_abbr_dst(blob)$total_commits:"
+                               echo "* $name $sha1_abbr_src($submodule)->$sha1_abbr_dst($blob)$total_commits:"
                        fi
                else
                        echo "* $name $sha1_abbr_src...$sha1_abbr_dst$total_commits:"
@@ -742,9 +789,9 @@ cmd_summary() {
        done |
        if test -n "$for_status"; then
                if [ -n "$files" ]; then
-                       echo "# Submodules changed but not updated:"
+                       gettext "# Submodules changed but not updated:"; echo
                else
-                       echo "# Submodule changes to be committed:"
+                       gettext "# Submodule changes to be committed:"; echo
                fi
                echo "#"
                sed -e 's|^|# |' -e 's|^# $|#|'
@@ -830,7 +877,7 @@ cmd_status()
                                cd "$path" &&
                                eval cmd_status "$orig_args"
                        ) ||
-                       die "Failed to recurse into submodule path '$path'"
+                       die "$(eval_gettext "Failed to recurse into submodule path '\$path'")"
                fi
        done
 }
@@ -874,7 +921,7 @@ cmd_sync()
                        ;;
                esac
 
-               say "Synchronizing submodule url for '$name'"
+               say "$(eval_gettext "Synchronizing submodule url for '\$name'")"
                git config submodule."$name".url "$url"
 
                if test -e "$path"/.git
index 7849cfc141d384bc28479c2f37fd128c77fe0fbe..89f83fd27abe315804173a809b3c7ef00ead6527 100755 (executable)
@@ -3124,8 +3124,12 @@ sub lookup_svn_merge {
                        next;
                }
 
-               push @merged_commit_ranges,
-                       "$bottom_commit^..$top_commit";
+               if (scalar(command('rev-parse', "$bottom_commit^@"))) {
+                       push @merged_commit_ranges,
+                            "$bottom_commit^..$top_commit";
+               } else {
+                       push @merged_commit_ranges, "$top_commit";
+               }
 
                if ( !defined $tip or $top > $tip ) {
                        $tip = $top;
@@ -3154,9 +3158,9 @@ sub check_cherry_pick {
        my $parents = shift;
        my @ranges = @_;
        my %commits = map { $_ => 1 }
-               _rev_list("--no-merges", $tip, "--not", $base, @$parents);
+               _rev_list("--no-merges", $tip, "--not", $base, @$parents, "--");
        for my $range ( @ranges ) {
-               delete @commits{_rev_list($range)};
+               delete @commits{_rev_list($range, "--")};
        }
        for my $commit (keys %commits) {
                if (has_no_changes($commit)) {
diff --git a/git.c b/git.c
index 89721d420a09bfc8eb6a2d4010f86f5685cfd255..8828c18d6cce99b8becfbb510fcc9b2b752598ca 100644 (file)
--- a/git.c
+++ b/git.c
@@ -183,8 +183,6 @@ static int handle_alias(int *argcp, const char ***argv)
                if (alias_string[0] == '!') {
                        const char **alias_argv;
                        int argc = *argcp, i;
-                       struct strbuf sb = STRBUF_INIT;
-                       const char *env[2];
 
                        commit_pager_choice();
 
@@ -195,13 +193,7 @@ static int handle_alias(int *argcp, const char ***argv)
                                alias_argv[i] = (*argv)[i];
                        alias_argv[argc] = NULL;
 
-                       strbuf_addstr(&sb, "GIT_PREFIX=");
-                       if (subdir)
-                               strbuf_addstr(&sb, subdir);
-                       env[0] = sb.buf;
-                       env[1] = NULL;
-                       ret = run_command_v_opt_cd_env(alias_argv, RUN_USING_SHELL, NULL, env);
-                       strbuf_release(&sb);
+                       ret = run_command_v_opt(alias_argv, RUN_USING_SHELL);
                        if (ret >= 0)   /* normal exit */
                                exit(ret);
 
index 1b83a8d0a0adca8ffcc948bdca55d1f8e2bbaa48..ecc9d3cca3b86131893e709ea4fc73266630dcaf 100755 (executable)
@@ -328,6 +328,7 @@ sub evaluate_uri {
        # Enable grep search, which will list the files in currently selected
        # tree containing the given string. Enabled by default. This can be
        # potentially CPU-intensive, of course.
+       # Note that you need to have 'search' feature enabled too.
 
        # To enable system wide have in $GITWEB_CONFIG
        # $feature{'grep'}{'default'} = [1];
@@ -342,6 +343,7 @@ sub evaluate_uri {
        # Enable the pickaxe search, which will list the commits that modified
        # a given string in a file. This can be practical and quite faster
        # alternative to 'blame', but still potentially CPU-intensive.
+       # Note that you need to have 'search' feature enabled too.
 
        # To enable system wide have in $GITWEB_CONFIG
        # $feature{'pickaxe'}{'default'} = [1];
@@ -3560,12 +3562,9 @@ sub mimetype_guess_file {
        open(my $mh, '<', $mimemap) or return undef;
        while (<$mh>) {
                next if m/^#/; # skip comments
-               my ($mimetype, $exts) = split(/\t+/);
-               if (defined $exts) {
-                       my @exts = split(/\s+/, $exts);
-                       foreach my $ext (@exts) {
-                               $mimemap{$ext} = $mimetype;
-                       }
+               my ($mimetype, @exts) = split(/\s+/);
+               foreach my $ext (@exts) {
+                       $mimemap{$ext} = $mimetype;
                }
        }
        close($mh);
index 676da6b590bb9335d8fe8d65d588fc72289b871c..db6eb505846aedfaca1cdbaf6f4d399d049de777 100644 (file)
@@ -29,7 +29,6 @@
 /* ............................................................ */
 /* utility/helper functions (and variables) */
 
-var xhr;        // XMLHttpRequest object
 var projectUrl; // partial query + separator ('?' or ';')
 
 // 'commits' is an associative map. It maps SHA1s to Commit objects.
@@ -420,8 +419,6 @@ function handleLine(commit, group) {
 
 // ----------------------------------------------------------------------
 
-var inProgress = false;   // are we processing response
-
 /**#@+
  * @constant
  */
@@ -433,8 +430,6 @@ var endRe  = /^END ?([^ ]*) ?(.*)/;
 var curCommit = new Commit();
 var curGroup  = {};
 
-var pollTimer = null;
-
 /**
  * Parse output from 'git blame --incremental [...]', received via
  * XMLHttpRequest from server (blamedataUrl), and call handleLine
@@ -535,43 +530,51 @@ function processData(unprocessed, nextReadPos) {
  * Handle XMLHttpRequest errors
  *
  * @param {XMLHttpRequest} xhr: XMLHttpRequest object
+ * @param {Number} [xhr.pollTimer] ID of the timeout to clear
  *
- * @globals pollTimer, commits, inProgress
+ * @globals commits
  */
 function handleError(xhr) {
        errorInfo('Server error: ' +
                xhr.status + ' - ' + (xhr.statusText || 'Error contacting server'));
 
-       clearInterval(pollTimer);
+       if (typeof xhr.pollTimer === "number") {
+               clearTimeout(xhr.pollTimer);
+               delete xhr.pollTimer;
+       }
        commits = {}; // free memory
-
-       inProgress = false;
 }
 
 /**
  * Called after XMLHttpRequest finishes (loads)
  *
- * @param {XMLHttpRequest} xhr: XMLHttpRequest object (unused)
+ * @param {XMLHttpRequest} xhr: XMLHttpRequest object
+ * @param {Number} [xhr.pollTimer] ID of the timeout to clear
  *
- * @globals pollTimer, commits, inProgress
+ * @globals commits
  */
 function responseLoaded(xhr) {
-       clearInterval(pollTimer);
+       if (typeof xhr.pollTimer === "number") {
+               clearTimeout(xhr.pollTimer);
+               delete xhr.pollTimer;
+       }
 
        fixColorsAndGroups();
        writeTimeInterval();
        commits = {}; // free memory
-
-       inProgress = false;
 }
 
 /**
  * handler for XMLHttpRequest onreadystatechange event
  * @see startBlame
  *
- * @globals xhr, inProgress
+ * @param {XMLHttpRequest} xhr: XMLHttpRequest object
+ * @param {Number} xhr.prevDataLength: previous value of xhr.responseText.length
+ * @param {Number} xhr.nextReadPos: start of unread part of xhr.responseText
+ * @param {Number} [xhr.pollTimer] ID of the timeout (to reset or cancel)
+ * @param {Boolean} fromTimer: if handler was called from timer
  */
-function handleResponse() {
+function handleResponse(xhr, fromTimer) {
 
        /*
         * xhr.readyState
@@ -609,32 +612,31 @@ function handleResponse() {
                return;
        }
 
-       // in case we were called before finished processing
-       if (inProgress) {
-               return;
-       } else {
-               inProgress = true;
-       }
 
        // extract new whole (complete) lines, and process them
-       while (xhr.prevDataLength !== xhr.responseText.length) {
-               if (xhr.readyState === 4 &&
-                   xhr.prevDataLength === xhr.responseText.length) {
-                       break;
-               }
-
+       if (xhr.prevDataLength !== xhr.responseText.length) {
                xhr.prevDataLength = xhr.responseText.length;
                var unprocessed = xhr.responseText.substring(xhr.nextReadPos);
                xhr.nextReadPos = processData(unprocessed, xhr.nextReadPos);
-       } // end while
+       }
 
        // did we finish work?
-       if (xhr.readyState === 4 &&
-           xhr.prevDataLength === xhr.responseText.length) {
+       if (xhr.readyState === 4) {
                responseLoaded(xhr);
+               return;
        }
 
-       inProgress = false;
+       // if we get from timer, we have to restart it
+       // otherwise onreadystatechange gives us partial response, timer not needed
+       if (fromTimer) {
+               setTimeout(function () {
+                       handleResponse(xhr, true);
+               }, 1000);
+
+       } else if (typeof xhr.pollTimer === "number") {
+               clearTimeout(xhr.pollTimer);
+               delete xhr.pollTimer;
+       }
 }
 
 // ============================================================
@@ -649,11 +651,11 @@ function handleResponse() {
  * Called from 'blame_incremental' view after loading table with
  * file contents, a base for blame view.
  *
- * @globals xhr, t0, projectUrl, div_progress_bar, totalLines, pollTimer
+ * @globals t0, projectUrl, div_progress_bar, totalLines
 */
 function startBlame(blamedataUrl, bUrl) {
 
-       xhr = createRequestObject();
+       var xhr = createRequestObject();
        if (!xhr) {
                errorInfo('ERROR: XMLHttpRequest not supported');
                return;
@@ -672,8 +674,9 @@ function startBlame(blamedataUrl, bUrl) {
        xhr.prevDataLength = -1;  // used to detect if we have new data
        xhr.nextReadPos = 0;      // where unread part of response starts
 
-       xhr.onreadystatechange = handleResponse;
-       //xhr.onreadystatechange = function () { handleResponse(xhr); };
+       xhr.onreadystatechange = function () {
+               handleResponse(xhr, false);
+       };
 
        xhr.open('GET', blamedataUrl);
        xhr.setRequestHeader('Accept', 'text/plain');
@@ -681,7 +684,9 @@ function startBlame(blamedataUrl, bUrl) {
 
        // not all browsers call onreadystatechange event on each server flush
        // poll response using timer every second to handle this issue
-       pollTimer = setInterval(xhr.onreadystatechange, 1000);
+       xhr.pollTimer = setTimeout(function () {
+               handleResponse(xhr, true);
+       }, 1000);
 }
 
 /* end of blame_incremental.js */
diff --git a/grep.c b/grep.c
index d03d9e24c23eff2d60ae7226a412f3ccf66670fd..04e9ba4ec46b9f2002135293ede1bc5570fa73dc 100644 (file)
--- a/grep.c
+++ b/grep.c
@@ -721,7 +721,10 @@ static void show_line(struct grep_opt *opt, char *bol, char *eol,
        int rest = eol - bol;
        char *line_color = NULL;
 
-       if (opt->pre_context || opt->post_context) {
+       if (opt->file_break && opt->last_shown == 0) {
+               if (opt->show_hunk_mark)
+                       opt->output(opt, "\n", 1);
+       } else if (opt->pre_context || opt->post_context) {
                if (opt->last_shown == 0) {
                        if (opt->show_hunk_mark) {
                                output_color(opt, "--", 2, opt->color_sep);
@@ -732,9 +735,13 @@ static void show_line(struct grep_opt *opt, char *bol, char *eol,
                        opt->output(opt, "\n", 1);
                }
        }
+       if (opt->heading && opt->last_shown == 0) {
+               output_color(opt, name, strlen(name), opt->color_filename);
+               opt->output(opt, "\n", 1);
+       }
        opt->last_shown = lno;
 
-       if (opt->pathname) {
+       if (!opt->heading && opt->pathname) {
                output_color(opt, name, strlen(name), opt->color_filename);
                output_sep(opt, sign);
        }
@@ -941,9 +948,18 @@ static int grep_buffer_1(struct grep_opt *opt, const char *name,
        if (!opt->output)
                opt->output = std_output;
 
-       if (opt->last_shown && (opt->pre_context || opt->post_context) &&
-           opt->output == std_output)
-               opt->show_hunk_mark = 1;
+       if (opt->pre_context || opt->post_context || opt->file_break) {
+               /* Show hunk marks, except for the first file. */
+               if (opt->last_shown)
+                       opt->show_hunk_mark = 1;
+               /*
+                * If we're using threads then we can't easily identify
+                * the first file.  Always put hunk marks in that case
+                * and skip the very first one later in work_done().
+                */
+               if (opt->output != std_output)
+                       opt->show_hunk_mark = 1;
+       }
        opt->last_shown = 0;
 
        switch (opt->binary) {
diff --git a/grep.h b/grep.h
index cd055cdfa8cac903382d592f1ec7e2a22bf7f897..c5682973eaf696099b0d0367e21df6c0a4624836 100644 (file)
--- a/grep.h
+++ b/grep.h
@@ -110,6 +110,8 @@ struct grep_opt {
        unsigned post_context;
        unsigned last_shown;
        int show_hunk_mark;
+       int file_break;
+       int heading;
        void *priv;
 
        void (*output)(struct grep_opt *opt, const void *data, size_t size);
diff --git a/http.c b/http.c
index b2ae8de16db3abe2cad27249ae767f421aa6bb24..a1ea3db499eebc2deabf1a64e43b063cbe63ad2a 100644 (file)
--- a/http.c
+++ b/http.c
@@ -41,6 +41,7 @@ static long curl_low_speed_limit = -1;
 static long curl_low_speed_time = -1;
 static int curl_ftp_no_epsv;
 static const char *curl_http_proxy;
+static const char *curl_cookie_file;
 static char *user_name, *user_pass;
 static const char *user_agent;
 
@@ -191,6 +192,9 @@ static int http_options(const char *var, const char *value, void *cb)
        if (!strcmp("http.proxy", var))
                return git_config_string(&curl_http_proxy, var, value);
 
+       if (!strcmp("http.cookiefile", var))
+               return git_config_string(&curl_cookie_file, var, value);
+
        if (!strcmp("http.postbuffer", var)) {
                http_post_buffer = git_config_int(var, value);
                if (http_post_buffer < LARGE_PACKET_MAX)
@@ -531,6 +535,7 @@ struct active_request_slot *get_active_slot(void)
        slot->finished = NULL;
        slot->callback_data = NULL;
        slot->callback_func = NULL;
+       curl_easy_setopt(slot->curl, CURLOPT_COOKIEFILE, curl_cookie_file);
        curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, pragma_header);
        curl_easy_setopt(slot->curl, CURLOPT_ERRORBUFFER, curl_errorstr);
        curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, NULL);
index 4ac9a037f478e769a69072c324a47876e298cae4..46a9e60708ad98db5299f9242db05de5915a0ebb 100644 (file)
@@ -726,11 +726,12 @@ static int verify_dotfile(const char *rest)
         * has already been discarded, we now test
         * the rest.
         */
-       switch (*rest) {
+
        /* "." is not allowed */
-       case '\0': case '/':
+       if (*rest == '\0' || is_dir_sep(*rest))
                return 0;
 
+       switch (*rest) {
        /*
         * ".git" followed by  NUL or slash is bad. This
         * shares the path end test with the ".." case.
@@ -743,7 +744,7 @@ static int verify_dotfile(const char *rest)
                rest += 2;
        /* fallthrough */
        case '.':
-               if (rest[1] == '\0' || rest[1] == '/')
+               if (rest[1] == '\0' || is_dir_sep(rest[1]))
                        return 0;
        }
        return 1;
@@ -753,23 +754,19 @@ int verify_path(const char *path)
 {
        char c;
 
+       if (has_dos_drive_prefix(path))
+               return 0;
+
        goto inside;
        for (;;) {
                if (!c)
                        return 1;
-               if (c == '/') {
+               if (is_dir_sep(c)) {
 inside:
                        c = *path++;
-                       switch (c) {
-                       default:
-                               continue;
-                       case '/': case '\0':
-                               break;
-                       case '.':
-                               if (verify_dotfile(path))
-                                       continue;
-                       }
-                       return 0;
+                       if ((c == '.' && !verify_dotfile(path)) ||
+                           is_dir_sep(c) || c == '\0')
+                               return 0;
                }
                c = *path++;
        }
diff --git a/refs.c b/refs.c
index e3c05110e58ca5684f9c43b1e1a5eb2587c96828..3a8789d3857d17a3a0a94ba2750e9f22857b8667 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -1451,7 +1451,7 @@ int write_ref_sha1(struct ref_lock *lock,
        }
        o = parse_object(sha1);
        if (!o) {
-               error("Trying to write ref %s with nonexistant object %s",
+               error("Trying to write ref %s with nonexistent object %s",
                        lock->ref_name, sha1_to_hex(sha1));
                unlock_ref(lock);
                return -1;
@@ -1826,6 +1826,12 @@ int update_ref(const char *action, const char *refname,
        return 0;
 }
 
+int ref_exists(char *refname)
+{
+       unsigned char sha1[20];
+       return !!resolve_ref(refname, sha1, 1, NULL);
+}
+
 struct ref *find_ref_by_name(const struct ref *list, const char *name)
 {
        for ( ; list; list = list->next)
diff --git a/refs.h b/refs.h
index 5e7a9a59f5f6b687d15eb37a11696433bdc8f15c..5de06e57e7a9644a7dd51832552e9d1afa53c8cd 100644 (file)
--- a/refs.h
+++ b/refs.h
@@ -54,6 +54,7 @@ extern void warn_dangling_symref(FILE *fp, const char *msg_fmt, const char *refn
  */
 extern void add_extra_ref(const char *refname, const unsigned char *sha1, int flags);
 extern void clear_extra_refs(void);
+extern int ref_exists(char *);
 
 extern int peel_ref(const char *, unsigned char *);
 
index 17d8a9b377265aeed9765f04d505959e1f7fb9b0..b5be25ce9623936e42566ca52b9e4d1d33153111 100644 (file)
@@ -811,19 +811,21 @@ static void parse_push(struct strbuf *buf)
 
                strbuf_reset(buf);
                if (strbuf_getline(buf, stdin, '\n') == EOF)
-                       return;
+                       goto free_specs;
                if (!*buf->buf)
                        break;
        } while (1);
 
        if (push(nr_spec, specs))
                exit(128); /* error already reported */
-       for (i = 0; i < nr_spec; i++)
-               free(specs[i]);
-       free(specs);
 
        printf("\n");
        fflush(stdout);
+
+ free_specs:
+       for (i = 0; i < nr_spec; i++)
+               free(specs[i]);
+       free(specs);
 }
 
 int main(int argc, const char **argv)
diff --git a/setup.c b/setup.c
index ce87900ce3c68ace0f231827bdda0e9a65ef15b3..5ea5502e4881a806ebb3aa9fd38f146cf9f65cf9 100644 (file)
--- a/setup.c
+++ b/setup.c
@@ -710,6 +710,11 @@ const char *setup_git_directory_gently(int *nongit_ok)
        const char *prefix;
 
        prefix = setup_git_directory_gently_1(nongit_ok);
+       if (prefix)
+               setenv("GIT_PREFIX", prefix, 1);
+       else
+               setenv("GIT_PREFIX", "", 1);
+
        if (startup_info) {
                startup_info->have_repository = !nongit_ok || !*nongit_ok;
                startup_info->prefix = prefix;
index 2eb0ee44b87a08750e3789ff9132d55a6abd96f9..5ddd6886c85356164e81c477d3a170343bc28049 100644 (file)
@@ -51,7 +51,6 @@
    Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
 
 #include <errno.h>
-#include <getopt.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -74,6 +73,7 @@ main (int argc, char *argv[])
        {
        case 1:
          error ("we won't substitute all variables on stdin for you");
+         break;
          /*
          all_variables = 1;
       subst_from_stdin ();
index 064a33040812ba8782bf602c693abf08613d6ec7..bd6fb2f95dda17adcc56ae288af971a781cb7cdf 100644 (file)
@@ -1186,7 +1186,7 @@ static int open_sha1_file(const unsigned char *sha1)
        return -1;
 }
 
-static void *map_sha1_file(const unsigned char *sha1, unsigned long *size)
+void *map_sha1_file(const unsigned char *sha1, unsigned long *size)
 {
        void *map;
        int fd;
@@ -1205,20 +1205,29 @@ static void *map_sha1_file(const unsigned char *sha1, unsigned long *size)
        return map;
 }
 
-static int legacy_loose_object(unsigned char *map)
+/*
+ * There used to be a second loose object header format which
+ * was meant to mimic the in-pack format, allowing for direct
+ * copy of the object data.  This format turned up not to be
+ * really worth it and we no longer write loose objects in that
+ * format.
+ */
+static int experimental_loose_object(unsigned char *map)
 {
        unsigned int word;
 
        /*
         * Is it a zlib-compressed buffer? If so, the first byte
         * must be 0x78 (15-bit window size, deflated), and the
-        * first 16-bit word is evenly divisible by 31
+        * first 16-bit word is evenly divisible by 31. If so,
+        * we are looking at the official format, not the experimental
+        * one.
         */
        word = (map[0] << 8) + map[1];
        if (map[0] == 0x78 && !(word % 31))
-               return 1;
-       else
                return 0;
+       else
+               return 1;
 }
 
 unsigned long unpack_object_header_buffer(const unsigned char *buf,
@@ -1245,7 +1254,7 @@ unsigned long unpack_object_header_buffer(const unsigned char *buf,
        return used;
 }
 
-static int unpack_sha1_header(z_stream *stream, unsigned char *map, unsigned long mapsize, void *buffer, unsigned long bufsiz)
+int unpack_sha1_header(z_stream *stream, unsigned char *map, unsigned long mapsize, void *buffer, unsigned long bufsiz)
 {
        unsigned long size, used;
        static const char valid_loose_object_type[8] = {
@@ -1262,34 +1271,29 @@ static int unpack_sha1_header(z_stream *stream, unsigned char *map, unsigned lon
        stream->next_out = buffer;
        stream->avail_out = bufsiz;
 
-       if (legacy_loose_object(map)) {
-               git_inflate_init(stream);
-               return git_inflate(stream, 0);
-       }
-
+       if (experimental_loose_object(map)) {
+               /*
+                * The old experimental format we no longer produce;
+                * we can still read it.
+                */
+               used = unpack_object_header_buffer(map, mapsize, &type, &size);
+               if (!used || !valid_loose_object_type[type])
+                       return -1;
+               map += used;
+               mapsize -= used;
 
-       /*
-        * There used to be a second loose object header format which
-        * was meant to mimic the in-pack format, allowing for direct
-        * copy of the object data.  This format turned up not to be
-        * really worth it and we don't write it any longer.  But we
-        * can still read it.
-        */
-       used = unpack_object_header_buffer(map, mapsize, &type, &size);
-       if (!used || !valid_loose_object_type[type])
-               return -1;
-       map += used;
-       mapsize -= used;
+               /* Set up the stream for the rest.. */
+               stream->next_in = map;
+               stream->avail_in = mapsize;
+               git_inflate_init(stream);
 
-       /* Set up the stream for the rest.. */
-       stream->next_in = map;
-       stream->avail_in = mapsize;
+               /* And generate the fake traditional header */
+               stream->total_out = 1 + snprintf(buffer, bufsiz, "%s %lu",
+                                                typename(type), size);
+               return 0;
+       }
        git_inflate_init(stream);
-
-       /* And generate the fake traditional header */
-       stream->total_out = 1 + snprintf(buffer, bufsiz, "%s %lu",
-                                        typename(type), size);
-       return 0;
+       return git_inflate(stream, 0);
 }
 
 static void *unpack_sha1_rest(z_stream *stream, void *buffer, unsigned long size, const unsigned char *sha1)
@@ -1342,7 +1346,7 @@ static void *unpack_sha1_rest(z_stream *stream, void *buffer, unsigned long size
  * too permissive for what we want to check. So do an anal
  * object header parse by hand.
  */
-static int parse_sha1_header(const char *hdr, unsigned long *sizep)
+int parse_sha1_header(const char *hdr, unsigned long *sizep)
 {
        char type[10];
        int i;
@@ -1481,7 +1485,7 @@ static off_t get_delta_base(struct packed_git *p,
 
 /* forward declaration for a mutually recursive function */
 static int packed_object_info(struct packed_git *p, off_t offset,
-                             unsigned long *sizep);
+                             unsigned long *sizep, int *rtype);
 
 static int packed_delta_info(struct packed_git *p,
                             struct pack_window **w_curs,
@@ -1495,7 +1499,7 @@ static int packed_delta_info(struct packed_git *p,
        base_offset = get_delta_base(p, w_curs, &curpos, type, obj_offset);
        if (!base_offset)
                return OBJ_BAD;
-       type = packed_object_info(p, base_offset, NULL);
+       type = packed_object_info(p, base_offset, NULL, NULL);
        if (type <= OBJ_NONE) {
                struct revindex_entry *revidx;
                const unsigned char *base_sha1;
@@ -1523,10 +1527,10 @@ static int packed_delta_info(struct packed_git *p,
        return type;
 }
 
-static int unpack_object_header(struct packed_git *p,
-                               struct pack_window **w_curs,
-                               off_t *curpos,
-                               unsigned long *sizep)
+int unpack_object_header(struct packed_git *p,
+                        struct pack_window **w_curs,
+                        off_t *curpos,
+                        unsigned long *sizep)
 {
        unsigned char *base;
        unsigned int left;
@@ -1549,7 +1553,7 @@ static int unpack_object_header(struct packed_git *p,
        return type;
 }
 
-const char *packed_object_info_detail(struct packed_git *p,
+int packed_object_info_detail(struct packed_git *p,
                                      off_t obj_offset,
                                      unsigned long *size,
                                      unsigned long *store_size,
@@ -1580,7 +1584,7 @@ const char *packed_object_info_detail(struct packed_git *p,
                case OBJ_BLOB:
                case OBJ_TAG:
                        unuse_pack(&w_curs);
-                       return typename(type);
+                       return type;
                case OBJ_OFS_DELTA:
                        obj_offset = get_delta_base(p, &w_curs, &curpos, type, obj_offset);
                        if (!obj_offset)
@@ -1605,7 +1609,7 @@ const char *packed_object_info_detail(struct packed_git *p,
 }
 
 static int packed_object_info(struct packed_git *p, off_t obj_offset,
-                             unsigned long *sizep)
+                             unsigned long *sizep, int *rtype)
 {
        struct pack_window *w_curs = NULL;
        unsigned long size;
@@ -1613,6 +1617,8 @@ static int packed_object_info(struct packed_git *p, off_t obj_offset,
        enum object_type type;
 
        type = unpack_object_header(p, &w_curs, &curpos, &size);
+       if (rtype)
+               *rtype = type; /* representation type */
 
        switch (type) {
        case OBJ_OFS_DELTA:
@@ -1695,6 +1701,13 @@ static unsigned long pack_entry_hash(struct packed_git *p, off_t base_offset)
        return hash % MAX_DELTA_CACHE;
 }
 
+static int in_delta_base_cache(struct packed_git *p, off_t base_offset)
+{
+       unsigned long hash = pack_entry_hash(p, base_offset);
+       struct delta_base_cache_entry *ent = delta_base_cache + hash;
+       return (ent->data && ent->p == p && ent->base_offset == base_offset);
+}
+
 static void *cache_or_unpack_entry(struct packed_git *p, off_t base_offset,
        unsigned long *base_size, enum object_type *type, int keep_cache)
 {
@@ -2093,24 +2106,28 @@ static int sha1_loose_object_info(const unsigned char *sha1, unsigned long *size
        return status;
 }
 
-int sha1_object_info(const unsigned char *sha1, unsigned long *sizep)
+/* returns enum object_type or negative */
+int sha1_object_info_extended(const unsigned char *sha1, struct object_info *oi)
 {
        struct cached_object *co;
        struct pack_entry e;
-       int status;
+       int status, rtype;
 
        co = find_cached_object(sha1);
        if (co) {
-               if (sizep)
-                       *sizep = co->size;
+               if (oi->sizep)
+                       *(oi->sizep) = co->size;
+               oi->whence = OI_CACHED;
                return co->type;
        }
 
        if (!find_pack_entry(sha1, &e)) {
                /* Most likely it's a loose object. */
-               status = sha1_loose_object_info(sha1, sizep);
-               if (status >= 0)
+               status = sha1_loose_object_info(sha1, oi->sizep);
+               if (status >= 0) {
+                       oi->whence = OI_LOOSE;
                        return status;
+               }
 
                /* Not a loose object; someone else may have just packed it. */
                reprepare_packed_git();
@@ -2118,15 +2135,31 @@ int sha1_object_info(const unsigned char *sha1, unsigned long *sizep)
                        return status;
        }
 
-       status = packed_object_info(e.p, e.offset, sizep);
+       status = packed_object_info(e.p, e.offset, oi->sizep, &rtype);
        if (status < 0) {
                mark_bad_packed_object(e.p, sha1);
-               status = sha1_object_info(sha1, sizep);
+               status = sha1_object_info_extended(sha1, oi);
+       } else if (in_delta_base_cache(e.p, e.offset)) {
+               oi->whence = OI_DBCACHED;
+       } else {
+               oi->whence = OI_PACKED;
+               oi->u.packed.offset = e.offset;
+               oi->u.packed.pack = e.p;
+               oi->u.packed.is_delta = (rtype == OBJ_REF_DELTA ||
+                                        rtype == OBJ_OFS_DELTA);
        }
 
        return status;
 }
 
+int sha1_object_info(const unsigned char *sha1, unsigned long *sizep)
+{
+       struct object_info oi;
+
+       oi.sizep = sizep;
+       return sha1_object_info_extended(sha1, &oi);
+}
+
 static void *read_packed_sha1(const unsigned char *sha1,
                              enum object_type *type, unsigned long *size)
 {
@@ -2704,7 +2737,7 @@ static int index_stream(unsigned char *sha1, int fd, size_t size,
        while (size) {
                char buf[10240];
                size_t sz = size < sizeof(buf) ? size : sizeof(buf);
-               size_t actual;
+               ssize_t actual;
 
                actual = read_in_full(fd, buf, sz);
                if (actual < 0)
diff --git a/streaming.c b/streaming.c
new file mode 100644 (file)
index 0000000..91414f4
--- /dev/null
@@ -0,0 +1,489 @@
+/*
+ * Copyright (c) 2011, Google Inc.
+ */
+#include "cache.h"
+#include "streaming.h"
+
+enum input_source {
+       stream_error = -1,
+       incore = 0,
+       loose = 1,
+       pack_non_delta = 2
+};
+
+typedef int (*open_istream_fn)(struct git_istream *,
+                              struct object_info *,
+                              const unsigned char *,
+                              enum object_type *);
+typedef int (*close_istream_fn)(struct git_istream *);
+typedef ssize_t (*read_istream_fn)(struct git_istream *, char *, size_t);
+
+struct stream_vtbl {
+       close_istream_fn close;
+       read_istream_fn read;
+};
+
+#define open_method_decl(name) \
+       int open_istream_ ##name \
+       (struct git_istream *st, struct object_info *oi, \
+        const unsigned char *sha1, \
+        enum object_type *type)
+
+#define close_method_decl(name) \
+       int close_istream_ ##name \
+       (struct git_istream *st)
+
+#define read_method_decl(name) \
+       ssize_t read_istream_ ##name \
+       (struct git_istream *st, char *buf, size_t sz)
+
+/* forward declaration */
+static open_method_decl(incore);
+static open_method_decl(loose);
+static open_method_decl(pack_non_delta);
+static struct git_istream *attach_stream_filter(struct git_istream *st,
+                                               struct stream_filter *filter);
+
+
+static open_istream_fn open_istream_tbl[] = {
+       open_istream_incore,
+       open_istream_loose,
+       open_istream_pack_non_delta,
+};
+
+#define FILTER_BUFFER (1024*16)
+
+struct filtered_istream {
+       struct git_istream *upstream;
+       struct stream_filter *filter;
+       char ibuf[FILTER_BUFFER];
+       char obuf[FILTER_BUFFER];
+       int i_end, i_ptr;
+       int o_end, o_ptr;
+       int input_finished;
+};
+
+struct git_istream {
+       const struct stream_vtbl *vtbl;
+       unsigned long size; /* inflated size of full object */
+       z_stream z;
+       enum { z_unused, z_used, z_done, z_error } z_state;
+
+       union {
+               struct {
+                       char *buf; /* from read_object() */
+                       unsigned long read_ptr;
+               } incore;
+
+               struct {
+                       void *mapped;
+                       unsigned long mapsize;
+                       char hdr[32];
+                       int hdr_avail;
+                       int hdr_used;
+               } loose;
+
+               struct {
+                       struct packed_git *pack;
+                       off_t pos;
+               } in_pack;
+
+               struct filtered_istream filtered;
+       } u;
+};
+
+int close_istream(struct git_istream *st)
+{
+       return st->vtbl->close(st);
+}
+
+ssize_t read_istream(struct git_istream *st, char *buf, size_t sz)
+{
+       return st->vtbl->read(st, buf, sz);
+}
+
+static enum input_source istream_source(const unsigned char *sha1,
+                                       enum object_type *type,
+                                       struct object_info *oi)
+{
+       unsigned long size;
+       int status;
+
+       oi->sizep = &size;
+       status = sha1_object_info_extended(sha1, oi);
+       if (status < 0)
+               return stream_error;
+       *type = status;
+
+       switch (oi->whence) {
+       case OI_LOOSE:
+               return loose;
+       case OI_PACKED:
+               if (!oi->u.packed.is_delta && big_file_threshold <= size)
+                       return pack_non_delta;
+               /* fallthru */
+       default:
+               return incore;
+       }
+}
+
+struct git_istream *open_istream(const unsigned char *sha1,
+                                enum object_type *type,
+                                unsigned long *size,
+                                struct stream_filter *filter)
+{
+       struct git_istream *st;
+       struct object_info oi;
+       const unsigned char *real = lookup_replace_object(sha1);
+       enum input_source src = istream_source(real, type, &oi);
+
+       if (src < 0)
+               return NULL;
+
+       st = xmalloc(sizeof(*st));
+       if (open_istream_tbl[src](st, &oi, real, type)) {
+               if (open_istream_incore(st, &oi, real, type)) {
+                       free(st);
+                       return NULL;
+               }
+       }
+       if (st && filter) {
+               /* Add "&& !is_null_stream_filter(filter)" for performance */
+               struct git_istream *nst = attach_stream_filter(st, filter);
+               if (!nst)
+                       close_istream(st);
+               st = nst;
+       }
+
+       *size = st->size;
+       return st;
+}
+
+
+/*****************************************************************
+ *
+ * Common helpers
+ *
+ *****************************************************************/
+
+static void close_deflated_stream(struct git_istream *st)
+{
+       if (st->z_state == z_used)
+               git_inflate_end(&st->z);
+}
+
+
+/*****************************************************************
+ *
+ * Filtered stream
+ *
+ *****************************************************************/
+
+static close_method_decl(filtered)
+{
+       free_stream_filter(st->u.filtered.filter);
+       return close_istream(st->u.filtered.upstream);
+}
+
+static read_method_decl(filtered)
+{
+       struct filtered_istream *fs = &(st->u.filtered);
+       size_t filled = 0;
+
+       while (sz) {
+               /* do we already have filtered output? */
+               if (fs->o_ptr < fs->o_end) {
+                       size_t to_move = fs->o_end - fs->o_ptr;
+                       if (sz < to_move)
+                               to_move = sz;
+                       memcpy(buf + filled, fs->obuf + fs->o_ptr, to_move);
+                       fs->o_ptr += to_move;
+                       sz -= to_move;
+                       filled += to_move;
+                       continue;
+               }
+               fs->o_end = fs->o_ptr = 0;
+
+               /* do we have anything to feed the filter with? */
+               if (fs->i_ptr < fs->i_end) {
+                       size_t to_feed = fs->i_end - fs->i_ptr;
+                       size_t to_receive = FILTER_BUFFER;
+                       if (stream_filter(fs->filter,
+                                         fs->ibuf + fs->i_ptr, &to_feed,
+                                         fs->obuf, &to_receive))
+                               return -1;
+                       fs->i_ptr = fs->i_end - to_feed;
+                       fs->o_end = FILTER_BUFFER - to_receive;
+                       continue;
+               }
+
+               /* tell the filter to drain upon no more input */
+               if (fs->input_finished) {
+                       size_t to_receive = FILTER_BUFFER;
+                       if (stream_filter(fs->filter,
+                                         NULL, NULL,
+                                         fs->obuf, &to_receive))
+                               return -1;
+                       fs->o_end = FILTER_BUFFER - to_receive;
+                       if (!fs->o_end)
+                               break;
+                       continue;
+               }
+               fs->i_end = fs->i_ptr = 0;
+
+               /* refill the input from the upstream */
+               if (!fs->input_finished) {
+                       fs->i_end = read_istream(fs->upstream, fs->ibuf, FILTER_BUFFER);
+                       if (fs->i_end < 0)
+                               break;
+                       if (fs->i_end)
+                               continue;
+               }
+               fs->input_finished = 1;
+       }
+       return filled;
+}
+
+static struct stream_vtbl filtered_vtbl = {
+       close_istream_filtered,
+       read_istream_filtered,
+};
+
+static struct git_istream *attach_stream_filter(struct git_istream *st,
+                                               struct stream_filter *filter)
+{
+       struct git_istream *ifs = xmalloc(sizeof(*ifs));
+       struct filtered_istream *fs = &(ifs->u.filtered);
+
+       ifs->vtbl = &filtered_vtbl;
+       fs->upstream = st;
+       fs->filter = filter;
+       fs->i_end = fs->i_ptr = 0;
+       fs->o_end = fs->o_ptr = 0;
+       fs->input_finished = 0;
+       ifs->size = -1; /* unknown */
+       return ifs;
+}
+
+/*****************************************************************
+ *
+ * Loose object stream
+ *
+ *****************************************************************/
+
+static read_method_decl(loose)
+{
+       size_t total_read = 0;
+
+       switch (st->z_state) {
+       case z_done:
+               return 0;
+       case z_error:
+               return -1;
+       default:
+               break;
+       }
+
+       if (st->u.loose.hdr_used < st->u.loose.hdr_avail) {
+               size_t to_copy = st->u.loose.hdr_avail - st->u.loose.hdr_used;
+               if (sz < to_copy)
+                       to_copy = sz;
+               memcpy(buf, st->u.loose.hdr + st->u.loose.hdr_used, to_copy);
+               st->u.loose.hdr_used += to_copy;
+               total_read += to_copy;
+       }
+
+       while (total_read < sz) {
+               int status;
+
+               st->z.next_out = (unsigned char *)buf + total_read;
+               st->z.avail_out = sz - total_read;
+               status = git_inflate(&st->z, Z_FINISH);
+
+               total_read = st->z.next_out - (unsigned char *)buf;
+
+               if (status == Z_STREAM_END) {
+                       git_inflate_end(&st->z);
+                       st->z_state = z_done;
+                       break;
+               }
+               if (status != Z_OK && status != Z_BUF_ERROR) {
+                       git_inflate_end(&st->z);
+                       st->z_state = z_error;
+                       return -1;
+               }
+       }
+       return total_read;
+}
+
+static close_method_decl(loose)
+{
+       close_deflated_stream(st);
+       munmap(st->u.loose.mapped, st->u.loose.mapsize);
+       return 0;
+}
+
+static struct stream_vtbl loose_vtbl = {
+       close_istream_loose,
+       read_istream_loose,
+};
+
+static open_method_decl(loose)
+{
+       st->u.loose.mapped = map_sha1_file(sha1, &st->u.loose.mapsize);
+       if (!st->u.loose.mapped)
+               return -1;
+       if (unpack_sha1_header(&st->z,
+                              st->u.loose.mapped,
+                              st->u.loose.mapsize,
+                              st->u.loose.hdr,
+                              sizeof(st->u.loose.hdr)) < 0) {
+               git_inflate_end(&st->z);
+               munmap(st->u.loose.mapped, st->u.loose.mapsize);
+               return -1;
+       }
+
+       parse_sha1_header(st->u.loose.hdr, &st->size);
+       st->u.loose.hdr_used = strlen(st->u.loose.hdr) + 1;
+       st->u.loose.hdr_avail = st->z.total_out;
+       st->z_state = z_used;
+
+       st->vtbl = &loose_vtbl;
+       return 0;
+}
+
+
+/*****************************************************************
+ *
+ * Non-delta packed object stream
+ *
+ *****************************************************************/
+
+static read_method_decl(pack_non_delta)
+{
+       size_t total_read = 0;
+
+       switch (st->z_state) {
+       case z_unused:
+               memset(&st->z, 0, sizeof(st->z));
+               git_inflate_init(&st->z);
+               st->z_state = z_used;
+               break;
+       case z_done:
+               return 0;
+       case z_error:
+               return -1;
+       case z_used:
+               break;
+       }
+
+       while (total_read < sz) {
+               int status;
+               struct pack_window *window = NULL;
+               unsigned char *mapped;
+
+               mapped = use_pack(st->u.in_pack.pack, &window,
+                                 st->u.in_pack.pos, &st->z.avail_in);
+
+               st->z.next_out = (unsigned char *)buf + total_read;
+               st->z.avail_out = sz - total_read;
+               st->z.next_in = mapped;
+               status = git_inflate(&st->z, Z_FINISH);
+
+               st->u.in_pack.pos += st->z.next_in - mapped;
+               total_read = st->z.next_out - (unsigned char *)buf;
+               unuse_pack(&window);
+
+               if (status == Z_STREAM_END) {
+                       git_inflate_end(&st->z);
+                       st->z_state = z_done;
+                       break;
+               }
+               if (status != Z_OK && status != Z_BUF_ERROR) {
+                       git_inflate_end(&st->z);
+                       st->z_state = z_error;
+                       return -1;
+               }
+       }
+       return total_read;
+}
+
+static close_method_decl(pack_non_delta)
+{
+       close_deflated_stream(st);
+       return 0;
+}
+
+static struct stream_vtbl pack_non_delta_vtbl = {
+       close_istream_pack_non_delta,
+       read_istream_pack_non_delta,
+};
+
+static open_method_decl(pack_non_delta)
+{
+       struct pack_window *window;
+       enum object_type in_pack_type;
+
+       st->u.in_pack.pack = oi->u.packed.pack;
+       st->u.in_pack.pos = oi->u.packed.offset;
+       window = NULL;
+
+       in_pack_type = unpack_object_header(st->u.in_pack.pack,
+                                           &window,
+                                           &st->u.in_pack.pos,
+                                           &st->size);
+       unuse_pack(&window);
+       switch (in_pack_type) {
+       default:
+               return -1; /* we do not do deltas for now */
+       case OBJ_COMMIT:
+       case OBJ_TREE:
+       case OBJ_BLOB:
+       case OBJ_TAG:
+               break;
+       }
+       st->z_state = z_unused;
+       st->vtbl = &pack_non_delta_vtbl;
+       return 0;
+}
+
+
+/*****************************************************************
+ *
+ * In-core stream
+ *
+ *****************************************************************/
+
+static close_method_decl(incore)
+{
+       free(st->u.incore.buf);
+       return 0;
+}
+
+static read_method_decl(incore)
+{
+       size_t read_size = sz;
+       size_t remainder = st->size - st->u.incore.read_ptr;
+
+       if (remainder <= read_size)
+               read_size = remainder;
+       if (read_size) {
+               memcpy(buf, st->u.incore.buf + st->u.incore.read_ptr, read_size);
+               st->u.incore.read_ptr += read_size;
+       }
+       return read_size;
+}
+
+static struct stream_vtbl incore_vtbl = {
+       close_istream_incore,
+       read_istream_incore,
+};
+
+static open_method_decl(incore)
+{
+       st->u.incore.buf = read_sha1_file_extended(sha1, type, &st->size, 0);
+       st->u.incore.read_ptr = 0;
+       st->vtbl = &incore_vtbl;
+
+       return st->u.incore.buf ? 0 : -1;
+}
diff --git a/streaming.h b/streaming.h
new file mode 100644 (file)
index 0000000..589e857
--- /dev/null
@@ -0,0 +1,15 @@
+/*
+ * Copyright (c) 2011, Google Inc.
+ */
+#ifndef STREAMING_H
+#define STREAMING_H 1
+#include "cache.h"
+
+/* opaque */
+struct git_istream;
+
+extern struct git_istream *open_istream(const unsigned char *, enum object_type *, unsigned long *, struct stream_filter *);
+extern int close_istream(struct git_istream *);
+extern ssize_t read_istream(struct git_istream *, char *, size_t);
+
+#endif /* STREAMING_H */
index 47cbeb6e68ec910a691f2feca18b7b160efd0ced..9046ec98164f44811b67124e869353db4050ec06 100644 (file)
@@ -71,7 +71,7 @@ gitweb-test:
        $(MAKE) $(TGITWEB)
 
 valgrind:
-       GIT_TEST_OPTS=--valgrind $(MAKE)
+       $(MAKE) GIT_TEST_OPTS="$(GIT_TEST_OPTS) --valgrind"
 
 # Smoke testing targets
 -include ../GIT-VERSION-FILE
index 143eb1f24092df070c5ddf75340bebb319a0c746..292753f77c4daf5f3cb55de1c28269b13455544f 100644 (file)
@@ -107,4 +107,9 @@ perl -MEncode -e '$e="";decode_utf8($e, Encode::FB_CROAK)' >/dev/null 2>&1 || {
        test_done
 }
 
+perl -MCGI -MCGI::Util -MCGI::Carp -e 0 >/dev/null 2>&1 || {
+       skip_all='skipping gitweb tests, CGI module unusable'
+       test_done
+}
+
 gitweb_init
index 9078b84ae68b430285312749f62daee103dcc428..f19e6510d04583866e39cbdea545a0d1323b7f76 100755 (executable)
@@ -66,31 +66,48 @@ test_expect_success expanded_in_repo '
                echo "\$Id:NoSpaceAtEitherEnd\$"
                echo "\$Id: NoTerminatingSymbol"
                echo "\$Id: Foreign Commit With Spaces \$"
-               echo "\$Id: NoTerminatingSymbolAtEOF"
-       } > expanded-keywords &&
+       } >expanded-keywords.0 &&
+
+       {
+               cat expanded-keywords.0 &&
+               printf "\$Id: NoTerminatingSymbolAtEOF"
+       } >expanded-keywords &&
+       cat expanded-keywords >expanded-keywords-crlf &&
+       git add expanded-keywords expanded-keywords-crlf &&
+       git commit -m "File with keywords expanded" &&
+       id=$(git rev-parse --verify :expanded-keywords) &&
 
        {
                echo "File with expanded keywords"
-               echo "\$Id: fd0478f5f1486f3d5177d4c3f6eb2765e8fc56b9 \$"
-               echo "\$Id: fd0478f5f1486f3d5177d4c3f6eb2765e8fc56b9 \$"
-               echo "\$Id: fd0478f5f1486f3d5177d4c3f6eb2765e8fc56b9 \$"
-               echo "\$Id: fd0478f5f1486f3d5177d4c3f6eb2765e8fc56b9 \$"
-               echo "\$Id: fd0478f5f1486f3d5177d4c3f6eb2765e8fc56b9 \$"
-               echo "\$Id: fd0478f5f1486f3d5177d4c3f6eb2765e8fc56b9 \$"
+               echo "\$Id: $id \$"
+               echo "\$Id: $id \$"
+               echo "\$Id: $id \$"
+               echo "\$Id: $id \$"
+               echo "\$Id: $id \$"
+               echo "\$Id: $id \$"
                echo "\$Id: NoTerminatingSymbol"
                echo "\$Id: Foreign Commit With Spaces \$"
-               echo "\$Id: NoTerminatingSymbolAtEOF"
-       } > expected-output &&
-
-       git add expanded-keywords &&
-       git commit -m "File with keywords expanded" &&
+       } >expected-output.0 &&
+       {
+               cat expected-output.0 &&
+               printf "\$Id: NoTerminatingSymbolAtEOF"
+       } >expected-output &&
+       {
+               append_cr <expected-output.0 &&
+               printf "\$Id: NoTerminatingSymbolAtEOF"
+       } >expected-output-crlf &&
+       {
+               echo "expanded-keywords ident"
+               echo "expanded-keywords-crlf ident text eol=crlf"
+       } >>.gitattributes &&
 
-       echo "expanded-keywords ident" >> .gitattributes &&
+       rm -f expanded-keywords expanded-keywords-crlf &&
 
-       rm -f expanded-keywords &&
        git checkout -- expanded-keywords &&
-       cat expanded-keywords &&
-       cmp expanded-keywords expected-output
+       test_cmp expanded-keywords expected-output &&
+
+       git checkout -- expanded-keywords-crlf &&
+       test_cmp expanded-keywords-crlf expected-output-crlf
 '
 
 # The use of %f in a filter definition is expanded to the path to
index f6a44c9ee07329c8909662d2a807acf0dd6a8e43..865b8ed26d577e154276887f88c8af9d13e62170 100755 (executable)
@@ -140,6 +140,22 @@ test_expect_success 'GIT_PREFIX for !alias' '
        test_cmp expect actual
 '
 
+test_expect_success 'GIT_PREFIX for built-ins' '
+       # Use GIT_EXTERNAL_DIFF to test that the "diff" built-in
+       # receives the GIT_PREFIX variable.
+       printf "dir/" >expect &&
+       printf "#!/bin/sh\n" >diff &&
+       printf "printf \"\$GIT_PREFIX\"" >>diff &&
+       chmod +x diff &&
+       (
+               cd dir &&
+               printf "change" >two &&
+               env GIT_EXTERNAL_DIFF=./diff git diff >../actual
+               git checkout -- two
+       ) &&
+       test_cmp expect actual
+'
+
 test_expect_success 'no file/rev ambiguity check inside .git' '
        git commit -a -m 1 &&
        (
index fa69016381b0196c49472af51e36948ec7c5a2a9..a42e03967b1df3001df24089f2c50008c092ac51 100755 (executable)
@@ -169,4 +169,15 @@ test_expect_success 'checkout -f -B to an existing branch with mergeable changes
        test_must_fail test_dirty_mergeable
 '
 
+test_expect_success 'checkout -b <describe>' '
+       git tag -f -m "First commit" initial initial &&
+       git checkout -f change1 &&
+       name=$(git describe) &&
+       git checkout -b $name &&
+       git diff --exit-code change1 &&
+       echo "refs/heads/$name" >expect &&
+       git symbolic-ref HEAD >actual &&
+       test_cmp expect actual
+'
+
 test_done
index 5c725406422dcedb6c6d5e5d61cfc80f87d3595c..7197aae1ed10add49aa72748188fecb4c2ef90a4 100755 (executable)
@@ -536,7 +536,7 @@ test_expect_success 'stash pop - fail early if specified stash is not a stash re
        git reset --hard HEAD
 '
 
-test_expect_success 'ref with non-existant reflog' '
+test_expect_success 'ref with non-existent reflog' '
        git stash clear &&
        echo bar5 > file &&
        echo bar6 > file2 &&
diff --git a/t/t4048-diff-combined-binary.sh b/t/t4048-diff-combined-binary.sh
new file mode 100755 (executable)
index 0000000..87a8949
--- /dev/null
@@ -0,0 +1,212 @@
+#!/bin/sh
+
+test_description='combined and merge diff handle binary files and textconv'
+. ./test-lib.sh
+
+test_expect_success 'setup binary merge conflict' '
+       echo oneQ1 | q_to_nul >binary &&
+       git add binary &&
+       git commit -m one &&
+       echo twoQ2 | q_to_nul >binary &&
+       git commit -a -m two &&
+       git checkout -b branch-binary HEAD^ &&
+       echo threeQ3 | q_to_nul >binary &&
+       git commit -a -m three &&
+       test_must_fail git merge master &&
+       echo resolvedQhooray | q_to_nul >binary &&
+       git commit -a -m resolved
+'
+
+cat >expect <<'EOF'
+resolved
+
+diff --git a/binary b/binary
+index 7ea6ded..9563691 100644
+Binary files a/binary and b/binary differ
+resolved
+
+diff --git a/binary b/binary
+index 6197570..9563691 100644
+Binary files a/binary and b/binary differ
+EOF
+test_expect_success 'diff -m indicates binary-ness' '
+       git show --format=%s -m >actual &&
+       test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+resolved
+
+diff --combined binary
+index 7ea6ded,6197570..9563691
+Binary files differ
+EOF
+test_expect_success 'diff -c indicates binary-ness' '
+       git show --format=%s -c >actual &&
+       test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+resolved
+
+diff --cc binary
+index 7ea6ded,6197570..9563691
+Binary files differ
+EOF
+test_expect_success 'diff --cc indicates binary-ness' '
+       git show --format=%s --cc >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'setup non-binary with binary attribute' '
+       git checkout master &&
+       test_commit one text &&
+       test_commit two text &&
+       git checkout -b branch-text HEAD^ &&
+       test_commit three text &&
+       test_must_fail git merge master &&
+       test_commit resolved text &&
+       echo text -diff >.gitattributes
+'
+
+cat >expect <<'EOF'
+resolved
+
+diff --git a/text b/text
+index 2bdf67a..2ab19ae 100644
+Binary files a/text and b/text differ
+resolved
+
+diff --git a/text b/text
+index f719efd..2ab19ae 100644
+Binary files a/text and b/text differ
+EOF
+test_expect_success 'diff -m respects binary attribute' '
+       git show --format=%s -m >actual &&
+       test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+resolved
+
+diff --combined text
+index 2bdf67a,f719efd..2ab19ae
+Binary files differ
+EOF
+test_expect_success 'diff -c respects binary attribute' '
+       git show --format=%s -c >actual &&
+       test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+resolved
+
+diff --cc text
+index 2bdf67a,f719efd..2ab19ae
+Binary files differ
+EOF
+test_expect_success 'diff --cc respects binary attribute' '
+       git show --format=%s --cc >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'setup textconv attribute' '
+       echo "text diff=upcase" >.gitattributes &&
+       git config diff.upcase.textconv "tr a-z A-Z <"
+'
+
+cat >expect <<'EOF'
+resolved
+
+diff --git a/text b/text
+index 2bdf67a..2ab19ae 100644
+--- a/text
++++ b/text
+@@ -1 +1 @@
+-THREE
++RESOLVED
+resolved
+
+diff --git a/text b/text
+index f719efd..2ab19ae 100644
+--- a/text
++++ b/text
+@@ -1 +1 @@
+-TWO
++RESOLVED
+EOF
+test_expect_success 'diff -m respects textconv attribute' '
+       git show --format=%s -m >actual &&
+       test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+resolved
+
+diff --combined text
+index 2bdf67a,f719efd..2ab19ae
+--- a/text
++++ b/text
+@@@ -1,1 -1,1 +1,1 @@@
+- THREE
+ -TWO
+++RESOLVED
+EOF
+test_expect_success 'diff -c respects textconv attribute' '
+       git show --format=%s -c >actual &&
+       test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+resolved
+
+diff --cc text
+index 2bdf67a,f719efd..2ab19ae
+--- a/text
++++ b/text
+@@@ -1,1 -1,1 +1,1 @@@
+- THREE
+ -TWO
+++RESOLVED
+EOF
+test_expect_success 'diff --cc respects textconv attribute' '
+       git show --format=%s --cc >actual &&
+       test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+diff --combined text
+index 2bdf67a,f719efd..2ab19ae
+--- a/text
++++ b/text
+@@@ -1,1 -1,1 +1,1 @@@
+- three
+ -two
+++resolved
+EOF
+test_expect_success 'diff-tree plumbing does not respect textconv' '
+       git diff-tree HEAD -c -p >full &&
+       tail -n +2 full >actual &&
+       test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+diff --cc text
+index 2bdf67a,f719efd..0000000
+--- a/text
++++ b/text
+@@@ -1,1 -1,1 +1,5 @@@
+++<<<<<<< HEAD
+ +THREE
+++=======
++ TWO
+++>>>>>>> MASTER
+EOF
+test_expect_success 'diff --cc respects textconv on worktree file' '
+       git reset --hard HEAD^ &&
+       test_must_fail git merge master &&
+       git diff >actual &&
+       test_cmp expect actual
+'
+
+test_done
diff --git a/t/t4049-diff-stat-count.sh b/t/t4049-diff-stat-count.sh
new file mode 100755 (executable)
index 0000000..641e70d
--- /dev/null
@@ -0,0 +1,25 @@
+#!/bin/sh
+# Copyright (c) 2011, Google Inc.
+
+test_description='diff --stat-count'
+. ./test-lib.sh
+
+test_expect_success setup '
+       >a &&
+       >b &&
+       >c &&
+       >d &&
+       git add a b c d &&
+       chmod +x c d &&
+       echo a >a &&
+       echo b >b &&
+       cat >expect <<-\EOF
+        a |    1 +
+        b |    1 +
+        2 files changed, 2 insertions(+), 0 deletions(-)
+       EOF
+       git diff --stat --stat-count=2 >actual &&
+       test_cmp expect actual
+'
+
+test_done
index 850fc96d1f07b19310cb4672ab44374b37b82d67..151404e0908c55f3c0d399d03225b077dc226562 100755 (executable)
@@ -465,7 +465,7 @@ test_expect_success 'am newline in subject' '
        test_tick &&
        sed -e "s/second/second \\\n foo/" patch1 >patchnl &&
        git am <patchnl >output.out 2>&1 &&
-       grep "^Applying: second \\\n foo$" output.out
+       test_i18ngrep "^Applying: second \\\n foo$" output.out
 '
 
 test_expect_success 'am -q is quiet' '
index c95c4ccc393d0863ad53b6a2a684893282d7d9e6..1176bcccf3b3f3708df04f49bfd084190cd27600 100755 (executable)
@@ -45,8 +45,9 @@ do
 
        test_expect_success "am$with3 --skip continue after failed am$with3" '
                test_must_fail git am$with3 --skip >output &&
-               test "$(grep "^Applying" output)" = "Applying: 6" &&
-               test_cmp file-2-expect file-2 &&
+               test_i18ngrep "^Applying" output >output.applying &&
+               test_i18ngrep "^Applying: 6$" output.applying &&
+               test_i18ncmp file-2-expect file-2 &&
                test ! -f .git/MERGE_RR
        '
 
index e818de6ddd904378265cb11f2d48075cda474f5f..1f182f612c7e2376b503cf0b9cf7389e37903239 100755 (executable)
@@ -94,7 +94,7 @@ nick1 (1):
 
 EOF
 
-test_expect_success 'mailmap.file non-existant' '
+test_expect_success 'mailmap.file non-existent' '
        rm internal_mailmap/.mailmap &&
        rmdir internal_mailmap &&
        git shortlog HEAD >actual &&
index cb9f2bdd2956244955d56ec8a0cd2320726e1030..2ae9faa8b37821db6e7c28ae3d98e53cb25264b1 100755 (executable)
@@ -45,7 +45,7 @@ test_expect_success 'alias user-defined tformat' '
        test_cmp expected actual
 '
 
-test_expect_success 'alias non-existant format' '
+test_expect_success 'alias non-existent format' '
        git config pretty.test-alias format-that-will-never-exist &&
        test_must_fail git log --pretty=test-alias
 '
index b7b7ddaa403d7a06c0203b89e484d56d20863f4a..530b01678e5c9cfc1aed2fe2c6fcd96b9a09a90d 100755 (executable)
@@ -43,10 +43,10 @@ test_expect_success 'no group updates all' '
        repo_fetched two
 '
 
-test_expect_success 'nonexistant group produces error' '
-       mark nonexistant &&
+test_expect_success 'nonexistent group produces error' '
+       mark nonexistent &&
        update_repos &&
-       test_must_fail git remote update nonexistant &&
+       test_must_fail git remote update nonexistent &&
        ! repo_fetched one &&
        ! repo_fetched two
 '
index d73731e6446f71480db1ec7cceb73f27ad51ecd3..3abb2907ea91c5ef6298ca7ee64b7a8156d65d96 100755 (executable)
@@ -367,7 +367,7 @@ test_expect_success 'push with colon-less refspec (4)' '
 
 '
 
-test_expect_success 'push head with non-existant, incomplete dest' '
+test_expect_success 'push head with non-existent, incomplete dest' '
 
        mk_test &&
        git push testrepo master:branch &&
@@ -375,7 +375,7 @@ test_expect_success 'push head with non-existant, incomplete dest' '
 
 '
 
-test_expect_success 'push tag with non-existant, incomplete dest' '
+test_expect_success 'push tag with non-existent, incomplete dest' '
 
        mk_test &&
        git tag -f v1.0 &&
@@ -384,14 +384,14 @@ test_expect_success 'push tag with non-existant, incomplete dest' '
 
 '
 
-test_expect_success 'push sha1 with non-existant, incomplete dest' '
+test_expect_success 'push sha1 with non-existent, incomplete dest' '
 
        mk_test &&
        test_must_fail git push testrepo `git rev-parse master`:foo
 
 '
 
-test_expect_success 'push ref expression with non-existant, incomplete dest' '
+test_expect_success 'push ref expression with non-existent, incomplete dest' '
 
        mk_test &&
        test_must_fail git push testrepo master^:branch
@@ -436,7 +436,7 @@ test_expect_success 'push with +HEAD' '
 
 '
 
-test_expect_success 'push HEAD with non-existant, incomplete dest' '
+test_expect_success 'push HEAD with non-existent, incomplete dest' '
 
        mk_test &&
        git checkout master &&
index 874279e32da98ac2c20137a207a0d115f073e444..b2b26b72d05a392d8adb83be50c1bb9e73c4eb2d 100755 (executable)
@@ -99,7 +99,7 @@ test_expect_success 'submodule add to .gitignored path fails' '
                git add --force .gitignore &&
                git commit -m"Ignore everything" &&
                ! git submodule add "$submodurl" submod >actual 2>&1 &&
-               test_cmp expect actual
+               test_i18ncmp expect actual
        )
 '
 
@@ -357,7 +357,7 @@ test_expect_success 'update --init' '
 
        git submodule update init > update.out &&
        cat update.out &&
-       grep "not initialized" update.out &&
+       test_i18ngrep "not initialized" update.out &&
        ! test -d init/.git &&
 
        git submodule update --init init &&
index 7d7fde057b04e4615e32b0ddc71fd3b07968a129..30b429e7dcbcd56af21ded67128aa18901133275 100755 (executable)
@@ -128,7 +128,7 @@ test_expect_success 'typechanged submodule(submodule->blob), --cached' "
   < Add foo5
 
 EOF
-       test_cmp actual expected
+       test_i18ncmp actual expected
 "
 
 test_expect_success 'typechanged submodule(submodule->blob), --files' "
@@ -138,7 +138,7 @@ test_expect_success 'typechanged submodule(submodule->blob), --files' "
   > Add foo5
 
 EOF
-    test_cmp actual expected
+    test_i18ncmp actual expected
 "
 
 rm -rf sm1 &&
@@ -149,7 +149,7 @@ test_expect_success 'typechanged submodule(submodule->blob)' "
 * sm1 $head4(submodule)->$head5(blob):
 
 EOF
-    test_cmp actual expected
+    test_i18ncmp actual expected
 "
 
 rm -f sm1 &&
@@ -162,7 +162,7 @@ test_expect_success 'nonexistent commit' "
   Warn: sm1 doesn't contain commit $head4_full
 
 EOF
-    test_cmp actual expected
+    test_i18ncmp actual expected
 "
 
 commit_file
@@ -173,7 +173,7 @@ test_expect_success 'typechanged submodule(blob->submodule)' "
   > Add foo7
 
 EOF
-    test_cmp expected actual
+    test_i18ncmp expected actual
 "
 
 commit_file sm1 &&
@@ -228,7 +228,7 @@ EOF
 
 test_expect_success '--for-status' "
     git submodule summary --for-status HEAD^ >actual &&
-    test_cmp actual - <<EOF
+    test_i18ncmp actual - <<EOF
 # Submodule changes to be committed:
 #
 # * sm1 $head6...0000000:
index 4f16fcce2bfcb63f437fa6b495fdb5c4370fccc1..c679f36a4daeab3f114c9cf0c84c93549d28c680 100755 (executable)
@@ -90,7 +90,7 @@ test_expect_success 'submodule update does not fetch already present commits' '
        (cd super &&
          git submodule update > ../actual 2> ../actual.err
        ) &&
-       test_cmp expected actual &&
+       test_i18ncmp expected actual &&
        ! test -s actual.err
 '
 
@@ -298,4 +298,148 @@ test_expect_success 'submodule update ignores update=rebase config for new submo
        )
 '
 
+test_expect_success 'submodule update continues after checkout error' '
+       (cd super &&
+        git reset --hard HEAD &&
+        git submodule add ../submodule submodule2 &&
+        git submodule init &&
+        git commit -am "new_submodule" &&
+        (cd submodule2 &&
+         git rev-parse --max-count=1 HEAD > ../expect
+        ) &&
+        (cd submodule &&
+         test_commit "update_submodule" file
+        ) &&
+        (cd submodule2 &&
+         test_commit "update_submodule2" file
+        ) &&
+        git add submodule &&
+        git add submodule2 &&
+        git commit -m "two_new_submodule_commits" &&
+        (cd submodule &&
+         echo "" > file
+        ) &&
+        git checkout HEAD^ &&
+        test_must_fail git submodule update &&
+        (cd submodule2 &&
+         git rev-parse --max-count=1 HEAD > ../actual
+        ) &&
+        test_cmp expect actual
+       )
+'
+test_expect_success 'submodule update continues after recursive checkout error' '
+       (cd super &&
+        git reset --hard HEAD &&
+        git checkout master &&
+        git submodule update &&
+        (cd submodule &&
+         git submodule add ../submodule subsubmodule &&
+         git submodule init &&
+         git commit -m "new_subsubmodule"
+        ) &&
+        git add submodule &&
+        git commit -m "update_submodule" &&
+        (cd submodule &&
+         (cd subsubmodule &&
+          test_commit "update_subsubmodule" file
+         ) &&
+         git add subsubmodule &&
+         test_commit "update_submodule_again" file &&
+         (cd subsubmodule &&
+          test_commit "update_subsubmodule_again" file
+         ) &&
+         test_commit "update_submodule_again_again" file
+        ) &&
+        (cd submodule2 &&
+         git rev-parse --max-count=1 HEAD > ../expect &&
+         test_commit "update_submodule2_again" file
+        ) &&
+        git add submodule &&
+        git add submodule2 &&
+        git commit -m "new_commits" &&
+        git checkout HEAD^ &&
+        (cd submodule &&
+         git checkout HEAD^ &&
+         (cd subsubmodule &&
+          echo "" > file
+         )
+        ) &&
+        test_must_fail git submodule update --recursive &&
+        (cd submodule2 &&
+         git rev-parse --max-count=1 HEAD > ../actual
+        ) &&
+        test_cmp expect actual
+       )
+'
+
+test_expect_success 'submodule update exit immediately in case of merge conflict' '
+       (cd super &&
+        git checkout master &&
+        git reset --hard HEAD &&
+        (cd submodule &&
+         (cd subsubmodule &&
+          git reset --hard HEAD
+         )
+        ) &&
+        git submodule update --recursive &&
+        (cd submodule &&
+         test_commit "update_submodule_2" file
+        ) &&
+        (cd submodule2 &&
+         test_commit "update_submodule2_2" file
+        ) &&
+        git add submodule &&
+        git add submodule2 &&
+        git commit -m "two_new_submodule_commits" &&
+        (cd submodule &&
+         git checkout master &&
+         test_commit "conflict" file &&
+         echo "conflict" > file
+        ) &&
+        git checkout HEAD^ &&
+        (cd submodule2 &&
+         git rev-parse --max-count=1 HEAD > ../expect
+        ) &&
+        git config submodule.submodule.update merge &&
+        test_must_fail git submodule update &&
+        (cd submodule2 &&
+         git rev-parse --max-count=1 HEAD > ../actual
+        ) &&
+        test_cmp expect actual
+       )
+'
+test_expect_success 'submodule update exit immediately after recursive rebase error' '
+       (cd super &&
+        git checkout master &&
+        git reset --hard HEAD &&
+        (cd submodule &&
+         git reset --hard HEAD &&
+         git submodule update --recursive
+        ) &&
+        (cd submodule &&
+         test_commit "update_submodule_3" file
+        ) &&
+        (cd submodule2 &&
+         test_commit "update_submodule2_3" file
+        ) &&
+        git add submodule &&
+        git add submodule2 &&
+        git commit -m "two_new_submodule_commits" &&
+        (cd submodule &&
+         git checkout master &&
+         test_commit "conflict2" file &&
+         echo "conflict" > file
+        ) &&
+        git checkout HEAD^ &&
+        (cd submodule2 &&
+         git rev-parse --max-count=1 HEAD > ../expect
+        ) &&
+        git config submodule.submodule.update rebase &&
+        test_must_fail git submodule update &&
+        (cd submodule2 &&
+         git rev-parse --max-count=1 HEAD > ../actual
+        ) &&
+        test_cmp expect actual
+       )
+'
 test_done
index e5be13c271c92ce7c51601ee1eaf966d849d8ae4..ae3bd18a5ed167caa461f269cd77b6f34a6ae7b0 100755 (executable)
@@ -77,7 +77,7 @@ test_expect_success 'test basic "submodule foreach" usage' '
                git config foo.bar zar &&
                git submodule foreach "git config --file \"\$toplevel/.git/config\" foo.bar"
        ) &&
-       test_cmp expect actual
+       test_i18ncmp expect actual
 '
 
 test_expect_success 'setup nested submodules' '
@@ -158,7 +158,7 @@ test_expect_success 'test messages from "foreach --recursive"' '
                cd clone2 &&
                git submodule foreach --recursive "true" > ../actual
        ) &&
-       test_cmp expect actual
+       test_i18ncmp expect actual
 '
 
 cat > expect <<EOF
index 8528f64c8d1491fd3c279f030b6f8aee2050cdf7..ee7f0cd4596f982f16cbf3859675e6faba424faa 100755 (executable)
@@ -84,5 +84,38 @@ test_expect_success POSIXPERM '--no-verify with non-executable hook' '
        git commit --no-verify -m "more content"
 
 '
+chmod +x "$HOOK"
+
+# a hook that checks $GIT_PREFIX and succeeds inside the
+# success/ subdirectory only
+cat > "$HOOK" <<EOF
+#!/bin/sh
+test \$GIT_PREFIX = success/
+EOF
+
+test_expect_success 'with hook requiring GIT_PREFIX' '
+
+       echo "more content" >> file &&
+       git add file &&
+       mkdir success &&
+       (
+               cd success &&
+               git commit -m "hook requires GIT_PREFIX = success/"
+       ) &&
+       rmdir success
+'
+
+test_expect_success 'with failing hook requiring GIT_PREFIX' '
+
+       echo "more content" >> file &&
+       git add file &&
+       mkdir fail &&
+       (
+               cd fail &&
+               test_must_fail git commit -m "hook must fail"
+       ) &&
+       rmdir fail &&
+       git checkout -- file
+'
 
 test_done
index 1fdfbd38654a83771f677f2219403e3713abef51..905255adf0ca5b15d9befa772cda4a650bd15f34 100755 (executable)
@@ -131,6 +131,127 @@ test_expect_success 'status -s' '
 
 '
 
+test_expect_success 'status with gitignore' '
+       {
+               echo ".gitignore" &&
+               echo "expect" &&
+               echo "output" &&
+               echo "untracked"
+       } >.gitignore &&
+
+       cat >expect <<-\EOF &&
+        M dir1/modified
+       A  dir2/added
+       ?? dir2/modified
+       EOF
+       git status -s >output &&
+       test_cmp expect output &&
+
+       cat >expect <<-\EOF &&
+        M dir1/modified
+       A  dir2/added
+       ?? dir2/modified
+       !! .gitignore
+       !! dir1/untracked
+       !! dir2/untracked
+       !! expect
+       !! output
+       !! untracked
+       EOF
+       git status -s --ignored >output &&
+       test_cmp expect output &&
+
+       cat >expect <<-\EOF &&
+       # On branch master
+       # Changes to be committed:
+       #   (use "git reset HEAD <file>..." to unstage)
+       #
+       #       new file:   dir2/added
+       #
+       # Changes not staged for commit:
+       #   (use "git add <file>..." to update what will be committed)
+       #   (use "git checkout -- <file>..." to discard changes in working directory)
+       #
+       #       modified:   dir1/modified
+       #
+       # Untracked files:
+       #   (use "git add <file>..." to include in what will be committed)
+       #
+       #       dir2/modified
+       # Ignored files:
+       #   (use "git add -f <file>..." to include in what will be committed)
+       #
+       #       .gitignore
+       #       dir1/untracked
+       #       dir2/untracked
+       #       expect
+       #       output
+       #       untracked
+       EOF
+       git status --ignored >output &&
+       test_cmp expect output
+'
+
+test_expect_success 'status with gitignore (nothing untracked)' '
+       {
+               echo ".gitignore" &&
+               echo "expect" &&
+               echo "dir2/modified" &&
+               echo "output" &&
+               echo "untracked"
+       } >.gitignore &&
+
+       cat >expect <<-\EOF &&
+        M dir1/modified
+       A  dir2/added
+       EOF
+       git status -s >output &&
+       test_cmp expect output &&
+
+       cat >expect <<-\EOF &&
+        M dir1/modified
+       A  dir2/added
+       !! .gitignore
+       !! dir1/untracked
+       !! dir2/modified
+       !! dir2/untracked
+       !! expect
+       !! output
+       !! untracked
+       EOF
+       git status -s --ignored >output &&
+       test_cmp expect output &&
+
+       cat >expect <<-\EOF &&
+       # On branch master
+       # Changes to be committed:
+       #   (use "git reset HEAD <file>..." to unstage)
+       #
+       #       new file:   dir2/added
+       #
+       # Changes not staged for commit:
+       #   (use "git add <file>..." to update what will be committed)
+       #   (use "git checkout -- <file>..." to discard changes in working directory)
+       #
+       #       modified:   dir1/modified
+       #
+       # Ignored files:
+       #   (use "git add -f <file>..." to include in what will be committed)
+       #
+       #       .gitignore
+       #       dir1/untracked
+       #       dir2/modified
+       #       dir2/untracked
+       #       expect
+       #       output
+       #       untracked
+       EOF
+       git status --ignored >output &&
+       test_cmp expect output
+'
+
+rm -f .gitignore
+
 cat >expect <<\EOF
 ## master
  M dir1/modified
index 69bd576d1c1a0bfcd086b68d8dc07c03c22b7a5a..a29ae45b399f89123f886f5b35580bcdc0e803f1 100755 (executable)
@@ -658,9 +658,9 @@ test_expect_success LIBPCRE 'grep -P -v pattern' '
 '
 
 test_expect_success LIBPCRE 'grep -P -i pattern' '
-       {
-               echo "hello.c:  printf(\"Hello world.\n\");"
-       } >expected &&
+       cat >expected <<-EOF &&
+       hello.c:        printf("Hello world.\n");
+       EOF
        git grep -P -i "PRINTF\([^\d]+\)" hello.c >actual &&
        test_cmp expected actual
 '
@@ -716,4 +716,99 @@ test_expect_success LIBPCRE 'grep -G -F -E -P pattern' '
        test_cmp expected actual
 '
 
+test_config() {
+       git config "$1" "$2" &&
+       test_when_finished "git config --unset $1"
+}
+
+cat >expected <<EOF
+hello.c<RED>:<RESET>int main(int argc, const char **argv)
+hello.c<RED>-<RESET>{
+<RED>--<RESET>
+hello.c<RED>:<RESET>   /* char ?? */
+hello.c<RED>-<RESET>}
+<RED>--<RESET>
+hello_world<RED>:<RESET>Hello_world
+hello_world<RED>-<RESET>HeLLo_world
+EOF
+
+test_expect_success 'grep --color, separator' '
+       test_config color.grep.context          normal &&
+       test_config color.grep.filename         normal &&
+       test_config color.grep.function         normal &&
+       test_config color.grep.linenumber       normal &&
+       test_config color.grep.match            normal &&
+       test_config color.grep.selected         normal &&
+       test_config color.grep.separator        red &&
+
+       git grep --color=always -A1 -e char -e lo_w hello.c hello_world |
+       test_decode_color >actual &&
+       test_cmp expected actual
+'
+
+cat >expected <<EOF
+hello.c:int main(int argc, const char **argv)
+hello.c:       /* char ?? */
+
+hello_world:Hello_world
+EOF
+
+test_expect_success 'grep --break' '
+       git grep --break -e char -e lo_w hello.c hello_world >actual &&
+       test_cmp expected actual
+'
+
+cat >expected <<EOF
+hello.c:int main(int argc, const char **argv)
+hello.c-{
+--
+hello.c:       /* char ?? */
+hello.c-}
+
+hello_world:Hello_world
+hello_world-HeLLo_world
+EOF
+
+test_expect_success 'grep --break with context' '
+       git grep --break -A1 -e char -e lo_w hello.c hello_world >actual &&
+       test_cmp expected actual
+'
+
+cat >expected <<EOF
+hello.c
+int main(int argc, const char **argv)
+       /* char ?? */
+hello_world
+Hello_world
+EOF
+
+test_expect_success 'grep --heading' '
+       git grep --heading -e char -e lo_w hello.c hello_world >actual &&
+       test_cmp expected actual
+'
+
+cat >expected <<EOF
+<BOLD;GREEN>hello.c<RESET>
+2:int main(int argc, const <BLACK;BYELLOW>char<RESET> **argv)
+6:     /* <BLACK;BYELLOW>char<RESET> ?? */
+
+<BOLD;GREEN>hello_world<RESET>
+3:Hel<BLACK;BYELLOW>lo_w<RESET>orld
+EOF
+
+test_expect_success 'mimic ack-grep --group' '
+       test_config color.grep.context          normal &&
+       test_config color.grep.filename         "bold green" &&
+       test_config color.grep.function         normal &&
+       test_config color.grep.linenumber       normal &&
+       test_config color.grep.match            "black yellow" &&
+       test_config color.grep.selected         normal &&
+       test_config color.grep.separator        normal &&
+
+       git grep --break --heading -n --color \
+               -e char -e lo_w hello.c hello_world |
+       test_decode_color >actual &&
+       test_cmp expected actual
+'
+
 test_done
diff --git a/t/t9159-git-svn-no-parent-mergeinfo.sh b/t/t9159-git-svn-no-parent-mergeinfo.sh
new file mode 100755 (executable)
index 0000000..85120b7
--- /dev/null
@@ -0,0 +1,33 @@
+#!/bin/sh
+test_description='git svn handling of root commits in merge ranges'
+. ./lib-git-svn.sh
+
+test_expect_success 'test handling of root commits in merge ranges' '
+       mkdir -p init/trunk init/branches init/tags &&
+       echo "r1" > init/trunk/file.txt &&
+       svn_cmd import -m "initial import" init "$svnrepo" &&
+       svn_cmd co "$svnrepo" tmp &&
+       (
+               cd tmp &&
+               echo "r2" > trunk/file.txt &&
+               svn_cmd commit -m "Modify file.txt on trunk" &&
+               svn_cmd cp trunk@1 branches/a &&
+               svn_cmd commit -m "Create branch a from trunk r1" &&
+               svn_cmd propset svn:mergeinfo /trunk:1-2 branches/a &&
+               svn_cmd commit -m "Fake merge of trunk r2 into branch a" &&
+               mkdir branches/b &&
+               echo "r5" > branches/b/file2.txt &&
+               svn_cmd add branches/b &&
+               svn_cmd commit -m "Create branch b from thin air" &&
+               echo "r6" > branches/b/file2.txt &&
+               svn_cmd commit -m "Modify file2.txt on branch b" &&
+               svn_cmd cp branches/b@5 branches/c &&
+               svn_cmd commit -m "Create branch c from branch b r5" &&
+               svn_cmd propset svn:mergeinfo /branches/b:5-6 branches/c &&
+               svn_cmd commit -m "Fake merge of branch b r6 into branch c"
+       ) &&
+       git svn init -s "$svnrepo" &&
+       git svn fetch
+       '
+
+test_done
index 6b1ba6c858562b7c085951fb1d69d4d2371e866c..2a53640c5b29c19a2327570148638dbf94bc2afa 100755 (executable)
@@ -1893,7 +1893,7 @@ test_expect_success \
     test_cmp marks.out marks.new'
 
 cat >input <<EOF
-feature import-marks=nonexistant.marks
+feature import-marks=nonexistent.marks
 feature export-marks=marks.new
 EOF
 
@@ -1904,7 +1904,7 @@ test_expect_success \
 
 
 cat >input <<EOF
-feature import-marks=nonexistant.marks
+feature import-marks=nonexistent.marks
 feature export-marks=combined.marks
 EOF
 
index 64390d716d68b79b5a0915a707c95f5c2df39a46..df25f1792923a65aab2c4ce88e7b528effb381f5 100644 (file)
@@ -446,9 +446,14 @@ test_debug () {
 
 test_run_ () {
        test_cleanup=:
+       expecting_failure=$2
        eval >&3 2>&4 "$1"
        eval_ret=$?
-       eval >&3 2>&4 "$test_cleanup"
+
+       if test -z "$immediate" || test $eval_ret = 0 || test -n "$expecting_failure"
+       then
+               eval >&3 2>&4 "$test_cleanup"
+       fi
        if test "$verbose" = "t" && test -n "$HARNESS_ACTIVE"; then
                echo ""
        fi
@@ -497,7 +502,7 @@ test_expect_failure () {
        if ! test_skip "$@"
        then
                say >&3 "checking known breakage: $2"
-               test_run_ "$2"
+               test_run_ "$2" expecting_failure
                if [ "$?" = 0 -a "$eval_ret" = 0 ]
                then
                        test_known_broken_ok_ "$1"
@@ -774,6 +779,9 @@ test_cmp() {
 #
 # except that the greeting and config --unset must both succeed for
 # the test to pass.
+#
+# Note that under --immediate mode, no clean-up is done to help diagnose
+# what went wrong.
 
 test_when_finished () {
        test_cleanup="{ $*
@@ -884,8 +892,13 @@ then
        }
 
        make_valgrind_symlink () {
-               # handle only executables
-               test -x "$1" || return
+               # handle only executables, unless they are shell libraries that
+               # need to be in the exec-path.  We will just use "#!" as a
+               # guess for a shell-script, since we have no idea what the user
+               # may have configured as the shell path.
+               test -x "$1" ||
+               test "#!" = "$(head -c 2 <"$1")" ||
+               return;
 
                base=$(basename "$1")
                symlink_target=$GIT_BUILD_DIR/$base
index 07f83642443601d107e0a2425407b3250c022dcd..3a61d821ee85a9cfb026909c6ae90def360a7681 100644 (file)
@@ -593,7 +593,7 @@ static int unpack_nondirectories(int n, unsigned long mask,
 static int unpack_failed(struct unpack_trees_options *o, const char *message)
 {
        discard_index(&o->result);
-       if (!o->gently) {
+       if (!o->gently && !o->exiting_early) {
                if (message)
                        return error("%s", message);
                return -1;
@@ -1133,6 +1133,8 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
                display_error_msgs(o);
        mark_all_ce_unused(o->src_index);
        ret = unpack_failed(o, NULL);
+       if (o->exiting_early)
+               ret = 0;
        goto done;
 }
 
index 64f02cb03ab242ac08ea0f1afbb24e71cf6664aa..79989483079970e9dad42512e522eef8ea2a75a4 100644 (file)
@@ -46,6 +46,7 @@ struct unpack_trees_options {
                     debug_unpack,
                     skip_sparse_checkout,
                     gently,
+                    exiting_early,
                     show_all_errors,
                     dry_run;
        const char *prefix;
index e55310cd02c8e7d1f0404b2fee9539e7340993d9..01d3a8b81e628b0a088e3e9fc0ca59acf7d9c58d 100644 (file)
@@ -281,3 +281,20 @@ struct userdiff_driver *userdiff_find_by_path(const char *path)
                return NULL;
        return userdiff_find_by_name(check.value);
 }
+
+struct userdiff_driver *userdiff_get_textconv(struct userdiff_driver *driver)
+{
+       if (!driver->textconv)
+               return NULL;
+
+       if (driver->textconv_want_cache && !driver->textconv_cache) {
+               struct notes_cache *c = xmalloc(sizeof(*c));
+               struct strbuf name = STRBUF_INIT;
+
+               strbuf_addf(&name, "textconv/%s", driver->name);
+               notes_cache_init(c, name.buf, driver->textconv);
+               driver->textconv_cache = c;
+       }
+
+       return driver;
+}
index 942d5949501027fb5d30e0d59629aab38fd2669a..4a7e78ffbcc6d552a39dcccd9008d6c11919e432 100644 (file)
@@ -23,4 +23,6 @@ int userdiff_config(const char *k, const char *v);
 struct userdiff_driver *userdiff_find_by_name(const char *name);
 struct userdiff_driver *userdiff_find_by_path(const char *path);
 
+struct userdiff_driver *userdiff_get_textconv(struct userdiff_driver *driver);
+
 #endif /* USERDIFF */
index 9f4e0ba9c17120ca2903b30b95b6d8fbcc62cd9c..02377729c401fa6e041a713a89fe986fa0f562a0 100644 (file)
@@ -642,7 +642,7 @@ static void wt_status_print_other(struct wt_status *s,
        int i;
        struct strbuf buf = STRBUF_INIT;
 
-       if (!s->untracked.nr)
+       if (!l->nr)
                return;
 
        wt_status_print_other_header(s, what, how);