Code

Merge branch 'jk/archive-tar-filter'
authorJunio C Hamano <gitster@pobox.com>
Tue, 19 Jul 2011 16:45:32 +0000 (09:45 -0700)
committerJunio C Hamano <gitster@pobox.com>
Tue, 19 Jul 2011 16:45:32 +0000 (09:45 -0700)
* jk/archive-tar-filter:
  upload-archive: allow user to turn off filters
  archive: provide builtin .tar.gz filter
  archive: implement configurable tar filters
  archive: refactor file extension format-guessing
  archive: move file extension format-guessing lower
  archive: pass archiver struct to write_archive callback
  archive: refactor list of archive formats
  archive-tar: don't reload default config options
  archive: reorder option parsing and config reading

134 files changed:
.gitignore
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-check-ref-format.txt
Documentation/git-clone.txt
Documentation/git-commit.txt
Documentation/git-grep.txt
Documentation/git-instaweb.txt
Documentation/git-mergetool--lib.txt
Documentation/git-remote.txt
Documentation/git-revert.txt
Documentation/git-sh-i18n--envsubst.txt
Documentation/git-sh-i18n.txt
Documentation/git-status.txt
Documentation/git-submodule.txt
Documentation/git-tag.txt
Documentation/git-web--browse.txt
Documentation/git.txt
Documentation/glossary-content.txt
Documentation/rev-list-options.txt
Documentation/technical/pack-protocol.txt
GIT-VERSION-GEN
INSTALL
Makefile
RelNotes
abspath.c
archive-zip.c
builtin/apply.c
builtin/branch.c
builtin/cat-file.c
builtin/checkout.c
builtin/clone.c
builtin/diff.c
builtin/gc.c
builtin/grep.c
builtin/index-pack.c
builtin/pack-objects.c
builtin/remote.c
builtin/tag.c
builtin/unpack-objects.c
builtin/update-index.c
builtin/verify-pack.c
cache.h
combine-diff.c
compat/cygwin.c
compat/mingw.c
compat/mingw.h
config.c
contrib/completion/git-completion.bash
convert.c
convert.h [new file with mode: 0644]
diff-lib.c
diff.c
diff.h
entry.c
fast-import.c
git-am.sh
git-bisect.sh
git-compat-util.h
git-instaweb.sh
git-mergetool--lib.sh
git-pull.sh
git-rebase--interactive.sh
git-rebase.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
help.c
http-backend.c
http-push.c
http.c
http.h
pack-check.c
parse-options.c
parse-options.h
read-cache.c
refs.c
refs.h
remote-curl.c
setup.c
sh-i18n--envsubst.c
sha1_file.c
strbuf.c
strbuf.h
streaming.c [new file with mode: 0644]
streaming.h [new file with mode: 0644]
submodule.c
t/Makefile
t/gitweb-lib.sh
t/t0021-conversion.sh
t/t0040-parse-options.sh
t/t1020-subdirectory.sh
t/t1300-repo-config.sh
t/t2018-checkout-branch.sh
t/t3404-rebase-interactive.sh
t/t3409-rebase-preserve-merges.sh
t/t3411-rebase-preserve-around-merges.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/t5526-fetch-submodules.sh
t/t5708-clone-config.sh [new file with mode: 0755]
t/t7004-tag.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
test-parse-options.c
unpack-trees.c
unpack-trees.h
userdiff.c
userdiff.h
wt-status.c
zlib.c

index acffdfaae684dc49d7205d580fd8921ca4696f7f..8572c8c0b0199589a8c1875825f5b2e7e4dc4a86 100644 (file)
@@ -1,5 +1,6 @@
 /GIT-BUILD-OPTIONS
 /GIT-CFLAGS
+/GIT-LDFLAGS
 /GIT-GUI-VARS
 /GIT-VERSION-FILE
 /bin-wrappers/
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..b56959b5dce4d9cec9e9eb5a9aa7894ab6421b52 100644 (file)
@@ -676,7 +676,7 @@ branch.<name>.rebase::
 browser.<tool>.cmd::
        Specify the command to invoke the specified browser. The
        specified command is evaluated in shell with the URLs passed
-       as arguments. (See linkgit:git-web--browse[1].)
+       as arguments. (See linkgit:git-web{litdd}browse[1].)
 
 browser.<tool>.path::
        Override the path for the given tool that may be used to
@@ -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 205d83dd0b281d59e0f53284d29846c8eb58a246..c9fdf84a08847bd8cb8c9ce1e2f50241975e267c 100644 (file)
@@ -18,9 +18,12 @@ Checks if a given 'refname' is acceptable, and exits with a non-zero
 status if it is not.
 
 A reference is used in git to specify branches and tags.  A
-branch head is stored under the `$GIT_DIR/refs/heads` directory, and
-a tag is stored under the `$GIT_DIR/refs/tags` directory (or, if refs
-are packed by `git gc`, as entries in the `$GIT_DIR/packed-refs` file).
+branch head is stored in the `refs/heads` hierarchy, while
+a tag is stored in the `refs/tags` hierarchy of the ref namespace
+(typically in `$GIT_DIR/refs/heads` and `$GIT_DIR/refs/tags`
+directories or, as entries in file `$GIT_DIR/packed-refs`
+if refs are packed by `git gc`).
+
 git imposes the following rules on how references are named:
 
 . They can include slash `/` for hierarchical (directory)
index b093e45497248076c335391f3e95b6384520ef6b..4b8b26b75e63cc56e679d2e2c6d8fd1240010419 100644 (file)
@@ -159,6 +159,17 @@ objects from the source repository into a pack in the cloned repository.
        Specify the directory from which templates will be used;
        (See the "TEMPLATE DIRECTORY" section of linkgit:git-init[1].)
 
+--config <key>=<value>::
+-c <key>=<value>::
+       Set a configuration variable in the newly-created repository;
+       this takes effect immediately after the repository is
+       initialized, but before the remote history is fetched or any
+       files checked out.  The key is in the same format as expected by
+       linkgit:git-config[1] (e.g., `core.eol=true`). If multiple
+       values are given for the same key, each value will be written to
+       the config file. This makes it safe, for example, to add
+       additional fetch refspecs to the origin remote.
+
 --depth <depth>::
        Create a 'shallow' clone with a history truncated to the
        specified number of revisions.  A shallow repository has a
index 7951cb7b005bf472c449a563d073036ceb8b921a..5cc84a139133dca2fdcb594007c8b0d6464d5ca8 100644 (file)
@@ -284,7 +284,7 @@ When recording your own work, the contents of modified files in
 your working tree are temporarily stored to a staging area
 called the "index" with 'git add'.  A file can be
 reverted back, only in the index but not in the working tree,
-to that of the last commit with `git reset HEAD -- <file>`,
+to that of the last commit with `git reset HEAD \-- <file>`,
 which effectively reverts 'git add' and prevents the changes to
 this file from participating in the next commit.  After building
 the state to be committed incrementally with these commands,
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 63ededec1d7099bb2156461895f32dcf008dedcc..5b0d51f7f8cfc52739463dd90827fe5d0dc1b58c 100644 (file)
@@ -7,7 +7,7 @@ git-mergetool--lib - Common git merge tool shell scriptlets
 
 SYNOPSIS
 --------
-'TOOL_MODE=(diff|merge) . "$(git --exec-path)/git-mergetool--lib"'
+'TOOL_MODE=(diff|merge) . "$(git --exec-path)/git-mergetool{litdd}lib"'
 
 DESCRIPTION
 -----------
index 528f34a13106836c1eace8c5d8972543b6fcc121..5a8c5061f3701c57bab75b2b4c70ad620f7e536f 100644 (file)
@@ -60,11 +60,11 @@ the remote repository.
 +
 With `-t <branch>` option, instead of the default glob
 refspec for the remote to track all branches under
-`$GIT_DIR/remotes/<name>/`, a refspec to track only `<branch>`
+the `refs/remotes/<name>/` namespace, a refspec to track only `<branch>`
 is created.  You can give more than one `-t <branch>` to track
 multiple branches without grabbing all branches.
 +
-With `-m <master>` option, `$GIT_DIR/remotes/<name>/HEAD` is set
+With `-m <master>` option, a symbolic-ref `refs/remotes/<name>/HEAD` is set
 up to point at remote's `<master>` branch. See also the set-head command.
 +
 When a fetch mirror is created with `\--mirror=fetch`, the refs will not
@@ -92,24 +92,25 @@ configuration settings for the remote are removed.
 
 'set-head'::
 
-Sets or deletes the default branch (`$GIT_DIR/remotes/<name>/HEAD`) for
+Sets or deletes the default branch (i.e. the target of the
+symbolic-ref `refs/remotes/<name>/HEAD`) for
 the named remote. Having a default branch for a remote is not required,
 but allows the name of the remote to be specified in lieu of a specific
 branch. For example, if the default branch for `origin` is set to
 `master`, then `origin` may be specified wherever you would normally
 specify `origin/master`.
 +
-With `-d`, `$GIT_DIR/remotes/<name>/HEAD` is deleted.
+With `-d`, the symbolic ref `refs/remotes/<name>/HEAD` is deleted.
 +
-With `-a`, the remote is queried to determine its `HEAD`, then
-`$GIT_DIR/remotes/<name>/HEAD` is set to the same branch. e.g., if the remote
+With `-a`, the remote is queried to determine its `HEAD`, then the
+symbolic-ref `refs/remotes/<name>/HEAD` is set to the same branch. e.g., if the remote
 `HEAD` is pointed at `next`, "`git remote set-head origin -a`" will set
-`$GIT_DIR/refs/remotes/origin/HEAD` to `refs/remotes/origin/next`. This will
+the symbolic-ref `refs/remotes/origin/HEAD` to `refs/remotes/origin/next`. This will
 only work if `refs/remotes/origin/next` already exists; if not it must be
 fetched first.
 +
-Use `<branch>` to set `$GIT_DIR/remotes/<name>/HEAD` explicitly. e.g., "git
-remote set-head origin master" will set `$GIT_DIR/refs/remotes/origin/HEAD` to
+Use `<branch>` to set the symbolic-ref `refs/remotes/<name>/HEAD` explicitly. e.g., "git
+remote set-head origin master" will set the symbolic-ref `refs/remotes/origin/HEAD` to
 `refs/remotes/origin/master`. This will only work if
 `refs/remotes/origin/master` already exists; if not it must be fetched first.
 +
index ac10cfbb14aba460973019d712ee90e8804de34d..6a21b37f95986ad04140be4cbef3ba011cbe5220 100644 (file)
@@ -23,7 +23,7 @@ throw away all uncommitted changes in your working directory, you
 should see linkgit:git-reset[1], particularly the '--hard' option.  If
 you want to extract specific files as they were in another commit, you
 should see linkgit:git-checkout[1], specifically the `git checkout
-<commit> -- <filename>` syntax.  Take care with these alternatives as
+<commit> \-- <filename>` syntax.  Take care with these alternatives as
 both will discard uncommitted changes in your working directory.
 
 OPTIONS
index 61e4c08dacf796853284ee03c3ccdbebddb51a9b..5c3ec327bbc5836c53b0b94f9dc0de4772661e36 100644 (file)
@@ -1,5 +1,5 @@
-git-sh-i18n--envsubst(1)
-========================
+git-sh-i18n{litdd}envsubst(1)
+=============================
 
 NAME
 ----
@@ -10,8 +10,8 @@ SYNOPSIS
 [verse]
 eval_gettext () {
        printf "%s" "$1" | (
-               export PATH $('git sh-i18n--envsubst' --variables "$1");
-               'git sh-i18n--envsubst' "$1"
+               export PATH $('git sh-i18n{litdd}envsubst' --variables "$1");
+               'git sh-i18n{litdd}envsubst' "$1"
        )
 }
 
@@ -22,7 +22,7 @@ This is not a command the end user would want to run.  Ever.
 This documentation is meant for people who are studying the
 plumbing scripts and/or are writing new ones.
 
-git-sh-i18n--envsubst is Git's stripped-down copy of the GNU
+'git sh-i18n{litdd}envsubst' is Git's stripped-down copy of the GNU
 `envsubst(1)` program that comes with the GNU gettext package. It's
 used internally by linkgit:git-sh-i18n[1] to interpolate the variables
 passed to the the `eval_gettext` function.
index 3b1f7ac7b5d82ed6fd8083342f441fa1204aff4b..eca69e3d82be1a54770c5efba5362410cd0cf43c 100644 (file)
@@ -34,7 +34,7 @@ gettext::
 eval_gettext::
        Currently a dummy fall-through function implemented as a wrapper
        around `printf(1)` with variables expanded by the
-       linkgit:git-sh-i18n--envsubst[1] helper. Will be replaced by a
+       linkgit:git-sh-i18n{litdd}envsubst[1] helper. Will be replaced by a
        real gettext implementation in a later version.
 
 GIT
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 5e7a4130eeec48c27abf92b37834613692446723..12af4de5e1e156d01c5953b1023867b9d29ba848 100644 (file)
@@ -172,7 +172,7 @@ sync::
        repositories accordingly.
 +
 "git submodule sync" synchronizes all submodules while
-"git submodule sync -- A" synchronizes submodule "A" only.
+"git submodule sync \-- A" synchronizes submodule "A" only.
 
 OPTIONS
 -------
index d82f62120a21059a31d9c708c39d93b2a630d590..fb1c0ac694bdac20e2fef74710df35c1c4b1e774 100644 (file)
@@ -12,7 +12,7 @@ SYNOPSIS
 'git tag' [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>]
        <tagname> [<commit> | <object>]
 'git tag' -d <tagname>...
-'git tag' [-n[<num>]] -l [--contains <commit>] [<pattern>]
+'git tag' [-n[<num>]] -l [--contains <commit>] [<pattern>...]
 'git tag' -v <tagname>...
 
 DESCRIPTION
@@ -69,8 +69,11 @@ OPTIONS
        If the tag is not annotated, the commit message is displayed instead.
 
 -l <pattern>::
-       List tags with names that match the given pattern (or all if no pattern is given).
-       Typing "git tag" without arguments, also lists all tags.
+       List tags with names that match the given pattern (or all if no
+       pattern is given).  Running "git tag" without arguments also
+       lists all tags. The pattern is a shell wildcard (i.e., matched
+       using fnmatch(3)).  Multiple patterns may be given; if any of
+       them matches, the tag is shown.
 
 --contains <commit>::
        Only list tags which contain the specified commit.
index 69d92fa00ef91dff0346925ba1c50857132e9c03..9b0d1fe395df42d18ee47bb2c1c2a207639c85b8 100644 (file)
@@ -68,7 +68,7 @@ browser.<tool>.path
 You can explicitly provide a full path to your preferred browser by
 setting the configuration variable 'browser.<tool>.path'. For example,
 you can configure the absolute path to firefox by setting
-'browser.firefox.path'. Otherwise, 'git web--browse' assumes the tool
+'browser.firefox.path'. Otherwise, 'git web{litdd}browse' assumes the tool
 is available in PATH.
 
 browser.<tool>.cmd
index 5c45446a4ac955630c8709e2184e52163185b8d4..0172cd70149914897862fbf0f3f221c7ef31ea5c 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
@@ -518,16 +523,15 @@ Any git command accepting any <object> can also use the following
 symbolic notation:
 
 HEAD::
-       indicates the head of the current branch (i.e. the
-       contents of `$GIT_DIR/HEAD`).
+       indicates the head of the current branch.
 
 <tag>::
        a valid tag 'name'
-       (i.e. the contents of `$GIT_DIR/refs/tags/<tag>`).
+       (i.e. a `refs/tags/<tag>` reference).
 
 <head>::
        a valid head 'name'
-       (i.e. the contents of `$GIT_DIR/refs/heads/<head>`).
+       (i.e. a `refs/heads/<head>` reference).
 
 For a more complete list of ways to spell object names, see
 "SPECIFYING REVISIONS" section in linkgit:gitrevisions[7].
index 8f62d1abeeb9b80fd36aa8ea12120c640a98414e..3595b586bc35d865a08ab538a1908cb2abe8e1a8 100644 (file)
@@ -161,8 +161,8 @@ to point at the new commit.
 
 [[def_head]]head::
        A <<def_ref,named reference>> to the <<def_commit,commit>> at the tip of a
-       <<def_branch,branch>>.  Heads are stored in
-       `$GIT_DIR/refs/heads/`, except when using packed refs. (See
+       <<def_branch,branch>>.  Heads are stored in a file in
+       `$GIT_DIR/refs/heads/` directory, except when using packed refs. (See
        linkgit:git-pack-refs[1].)
 
 [[def_HEAD]]HEAD::
@@ -170,8 +170,8 @@ to point at the new commit.
        working tree>> is normally derived from the state of the tree
        referred to by HEAD.  HEAD is a reference to one of the
        <<def_head,heads>> in your repository, except when using a
-       <<def_detached_HEAD,detached HEAD>>, in which case it may
-       reference an arbitrary commit.
+       <<def_detached_HEAD,detached HEAD>>, in which case it directly
+       references an arbitrary commit.
 
 [[def_head_ref]]head ref::
        A synonym for <<def_head,head>>.
@@ -382,8 +382,9 @@ should not be combined with other pathspec.
 
 [[def_ref]]ref::
        A 40-byte hex representation of a <<def_SHA1,SHA1>> or a name that
-       denotes a particular <<def_object,object>>. These may be stored in
-       `$GIT_DIR/refs/`.
+       denotes a particular <<def_object,object>>. They may be stored in
+       a file under `$GIT_DIR/refs/` directory, or
+       in the `$GIT_DIR/packed-refs` file.
 
 [[def_reflog]]reflog::
        A reflog shows the local "history" of a ref.  In other words,
@@ -459,14 +460,14 @@ should not be combined with other pathspec.
        command.
 
 [[def_tag]]tag::
-       A <<def_ref,ref>> pointing to a <<def_tag_object,tag>> or
-       <<def_commit_object,commit object>>. In contrast to a <<def_head,head>>,
-       a tag is not changed by a <<def_commit,commit>>. Tags (not
-       <<def_tag_object,tag objects>>) are stored in `$GIT_DIR/refs/tags/`. A
-       git tag has nothing to do with a Lisp tag (which would be
-       called an <<def_object_type,object type>> in git's context). A
-       tag is most typically used to mark a particular point in the
-       commit ancestry <<def_chain,chain>>.
+       A <<def_ref,ref>> under `refs/tags/` namespace that points to an
+       object of an arbitrary type (typically a tag points to either a
+       <<def_tag_object,tag>> or a <<def_commit_object,commit object>>).
+       In contrast to a <<def_head,head>>, a tag is not updated by
+       the `commit` command. A git tag has nothing to do with a Lisp
+       tag (which would be called an <<def_object_type,object type>>
+       in git's context). A tag is most typically used to mark a particular
+       point in the commit ancestry <<def_chain,chain>>.
 
 [[def_tag_object]]tag object::
        An <<def_object,object>> containing a <<def_ref,ref>> pointing to
index 7e7ba68781f98b55f4189fe0fb4dd6e96c47c0af..554ab8c88bdef6bb64ba9496295b563083657077 100644 (file)
@@ -313,7 +313,7 @@ that you are filtering for a file `foo` in this commit graph:
         \   /   /   /   /
          `-------------'
 -----------------------------------------------------------------------
-The horizontal line of history A--P is taken to be the first parent of
+The horizontal line of history A---P is taken to be the first parent of
 each merge.  The commits are:
 
 * `I` is the initial commit, in which `foo` exists with contents
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='
 '
diff --git a/INSTALL b/INSTALL
index 16e45f114f18d5d6964ab9fa39eee617dfa59bfc..bbb9d4dc9ab37a242d8accb86fa8a0d35d677ab8 100644 (file)
--- a/INSTALL
+++ b/INSTALL
@@ -25,6 +25,19 @@ set up install paths (via config.mak.autogen), so you can write instead
        $ make all doc ;# as yourself
        # make install install-doc install-html;# as root
 
+If you're willing to trade off (much) longer build time for a later
+faster git you can also do a profile feedback build with
+
+       $ make profile-all
+       # make prefix=... install
+
+This will run the complete test suite as training workload and then
+rebuild git with the generated profile feedback. This results in a git
+which is a few percent faster on CPU intensive workloads.  This
+may be a good tradeoff for distribution packagers.
+
+Note that the profile feedback build stage currently generates
+a lot of additional compiler warnings.
 
 Issues of note:
 
index e40ac0c7f5ec2f304b88d92f47ff94272f5ce2a4..4ed7996f7bcc3a044b0da54e8319fc0339ff009d 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -153,6 +153,9 @@ all::
 # that tells runtime paths to dynamic libraries;
 # "-Wl,-rpath=/path/lib" is used instead.
 #
+# Define NO_NORETURN if using buggy versions of gcc 4.6+ and profile feedback,
+# as the compiler can crash (http://gcc.gnu.org/bugzilla/show_bug.cgi?id=49299)
+#
 # Define USE_NSEC below if you want git to care about sub-second file mtimes
 # and ctimes. Note that you need recent glibc (at least 2.2.4) for this, and
 # it will BREAK YOUR LOCAL DIFFS! show-diff and anything using it will likely
@@ -556,6 +559,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 +666,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 +1129,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 +1139,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)))
@@ -1374,6 +1381,9 @@ endif
 ifdef USE_ST_TIMESPEC
        BASIC_CFLAGS += -DUSE_ST_TIMESPEC
 endif
+ifdef NO_NORETURN
+       BASIC_CFLAGS += -DNO_NORETURN
+endif
 ifdef NO_NSEC
        BASIC_CFLAGS += -DNO_NSEC
 endif
@@ -1706,7 +1716,7 @@ git.sp git.s git.o: EXTRA_CPPFLAGS = -DGIT_VERSION='"$(GIT_VERSION)"' \
        '-DGIT_MAN_PATH="$(mandir_SQ)"' \
        '-DGIT_INFO_PATH="$(infodir_SQ)"'
 
-git$X: git.o $(BUILTIN_OBJS) $(GITLIBS)
+git$X: git.o GIT-LDFLAGS $(BUILTIN_OBJS) $(GITLIBS)
        $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ git.o \
                $(BUILTIN_OBJS) $(ALL_LDFLAGS) $(LIBS)
 
@@ -2004,17 +2014,17 @@ compat/nedmalloc/nedmalloc.sp compat/nedmalloc/nedmalloc.o: EXTRA_CPPFLAGS = \
        -DNDEBUG -DOVERRIDE_STRDUP -DREPLACE_SYSTEM_ALLOCATOR
 endif
 
-git-%$X: %.o $(GITLIBS)
+git-%$X: %.o GIT-LDFLAGS $(GITLIBS)
        $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS)
 
-git-imap-send$X: imap-send.o $(GITLIBS)
+git-imap-send$X: imap-send.o GIT-LDFLAGS $(GITLIBS)
        $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
                $(LIBS) $(OPENSSL_LINK) $(OPENSSL_LIBSSL) $(LIB_4_CRYPTO)
 
-git-http-fetch$X: revision.o http.o http-walker.o http-fetch.o $(GITLIBS)
+git-http-fetch$X: revision.o http.o http-walker.o http-fetch.o GIT-LDFLAGS $(GITLIBS)
        $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
                $(LIBS) $(CURL_LIBCURL)
-git-http-push$X: revision.o http.o http-push.o $(GITLIBS)
+git-http-push$X: revision.o http.o http-push.o GIT-LDFLAGS $(GITLIBS)
        $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
                $(LIBS) $(CURL_LIBCURL) $(EXPAT_LIBEXPAT)
 
@@ -2024,7 +2034,7 @@ $(REMOTE_CURL_ALIASES): $(REMOTE_CURL_PRIMARY)
        ln -s $< $@ 2>/dev/null || \
        cp $< $@
 
-$(REMOTE_CURL_PRIMARY): remote-curl.o http.o http-walker.o $(GITLIBS)
+$(REMOTE_CURL_PRIMARY): remote-curl.o http.o http-walker.o GIT-LDFLAGS $(GITLIBS)
        $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
                $(LIBS) $(CURL_LIBCURL) $(EXPAT_LIBEXPAT)
 
@@ -2094,6 +2104,15 @@ GIT-CFLAGS: FORCE
                echo "$$FLAGS" >GIT-CFLAGS; \
             fi
 
+TRACK_LDFLAGS = $(subst ','\'',$(ALL_LDFLAGS))
+
+GIT-LDFLAGS: FORCE
+       @FLAGS='$(TRACK_LDFLAGS)'; \
+           if test x"$$FLAGS" != x"`cat GIT-LDFLAGS 2>/dev/null`" ; then \
+               echo 1>&2 "    * new link flags"; \
+               echo "$$FLAGS" >GIT-LDFLAGS; \
+            fi
+
 # We need to apply sq twice, once to protect from the shell
 # that runs GIT-BUILD-OPTIONS, and then again to protect it
 # and the first level quoting from the shell that runs "echo".
@@ -2165,7 +2184,7 @@ test-svn-fe$X: vcs-svn/lib.a
 
 .PRECIOUS: $(TEST_OBJS)
 
-test-%$X: test-%.o $(GITLIBS)
+test-%$X: test-%.o GIT-LDFLAGS $(GITLIBS)
        $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(filter %.a,$^) $(LIBS)
 
 check-sha1:: test-sha1$X
@@ -2375,7 +2394,7 @@ ifndef NO_TCLTK
        $(MAKE) -C gitk-git clean
        $(MAKE) -C git-gui clean
 endif
-       $(RM) GIT-VERSION-FILE GIT-CFLAGS GIT-GUI-VARS GIT-BUILD-OPTIONS
+       $(RM) GIT-VERSION-FILE GIT-CFLAGS GIT-LDFLAGS GIT-GUI-VARS GIT-BUILD-OPTIONS
 
 .PHONY: all install clean strip
 .PHONY: shell_compatibility_test please_set_SHELL_PATH_to_a_more_modern_shell
@@ -2486,3 +2505,19 @@ cover_db: coverage-report
 
 cover_db_html: cover_db
        cover -report html -outputdir cover_db_html cover_db
+
+### profile feedback build
+#
+.PHONY: profile-all profile-clean
+
+PROFILE_GEN_CFLAGS := $(CFLAGS) -fprofile-generate -DNO_NORETURN=1
+PROFILE_USE_CFLAGS := $(CFLAGS) -fprofile-use -fprofile-correction -DNO_NORETURN=1
+
+profile-clean:
+       $(RM) $(addsuffix *.gcda,$(object_dirs))
+       $(RM) $(addsuffix *.gcno,$(object_dirs))
+
+profile-all: profile-clean
+       $(MAKE) CFLAGS="$(PROFILE_GEN_CFLAGS)" all
+       $(MAKE) CFLAGS="$(PROFILE_GEN_CFLAGS)" -j1 test
+       $(MAKE) CFLAGS="$(PROFILE_USE_CFLAGS)" all
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 3c102a1648304552ff24f8a7fad92b2fc35bba01..02d1f3787acd8d6583073458ab7c8946684f9371 100644 (file)
@@ -90,14 +90,14 @@ static void copy_le32(unsigned char *dest, unsigned int n)
 static void *zlib_deflate(void *data, unsigned long size,
                int compression_level, unsigned long *compressed_size)
 {
-       z_stream stream;
+       git_zstream stream;
        unsigned long maxsize;
        void *buffer;
        int result;
 
        memset(&stream, 0, sizeof(stream));
-       deflateInit(&stream, compression_level);
-       maxsize = deflateBound(&stream, size);
+       git_deflate_init(&stream, compression_level);
+       maxsize = git_deflate_bound(&stream, size);
        buffer = xmalloc(maxsize);
 
        stream.next_in = data;
@@ -106,7 +106,7 @@ static void *zlib_deflate(void *data, unsigned long size,
        stream.avail_out = maxsize;
 
        do {
-               result = deflate(&stream, Z_FINISH);
+               result = git_deflate(&stream, Z_FINISH);
        } while (result == Z_OK);
 
        if (result != Z_STREAM_END) {
@@ -114,7 +114,7 @@ static void *zlib_deflate(void *data, unsigned long size,
                return NULL;
        }
 
-       deflateEnd(&stream);
+       git_deflate_end(&stream);
        *compressed_size = stream.total_out;
 
        return buffer;
index 530d4bb7e76da8e402f78ab7f981a080d25d6e78..f2edc52818e817e83717f992a6bbd66dad4ef24d 100644 (file)
@@ -1634,7 +1634,7 @@ static inline int metadata_changes(struct patch *patch)
 static char *inflate_it(const void *data, unsigned long size,
                        unsigned long inflated_size)
 {
-       z_stream stream;
+       git_zstream stream;
        void *out;
        int st;
 
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 f579794d9a93a0e55289921f20b8f68b85211ca1..a15784a7b8406be3ead20839aab0f8de2cc52f3d 100644 (file)
@@ -46,6 +46,7 @@ static const char *real_git_dir;
 static char *option_upload_pack = "git-upload-pack";
 static int option_verbosity;
 static int option_progress;
+static struct string_list option_config;
 
 static struct option builtin_clone_options[] = {
        OPT__VERBOSITY(&option_verbosity),
@@ -83,7 +84,8 @@ static struct option builtin_clone_options[] = {
                    "create a shallow clone of that depth"),
        OPT_STRING(0, "separate-git-dir", &real_git_dir, "gitdir",
                   "separate git dir from working tree"),
-
+       OPT_STRING_LIST('c', "config", &option_config, "key=value",
+                       "set config inside the new repository"),
        OPT_END()
 };
 
@@ -364,6 +366,22 @@ static void write_remote_refs(const struct ref *local_refs)
        clear_extra_refs();
 }
 
+static int write_one_config(const char *key, const char *value, void *data)
+{
+       return git_config_set_multivar(key, value ? value : "true", "^$", 0);
+}
+
+static void write_config(struct string_list *config)
+{
+       int i;
+
+       for (i = 0; i < config->nr; i++) {
+               if (git_config_parse_parameter(config->items[i].string,
+                                              write_one_config, NULL) < 0)
+                       die("unable to write parameters to config file");
+       }
+}
+
 int cmd_clone(int argc, const char **argv, const char *prefix)
 {
        int is_bundle = 0, is_local;
@@ -482,6 +500,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
                        printf(_("Cloning into %s...\n"), dir);
        }
        init_db(option_template, INIT_DB_QUIET);
+       write_config(&option_config);
 
        /*
         * At this point, the config exists, so we do not need the
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 e40451ffb4ecd31107bfe51817cdce18e1dfe859..81cdc28b30731e722eaa2af045a3464bc83f0e16 100644 (file)
@@ -265,7 +265,7 @@ static void unlink_base_data(struct base_data *c)
 static void *unpack_entry_data(unsigned long offset, unsigned long size)
 {
        int status;
-       z_stream stream;
+       git_zstream stream;
        void *buf = xmalloc(size);
 
        memset(&stream, 0, sizeof(stream));
@@ -355,7 +355,7 @@ static void *get_data_from_pack(struct object_entry *obj)
        off_t from = obj[0].idx.offset + obj[0].hdr_size;
        unsigned long len = obj[1].idx.offset - from;
        unsigned char *data, *inbuf;
-       z_stream stream;
+       git_zstream stream;
        int status;
 
        data = xmalloc(obj->size);
@@ -666,26 +666,26 @@ static void parse_pack_objects(unsigned char *sha1)
 
 static int write_compressed(struct sha1file *f, void *in, unsigned int size)
 {
-       z_stream stream;
+       git_zstream stream;
        int status;
        unsigned char outbuf[4096];
 
        memset(&stream, 0, sizeof(stream));
-       deflateInit(&stream, zlib_compression_level);
+       git_deflate_init(&stream, zlib_compression_level);
        stream.next_in = in;
        stream.avail_in = size;
 
        do {
                stream.next_out = outbuf;
                stream.avail_out = sizeof(outbuf);
-               status = deflate(&stream, Z_FINISH);
+               status = git_deflate(&stream, Z_FINISH);
                sha1write(f, outbuf, sizeof(outbuf) - stream.avail_out);
        } while (status == Z_OK);
 
        if (status != Z_STREAM_END)
                die("unable to deflate appended object (%d)", status);
        size = stream.total_out;
-       deflateEnd(&stream);
+       git_deflate_end(&stream);
        return size;
 }
 
index f402a843bb5ee23360d29bdbbc6cc9c5b9c72008..c6e2d8766b0ec15fcfe9dc0a60ee81db6750b527 100644 (file)
@@ -126,13 +126,13 @@ static void *get_delta(struct object_entry *entry)
 
 static unsigned long do_compress(void **pptr, unsigned long size)
 {
-       z_stream stream;
+       git_zstream stream;
        void *in, *out;
        unsigned long maxsize;
 
        memset(&stream, 0, sizeof(stream));
-       deflateInit(&stream, pack_compression_level);
-       maxsize = deflateBound(&stream, size);
+       git_deflate_init(&stream, pack_compression_level);
+       maxsize = git_deflate_bound(&stream, size);
 
        in = *pptr;
        out = xmalloc(maxsize);
@@ -142,9 +142,9 @@ static unsigned long do_compress(void **pptr, unsigned long size)
        stream.avail_in = size;
        stream.next_out = out;
        stream.avail_out = maxsize;
-       while (deflate(&stream, Z_FINISH) == Z_OK)
+       while (git_deflate(&stream, Z_FINISH) == Z_OK)
                ; /* nothing */
-       deflateEnd(&stream);
+       git_deflate_end(&stream);
 
        free(in);
        return stream.total_out;
@@ -160,7 +160,7 @@ static int check_pack_inflate(struct packed_git *p,
                off_t len,
                unsigned long expect)
 {
-       z_stream stream;
+       git_zstream stream;
        unsigned char fakebuf[4096], *in;
        int st;
 
@@ -187,12 +187,12 @@ static void copy_pack_data(struct sha1file *f,
                off_t len)
 {
        unsigned char *in;
-       unsigned int avail;
+       unsigned long avail;
 
        while (len) {
                in = use_pack(p, w_curs, offset, &avail);
                if (avail > len)
-                       avail = (unsigned int)len;
+                       avail = (unsigned long)len;
                sha1write(f, in, avail);
                offset += avail;
                len -= avail;
@@ -994,7 +994,7 @@ static void check_object(struct object_entry *entry)
                const unsigned char *base_ref = NULL;
                struct object_entry *base_entry;
                unsigned long used, used_0;
-               unsigned int avail;
+               unsigned long avail;
                off_t ofs;
                unsigned char *buf, c;
 
index 9ff1cac69b9fd21c5f7dfc06b5859fedccc67644..05b1f5b76de36b19ef4bebba32aff1b40cbc2030 100644 (file)
@@ -88,16 +88,6 @@ static inline int postfixcmp(const char *string, const char *postfix)
        return strcmp(string + len1 - len2, postfix);
 }
 
-static int opt_parse_track(const struct option *opt, const char *arg, int not)
-{
-       struct string_list *list = opt->value;
-       if (not)
-               string_list_clear(list, 0);
-       else
-               string_list_append(list, arg);
-       return 0;
-}
-
 static int fetch_remote(const char *name)
 {
        const char *argv[] = { "fetch", name, NULL, NULL };
@@ -176,8 +166,8 @@ static int add(int argc, const char **argv)
                            TAGS_SET),
                OPT_SET_INT(0, NULL, &fetch_tags,
                            "or do not fetch any tag at all (--no-tags)", TAGS_UNSET),
-               OPT_CALLBACK('t', "track", &track, "branch",
-                       "branch(es) to track", opt_parse_track),
+               OPT_STRING_LIST('t', "track", &track, "branch",
+                               "branch(es) to track"),
                OPT_STRING('m', "master", &master, "branch", "master branch"),
                { OPTION_CALLBACK, 0, "mirror", &mirror, "push|fetch",
                        "set up remote as a mirror to push to or fetch from",
index ec926fc8ee4512abddc67309421b4394706329f5..cef27263bc79e519398ae5eb0a49d78f3b389090 100644 (file)
@@ -16,7 +16,7 @@
 static const char * const git_tag_usage[] = {
        "git tag [-a|-s|-u <key-id>] [-f] [-m <msg>|-F <file>] <tagname> [<head>]",
        "git tag -d <tagname>...",
-       "git tag -l [-n[<num>]] [<pattern>]",
+       "git tag -l [-n[<num>]] [<pattern>...]",
        "git tag -v <tagname>...",
        NULL
 };
@@ -24,17 +24,28 @@ static const char * const git_tag_usage[] = {
 static char signingkey[1000];
 
 struct tag_filter {
-       const char *pattern;
+       const char **patterns;
        int lines;
        struct commit_list *with_commit;
 };
 
+static int match_pattern(const char **patterns, const char *ref)
+{
+       /* no pattern means match everything */
+       if (!*patterns)
+               return 1;
+       for (; *patterns; patterns++)
+               if (!fnmatch(*patterns, ref, 0))
+                       return 1;
+       return 0;
+}
+
 static int show_reference(const char *refname, const unsigned char *sha1,
                          int flag, void *cb_data)
 {
        struct tag_filter *filter = cb_data;
 
-       if (!fnmatch(filter->pattern, refname, 0)) {
+       if (match_pattern(filter->patterns, refname)) {
                int i;
                unsigned long size;
                enum object_type type;
@@ -88,15 +99,12 @@ static int show_reference(const char *refname, const unsigned char *sha1,
        return 0;
 }
 
-static int list_tags(const char *pattern, int lines,
+static int list_tags(const char **patterns, int lines,
                        struct commit_list *with_commit)
 {
        struct tag_filter filter;
 
-       if (pattern == NULL)
-               pattern = "*";
-
-       filter.pattern = pattern;
+       filter.patterns = patterns;
        filter.lines = lines;
        filter.with_commit = with_commit;
 
@@ -425,7 +433,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
        if (list + delete + verify > 1)
                usage_with_options(git_tag_usage, options);
        if (list)
-               return list_tags(argv[0], lines == -1 ? 0 : lines,
+               return list_tags(argv, lines == -1 ? 0 : lines,
                                 with_commit);
        if (lines != -1)
                die(_("-n option is only allowed with -l."));
index f63973c9143c4109a3d1e40df9ec66a32498fe00..14e04e6795bd514fb1fec506073d8a9a71668cfa 100644 (file)
@@ -90,7 +90,7 @@ static void use(int bytes)
 
 static void *get_data(unsigned long size)
 {
-       z_stream stream;
+       git_zstream stream;
        void *buf = xmalloc(size);
 
        memset(&stream, 0, sizeof(stream));
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..5e80113ee93c5c44a7ada5982faaad9a3fcbdc2e 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
 #endif
 
 #include <zlib.h>
-#if defined(NO_DEFLATE_BOUND) || ZLIB_VERNUM < 0x1200
-#define deflateBound(c,s)  ((s) + (((s) + 7) >> 3) + (((s) + 63) >> 6) + 11)
-#endif
-
-void git_inflate_init(z_streamp strm);
-void git_inflate_end(z_streamp strm);
-int git_inflate(z_streamp strm, int flush);
+typedef struct git_zstream {
+       z_stream z;
+       unsigned long avail_in;
+       unsigned long avail_out;
+       unsigned long total_in;
+       unsigned long total_out;
+       unsigned char *next_in;
+       unsigned char *next_out;
+} git_zstream;
+
+void git_inflate_init(git_zstream *);
+void git_inflate_init_gzip_only(git_zstream *);
+void git_inflate_end(git_zstream *);
+int git_inflate(git_zstream *, int flush);
+
+void git_deflate_init(git_zstream *, int level);
+void git_deflate_init_gzip(git_zstream *, int level);
+void git_deflate_end(git_zstream *);
+int git_deflate_end_gently(git_zstream *);
+int git_deflate(git_zstream *, int flush);
+unsigned long git_deflate_bound(git_zstream *, unsigned long);
 
 #if defined(DT_UNKNOWN) && !defined(NO_D_TYPE_IN_DIRENT)
 #define DTYPE(de)      ((de)->d_type)
@@ -582,35 +597,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 +733,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 +766,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(git_zstream *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;
@@ -1009,7 +998,7 @@ extern struct packed_git *find_sha1_pack(const unsigned char *sha1,
 extern void pack_report(void);
 extern int open_pack_index(struct packed_git *);
 extern void close_pack_index(struct packed_git *);
-extern unsigned char *use_pack(struct packed_git *, struct pack_window **, off_t, unsigned int *);
+extern unsigned char *use_pack(struct packed_git *, struct pack_window **, off_t, unsigned long *);
 extern void close_pack_windows(struct packed_git *);
 extern void unuse_pack(struct pack_window **);
 extern void free_pack_by_name(const char *);
@@ -1021,7 +1010,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);
@@ -1063,6 +1082,8 @@ extern int config_error_nonbool(const char *);
 extern const char *get_log_output_encoding(void);
 extern const char *get_commit_output_encoding(void);
 
+extern int git_config_parse_parameter(const char *, config_fn_t fn, void *data);
+
 extern const char *config_exclusive_filename;
 
 #define MAX_GITNAME (1000)
@@ -1129,13 +1150,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 b4a51b958c5651d3b509013aaef0c4e30b68a816..dfe9b3084ffbfd6c41a648e32a1857e78b566215 100644 (file)
@@ -101,7 +101,7 @@ static int cygwin_stat(const char *path, struct stat *buf)
  * and calling git_default_config() from here would break such variables.
  */
 static int native_stat = 1;
-static int core_filemode;
+static int core_filemode = 1; /* matches trust_executable_bit default */
 
 static int git_cygwin_config(const char *var, const char *value, void *cb)
 {
@@ -114,8 +114,7 @@ static int git_cygwin_config(const char *var, const char *value, void *cb)
 
 static int init_stat(void)
 {
-       if (have_git_dir()) {
-               git_config(git_cygwin_config, NULL);
+       if (have_git_dir() && git_config(git_cygwin_config,NULL)) {
                if (!core_filemode && native_stat) {
                        cygwin_stat_fn = cygwin_stat;
                        cygwin_lstat_fn = cygwin_lstat;
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 e0b3b80d92ece9f37e0e76292bd61ce97271fcce..6b61a849d26c7a35f57d74c9d5f0adbb1f85c72d 100644 (file)
--- a/config.c
+++ b/config.c
 
 #define MAXNAME (256)
 
-static FILE *config_file;
-static const char *config_file_name;
-static int config_linenr;
-static int config_file_eof;
+typedef struct config_file {
+       struct config_file *prev;
+       FILE *f;
+       const char *name;
+       int linenr;
+       int eof;
+       struct strbuf value;
+       char var[MAXNAME];
+} config_file;
+
+static config_file *cf;
+
 static int zlib_compression_seen;
 
 const char *config_exclusive_filename = NULL;
@@ -39,13 +47,13 @@ void git_config_push_parameter(const char *text)
        strbuf_release(&env);
 }
 
-static int git_config_parse_parameter(const char *text,
-                                     config_fn_t fn, void *data)
+int git_config_parse_parameter(const char *text,
+                              config_fn_t fn, void *data)
 {
-       struct strbuf tmp = STRBUF_INIT;
        struct strbuf **pair;
-       strbuf_addstr(&tmp, text);
-       pair = strbuf_split(&tmp, '=');
+       pair = strbuf_split_str(text, '=', 2);
+       if (!pair[0])
+               return error("bogus config parameter: %s", text);
        if (pair[0]->len && pair[0]->buf[pair[0]->len - 1] == '=')
                strbuf_setlen(pair[0], pair[0]->len - 1);
        strbuf_trim(pair[0]);
@@ -99,7 +107,7 @@ static int get_next_char(void)
        FILE *f;
 
        c = '\n';
-       if ((f = config_file) != NULL) {
+       if (cf && ((f = cf->f) != NULL)) {
                c = fgetc(f);
                if (c == '\r') {
                        /* DOS like systems */
@@ -110,9 +118,9 @@ static int get_next_char(void)
                        }
                }
                if (c == '\n')
-                       config_linenr++;
+                       cf->linenr++;
                if (c == EOF) {
-                       config_file_eof = 1;
+                       cf->eof = 1;
                        c = '\n';
                }
        }
@@ -121,21 +129,20 @@ static int get_next_char(void)
 
 static char *parse_value(void)
 {
-       static struct strbuf value = STRBUF_INIT;
        int quote = 0, comment = 0, space = 0;
 
-       strbuf_reset(&value);
+       strbuf_reset(&cf->value);
        for (;;) {
                int c = get_next_char();
                if (c == '\n') {
                        if (quote)
                                return NULL;
-                       return value.buf;
+                       return cf->value.buf;
                }
                if (comment)
                        continue;
                if (isspace(c) && !quote) {
-                       if (value.len)
+                       if (cf->value.len)
                                space++;
                        continue;
                }
@@ -146,7 +153,7 @@ static char *parse_value(void)
                        }
                }
                for (; space; space--)
-                       strbuf_addch(&value, ' ');
+                       strbuf_addch(&cf->value, ' ');
                if (c == '\\') {
                        c = get_next_char();
                        switch (c) {
@@ -168,14 +175,14 @@ static char *parse_value(void)
                        default:
                                return NULL;
                        }
-                       strbuf_addch(&value, c);
+                       strbuf_addch(&cf->value, c);
                        continue;
                }
                if (c == '"') {
                        quote = 1-quote;
                        continue;
                }
-               strbuf_addch(&value, c);
+               strbuf_addch(&cf->value, c);
        }
 }
 
@@ -192,7 +199,7 @@ static int get_value(config_fn_t fn, void *data, char *name, unsigned int len)
        /* Get the full name */
        for (;;) {
                c = get_next_char();
-               if (config_file_eof)
+               if (cf->eof)
                        break;
                if (!iskeychar(c))
                        break;
@@ -256,7 +263,7 @@ static int get_base_var(char *name)
 
        for (;;) {
                int c = get_next_char();
-               if (config_file_eof)
+               if (cf->eof)
                        return -1;
                if (c == ']')
                        return baselen;
@@ -274,7 +281,7 @@ static int git_parse_file(config_fn_t fn, void *data)
 {
        int comment = 0;
        int baselen = 0;
-       static char var[MAXNAME];
+       char *var = cf->var;
 
        /* U+FEFF Byte Order Mark in UTF8 */
        static const unsigned char *utf8_bom = (unsigned char *) "\xef\xbb\xbf";
@@ -298,7 +305,7 @@ static int git_parse_file(config_fn_t fn, void *data)
                        }
                }
                if (c == '\n') {
-                       if (config_file_eof)
+                       if (cf->eof)
                                return 0;
                        comment = 0;
                        continue;
@@ -323,7 +330,7 @@ static int git_parse_file(config_fn_t fn, void *data)
                if (get_value(fn, data, var, baselen+1) < 0)
                        break;
        }
-       die("bad config file line %d in %s", config_linenr, config_file_name);
+       die("bad config file line %d in %s", cf->linenr, cf->name);
 }
 
 static int parse_unit_factor(const char *end, unsigned long *val)
@@ -374,8 +381,8 @@ int git_parse_ulong(const char *value, unsigned long *ret)
 
 static void die_bad_config(const char *name)
 {
-       if (config_file_name)
-               die("bad config value for '%s' in %s", name, config_file_name);
+       if (cf && cf->name)
+               die("bad config value for '%s' in %s", name, cf->name);
        die("bad config value for '%s'", name);
 }
 
@@ -795,13 +802,24 @@ int git_config_from_file(config_fn_t fn, const char *filename, void *data)
 
        ret = -1;
        if (f) {
-               config_file = f;
-               config_file_name = filename;
-               config_linenr = 1;
-               config_file_eof = 0;
+               config_file top;
+
+               /* push config-file parsing state stack */
+               top.prev = cf;
+               top.f = f;
+               top.name = filename;
+               top.linenr = 1;
+               top.eof = 0;
+               strbuf_init(&top.value, 1024);
+               cf = &top;
+
                ret = git_parse_file(fn, data);
+
+               /* pop config-file parsing state stack */
+               strbuf_release(&top.value);
+               cf = top.prev;
+
                fclose(f);
-               config_file_name = NULL;
        }
        return ret;
 }
@@ -856,7 +874,7 @@ int git_config_early(config_fn_t fn, void *data, const char *repo_config)
 
        switch (git_config_from_parameters(fn, data)) {
        case -1: /* error */
-               ret--;
+               die("unable to parse command-line config");
                break;
        case 0: /* found nothing */
                break;
@@ -909,6 +927,7 @@ static int store_aux(const char *key, const char *value, void *cb)
 {
        const char *ep;
        size_t section_len;
+       FILE *f = cf->f;
 
        switch (store.state) {
        case KEY_SEEN:
@@ -920,7 +939,7 @@ static int store_aux(const char *key, const char *value, void *cb)
                                return 1;
                        }
 
-                       store.offset[store.seen] = ftell(config_file);
+                       store.offset[store.seen] = ftell(f);
                        store.seen++;
                }
                break;
@@ -947,19 +966,19 @@ static int store_aux(const char *key, const char *value, void *cb)
                 * Do not increment matches: this is no match, but we
                 * just made sure we are in the desired section.
                 */
-               store.offset[store.seen] = ftell(config_file);
+               store.offset[store.seen] = ftell(f);
                /* fallthru */
        case SECTION_END_SEEN:
        case START:
                if (matches(key, value)) {
-                       store.offset[store.seen] = ftell(config_file);
+                       store.offset[store.seen] = ftell(f);
                        store.state = KEY_SEEN;
                        store.seen++;
                } else {
                        if (strrchr(key, '.') - key == store.baselen &&
                              !strncmp(key, store.key, store.baselen)) {
                                        store.state = SECTION_SEEN;
-                                       store.offset[store.seen] = ftell(config_file);
+                                       store.offset[store.seen] = ftell(f);
                        }
                }
        }
@@ -1415,6 +1434,7 @@ int git_config_rename_section(const char *old_name, const char *new_name)
        struct lock_file *lock = xcalloc(sizeof(struct lock_file), 1);
        int out_fd;
        char buf[1024];
+       FILE *config_file;
 
        if (config_exclusive_filename)
                config_filename = xstrdup(config_exclusive_filename);
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..93ef9a265ca6b52b644468979dba732c70b5097a 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",
@@ -1839,20 +1861,20 @@ static unsigned char *deflate_it(char *data,
 {
        int bound;
        unsigned char *deflated;
-       z_stream stream;
+       git_zstream stream;
 
        memset(&stream, 0, sizeof(stream));
-       deflateInit(&stream, zlib_compression_level);
-       bound = deflateBound(&stream, size);
+       git_deflate_init(&stream, zlib_compression_level);
+       bound = git_deflate_bound(&stream, size);
        deflated = xmalloc(bound);
        stream.next_out = deflated;
        stream.avail_out = bound;
 
        stream.next_in = (unsigned char *)data;
        stream.avail_in = size;
-       while (deflate(&stream, Z_FINISH) == Z_OK)
+       while (git_deflate(&stream, Z_FINISH) == Z_OK)
                ; /* nothing */
-       deflateEnd(&stream);
+       git_deflate_end(&stream);
        *result_size = stream.total_out;
        return deflated;
 }
@@ -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 78d978684da2e2b79340c138e1b6300100df1221..1d5e3336a51a4bf19c0ab700565826b438c43266 100644 (file)
@@ -1017,7 +1017,7 @@ static int store_object(
        unsigned char sha1[20];
        unsigned long hdrlen, deltalen;
        git_SHA_CTX c;
-       z_stream s;
+       git_zstream s;
 
        hdrlen = sprintf((char *)hdr,"%s %lu", typename(type),
                (unsigned long)dat->len) + 1;
@@ -1050,7 +1050,7 @@ static int store_object(
                delta = NULL;
 
        memset(&s, 0, sizeof(s));
-       deflateInit(&s, pack_compression_level);
+       git_deflate_init(&s, pack_compression_level);
        if (delta) {
                s.next_in = delta;
                s.avail_in = deltalen;
@@ -1058,11 +1058,11 @@ static int store_object(
                s.next_in = (void *)dat->buf;
                s.avail_in = dat->len;
        }
-       s.avail_out = deflateBound(&s, s.avail_in);
+       s.avail_out = git_deflate_bound(&s, s.avail_in);
        s.next_out = out = xmalloc(s.avail_out);
-       while (deflate(&s, Z_FINISH) == Z_OK)
-               /* nothing */;
-       deflateEnd(&s);
+       while (git_deflate(&s, Z_FINISH) == Z_OK)
+               ; /* nothing */
+       git_deflate_end(&s);
 
        /* Determine if we should auto-checkpoint. */
        if ((max_packsize && (pack_size + 60 + s.total_out) > max_packsize)
@@ -1078,14 +1078,14 @@ static int store_object(
                        delta = NULL;
 
                        memset(&s, 0, sizeof(s));
-                       deflateInit(&s, pack_compression_level);
+                       git_deflate_init(&s, pack_compression_level);
                        s.next_in = (void *)dat->buf;
                        s.avail_in = dat->len;
-                       s.avail_out = deflateBound(&s, s.avail_in);
+                       s.avail_out = git_deflate_bound(&s, s.avail_in);
                        s.next_out = out = xrealloc(out, s.avail_out);
-                       while (deflate(&s, Z_FINISH) == Z_OK)
-                               /* nothing */;
-                       deflateEnd(&s);
+                       while (git_deflate(&s, Z_FINISH) == Z_OK)
+                               ; /* nothing */
+                       git_deflate_end(&s);
                }
        }
 
@@ -1163,7 +1163,7 @@ static void stream_blob(uintmax_t len, unsigned char *sha1out, uintmax_t mark)
        off_t offset;
        git_SHA_CTX c;
        git_SHA_CTX pack_file_ctx;
-       z_stream s;
+       git_zstream s;
        int status = Z_OK;
 
        /* Determine if we should auto-checkpoint. */
@@ -1187,7 +1187,7 @@ static void stream_blob(uintmax_t len, unsigned char *sha1out, uintmax_t mark)
        crc32_begin(pack_file);
 
        memset(&s, 0, sizeof(s));
-       deflateInit(&s, pack_compression_level);
+       git_deflate_init(&s, pack_compression_level);
 
        hdrlen = encode_in_pack_object_header(OBJ_BLOB, len, out_buf);
        if (out_sz <= hdrlen)
@@ -1209,7 +1209,7 @@ static void stream_blob(uintmax_t len, unsigned char *sha1out, uintmax_t mark)
                        len -= n;
                }
 
-               status = deflate(&s, len ? 0 : Z_FINISH);
+               status = git_deflate(&s, len ? 0 : Z_FINISH);
 
                if (!s.avail_out || status == Z_STREAM_END) {
                        size_t n = s.next_out - out_buf;
@@ -1228,7 +1228,7 @@ static void stream_blob(uintmax_t len, unsigned char *sha1out, uintmax_t mark)
                        die("unexpected deflate failure: %d", status);
                }
        }
-       deflateEnd(&s);
+       git_deflate_end(&s);
        git_SHA1_Final(sha1, &c);
 
        if (sha1out)
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..ddfbf771492c599e7a6ca04bb39962cf4c7a784c 100644 (file)
@@ -215,10 +215,14 @@ 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
-#elif defined(__GNUC__)
+#elif defined(__GNUC__) && !defined(NO_NORETURN)
 #define NORETURN __attribute__((__noreturn__))
 #define NORETURN_PTR __attribute__((__noreturn__))
 #elif defined(_MSC_VER)
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 65690af89307abd72749123caddce9e6c4692383..c6ba7c15510935353bd17b2b8d8188fe16052907 100644 (file)
@@ -713,7 +713,6 @@ then
        # parents to rewrite and skipping dropped commits would
        # prematurely end our probe
        merges_option=
-       first_after_upstream="$(git rev-list --reverse --first-parent $upstream..$orig_head | head -n 1)"
 else
        merges_option="--no-merges --cherry-pick"
 fi
@@ -746,7 +745,7 @@ do
                        preserve=t
                        for p in $(git rev-list --parents -1 $sha1 | cut -d' ' -s -f2-)
                        do
-                               if test -f "$rewritten"/$p -a \( $p != $onto -o $sha1 = $first_after_upstream \)
+                               if test -f "$rewritten"/$p
                                then
                                        preserve=f
                                fi
index d7855ea1c6dae8d8cbe35ae39f9c65e7bc9d7b11..4761f288906a53f0336161142ddcf7799a38c4b8 100755 (executable)
@@ -441,8 +441,7 @@ case "$#" in
        then
                head_name="detached HEAD"
        else
-               echo >&2 "fatal: no such branch: $1"
-               usage
+               die "fatal: no such branch: $1"
        fi
        ;;
 *)
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..87c9452c8a8ae179ffb7e7958ba95688e95bd0e9 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'")"
 }
 
 #
@@ -300,12 +304,16 @@ cmd_foreach()
 
        toplevel=$(pwd)
 
+       # dup stdin so that it can be restored when running the external
+       # command in the subshell (and a recursive call to this function)
+       exec 3<&0
+
        module_list |
        while read mode sha1 stage path
        do
                if test -e "$path"/.git
                then
-                       say "Entering '$prefix$path'"
+                       say "$(eval_gettext "Entering '\$prefix\$path'")"
                        name=$(module_name "$path")
                        (
                                prefix="$prefix$path/"
@@ -316,8 +324,8 @@ cmd_foreach()
                                then
                                        cmd_foreach "--recursive" "$@"
                                fi
-                       ) ||
-                       die "Stopping at '$path'; script returned non-zero status."
+                       ) <&3 3<&- ||
+                       die "$(eval_gettext "Stopping at '\$path'; script returned non-zero status.")"
                fi
        done
 }
@@ -360,7 +368,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 +378,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 +452,8 @@ cmd_update()
        fi
 
        cloned_modules=
-       module_list "$@" |
+       module_list "$@" | {
+       err=
        while read mode sha1 stage path
        do
                if test "$stage" = U
@@ -460,8 +469,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 +482,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 +506,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 +516,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 +663,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 +703,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 +724,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 +755,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 +793,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 +881,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 +925,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..a62b510ba74bc4734c76fc823a73cc6003637908 100755 (executable)
@@ -328,6 +328,7 @@ our %feature = (
        # 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 @@ our %feature = (
        # 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);
@@ -3681,6 +3680,20 @@ sub get_page_title {
        return $title;
 }
 
+sub get_content_type_html {
+       # require explicit support from the UA if we are to send the page as
+       # 'application/xhtml+xml', otherwise send it as plain old 'text/html'.
+       # we have to do this because MSIE sometimes globs '*/*', pretending to
+       # support xhtml+xml but choking when it gets what it asked for.
+       if (defined $cgi->http('HTTP_ACCEPT') &&
+           $cgi->http('HTTP_ACCEPT') =~ m/(,|;|\s|^)application\/xhtml\+xml(,|;|\s|$)/ &&
+           $cgi->Accept('application/xhtml+xml') != 0) {
+               return 'application/xhtml+xml';
+       } else {
+               return 'text/html';
+       }
+}
+
 sub print_feed_meta {
        if (defined $project) {
                my %href_params = get_feed_info();
@@ -3726,24 +3739,90 @@ sub print_feed_meta {
        }
 }
 
+sub print_header_links {
+       my $status = shift;
+
+       # print out each stylesheet that exist, providing backwards capability
+       # for those people who defined $stylesheet in a config file
+       if (defined $stylesheet) {
+               print '<link rel="stylesheet" type="text/css" href="'.esc_url($stylesheet).'"/>'."\n";
+       } else {
+               foreach my $stylesheet (@stylesheets) {
+                       next unless $stylesheet;
+                       print '<link rel="stylesheet" type="text/css" href="'.esc_url($stylesheet).'"/>'."\n";
+               }
+       }
+       print_feed_meta()
+               if ($status eq '200 OK');
+       if (defined $favicon) {
+               print qq(<link rel="shortcut icon" href=").esc_url($favicon).qq(" type="image/png" />\n);
+       }
+}
+
+sub print_nav_breadcrumbs {
+       my %opts = @_;
+
+       print $cgi->a({-href => esc_url($home_link)}, $home_link_str) . " / ";
+       if (defined $project) {
+               print $cgi->a({-href => href(action=>"summary")}, esc_html($project));
+               if (defined $action) {
+                       my $action_print = $action ;
+                       if (defined $opts{-action_extra}) {
+                               $action_print = $cgi->a({-href => href(action=>$action)},
+                                       $action);
+                       }
+                       print " / $action_print";
+               }
+               if (defined $opts{-action_extra}) {
+                       print " / $opts{-action_extra}";
+               }
+               print "\n";
+       }
+}
+
+sub print_search_form {
+       if (!defined $searchtext) {
+               $searchtext = "";
+       }
+       my $search_hash;
+       if (defined $hash_base) {
+               $search_hash = $hash_base;
+       } elsif (defined $hash) {
+               $search_hash = $hash;
+       } else {
+               $search_hash = "HEAD";
+       }
+       my $action = $my_uri;
+       my $use_pathinfo = gitweb_check_feature('pathinfo');
+       if ($use_pathinfo) {
+               $action .= "/".esc_url($project);
+       }
+       print $cgi->startform(-method => "get", -action => $action) .
+             "<div class=\"search\">\n" .
+             (!$use_pathinfo &&
+             $cgi->input({-name=>"p", -value=>$project, -type=>"hidden"}) . "\n") .
+             $cgi->input({-name=>"a", -value=>"search", -type=>"hidden"}) . "\n" .
+             $cgi->input({-name=>"h", -value=>$search_hash, -type=>"hidden"}) . "\n" .
+             $cgi->popup_menu(-name => 'st', -default => 'commit',
+                              -values => ['commit', 'grep', 'author', 'committer', 'pickaxe']) .
+             $cgi->sup($cgi->a({-href => href(action=>"search_help")}, "?")) .
+             " search:\n",
+             $cgi->textfield(-name => "s", -value => $searchtext) . "\n" .
+             "<span title=\"Extended regular expression\">" .
+             $cgi->checkbox(-name => 'sr', -value => 1, -label => 're',
+                            -checked => $search_use_regexp) .
+             "</span>" .
+             "</div>" .
+             $cgi->end_form() . "\n";
+}
+
 sub git_header_html {
        my $status = shift || "200 OK";
        my $expires = shift;
        my %opts = @_;
 
        my $title = get_page_title();
-       my $content_type;
-       # require explicit support from the UA if we are to send the page as
-       # 'application/xhtml+xml', otherwise send it as plain old 'text/html'.
-       # we have to do this because MSIE sometimes globs '*/*', pretending to
-       # support xhtml+xml but choking when it gets what it asked for.
-       if (defined $cgi->http('HTTP_ACCEPT') &&
-           $cgi->http('HTTP_ACCEPT') =~ m/(,|;|\s|^)application\/xhtml\+xml(,|;|\s|$)/ &&
-           $cgi->Accept('application/xhtml+xml') != 0) {
-               $content_type = 'application/xhtml+xml';
-       } else {
-               $content_type = 'text/html';
-       }
+       my $content_type = get_content_type_html();
        print $cgi->header(-type=>$content_type, -charset => 'utf-8',
                           -status=> $status, -expires => $expires)
                unless ($opts{'-no_http_header'});
@@ -3765,22 +3844,7 @@ EOF
        if ($ENV{'PATH_INFO'}) {
                print "<base href=\"".esc_url($base_url)."\" />\n";
        }
-       # print out each stylesheet that exist, providing backwards capability
-       # for those people who defined $stylesheet in a config file
-       if (defined $stylesheet) {
-               print '<link rel="stylesheet" type="text/css" href="'.esc_url($stylesheet).'"/>'."\n";
-       } else {
-               foreach my $stylesheet (@stylesheets) {
-                       next unless $stylesheet;
-                       print '<link rel="stylesheet" type="text/css" href="'.esc_url($stylesheet).'"/>'."\n";
-               }
-       }
-       print_feed_meta()
-               if ($status eq '200 OK');
-       if (defined $favicon) {
-               print qq(<link rel="shortcut icon" href=").esc_url($favicon).qq(" type="image/png" />\n);
-       }
-
+       print_header_links($status);
        print "</head>\n" .
              "<body>\n";
 
@@ -3797,59 +3861,12 @@ EOF
                                         -alt => "git",
                                         -class => "logo"}));
        }
-       print $cgi->a({-href => esc_url($home_link)}, $home_link_str) . " / ";
-       if (defined $project) {
-               print $cgi->a({-href => href(action=>"summary")}, esc_html($project));
-               if (defined $action) {
-                       my $action_print = $action ;
-                       if (defined $opts{-action_extra}) {
-                               $action_print = $cgi->a({-href => href(action=>$action)},
-                                       $action);
-                       }
-                       print " / $action_print";
-               }
-               if (defined $opts{-action_extra}) {
-                       print " / $opts{-action_extra}";
-               }
-               print "\n";
-       }
+       print_nav_breadcrumbs(%opts);
        print "</div>\n";
 
        my $have_search = gitweb_check_feature('search');
        if (defined $project && $have_search) {
-               if (!defined $searchtext) {
-                       $searchtext = "";
-               }
-               my $search_hash;
-               if (defined $hash_base) {
-                       $search_hash = $hash_base;
-               } elsif (defined $hash) {
-                       $search_hash = $hash;
-               } else {
-                       $search_hash = "HEAD";
-               }
-               my $action = $my_uri;
-               my $use_pathinfo = gitweb_check_feature('pathinfo');
-               if ($use_pathinfo) {
-                       $action .= "/".esc_url($project);
-               }
-               print $cgi->startform(-method => "get", -action => $action) .
-                     "<div class=\"search\">\n" .
-                     (!$use_pathinfo &&
-                     $cgi->input({-name=>"p", -value=>$project, -type=>"hidden"}) . "\n") .
-                     $cgi->input({-name=>"a", -value=>"search", -type=>"hidden"}) . "\n" .
-                     $cgi->input({-name=>"h", -value=>$search_hash, -type=>"hidden"}) . "\n" .
-                     $cgi->popup_menu(-name => 'st', -default => 'commit',
-                                      -values => ['commit', 'grep', 'author', 'committer', 'pickaxe']) .
-                     $cgi->sup($cgi->a({-href => href(action=>"search_help")}, "?")) .
-                     " search:\n",
-                     $cgi->textfield(-name => "s", -value => $searchtext) . "\n" .
-                     "<span title=\"Extended regular expression\">" .
-                     $cgi->checkbox(-name => 'sr', -value => 1, -label => 're',
-                                    -checked => $search_use_regexp) .
-                     "</span>" .
-                     "</div>" .
-                     $cgi->end_form() . "\n";
+               print_search_form();
        }
 }
 
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/help.c b/help.c
index 7654f1bfd608d151a213937614449cc497bb638b..e925ca1f8973a05a66b5c09b45e3451f65e3a1e7 100644 (file)
--- a/help.c
+++ b/help.c
@@ -127,7 +127,10 @@ static int is_executable(const char *name)
            !S_ISREG(st.st_mode))
                return 0;
 
-#ifdef WIN32
+#if defined(WIN32) || defined(__CYGWIN__)
+#if defined(__CYGWIN__)
+if ((st.st_mode & S_IXUSR) == 0)
+#endif
 {      /* cannot trust the executable bit, peek into the file instead */
        char buf[3] = { 0 };
        int n;
index 85015048dd466fb2f2afe12086379ecf579d0a56..59ad7da605f711af6970d6832db32efd62b6ea97 100644 (file)
@@ -271,16 +271,13 @@ static struct rpc_service *select_service(const char *name)
 
 static void inflate_request(const char *prog_name, int out)
 {
-       z_stream stream;
+       git_zstream stream;
        unsigned char in_buf[8192];
        unsigned char out_buf[8192];
        unsigned long cnt = 0;
-       int ret;
 
        memset(&stream, 0, sizeof(stream));
-       ret = inflateInit2(&stream, (15 + 16));
-       if (ret != Z_OK)
-               die("cannot start zlib inflater, zlib err %d", ret);
+       git_inflate_init_gzip_only(&stream);
 
        while (1) {
                ssize_t n = xread(0, in_buf, sizeof(in_buf));
@@ -296,7 +293,7 @@ static void inflate_request(const char *prog_name, int out)
                        stream.next_out = out_buf;
                        stream.avail_out = sizeof(out_buf);
 
-                       ret = inflate(&stream, Z_NO_FLUSH);
+                       ret = git_inflate(&stream, Z_NO_FLUSH);
                        if (ret != Z_OK && ret != Z_STREAM_END)
                                die("zlib error inflating request, result %d", ret);
 
@@ -311,7 +308,7 @@ static void inflate_request(const char *prog_name, int out)
        }
 
 done:
-       inflateEnd(&stream);
+       git_inflate_end(&stream);
        close(out);
 }
 
index 28bfe768f7749e455f522fcfedeb2350da646416..6e8f6d09abb6397f1782fa32d9d273fc4fc740fe 100644 (file)
@@ -377,15 +377,15 @@ static void start_put(struct transfer_request *request)
        unsigned long len;
        int hdrlen;
        ssize_t size;
-       z_stream stream;
+       git_zstream stream;
 
        unpacked = read_sha1_file(request->obj->sha1, &type, &len);
        hdrlen = sprintf(hdr, "%s %lu", typename(type), len) + 1;
 
        /* Set it up */
        memset(&stream, 0, sizeof(stream));
-       deflateInit(&stream, zlib_compression_level);
-       size = deflateBound(&stream, len + hdrlen);
+       git_deflate_init(&stream, zlib_compression_level);
+       size = git_deflate_bound(&stream, len + hdrlen);
        strbuf_init(&request->buffer.buf, size);
        request->buffer.posn = 0;
 
@@ -396,15 +396,15 @@ static void start_put(struct transfer_request *request)
        /* First header.. */
        stream.next_in = (void *)hdr;
        stream.avail_in = hdrlen;
-       while (deflate(&stream, 0) == Z_OK)
-               /* nothing */;
+       while (git_deflate(&stream, 0) == Z_OK)
+               ; /* nothing */
 
        /* Then the data itself.. */
        stream.next_in = unpacked;
        stream.avail_in = len;
-       while (deflate(&stream, Z_FINISH) == Z_OK)
-               /* nothing */;
-       deflateEnd(&stream);
+       while (git_deflate(&stream, Z_FINISH) == Z_OK)
+               ; /* nothing */
+       git_deflate_end(&stream);
        free(unpacked);
 
        request->buffer.buf.len = stream.total_out;
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);
diff --git a/http.h b/http.h
index 19b7134fa5c7ff9b65565dcbddccae1354f024c7..0bf8592dc45209c9be5b8ddac3a53f6fc11e29be 100644 (file)
--- a/http.h
+++ b/http.h
@@ -172,7 +172,7 @@ struct http_object_request {
        unsigned char sha1[20];
        unsigned char real_sha1[20];
        git_SHA_CTX c;
-       z_stream stream;
+       git_zstream stream;
        int zret;
        int rename;
        struct active_request_slot *slot;
index a1a521648deee8e7e82c4e2cb7fe8fe03605dc06..0c19b6e5a5677bd14989175abddc119381fac4ef 100644 (file)
@@ -26,7 +26,7 @@ int check_pack_crc(struct packed_git *p, struct pack_window **w_curs,
        uint32_t data_crc = crc32(0, NULL, 0);
 
        do {
-               unsigned int avail;
+               unsigned long avail;
                void *data = use_pack(p, w_curs, offset, &avail);
                if (avail > len)
                        avail = len;
@@ -61,7 +61,7 @@ static int verify_packfile(struct packed_git *p,
 
        git_SHA1_Init(&ctx);
        do {
-               unsigned int remaining;
+               unsigned long remaining;
                unsigned char *in = use_pack(p, w_curs, offset, &remaining);
                offset += remaining;
                if (!pack_sig_ofs)
index 73bd28ad90986af1618d82176076133186d045de..879ea82a3158ca5e6d4ca397e0c2dd2265e15edc 100644 (file)
@@ -3,6 +3,7 @@
 #include "cache.h"
 #include "commit.h"
 #include "color.h"
+#include "string-list.h"
 
 static int parse_options_usage(struct parse_opt_ctx_t *ctx,
                               const char * const *usagestr,
@@ -687,3 +688,19 @@ int parse_options_concat(struct option *dst, size_t dst_size, struct option *src
        }
        return -1;
 }
+
+int parse_opt_string_list(const struct option *opt, const char *arg, int unset)
+{
+       struct string_list *v = opt->value;
+
+       if (unset) {
+               string_list_clear(v, 0);
+               return 0;
+       }
+
+       if (!arg)
+               return -1;
+
+       string_list_append(v, xstrdup(arg));
+       return 0;
+}
index d1b12fe979b34ac9bbf2ff637934b96f3878a53d..05eb09b878fd94cfcc549622a35a0b897fa56cb9 100644 (file)
@@ -130,6 +130,9 @@ struct option {
                                      (h), PARSE_OPT_NOARG, NULL, (p) }
 #define OPT_INTEGER(s, l, v, h)     { OPTION_INTEGER, (s), (l), (v), "n", (h) }
 #define OPT_STRING(s, l, v, a, h)   { OPTION_STRING,  (s), (l), (v), (a), (h) }
+#define OPT_STRING_LIST(s, l, v, a, h) \
+                                   { OPTION_CALLBACK, (s), (l), (v), (a), \
+                                     (h), 0, &parse_opt_string_list }
 #define OPT_UYN(s, l, v, h)         { OPTION_CALLBACK, (s), (l), (v), NULL, \
                                      (h), PARSE_OPT_NOARG, &parse_opt_tertiary }
 #define OPT_DATE(s, l, v, h) \
@@ -204,6 +207,7 @@ extern int parse_opt_color_flag_cb(const struct option *, const char *, int);
 extern int parse_opt_verbosity_cb(const struct option *, const char *, int);
 extern int parse_opt_with_commit(const struct option *, const char *, int);
 extern int parse_opt_tertiary(const struct option *, const char *, int);
+extern int parse_opt_string_list(const struct option *, const char *, int);
 
 #define OPT__VERBOSE(var, h)  OPT_BOOLEAN('v', "verbose", (var), (h))
 #define OPT__QUIET(var, h)    OPT_BOOLEAN('q', "quiet",   (var), (h))
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..faaeda44a9225ff56d45e9566cccf1a2278abb27 100644 (file)
@@ -471,16 +471,12 @@ static int post_rpc(struct rpc_state *rpc)
                 * the transfer time.
                 */
                size_t size;
-               z_stream stream;
+               git_zstream stream;
                int ret;
 
                memset(&stream, 0, sizeof(stream));
-               ret = deflateInit2(&stream, Z_BEST_COMPRESSION,
-                               Z_DEFLATED, (15 + 16),
-                               8, Z_DEFAULT_STRATEGY);
-               if (ret != Z_OK)
-                       die("cannot deflate request; zlib init error %d", ret);
-               size = deflateBound(&stream, rpc->len);
+               git_deflate_init_gzip(&stream, Z_BEST_COMPRESSION);
+               size = git_deflate_bound(&stream, rpc->len);
                gzip_body = xmalloc(size);
 
                stream.next_in = (unsigned char *)rpc->buf;
@@ -488,11 +484,11 @@ static int post_rpc(struct rpc_state *rpc)
                stream.next_out = (unsigned char *)gzip_body;
                stream.avail_out = size;
 
-               ret = deflate(&stream, Z_FINISH);
+               ret = git_deflate(&stream, Z_FINISH);
                if (ret != Z_STREAM_END)
                        die("cannot deflate request; zlib deflate error %d", ret);
 
-               ret = deflateEnd(&stream);
+               ret = git_deflate_end_gently(&stream);
                if (ret != Z_OK)
                        die("cannot deflate request; zlib end error %d", ret);
 
@@ -811,19 +807,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..a6aac70923cad6ed3f2afec631dcc67d5f28a019 100644 (file)
@@ -834,7 +834,7 @@ static int in_window(struct pack_window *win, off_t offset)
 unsigned char *use_pack(struct packed_git *p,
                struct pack_window **w_cursor,
                off_t offset,
-               unsigned int *left)
+               unsigned long *left)
 {
        struct pack_window *win = *w_cursor;
 
@@ -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(git_zstream *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,37 +1271,32 @@ 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)
+static void *unpack_sha1_rest(git_zstream *stream, void *buffer, unsigned long size, const unsigned char *sha1)
 {
        int bytes = strlen(buffer) + 1;
        unsigned char *buf = xmallocz(size);
@@ -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;
@@ -1391,7 +1395,7 @@ static int parse_sha1_header(const char *hdr, unsigned long *sizep)
 static void *unpack_sha1_file(void *map, unsigned long mapsize, enum object_type *type, unsigned long *size, const unsigned char *sha1)
 {
        int ret;
-       z_stream stream;
+       git_zstream stream;
        char hdr[8192];
 
        ret = unpack_sha1_header(&stream, map, mapsize, hdr, sizeof(hdr));
@@ -1407,7 +1411,7 @@ unsigned long get_size_from_delta(struct packed_git *p,
 {
        const unsigned char *data;
        unsigned char delta_head[20], *in;
-       z_stream stream;
+       git_zstream stream;
        int st;
 
        memset(&stream, 0, sizeof(stream));
@@ -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,13 +1527,13 @@ 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;
+       unsigned long left;
        unsigned long used;
        enum object_type type;
 
@@ -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:
@@ -1642,7 +1648,7 @@ static void *unpack_compressed_entry(struct packed_git *p,
                                    unsigned long size)
 {
        int st;
-       z_stream stream;
+       git_zstream stream;
        unsigned char *buffer, *in;
 
        buffer = xmallocz(size);
@@ -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)
 {
@@ -2075,7 +2088,7 @@ static int sha1_loose_object_info(const unsigned char *sha1, unsigned long *size
        int status;
        unsigned long mapsize, size;
        void *map;
-       z_stream stream;
+       git_zstream stream;
        char hdr[32];
 
        map = map_sha1_file(sha1, &mapsize);
@@ -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)
 {
@@ -2424,7 +2457,7 @@ static int write_loose_object(const unsigned char *sha1, char *hdr, int hdrlen,
 {
        int fd, ret;
        unsigned char compressed[4096];
-       z_stream stream;
+       git_zstream stream;
        git_SHA_CTX c;
        unsigned char parano_sha1[20];
        char *filename;
@@ -2441,7 +2474,7 @@ static int write_loose_object(const unsigned char *sha1, char *hdr, int hdrlen,
 
        /* Set it up */
        memset(&stream, 0, sizeof(stream));
-       deflateInit(&stream, zlib_compression_level);
+       git_deflate_init(&stream, zlib_compression_level);
        stream.next_out = compressed;
        stream.avail_out = sizeof(compressed);
        git_SHA1_Init(&c);
@@ -2449,8 +2482,8 @@ static int write_loose_object(const unsigned char *sha1, char *hdr, int hdrlen,
        /* First header.. */
        stream.next_in = (unsigned char *)hdr;
        stream.avail_in = hdrlen;
-       while (deflate(&stream, 0) == Z_OK)
-               /* nothing */;
+       while (git_deflate(&stream, 0) == Z_OK)
+               ; /* nothing */
        git_SHA1_Update(&c, hdr, hdrlen);
 
        /* Then the data itself.. */
@@ -2458,7 +2491,7 @@ static int write_loose_object(const unsigned char *sha1, char *hdr, int hdrlen,
        stream.avail_in = len;
        do {
                unsigned char *in0 = stream.next_in;
-               ret = deflate(&stream, Z_FINISH);
+               ret = git_deflate(&stream, Z_FINISH);
                git_SHA1_Update(&c, in0, stream.next_in - in0);
                if (write_buffer(fd, compressed, stream.next_out - compressed) < 0)
                        die("unable to write sha1 file");
@@ -2468,7 +2501,7 @@ static int write_loose_object(const unsigned char *sha1, char *hdr, int hdrlen,
 
        if (ret != Z_STREAM_END)
                die("unable to deflate new object %s (%d)", sha1_to_hex(sha1), ret);
-       ret = deflateEnd(&stream);
+       ret = git_deflate_end_gently(&stream);
        if (ret != Z_OK)
                die("deflateEnd on object %s failed (%d)", sha1_to_hex(sha1), ret);
        git_SHA1_Final(parano_sha1, &c);
@@ -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)
index 09c43ae59a7d4715c26f13d72ca37bc759d1c76d..1a7df12e8f233863cd931a960d453d54050f5584 100644 (file)
--- a/strbuf.c
+++ b/strbuf.c
@@ -103,24 +103,27 @@ void strbuf_ltrim(struct strbuf *sb)
        sb->buf[sb->len] = '\0';
 }
 
-struct strbuf **strbuf_split(const struct strbuf *sb, int delim)
+struct strbuf **strbuf_split_buf(const char *str, size_t slen, int delim, int max)
 {
        int alloc = 2, pos = 0;
-       char *n, *p;
+       const char *n, *p;
        struct strbuf **ret;
        struct strbuf *t;
 
        ret = xcalloc(alloc, sizeof(struct strbuf *));
-       p = n = sb->buf;
-       while (n < sb->buf + sb->len) {
+       p = n = str;
+       while (n < str + slen) {
                int len;
-               n = memchr(n, delim, sb->len - (n - sb->buf));
+               if (max <= 0 || pos + 1 < max)
+                       n = memchr(n, delim, slen - (n - str));
+               else
+                       n = NULL;
                if (pos + 1 >= alloc) {
                        alloc = alloc * 2;
                        ret = xrealloc(ret, sizeof(struct strbuf *) * alloc);
                }
                if (!n)
-                       n = sb->buf + sb->len - 1;
+                       n = str + slen - 1;
                len = n - p + 1;
                t = xmalloc(sizeof(struct strbuf));
                strbuf_init(t, len);
index 9e6d9fa53fc04156452bf850af1a9b2d3ba830f2..46a33f8c46985c4377071011d6ea48d6d3fe5331 100644 (file)
--- a/strbuf.h
+++ b/strbuf.h
@@ -44,7 +44,22 @@ extern void strbuf_rtrim(struct strbuf *);
 extern void strbuf_ltrim(struct strbuf *);
 extern int strbuf_cmp(const struct strbuf *, const struct strbuf *);
 
-extern struct strbuf **strbuf_split(const struct strbuf *, int delim);
+extern struct strbuf **strbuf_split_buf(const char *, size_t,
+                                       int delim, int max);
+static inline struct strbuf **strbuf_split_str(const char *str,
+                                              int delim, int max)
+{
+       return strbuf_split_buf(str, strlen(str), delim, max);
+}
+static inline struct strbuf **strbuf_split_max(const struct strbuf *sb,
+                                               int delim, int max)
+{
+       return strbuf_split_buf(sb->buf, sb->len, delim, max);
+}
+static inline struct strbuf **strbuf_split(const struct strbuf *sb, int delim)
+{
+       return strbuf_split_max(sb, delim, 0);
+}
 extern void strbuf_list_free(struct strbuf **);
 
 /*----- add data in your buffer -----*/
diff --git a/streaming.c b/streaming.c
new file mode 100644 (file)
index 0000000..25c9a20
--- /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 */
+       git_zstream 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 b6dec70bd1a6b35c8ecc6a1a9953d64bfe6c4510..1ba9646d3484fe2a11c1faba339c4e333186085e 100644 (file)
@@ -388,6 +388,7 @@ void check_for_new_submodule_commits(unsigned char new_sha1[20])
                while (parent) {
                        struct diff_options diff_opts;
                        diff_setup(&diff_opts);
+                       DIFF_OPT_SET(&diff_opts, RECURSIVE);
                        diff_opts.output_format |= DIFF_FORMAT_CALLBACK;
                        diff_opts.format_callback = submodule_collect_changed_cb;
                        if (diff_setup_done(&diff_opts) < 0)
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 ae266147b6db9e06abeb4dc30e4271b6c611deac..007f39d5e1dca6eff7e01969282c4cf2dc1bb57a 100755 (executable)
@@ -28,6 +28,7 @@ String options
     --st <st>             get another string (pervert ordering)
     -o <str>              get another string
     --default-string      set string to default
+    --list <str>          add str to list
 
 Magic arguments
     --quux                means --quux
@@ -337,4 +338,20 @@ test_expect_success 'negation of OPT_NONEG flags is not ambiguous' '
        test_cmp expect output
 '
 
+cat >>expect <<'EOF'
+list: foo
+list: bar
+list: baz
+EOF
+test_expect_success '--list keeps list of strings' '
+       test-parse-options --list foo --list=bar --list=baz >output &&
+       test_cmp expect output
+'
+
+test_expect_success '--no-list resets list' '
+       test-parse-options --list=other --list=irrelevant --list=options \
+               --no-list --list=foo --list=bar --list=baz >output &&
+       test_cmp expect output
+'
+
 test_done
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 3db56267ee89e1b585a548f7bee13386e47395f4..3e140c18f4183c41c76aaab0834f1d23ce7bcd2d 100755 (executable)
@@ -904,4 +904,22 @@ test_expect_success 'git -c works with aliases of builtins' '
        test_cmp expect actual
 '
 
+test_expect_success 'git -c does not split values on equals' '
+       echo "value with = in it" >expect &&
+       git -c core.foo="value with = in it" config core.foo >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'git -c dies on bogus config' '
+       test_must_fail git -c core.bare=foo rev-parse
+'
+
+test_expect_success 'git -c complains about empty key' '
+       test_must_fail git -c "=foo" rev-parse
+'
+
+test_expect_success 'git -c complains about empty key and value' '
+       test_must_fail git -c "" rev-parse
+'
+
 test_done
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 47c8371c7edfe7945ded380bb6f96a00d26c04eb..8538813d1d0dfa72008da1d711cf2c042796a6ee 100755 (executable)
@@ -295,7 +295,7 @@ test_expect_success 'preserve merges with -p' '
 '
 
 test_expect_success 'edit ancestor with -p' '
-       FAKE_LINES="1 edit 2 3 4" git rebase -i -p HEAD~3 &&
+       FAKE_LINES="1 2 edit 3 4" git rebase -i -p HEAD~3 &&
        echo 2 > unrelated-file &&
        test_tick &&
        git commit -m L2-modified --amend unrelated-file &&
index 08201e2331d36af769d832e3d9551c48331b7033..6de4e2263f9ec65d2de2e28af6a12d5c1065f686 100755 (executable)
@@ -37,7 +37,15 @@ export GIT_AUTHOR_EMAIL
 #      \
 #       B2     <-- origin/topic
 #
-# In all cases, 'topic' is rebased onto 'origin/topic'.
+# Clone 4 (merge using second parent as base):
+#
+# A1--A2--B3   <-- origin/master
+#  \
+#   B1--A3--M  <-- topic
+#    \     /
+#     \--A4    <-- topic2
+#      \
+#       B2     <-- origin/topic
 
 test_expect_success 'setup for merge-preserving rebase' \
        'echo First > A &&
@@ -57,6 +65,13 @@ test_expect_success 'setup for merge-preserving rebase' \
        git merge origin/master
        ) &&
 
+       git clone ./. clone4 &&
+       (
+               cd clone4 &&
+               git checkout -b topic origin/topic &&
+               git merge origin/master
+       ) &&
+
        echo Fifth > B &&
        git add B &&
        git commit -m "Add different B" &&
@@ -123,4 +138,15 @@ test_expect_success 'rebase -p preserves no-ff merges' '
        )
 '
 
+test_expect_success 'rebase -p works when base inside second parent' '
+       (
+       cd clone4 &&
+       git fetch &&
+       git rebase -p HEAD^2 &&
+       test 1 = $(git rev-list --all --pretty=oneline | grep "Modify A" | wc -l) &&
+       test 1 = $(git rev-list --all --pretty=oneline | grep "Modify B" | wc -l) &&
+       test 1 = $(git rev-list --all --pretty=oneline | grep "Merge remote-tracking branch " | wc -l)
+       )
+'
+
 test_done
index 14a23cd8726fbb33df480133ed0466177916b7fb..ace8e54e9b374688751cc470eeb9003ca7f5c48f 100755 (executable)
@@ -37,7 +37,7 @@ test_expect_success 'setup' '
 #        -- C1 --
 #
 test_expect_success 'squash F1 into D1' '
-       FAKE_LINES="1 squash 3 2" git rebase -i -p B1 &&
+       FAKE_LINES="1 squash 4 2 3" git rebase -i -p B1 &&
        test "$(git rev-parse HEAD^2)" = "$(git rev-parse C1)" &&
        test "$(git rev-parse HEAD~2)" = "$(git rev-parse B1)" &&
        git tag E2
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 a1fddd4d15fb2c3cb5d0ab6e09a461314fb4b1f1..ca5b027c557014cff85ca4ba7d118db578a57256 100755 (executable)
@@ -47,7 +47,7 @@ test_expect_success setup '
                git init &&
                echo subcontent > subfile &&
                git add subfile &&
-               git submodule add "$pwd/deepsubmodule" deepsubmodule &&
+               git submodule add "$pwd/deepsubmodule" subdir/deepsubmodule &&
                git commit -a -m new
        ) &&
        git submodule add "$pwd/submodule" submodule &&
@@ -58,7 +58,7 @@ test_expect_success setup '
                git submodule update --init --recursive
        ) &&
        echo "Fetching submodule submodule" > expect.out &&
-       echo "Fetching submodule submodule/deepsubmodule" >> expect.out
+       echo "Fetching submodule submodule/subdir/deepsubmodule" >> expect.out
 '
 
 test_expect_success "fetch --recurse-submodules recurses into submodules" '
@@ -277,12 +277,12 @@ test_expect_success "Recursion picks up all submodules when necessary" '
        (
                cd submodule &&
                (
-                       cd deepsubmodule &&
+                       cd subdir/deepsubmodule &&
                        git fetch &&
                        git checkout -q FETCH_HEAD
                ) &&
                head1=$(git rev-parse --short HEAD^) &&
-               git add deepsubmodule &&
+               git add subdir/deepsubmodule &&
                git commit -m "new deepsubmodule"
                head2=$(git rev-parse --short HEAD) &&
                echo "From $pwd/submodule" > ../expect.err.sub &&
@@ -309,12 +309,12 @@ test_expect_success "'--recurse-submodules=on-demand' doesn't recurse when no ne
        (
                cd submodule &&
                (
-                       cd deepsubmodule &&
+                       cd subdir/deepsubmodule &&
                        git fetch &&
                        git checkout -q FETCH_HEAD
                ) &&
                head1=$(git rev-parse --short HEAD^) &&
-               git add deepsubmodule &&
+               git add subdir/deepsubmodule &&
                git commit -m "new deepsubmodule"
                head2=$(git rev-parse --short HEAD) &&
                echo "From $pwd/submodule" > ../expect.err.sub &&
@@ -345,13 +345,13 @@ test_expect_success "'--recurse-submodules=on-demand' recurses as deep as necess
                git config fetch.recurseSubmodules false &&
                (
                        cd submodule &&
-                       git config -f .gitmodules submodule.deepsubmodule.fetchRecursive false
+                       git config -f .gitmodules submodule.subdir/deepsubmodule.fetchRecursive false
                ) &&
                git fetch --recurse-submodules=on-demand >../actual.out 2>../actual.err &&
                git config --unset fetch.recurseSubmodules
                (
                        cd submodule &&
-                       git config --unset -f .gitmodules submodule.deepsubmodule.fetchRecursive
+                       git config --unset -f .gitmodules submodule.subdir/deepsubmodule.fetchRecursive
                )
        ) &&
        test_i18ncmp expect.out actual.out &&
diff --git a/t/t5708-clone-config.sh b/t/t5708-clone-config.sh
new file mode 100755 (executable)
index 0000000..27d730c
--- /dev/null
@@ -0,0 +1,40 @@
+#!/bin/sh
+
+test_description='tests for git clone -c key=value'
+. ./test-lib.sh
+
+test_expect_success 'clone -c sets config in cloned repo' '
+       rm -rf child &&
+       git clone -c core.foo=bar . child &&
+       echo bar >expect &&
+       git --git-dir=child/.git config core.foo >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'clone -c can set multi-keys' '
+       rm -rf child &&
+       git clone -c core.foo=bar -c core.foo=baz . child &&
+       { echo bar; echo baz; } >expect &&
+       git --git-dir=child/.git config --get-all core.foo >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'clone -c without a value is boolean true' '
+       rm -rf child &&
+       git clone -c core.foo . child &&
+       echo true >expect &&
+       git --git-dir=child/.git config --bool core.foo >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'clone -c config is available during clone' '
+       echo content >file &&
+       git add file &&
+       git commit -m one &&
+       rm -rf child &&
+       git clone -c core.autocrlf . child &&
+       printf "content\\r\\n" >expect &&
+       test_cmp expect child/file
+'
+
+test_done
index 2ac1c66079b7656a60a25fbbb91c48c0b1db010b..097ce2bc8382e748925aa25c2dc0a1f06f6c7b4c 100755 (executable)
@@ -257,6 +257,11 @@ test_expect_success \
        test_cmp expect actual
 '
 
+test_expect_success 'tag -l can accept multiple patterns' '
+       git tag -l "v1*" "v0*" >actual &&
+       test_cmp expect actual
+'
+
 # creating and verifying lightweight tags:
 
 test_expect_success \
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..be745fb23f3f27ed319143d3d3ab3f26d01857bc 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
@@ -292,4 +292,22 @@ test_expect_success 'use "update --recursive nested1" to checkout all submodules
        )
 '
 
+test_expect_success 'command passed to foreach retains notion of stdin' '
+       (
+               cd super &&
+               git submodule foreach echo success >../expected &&
+               yes | git submodule foreach "read y && test \"x\$y\" = xy && echo success" >../actual
+       ) &&
+       test_cmp expected actual
+'
+
+test_expect_success 'command passed to foreach --recursive retains notion of stdin' '
+       (
+               cd clone2 &&
+               git submodule foreach --recursive echo success >../expected &&
+               yes | git submodule foreach --recursive "read y && test \"x\$y\" = xy && echo success" >../actual
+       ) &&
+       test_cmp expected actual
+'
+
 test_done
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 4e3710b9a8fc868642e1e0e8f4af37eeb1ce0ce4..91a5701657556e07b4f4daafa947346d3b197cd1 100644 (file)
@@ -1,5 +1,6 @@
 #include "cache.h"
 #include "parse-options.h"
+#include "string-list.h"
 
 static int boolean = 0;
 static int integer = 0;
@@ -9,6 +10,7 @@ static int verbose = 0, dry_run = 0, quiet = 0;
 static char *string = NULL;
 static char *file = NULL;
 static int ambiguous;
+static struct string_list list;
 
 static int length_callback(const struct option *opt, const char *arg, int unset)
 {
@@ -54,6 +56,7 @@ int main(int argc, const char **argv)
                OPT_STRING('o', NULL, &string, "str", "get another string"),
                OPT_SET_PTR(0, "default-string", &string,
                        "set string to default", (unsigned long)"default"),
+               OPT_STRING_LIST(0, "list", &list, "str", "add str to list"),
                OPT_GROUP("Magic arguments"),
                OPT_ARGUMENT("quux", "means --quux"),
                OPT_NUMBER_CALLBACK(&integer, "set integer to NUM",
@@ -85,6 +88,9 @@ int main(int argc, const char **argv)
        printf("dry run: %s\n", dry_run ? "yes" : "no");
        printf("file: %s\n", file ? file : "(not set)");
 
+       for (i = 0; i < list.nr; i++)
+               printf("list: %s\n", list.items[i].string);
+
        for (i = 0; i < argc; i++)
                printf("arg %02d: %s\n", i, argv[i]);
 
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 @@ return_failed:
                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);
diff --git a/zlib.c b/zlib.c
index c4d58da4e9050c6330ff145914cc379f0600f703..3c63d480c75e9939fb3a047f595b032e9509d681 100644 (file)
--- a/zlib.c
+++ b/zlib.c
  */
 #include "cache.h"
 
-void git_inflate_init(z_streamp strm)
+static const char *zerr_to_string(int status)
 {
-       const char *err;
+       switch (status) {
+       case Z_MEM_ERROR:
+               return "out of memory";
+       case Z_VERSION_ERROR:
+               return "wrong version";
+       case Z_NEED_DICT:
+               return "needs dictionary";
+       case Z_DATA_ERROR:
+               return "data stream error";
+       case Z_STREAM_ERROR:
+               return "stream consistency error";
+       default:
+               return "unknown error";
+       }
+}
 
-       switch (inflateInit(strm)) {
-       case Z_OK:
+/*
+ * avail_in and avail_out in zlib are counted in uInt, which typically
+ * limits the size of the buffer we can use to 4GB when interacting
+ * with zlib in a single call to inflate/deflate.
+ */
+/* #define ZLIB_BUF_MAX ((uInt)-1) */
+#define ZLIB_BUF_MAX ((uInt) 1024 * 1024 * 1024) /* 1GB */
+static inline uInt zlib_buf_cap(unsigned long len)
+{
+       return (ZLIB_BUF_MAX < len) ? ZLIB_BUF_MAX : len;
+}
+
+static void zlib_pre_call(git_zstream *s)
+{
+       s->z.next_in = s->next_in;
+       s->z.next_out = s->next_out;
+       s->z.total_in = s->total_in;
+       s->z.total_out = s->total_out;
+       s->z.avail_in = zlib_buf_cap(s->avail_in);
+       s->z.avail_out = zlib_buf_cap(s->avail_out);
+}
+
+static void zlib_post_call(git_zstream *s)
+{
+       unsigned long bytes_consumed;
+       unsigned long bytes_produced;
+
+       bytes_consumed = s->z.next_in - s->next_in;
+       bytes_produced = s->z.next_out - s->next_out;
+       if (s->z.total_out != s->total_out + bytes_produced)
+               die("BUG: total_out mismatch");
+       if (s->z.total_in != s->total_in + bytes_consumed)
+               die("BUG: total_in mismatch");
+
+       s->total_out = s->z.total_out;
+       s->total_in = s->z.total_in;
+       s->next_in = s->z.next_in;
+       s->next_out = s->z.next_out;
+       s->avail_in -= bytes_consumed;
+       s->avail_out -= bytes_produced;
+}
+
+void git_inflate_init(git_zstream *strm)
+{
+       int status;
+
+       zlib_pre_call(strm);
+       status = inflateInit(&strm->z);
+       zlib_post_call(strm);
+       if (status == Z_OK)
                return;
+       die("inflateInit: %s (%s)", zerr_to_string(status),
+           strm->z.msg ? strm->z.msg : "no message");
+}
 
-       case Z_MEM_ERROR:
-               err = "out of memory";
-               break;
-       case Z_VERSION_ERROR:
-               err = "wrong version";
+void git_inflate_init_gzip_only(git_zstream *strm)
+{
+       /*
+        * Use default 15 bits, +16 is to accept only gzip and to
+        * yield Z_DATA_ERROR when fed zlib format.
+        */
+       const int windowBits = 15 + 16;
+       int status;
+
+       zlib_pre_call(strm);
+       status = inflateInit2(&strm->z, windowBits);
+       zlib_post_call(strm);
+       if (status == Z_OK)
+               return;
+       die("inflateInit2: %s (%s)", zerr_to_string(status),
+           strm->z.msg ? strm->z.msg : "no message");
+}
+
+void git_inflate_end(git_zstream *strm)
+{
+       int status;
+
+       zlib_pre_call(strm);
+       status = inflateEnd(&strm->z);
+       zlib_post_call(strm);
+       if (status == Z_OK)
+               return;
+       error("inflateEnd: %s (%s)", zerr_to_string(status),
+             strm->z.msg ? strm->z.msg : "no message");
+}
+
+int git_inflate(git_zstream *strm, int flush)
+{
+       int status;
+
+       for (;;) {
+               zlib_pre_call(strm);
+               /* Never say Z_FINISH unless we are feeding everything */
+               status = inflate(&strm->z,
+                                (strm->z.avail_in != strm->avail_in)
+                                ? 0 : flush);
+               if (status == Z_MEM_ERROR)
+                       die("inflate: out of memory");
+               zlib_post_call(strm);
+
+               /*
+                * Let zlib work another round, while we can still
+                * make progress.
+                */
+               if ((strm->avail_out && !strm->z.avail_out) &&
+                   (status == Z_OK || status == Z_BUF_ERROR))
+                       continue;
                break;
+       }
+
+       switch (status) {
+       /* Z_BUF_ERROR: normal, needs more space in the output buffer */
+       case Z_BUF_ERROR:
+       case Z_OK:
+       case Z_STREAM_END:
+               return status;
        default:
-               err = "error";
+               break;
        }
-       die("inflateInit: %s (%s)", err, strm->msg ? strm->msg : "no message");
+       error("inflate: %s (%s)", zerr_to_string(status),
+             strm->z.msg ? strm->z.msg : "no message");
+       return status;
 }
 
-void git_inflate_end(z_streamp strm)
+#if defined(NO_DEFLATE_BOUND) || ZLIB_VERNUM < 0x1200
+#define deflateBound(c,s)  ((s) + (((s) + 7) >> 3) + (((s) + 63) >> 6) + 11)
+#endif
+
+unsigned long git_deflate_bound(git_zstream *strm, unsigned long size)
 {
-       if (inflateEnd(strm) != Z_OK)
-               error("inflateEnd: %s", strm->msg ? strm->msg : "failed");
+       return deflateBound(&strm->z, size);
 }
 
-int git_inflate(z_streamp strm, int flush)
+void git_deflate_init(git_zstream *strm, int level)
 {
-       int ret = inflate(strm, flush);
-       const char *err;
+       int status;
 
-       switch (ret) {
-       /* Out of memory is fatal. */
-       case Z_MEM_ERROR:
-               die("inflate: out of memory");
+       zlib_pre_call(strm);
+       status = deflateInit(&strm->z, level);
+       zlib_post_call(strm);
+       if (status == Z_OK)
+               return;
+       die("deflateInit: %s (%s)", zerr_to_string(status),
+           strm->z.msg ? strm->z.msg : "no message");
+}
 
-       /* Data corruption errors: we may want to recover from them (fsck) */
-       case Z_NEED_DICT:
-               err = "needs dictionary"; break;
-       case Z_DATA_ERROR:
-               err = "data stream error"; break;
-       case Z_STREAM_ERROR:
-               err = "stream consistency error"; break;
-       default:
-               err = "unknown error"; break;
+void git_deflate_init_gzip(git_zstream *strm, int level)
+{
+       /*
+        * Use default 15 bits, +16 is to generate gzip header/trailer
+        * instead of the zlib wrapper.
+        */
+       const int windowBits = 15 + 16;
+       int status;
 
+       zlib_pre_call(strm);
+       status = deflateInit2(&strm->z, level,
+                                 Z_DEFLATED, windowBits,
+                                 8, Z_DEFAULT_STRATEGY);
+       zlib_post_call(strm);
+       if (status == Z_OK)
+               return;
+       die("deflateInit2: %s (%s)", zerr_to_string(status),
+           strm->z.msg ? strm->z.msg : "no message");
+}
+
+void git_deflate_end(git_zstream *strm)
+{
+       int status;
+
+       zlib_pre_call(strm);
+       status = deflateEnd(&strm->z);
+       zlib_post_call(strm);
+       if (status == Z_OK)
+               return;
+       error("deflateEnd: %s (%s)", zerr_to_string(status),
+             strm->z.msg ? strm->z.msg : "no message");
+}
+
+int git_deflate_end_gently(git_zstream *strm)
+{
+       int status;
+
+       zlib_pre_call(strm);
+       status = deflateEnd(&strm->z);
+       zlib_post_call(strm);
+       return status;
+}
+
+int git_deflate(git_zstream *strm, int flush)
+{
+       int status;
+
+       for (;;) {
+               zlib_pre_call(strm);
+
+               /* Never say Z_FINISH unless we are feeding everything */
+               status = deflate(&strm->z,
+                                (strm->z.avail_in != strm->avail_in)
+                                ? 0 : flush);
+               if (status == Z_MEM_ERROR)
+                       die("deflate: out of memory");
+               zlib_post_call(strm);
+
+               /*
+                * Let zlib work another round, while we can still
+                * make progress.
+                */
+               if ((strm->avail_out && !strm->z.avail_out) &&
+                   (status == Z_OK || status == Z_BUF_ERROR))
+                       continue;
+               break;
+       }
+
+       switch (status) {
        /* Z_BUF_ERROR: normal, needs more space in the output buffer */
        case Z_BUF_ERROR:
        case Z_OK:
        case Z_STREAM_END:
-               return ret;
+               return status;
+       default:
+               break;
        }
-       error("inflate: %s (%s)", err, strm->msg ? strm->msg : "no message");
-       return ret;
+       error("deflate: %s (%s)", zerr_to_string(status),
+             strm->z.msg ? strm->z.msg : "no message");
+       return status;
 }