Merge branch 'jk/combine-diff-binary-etc'
authorJunio C Hamano <gitster@pobox.com>
Thu, 30 Jun 2011 00:03:10 +0000 (17:03 -0700)
committerJunio C Hamano <gitster@pobox.com>
Thu, 30 Jun 2011 00:03:10 +0000 (17:03 -0700)
* jk/combine-diff-binary-etc:
combine-diff: respect textconv attributes
refactor get_textconv to not require diff_filespec
combine-diff: handle binary files as binary
combine-diff: calculate mode_differs earlier
combine-diff: split header printing into its own function

165 files changed:
.gitignore
Documentation/RelNotes/1.7.5.3.txt [new file with mode: 0644]
Documentation/RelNotes/1.7.5.4.txt [new file with mode: 0644]
Documentation/RelNotes/1.7.6.txt
Documentation/blame-options.txt
Documentation/config.txt
Documentation/diff-options.txt
Documentation/git-blame.txt
Documentation/git-clone.txt
Documentation/git-config.txt
Documentation/git-cvsserver.txt
Documentation/git-diff-index.txt
Documentation/git-diff-tree.txt
Documentation/git-grep.txt
Documentation/git-init-db.txt
Documentation/git-init.txt
Documentation/git-instaweb.txt
Documentation/git-ls-remote.txt
Documentation/git-notes.txt
Documentation/git-read-tree.txt
Documentation/git-remote-helpers.txt
Documentation/git-rev-list.txt
Documentation/git-sh-i18n--envsubst.txt [new file with mode: 0644]
Documentation/git-sh-i18n.txt [new file with mode: 0644]
Documentation/git-sh-setup.txt
Documentation/git-status.txt
Documentation/git.txt
Documentation/gitattributes.txt
Documentation/glossary-content.txt
Documentation/pretty-options.txt
Documentation/rev-list-options.txt
Documentation/technical/api-diff.txt
GIT-VERSION-GEN
Makefile
bisect.c
builtin/blame.c
builtin/branch.c
builtin/cat-file.c
builtin/checkout.c
builtin/clone.c
builtin/commit.c
builtin/config.c
builtin/diff.c
builtin/fetch-pack.c
builtin/fetch.c
builtin/gc.c
builtin/grep.c
builtin/hash-object.c
builtin/init-db.c
builtin/log.c
builtin/ls-remote.c
builtin/mailinfo.c
builtin/merge.c
builtin/notes.c
builtin/read-tree.c
builtin/receive-pack.c
builtin/remote.c
builtin/rev-list.c
builtin/rev-parse.c
builtin/shortlog.c
builtin/show-branch.c
builtin/update-index.c
cache.h
commit.h
compat/fnmatch/fnmatch.c
compat/mingw.c
compat/mingw.h
config.c
config.mak.in
configure.ac
contrib/completion/git-completion.bash
ctype.c
diff-lib.c
diff.c
diff.h
fsck.c
git-add--interactive.perl
git-compat-util.h
git-instaweb.sh
git-rebase--interactive.sh
git-rebase.sh
git-sh-i18n.sh [new file with mode: 0644]
git-sh-setup.sh
git-submodule.sh
git-svn.perl
git.c
gitweb/INSTALL
gitweb/Makefile
gitweb/README
gitweb/gitweb.perl
gitweb/static/gitweb.css
gitweb/static/gitweb.js [deleted file]
gitweb/static/js/README [new file with mode: 0644]
gitweb/static/js/adjust-timezone.js [new file with mode: 0644]
gitweb/static/js/blame_incremental.js [new file with mode: 0644]
gitweb/static/js/javascript-detection.js [new file with mode: 0644]
gitweb/static/js/lib/common-lib.js [new file with mode: 0644]
gitweb/static/js/lib/cookies.js [new file with mode: 0644]
gitweb/static/js/lib/datetime.js [new file with mode: 0644]
grep.c
grep.h
ident.c
log-tree.c
notes-merge.c
pretty.c
read-cache.c
refs.c
remote-curl.c
rerere.c
revision.c
revision.h
setup.c
sh-i18n--envsubst.c [new file with mode: 0644]
sha1-array.c [new file with mode: 0644]
sha1-array.h [new file with mode: 0644]
sha1_file.c
sha1_name.c
t/Makefile
t/README
t/lib-read-tree.sh [new file with mode: 0644]
t/t0001-init.sh
t/t0081-line-buffer.sh
t/t0201-gettext-fallbacks.sh [new file with mode: 0755]
t/t1000-read-tree-m-3way.sh
t/t1001-read-tree-m-2way.sh
t/t1002-read-tree-m-u-2way.sh
t/t1004-read-tree-m-u-wf.sh
t/t1005-read-tree-reset.sh
t/t1008-read-tree-overlay.sh
t/t1011-read-tree-sparse-checkout.sh
t/t1012-read-tree-df.sh
t/t1020-subdirectory.sh
t/t1050-large.sh [new file with mode: 0755]
t/t1300-repo-config.sh
t/t3301-notes.sh
t/t3404-rebase-interactive.sh
t/t3703-add-magic-pathspec.sh [new file with mode: 0755]
t/t3903-stash.sh
t/t4014-format-patch.sh
t/t4018-diff-funcname.sh
t/t4034-diff-words.sh
t/t4040-whitespace-status.sh
t/t4152-am-subjects.sh [new file with mode: 0755]
t/t4202-log.sh
t/t4203-mailmap.sh
t/t4205-log-pretty-formats.sh
t/t4208-log-magic-pathspec.sh [new file with mode: 0755]
t/t5505-remote.sh
t/t5506-remote-groups.sh
t/t5512-ls-remote.sh
t/t5516-fetch-push.sh
t/t7508-status.sh
t/t7810-grep.sh
t/t8008-blame-formats.sh [new file with mode: 0755]
t/t9159-git-svn-no-parent-mergeinfo.sh [new file with mode: 0755]
t/t9300-fast-import.sh
t/t9500-gitweb-standalone-no-errors.sh
t/test-lib.sh
transport.c
transport.h
tree-diff.c
unpack-trees.c
unpack-trees.h
userdiff.c
wrapper.c
index 711fce7e08beea95d1870047452131e3bf5278dd..acffdfaae684dc49d7205d580fd8921ca4696f7f 100644 (file)
 /git-rm
 /git-send-email
 /git-send-pack
+/git-sh-i18n
+/git-sh-i18n--envsubst
 /git-sh-setup
+/git-sh-i18n
 /git-shell
 /git-shortlog
 /git-show
 /gitk-git/gitk-wish
 /gitweb/GITWEB-BUILD-OPTIONS
 /gitweb/gitweb.cgi
+/gitweb/static/gitweb.js
 /gitweb/static/gitweb.min.*
 /test-chmtime
 /test-ctype
diff --git a/Documentation/RelNotes/1.7.5.3.txt b/Documentation/RelNotes/1.7.5.3.txt
new file mode 100644 (file)
index 0000000..9c03353
--- /dev/null
@@ -0,0 +1,32 @@
+Git v1.7.5.3 Release Notes
+==========================
+
+Fixes since v1.7.5.2
+--------------------
+
+ * The bash completion scripts should correctly work using zsh's bash
+   completion emulation layer now.
+
+ * Setting $(prefix) in config.mak did not affect where etc/gitconfig
+   file is read from, even though passing it from the command line of
+   $(MAKE) did.
+
+ * The logic to handle "&" (expand to UNIX username) in GECOS field
+   miscounted the length of the name it formatted.
+
+ * "git cherry-pick -s resolve" failed to cherry-pick a root commit.
+
+ * "git diff --word-diff" misbehaved when diff.suppress-blank-empty was
+   in effect.
+
+ * "git log --stdin path" with an input that has additional pathspec
+   used to corrupt memory.
+
+ * "git send-pack" (hence "git push") over smalt-HTTP protocol could
+   deadlock when the client side pack-object died early.
+
+ * Compressed tarball gitweb generates used to be made with the timestamp
+   of the tarball generation; this was bad because snapshot from the same
+   tree should result in a same tarball.
+
+And other minor fixes and documentation updates.
diff --git a/Documentation/RelNotes/1.7.5.4.txt b/Documentation/RelNotes/1.7.5.4.txt
new file mode 100644 (file)
index 0000000..cf3f455
--- /dev/null
@@ -0,0 +1,21 @@
+Git v1.7.5.4 Release Notes
+==========================
+
+Fixes since v1.7.5.3
+--------------------
+
+ * The single-key mode of "git add -p" was easily fooled into thinking
+   that it was told to add everthing ('a') when up-arrow was pressed by
+   mistake.
+
+ * Setting a git command that uses custom configuration via "-c var=val"
+   as an alias caused a crash due to a realloc(3) failure.
+
+ * "git diff -C -C" used to disable the rename detection entirely when
+   there are too many copy candidate paths in the tree; now it falls
+   back to "-C" when doing so would keep the copy candidate paths
+   under the rename detection limit.
+
+ * "git rerere" did not diagnose a corrupt MERGE_RR file in some cases.
+
+And other minor fixes and documentation updates.
index f5faf280c5b9d4ce781ec23cb7a318e09d82d724..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
@@ -6,7 +6,8 @@ Updates since v1.7.5
 
  * Various git-svn updates.
 
- * Updates the way content tags are handled in gitweb.
+ * Updates the way content tags are handled in gitweb.  Also adds
+   a UI to choose common timezone for displaying the dates.
 
  * Similar to branch names, tagnames that begin with "-" are now
    disallowed.
@@ -14,24 +15,44 @@ Updates since v1.7.5
  * Clean-up of the C part of i18n (but not l10n---please wait)
    continues.
 
+ * The scripting part of the codebase is getting prepared for i18n/l10n.
+
+ * Pushing and pulling from a repository with large number of refs that
+   point to identical commits are optimized by not listing the same commit
+   during the common ancestor negotiation exchange with the other side.
+
+ * Adding a file larger than core.bigfilethreshold (defaults to 1/2 Gig)
+   using "git add" will send the contents straight to a packfile without
+   having to hold it and its compressed representation both at the same
+   time in memory.
+
  * Processes spawned by "[alias] <name> = !process" in the configuration
    can inspect GIT_PREFIX environment variable to learn where in the
    working tree the original command was invoked.
 
+ * A magic pathspec ":/" tells a command that limits its operation to
+   the current directory when ran from a subdirectory to work on the
+   entire working tree. In general, ":/path/to/file" would be relative
+   to the root of the working tree hierarchy.
+
+   After "git reset --hard; edit Makefile; cd t/", "git add -u" would
+   be a no-op, but "git add -u :/" would add the updated contents of
+   the Makefile at the top level. If you want to name a path in the
+   current subdirectory whose unusual name begins with ":/", you can
+   name it by "./:/that/path" or by "\:/that/path".
+
  * "git blame" learned "--abbrev[=<n>]" option to control the minimum
    number of hexdigits shown for commit object names.
 
+ * "git blame" learned "--line-porcelain" that is less efficient but is
+   easier to parse.
+
  * Aborting "git commit --interactive" discards updates to the index
-   made during the interctive session.
+   made during the interactive session.
 
  * "git commit" learned a "--patch" option to directly jump to the
    per-hunk selection UI of the interactive mode.
 
- * "git diff -C -C" used to disable the rename detection entirely when
-   there are too many copy candidate paths in the tree; now it falls
-   back to "-C" when doing so would keep the copy candidate paths
-   under the rename detection limit.
-
  * "git diff" and its family of commands learned --dirstat=0 to show
    directories that contribute less than 0.1% of changes.
 
@@ -46,10 +67,20 @@ Updates since v1.7.5
    characters in it, e.g. "Junio C. Hamano" <jch@example.com>.  Earlier
    it was up to the user to do this when using its output.
 
+ * "git format-patch" can take an empty --subject-prefix now.
+
+ * "git grep" learned the "-P" option to take pcre regular expressions.
+
  * "git log" and friends learned a new "--notes" option to replace the
    "--show-notes" option.  Unlike "--show-notes", "--notes=<ref>" does
    not imply showing the default notes.
 
+ * They also learned a log.abbrevCommit configuration variable to augment
+   the --abbrev-commit command line option.
+
+ * "git ls-remote" learned "--exit-code" option to consider it a
+   different kind of error when no remote ref to be shown.
+
  * "git merge" learned "-" as a short-hand for "the previous branch", just
    like the way "git checkout -" works.
 
@@ -61,9 +92,20 @@ Updates since v1.7.5
 
  * p4-import (from contrib) learned a new option --preserve-user.
 
+ * "git read-tree -m" learned "--dry-run" option that reports if a merge
+   would fail without touching the index nor the working tree.
+
  * "git rebase" that does not specify on top of which branch to rebase
    the current branch now uses @{upstream} of the current branch.
 
+ * "git rebase" finished either normally or with --abort did not
+   update the reflog for HEAD to record the event to come back to
+   where it started from.
+
+ * "git remote add -t only-this-branch --mirror=fetch" is now allowed. Earlier
+   a fetch-mode mirror meant mirror everything, but now it only means refs are
+   not renamed.
+
  * "git rev-list --count" used with "--cherry-mark" counts the cherry-picked
    commits separately, producing more a useful output.
 
@@ -74,10 +116,6 @@ Updates since v1.7.5
    still in a conflicted state during a merge, to avoid using information
    that is not final and possibly corrupt with conflict markers.
 
- * Compressed tarball gitweb generates is made without the timestamp of
-   the tarball generation; snapshot from the same tree should result in
-   a same tarball.
-
 Also contains various documentation updates and minor miscellaneous
 changes.
 
@@ -88,36 +126,11 @@ Fixes since v1.7.5
 Unless otherwise noted, all the fixes in 1.7.5.X maintenance track are
 included in this release.
 
- * Setting $(prefix) in config.mak did not affect where etc/gitconfig
-   file is read from, even though passing it from the command line of
-   $(MAKE) did.
-   (merge kk/maint-prefix-in-config-mak later)
-
- * The bash completion scripts should correctly work using zsh's bash
-   completion emulation layer now.
-   (merge fc/completion-zsh later)
-
- * The logic to handle "&" (expand to UNIX username) in GECOS field
-   miscounted the length of the name it formatted.
-   (merge rg/copy-gecos-username later)
-
- * "git cherry-pick -s resolve" failed to cherry-pick a root commit.
-   (merge jk/cherry-pick-root-with-resolve later)
-
  * "git config" used to choke with an insanely long line.
    (merge ef/maint-strbuf-init later)
 
- * "git log --stdin path" with an input that has additional pathspec
-   used to corrupt memory.
-   (merge jc/maint-pathspec-stdin-and-cmdline later)
-
- * "git send-pack" (hence "git push") over smalt-HTTP protocol could
-   deadlock when the client side pack-object died early.
-   (merge js/maint-send-pack-stateless-rpc-deadlock-fix later)
-   (merge jk/git-connection-deadlock-fix later)
+ * "git diff --quiet" did not work well with --diff-filter.
+   (merge jk/diff-not-so-quick later)
 
----
-exec >/var/tmp/1
-echo O=$(git describe master)
-O=v1.7.5.1-339-g254fd97
-git shortlog --no-merges ^maint ^$O master
+ * "git status -z" did not default to --porcelain output format.
+   (merge bc/maint-status-z-to-use-porcelain later)
index 16e3c685762a311eda1bbc39adb8ab19e000ec97..e76195ac9721277e9ef270be8eb97dba2462bc37 100644 (file)
@@ -52,6 +52,11 @@ of lines before or after the line given by <start>.
 --porcelain::
        Show in a format designed for machine consumption.
 
+--line-porcelain::
+       Show the porcelain format, but output commit information for
+       each line, not just the first time a commit is referenced.
+       Implies --porcelain.
+
 --incremental::
        Show the result incrementally in a format designed for
        machine consumption.
index 1a060ecbc8b137bff3df2c689d7e803937c66d18..6b937771994f5b0a532b6f2cc522a9d3f35c9c09 100644 (file)
@@ -1315,6 +1315,11 @@ interactive.singlekey::
        setting is silently ignored if portable keystroke input
        is not available.
 
+log.abbrevCommit::
+       If true, makes linkgit:git-log[1], linkgit:git-show[1], and
+       linkgit:git-whatchanged[1] assume `\--abbrev-commit`. You may
+       override this option with `\--no-abbrev-commit`.
+
 log.date::
        Set the default date-time mode for the 'log' command.
        Setting a value for log.date is similar to using 'git log''s
index c7ed94635786789baca1de41d482cd6334d1181a..24f189f96b760dc9201a202827351ca82aab6780 100644 (file)
@@ -224,10 +224,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::
index bb8edb4abc5adc8a7b9d46d839415997919871af..9516914236bbfa675006994620b1c8f61855de1d 100644 (file)
@@ -105,6 +105,19 @@ The contents of the actual line is output after the above
 header, prefixed by a TAB. This is to allow adding more
 header elements later.
 
+The porcelain format generally suppresses commit information that has
+already been seen. For example, two lines that are blamed to the same
+commit will both be shown, but the details for that commit will be shown
+only once. This is more efficient, but may require more state be kept by
+the reader. The `--line-porcelain` option can be used to output full
+commit information for each line, allowing simpler (but less efficient)
+usage like:
+
+       # count the number of lines attributed to each author
+       git blame --line-porcelain file |
+       sed -n 's/^author //p' |
+       sort | uniq -c | sort -rn
+
 
 SPECIFYING RANGES
 -----------------
index 86eb4c93682aa3d92fe001ae871913fc0dec8607..b093e45497248076c335391f3e95b6384520ef6b 100644 (file)
@@ -12,7 +12,7 @@ SYNOPSIS
 'git clone' [--template=<template_directory>]
          [-l] [-s] [--no-hardlinks] [-q] [-n] [--bare] [--mirror]
          [-o <name>] [-b <name>] [-u <upload-pack>] [--reference <repository>]
-         [--separate-git-dir|-L <git dir>]
+         [--separate-git-dir <git dir>]
          [--depth <depth>] [--recursive|--recurse-submodules] [--] <repository>
          [<directory>]
 
@@ -177,7 +177,6 @@ objects from the source repository into a pack in the cloned repository.
        repository does not have a worktree/checkout (i.e. if any of
        `--no-checkout`/`-n`, `--bare`, or `--mirror` is given)
 
--L=<git dir>::
 --separate-git-dir=<git dir>::
        Instead of placing the cloned repository where it is supposed
        to be, place the cloned repository at the specified directory,
index 8804de327fd5973ae5d36bff18cfcac3e738763d..e7ecf5d803e14dfa452671cf01e7730dce48b984 100644 (file)
@@ -50,16 +50,18 @@ The default is to assume the config file of the current repository,
 .git/config unless defined otherwise with GIT_DIR and GIT_CONFIG
 (see <<FILES>>).
 
-This command will fail if:
-
-. The config file is invalid,
-. Can not write to the config file,
-. no section was provided,
-. the section or key is invalid,
-. you try to unset an option which does not exist,
-. you try to unset/set an option for which multiple lines match, or
-. you use '--global' option without $HOME being properly set.
-
+This command will fail (with exit code ret) if:
+
+. The config file is invalid (ret=3),
+. can not write to the config file (ret=4),
+. no section or name was provided (ret=2),
+. the section or key is invalid (ret=1),
+. you try to unset an option which does not exist (ret=5),
+. you try to unset/set an option for which multiple lines match (ret=5),
+. you try to use an invalid regexp (ret=6), or
+. you use '--global' option without $HOME being properly set (ret=128).
+
+On success, the command returns the exit code 0.
 
 OPTIONS
 -------
index 88d814af0e050eb3b31f5763cbf12f03a5f6cbc3..827bc988ed5562c2dbc6566d6732b01f20bb4f05 100644 (file)
@@ -252,7 +252,7 @@ Configuring database backend
 
 'git-cvsserver' uses the Perl DBI module. Please also read
 its documentation if changing these variables, especially
-about `DBI->connect()`.
+about `DBI\->connect()`.
 
 gitcvs.dbname::
        Database name. The exact meaning depends on the
index 6d184864022cef90e284113a627103db99327052..2ea22abca2bc72abb0cedfe6f47616c52a60d3f4 100644 (file)
@@ -96,8 +96,8 @@ show that. So let's say that you have edited `kernel/sched.c`, but
 have not actually done a 'git update-index' on it yet - there is no
 "object" associated with the new state, and you get:
 
-  torvalds@ppc970:~/v2.6/linux> git diff-index HEAD
-  *100644->100664 blob    7476bb......->000000......      kernel/sched.c
+  torvalds@ppc970:~/v2.6/linux> git diff-index --abbrev HEAD
+  :100644 100664 7476bb... 000000...      kernel/sched.c
 
 i.e., it shows that the tree has changed, and that `kernel/sched.c` has is
 not up-to-date and may contain new stuff. The all-zero sha1 means that to
index 4e5f127efa634dd4ea68bb73508c59bd1cad32e5..1439486e40ec4a8a1146310bf929d688eb01d533 100644 (file)
@@ -138,8 +138,8 @@ so it can be used to name subdirectories.
 
 An example of normal usage is:
 
-  torvalds@ppc970:~/git> git diff-tree 5319e4......
-  *100664->100664 blob    ac348b.......->a01513.......      git-fsck-objects.c
+  torvalds@ppc970:~/git> git diff-tree --abbrev 5319e4
+  :100664 100664 ac348b... a01513...   git-fsck-objects.c
 
 which tells you that the last commit changed just one file (it's from
 this one:
index d7523b3e45ff06bd7a8abf4ff466fe619c8268e7..e150c77cffb5d082822bd65c7bfbeb1f431fc42f 100644 (file)
@@ -12,7 +12,8 @@ SYNOPSIS
 'git grep' [-a | --text] [-I] [-i | --ignore-case] [-w | --word-regexp]
           [-v | --invert-match] [-h|-H] [--full-name]
           [-E | --extended-regexp] [-G | --basic-regexp]
-          [-F | --fixed-strings] [-n]
+          [-P | --perl-regexp]
+          [-F | --fixed-strings] [-n | --line-number]
           [-l | --files-with-matches] [-L | --files-without-match]
           [(-O | --open-files-in-pager) [<pager>]]
           [-z | --null]
@@ -97,6 +98,11 @@ OPTIONS
        Use POSIX extended/basic regexp for patterns.  Default
        is to use basic regexp.
 
+-P::
+--perl-regexp::
+       Use Perl-compatible regexp for patterns. Requires libpcre to be
+       compiled in.
+
 -F::
 --fixed-strings::
        Use fixed strings for patterns (don't interpret pattern
index 2c4c716f33f9dd3f0b876a013e22ac115ac3c936..9f97f5a91584a7469a3b0d2f8705febbe5c5ab58 100644 (file)
@@ -8,7 +8,7 @@ git-init-db - Creates an empty git repository
 
 SYNOPSIS
 --------
-'git init-db' [-q | --quiet] [--bare] [--template=<template_directory>] [--shared[=<permissions>]]
+'git init-db' [-q | --quiet] [--bare] [--template=<template_directory>] [--separate-git-dir <git dir>] [--shared[=<permissions>]]
 
 
 DESCRIPTION
index 58cd01145ab8605d30a9c670ee70391b9d974a32..f2777a7786e6bf9c0cb907c77134b06321523654 100644 (file)
@@ -9,7 +9,7 @@ git-init - Create an empty git repository or reinitialize an existing one
 SYNOPSIS
 --------
 'git init' [-q | --quiet] [--bare] [--template=<template_directory>]
-         [--separate-git-dir|-L <git dir>]
+         [--separate-git-dir <git dir>]
          [--shared[=<permissions>]] [directory]
 
 
@@ -54,7 +54,6 @@ current working directory.
 Specify the directory from which templates will be used.  (See the "TEMPLATE
 DIRECTORY" section below.)
 
--L=<git dir>::
 --separate-git-dir=<git dir>::
 
 Instead of initializing the repository where it is supposed to be,
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 c3df8c0ebe48af7192f7d593c13af69825b6560f..7a9b86a58a1c00c08803e9ef40bb6a17146125a9 100644 (file)
@@ -10,7 +10,7 @@ SYNOPSIS
 --------
 [verse]
 'git ls-remote' [--heads] [--tags]  [-u <exec> | --upload-pack <exec>]
-             <repository> [<refs>...]
+             [--exit-code] <repository> [<refs>...]
 
 DESCRIPTION
 -----------
@@ -36,6 +36,12 @@ OPTIONS
        SSH and where the SSH daemon does not use the PATH configured by the
        user.
 
+--exit-code::
+       Exit with status "2" when no matching refs are found in the remote
+       repository. Usually the command exits with status "0" to indicate
+       it successfully talked with the remote repository, whether it
+       found any matching refs.
+
 <repository>::
        Location of the repository.  The shorthand defined in
        $GIT_DIR/branches/ can be used. Use "." (dot) to list references in
index 913ecd8c433b7ebaaf932dd5cf9b789dc06b7b95..6a187f2e2336575fb4974b8c492359913a8c7a9e 100644 (file)
@@ -17,7 +17,7 @@ SYNOPSIS
 'git notes' merge [-v | -q] [-s <strategy> ] <notes_ref>
 'git notes' merge --commit [-v | -q]
 'git notes' merge --abort [-v | -q]
-'git notes' remove [<object>]
+'git notes' remove [--ignore-missing] [--stdin] [<object>...]
 'git notes' prune [-n | -v]
 'git notes' get-ref
 
@@ -106,8 +106,9 @@ When done, the user can either finalize the merge with
 'git notes merge --abort'.
 
 remove::
-       Remove the notes for a given object (defaults to HEAD).
-       This is equivalent to specifying an empty note message to
+       Remove the notes for given objects (defaults to HEAD). When
+       giving zero or one object from the command line, this is
+       equivalent to specifying an empty note message to
        the `edit` subcommand.
 
 prune::
@@ -154,6 +155,15 @@ OPTIONS
        'GIT_NOTES_REF' and the "core.notesRef" configuration.  The ref
        is taken to be in `refs/notes/` if it is not qualified.
 
+--ignore-missing::
+       Do not consider it an error to request removing notes from an
+       object that does not have notes attached to it.
+
+--stdin::
+       Also read the object names to remove notes from from the standard
+       input (there is no reason you cannot combine this with object
+       names from the command line).
+
 -n::
 --dry-run::
        Do not remove anything; just report the object names whose notes
index 26fdadc642a3534c4005488cb9ca2b2a47a495a1..46a96f2313e0cb48bff9d5de26f403b35d327d2c 100644 (file)
@@ -53,6 +53,11 @@ OPTIONS
        trees that are not directly related to the current
        working tree status into a temporary index file.
 
+-n::
+--dry-run::
+       Check if the command would error out, without updating the index
+       nor the files in the working tree for real.
+
 -v::
        Show the progress of checking files out.
 
index 87cd11f2c47b66387d31c38f648c02f4c2734955..58f6ad4994dae770b1898888d3073fca777743af 100644 (file)
@@ -181,11 +181,11 @@ CAPABILITIES
        When using the import command, expect the source ref to have
        been written to the destination ref. The earliest applicable
        refspec takes precedence. For example
-       "refs/heads/*:refs/svn/origin/branches/*" means that, after an
-       "import refs/heads/name", the script has written to
+       "refs/heads/{asterisk}:refs/svn/origin/branches/{asterisk}" means
+       that, after an "import refs/heads/name", the script has written to
        refs/svn/origin/branches/name. If this capability is used at
        all, it must cover all refs reported by the list command; if
-       it is not used, it is effectively "*:*"
+       it is not used, it is effectively "{asterisk}:{asterisk}"
 
 REF LIST ATTRIBUTES
 -------------------
index 415f4f0b303da22e9eec19c1b9e24732a74ecf67..38fafcaa6b4173ad7f900c71efaa8ea25470072f 100644 (file)
@@ -29,6 +29,7 @@ SYNOPSIS
             [ \--tags[=<pattern>] ]
             [ \--remotes[=<pattern>] ]
             [ \--glob=<glob-pattern> ]
+            [ \--ignore-missing ]
             [ \--stdin ]
             [ \--quiet ]
             [ \--topo-order ]
diff --git a/Documentation/git-sh-i18n--envsubst.txt b/Documentation/git-sh-i18n--envsubst.txt
new file mode 100644 (file)
index 0000000..61e4c08
--- /dev/null
@@ -0,0 +1,36 @@
+git-sh-i18n--envsubst(1)
+========================
+
+NAME
+----
+git-sh-i18n--envsubst - Git's own envsubst(1) for i18n fallbacks
+
+SYNOPSIS
+--------
+[verse]
+eval_gettext () {
+       printf "%s" "$1" | (
+               export PATH $('git sh-i18n--envsubst' --variables "$1");
+               'git sh-i18n--envsubst' "$1"
+       )
+}
+
+DESCRIPTION
+-----------
+
+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
+`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.
+
+No promises are made about the interface, or that this
+program won't disappear without warning in the next version
+of Git. Don't use it.
+
+GIT
+---
+Part of the linkgit:git[1] suite
diff --git a/Documentation/git-sh-i18n.txt b/Documentation/git-sh-i18n.txt
new file mode 100644 (file)
index 0000000..3b1f7ac
--- /dev/null
@@ -0,0 +1,42 @@
+git-sh-i18n(1)
+==============
+
+NAME
+----
+git-sh-i18n - Git's i18n setup code for shell scripts
+
+SYNOPSIS
+--------
+'. "$(git --exec-path)/git-sh-i18n"'
+
+DESCRIPTION
+-----------
+
+This is not a command the end user would want to run.  Ever.
+This documentation is meant for people who are studying the
+Porcelain-ish scripts and/or are writing new ones.
+
+The 'git sh-i18n scriptlet is designed to be sourced (using
+`.`) by Git's porcelain programs implemented in shell
+script. It provides wrappers for the GNU `gettext` and
+`eval_gettext` functions accessible through the `gettext.sh`
+script, and provides pass-through fallbacks on systems
+without GNU gettext.
+
+FUNCTIONS
+---------
+
+gettext::
+       Currently a dummy fall-through function implemented as a wrapper
+       around `printf(1)`. Will be replaced by a real gettext
+       implementation in a later version.
+
+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
+       real gettext implementation in a later version.
+
+GIT
+---
+Part of the linkgit:git[1] suite
index 053df505bc3aaa5e74c3a9266858eef0de1c2bd2..27fd8ba854698dcd31f5348ddc00a607abb64965 100644 (file)
@@ -58,9 +58,14 @@ cd_to_toplevel::
        runs chdir to the toplevel of the working tree.
 
 require_work_tree::
-       checks if the repository is a bare repository, and dies
-       if so.  Used by scripts that require working tree
-       (e.g. `checkout`).
+       checks if the current directory is within the working tree
+       of the repository, and otherwise dies.
+
+require_work_tree_exists::
+       checks if the working tree associated with the repository
+       exists, and otherwise dies.  Often done before calling
+       cd_to_toplevel, which is impossible to do if there is no
+       working tree.
 
 get_author_ident_from_commit::
        outputs code for use with eval to set the GIT_AUTHOR_NAME,
index 00b699fef71b45c78e298fa48609b553a32fd175..38cb741f180e0869a91a61a5f7706eb664b86d91 100644 (file)
@@ -32,9 +32,10 @@ OPTIONS
        Show the branch and tracking info even in short-format.
 
 --porcelain::
-       Give the output in a stable, easy-to-parse format for scripts.
-       Currently this is identical to --short output, but is guaranteed
-       not to change in the future, making it safe for scripts.
+       Give the output in an easy-to-parse format for scripts.
+       This is similar to the short output, but will remain stable
+       across git versions and regardless of user configuration. See
+       below for details.
 
 -u[<mode>]::
 --untracked-files[=<mode>]::
@@ -78,23 +79,27 @@ OUTPUT
 The output from this command is designed to be used as a commit
 template comment, and all the output lines are prefixed with '#'.
 The default, long format, is designed to be human readable,
-verbose and descriptive.  They are subject to change in any time.
+verbose and descriptive.  Its contents and format are subject to change
+at any time.
 
 The paths mentioned in the output, unlike many other git commands, are
 made relative to the current directory if you are working in a
 subdirectory (this is on purpose, to help cutting and pasting). See
 the status.relativePaths config option below.
 
-In short-format, the status of each path is shown as
+Short Format
+~~~~~~~~~~~~
+
+In the short-format, the status of each path is shown as
 
        XY PATH1 -> PATH2
 
-where `PATH1` is the path in the `HEAD`, and -> PATH2` part is
+where `PATH1` is the path in the `HEAD`, and the ` \-> PATH2` part is
 shown only when `PATH1` corresponds to a different path in the
 index/worktree (i.e. the file is renamed). The 'XY' is a two-letter
 status code.
 
-The fields (including the `->`) are separated from each other by a
+The fields (including the `\->`) are separated from each other by a
 single space. If a filename contains whitespace or other nonprintable
 characters, that field will be quoted in the manner of a C string
 literal: surrounded by ASCII double quote (34) characters, and with
@@ -143,10 +148,25 @@ If -b is used the short-format status is preceded by a line
 
 ## branchname tracking info
 
-There is an alternate -z format recommended for machine parsing.  In
+Porcelain Format
+~~~~~~~~~~~~~~~~
+
+The porcelain format is similar to the short format, but is guaranteed
+not to change in a backwards-incompatible way between git versions or
+based on user configuration. This makes it ideal for parsing by scripts.
+The description of the short format above also describes the porcelain
+format, with a few exceptions:
+
+1. The user's color.status configuration is not respected; color will
+   always be off.
+
+2. The user's status.relativePaths configuration is not respected; paths
+   shown will always be relative to the repository root.
+
+There is also an alternate -z format recommended for machine parsing. In
 that format, the status field is the same, but some other things
-change.  First, the '->' is omitted from rename entries and the field
-order is reversed (e.g 'from -> to' becomes 'to from'). Second, a NUL
+change.  First, the '\->' is omitted from rename entries and the field
+order is reversed (e.g 'from \-> to' becomes 'to from'). Second, a NUL
 (ASCII 0) follows each filename, replacing space as a field separator
 and the terminating newline (but a space still separates the status
 field from the first filename).  Third, filenames containing special
index 19d4deff7031b433eb1f6b80c873b86183faefbb..3c7a832343ea983f7122c5d33429a4129fdba70a 100644 (file)
@@ -44,9 +44,16 @@ 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.5.2/git.html[documentation for release 1.7.5.2]
+* 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
+  link:RelNotes/1.7.5.4.txt[1.7.5.4],
+  link:RelNotes/1.7.5.3.txt[1.7.5.3],
   link:RelNotes/1.7.5.2.txt[1.7.5.2],
   link:RelNotes/1.7.5.1.txt[1.7.5.1],
   link:RelNotes/1.7.5.txt[1.7.5].
index 15aebc60623d97053aa14f3e6463d12eb8f60f26..412c55b549354471a56a4e56ca5533277c83bc54 100644 (file)
@@ -593,6 +593,37 @@ and now produces better output), you can remove the cache
 manually with `git update-ref -d refs/notes/textconv/jpg` (where
 "jpg" is the name of the diff driver, as in the example above).
 
+Choosing textconv versus external diff
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+If you want to show differences between binary or specially-formatted
+blobs in your repository, you can choose to use either an external diff
+command, or to use textconv to convert them to a diff-able text format.
+Which method you choose depends on your exact situation.
+
+The advantage of using an external diff command is flexibility. You are
+not bound to find line-oriented changes, nor is it necessary for the
+output to resemble unified diff. You are free to locate and report
+changes in the most appropriate way for your data format.
+
+A textconv, by comparison, is much more limiting. You provide a
+transformation of the data into a line-oriented text format, and git
+uses its regular diff tools to generate the output. There are several
+advantages to choosing this method:
+
+1. Ease of use. It is often much simpler to write a binary to text
+   transformation than it is to perform your own diff. In many cases,
+   existing programs can be used as textconv filters (e.g., exif,
+   odt2txt).
+
+2. Git diff features. By performing only the transformation step
+   yourself, you can still utilize many of git's diff features,
+   including colorization, word-diff, and combined diffs for merges.
+
+3. Caching. Textconv caching can speed up repeated diffs, such as those
+   you might trigger by running `git log -p`.
+
+
 Marking files as binary
 ^^^^^^^^^^^^^^^^^^^^^^^
 
index 33716a31d0c60093c14f894ef07a87bee73fafc9..8f62d1abeeb9b80fd36aa8ea12120c640a98414e 100644 (file)
@@ -277,7 +277,8 @@ This commit is referred to as a "merge commit", or sometimes just a
        Pattern used to specify paths.
 +
 Pathspecs are used on the command line of "git ls-files", "git
-ls-tree", "git grep", "git checkout", and many other commands to
+ls-tree", "git add", "git grep", "git diff", "git checkout",
+and many other commands to
 limit the scope of operations to some subset of the tree or
 worktree.  See the documentation of each command for whether
 paths are relative to the current directory or toplevel.  The
@@ -296,6 +297,37 @@ For example, Documentation/*.jpg will match all .jpg files
 in the Documentation subtree,
 including Documentation/chapter_1/figure_1.jpg.
 
++
+A pathspec that begins with a colon `:` has special meaning.  In the
+short form, the leading colon `:` is followed by zero or more "magic
+signature" letters (which optionally is terminated by another colon `:`),
+and the remainder is the pattern to match against the path. The optional
+colon that terminates the "magic signature" can be omitted if the pattern
+begins with a character that cannot be a "magic signature" and is not a
+colon.
++
+In the long form, the leading colon `:` is followed by a open
+parenthesis `(`, a comma-separated list of zero or more "magic words",
+and a close parentheses `)`, and the remainder is the pattern to match
+against the path.
++
+The "magic signature" consists of an ASCII symbol that is not
+alphanumeric.
++
+--
+top `/`;;
+       The magic word `top` (mnemonic: `/`) makes the pattern match
+       from the root of the working tree, even when you are running
+       the command from inside a subdirectory.
+--
++
+Currently only the slash `/` is recognized as the "magic signature",
+but it is envisioned that we will support more types of magic in later
+versions of git.
++
+A pathspec with only a colon means "there is no pathspec". This form
+should not be combined with other pathspec.
+
 [[def_parent]]parent::
        A <<def_commit_object,commit object>> contains a (possibly empty) list
        of the logical predecessor(s) in the line of development, i.e. its
index d5c977262a818683ebd47d77d37a26c66737119f..2a3dc8664f16957a05bc4d81824d7995517ac89c 100644 (file)
@@ -19,6 +19,11 @@ configuration (see linkgit:git-config[1]).
 This should make "--pretty=oneline" a whole lot more readable for
 people using 80-column terminals.
 
+--no-abbrev-commit::
+       Show the full 40-byte hexadecimal commit object name. This negates
+       `--abbrev-commit` and those options which imply it such as
+       "--oneline". It also overrides the 'log.abbrevCommit' variable.
+
 --oneline::
        This is a shorthand for "--pretty=oneline --abbrev-commit"
        used together.
index 52bae31fcb7f741c34911d94a9253a2a546ee2c2..7e7ba68781f98b55f4189fe0fb4dd6e96c47c0af 100644 (file)
@@ -139,6 +139,10 @@ parents) and `--max-parents=-1` (negative numbers denote no upper limit).
        is automatically prepended if missing. If pattern lacks '?', '*',
        or '[', '/*' at the end is implied.
 
+--ignore-missing::
+
+       Upon seeing an invalid object name in the input, pretend as if
+       the bad input was not given.
 
 ifndef::git-rev-list[]
 --bisect::
index 20b0241d30026747391fa4b6b38de5cf959cee70..2d2ebc04b74ef2b1bf39df892d303d37076325d1 100644 (file)
@@ -32,7 +32,7 @@ Calling sequence
 
 * As you find different pairs of files, call `diff_change()` to feed
   modified files, `diff_addremove()` to feed created or deleted files,
-  or `diff_unmerged()` to feed a file whose state is 'unmerged' to the
+  or `diff_unmerge()` to feed a file whose state is 'unmerged' to the
   API.  These are thin wrappers to a lower-level `diff_queue()` function
   that is flexible enough to record any of these kinds of changes.
 
@@ -50,7 +50,7 @@ Data structures
 This is the internal representation for a single file (blob).  It
 records the blob object name (if known -- for a work tree file it
 typically is a NUL SHA-1), filemode and pathname.  This is what the
-`diff_addremove()`, `diff_change()` and `diff_unmerged()` synthesize and
+`diff_addremove()`, `diff_change()` and `diff_unmerge()` synthesize and
 feed `diff_queue()` function with.
 
 * `struct diff_filepair`
index 7f48227494da7fe1bd57883d41e10b487c3f2fbf..f04e4b138d344155b767a0538321dda34552906e 100755 (executable)
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 GVF=GIT-VERSION-FILE
-DEF_VER=v1.7.5.GIT
+DEF_VER=v1.7.6
 
 LF='
 '
index d09ee701885b4f977ae4399caa4be6133570e1be..e40ac0c7f5ec2f304b88d92f47ff94272f5ce2a4 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -24,6 +24,12 @@ all::
 # Define NO_OPENSSL environment variable if you do not have OpenSSL.
 # This also implies BLK_SHA1.
 #
+# Define USE_LIBPCRE if you have and want to use libpcre. git-grep will be
+# able to use Perl-compatible regular expressions.
+#
+# Define LIBPCREDIR=/foo/bar if your libpcre header and library files are in
+# /foo/bar/include and /foo/bar/lib directories.
+#
 # Define NO_CURL if you do not have libcurl installed.  git-http-pull and
 # git-http-push are not built, and you cannot use http:// and https://
 # transports.
@@ -70,6 +76,9 @@ all::
 # Define NO_FNMATCH_CASEFOLD if your fnmatch function doesn't have the
 # FNM_CASEFOLD GNU extension.
 #
+# Define NO_GECOS_IN_PWENT if you don't have pw_gecos in struct passwd
+# in the C library.
+#
 # Define NO_LIBGEN_H if you don't have libgen.h.
 #
 # Define NEEDS_LIBGEN if your libgen needs -lgen when linking
@@ -373,6 +382,7 @@ SCRIPT_LIB += git-rebase--am
 SCRIPT_LIB += git-rebase--interactive
 SCRIPT_LIB += git-rebase--merge
 SCRIPT_LIB += git-sh-setup
+SCRIPT_LIB += git-sh-i18n
 
 SCRIPT_PERL += git-add--interactive.perl
 SCRIPT_PERL += git-difftool.perl
@@ -406,6 +416,7 @@ PROGRAM_OBJS += shell.o
 PROGRAM_OBJS += show-index.o
 PROGRAM_OBJS += upload-pack.o
 PROGRAM_OBJS += http-backend.o
+PROGRAM_OBJS += sh-i18n--envsubst.o
 
 PROGRAMS += $(patsubst %.o,git-%$X,$(PROGRAM_OBJS))
 
@@ -540,6 +551,7 @@ LIB_H += rerere.h
 LIB_H += resolve-undo.h
 LIB_H += revision.h
 LIB_H += run-command.h
+LIB_H += sha1-array.h
 LIB_H += sha1-lookup.h
 LIB_H += sideband.h
 LIB_H += sigchain.h
@@ -642,6 +654,7 @@ LIB_OBJS += revision.o
 LIB_OBJS += run-command.o
 LIB_OBJS += server-info.o
 LIB_OBJS += setup.o
+LIB_OBJS += sha1-array.o
 LIB_OBJS += sha1-lookup.o
 LIB_OBJS += sha1_file.o
 LIB_OBJS += sha1_name.o
@@ -1251,6 +1264,15 @@ ifdef NO_LIBGEN_H
        COMPAT_OBJS += compat/basename.o
 endif
 
+ifdef USE_LIBPCRE
+       BASIC_CFLAGS += -DUSE_LIBPCRE
+       ifdef LIBPCREDIR
+               BASIC_CFLAGS += -I$(LIBPCREDIR)/include
+               EXTLIBS += -L$(LIBPCREDIR)/$(lib) $(CC_LD_DYNPATH)$(LIBPCREDIR)/$(lib)
+       endif
+       EXTLIBS += -lpcre
+endif
+
 ifdef NO_CURL
        BASIC_CFLAGS += -DNO_CURL
        REMOTE_CURL_PRIMARY =
@@ -2037,10 +2059,14 @@ XGETTEXT_FLAGS = \
        --from-code=UTF-8
 XGETTEXT_FLAGS_C = $(XGETTEXT_FLAGS) --language=C \
        --keyword=_ --keyword=N_ --keyword="Q_:1,2"
+XGETTEXT_FLAGS_SH = $(XGETTEXT_FLAGS) --language=Shell
 LOCALIZED_C := $(C_OBJ:o=c)
+LOCALIZED_SH := $(SCRIPT_SH)
 
 po/git.pot: $(LOCALIZED_C)
-       $(QUIET_XGETTEXT)$(XGETTEXT) -o$@+ $(XGETTEXT_FLAGS_C) $(LOCALIZED_C) && \
+       $(QUIET_XGETTEXT)$(XGETTEXT) -o$@+ $(XGETTEXT_FLAGS_C) $(LOCALIZED_C)
+       $(QUIET_XGETTEXT)$(XGETTEXT) -o$@+ --join-existing $(XGETTEXT_FLAGS_SH) \
+               $(LOCALIZED_SH)
        mv $@+ $@
 
 pot: po/git.pot
@@ -2078,6 +2104,7 @@ GIT-BUILD-OPTIONS: FORCE
        @echo PYTHON_PATH=\''$(subst ','\'',$(PYTHON_PATH_SQ))'\' >>$@
        @echo TAR=\''$(subst ','\'',$(subst ','\'',$(TAR)))'\' >>$@
        @echo NO_CURL=\''$(subst ','\'',$(subst ','\'',$(NO_CURL)))'\' >>$@
+       @echo USE_LIBPCRE=\''$(subst ','\'',$(subst ','\'',$(USE_LIBPCRE)))'\' >>$@
        @echo NO_PERL=\''$(subst ','\'',$(subst ','\'',$(NO_PERL)))'\' >>$@
        @echo NO_PYTHON=\''$(subst ','\'',$(subst ','\'',$(NO_PYTHON)))'\' >>$@
 ifdef GIT_TEST_CMP
index 060c042f8bcc2d402cb9908be3bdbd3bb180a862..dd7e8ed69bc0c1268dc3b0a6590501ba9c10d7db 100644 (file)
--- a/bisect.c
+++ b/bisect.c
@@ -9,13 +9,7 @@
 #include "run-command.h"
 #include "log-tree.h"
 #include "bisect.h"
-
-struct sha1_array {
-       unsigned char (*sha1)[20];
-       int sha1_nr;
-       int sha1_alloc;
-       int sorted;
-};
+#include "sha1-array.h"
 
 static struct sha1_array good_revs;
 static struct sha1_array skipped_revs;
@@ -425,22 +419,15 @@ static void argv_array_push_sha1(struct argv_array *array,
        argv_array_push(array, strbuf_detach(&buf, NULL));
 }
 
-static void sha1_array_push(struct sha1_array *array,
-                           const unsigned char *sha1)
-{
-       ALLOC_GROW(array->sha1, array->sha1_nr + 1, array->sha1_alloc);
-       hashcpy(array->sha1[array->sha1_nr++], sha1);
-}
-
 static int register_ref(const char *refname, const unsigned char *sha1,
                        int flags, void *cb_data)
 {
        if (!strcmp(refname, "bad")) {
                current_bad_sha1 = sha1;
        } else if (!prefixcmp(refname, "good-")) {
-               sha1_array_push(&good_revs, sha1);
+               sha1_array_append(&good_revs, sha1);
        } else if (!prefixcmp(refname, "skip-")) {
-               sha1_array_push(&skipped_revs, sha1);
+               sha1_array_append(&skipped_revs, sha1);
        }
 
        return 0;
@@ -477,41 +464,14 @@ static void read_bisect_paths(struct argv_array *array)
        fclose(fp);
 }
 
-static int array_cmp(const void *a, const void *b)
-{
-       return hashcmp(a, b);
-}
-
-static void sort_sha1_array(struct sha1_array *array)
-{
-       qsort(array->sha1, array->sha1_nr, sizeof(*array->sha1), array_cmp);
-
-       array->sorted = 1;
-}
-
-static const unsigned char *sha1_access(size_t index, void *table)
-{
-       unsigned char (*array)[20] = table;
-       return array[index];
-}
-
-static int lookup_sha1_array(struct sha1_array *array,
-                            const unsigned char *sha1)
-{
-       if (!array->sorted)
-               sort_sha1_array(array);
-
-       return sha1_pos(sha1, array->sha1, array->sha1_nr, sha1_access);
-}
-
 static char *join_sha1_array_hex(struct sha1_array *array, char delim)
 {
        struct strbuf joined_hexs = STRBUF_INIT;
        int i;
 
-       for (i = 0; i < array->sha1_nr; i++) {
+       for (i = 0; i < array->nr; i++) {
                strbuf_addstr(&joined_hexs, sha1_to_hex(array->sha1[i]));
-               if (i + 1 < array->sha1_nr)
+               if (i + 1 < array->nr)
                        strbuf_addch(&joined_hexs, delim);
        }
 
@@ -546,13 +506,13 @@ struct commit_list *filter_skipped(struct commit_list *list,
        if (count)
                *count = 0;
 
-       if (!skipped_revs.sha1_nr)
+       if (!skipped_revs.nr)
                return list;
 
        while (list) {
                struct commit_list *next = list->next;
                list->next = NULL;
-               if (0 <= lookup_sha1_array(&skipped_revs,
+               if (0 <= sha1_array_lookup(&skipped_revs,
                                           list->item->object.sha1)) {
                        if (skipped_first && !*skipped_first)
                                *skipped_first = 1;
@@ -647,7 +607,7 @@ static struct commit_list *managed_skipped(struct commit_list *list,
 
        *tried = NULL;
 
-       if (!skipped_revs.sha1_nr)
+       if (!skipped_revs.nr)
                return list;
 
        list = filter_skipped(list, tried, 0, &count, &skipped_first);
@@ -672,7 +632,7 @@ static void bisect_rev_setup(struct rev_info *revs, const char *prefix,
        /* rev_argv.argv[0] will be ignored by setup_revisions */
        argv_array_push(&rev_argv, xstrdup("bisect_rev_setup"));
        argv_array_push_sha1(&rev_argv, current_bad_sha1, bad_format);
-       for (i = 0; i < good_revs.sha1_nr; i++)
+       for (i = 0; i < good_revs.nr; i++)
                argv_array_push_sha1(&rev_argv, good_revs.sha1[i],
                                     good_format);
        argv_array_push(&rev_argv, xstrdup("--"));
@@ -772,12 +732,12 @@ static struct commit *get_commit_reference(const unsigned char *sha1)
 
 static struct commit **get_bad_and_good_commits(int *rev_nr)
 {
-       int len = 1 + good_revs.sha1_nr;
+       int len = 1 + good_revs.nr;
        struct commit **rev = xmalloc(len * sizeof(*rev));
        int i, n = 0;
 
        rev[n++] = get_commit_reference(current_bad_sha1);
-       for (i = 0; i < good_revs.sha1_nr; i++)
+       for (i = 0; i < good_revs.nr; i++)
                rev[n++] = get_commit_reference(good_revs.sha1[i]);
        *rev_nr = n;
 
@@ -840,9 +800,9 @@ static void check_merge_bases(void)
                const unsigned char *mb = result->item->object.sha1;
                if (!hashcmp(mb, current_bad_sha1)) {
                        handle_bad_merge_base();
-               } else if (0 <= lookup_sha1_array(&good_revs, mb)) {
+               } else if (0 <= sha1_array_lookup(&good_revs, mb)) {
                        continue;
-               } else if (0 <= lookup_sha1_array(&skipped_revs, mb)) {
+               } else if (0 <= sha1_array_lookup(&skipped_revs, mb)) {
                        handle_skipped_merge_base(mb);
                } else {
                        printf("Bisecting: a merge base must be tested\n");
@@ -903,7 +863,7 @@ static void check_good_are_ancestors_of_bad(const char *prefix)
                return;
 
        /* Bisecting with no good rev is ok. */
-       if (good_revs.sha1_nr == 0)
+       if (good_revs.nr == 0)
                return;
 
        /* Check if all good revs are ancestor of the bad rev. */
@@ -968,7 +928,7 @@ int bisect_next_all(const char *prefix)
        bisect_common(&revs);
 
        revs.commits = find_bisection(revs.commits, &reaches, &all,
-                                      !!skipped_revs.sha1_nr);
+                                      !!skipped_revs.nr);
        revs.commits = managed_skipped(revs.commits, &tried);
 
        if (!revs.commits) {
index 4242e4b513101a09b0f027188095ba3032802d3b..26a5d424b8ceb0fd403a492e46e3637fd35068ba 100644 (file)
@@ -1484,13 +1484,14 @@ static void write_filename_info(const char *path)
 /*
  * Porcelain/Incremental format wants to show a lot of details per
  * commit.  Instead of repeating this every line, emit it only once,
- * the first time each commit appears in the output.
+ * the first time each commit appears in the output (unless the
+ * user has specifically asked for us to repeat).
  */
-static int emit_one_suspect_detail(struct origin *suspect)
+static int emit_one_suspect_detail(struct origin *suspect, int repeat)
 {
        struct commit_info ci;
 
-       if (suspect->commit->object.flags & METAINFO_SHOWN)
+       if (!repeat && (suspect->commit->object.flags & METAINFO_SHOWN))
                return 0;
 
        suspect->commit->object.flags |= METAINFO_SHOWN;
@@ -1529,7 +1530,7 @@ static void found_guilty_entry(struct blame_entry *ent)
                printf("%s %d %d %d\n",
                       sha1_to_hex(suspect->commit->object.sha1),
                       ent->s_lno + 1, ent->lno + 1, ent->num_lines);
-               emit_one_suspect_detail(suspect);
+               emit_one_suspect_detail(suspect, 0);
                write_filename_info(suspect->path);
                maybe_flush_or_die(stdout, "stdout");
        }
@@ -1618,9 +1619,19 @@ static const char *format_time(unsigned long time, const char *tz_str,
 #define OUTPUT_SHOW_SCORE      0100
 #define OUTPUT_NO_AUTHOR       0200
 #define OUTPUT_SHOW_EMAIL      0400
+#define OUTPUT_LINE_PORCELAIN 01000
 
-static void emit_porcelain(struct scoreboard *sb, struct blame_entry *ent)
+static void emit_porcelain_details(struct origin *suspect, int repeat)
 {
+       if (emit_one_suspect_detail(suspect, repeat) ||
+           (suspect->commit->object.flags & MORE_THAN_ONE_PATH))
+               write_filename_info(suspect->path);
+}
+
+static void emit_porcelain(struct scoreboard *sb, struct blame_entry *ent,
+                          int opt)
+{
+       int repeat = opt & OUTPUT_LINE_PORCELAIN;
        int cnt;
        const char *cp;
        struct origin *suspect = ent->suspect;
@@ -1633,17 +1644,18 @@ static void emit_porcelain(struct scoreboard *sb, struct blame_entry *ent)
               ent->s_lno + 1,
               ent->lno + 1,
               ent->num_lines);
-       if (emit_one_suspect_detail(suspect) ||
-           (suspect->commit->object.flags & MORE_THAN_ONE_PATH))
-               write_filename_info(suspect->path);
+       emit_porcelain_details(suspect, repeat);
 
        cp = nth_line(sb, ent->lno);
        for (cnt = 0; cnt < ent->num_lines; cnt++) {
                char ch;
-               if (cnt)
+               if (cnt) {
                        printf("%s %d %d\n", hex,
                               ent->s_lno + 1 + cnt,
                               ent->lno + 1 + cnt);
+                       if (repeat)
+                               emit_porcelain_details(suspect, 1);
+               }
                putchar('\t');
                do {
                        ch = *cp++;
@@ -1756,7 +1768,7 @@ static void output(struct scoreboard *sb, int option)
 
        for (ent = sb->ent; ent; ent = ent->next) {
                if (option & OUTPUT_PORCELAIN)
-                       emit_porcelain(sb, ent);
+                       emit_porcelain(sb, ent, option);
                else {
                        emit_other(sb, ent, option);
                }
@@ -2300,6 +2312,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
                OPT_BIT('f', "show-name", &output_option, "Show original filename (Default: auto)", OUTPUT_SHOW_NAME),
                OPT_BIT('n', "show-number", &output_option, "Show original linenumber (Default: off)", OUTPUT_SHOW_NUMBER),
                OPT_BIT('p', "porcelain", &output_option, "Show in a format designed for machine consumption", OUTPUT_PORCELAIN),
+               OPT_BIT(0, "line-porcelain", &output_option, "Show porcelain format with per-line commit information", OUTPUT_PORCELAIN|OUTPUT_LINE_PORCELAIN),
                OPT_BIT('c', NULL, &output_option, "Use the same output mode as git-annotate (Default: off)", OUTPUT_ANNOTATE_COMPAT),
                OPT_BIT('t', NULL, &output_option, "Show raw timestamp (Default: off)", OUTPUT_RAW_TIMESTAMP),
                OPT_BIT('l', NULL, &output_option, "Show long commit SHA1 (Default: off)", OUTPUT_LONG_OBJECT_NAME),
index 9cca1b9afc20caf6333d0269386249f41fa8746f..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
 };
@@ -399,9 +399,7 @@ static void add_verbose_info(struct strbuf *out, struct ref_item *item,
        struct commit *commit = item->commit;
 
        if (commit && !parse_commit(commit)) {
-               struct pretty_print_context ctx = {0};
-               pretty_print_commit(CMIT_FMT_ONELINE, commit,
-                                   &subject, &ctx);
+               pp_commit_easy(CMIT_FMT_ONELINE, commit, &subject);
                sub = subject.buf;
        }
 
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 4761769512a8abcdd5652a93d39be18154e251dd..28cdc51b85e7d433dca085c0080f964d19a391b4 100644 (file)
@@ -306,9 +306,8 @@ static void show_local_changes(struct object *head, struct diff_options *opts)
 static void describe_detached_head(const char *msg, struct commit *commit)
 {
        struct strbuf sb = STRBUF_INIT;
-       struct pretty_print_context ctx = {0};
        parse_commit(commit);
-       pretty_print_commit(CMIT_FMT_ONELINE, commit, &sb, &ctx);
+       pp_commit_easy(CMIT_FMT_ONELINE, commit, &sb);
        fprintf(stderr, "%s %s... %s\n", msg,
                find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV), sb.buf);
        strbuf_release(&sb);
@@ -623,14 +622,12 @@ static int clear_commit_marks_from_one_ref(const char *refname,
 
 static void describe_one_orphan(struct strbuf *sb, struct commit *commit)
 {
-       struct pretty_print_context ctx = { 0 };
-
        parse_commit(commit);
        strbuf_addstr(sb, "  ");
        strbuf_addstr(sb,
                find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV));
        strbuf_addch(sb, ' ');
-       pretty_print_commit(CMIT_FMT_ONELINE, commit, sb, &ctx);
+       pp_commit_easy(CMIT_FMT_ONELINE, commit, sb);
        strbuf_addch(sb, '\n');
 }
 
index 49c838fd3ff24ecc7821fbcf8f312d76967c168d..f579794d9a93a0e55289921f20b8f68b85211ca1 100644 (file)
@@ -81,7 +81,7 @@ static struct option builtin_clone_options[] = {
                   "path to git-upload-pack on the remote"),
        OPT_STRING(0, "depth", &option_depth, "depth",
                    "create a shallow clone of that depth"),
-       OPT_STRING('L', "separate-git-dir", &real_git_dir, "gitdir",
+       OPT_STRING(0, "separate-git-dir", &real_git_dir, "gitdir",
                   "separate git dir from working tree"),
 
        OPT_END()
index 5286432f39b87e03a08cc86bb622b6ca3b911365..e1af9b19f0be71484ae9341762dc2bf89cabb70c 100644 (file)
@@ -1207,9 +1207,6 @@ int cmd_status(int argc, const char **argv, const char *prefix)
        if (argc == 2 && !strcmp(argv[1], "-h"))
                usage_with_options(builtin_status_usage, builtin_status_options);
 
-       if (null_termination && status_format == STATUS_FORMAT_LONG)
-               status_format = STATUS_FORMAT_PORCELAIN;
-
        wt_status_prepare(&s);
        gitmodules_config();
        git_config(git_status_config, &s);
@@ -1217,6 +1214,10 @@ int cmd_status(int argc, const char **argv, const char *prefix)
        argc = parse_options(argc, argv, prefix,
                             builtin_status_options,
                             builtin_status_usage, 0);
+
+       if (null_termination && status_format == STATUS_FORMAT_LONG)
+               status_format = STATUS_FORMAT_PORCELAIN;
+
        handle_untracked_files_arg(&s);
        if (show_ignored_in_status)
                s.show_ignored_files = 1;
index 3e3c52849773348a568241f678b3e3ee2e8d123a..211e118d575411b712ccf0387db2408708576902 100644 (file)
@@ -436,9 +436,14 @@ int cmd_config(int argc, const char **argv, const char *prefix)
                              NULL, NULL);
        }
        else if (actions == ACTION_SET) {
+               int ret;
                check_argc(argc, 2, 2);
                value = normalize_value(argv[0], argv[1]);
-               return git_config_set(argv[0], value);
+               ret = git_config_set(argv[0], value);
+               if (ret == CONFIG_NOTHING_SET)
+                       error("cannot overwrite multiple values with a single value\n"
+                       "       Use a regexp, --add or --set-all to change %s.", argv[0]);
+               return ret;
        }
        else if (actions == ACTION_SET_ALL) {
                check_argc(argc, 2, 3);
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 85aff029b225c023f09fda635f16cc00a22ed7ca..436798410210b868cfbe439a1603b0a340a32c50 100644 (file)
@@ -226,7 +226,7 @@ static void insert_one_alternate_ref(const struct ref *ref, void *unused)
 
 static void insert_alternate_refs(void)
 {
-       foreach_alt_odb(refs_from_alternate_cb, insert_one_alternate_ref);
+       for_each_alternate_ref(insert_one_alternate_ref, NULL);
 }
 
 #define INITIAL_FLUSH 16
@@ -472,8 +472,10 @@ static int mark_complete(const char *path, const unsigned char *sha1, int flag,
        }
        if (o && o->type == OBJ_COMMIT) {
                struct commit *commit = (struct commit *)o;
-               commit->object.flags |= COMPLETE;
-               commit_list_insert_by_date(commit, &complete);
+               if (!(commit->object.flags & COMPLETE)) {
+                       commit->object.flags |= COMPLETE;
+                       commit_list_insert_by_date(commit, &complete);
+               }
        }
        return 0;
 }
index f9c41da475289b84fe849f7641406ebc94059630..93c99385a95e6ab525fa64c1dbb70d5d4ca103ba 100644 (file)
@@ -875,6 +875,7 @@ static int fetch_one(struct remote *remote, int argc, const char **argv)
 {
        int i;
        static const char **refs = NULL;
+       struct refspec *refspec;
        int ref_nr = 0;
        int exit_code;
 
@@ -915,8 +916,9 @@ static int fetch_one(struct remote *remote, int argc, const char **argv)
 
        sigchain_push_common(unlock_pack_on_signal);
        atexit(unlock_pack);
-       exit_code = do_fetch(transport,
-                       parse_fetch_refspec(ref_nr, refs), ref_nr);
+       refspec = parse_fetch_refspec(ref_nr, refs);
+       exit_code = do_fetch(transport, refspec, ref_nr);
+       free(refspec);
        transport_disconnect(transport);
        transport = NULL;
        return exit_code;
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 3ee2ec51def59695813ee14f104d142a62d530b6..871afaa3c76ea34b67671a6e4a46957a08a946e5 100644 (file)
@@ -753,6 +753,15 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
        int i;
        int dummy;
        int use_index = 1;
+       enum {
+               pattern_type_unspecified = 0,
+               pattern_type_bre,
+               pattern_type_ere,
+               pattern_type_fixed,
+               pattern_type_pcre,
+       };
+       int pattern_type = pattern_type_unspecified;
+
        struct option options[] = {
                OPT_BOOLEAN(0, "cached", &cached,
                        "search in index instead of in the work tree"),
@@ -774,13 +783,18 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
                        "descend at most <depth> levels", PARSE_OPT_NONEG,
                        NULL, 1 },
                OPT_GROUP(""),
-               OPT_BIT('E', "extended-regexp", &opt.regflags,
-                       "use extended POSIX regular expressions", REG_EXTENDED),
-               OPT_NEGBIT('G', "basic-regexp", &opt.regflags,
-                       "use basic POSIX regular expressions (default)",
-                       REG_EXTENDED),
-               OPT_BOOLEAN('F', "fixed-strings", &opt.fixed,
-                       "interpret patterns as fixed strings"),
+               OPT_SET_INT('E', "extended-regexp", &pattern_type,
+                           "use extended POSIX regular expressions",
+                           pattern_type_ere),
+               OPT_SET_INT('G', "basic-regexp", &pattern_type,
+                           "use basic POSIX regular expressions (default)",
+                           pattern_type_bre),
+               OPT_SET_INT('F', "fixed-strings", &pattern_type,
+                           "interpret patterns as fixed strings",
+                           pattern_type_fixed),
+               OPT_SET_INT('P', "perl-regexp", &pattern_type,
+                           "use Perl-compatible regular expressions",
+                           pattern_type_pcre),
                OPT_GROUP(""),
                OPT_BOOLEAN('n', "line-number", &opt.linenum, "show line numbers"),
                OPT_NEGBIT('h', NULL, &opt.pathname, "don't show filenames", 1),
@@ -886,6 +900,28 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
                             PARSE_OPT_KEEP_DASHDASH |
                             PARSE_OPT_STOP_AT_NON_OPTION |
                             PARSE_OPT_NO_INTERNAL_HELP);
+       switch (pattern_type) {
+       case pattern_type_fixed:
+               opt.fixed = 1;
+               opt.pcre = 0;
+               break;
+       case pattern_type_bre:
+               opt.fixed = 0;
+               opt.pcre = 0;
+               opt.regflags &= ~REG_EXTENDED;
+               break;
+       case pattern_type_ere:
+               opt.fixed = 0;
+               opt.pcre = 0;
+               opt.regflags |= REG_EXTENDED;
+               break;
+       case pattern_type_pcre:
+               opt.fixed = 0;
+               opt.pcre = 1;
+               break;
+       default:
+               break; /* nothing */
+       }
 
        if (use_index && !startup_info->have_repository)
                /* die the same way as if we did it at the beginning */
@@ -925,8 +961,6 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
                die(_("no pattern given."));
        if (!opt.fixed && opt.ignore_case)
                opt.regflags |= REG_ICASE;
-       if ((opt.regflags != REG_NEWLINE) && opt.fixed)
-               die(_("cannot mix --fixed-strings and regexp"));
 
 #ifndef NO_PTHREADS
        if (online_cpus() == 1 || !grep_threads_ok(&opt))
@@ -969,13 +1003,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
                        verify_filename(prefix, argv[j]);
        }
 
-       if (i < argc)
-               paths = get_pathspec(prefix, argv + i);
-       else if (prefix) {
-               paths = xcalloc(2, sizeof(const char *));
-               paths[0] = prefix;
-               paths[1] = NULL;
-       }
+       paths = get_pathspec(prefix, argv + i);
        init_pathspec(&pathspec, paths);
        pathspec.max_depth = opt.max_depth;
        pathspec.recursive = 1;
index b96f46acf52d23f68c1ec164be8264396dea22db..33911fd5e9325210dae63b3252fa3b56d42b545c 100644 (file)
@@ -14,8 +14,11 @@ static void hash_fd(int fd, const char *type, int write_object, const char *path
 {
        struct stat st;
        unsigned char sha1[20];
+       unsigned flags = (HASH_FORMAT_CHECK |
+                         (write_object ? HASH_WRITE_OBJECT : 0));
+
        if (fstat(fd, &st) < 0 ||
-           index_fd(sha1, fd, &st, write_object, type_from_string(type), path, 1))
+           index_fd(sha1, fd, &st, type_from_string(type), path, flags))
                die(write_object
                    ? "Unable to add %s to database"
                    : "Unable to hash %s", path);
index ba13a54793d46de2410db14e6f387674e3b27c82..025aa47c804e4400cfa16ae8acd8dc2a5175e7c0 100644 (file)
@@ -490,7 +490,7 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
                        "specify that the git repository is to be shared amongst several users",
                        PARSE_OPT_OPTARG | PARSE_OPT_NONEG, shared_callback, 0},
                OPT_BIT('q', "quiet", &flags, "be quiet", INIT_DB_QUIET),
-               OPT_STRING('L', "separate-git-dir", &real_git_dir, "gitdir",
+               OPT_STRING(0, "separate-git-dir", &real_git_dir, "gitdir",
                           "separate git dir from working tree"),
                OPT_END()
        };
index f6219909a787ae355a43e39974140a36848ab1d6..5c2af590047d92554d87acc5d7113a2f1f730a96 100644 (file)
@@ -23,6 +23,7 @@
 /* Set a default date-time format for git log ("log.date" config variable) */
 static const char *default_date_mode = NULL;
 
+static int default_abbrev_commit;
 static int default_show_root = 1;
 static int decoration_style;
 static int decoration_given;
@@ -77,6 +78,7 @@ static void cmd_log_init_defaults(struct rev_info *rev)
                get_commit_format(fmt_pretty, rev);
        rev->verbose_header = 1;
        DIFF_OPT_SET(&rev->diffopt, RECURSIVE);
+       rev->abbrev_commit = default_abbrev_commit;
        rev->show_root_diff = default_show_root;
        rev->subject_prefix = fmt_patch_subject_prefix;
        DIFF_OPT_SET(&rev->diffopt, ALLOW_TEXTCONV);
@@ -92,7 +94,7 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,
        int quiet = 0, source = 0;
 
        const struct option builtin_log_options[] = {
-               OPT_BOOLEAN(0, "quiet", &quiet, "supress diff output"),
+               OPT_BOOLEAN(0, "quiet", &quiet, "suppress diff output"),
                OPT_BOOLEAN(0, "source", &source, "show source"),
                { OPTION_CALLBACK, 0, "decorate", NULL, NULL, "decorate options",
                  PARSE_OPT_OPTARG, decorate_callback},
@@ -105,6 +107,8 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,
                             PARSE_OPT_KEEP_DASHDASH);
 
        argc = setup_revisions(argc, argv, rev, opt);
+       if (quiet)
+               rev->diffopt.output_format |= DIFF_FORMAT_NO_OUTPUT;
 
        /* Any arguments at this point are not recognized */
        if (argc > 1)
@@ -129,13 +133,16 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,
        if (source)
                rev->show_source = 1;
 
-       /*
-        * defeat log.decorate configuration interacting with --pretty=raw
-        * from the command line.
-        */
-       if (!decoration_given && rev->pretty_given
-           && rev->commit_format == CMIT_FMT_RAW)
-               decoration_style = 0;
+       if (rev->pretty_given && rev->commit_format == CMIT_FMT_RAW) {
+               /*
+                * "log --pretty=raw" is special; ignore UI oriented
+                * configuration variables such as decoration.
+                */
+               if (!decoration_given)
+                       decoration_style = 0;
+               if (!rev->abbrev_commit_given)
+                       rev->abbrev_commit = 0;
+       }
 
        if (decoration_style) {
                rev->show_decorations = 1;
@@ -323,6 +330,10 @@ static int git_log_config(const char *var, const char *value, void *cb)
                return git_config_string(&fmt_pretty, var, value);
        if (!strcmp(var, "format.subjectprefix"))
                return git_config_string(&fmt_patch_subject_prefix, var, value);
+       if (!strcmp(var, "log.abbrevcommit")) {
+               default_abbrev_commit = git_config_bool(var, value);
+               return 0;
+       }
        if (!strcmp(var, "log.date"))
                return git_config_string(&default_date_mode, var, value);
        if (!strcmp(var, "log.decorate")) {
@@ -365,9 +376,11 @@ int cmd_whatchanged(int argc, const char **argv, const char *prefix)
 static void show_tagger(char *buf, int len, struct rev_info *rev)
 {
        struct strbuf out = STRBUF_INIT;
+       struct pretty_print_context pp = {0};
 
-       pp_user_info("Tagger", rev->commit_format, &out, buf, rev->date_mode,
-               get_log_output_encoding());
+       pp.fmt = rev->commit_format;
+       pp.date_mode = rev->date_mode;
+       pp_user_info(&pp, "Tagger", &out, buf, get_log_output_encoding());
        printf("%s", out.buf);
        strbuf_release(&out);
 }
@@ -516,11 +529,11 @@ int cmd_log_reflog(int argc, const char **argv, const char *prefix)
 
        init_revisions(&rev, prefix);
        init_reflog_walk(&rev.reflog_info);
-       rev.abbrev_commit = 1;
        rev.verbose_header = 1;
        memset(&opt, 0, sizeof(opt));
        opt.def = "HEAD";
        cmd_log_init_defaults(&rev);
+       rev.abbrev_commit = 1;
        rev.commit_format = CMIT_FMT_ONELINE;
        rev.use_terminator = 1;
        rev.always_show_header = 1;
@@ -751,10 +764,8 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,
                              int quiet)
 {
        const char *committer;
-       const char *subject_start = NULL;
        const char *body = "*** SUBJECT HERE ***\n\n*** BLURB HERE ***\n";
        const char *msg;
-       const char *extra_headers = rev->extra_headers;
        struct shortlog log;
        struct strbuf sb = STRBUF_INIT;
        int i;
@@ -762,6 +773,7 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,
        struct diff_options opts;
        int need_8bit_cte = 0;
        struct commit *commit = NULL;
+       struct pretty_print_context pp = {0};
 
        if (rev->commit_format != CMIT_FMT_EMAIL)
                die(_("Cover letter needs email format"));
@@ -793,7 +805,7 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,
                free(commit);
        }
 
-       log_write_email_headers(rev, head, &subject_start, &extra_headers,
+       log_write_email_headers(rev, head, &pp.subject, &pp.after_subject,
                                &need_8bit_cte);
 
        for (i = 0; !need_8bit_cte && i < nr; i++)
@@ -801,11 +813,11 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,
                        need_8bit_cte = 1;
 
        msg = body;
-       pp_user_info(NULL, CMIT_FMT_EMAIL, &sb, committer, DATE_RFC2822,
-                    encoding);
-       pp_title_line(CMIT_FMT_EMAIL, &msg, &sb, subject_start, extra_headers,
-                     encoding, need_8bit_cte);
-       pp_remainder(CMIT_FMT_EMAIL, &msg, &sb, 0);
+       pp.fmt = CMIT_FMT_EMAIL;
+       pp.date_mode = DATE_RFC2822;
+       pp_user_info(&pp, NULL, &sb, committer, encoding);
+       pp_title_line(&pp, &msg, &sb, encoding, need_8bit_cte);
+       pp_remainder(&pp, &msg, &sb, 0);
        printf("%s\n", sb.buf);
 
        strbuf_release(&sb);
@@ -1169,6 +1181,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                die (_("-n and -k are mutually exclusive."));
        if (keep_subject && subject_prefix)
                die (_("--subject-prefix and -k are mutually exclusive."));
+       rev.preserve_subject = keep_subject;
 
        argc = setup_revisions(argc, argv, &rev, &s_r_opt);
        if (argc > 1)
@@ -1399,8 +1412,7 @@ static void print_commit(char sign, struct commit *commit, int verbose,
                       find_unique_abbrev(commit->object.sha1, abbrev));
        } else {
                struct strbuf buf = STRBUF_INIT;
-               struct pretty_print_context ctx = {0};
-               pretty_print_commit(CMIT_FMT_ONELINE, commit, &buf, &ctx);
+               pp_commit_easy(CMIT_FMT_ONELINE, commit, &buf);
                printf("%c %s %s\n", sign,
                       find_unique_abbrev(commit->object.sha1, abbrev),
                       buf.buf);
index 1a1ff87e8f9ed7db84f106960f36888835f7057b..10223092a9ebfed4092db680529a42ca8886a9f2 100644 (file)
@@ -5,7 +5,7 @@
 
 static const char ls_remote_usage[] =
 "git ls-remote [--heads] [--tags]  [-u <exec> | --upload-pack <exec>]\n"
-"                     [-q|--quiet] [<repository> [<refs>...]]";
+"                     [-q|--quiet] [--exit-code] [<repository> [<refs>...]]";
 
 /*
  * Is there one among the list of patterns that match the tail part
@@ -35,6 +35,7 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)
        unsigned flags = 0;
        int get_url = 0;
        int quiet = 0;
+       int status = 0;
        const char *uploadpack = NULL;
        const char **pattern = NULL;
 
@@ -74,6 +75,11 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)
                                get_url = 1;
                                continue;
                        }
+                       if (!strcmp("--exit-code", arg)) {
+                               /* return this code if no refs are reported */
+                               status = 2;
+                               continue;
+                       }
                        usage(ls_remote_usage);
                }
                dest = arg;
@@ -121,6 +127,7 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)
                if (!tail_match(pattern, ref->name))
                        continue;
                printf("%s      %s\n", sha1_to_hex(ref->old_sha1), ref->name);
+               status = 0; /* we found something */
        }
-       return 0;
+       return status;
 }
index 71e6262a87d883a4a82953db5f742bccf99e5c77..bfb32b7233850a68bdc226038a9c0f973037499b 100644 (file)
@@ -400,7 +400,7 @@ static int read_one_header_line(struct strbuf *line, FILE *in)
                        break;
                if (strbuf_getline(&continuation, in, '\n'))
                        break;
-               continuation.buf[0] = '\n';
+               continuation.buf[0] = ' ';
                strbuf_rtrim(&continuation);
                strbuf_addbuf(line, &continuation);
        }
index 5a2a1eb797c88990337f0b9cbb5dcabc73222a22..325891edb610945d01899b102993202af279bf3f 100644 (file)
@@ -339,13 +339,14 @@ static void squash_message(void)
 
        ctx.abbrev = rev.abbrev;
        ctx.date_mode = rev.date_mode;
+       ctx.fmt = rev.commit_format;
 
        strbuf_addstr(&out, "Squashed commit of the following:\n");
        while ((commit = get_revision(&rev)) != NULL) {
                strbuf_addch(&out, '\n');
                strbuf_addf(&out, "commit %s\n",
                        sha1_to_hex(commit->object.sha1));
-               pretty_print_commit(rev.commit_format, commit, &out, &ctx);
+               pretty_print_commit(&ctx, commit, &out);
        }
        if (write(fd, out.buf, out.len) < 0)
                die_errno(_("Writing SQUASH_MSG"));
index 1fb1f734397668280b444e63dc8d3c129d41e3b9..f8e437db0156043f1586e66adf343e34ef6cf4dc 100644 (file)
@@ -29,7 +29,7 @@ static const char * const git_notes_usage[] = {
        "git notes [--ref <notes_ref>] merge [-v | -q] [-s <strategy> ] <notes_ref>",
        "git notes merge --commit [-v | -q]",
        "git notes merge --abort [-v | -q]",
-       "git notes [--ref <notes_ref>] remove [<object>]",
+       "git notes [--ref <notes_ref>] remove [<object>...]",
        "git notes [--ref <notes_ref>] prune [-n | -v]",
        "git notes [--ref <notes_ref>] get-ref",
        NULL
@@ -953,40 +953,60 @@ static int merge(int argc, const char **argv, const char *prefix)
        return result < 0; /* return non-zero on conflicts */
 }
 
+#define IGNORE_MISSING 1
+
+static int remove_one_note(struct notes_tree *t, const char *name, unsigned flag)
+{
+       int status;
+       unsigned char sha1[20];
+       if (get_sha1(name, sha1))
+               return error(_("Failed to resolve '%s' as a valid ref."), name);
+       status = remove_note(t, sha1);
+       if (status)
+               fprintf(stderr, _("Object %s has no note\n"), name);
+       else
+               fprintf(stderr, _("Removing note for object %s\n"), name);
+       return (flag & IGNORE_MISSING) ? 0 : status;
+}
+
 static int remove_cmd(int argc, const char **argv, const char *prefix)
 {
+       unsigned flag = 0;
+       int from_stdin = 0;
        struct option options[] = {
+               OPT_BIT(0, "ignore-missing", &flag,
+                       "attempt to remove non-existent note is not an error",
+                       IGNORE_MISSING),
+               OPT_BOOLEAN(0, "stdin", &from_stdin,
+                           "read object names from the standard input"),
                OPT_END()
        };
-       const char *object_ref;
        struct notes_tree *t;
-       unsigned char object[20];
-       int retval;
+       int retval = 0;
 
        argc = parse_options(argc, argv, prefix, options,
                             git_notes_remove_usage, 0);
 
-       if (1 < argc) {
-               error(_("too many parameters"));
-               usage_with_options(git_notes_remove_usage, options);
-       }
-
-       object_ref = argc ? argv[0] : "HEAD";
-
-       if (get_sha1(object_ref, object))
-               die(_("Failed to resolve '%s' as a valid ref."), object_ref);
-
        t = init_notes_check("remove");
 
-       retval = remove_note(t, object);
-       if (retval)
-               fprintf(stderr, _("Object %s has no note\n"), sha1_to_hex(object));
-       else {
-               fprintf(stderr, _("Removing note for object %s\n"),
-                       sha1_to_hex(object));
-
-               commit_notes(t, "Notes removed by 'git notes remove'");
+       if (!argc && !from_stdin) {
+               retval = remove_one_note(t, "HEAD", flag);
+       } else {
+               while (*argv) {
+                       retval |= remove_one_note(t, *argv, flag);
+                       argv++;
+               }
        }
+       if (from_stdin) {
+               struct strbuf sb = STRBUF_INIT;
+               while (strbuf_getwholeline(&sb, stdin, '\n') != EOF) {
+                       strbuf_rtrim(&sb);
+                       retval |= remove_one_note(t, sb.buf, flag);
+               }
+               strbuf_release(&sb);
+       }
+       if (!retval)
+               commit_notes(t, "Notes removed by 'git notes remove'");
        free_notes(t);
        return retval;
 }
index 93c92814cf28855aee87cabf4a3b906e11a36125..df6c4c8819e7903f34cd6dc6bb0078a6cddac310 100644 (file)
@@ -130,6 +130,7 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)
                  PARSE_OPT_NONEG, exclude_per_directory_cb },
                OPT_SET_INT('i', NULL, &opts.index_only,
                            "don't check the working tree after merging", 1),
+               OPT__DRY_RUN(&opts.dry_run, "don't update the index or the work tree"),
                OPT_SET_INT(0, "no-sparse-checkout", &opts.skip_sparse_checkout,
                            "skip applying sparse checkout filter", 1),
                OPT_SET_INT(0, "debug-unpack", &opts.debug_unpack,
@@ -219,7 +220,7 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)
        if (unpack_trees(nr_trees, t, &opts))
                return 128;
 
-       if (opts.debug_unpack)
+       if (opts.debug_unpack || opts.dry_run)
                return 0; /* do not write the index out */
 
        /*
index e1ba4dc697abd1b2582713744c5a1892ef4a4882..e1a687ad0761e46f6ec95659bb585b9014d4abf4 100644 (file)
@@ -10,6 +10,7 @@
 #include "remote.h"
 #include "transport.h"
 #include "string-list.h"
+#include "sha1-array.h"
 
 static const char receive_pack_usage[] = "git receive-pack <git-dir>";
 
@@ -731,14 +732,23 @@ static int delete_only(struct command *commands)
        return 1;
 }
 
-static void add_one_alternate_ref(const struct ref *ref, void *unused)
+static void add_one_alternate_sha1(const unsigned char sha1[20], void *unused)
 {
-       add_extra_ref(".have", ref->old_sha1, 0);
+       add_extra_ref(".have", sha1, 0);
+}
+
+static void collect_one_alternate_ref(const struct ref *ref, void *data)
+{
+       struct sha1_array *sa = data;
+       sha1_array_append(sa, ref->old_sha1);
 }
 
 static void add_alternate_refs(void)
 {
-       foreach_alt_odb(refs_from_alternate_cb, add_one_alternate_ref);
+       struct sha1_array sa = SHA1_ARRAY_INIT;
+       for_each_alternate_ref(collect_one_alternate_ref, &sa);
+       sha1_array_for_each_unique(&sa, add_one_alternate_sha1, NULL);
+       sha1_array_clear(&sa);
 }
 
 int cmd_receive_pack(int argc, const char **argv, const char *prefix)
index 8424152269e309cb86e4ff530b9feacc23fc17a6..9ff1cac69b9fd21c5f7dfc06b5859fedccc67644 100644 (file)
@@ -193,8 +193,8 @@ static int add(int argc, const char **argv)
 
        if (mirror && master)
                die("specifying a master branch makes no sense with --mirror");
-       if (mirror && track.nr)
-               die("specifying branches to track makes no sense with --mirror");
+       if (mirror && !(mirror & MIRROR_FETCH) && track.nr)
+               die("specifying branches to track makes sense only with fetch mirrors");
 
        name = argv[0];
        url = argv[1];
index 4be66998f6bcb998df094d0c434dfab6bbdcc8c7..56727e8c1d9d87fe3e3bf3919ba86d27d0735758 100644 (file)
@@ -104,7 +104,8 @@ static void show_commit(struct commit *commit, void *data)
                struct pretty_print_context ctx = {0};
                ctx.abbrev = revs->abbrev;
                ctx.date_mode = revs->date_mode;
-               pretty_print_commit(revs->commit_format, commit, &buf, &ctx);
+               ctx.fmt = revs->commit_format;
+               pretty_print_commit(&ctx, commit, &buf);
                if (revs->graph) {
                        if (buf.len) {
                                if (revs->commit_format != CMIT_FMT_ONELINE)
index adb1cae4f27ad3993aa2418f7b0fd355d19cdab9..4c19f844a977b6413afcd5df6b4f8bc0875775a8 100644 (file)
@@ -44,6 +44,7 @@ static int is_rev_argument(const char *arg)
                "--branches=",
                "--branches",
                "--header",
+               "--ignore-missing",
                "--max-age=",
                "--max-count=",
                "--min-age=",
index b6f4b0eb03b66dda2f691ff5c80b78321c1b701d..37f3193a3366725a6b11bdb55db7b6ffc6e9b3ce 100644 (file)
@@ -138,9 +138,8 @@ void shortlog_add_commit(struct shortlog *log, struct commit *commit)
        const char *author = NULL, *buffer;
        struct strbuf buf = STRBUF_INIT;
        struct strbuf ufbuf = STRBUF_INIT;
-       struct pretty_print_context ctx = {0};
 
-       pretty_print_commit(CMIT_FMT_RAW, commit, &buf, &ctx);
+       pp_commit_easy(CMIT_FMT_RAW, commit, &buf);
        buffer = buf.buf;
        while (*buffer && *buffer != '\n') {
                const char *eol = strchr(buffer, '\n');
@@ -159,11 +158,12 @@ void shortlog_add_commit(struct shortlog *log, struct commit *commit)
                    sha1_to_hex(commit->object.sha1));
        if (log->user_format) {
                struct pretty_print_context ctx = {0};
+               ctx.fmt = CMIT_FMT_USERFORMAT;
                ctx.abbrev = log->abbrev;
                ctx.subject = "";
                ctx.after_subject = "";
                ctx.date_mode = DATE_NORMAL;
-               pretty_print_commit(CMIT_FMT_USERFORMAT, commit, &ufbuf, &ctx);
+               pretty_print_commit(&ctx, commit, &ufbuf);
                buffer = ufbuf.buf;
        } else if (*buffer) {
                buffer++;
index 1abcd9e02e64022e2619db3de40c7b3a731d4479..facc63a79ec0c86fd9df59792cd4f20030af5db1 100644 (file)
@@ -283,8 +283,7 @@ static void show_one_commit(struct commit *commit, int no_name)
        struct commit_name *name = commit->util;
 
        if (commit->object.parsed) {
-               struct pretty_print_context ctx = {0};
-               pretty_print_commit(CMIT_FMT_ONELINE, commit, &pretty, &ctx);
+               pp_commit_easy(CMIT_FMT_ONELINE, commit, &pretty);
                pretty_str = pretty.buf;
        }
        if (!prefixcmp(pretty_str, "[PATCH] "))
index d7850c6309aec0c1a4f640999fb757d7b32ba1b8..a6a23fa1f3c7782566d7fcfe470dd424b876e4a5 100644 (file)
@@ -99,8 +99,11 @@ static int add_one_path(struct cache_entry *old, const char *path, int len, stru
        fill_stat_cache_info(ce, st);
        ce->ce_mode = ce_mode_from_stat(old, st->st_mode);
 
-       if (index_path(ce->sha1, path, st, !info_only))
+       if (index_path(ce->sha1, path, st,
+                      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))
diff --git a/cache.h b/cache.h
index 66ddfb71b66922e47d95efed4a80df39f1e829da..e11cf6ab1c73ac97c94e76e8c8699d55af95b978 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -518,8 +518,11 @@ struct pathspec {
 extern int init_pathspec(struct pathspec *, const char **);
 extern void free_pathspec(struct pathspec *);
 extern int ce_path_match(const struct cache_entry *ce, const struct pathspec *pathspec);
-extern int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, enum object_type type, const char *path, int format_check);
-extern int index_path(unsigned char *sha1, const char *path, struct stat *st, int write_object);
+
+#define HASH_WRITE_OBJECT 1
+#define HASH_FORMAT_CHECK 2
+extern int index_fd(unsigned char *sha1, int fd, struct stat *st, enum object_type type, const char *path, unsigned flags);
+extern int index_path(unsigned char *sha1, const char *path, struct stat *st, unsigned flags);
 extern void fill_stat_cache_info(struct cache_entry *ce, struct stat *st);
 
 #define REFRESH_REALLY         0x0001  /* ignore_valid */
@@ -810,15 +813,15 @@ struct object_context {
 };
 
 extern int get_sha1(const char *str, unsigned char *sha1);
-extern int get_sha1_with_mode_1(const char *str, unsigned char *sha1, unsigned *mode, int gently, const char *prefix);
+extern int get_sha1_with_mode_1(const char *str, unsigned char *sha1, unsigned *mode, int only_to_die, const char *prefix);
 static inline int get_sha1_with_mode(const char *str, unsigned char *sha1, unsigned *mode)
 {
-       return get_sha1_with_mode_1(str, sha1, mode, 1, NULL);
+       return get_sha1_with_mode_1(str, sha1, mode, 0, NULL);
 }
-extern int get_sha1_with_context_1(const char *name, unsigned char *sha1, struct object_context *orc, int gently, const char *prefix);
+extern int get_sha1_with_context_1(const char *name, unsigned char *sha1, struct object_context *orc, int only_to_die, const char *prefix);
 static inline int get_sha1_with_context(const char *str, unsigned char *sha1, struct object_context *orc)
 {
-       return get_sha1_with_context_1(str, sha1, orc, 1, NULL);
+       return get_sha1_with_context_1(str, sha1, orc, 0, NULL);
 }
 extern int get_sha1_hex(const char *hex, unsigned char *sha1);
 extern char *sha1_to_hex(const unsigned char *sha1);   /* static buffer result! */
@@ -1023,12 +1026,20 @@ extern const char *packed_object_info_detail(struct packed_git *, off_t, unsigne
 /* Dumb servers support */
 extern int update_server_info(int);
 
+/* git_config_parse_key() returns these negated: */
+#define CONFIG_INVALID_KEY 1
+#define CONFIG_NO_SECTION_OR_NAME 2
+/* git_config_set(), git_config_set_multivar() return the above or these: */
+#define CONFIG_NO_LOCK -1
+#define CONFIG_INVALID_FILE 3
+#define CONFIG_NO_WRITE 4
+#define CONFIG_NOTHING_SET 5
+#define CONFIG_INVALID_PATTERN 6
+
 typedef int (*config_fn_t)(const char *, const char *, void *);
 extern int git_default_config(const char *, const char *, void *);
 extern int git_config_from_file(config_fn_t fn, const char *, void *);
 extern void git_config_push_parameter(const char *text);
-extern int git_config_parse_parameter(const char *text);
-extern int git_config_parse_environment(void);
 extern int git_config_from_parameters(config_fn_t fn, void *data);
 extern int git_config(config_fn_t fn, void *);
 extern int git_config_early(config_fn_t fn, void *, const char *repo_config);
index 3114bd1781c3c5e735dfc1b1a7b4131270992f14..a2d571b97410fa857b4c177325c4556dac50fe3f 100644 (file)
--- a/commit.h
+++ b/commit.h
@@ -69,9 +69,11 @@ enum cmit_fmt {
 };
 
 struct pretty_print_context {
+       enum cmit_fmt fmt;
        int abbrev;
        const char *subject;
        const char *after_subject;
+       int preserve_subject;
        enum date_mode date_mode;
        int need_8bit_cte;
        int show_notes;
@@ -96,20 +98,20 @@ extern void userformat_find_requirements(const char *fmt, struct userformat_want
 extern void format_commit_message(const struct commit *commit,
                                  const char *format, struct strbuf *sb,
                                  const struct pretty_print_context *context);
-extern void pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit,
-                               struct strbuf *sb,
-                               const struct pretty_print_context *context);
-void pp_user_info(const char *what, enum cmit_fmt fmt, struct strbuf *sb,
-                  const char *line, enum date_mode dmode,
-                  const char *encoding);
-void pp_title_line(enum cmit_fmt fmt,
+extern void pretty_print_commit(const struct pretty_print_context *pp,
+                               const struct commit *commit,
+                               struct strbuf *sb);
+extern void pp_commit_easy(enum cmit_fmt fmt, const struct commit *commit,
+                          struct strbuf *sb);
+void pp_user_info(const struct pretty_print_context *pp,
+                 const char *what, struct strbuf *sb,
+                 const char *line, const char *encoding);
+void pp_title_line(const struct pretty_print_context *pp,
                   const char **msg_p,
                   struct strbuf *sb,
-                  const char *subject,
-                  const char *after_subject,
                   const char *encoding,
                   int need_8bit_cte);
-void pp_remainder(enum cmit_fmt fmt,
+void pp_remainder(const struct pretty_print_context *pp,
                  const char **msg_p,
                  struct strbuf *sb,
                  int indent);
index 14feac7fe179069908df6dbc084b2adcc78c9242..9473aed2bbcb91994f166cf66d24459c66ac2fcb 100644 (file)
@@ -127,6 +127,10 @@ extern char *getenv ();
 extern int errno;
 # endif
 
+# ifndef NULL
+#  define NULL 0
+# endif
+
 /* This function doesn't exist on most systems.  */
 
 # if !defined HAVE___STRCHRNUL && !defined _LIBC
index 4423961768b7389e07090ba8531e086f21d678cf..f6e9ff7762356e099d2fe20fd31359bc0a1f68a2 100644 (file)
@@ -1381,6 +1381,13 @@ int mingw_setsockopt(int sockfd, int lvl, int optname, void *optval, int optlen)
        return setsockopt(s, lvl, optname, (const char*)optval, optlen);
 }
 
+#undef shutdown
+int mingw_shutdown(int sockfd, int how)
+{
+       SOCKET s = (SOCKET)_get_osfhandle(sockfd);
+       return shutdown(s, how);
+}
+
 #undef listen
 int mingw_listen(int sockfd, int backlog)
 {
index 62eccd33911e2f332dc9d0faf097ecff6d6a7aa6..547568b9181d61d50f06cf5b4b0ab43af78e5aa2 100644 (file)
@@ -217,6 +217,9 @@ int mingw_bind(int sockfd, struct sockaddr *sa, size_t sz);
 int mingw_setsockopt(int sockfd, int lvl, int optname, void *optval, int optlen);
 #define setsockopt mingw_setsockopt
 
+int mingw_shutdown(int sockfd, int how);
+#define shutdown mingw_shutdown
+
 int mingw_listen(int sockfd, int backlog);
 #define listen mingw_listen
 
index 671c8df2cc4a856b3b32d4ec047298f0e5547878..e0b3b80d92ece9f37e0e76292bd61ce97271fcce 100644 (file)
--- a/config.c
+++ b/config.c
@@ -20,14 +20,6 @@ static int zlib_compression_seen;
 
 const char *config_exclusive_filename = NULL;
 
-struct config_item {
-       struct config_item *next;
-       char *name;
-       char *value;
-};
-static struct config_item *config_parameters;
-static struct config_item **config_parameters_tail = &config_parameters;
-
 static void lowercase(char *p)
 {
        for (; *p; p++)
@@ -47,9 +39,9 @@ void git_config_push_parameter(const char *text)
        strbuf_release(&env);
 }
 
-int git_config_parse_parameter(const char *text)
+static int git_config_parse_parameter(const char *text,
+                                     config_fn_t fn, void *data)
 {
-       struct config_item *ct;
        struct strbuf tmp = STRBUF_INIT;
        struct strbuf **pair;
        strbuf_addstr(&tmp, text);
@@ -59,22 +51,19 @@ int git_config_parse_parameter(const char *text)
        strbuf_trim(pair[0]);
        if (!pair[0]->len) {
                strbuf_list_free(pair);
-               return -1;
+               return error("bogus config parameter: %s", text);
        }
-       ct = xcalloc(1, sizeof(struct config_item));
-       ct->name = strbuf_detach(pair[0], NULL);
-       if (pair[1]) {
-               strbuf_trim(pair[1]);
-               ct->value = strbuf_detach(pair[1], NULL);
+       lowercase(pair[0]->buf);
+       if (fn(pair[0]->buf, pair[1] ? pair[1]->buf : NULL, data) < 0) {
+               strbuf_list_free(pair);
+               return -1;
        }
        strbuf_list_free(pair);
-       lowercase(ct->name);
-       *config_parameters_tail = ct;
-       config_parameters_tail = &ct->next;
        return 0;
 }
 
-int git_config_parse_environment(void) {
+int git_config_from_parameters(config_fn_t fn, void *data)
+{
        const char *env = getenv(CONFIG_DATA_ENVIRONMENT);
        char *envw;
        const char **argv = NULL;
@@ -92,8 +81,7 @@ int git_config_parse_environment(void) {
        }
 
        for (i = 0; i < nr; i++) {
-               if (git_config_parse_parameter(argv[i]) < 0) {
-                       error("bogus config parameter: %s", argv[i]);
+               if (git_config_parse_parameter(argv[i], fn, data) < 0) {
                        free(argv);
                        free(envw);
                        return -1;
@@ -102,7 +90,7 @@ int git_config_parse_environment(void) {
 
        free(argv);
        free(envw);
-       return 0;
+       return nr > 0;
 }
 
 static int get_next_char(void)
@@ -837,22 +825,6 @@ int git_config_system(void)
        return !git_env_bool("GIT_CONFIG_NOSYSTEM", 0);
 }
 
-int git_config_from_parameters(config_fn_t fn, void *data)
-{
-       static int loaded_environment;
-       const struct config_item *ct;
-
-       if (!loaded_environment) {
-               if (git_config_parse_environment() < 0)
-                       return -1;
-               loaded_environment = 1;
-       }
-       for (ct = config_parameters; ct; ct = ct->next)
-               if (fn(ct->name, ct->value, data) < 0)
-                       return -1;
-       return 0;
-}
-
 int git_config_early(config_fn_t fn, void *data, const char *repo_config)
 {
        int ret = 0, found = 0;
@@ -882,9 +854,16 @@ int git_config_early(config_fn_t fn, void *data, const char *repo_config)
                found += 1;
        }
 
-       ret += git_config_from_parameters(fn, data);
-       if (config_parameters)
-               found += 1;
+       switch (git_config_from_parameters(fn, data)) {
+       case -1: /* error */
+               ret--;
+               break;
+       case 0: /* found nothing */
+               break;
+       default: /* found at least one item */
+               found++;
+               break;
+       }
 
        return ret == 0 ? found : ret;
 }
@@ -1123,12 +1102,12 @@ int git_config_parse_key(const char *key, char **store_key, int *baselen_)
 
        if (last_dot == NULL || last_dot == key) {
                error("key does not contain a section: %s", key);
-               return -2;
+               return -CONFIG_NO_SECTION_OR_NAME;
        }
 
        if (!last_dot[1]) {
                error("key does not contain variable name: %s", key);
-               return -2;
+               return -CONFIG_NO_SECTION_OR_NAME;
        }
 
        baselen = last_dot - key;
@@ -1165,7 +1144,7 @@ int git_config_parse_key(const char *key, char **store_key, int *baselen_)
 
 out_free_ret_1:
        free(*store_key);
-       return -1;
+       return -CONFIG_INVALID_KEY;
 }
 
 /*
@@ -1221,7 +1200,7 @@ int git_config_set_multivar(const char *key, const char *value,
        if (fd < 0) {
                error("could not lock config file %s: %s", config_filename, strerror(errno));
                free(store.key);
-               ret = -1;
+               ret = CONFIG_NO_LOCK;
                goto out_free;
        }
 
@@ -1235,12 +1214,12 @@ int git_config_set_multivar(const char *key, const char *value,
                if ( ENOENT != errno ) {
                        error("opening %s: %s", config_filename,
                              strerror(errno));
-                       ret = 3; /* same as "invalid config file" */
+                       ret = CONFIG_INVALID_FILE; /* same as "invalid config file" */
                        goto out_free;
                }
                /* if nothing to unset, error out */
                if (value == NULL) {
-                       ret = 5;
+                       ret = CONFIG_NOTHING_SET;
                        goto out_free;
                }
 
@@ -1268,7 +1247,7 @@ int git_config_set_multivar(const char *key, const char *value,
                                        REG_EXTENDED)) {
                                error("invalid pattern: %s", value_regex);
                                free(store.value_regex);
-                               ret = 6;
+                               ret = CONFIG_INVALID_PATTERN;
                                goto out_free;
                        }
                }
@@ -1290,7 +1269,7 @@ int git_config_set_multivar(const char *key, const char *value,
                                regfree(store.value_regex);
                                free(store.value_regex);
                        }
-                       ret = 3;
+                       ret = CONFIG_INVALID_FILE;
                        goto out_free;
                }
 
@@ -1303,7 +1282,7 @@ int git_config_set_multivar(const char *key, const char *value,
                /* if nothing to unset, or too many matches, error out */
                if ((store.seen == 0 && value == NULL) ||
                                (store.seen > 1 && multi_replace == 0)) {
-                       ret = 5;
+                       ret = CONFIG_NOTHING_SET;
                        goto out_free;
                }
 
@@ -1364,7 +1343,7 @@ int git_config_set_multivar(const char *key, const char *value,
 
        if (commit_lock_file(lock) < 0) {
                error("could not commit config file %s", config_filename);
-               ret = 4;
+               ret = CONFIG_NO_WRITE;
                goto out_free;
        }
 
index e378534cbd57add4a6c75b8eb45353e8a9322914..ab371012a22f39bf31f512e276e12f55b6d1b8b7 100644 (file)
@@ -18,6 +18,7 @@ bindir = @bindir@
 gitexecdir = @libexecdir@/git-core
 datarootdir = @datarootdir@
 template_dir = @datadir@/git-core/templates
+sysconfdir = @sysconfdir@
 
 mandir=@mandir@
 
@@ -61,6 +62,7 @@ NO_INET_PTON=@NO_INET_PTON@
 NO_ICONV=@NO_ICONV@
 OLD_ICONV=@OLD_ICONV@
 NO_REGEX=@NO_REGEX@
+USE_LIBPCRE=@USE_LIBPCRE@
 NO_DEFLATE_BOUND=@NO_DEFLATE_BOUND@
 INLINE=@INLINE@
 SOCKLEN_T=@SOCKLEN_T@
index fafd81557c2d3dd17cb1c15c545f3602a3a0c6bf..048a1d4972769184ff857fb7681bc67b2ebdfb99 100644 (file)
@@ -220,6 +220,27 @@ AS_HELP_STRING([--with-openssl],[use OpenSSL library (default is YES)])
 AS_HELP_STRING([],              [ARG can be prefix for openssl library and headers]),\
 GIT_PARSE_WITH(openssl))
 #
+# Define USE_LIBPCRE if you have and want to use libpcre. git-grep will be
+# able to use Perl-compatible regular expressions.
+#
+# Define LIBPCREDIR=/foo/bar if your libpcre header and library files are in
+# /foo/bar/include and /foo/bar/lib directories.
+#
+AC_ARG_WITH(libpcre,
+AS_HELP_STRING([--with-libpcre],[support Perl-compatible regexes (default is NO)])
+AS_HELP_STRING([],           [ARG can be also prefix for libpcre library and headers]),
+if test "$withval" = "no"; then \
+       USE_LIBPCRE=; \
+elif test "$withval" = "yes"; then \
+       USE_LIBPCRE=YesPlease; \
+else
+       USE_LIBPCRE=YesPlease; \
+       LIBPCREDIR=$withval; \
+       AC_MSG_NOTICE([Setting LIBPCREDIR to $withval]); \
+       GIT_CONF_APPEND_LINE(LIBPCREDIR=$withval); \
+fi \
+)
+#
 # Define NO_CURL if you do not have curl installed.  git-http-pull and
 # git-http-push are not built, and you cannot use http:// and https://
 # transports.
@@ -434,6 +455,25 @@ GIT_UNSTASH_FLAGS($OPENSSLDIR)
 AC_SUBST(NEEDS_SSL_WITH_CRYPTO)
 AC_SUBST(NO_OPENSSL)
 
+#
+# Define USE_LIBPCRE if you have and want to use libpcre. git-grep will be
+# able to use Perl-compatible regular expressions.
+#
+
+if test -n "$USE_LIBPCRE"; then
+
+GIT_STASH_FLAGS($LIBPCREDIR)
+
+AC_CHECK_LIB([pcre], [pcre_version],
+[USE_LIBPCRE=YesPlease],
+[USE_LIBPCRE=])
+
+GIT_UNSTASH_FLAGS($LIBPCREDIR)
+
+AC_SUBST(USE_LIBPCRE)
+
+fi
+
 #
 # Define NO_CURL if you do not have libcurl installed.  git-http-pull and
 # git-http-push are not built, and you cannot use http:// and https://
index bb8d7d0878994eb3d53163c330de44d8c0d48b3f..5a8309076dc633f431797c4bd59876490b0d7408 100755 (executable)
@@ -1441,8 +1441,9 @@ _git_grep ()
                __gitcomp "
                        --cached
                        --text --ignore-case --word-regexp --invert-match
-                       --full-name
+                       --full-name --line-number
                        --extended-regexp --basic-regexp --fixed-strings
+                       --perl-regexp
                        --files-with-matches --name-only
                        --files-without-match
                        --max-depth
@@ -2057,7 +2058,7 @@ _git_config ()
                color.ui
                commit.status
                commit.template
-               core.abbrevguard
+               core.abbrev
                core.askpass
                core.attributesfile
                core.autocrlf
diff --git a/ctype.c b/ctype.c
index de600279eef4765db497599e6654c2bedd978129..b5d856fd26bd892a5f18202b054fc53e7c953429 100644 (file)
--- a/ctype.c
+++ b/ctype.c
@@ -10,17 +10,18 @@ enum {
        A = GIT_ALPHA,
        D = GIT_DIGIT,
        G = GIT_GLOB_SPECIAL,   /* *, ?, [, \\ */
-       R = GIT_REGEX_SPECIAL   /* $, (, ), +, ., ^, {, | */
+       R = GIT_REGEX_SPECIAL,  /* $, (, ), +, ., ^, {, | */
+       P = GIT_PATHSPEC_MAGIC  /* other non-alnum, except for ] and } */
 };
 
 unsigned char sane_ctype[256] = {
        0, 0, 0, 0, 0, 0, 0, 0, 0, S, S, 0, 0, S, 0, 0,         /*   0.. 15 */
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,         /*  16.. 31 */
-       S, 0, 0, 0, R, 0, 0, 0, R, R, G, R, 0, 0, R, 0,         /*  32.. 47 */
-       D, D, D, D, D, D, D, D, D, D, 0, 0, 0, 0, 0, G,         /*  48.. 63 */
-       0, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A,         /*  64.. 79 */
-       A, A, A, A, A, A, A, A, A, A, A, G, G, 0, R, 0,         /*  80.. 95 */
-       0, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A,         /*  96..111 */
-       A, A, A, A, A, A, A, A, A, A, A, R, R, 0, 0, 0,         /* 112..127 */
+       S, P, P, P, R, P, P, P, R, R, G, R, P, P, R, P,         /*  32.. 47 */
+       D, D, D, D, D, D, D, D, D, D, P, P, P, P, P, G,         /*  48.. 63 */
+       P, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A,         /*  64.. 79 */
+       A, A, A, A, A, A, A, A, A, A, A, G, G, 0, R, P,         /*  80.. 95 */
+       P, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A,         /*  96..111 */
+       A, A, A, A, A, A, A, A, A, A, A, R, R, 0, P, 0,         /* 112..127 */
        /* Nothing in the 128.. range */
 };
index 3b5f2242a597ff1b44a3af6be72cb14e6e0d5455..9c29293bbc05d175ba13338813e8532c7ad677cf 100644 (file)
@@ -102,9 +102,7 @@ int run_diff_files(struct rev_info *revs, unsigned int option)
                int changed;
                unsigned dirty_submodule = 0;
 
-               if (DIFF_OPT_TST(&revs->diffopt, QUICK) &&
-                   !revs->diffopt.filter &&
-                   DIFF_OPT_TST(&revs->diffopt, HAS_CHANGES))
+               if (diff_can_quit_early(&revs->diffopt))
                        break;
 
                if (!ce_path_match(ce, &revs->prune_data))
diff --git a/diff.c b/diff.c
index 3f538f5803e414fb313ab60598682b8b8491e204..5dd9049c7d354110534b2770fbf6cbc8e74e359e 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -1117,8 +1117,16 @@ static void fn_out_consume(void *priv, char *line, unsigned long len)
                        emit_line(ecbdata->opt, plain, reset, line, len);
                        fputs("~\n", ecbdata->opt->file);
                } else {
-                       /* don't print the prefix character */
-                       emit_line(ecbdata->opt, plain, reset, line+1, len-1);
+                       /*
+                        * Skip the prefix character, if any.  With
+                        * diff_suppress_blank_empty, there may be
+                        * none.
+                        */
+                       if (line[0] != '\n') {
+                             line++;
+                             len--;
+                       }
+                       emit_line(ecbdata->opt, plain, reset, line, len);
                }
                return;
        }
@@ -4436,6 +4444,13 @@ int diff_result_code(struct diff_options *opt, int status)
        return result;
 }
 
+int diff_can_quit_early(struct diff_options *opt)
+{
+       return (DIFF_OPT_TST(opt, QUICK) &&
+               !opt->filter &&
+               DIFF_OPT_TST(opt, HAS_CHANGES));
+}
+
 /*
  * Shall changes to this submodule be ignored?
  *
diff --git a/diff.h b/diff.h
index adb40ba273ffec74010a36e98a538fcc204919f1..6d303c1d50aa799ec90aeb64b2a1b1f55811dd46 100644 (file)
--- a/diff.h
+++ b/diff.h
@@ -198,6 +198,8 @@ extern void diff_tree_combined_merge(const unsigned char *sha1, int, struct rev_
 
 void diff_set_mnemonic_prefix(struct diff_options *options, const char *a, const char *b);
 
+extern int diff_can_quit_early(struct diff_options *);
+
 extern void diff_addremove(struct diff_options *,
                           int addremove,
                           unsigned mode,
diff --git a/fsck.c b/fsck.c
index 6f266c1ea4b8df503c211170741fa5b490a6a02a..60bd4bbf6a2206e2b1991093ea5d84be7f338f73 100644 (file)
--- a/fsck.c
+++ b/fsck.c
@@ -349,7 +349,7 @@ int fsck_error_function(struct object *obj, int type, const char *fmt, ...)
        va_list ap;
        struct strbuf sb = STRBUF_INIT;
 
-       strbuf_addf(&sb, "object %s:", obj->sha1?sha1_to_hex(obj->sha1):"(null)");
+       strbuf_addf(&sb, "object %s:", sha1_to_hex(obj->sha1));
 
        va_start(ap, fmt);
        strbuf_vaddf(&sb, fmt, ap);
index 4f08fe704bbaff64613ab07630a71da8bf8bbca0..8f0839d205e0c4010e256bb5cf81c73cc2f438ab 100755 (executable)
@@ -45,6 +45,9 @@
 my $normal_color = $repo->get_color("", "reset");
 
 my $use_readkey = 0;
+my $use_termcap = 0;
+my %term_escapes;
+
 sub ReadMode;
 sub ReadKey;
 if ($repo->config_bool("interactive.singlekey")) {
                Term::ReadKey->import;
                $use_readkey = 1;
        };
+       eval {
+               require Term::Cap;
+               my $termcap = Term::Cap->Tgetent;
+               foreach (values %$termcap) {
+                       $term_escapes{$_} = 1 if /^\e/;
+               }
+               $use_termcap = 1;
+       };
 }
 
 sub colored {
@@ -1067,6 +1078,14 @@ sub prompt_single_character {
                ReadMode 'cbreak';
                my $key = ReadKey 0;
                ReadMode 'restore';
+               if ($use_termcap and $key eq "\e") {
+                       while (!defined $term_escapes{$key}) {
+                               my $next = ReadKey 0.5;
+                               last if (!defined $next);
+                               $key .= $next;
+                       }
+                       $key =~ s/\e/^[/;
+               }
                print "$key" if defined $key;
                print "\n";
                return $key;
index 40498b33c9f09ef9cac1f7340a9a1ceb2ffcd50d..e0bb81ed8d0bd89f18b31b1c03d3e23744aea5a1 100644 (file)
@@ -463,6 +463,7 @@ extern unsigned char sane_ctype[256];
 #define GIT_ALPHA 0x04
 #define GIT_GLOB_SPECIAL 0x08
 #define GIT_REGEX_SPECIAL 0x10
+#define GIT_PATHSPEC_MAGIC 0x20
 #define sane_istest(x,mask) ((sane_ctype[(unsigned char)(x)] & (mask)) != 0)
 #define isascii(x) (((x) & ~0x7f) == 0)
 #define isspace(x) sane_istest(x,GIT_SPACE)
@@ -473,6 +474,7 @@ extern unsigned char sane_ctype[256];
 #define is_regex_special(x) sane_istest(x,GIT_GLOB_SPECIAL | GIT_REGEX_SPECIAL)
 #define tolower(x) sane_case((unsigned char)(x), 0x20)
 #define toupper(x) sane_case((unsigned char)(x), 0)
+#define is_pathspec_magic(x) sane_istest(x,GIT_PATHSPEC_MAGIC)
 
 static inline int sane_case(int x, int high)
 {
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 41ba96aeb7f8ee32f7c346d5d7934f4d6b305d2f..65690af89307abd72749123caddce9e6c4692383 100644 (file)
@@ -510,7 +510,9 @@ do_next () {
        refs/*)
                message="$GIT_REFLOG_ACTION: $head_name onto $shortonto" &&
                git update-ref -m "$message" $head_name $newhead $orig_head &&
-               git symbolic-ref HEAD $head_name
+               git symbolic-ref \
+                 -m "$GIT_REFLOG_ACTION: returning to $head_name" \
+                 HEAD $head_name
                ;;
        esac && {
                test ! -f "$state_dir"/verbose ||
index 7a54bfc6182a567860af023cb7720680d1806c72..d7855ea1c6dae8d8cbe35ae39f9c65e7bc9d7b11 100755 (executable)
@@ -153,7 +153,9 @@ move_to_original_branch () {
                message="rebase finished: $head_name onto $onto"
                git update-ref -m "$message" \
                        $head_name $(git rev-parse HEAD) $orig_head &&
-               git symbolic-ref HEAD $head_name ||
+               git symbolic-ref \
+                       -m "rebase finished: returning to $head_name" \
+                       HEAD $head_name ||
                die "Could not move back to $head_name"
                ;;
        esac
@@ -332,7 +334,7 @@ abort)
        read_basic_state
        case "$head_name" in
        refs/*)
-               git symbolic-ref HEAD $head_name ||
+               git symbolic-ref -m "rebase: aborting" HEAD $head_name ||
                die "Could not move back to $head_name"
                ;;
        esac
diff --git a/git-sh-i18n.sh b/git-sh-i18n.sh
new file mode 100644 (file)
index 0000000..32ca59d
--- /dev/null
@@ -0,0 +1,29 @@
+#!/bin/sh
+#
+# Copyright (c) 2010 Ã†var Arnfjörð Bjarmason
+#
+# This is a skeleton no-op implementation of gettext for Git. It'll be
+# replaced by something that uses gettext.sh in a future patch series.
+
+if test -z "$GIT_GETTEXT_POISON"
+then
+       gettext () {
+               printf "%s" "$1"
+       }
+
+       eval_gettext () {
+               printf "%s" "$1" | (
+                       export PATH $(git sh-i18n--envsubst --variables "$1");
+                       git sh-i18n--envsubst "$1"
+               )
+       }
+else
+       gettext () {
+               printf "%s" "# GETTEXT POISON #"
+       }
+
+       eval_gettext () {
+               printf "%s" "# GETTEXT POISON #"
+       }
+fi
+
index aa16b8356507e4e669ee5c4cb4b5a667942d559e..94e26ed5e8dcf84c4f238c76b6c508dc84d0b7ea 100644 (file)
@@ -140,6 +140,13 @@ cd_to_toplevel () {
        }
 }
 
+require_work_tree_exists () {
+       if test "z$(git rev-parse --is-bare-repository)" != zfalse
+       then
+               die "fatal: $0 cannot be used without a working tree."
+       fi
+}
+
 require_work_tree () {
        test "$(git rev-parse --is-inside-work-tree 2>/dev/null)" = true ||
        die "fatal: $0 cannot be used without a working tree."
index bf110e9cb77a0e9930c427408e18d323db2c8916..d189a24c71c44806a9c1381e2a8e5993269e568a 100755 (executable)
@@ -495,7 +495,7 @@ cmd_update()
                                # Run fetch only if $sha1 isn't present or it
                                # is not reachable from a ref.
                                (clear_local_git_env; cd "$path" &&
-                                       ((rev=$(git rev-list -n 1 $sha1 --not --all 2>/dev/null) &&
+                                       ( (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'"
                        fi
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 a5ef3c69ff58434e521d2c42f1b2fa275b872828..89721d420a09bfc8eb6a2d4010f86f5685cfd255 100644 (file)
--- a/git.c
+++ b/git.c
@@ -66,7 +66,7 @@ static void commit_pager_choice(void) {
 
 static int handle_options(const char ***argv, int *argc, int *envchanged)
 {
-       int handled = 0;
+       const char **orig_argv = *argv;
 
        while (*argc > 0) {
                const char *cmd = (*argv)[0];
@@ -122,7 +122,6 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
                                *envchanged = 1;
                        (*argv)++;
                        (*argc)--;
-                       handled++;
                } else if (!prefixcmp(cmd, "--git-dir=")) {
                        setenv(GIT_DIR_ENVIRONMENT, cmd + 10, 1);
                        if (envchanged)
@@ -162,9 +161,8 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
 
                (*argv)++;
                (*argc)--;
-               handled++;
        }
-       return handled;
+       return (*argv) - orig_argv;
 }
 
 static int handle_alias(int *argcp, const char ***argv)
index 4964a679b3013caa9bfa2f438dc110eaa0274b69..c5236fee9dced35ab35d02eb7d42109f0107a165 100644 (file)
@@ -25,11 +25,25 @@ The above example assumes that your web server is configured to run
 scripts).
 
 
+Requirements
+------------
+
+ - Core git tools
+ - Perl
+ - Perl modules: CGI, Encode, Fcntl, File::Find, File::Basename.
+ - web server
+
+The following optional Perl modules are required for extra features
+ - Digest::MD5 - for gravatar support
+ - CGI::Fast and FCGI - for running gitweb as FastCGI script
+ - HTML::TagCloud - for fancy tag cloud in project list view
+ - HTTP::Date or Time::ParseDate - to support If-Modified-Since for feeds
+
+
 Build time configuration
 ------------------------
 
-See also "How to configure gitweb for your local system" in README
-file for gitweb (in gitweb/README).
+See also "How to configure gitweb for your local system" section below.
 
 - There are many configuration variables which affect building of
   gitweb.cgi; see "default configuration for gitweb" section in main
@@ -73,6 +87,125 @@ file for gitweb (in gitweb/README).
   substitute gitweb.min.js and gitweb.min.css for all uses of gitweb.js and
   gitweb.css in the help files.
 
+
+How to configure gitweb for your local system
+---------------------------------------------
+
+You can specify the following configuration variables when building GIT:
+
+ * GIT_BINDIR
+   Points where to find the git executable.  You should set it up to
+   the place where the git binary was installed (usually /usr/bin) if you
+   don't install git from sources together with gitweb.  [Default: $(bindir)]
+ * GITWEB_SITENAME
+   Shown in the title of all generated pages, defaults to the server name
+   (SERVER_NAME CGI environment variable) if not set. [No default]
+ * GITWEB_PROJECTROOT
+   The root directory for all projects shown by gitweb. Must be set
+   correctly for gitweb to find repositories to display.  See also
+   "Gitweb repositories" in the INSTALL file for gitweb.  [Default: /pub/git]
+ * GITWEB_PROJECT_MAXDEPTH
+   The filesystem traversing limit for getting the project list; the number
+   is taken as depth relative to the projectroot.  It is used when
+   GITWEB_LIST is a directory (or is not set; then project root is used).
+   This is meant to speed up project listing on large work trees by limiting
+   search depth.  [Default: 2007]
+ * GITWEB_LIST
+   Points to a directory to scan for projects (defaults to project root
+   if not set / if empty) or to a file with explicit listing of projects
+   (together with projects' ownership). See "Generating projects list
+   using gitweb" in INSTALL file for gitweb to find out how to generate
+   such file from scan of a directory. [No default, which means use root
+   directory for projects]
+ * GITWEB_EXPORT_OK
+   Show repository only if this file exists (in repository).  Only
+   effective if this variable evaluates to true.  [No default / Not set]
+ * GITWEB_STRICT_EXPORT
+   Only allow viewing of repositories also shown on the overview page.
+   This for example makes GITWEB_EXPORT_OK to decide if repository is
+   available and not only if it is shown.  If GITWEB_LIST points to
+   file with list of project, only those repositories listed would be
+   available for gitweb.  [No default]
+ * GITWEB_HOMETEXT
+   Points to an .html file which is included on the gitweb project
+   overview page ('projects_list' view), if it exists.  Relative to
+   gitweb.cgi script.  [Default: indextext.html]
+ * GITWEB_SITE_HEADER
+   Filename of html text to include at top of each page.  Relative to
+   gitweb.cgi script.  [No default]
+ * GITWEB_SITE_FOOTER
+   Filename of html text to include at bottom of each page.  Relative to
+   gitweb.cgi script.  [No default]
+ * GITWEB_HOME_LINK_STR
+   String of the home link on top of all pages, leading to $home_link
+   (usually main gitweb page, which means projects list).  Used as first
+   part of gitweb view "breadcrumb trail": <home> / <project> / <view>.
+   [Default: projects]
+ * GITWEB_SITENAME
+   Name of your site or organization to appear in page titles.  Set it
+   to something descriptive for clearer bookmarks etc.  If not set
+   (if empty) gitweb uses "$SERVER_NAME Git", or "Untitled Git" if
+   SERVER_NAME CGI environment variable is not set (e.g. if running
+   gitweb as standalone script).  [No default]
+ * GITWEB_BASE_URL
+   Git base URLs used for URL to where fetch project from, i.e. full
+   URL is "$git_base_url/$project".  Shown on projects summary page.
+   Repository URL for project can be also configured per repository; this
+   takes precedence over URLs composed from base URL and a project name.
+   Note that you can setup multiple base URLs (for example one for
+   git:// protocol access, another for http:// access) from the gitweb
+   config file.  [No default]
+ * GITWEB_CSS
+   Points to the location where you put gitweb.css on your web server
+   (or to be more generic, the URI of gitweb stylesheet).  Relative to the
+   base URI of gitweb.  Note that you can setup multiple stylesheets from
+   the gitweb config file.  [Default: static/gitweb.css (or
+   static/gitweb.min.css if the CSSMIN variable is defined / CSS minifier
+   is used)]
+ * GITWEB_JS
+   Points to the location where you put gitweb.js on your web server
+   (or to be more generic URI of JavaScript code used by gitweb).
+   Relative to base URI of gitweb.  [Default: static/gitweb.js (or
+   static/gitweb.min.js if JSMIN build variable is defined / JavaScript
+   minifier is used)]
+ * CSSMIN, JSMIN
+   Invocation of a CSS minifier or a JavaScript minifier, respectively,
+   working as a filter (source on standard input, minified result on
+   standard output).  If set, it is used to generate a minified version of
+   'static/gitweb.css' or 'static/gitweb.js', respectively.  *Note* that
+   minified files would have *.min.css and *.min.js extension, which is
+   important if you also set GITWEB_CSS and/or GITWEB_JS.  [No default]
+ * GITWEB_LOGO
+   Points to the location where you put git-logo.png on your web server
+   (or to be more generic URI of logo, 72x27 size, displayed in top right
+   corner of each gitweb page, and used as logo for Atom feed).  Relative
+   to base URI of gitweb.  [Default: static/git-logo.png]
+ * GITWEB_FAVICON
+   Points to the location where you put git-favicon.png on your web server
+   (or to be more generic URI of favicon, assumed to be image/png type;
+   web browsers that support favicons (website icons) may display them
+   in the browser's URL bar and next to site name in bookmarks).  Relative
+   to base URI of gitweb.  [Default: static/git-favicon.png]
+ * GITWEB_CONFIG
+   This Perl file will be loaded using 'do' and can be used to override any
+   of the options above as well as some other options -- see the "Runtime
+   gitweb configuration" section below, and top of 'gitweb.cgi' for their
+   full list and description.  If the environment variable GITWEB_CONFIG
+   is set when gitweb.cgi is executed, then the file specified in the
+   environment variable will be loaded instead of the file specified
+   when gitweb.cgi was created.  [Default: gitweb_config.perl]
+ * GITWEB_CONFIG_SYSTEM
+   This Perl file will be loaded using 'do' as a fallback if GITWEB_CONFIG
+   does not exist.  If the environment variable GITWEB_CONFIG_SYSTEM is set
+   when gitweb.cgi is executed, then the file specified in the environment
+   variable will be loaded instead of the file specified when gitweb.cgi was
+   created.  [Default: /etc/gitweb.conf]
+ * HIGHLIGHT_BIN
+   Path to the highlight executable to use (must be the one from
+   http://www.andre-simon.de due to assumptions about parameters and output).
+   Useful if highlight is not installed on your webserver's PATH.
+   [Default: highlight]
+
 Build example
 ~~~~~~~~~~~~~
 
@@ -229,21 +362,6 @@ $projects_list variable in gitweb config):
        perl -- /var/www/cgi-bin/gitweb.cgi
 
 
-Requirements
-------------
-
- - Core git tools
- - Perl
- - Perl modules: CGI, Encode, Fcntl, File::Find, File::Basename.
- - web server
-
-The following optional Perl modules are required for extra features
- - Digest::MD5 - for gravatar support
- - CGI::Fast and FCGI - for running gitweb as FastCGI script
- - HTML::TagCloud - for fancy tag cloud in project list view
- - HTTP::Date or Time::ParseDate - to support If-Modified-Since for feeds
-
-
 Example web server configuration
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
index 0a6ac00631ed8eac5c20248a345074e4b8ff3707..5d20515fba9251123e29be623178047d4131fc01 100644 (file)
@@ -86,7 +86,7 @@ ifndef V
 endif
 endif
 
-all:: gitweb.cgi
+all:: gitweb.cgi static/gitweb.js
 
 GITWEB_PROGRAMS = gitweb.cgi
 
@@ -112,6 +112,18 @@ endif
 
 GITWEB_FILES += static/git-logo.png static/git-favicon.png
 
+# JavaScript files that are composed (concatenated) to form gitweb.js
+#
+# js/lib/common-lib.js should be always first, then js/lib/*.js,
+# then the rest of files; js/gitweb.js should be last (if it exists)
+GITWEB_JSLIB_FILES += static/js/lib/common-lib.js
+GITWEB_JSLIB_FILES += static/js/lib/datetime.js
+GITWEB_JSLIB_FILES += static/js/lib/cookies.js
+GITWEB_JSLIB_FILES += static/js/javascript-detection.js
+GITWEB_JSLIB_FILES += static/js/adjust-timezone.js
+GITWEB_JSLIB_FILES += static/js/blame_incremental.js
+
+
 GITWEB_REPLACE = \
        -e 's|++GIT_VERSION++|$(GIT_VERSION)|g' \
        -e 's|++GIT_BINDIR++|$(bindir)|g' \
@@ -146,6 +158,11 @@ gitweb.cgi: gitweb.perl GITWEB-BUILD-OPTIONS
        chmod +x $@+ && \
        mv $@+ $@
 
+static/gitweb.js: $(GITWEB_JSLIB_FILES)
+       $(QUIET_GEN)$(RM) $@ $@+ && \
+       cat $^ >$@+ && \
+       mv $@+ $@
+
 ### Testing rules
 
 test:
index a92bde7f1430043e5955c61eba287a22d4810d22..a4cfcb42cd989c063c49b6511393ba936e93978a 100644 (file)
@@ -7,120 +7,6 @@ The one working on:
 From the git version 1.4.0 gitweb is bundled with git.
 
 
-How to configure gitweb for your local system
----------------------------------------------
-
-See also the "Build time configuration" section in the INSTALL
-file for gitweb (in gitweb/INSTALL).
-
-You can specify the following configuration variables when building GIT:
- * GIT_BINDIR
-   Points where to find the git executable.  You should set it up to
-   the place where the git binary was installed (usually /usr/bin) if you
-   don't install git from sources together with gitweb.  [Default: $(bindir)]
- * GITWEB_SITENAME
-   Shown in the title of all generated pages, defaults to the server name
-   (SERVER_NAME CGI environment variable) if not set. [No default]
- * GITWEB_PROJECTROOT
-   The root directory for all projects shown by gitweb. Must be set
-   correctly for gitweb to find repositories to display.  See also
-   "Gitweb repositories" in the INSTALL file for gitweb.  [Default: /pub/git]
- * GITWEB_PROJECT_MAXDEPTH
-   The filesystem traversing limit for getting the project list; the number
-   is taken as depth relative to the projectroot.  It is used when
-   GITWEB_LIST is a directory (or is not set; then project root is used).
-   This is meant to speed up project listing on large work trees by limiting
-   search depth.  [Default: 2007]
- * GITWEB_LIST
-   Points to a directory to scan for projects (defaults to project root
-   if not set / if empty) or to a file with explicit listing of projects
-   (together with projects' ownership). See "Generating projects list
-   using gitweb" in INSTALL file for gitweb to find out how to generate
-   such file from scan of a directory. [No default, which means use root
-   directory for projects]
- * GITWEB_EXPORT_OK
-   Show repository only if this file exists (in repository).  Only
-   effective if this variable evaluates to true.  [No default / Not set]
- * GITWEB_STRICT_EXPORT
-   Only allow viewing of repositories also shown on the overview page.
-   This for example makes GITWEB_EXPORT_OK to decide if repository is
-   available and not only if it is shown.  If GITWEB_LIST points to
-   file with list of project, only those repositories listed would be
-   available for gitweb.  [No default]
- * GITWEB_HOMETEXT
-   Points to an .html file which is included on the gitweb project
-   overview page ('projects_list' view), if it exists.  Relative to
-   gitweb.cgi script.  [Default: indextext.html]
- * GITWEB_SITE_HEADER
-   Filename of html text to include at top of each page.  Relative to
-   gitweb.cgi script.  [No default]
- * GITWEB_SITE_FOOTER
-   Filename of html text to include at bottom of each page.  Relative to
-   gitweb.cgi script.  [No default]
- * GITWEB_HOME_LINK_STR
-   String of the home link on top of all pages, leading to $home_link
-   (usually main gitweb page, which means projects list).  Used as first
-   part of gitweb view "breadcrumb trail": <home> / <project> / <view>.
-   [Default: projects]
- * GITWEB_SITENAME
-   Name of your site or organization to appear in page titles.  Set it
-   to something descriptive for clearer bookmarks etc.  If not set
-   (if empty) gitweb uses "$SERVER_NAME Git", or "Untitled Git" if
-   SERVER_NAME CGI environment variable is not set (e.g. if running
-   gitweb as standalone script).  [No default]
- * GITWEB_BASE_URL
-   Git base URLs used for URL to where fetch project from, i.e. full
-   URL is "$git_base_url/$project".  Shown on projects summary page.
-   Repository URL for project can be also configured per repository; this
-   takes precedence over URLs composed from base URL and a project name.
-   Note that you can setup multiple base URLs (for example one for
-   git:// protocol access, another for http:// access) from the gitweb
-   config file.  [No default]
- * GITWEB_CSS
-   Points to the location where you put gitweb.css on your web server
-   (or to be more generic, the URI of gitweb stylesheet).  Relative to the
-   base URI of gitweb.  Note that you can setup multiple stylesheets from
-   the gitweb config file.  [Default: static/gitweb.css (or
-   static/gitweb.min.css if the CSSMIN variable is defined / CSS minifier
-   is used)]
- * GITWEB_LOGO
-   Points to the location where you put git-logo.png on your web server
-   (or to be more generic URI of logo, 72x27 size, displayed in top right
-   corner of each gitweb page, and used as logo for Atom feed).  Relative
-   to base URI of gitweb.  [Default: static/git-logo.png]
- * GITWEB_FAVICON
-   Points to the location where you put git-favicon.png on your web server
-   (or to be more generic URI of favicon, assumed to be image/png type;
-   web browsers that support favicons (website icons) may display them
-   in the browser's URL bar and next to site name in bookmarks).  Relative
-   to base URI of gitweb.  [Default: static/git-favicon.png]
- * GITWEB_JS
-   Points to the location where you put gitweb.js on your web server
-   (or to be more generic URI of JavaScript code used by gitweb).
-   Relative to base URI of gitweb.  [Default: static/gitweb.js (or
-   static/gitweb.min.js if JSMIN build variable is defined / JavaScript
-   minifier is used)]
- * GITWEB_CONFIG
-   This Perl file will be loaded using 'do' and can be used to override any
-   of the options above as well as some other options -- see the "Runtime
-   gitweb configuration" section below, and top of 'gitweb.cgi' for their
-   full list and description.  If the environment variable GITWEB_CONFIG
-   is set when gitweb.cgi is executed, then the file specified in the
-   environment variable will be loaded instead of the file specified
-   when gitweb.cgi was created.  [Default: gitweb_config.perl]
- * GITWEB_CONFIG_SYSTEM
-   This Perl file will be loaded using 'do' as a fallback if GITWEB_CONFIG
-   does not exist.  If the environment variable GITWEB_CONFIG_SYSTEM is set
-   when gitweb.cgi is executed, then the file specified in the environment
-   variable will be loaded instead of the file specified when gitweb.cgi was
-   created.  [Default: /etc/gitweb.conf]
- * HIGHLIGHT_BIN
-   Path to the highlight executable to use (must be the one from
-   http://www.andre-simon.de due to assumptions about parameters and output).
-   Useful if highlight is not installed on your webserver's PATH.
-   [Default: highlight]
-
-
 Runtime gitweb configuration
 ----------------------------
 
@@ -207,6 +93,15 @@ not include variables usually directly set during build):
    full description is available as 'title' attribute (usually shown on
    mouseover).  By default set to 25, which might be too small if you
    use long project descriptions.
+ * $projects_list_group_categories
+   Enables the grouping of projects by category on the project list page.
+   The category of a project is determined by the $GIT_DIR/category
+   file or the 'gitweb.category' variable in its repository configuration.
+   Disabled by default.
+ * $project_list_default_category
+   Default category for projects for which none is specified.  If set
+   to the empty string, such projects will remain uncategorized and
+   listed at the top, above categorized projects.
  * @git_base_url_list
    List of git base URLs used for URL to where fetch project from, shown
    in project summary page.  Full URL is "$git_base_url/$project".
@@ -314,6 +209,13 @@ You can use the following files in repository:
    from the template during repository creation. You can use the
    gitweb.description repo configuration variable, but the file takes
    precedence.
+ * category (or gitweb.category)
+   Singe line category of a project, used to group projects if
+   $projects_list_group_categories is enabled. By default (file and
+   configuration variable absent), uncategorized projects are put in
+   the $project_list_default_category category. You can use the
+   gitweb.category repo configuration variable, but the file takes
+   precedence.
  * cloneurl (or multiple-valued gitweb.url)
    File with repository URL (used for clone and fetch), one per line.
    Displayed in the project summary page. You can use multiple-valued
index 66eadb4ab2939b193fcf84d8d805e0b29ad5e9d2..81dacf2b6a5d98550fa56adac9450560c550eee1 100755 (executable)
@@ -115,6 +115,14 @@ sub evaluate_uri {
 # the width (in characters) of the projects list "Description" column
 our $projects_list_description_width = 25;
 
+# group projects by category on the projects list
+# (enabled if this variable evaluates to true)
+our $projects_list_group_categories = 0;
+
+# default category if none specified
+# (leave the empty string for no category)
+our $project_list_default_category = "";
+
 # default order of projects list
 # valid values are none, project, descr, owner, and age
 our $default_projects_order = "project";
@@ -320,6 +328,7 @@ sub evaluate_uri {
        # Enable grep search, which will list the files in currently selected
        # tree containing the given string. Enabled by default. This can be
        # potentially CPU-intensive, of course.
+       # Note that you need to have 'search' feature enabled too.
 
        # To enable system wide have in $GITWEB_CONFIG
        # $feature{'grep'}{'default'} = [1];
@@ -334,6 +343,7 @@ sub evaluate_uri {
        # Enable the pickaxe search, which will list the commits that modified
        # a given string in a file. This can be practical and quite faster
        # alternative to 'blame', but still potentially CPU-intensive.
+       # Note that you need to have 'search' feature enabled too.
 
        # To enable system wide have in $GITWEB_CONFIG
        # $feature{'pickaxe'}{'default'} = [1];
@@ -483,6 +493,18 @@ sub evaluate_uri {
                'override' => 0,
                'default' => [0]},
 
+       # Enable and configure ability to change common timezone for dates
+       # in gitweb output via JavaScript.  Enabled by default.
+       # Project specific override is not supported.
+       'javascript-timezone' => {
+               'override' => 0,
+               'default' => [
+                       'local',     # default timezone: 'utc', 'local', or '(-|+)HHMM' format,
+                                    # or undef to turn off this feature
+                       'gitweb_tz', # name of cookie where to store selected timezone
+                       'datetime',  # CSS class used to mark up dates for manipulation
+               ]},
+
        # Syntax highlighting support. This is based on Daniel Svensson's
        # and Sham Chukoury's work in gitweb-xmms2.git.
        # It requires the 'highlight' program present in $PATH,
@@ -623,18 +645,30 @@ sub filter_snapshot_fmts {
 # if it is true then gitweb config would be run for each request.
 our $per_request_config = 1;
 
+# read and parse gitweb config file given by its parameter.
+# returns true on success, false on recoverable error, allowing
+# to chain this subroutine, using first file that exists.
+# dies on errors during parsing config file, as it is unrecoverable.
+sub read_config_file {
+       my $filename = shift;
+       return unless defined $filename;
+       # die if there are errors parsing config file
+       if (-e $filename) {
+               do $filename;
+               die $@ if $@;
+               return 1;
+       }
+       return;
+}
+
 our ($GITWEB_CONFIG, $GITWEB_CONFIG_SYSTEM);
 sub evaluate_gitweb_config {
        our $GITWEB_CONFIG = $ENV{'GITWEB_CONFIG'} || "++GITWEB_CONFIG++";
        our $GITWEB_CONFIG_SYSTEM = $ENV{'GITWEB_CONFIG_SYSTEM'} || "++GITWEB_CONFIG_SYSTEM++";
-       # die if there are errors parsing config file
-       if (-e $GITWEB_CONFIG) {
-               do $GITWEB_CONFIG;
-               die $@ if $@;
-       } elsif (-e $GITWEB_CONFIG_SYSTEM) {
-               do $GITWEB_CONFIG_SYSTEM;
-               die $@ if $@;
-       }
+
+       # use first config file that exists
+       read_config_file($GITWEB_CONFIG) or
+       read_config_file($GITWEB_CONFIG_SYSTEM);
 }
 
 # Get loadavg of system, to compare against $maxload.
@@ -2562,20 +2596,34 @@ sub git_get_path_by_hash {
 ## ......................................................................
 ## git utility functions, directly accessing git repository
 
-sub git_get_project_description {
-       my $path = shift;
+# get the value of config variable either from file named as the variable
+# itself in the repository ($GIT_DIR/$name file), or from gitweb.$name
+# configuration variable in the repository config file.
+sub git_get_file_or_project_config {
+       my ($path, $name) = @_;
 
        $git_dir = "$projectroot/$path";
-       open my $fd, '<', "$git_dir/description"
-               or return git_get_project_config('description');
-       my $descr = <$fd>;
+       open my $fd, '<', "$git_dir/$name"
+               or return git_get_project_config($name);
+       my $conf = <$fd>;
        close $fd;
-       if (defined $descr) {
-               chomp $descr;
+       if (defined $conf) {
+               chomp $conf;
        }
-       return $descr;
+       return $conf;
+}
+
+sub git_get_project_description {
+       my $path = shift;
+       return git_get_file_or_project_config($path, 'description');
+}
+
+sub git_get_project_category {
+       my $path = shift;
+       return git_get_file_or_project_config($path, 'category');
 }
 
+
 # supported formats:
 # * $GIT_DIR/ctags/<tagname> file (in 'ctags' subdirectory)
 #   - if its contents is a number, use it as tag weight,
@@ -2598,7 +2646,7 @@ sub git_get_project_ctags {
                        close $ct;
 
                        (my $ctag = $tagfile) =~ s#.*/##;
-                       if ($val =~ /\d+/) {
+                       if ($val =~ /^\d+$/) {
                                $ctags->{$ctag} = $val;
                        } else {
                                $ctags->{$ctag} = 1;
@@ -3863,9 +3911,20 @@ sub git_footer_html {
                      qq!startBlame("!. href(action=>"blame_data", -replay=>1) .qq!",\n!.
                      qq!           "!. href() .qq!");\n!.
                      qq!</script>\n!;
-       } elsif (gitweb_check_feature('javascript-actions')) {
+       } else {
+               my ($jstimezone, $tz_cookie, $datetime_class) =
+                       gitweb_get_feature('javascript-timezone');
+
                print qq!<script type="text/javascript">\n!.
-                     qq!window.onload = fixLinks;\n!.
+                     qq!window.onload = function () {\n!;
+               if (gitweb_check_feature('javascript-actions')) {
+                       print qq!       fixLinks();\n!;
+               }
+               if ($jstimezone && $tz_cookie && $datetime_class) {
+                       print qq!       var tz_cookie = { name: '$tz_cookie', expires: 14, path: '/' };\n!. # in days
+                             qq!       onloadTZSetup('$jstimezone', tz_cookie, '$datetime_class');\n!;
+               }
+               print qq!};\n!.
                      qq!</script>\n!;
        }
 
@@ -4069,22 +4128,25 @@ sub git_print_section {
        print $cgi->end_div;
 }
 
-sub print_local_time {
-       print format_local_time(@_);
-}
+sub format_timestamp_html {
+       my $date = shift;
+       my $strtime = $date->{'rfc2822'};
 
-sub format_local_time {
-       my $localtime = '';
-       my %date = @_;
-       if ($date{'hour_local'} < 6) {
-               $localtime .= sprintf(" (<span class=\"atnight\">%02d:%02d</span> %s)",
-                       $date{'hour_local'}, $date{'minute_local'}, $date{'tz_local'});
-       } else {
-               $localtime .= sprintf(" (%02d:%02d %s)",
-                       $date{'hour_local'}, $date{'minute_local'}, $date{'tz_local'});
+       my (undef, undef, $datetime_class) =
+               gitweb_get_feature('javascript-timezone');
+       if ($datetime_class) {
+               $strtime = qq!<span class="$datetime_class">$strtime</span>!;
+       }
+
+       my $localtime_format = '(%02d:%02d %s)';
+       if ($date->{'hour_local'} < 6) {
+               $localtime_format = '(<span class="atnight">%02d:%02d</span> %s)';
        }
+       $strtime .= ' ' .
+                   sprintf($localtime_format,
+                           $date->{'hour_local'}, $date->{'minute_local'}, $date->{'tz_local'});
 
-       return $localtime;
+       return $strtime;
 }
 
 # Outputs the author name and date in long form
@@ -4097,10 +4159,9 @@ sub git_print_authorship {
        my %ad = parse_date($co->{'author_epoch'}, $co->{'author_tz'});
        print "<$tag class=\"author_date\">" .
              format_search_author($author, "author", esc_html($author)) .
-             " [$ad{'rfc2822'}";
-       print_local_time(%ad) if ($opts{-localtime});
-       print "]" . git_get_avatar($co->{'author_email'}, -pad_before => 1)
-                 . "</$tag>\n";
+             " [".format_timestamp_html(\%ad)."]".
+             git_get_avatar($co->{'author_email'}, -pad_before => 1) .
+             "</$tag>\n";
 }
 
 # Outputs table rows containing the full author or committer information,
@@ -4117,16 +4178,16 @@ sub git_print_authorship_rows {
                my %wd = parse_date($co->{"${who}_epoch"}, $co->{"${who}_tz"});
                print "<tr><td>$who</td><td>" .
                      format_search_author($co->{"${who}_name"}, $who,
-                              esc_html($co->{"${who}_name"})) . " " .
+                                          esc_html($co->{"${who}_name"})) . " " .
                      format_search_author($co->{"${who}_email"}, $who,
-                              esc_html("<" . $co->{"${who}_email"} . ">")) .
+                                          esc_html("<" . $co->{"${who}_email"} . ">")) .
                      "</td><td rowspan=\"2\">" .
                      git_get_avatar($co->{"${who}_email"}, -size => 'double') .
                      "</td></tr>\n" .
                      "<tr>" .
-                     "<td></td><td> $wd{'rfc2822'}";
-               print_local_time(%wd);
-               print "</td>" .
+                     "<td></td><td>" .
+                     format_timestamp_html(\%wd) .
+                     "</td>" .
                      "</tr>\n";
        }
 }
@@ -4869,8 +4930,9 @@ sub git_patchset_body {
 
 # . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
 
-# fills project list info (age, description, owner, forks) for each
-# project in the list, removing invalid projects from returned list
+# fills project list info (age, description, owner, category, forks)
+# for each project in the list, removing invalid projects from
+# returned list
 # NOTE: modifies $projlist, but does not remove entries from it
 sub fill_project_list_info {
        my $projlist = shift;
@@ -4896,6 +4958,12 @@ sub fill_project_list_info {
                if ($show_ctags) {
                        $pr->{'ctags'} = git_get_project_ctags($pr->{'path'});
                }
+               if ($projects_list_group_categories && !defined $pr->{'category'}) {
+                       my $cat = git_get_project_category($pr->{'path'}) ||
+                                                          $project_list_default_category;
+                       $pr->{'category'} = to_utf8($cat);
+               }
+
                push @projects, $pr;
        }
 
@@ -4923,6 +4991,23 @@ sub sort_projects_list {
        return @projects;
 }
 
+# returns a hash of categories, containing the list of project
+# belonging to each category
+sub build_projlist_by_category {
+       my ($projlist, $from, $to) = @_;
+       my %categories;
+
+       $from = 0 unless defined $from;
+       $to = $#$projlist if (!defined $to || $#$projlist < $to);
+
+       for (my $i = $from; $i <= $to; $i++) {
+               my $pr = $projlist->[$i];
+               push @{$categories{ $pr->{'category'} }}, $pr;
+       }
+
+       return wantarray ? %categories : \%categories;
+}
+
 # print 'sort by' <th> element, generating 'sort by $name' replay link
 # if that order is not selected
 sub print_sort_th {
@@ -4946,6 +5031,55 @@ sub format_sort_th {
        return $sort_th;
 }
 
+sub git_project_list_rows {
+       my ($projlist, $from, $to, $check_forks) = @_;
+
+       $from = 0 unless defined $from;
+       $to = $#$projlist if (!defined $to || $#$projlist < $to);
+
+       my $alternate = 1;
+       for (my $i = $from; $i <= $to; $i++) {
+               my $pr = $projlist->[$i];
+
+               if ($alternate) {
+                       print "<tr class=\"dark\">\n";
+               } else {
+                       print "<tr class=\"light\">\n";
+               }
+               $alternate ^= 1;
+
+               if ($check_forks) {
+                       print "<td>";
+                       if ($pr->{'forks'}) {
+                               my $nforks = scalar @{$pr->{'forks'}};
+                               if ($nforks > 0) {
+                                       print $cgi->a({-href => href(project=>$pr->{'path'}, action=>"forks"),
+                                                      -title => "$nforks forks"}, "+");
+                               } else {
+                                       print $cgi->span({-title => "$nforks forks"}, "+");
+                               }
+                       }
+                       print "</td>\n";
+               }
+               print "<td>" . $cgi->a({-href => href(project=>$pr->{'path'}, action=>"summary"),
+                                       -class => "list"}, esc_html($pr->{'path'})) . "</td>\n" .
+                     "<td>" . $cgi->a({-href => href(project=>$pr->{'path'}, action=>"summary"),
+                                       -class => "list", -title => $pr->{'descr_long'}},
+                                       esc_html($pr->{'descr'})) . "</td>\n" .
+                     "<td><i>" . chop_and_escape_str($pr->{'owner'}, 15) . "</i></td>\n";
+               print "<td class=\"". age_class($pr->{'age'}) . "\">" .
+                     (defined $pr->{'age_string'} ? $pr->{'age_string'} : "No commits") . "</td>\n" .
+                     "<td class=\"link\">" .
+                     $cgi->a({-href => href(project=>$pr->{'path'}, action=>"summary")}, "summary")   . " | " .
+                     $cgi->a({-href => href(project=>$pr->{'path'}, action=>"shortlog")}, "shortlog") . " | " .
+                     $cgi->a({-href => href(project=>$pr->{'path'}, action=>"log")}, "log") . " | " .
+                     $cgi->a({-href => href(project=>$pr->{'path'}, action=>"tree")}, "tree") .
+                     ($pr->{'forks'} ? " | " . $cgi->a({-href => href(project=>$pr->{'path'}, action=>"forks")}, "forks") : '') .
+                     "</td>\n" .
+                     "</tr>\n";
+       }
+}
+
 sub git_project_list_body {
        # actually uses global variable $project
        my ($projlist, $order, $from, $to, $extra, $no_header) = @_;
@@ -5001,47 +5135,27 @@ sub git_project_list_body {
                print "<th></th>\n" . # for links
                      "</tr>\n";
        }
-       my $alternate = 1;
-       for (my $i = $from; $i <= $to; $i++) {
-               my $pr = $projects[$i];
 
-               if ($alternate) {
-                       print "<tr class=\"dark\">\n";
-               } else {
-                       print "<tr class=\"light\">\n";
-               }
-               $alternate ^= 1;
-
-               if ($check_forks) {
-                       print "<td>";
-                       if ($pr->{'forks'}) {
-                               my $nforks = scalar @{$pr->{'forks'}};
-                               if ($nforks > 0) {
-                                       print $cgi->a({-href => href(project=>$pr->{'path'}, action=>"forks"),
-                                                      -title => "$nforks forks"}, "+");
-                               } else {
-                                       print $cgi->span({-title => "$nforks forks"}, "+");
+       if ($projects_list_group_categories) {
+               # only display categories with projects in the $from-$to window
+               @projects = sort {$a->{'category'} cmp $b->{'category'}} @projects[$from..$to];
+               my %categories = build_projlist_by_category(\@projects, $from, $to);
+               foreach my $cat (sort keys %categories) {
+                       unless ($cat eq "") {
+                               print "<tr>\n";
+                               if ($check_forks) {
+                                       print "<td></td>\n";
                                }
+                               print "<td class=\"category\" colspan=\"5\">".esc_html($cat)."</td>\n";
+                               print "</tr>\n";
                        }
-                       print "</td>\n";
+
+                       git_project_list_rows($categories{$cat}, undef, undef, $check_forks);
                }
-               print "<td>" . $cgi->a({-href => href(project=>$pr->{'path'}, action=>"summary"),
-                                       -class => "list"}, esc_html($pr->{'path'})) . "</td>\n" .
-                     "<td>" . $cgi->a({-href => href(project=>$pr->{'path'}, action=>"summary"),
-                                       -class => "list", -title => $pr->{'descr_long'}},
-                                       esc_html($pr->{'descr'})) . "</td>\n" .
-                     "<td><i>" . chop_and_escape_str($pr->{'owner'}, 15) . "</i></td>\n";
-               print "<td class=\"". age_class($pr->{'age'}) . "\">" .
-                     (defined $pr->{'age_string'} ? $pr->{'age_string'} : "No commits") . "</td>\n" .
-                     "<td class=\"link\">" .
-                     $cgi->a({-href => href(project=>$pr->{'path'}, action=>"summary")}, "summary")   . " | " .
-                     $cgi->a({-href => href(project=>$pr->{'path'}, action=>"shortlog")}, "shortlog") . " | " .
-                     $cgi->a({-href => href(project=>$pr->{'path'}, action=>"log")}, "log") . " | " .
-                     $cgi->a({-href => href(project=>$pr->{'path'}, action=>"tree")}, "tree") .
-                     ($pr->{'forks'} ? " | " . $cgi->a({-href => href(project=>$pr->{'path'}, action=>"forks")}, "forks") : '') .
-                     "</td>\n" .
-                     "</tr>\n";
+       } else {
+               git_project_list_rows(\@projects, $from, $to, $check_forks);
        }
+
        if (defined $extra) {
                print "<tr>\n";
                if ($check_forks) {
@@ -5561,7 +5675,8 @@ sub git_summary {
              "<tr id=\"metadata_desc\"><td>description</td><td>" . esc_html($descr) . "</td></tr>\n" .
              "<tr id=\"metadata_owner\"><td>owner</td><td>" . esc_html($owner) . "</td></tr>\n";
        if (defined $cd{'rfc2822'}) {
-               print "<tr id=\"metadata_lchange\"><td>last change</td><td>$cd{'rfc2822'}</td></tr>\n";
+               print "<tr id=\"metadata_lchange\"><td>last change</td>" .
+                     "<td>".format_timestamp_html(\%cd)."</td></tr>\n";
        }
 
        # use per project git URL list in $projectroot/$project/cloneurl
@@ -6014,7 +6129,7 @@ sub git_blob_plain {
        # want to be sure not to break that by serving the image as an
        # attachment (though Firefox 3 doesn't seem to care).
        my $sandbox = $prevent_xss &&
-               $type !~ m!^(?:text/plain|image/(?:gif|png|jpeg))$!;
+               $type !~ m!^(?:text/plain|image/(?:gif|png|jpeg))(?:[ ;]|$)!;
 
        print $cgi->header(
                -type => $type,
index 79d7eebba797f3bd80a639f6ca0835e15016762d..7d88509208417e4b1222629002ea339ecc32526e 100644 (file)
@@ -295,6 +295,13 @@ td.current_head {
        text-decoration: underline;
 }
 
+td.category {
+       background-color: #d9d8d1;
+       border-top: 1px solid #000000;
+       border-left: 1px solid #000000;
+       font-weight: bold;
+}
+
 table.diff_tree span.file_status.new {
        color: #008000;
 }
@@ -579,6 +586,39 @@ div.remote {
        display: inline-block;
 }
 
+/* JavaScript-based timezone manipulation */
+
+.popup { /* timezone selection UI */
+       position: absolute;
+       /* "top: 0; right: 0;" would be better, if not for bugs in browsers */
+       top: 0; left: 0;
+       border: 1px solid;
+       padding: 2px;
+       background-color: #f0f0f0;
+       font-style: normal;
+       color: #000000;
+       cursor: auto;
+}
+
+.close-button { /* close timezone selection UI without selecting */
+       /* float doesn't work within absolutely positioned container,
+        * if width of container is not set explicitly */
+       /* float: right; */
+       position: absolute;
+       top: 0px; right: 0px;
+       border:  1px solid green;
+       margin:  1px 1px 1px 1px;
+       padding-bottom: 2px;
+       width:     12px;
+       height:    10px;
+       font-size:  9px;
+       font-weight: bold;
+       text-align: center;
+       background-color: #fff0f0;
+       cursor: pointer;
+}
+
+
 /* Style definition generated by highlight 2.4.5, http://www.andre-simon.de/ */
 
 /* Highlighting theme definition: */
diff --git a/gitweb/static/gitweb.js b/gitweb/static/gitweb.js
deleted file mode 100644 (file)
index 40ec084..0000000
+++ /dev/null
@@ -1,889 +0,0 @@
-// Copyright (C) 2007, Fredrik Kuivinen <frekui@gmail.com>
-//               2007, Petr Baudis <pasky@suse.cz>
-//          2008-2009, Jakub Narebski <jnareb@gmail.com>
-
-/**
- * @fileOverview JavaScript code for gitweb (git web interface).
- * @license GPLv2 or later
- */
-
-/* ============================================================ */
-/* functions for generic gitweb actions and views */
-
-/**
- * used to check if link has 'js' query parameter already (at end),
- * and other reasons to not add 'js=1' param at the end of link
- * @constant
- */
-var jsExceptionsRe = /[;?]js=[01]$/;
-
-/**
- * Add '?js=1' or ';js=1' to the end of every link in the document
- * that doesn't have 'js' query parameter set already.
- *
- * Links with 'js=1' lead to JavaScript version of given action, if it
- * exists (currently there is only 'blame_incremental' for 'blame')
- *
- * @globals jsExceptionsRe
- */
-function fixLinks() {
-       var allLinks = document.getElementsByTagName("a") || document.links;
-       for (var i = 0, len = allLinks.length; i < len; i++) {
-               var link = allLinks[i];
-               if (!jsExceptionsRe.test(link)) { // =~ /[;?]js=[01]$/;
-                       link.href +=
-                               (link.href.indexOf('?') === -1 ? '?' : ';') + 'js=1';
-               }
-       }
-}
-
-
-/* ============================================================ */
-
-/*
- * This code uses DOM methods instead of (nonstandard) innerHTML
- * to modify page.
- *
- * innerHTML is non-standard IE extension, though supported by most
- * browsers; however Firefox up to version 1.5 didn't implement it in
- * a strict mode (application/xml+xhtml mimetype).
- *
- * Also my simple benchmarks show that using elem.firstChild.data =
- * 'content' is slightly faster than elem.innerHTML = 'content'.  It
- * is however more fragile (text element fragment must exists), and
- * less feature-rich (we cannot add HTML).
- *
- * Note that DOM 2 HTML is preferred over generic DOM 2 Core; the
- * equivalent using DOM 2 Core is usually shown in comments.
- */
-
-
-/* ============================================================ */
-/* generic utility functions */
-
-
-/**
- * pad number N with nonbreakable spaces on the left, to WIDTH characters
- * example: padLeftStr(12, 3, '\u00A0') == '\u00A012'
- *          ('\u00A0' is nonbreakable space)
- *
- * @param {Number|String} input: number to pad
- * @param {Number} width: visible width of output
- * @param {String} str: string to prefix to string, e.g. '\u00A0'
- * @returns {String} INPUT prefixed with (WIDTH - INPUT.length) x STR
- */
-function padLeftStr(input, width, str) {
-       var prefix = '';
-
-       width -= input.toString().length;
-       while (width > 0) {
-               prefix += str;
-               width--;
-       }
-       return prefix + input;
-}
-
-/**
- * Pad INPUT on the left to SIZE width, using given padding character CH,
- * for example padLeft('a', 3, '_') is '__a'.
- *
- * @param {String} input: input value converted to string.
- * @param {Number} width: desired length of output.
- * @param {String} ch: single character to prefix to string.
- *
- * @returns {String} Modified string, at least SIZE length.
- */
-function padLeft(input, width, ch) {
-       var s = input + "";
-       while (s.length < width) {
-               s = ch + s;
-       }
-       return s;
-}
-
-/**
- * Create XMLHttpRequest object in cross-browser way
- * @returns XMLHttpRequest object, or null
- */
-function createRequestObject() {
-       try {
-               return new XMLHttpRequest();
-       } catch (e) {}
-       try {
-               return window.createRequest();
-       } catch (e) {}
-       try {
-               return new ActiveXObject("Msxml2.XMLHTTP");
-       } catch (e) {}
-       try {
-               return new ActiveXObject("Microsoft.XMLHTTP");
-       } catch (e) {}
-
-       return null;
-}
-
-
-/* ============================================================ */
-/* 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.
-var commits = {};
-
-/**
- * constructor for Commit objects, used in 'blame'
- * @class Represents a blamed commit
- * @param {String} sha1: SHA-1 identifier of a commit
- */
-function Commit(sha1) {
-       if (this instanceof Commit) {
-               this.sha1 = sha1;
-               this.nprevious = 0; /* number of 'previous', effective parents */
-       } else {
-               return new Commit(sha1);
-       }
-}
-
-/* ............................................................ */
-/* progress info, timing, error reporting */
-
-var blamedLines = 0;
-var totalLines  = '???';
-var div_progress_bar;
-var div_progress_info;
-
-/**
- * Detects how many lines does a blamed file have,
- * This information is used in progress info
- *
- * @returns {Number|String} Number of lines in file, or string '...'
- */
-function countLines() {
-       var table =
-               document.getElementById('blame_table') ||
-               document.getElementsByTagName('table')[0];
-
-       if (table) {
-               return table.getElementsByTagName('tr').length - 1; // for header
-       } else {
-               return '...';
-       }
-}
-
-/**
- * update progress info and length (width) of progress bar
- *
- * @globals div_progress_info, div_progress_bar, blamedLines, totalLines
- */
-function updateProgressInfo() {
-       if (!div_progress_info) {
-               div_progress_info = document.getElementById('progress_info');
-       }
-       if (!div_progress_bar) {
-               div_progress_bar = document.getElementById('progress_bar');
-       }
-       if (!div_progress_info && !div_progress_bar) {
-               return;
-       }
-
-       var percentage = Math.floor(100.0*blamedLines/totalLines);
-
-       if (div_progress_info) {
-               div_progress_info.firstChild.data  = blamedLines + ' / ' + totalLines +
-                       ' (' + padLeftStr(percentage, 3, '\u00A0') + '%)';
-       }
-
-       if (div_progress_bar) {
-               //div_progress_bar.setAttribute('style', 'width: '+percentage+'%;');
-               div_progress_bar.style.width = percentage + '%';
-       }
-}
-
-
-var t_interval_server = '';
-var cmds_server = '';
-var t0 = new Date();
-
-/**
- * write how much it took to generate data, and to run script
- *
- * @globals t0, t_interval_server, cmds_server
- */
-function writeTimeInterval() {
-       var info_time = document.getElementById('generating_time');
-       if (!info_time || !t_interval_server) {
-               return;
-       }
-       var t1 = new Date();
-       info_time.firstChild.data += ' + (' +
-               t_interval_server + ' sec server blame_data / ' +
-               (t1.getTime() - t0.getTime())/1000 + ' sec client JavaScript)';
-
-       var info_cmds = document.getElementById('generating_cmd');
-       if (!info_time || !cmds_server) {
-               return;
-       }
-       info_cmds.firstChild.data += ' + ' + cmds_server;
-}
-
-/**
- * show an error message alert to user within page (in prohress info area)
- * @param {String} str: plain text error message (no HTML)
- *
- * @globals div_progress_info
- */
-function errorInfo(str) {
-       if (!div_progress_info) {
-               div_progress_info = document.getElementById('progress_info');
-       }
-       if (div_progress_info) {
-               div_progress_info.className = 'error';
-               div_progress_info.firstChild.data = str;
-       }
-}
-
-/* ............................................................ */
-/* coloring rows during blame_data (git blame --incremental) run */
-
-/**
- * used to extract N from 'colorN', where N is a number,
- * @constant
- */
-var colorRe = /\bcolor([0-9]*)\b/;
-
-/**
- * return N if <tr class="colorN">, otherwise return null
- * (some browsers require CSS class names to begin with letter)
- *
- * @param {HTMLElement} tr: table row element to check
- * @param {String} tr.className: 'class' attribute of tr element
- * @returns {Number|null} N if tr.className == 'colorN', otherwise null
- *
- * @globals colorRe
- */
-function getColorNo(tr) {
-       if (!tr) {
-               return null;
-       }
-       var className = tr.className;
-       if (className) {
-               var match = colorRe.exec(className);
-               if (match) {
-                       return parseInt(match[1], 10);
-               }
-       }
-       return null;
-}
-
-var colorsFreq = [0, 0, 0];
-/**
- * return one of given possible colors (curently least used one)
- * example: chooseColorNoFrom(2, 3) returns 2 or 3
- *
- * @param {Number[]} arguments: one or more numbers
- *        assumes that  1 <= arguments[i] <= colorsFreq.length
- * @returns {Number} Least used color number from arguments
- * @globals colorsFreq
- */
-function chooseColorNoFrom() {
-       // choose the color which is least used
-       var colorNo = arguments[0];
-       for (var i = 1; i < arguments.length; i++) {
-               if (colorsFreq[arguments[i]-1] < colorsFreq[colorNo-1]) {
-                       colorNo = arguments[i];
-               }
-       }
-       colorsFreq[colorNo-1]++;
-       return colorNo;
-}
-
-/**
- * given two neigbour <tr> elements, find color which would be different
- * from color of both of neighbours; used to 3-color blame table
- *
- * @param {HTMLElement} tr_prev
- * @param {HTMLElement} tr_next
- * @returns {Number} color number N such that
- * colorN != tr_prev.className && colorN != tr_next.className
- */
-function findColorNo(tr_prev, tr_next) {
-       var color_prev = getColorNo(tr_prev);
-       var color_next = getColorNo(tr_next);
-
-
-       // neither of neighbours has color set
-       // THEN we can use any of 3 possible colors
-       if (!color_prev && !color_next) {
-               return chooseColorNoFrom(1,2,3);
-       }
-
-       // either both neighbours have the same color,
-       // or only one of neighbours have color set
-       // THEN we can use any color except given
-       var color;
-       if (color_prev === color_next) {
-               color = color_prev; // = color_next;
-       } else if (!color_prev) {
-               color = color_next;
-       } else if (!color_next) {
-               color = color_prev;
-       }
-       if (color) {
-               return chooseColorNoFrom((color % 3) + 1, ((color+1) % 3) + 1);
-       }
-
-       // neighbours have different colors
-       // THEN there is only one color left
-       return (3 - ((color_prev + color_next) % 3));
-}
-
-/* ............................................................ */
-/* coloring rows like 'blame' after 'blame_data' finishes */
-
-/**
- * returns true if given row element (tr) is first in commit group
- * to be used only after 'blame_data' finishes (after processing)
- *
- * @param {HTMLElement} tr: table row
- * @returns {Boolean} true if TR is first in commit group
- */
-function isStartOfGroup(tr) {
-       return tr.firstChild.className === 'sha1';
-}
-
-/**
- * change colors to use zebra coloring (2 colors) instead of 3 colors
- * concatenate neighbour commit groups belonging to the same commit
- *
- * @globals colorRe
- */
-function fixColorsAndGroups() {
-       var colorClasses = ['light', 'dark'];
-       var linenum = 1;
-       var tr, prev_group;
-       var colorClass = 0;
-       var table =
-               document.getElementById('blame_table') ||
-               document.getElementsByTagName('table')[0];
-
-       while ((tr = document.getElementById('l'+linenum))) {
-       // index origin is 0, which is table header; start from 1
-       //while ((tr = table.rows[linenum])) { // <- it is slower
-               if (isStartOfGroup(tr, linenum, document)) {
-                       if (prev_group &&
-                           prev_group.firstChild.firstChild.href ===
-                                   tr.firstChild.firstChild.href) {
-                               // we have to concatenate groups
-                               var prev_rows = prev_group.firstChild.rowSpan || 1;
-                               var curr_rows =         tr.firstChild.rowSpan || 1;
-                               prev_group.firstChild.rowSpan = prev_rows + curr_rows;
-                               //tr.removeChild(tr.firstChild);
-                               tr.deleteCell(0); // DOM2 HTML way
-                       } else {
-                               colorClass = (colorClass + 1) % 2;
-                               prev_group = tr;
-                       }
-               }
-               var tr_class = tr.className;
-               tr.className = tr_class.replace(colorRe, colorClasses[colorClass]);
-               linenum++;
-       }
-}
-
-/* ............................................................ */
-/* time and data */
-
-/**
- * used to extract hours and minutes from timezone info, e.g '-0900'
- * @constant
- */
-var tzRe = /^([+-])([0-9][0-9])([0-9][0-9])$/;
-
-/**
- * convert numeric timezone +/-ZZZZ to offset from UTC in seconds
- *
- * @param {String} timezoneInfo: numeric timezone '(+|-)HHMM'
- * @returns {Number} offset from UTC in seconds for timezone
- *
- * @globals tzRe
- */
-function timezoneOffset(timezoneInfo) {
-       var match = tzRe.exec(timezoneInfo);
-       var tz_sign = (match[1] === '-' ? -1 : +1);
-       var tz_hour = parseInt(match[2],10);
-       var tz_min  = parseInt(match[3],10);
-
-       return tz_sign*(((tz_hour*60) + tz_min)*60);
-}
-
-/**
- * return date in local time formatted in iso-8601 like format
- * 'yyyy-mm-dd HH:MM:SS +/-ZZZZ' e.g. '2005-08-07 21:49:46 +0200'
- *
- * @param {Number} epoch: seconds since '00:00:00 1970-01-01 UTC'
- * @param {String} timezoneInfo: numeric timezone '(+|-)HHMM'
- * @returns {String} date in local time in iso-8601 like format
- */
-function formatDateISOLocal(epoch, timezoneInfo) {
-       // date corrected by timezone
-       var localDate = new Date(1000 * (epoch +
-               timezoneOffset(timezoneInfo)));
-       var localDateStr = // e.g. '2005-08-07'
-               localDate.getUTCFullYear()                 + '-' +
-               padLeft(localDate.getUTCMonth()+1, 2, '0') + '-' +
-               padLeft(localDate.getUTCDate(),    2, '0');
-       var localTimeStr = // e.g. '21:49:46'
-               padLeft(localDate.getUTCHours(),   2, '0') + ':' +
-               padLeft(localDate.getUTCMinutes(), 2, '0') + ':' +
-               padLeft(localDate.getUTCSeconds(), 2, '0');
-
-       return localDateStr + ' ' + localTimeStr + ' ' + timezoneInfo;
-}
-
-/* ............................................................ */
-/* unquoting/unescaping filenames */
-
-/**#@+
- * @constant
- */
-var escCodeRe = /\\([^0-7]|[0-7]{1,3})/g;
-var octEscRe = /^[0-7]{1,3}$/;
-var maybeQuotedRe = /^\"(.*)\"$/;
-/**#@-*/
-
-/**
- * unquote maybe git-quoted filename
- * e.g. 'aa' -> 'aa', '"a\ta"' -> 'a   a'
- *
- * @param {String} str: git-quoted string
- * @returns {String} Unquoted and unescaped string
- *
- * @globals escCodeRe, octEscRe, maybeQuotedRe
- */
-function unquote(str) {
-       function unq(seq) {
-               var es = {
-                       // character escape codes, aka escape sequences (from C)
-                       // replacements are to some extent JavaScript specific
-                       t: "\t",   // tab            (HT, TAB)
-                       n: "\n",   // newline        (NL)
-                       r: "\r",   // return         (CR)
-                       f: "\f",   // form feed      (FF)
-                       b: "\b",   // backspace      (BS)
-                       a: "\x07", // alarm (bell)   (BEL)
-                       e: "\x1B", // escape         (ESC)
-                       v: "\v"    // vertical tab   (VT)
-               };
-
-               if (seq.search(octEscRe) !== -1) {
-                       // octal char sequence
-                       return String.fromCharCode(parseInt(seq, 8));
-               } else if (seq in es) {
-                       // C escape sequence, aka character escape code
-                       return es[seq];
-               }
-               // quoted ordinary character
-               return seq;
-       }
-
-       var match = str.match(maybeQuotedRe);
-       if (match) {
-               str = match[1];
-               // perhaps str = eval('"'+str+'"'); would be enough?
-               str = str.replace(escCodeRe,
-                       function (substr, p1, offset, s) { return unq(p1); });
-       }
-       return str;
-}
-
-/* ============================================================ */
-/* main part: parsing response */
-
-/**
- * Function called for each blame entry, as soon as it finishes.
- * It updates page via DOM manipulation, adding sha1 info, etc.
- *
- * @param {Commit} commit: blamed commit
- * @param {Object} group: object representing group of lines,
- *                        which blame the same commit (blame entry)
- *
- * @globals blamedLines
- */
-function handleLine(commit, group) {
-       /*
-          This is the structure of the HTML fragment we are working
-          with:
-
-          <tr id="l123" class="">
-            <td class="sha1" title=""><a href=""> </a></td>
-            <td class="linenr"><a class="linenr" href="">123</a></td>
-            <td class="pre"># times (my ext3 doesn&#39;t).</td>
-          </tr>
-       */
-
-       var resline = group.resline;
-
-       // format date and time string only once per commit
-       if (!commit.info) {
-               /* e.g. 'Kay Sievers, 2005-08-07 21:49:46 +0200' */
-               commit.info = commit.author + ', ' +
-                       formatDateISOLocal(commit.authorTime, commit.authorTimezone);
-       }
-
-       // color depends on group of lines, not only on blamed commit
-       var colorNo = findColorNo(
-               document.getElementById('l'+(resline-1)),
-               document.getElementById('l'+(resline+group.numlines))
-       );
-
-       // loop over lines in commit group
-       for (var i = 0; i < group.numlines; i++, resline++) {
-               var tr = document.getElementById('l'+resline);
-               if (!tr) {
-                       break;
-               }
-               /*
-                       <tr id="l123" class="">
-                         <td class="sha1" title=""><a href=""> </a></td>
-                         <td class="linenr"><a class="linenr" href="">123</a></td>
-                         <td class="pre"># times (my ext3 doesn&#39;t).</td>
-                       </tr>
-               */
-               var td_sha1  = tr.firstChild;
-               var a_sha1   = td_sha1.firstChild;
-               var a_linenr = td_sha1.nextSibling.firstChild;
-
-               /* <tr id="l123" class=""> */
-               var tr_class = '';
-               if (colorNo !== null) {
-                       tr_class = 'color'+colorNo;
-               }
-               if (commit.boundary) {
-                       tr_class += ' boundary';
-               }
-               if (commit.nprevious === 0) {
-                       tr_class += ' no-previous';
-               } else if (commit.nprevious > 1) {
-                       tr_class += ' multiple-previous';
-               }
-               tr.className = tr_class;
-
-               /* <td class="sha1" title="?" rowspan="?"><a href="?">?</a></td> */
-               if (i === 0) {
-                       td_sha1.title = commit.info;
-                       td_sha1.rowSpan = group.numlines;
-
-                       a_sha1.href = projectUrl + 'a=commit;h=' + commit.sha1;
-                       if (a_sha1.firstChild) {
-                               a_sha1.firstChild.data = commit.sha1.substr(0, 8);
-                       } else {
-                               a_sha1.appendChild(
-                                       document.createTextNode(commit.sha1.substr(0, 8)));
-                       }
-                       if (group.numlines >= 2) {
-                               var fragment = document.createDocumentFragment();
-                               var br   = document.createElement("br");
-                               var match = commit.author.match(/\b([A-Z])\B/g);
-                               if (match) {
-                                       var text = document.createTextNode(
-                                                       match.join(''));
-                               }
-                               if (br && text) {
-                                       var elem = fragment || td_sha1;
-                                       elem.appendChild(br);
-                                       elem.appendChild(text);
-                                       if (fragment) {
-                                               td_sha1.appendChild(fragment);
-                                       }
-                               }
-                       }
-               } else {
-                       //tr.removeChild(td_sha1); // DOM2 Core way
-                       tr.deleteCell(0); // DOM2 HTML way
-               }
-
-               /* <td class="linenr"><a class="linenr" href="?">123</a></td> */
-               var linenr_commit =
-                       ('previous' in commit ? commit.previous : commit.sha1);
-               var linenr_filename =
-                       ('file_parent' in commit ? commit.file_parent : commit.filename);
-               a_linenr.href = projectUrl + 'a=blame_incremental' +
-                       ';hb=' + linenr_commit +
-                       ';f='  + encodeURIComponent(linenr_filename) +
-                       '#l' + (group.srcline + i);
-
-               blamedLines++;
-
-               //updateProgressInfo();
-       }
-}
-
-// ----------------------------------------------------------------------
-
-var inProgress = false;   // are we processing response
-
-/**#@+
- * @constant
- */
-var sha1Re = /^([0-9a-f]{40}) ([0-9]+) ([0-9]+) ([0-9]+)/;
-var infoRe = /^([a-z-]+) ?(.*)/;
-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
- * (which updates page) as soon as blame entry is completed.
- *
- * @param {String[]} lines: new complete lines from blamedata server
- *
- * @globals commits, curCommit, curGroup, t_interval_server, cmds_server
- * @globals sha1Re, infoRe, endRe
- */
-function processBlameLines(lines) {
-       var match;
-
-       for (var i = 0, len = lines.length; i < len; i++) {
-
-               if ((match = sha1Re.exec(lines[i]))) {
-                       var sha1 = match[1];
-                       var srcline  = parseInt(match[2], 10);
-                       var resline  = parseInt(match[3], 10);
-                       var numlines = parseInt(match[4], 10);
-
-                       var c = commits[sha1];
-                       if (!c) {
-                               c = new Commit(sha1);
-                               commits[sha1] = c;
-                       }
-                       curCommit = c;
-
-                       curGroup.srcline = srcline;
-                       curGroup.resline = resline;
-                       curGroup.numlines = numlines;
-
-               } else if ((match = infoRe.exec(lines[i]))) {
-                       var info = match[1];
-                       var data = match[2];
-                       switch (info) {
-                       case 'filename':
-                               curCommit.filename = unquote(data);
-                               // 'filename' information terminates the entry
-                               handleLine(curCommit, curGroup);
-                               updateProgressInfo();
-                               break;
-                       case 'author':
-                               curCommit.author = data;
-                               break;
-                       case 'author-time':
-                               curCommit.authorTime = parseInt(data, 10);
-                               break;
-                       case 'author-tz':
-                               curCommit.authorTimezone = data;
-                               break;
-                       case 'previous':
-                               curCommit.nprevious++;
-                               // store only first 'previous' header
-                               if (!'previous' in curCommit) {
-                                       var parts = data.split(' ', 2);
-                                       curCommit.previous    = parts[0];
-                                       curCommit.file_parent = unquote(parts[1]);
-                               }
-                               break;
-                       case 'boundary':
-                               curCommit.boundary = true;
-                               break;
-                       } // end switch
-
-               } else if ((match = endRe.exec(lines[i]))) {
-                       t_interval_server = match[1];
-                       cmds_server = match[2];
-
-               } else if (lines[i] !== '') {
-                       // malformed line
-
-               } // end if (match)
-
-       } // end for (lines)
-}
-
-/**
- * Process new data and return pointer to end of processed part
- *
- * @param {String} unprocessed: new data (from nextReadPos)
- * @param {Number} nextReadPos: end of last processed data
- * @return {Number} end of processed data (new value for nextReadPos)
- */
-function processData(unprocessed, nextReadPos) {
-       var lastLineEnd = unprocessed.lastIndexOf('\n');
-       if (lastLineEnd !== -1) {
-               var lines = unprocessed.substring(0, lastLineEnd).split('\n');
-               nextReadPos += lastLineEnd + 1 /* 1 == '\n'.length */;
-
-               processBlameLines(lines);
-       } // end if
-
-       return nextReadPos;
-}
-
-/**
- * Handle XMLHttpRequest errors
- *
- * @param {XMLHttpRequest} xhr: XMLHttpRequest object
- *
- * @globals pollTimer, commits, inProgress
- */
-function handleError(xhr) {
-       errorInfo('Server error: ' +
-               xhr.status + ' - ' + (xhr.statusText || 'Error contacting server'));
-
-       clearInterval(pollTimer);
-       commits = {}; // free memory
-
-       inProgress = false;
-}
-
-/**
- * Called after XMLHttpRequest finishes (loads)
- *
- * @param {XMLHttpRequest} xhr: XMLHttpRequest object (unused)
- *
- * @globals pollTimer, commits, inProgress
- */
-function responseLoaded(xhr) {
-       clearInterval(pollTimer);
-
-       fixColorsAndGroups();
-       writeTimeInterval();
-       commits = {}; // free memory
-
-       inProgress = false;
-}
-
-/**
- * handler for XMLHttpRequest onreadystatechange event
- * @see startBlame
- *
- * @globals xhr, inProgress
- */
-function handleResponse() {
-
-       /*
-        * xhr.readyState
-        *
-        *  Value  Constant (W3C)    Description
-        *  -------------------------------------------------------------------
-        *  0      UNSENT            open() has not been called yet.
-        *  1      OPENED            send() has not been called yet.
-        *  2      HEADERS_RECEIVED  send() has been called, and headers
-        *                           and status are available.
-        *  3      LOADING           Downloading; responseText holds partial data.
-        *  4      DONE              The operation is complete.
-        */
-
-       if (xhr.readyState !== 4 && xhr.readyState !== 3) {
-               return;
-       }
-
-       // the server returned error
-       // try ... catch block is to work around bug in IE8
-       try {
-               if (xhr.readyState === 3 && xhr.status !== 200) {
-                       return;
-               }
-       } catch (e) {
-               return;
-       }
-       if (xhr.readyState === 4 && xhr.status !== 200) {
-               handleError(xhr);
-               return;
-       }
-
-       // In konqueror xhr.responseText is sometimes null here...
-       if (xhr.responseText === null) {
-               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;
-               }
-
-               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) {
-               responseLoaded(xhr);
-       }
-
-       inProgress = false;
-}
-
-// ============================================================
-// ------------------------------------------------------------
-
-/**
- * Incrementally update line data in blame_incremental view in gitweb.
- *
- * @param {String} blamedataUrl: URL to server script generating blame data.
- * @param {String} bUrl: partial URL to project, used to generate links.
- *
- * 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
-*/
-function startBlame(blamedataUrl, bUrl) {
-
-       xhr = createRequestObject();
-       if (!xhr) {
-               errorInfo('ERROR: XMLHttpRequest not supported');
-               return;
-       }
-
-       t0 = new Date();
-       projectUrl = bUrl + (bUrl.indexOf('?') === -1 ? '?' : ';');
-       if ((div_progress_bar = document.getElementById('progress_bar'))) {
-               //div_progress_bar.setAttribute('style', 'width: 100%;');
-               div_progress_bar.style.cssText = 'width: 100%;';
-       }
-       totalLines = countLines();
-       updateProgressInfo();
-
-       /* add extra properties to xhr object to help processing response */
-       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.open('GET', blamedataUrl);
-       xhr.setRequestHeader('Accept', 'text/plain');
-       xhr.send(null);
-
-       // 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);
-}
-
-// end of gitweb.js
diff --git a/gitweb/static/js/README b/gitweb/static/js/README
new file mode 100644 (file)
index 0000000..f8460ed
--- /dev/null
@@ -0,0 +1,20 @@
+GIT web interface (gitweb) - JavaScript
+=======================================
+
+This directory holds JavaScript code used by gitweb (GIT web interface).
+Scripts from there would be concatenated together in the order specified
+by gitweb/Makefile into gitweb/static/gitweb.js, during building of
+gitweb/gitweb.cgi (during gitweb building).  The resulting file (or its
+minification) would then be installed / deployed together with gitweb.
+
+Scripts in 'lib/' subdirectory compose generic JavaScript library,
+providing features required by gitweb but in no way limited to gitweb
+only.  In the future those scripts could be replaced by some JavaScript
+library / framework, like e.g. jQuery, YUI, Prototype, MooTools, Dojo,
+ExtJS, Script.aculo.us or SproutCore.
+
+All scripts that manipulate gitweb output should be put outside 'lib/',
+directly in this directory ('gitweb/static/js/').  Those scripts would
+have to be rewritten if gitweb moves to using some JavaScript library.
+
+See also comments in gitweb/Makefile.
diff --git a/gitweb/static/js/adjust-timezone.js b/gitweb/static/js/adjust-timezone.js
new file mode 100644 (file)
index 0000000..0c67779
--- /dev/null
@@ -0,0 +1,330 @@
+// Copyright (C) 2011, John 'Warthog9' Hawley <warthog9@eaglescrag.net>
+//               2011, Jakub Narebski <jnareb@gmail.com>
+
+/**
+ * @fileOverview Manipulate dates in gitweb output, adjusting timezone
+ * @license GPLv2 or later
+ */
+
+/**
+ * Get common timezone, add UI for changing timezones, and adjust
+ * dates to use requested common timezone.
+ *
+ * This function is called during onload event (added to window.onload).
+ *
+ * @param {String} tzDefault: default timezone, if there is no cookie
+ * @param {Object} tzCookieInfo: object literal with info about cookie to store timezone
+ * @param {String} tzCookieInfo.name: name of cookie to store timezone
+ * @param {String} tzClassName: denotes elements with date to be adjusted
+ */
+function onloadTZSetup(tzDefault, tzCookieInfo, tzClassName) {
+       var tzCookieTZ = getCookie(tzCookieInfo.name, tzCookieInfo);
+       var tz = tzDefault;
+
+       if (tzCookieTZ) {
+               // set timezone to value saved in a cookie
+               tz = tzCookieTZ;
+               // refresh cookie, so its expiration counts from last use of gitweb
+               setCookie(tzCookieInfo.name, tzCookieTZ, tzCookieInfo);
+       }
+
+       // add UI for changing timezone
+       addChangeTZ(tz, tzCookieInfo, tzClassName);
+
+       // server-side of gitweb produces datetime in UTC,
+       // so if tz is 'utc' there is no need for changes
+       var nochange = tz === 'utc';
+
+       // adjust dates to use specified common timezone
+       fixDatetimeTZ(tz, tzClassName, nochange);
+}
+
+
+/* ...................................................................... */
+/* Changing dates to use requested timezone */
+
+/**
+ * Replace RFC-2822 dates contained in SPAN elements with tzClassName
+ * CSS class with equivalent dates in given timezone.
+ *
+ * @param {String} tz: numeric timezone in '(-|+)HHMM' format, or 'utc', or 'local'
+ * @param {String} tzClassName: specifies elements to be changed
+ * @param {Boolean} nochange: markup for timezone change, but don't change it
+ */
+function fixDatetimeTZ(tz, tzClassName, nochange) {
+       // sanity check, method should be ensured by common-lib.js
+       if (!document.getElementsByClassName) {
+               return;
+       }
+
+       // translate to timezone in '(-|+)HHMM' format
+       tz = normalizeTimezoneInfo(tz);
+
+       // NOTE: result of getElementsByClassName should probably be cached
+       var classesFound = document.getElementsByClassName(tzClassName, "span");
+       for (var i = 0, len = classesFound.length; i < len; i++) {
+               var curElement = classesFound[i];
+
+               curElement.title = 'Click to change timezone';
+               if (!nochange) {
+                       // we use *.firstChild.data (W3C DOM) instead of *.innerHTML
+                       // as the latter doesn't always work everywhere in every browser
+                       var epoch = parseRFC2822Date(curElement.firstChild.data);
+                       var adjusted = formatDateRFC2882(epoch, tz);
+
+                       curElement.firstChild.data = adjusted;
+               }
+       }
+}
+
+
+/* ...................................................................... */
+/* Adding triggers, generating timezone menu, displaying and hiding */
+
+/**
+ * Adds triggers for UI to change common timezone used for dates in
+ * gitweb output: it marks up and/or creates item to click to invoke
+ * timezone change UI, creates timezone UI fragment to be attached,
+ * and installs appropriate onclick trigger (via event delegation).
+ *
+ * @param {String} tzSelected: pre-selected timezone,
+ *                             'utc' or 'local' or '(-|+)HHMM'
+ * @param {Object} tzCookieInfo: object literal with info about cookie to store timezone
+ * @param {String} tzClassName: specifies elements to install trigger
+ */
+function addChangeTZ(tzSelected, tzCookieInfo, tzClassName) {
+       // make link to timezone UI discoverable
+       addCssRule('.'+tzClassName + ':hover',
+                  'text-decoration: underline; cursor: help;');
+
+       // create form for selecting timezone (to be saved in a cookie)
+       var tzSelectFragment = document.createDocumentFragment();
+       tzSelectFragment = createChangeTZForm(tzSelectFragment,
+                                             tzSelected, tzCookieInfo, tzClassName);
+
+       // event delegation handler for timezone selection UI (clicking on entry)
+       // see http://www.nczonline.net/blog/2009/06/30/event-delegation-in-javascript/
+       // assumes that there is no existing document.onclick handler
+       document.onclick = function onclickHandler(event) {
+               //IE doesn't pass in the event object
+               event = event || window.event;
+
+               //IE uses srcElement as the target
+               var target = event.target || event.srcElement;
+
+               switch (target.className) {
+               case tzClassName:
+                       // don't display timezone menu if it is already displayed
+                       if (tzSelectFragment.childNodes.length > 0) {
+                               displayChangeTZForm(target, tzSelectFragment);
+                       }
+                       break;
+               } // end switch
+       };
+}
+
+/**
+ * Create DocumentFragment with UI for changing common timezone in
+ * which dates are shown in.
+ *
+ * @param {DocumentFragment} documentFragment: where attach UI
+ * @param {String} tzSelected: default (pre-selected) timezone
+ * @param {Object} tzCookieInfo: object literal with info about cookie to store timezone
+ * @returns {DocumentFragment}
+ */
+function createChangeTZForm(documentFragment, tzSelected, tzCookieInfo, tzClassName) {
+       var div = document.createElement("div");
+       div.className = 'popup';
+
+       /* '<div class="close-button" title="(click on this box to close)">X</div>' */
+       var closeButton = document.createElement('div');
+       closeButton.className = 'close-button';
+       closeButton.title = '(click on this box to close)';
+       closeButton.appendChild(document.createTextNode('X'));
+       closeButton.onclick = closeTZFormHandler(documentFragment, tzClassName);
+       div.appendChild(closeButton);
+
+       /* 'Select timezone: <br clear="all">' */
+       div.appendChild(document.createTextNode('Select timezone: '));
+       var br = document.createElement('br');
+       br.clear = 'all';
+       div.appendChild(br);
+
+       /* '<select name="tzoffset">
+        *    ...
+        *    <option value="-0700">UTC-07:00</option>
+        *    <option value="-0600">UTC-06:00</option>
+        *    ...
+        *  </select>' */
+       var select = document.createElement("select");
+       select.name = "tzoffset";
+       //select.style.clear = 'all';
+       select.appendChild(generateTZOptions(tzSelected));
+       select.onchange = selectTZHandler(documentFragment, tzCookieInfo, tzClassName);
+       div.appendChild(select);
+
+       documentFragment.appendChild(div);
+
+       return documentFragment;
+}
+
+
+/**
+ * Hide (remove from DOM) timezone change UI, ensuring that it is not
+ * garbage collected and that it can be re-enabled later.
+ *
+ * @param {DocumentFragment} documentFragment: contains detached UI
+ * @param {HTMLSelectElement} target: select element inside of UI
+ * @param {String} tzClassName: specifies element where UI was installed
+ * @returns {DocumentFragment} documentFragment
+ */
+function removeChangeTZForm(documentFragment, target, tzClassName) {
+       // find containing element, where we appended timezone selection UI
+       // `target' is somewhere inside timezone menu
+       var container = target.parentNode, popup = target;
+       while (container &&
+              container.className !== tzClassName) {
+               popup = container;
+               container = container.parentNode;
+       }
+       // safety check if we found correct container,
+       // and if it isn't deleted already
+       if (!container || !popup ||
+           container.className !== tzClassName ||
+           popup.className     !== 'popup') {
+               return documentFragment;
+       }
+
+       // timezone selection UI was appended as last child
+       // see also displayChangeTZForm function
+       var removed = popup.parentNode.removeChild(popup);
+       if (documentFragment.firstChild !== removed) { // the only child
+               // re-append it so it would be available for next time
+               documentFragment.appendChild(removed);
+       }
+       // all of inline style was added by this script
+       // it is not really needed to remove it, but it is a good practice
+       container.removeAttribute('style');
+
+       return documentFragment;
+}
+
+
+/**
+ * Display UI for changing common timezone for dates in gitweb output.
+ * To be used from 'onclick' event handler.
+ *
+ * @param {HTMLElement} target: where to install/display UI
+ * @param {DocumentFragment} tzSelectFragment: timezone selection UI
+ */
+function displayChangeTZForm(target, tzSelectFragment) {
+       // for absolute positioning to be related to target element
+       target.style.position = 'relative';
+       target.style.display = 'inline-block';
+
+       // show/display UI for changing timezone
+       target.appendChild(tzSelectFragment);
+}
+
+
+/* ...................................................................... */
+/* List of timezones for timezone selection menu */
+
+/**
+ * Generate list of timezones for creating timezone select UI
+ *
+ * @returns {Object[]} list of e.g. { value: '+0100', descr: 'GMT+01:00' }
+ */
+function generateTZList() {
+       var timezones = [
+               { value: "utc",   descr: "UTC/GMT"},
+               { value: "local", descr: "Local (per browser)"}
+       ];
+
+       // generate all full hour timezones (no fractional timezones)
+       for (var x = -12, idx = timezones.length; x <= +14; x++, idx++) {
+               var hours = (x >= 0 ? '+' : '-') + padLeft(x >=0 ? x : -x, 2);
+               timezones[idx] = { value: hours + '00', descr: 'UTC' + hours + ':00'};
+               if (x === 0) {
+                       timezones[idx].descr = 'UTC\u00B100:00'; // 'UTC&plusmn;00:00'
+               }
+       }
+
+       return timezones;
+}
+
+/**
+ * Generate <options> elements for timezone select UI
+ *
+ * @param {String} tzSelected: default timezone
+ * @returns {DocumentFragment} list of options elements to appendChild
+ */
+function generateTZOptions(tzSelected) {
+       var elems = document.createDocumentFragment();
+       var timezones = generateTZList();
+
+       for (var i = 0, len = timezones.length; i < len; i++) {
+               var tzone = timezones[i];
+               var option = document.createElement("option");
+               if (tzone.value === tzSelected) {
+                       option.defaultSelected = true;
+               }
+               option.value = tzone.value;
+               option.appendChild(document.createTextNode(tzone.descr));
+
+               elems.appendChild(option);
+       }
+
+       return elems;
+}
+
+
+/* ...................................................................... */
+/* Event handlers and/or their generators */
+
+/**
+ * Create event handler that select timezone and closes timezone select UI.
+ * To be used as $('select[name="tzselect"]').onchange handler.
+ *
+ * @param {DocumentFragment} tzSelectFragment: timezone selection UI
+ * @param {Object} tzCookieInfo: object literal with info about cookie to store timezone
+ * @param {String} tzCookieInfo.name: name of cookie to save result of selection
+ * @param {String} tzClassName: specifies element where UI was installed
+ * @returns {Function} event handler
+ */
+function selectTZHandler(tzSelectFragment, tzCookieInfo, tzClassName) {
+       //return function selectTZ(event) {
+       return function (event) {
+               event = event || window.event;
+               var target = event.target || event.srcElement;
+
+               var selected = target.options.item(target.selectedIndex);
+               removeChangeTZForm(tzSelectFragment, target, tzClassName);
+
+               if (selected) {
+                       selected.defaultSelected = true;
+                       setCookie(tzCookieInfo.name, selected.value, tzCookieInfo);
+                       fixDatetimeTZ(selected.value, tzClassName);
+               }
+       };
+}
+
+/**
+ * Create event handler that closes timezone select UI.
+ * To be used e.g. as $('.closebutton').onclick handler.
+ *
+ * @param {DocumentFragment} tzSelectFragment: timezone selection UI
+ * @param {String} tzClassName: specifies element where UI was installed
+ * @returns {Function} event handler
+ */
+function closeTZFormHandler(tzSelectFragment, tzClassName) {
+       //return function closeTZForm(event) {
+       return function (event) {
+               event = event || window.event;
+               var target = event.target || event.srcElement;
+
+               removeChangeTZForm(tzSelectFragment, target, tzClassName);
+       };
+}
+
+/* end of adjust-timezone.js */
diff --git a/gitweb/static/js/blame_incremental.js b/gitweb/static/js/blame_incremental.js
new file mode 100644 (file)
index 0000000..676da6b
--- /dev/null
@@ -0,0 +1,687 @@
+// Copyright (C) 2007, Fredrik Kuivinen <frekui@gmail.com>
+//               2007, Petr Baudis <pasky@suse.cz>
+//          2008-2011, Jakub Narebski <jnareb@gmail.com>
+
+/**
+ * @fileOverview JavaScript side of Ajax-y 'blame_incremental' view in gitweb
+ * @license GPLv2 or later
+ */
+
+/* ============================================================ */
+/*
+ * This code uses DOM methods instead of (nonstandard) innerHTML
+ * to modify page.
+ *
+ * innerHTML is non-standard IE extension, though supported by most
+ * browsers; however Firefox up to version 1.5 didn't implement it in
+ * a strict mode (application/xml+xhtml mimetype).
+ *
+ * Also my simple benchmarks show that using elem.firstChild.data =
+ * 'content' is slightly faster than elem.innerHTML = 'content'.  It
+ * is however more fragile (text element fragment must exists), and
+ * less feature-rich (we cannot add HTML).
+ *
+ * Note that DOM 2 HTML is preferred over generic DOM 2 Core; the
+ * equivalent using DOM 2 Core is usually shown in comments.
+ */
+
+
+/* ............................................................ */
+/* 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.
+var commits = {};
+
+/**
+ * constructor for Commit objects, used in 'blame'
+ * @class Represents a blamed commit
+ * @param {String} sha1: SHA-1 identifier of a commit
+ */
+function Commit(sha1) {
+       if (this instanceof Commit) {
+               this.sha1 = sha1;
+               this.nprevious = 0; /* number of 'previous', effective parents */
+       } else {
+               return new Commit(sha1);
+       }
+}
+
+/* ............................................................ */
+/* progress info, timing, error reporting */
+
+var blamedLines = 0;
+var totalLines  = '???';
+var div_progress_bar;
+var div_progress_info;
+
+/**
+ * Detects how many lines does a blamed file have,
+ * This information is used in progress info
+ *
+ * @returns {Number|String} Number of lines in file, or string '...'
+ */
+function countLines() {
+       var table =
+               document.getElementById('blame_table') ||
+               document.getElementsByTagName('table')[0];
+
+       if (table) {
+               return table.getElementsByTagName('tr').length - 1; // for header
+       } else {
+               return '...';
+       }
+}
+
+/**
+ * update progress info and length (width) of progress bar
+ *
+ * @globals div_progress_info, div_progress_bar, blamedLines, totalLines
+ */
+function updateProgressInfo() {
+       if (!div_progress_info) {
+               div_progress_info = document.getElementById('progress_info');
+       }
+       if (!div_progress_bar) {
+               div_progress_bar = document.getElementById('progress_bar');
+       }
+       if (!div_progress_info && !div_progress_bar) {
+               return;
+       }
+
+       var percentage = Math.floor(100.0*blamedLines/totalLines);
+
+       if (div_progress_info) {
+               div_progress_info.firstChild.data  = blamedLines + ' / ' + totalLines +
+                       ' (' + padLeftStr(percentage, 3, '\u00A0') + '%)';
+       }
+
+       if (div_progress_bar) {
+               //div_progress_bar.setAttribute('style', 'width: '+percentage+'%;');
+               div_progress_bar.style.width = percentage + '%';
+       }
+}
+
+
+var t_interval_server = '';
+var cmds_server = '';
+var t0 = new Date();
+
+/**
+ * write how much it took to generate data, and to run script
+ *
+ * @globals t0, t_interval_server, cmds_server
+ */
+function writeTimeInterval() {
+       var info_time = document.getElementById('generating_time');
+       if (!info_time || !t_interval_server) {
+               return;
+       }
+       var t1 = new Date();
+       info_time.firstChild.data += ' + (' +
+               t_interval_server + ' sec server blame_data / ' +
+               (t1.getTime() - t0.getTime())/1000 + ' sec client JavaScript)';
+
+       var info_cmds = document.getElementById('generating_cmd');
+       if (!info_time || !cmds_server) {
+               return;
+       }
+       info_cmds.firstChild.data += ' + ' + cmds_server;
+}
+
+/**
+ * show an error message alert to user within page (in progress info area)
+ * @param {String} str: plain text error message (no HTML)
+ *
+ * @globals div_progress_info
+ */
+function errorInfo(str) {
+       if (!div_progress_info) {
+               div_progress_info = document.getElementById('progress_info');
+       }
+       if (div_progress_info) {
+               div_progress_info.className = 'error';
+               div_progress_info.firstChild.data = str;
+       }
+}
+
+/* ............................................................ */
+/* coloring rows during blame_data (git blame --incremental) run */
+
+/**
+ * used to extract N from 'colorN', where N is a number,
+ * @constant
+ */
+var colorRe = /\bcolor([0-9]*)\b/;
+
+/**
+ * return N if <tr class="colorN">, otherwise return null
+ * (some browsers require CSS class names to begin with letter)
+ *
+ * @param {HTMLElement} tr: table row element to check
+ * @param {String} tr.className: 'class' attribute of tr element
+ * @returns {Number|null} N if tr.className == 'colorN', otherwise null
+ *
+ * @globals colorRe
+ */
+function getColorNo(tr) {
+       if (!tr) {
+               return null;
+       }
+       var className = tr.className;
+       if (className) {
+               var match = colorRe.exec(className);
+               if (match) {
+                       return parseInt(match[1], 10);
+               }
+       }
+       return null;
+}
+
+var colorsFreq = [0, 0, 0];
+/**
+ * return one of given possible colors (currently least used one)
+ * example: chooseColorNoFrom(2, 3) returns 2 or 3
+ *
+ * @param {Number[]} arguments: one or more numbers
+ *        assumes that  1 <= arguments[i] <= colorsFreq.length
+ * @returns {Number} Least used color number from arguments
+ * @globals colorsFreq
+ */
+function chooseColorNoFrom() {
+       // choose the color which is least used
+       var colorNo = arguments[0];
+       for (var i = 1; i < arguments.length; i++) {
+               if (colorsFreq[arguments[i]-1] < colorsFreq[colorNo-1]) {
+                       colorNo = arguments[i];
+               }
+       }
+       colorsFreq[colorNo-1]++;
+       return colorNo;
+}
+
+/**
+ * given two neighbor <tr> elements, find color which would be different
+ * from color of both of neighbors; used to 3-color blame table
+ *
+ * @param {HTMLElement} tr_prev
+ * @param {HTMLElement} tr_next
+ * @returns {Number} color number N such that
+ * colorN != tr_prev.className && colorN != tr_next.className
+ */
+function findColorNo(tr_prev, tr_next) {
+       var color_prev = getColorNo(tr_prev);
+       var color_next = getColorNo(tr_next);
+
+
+       // neither of neighbors has color set
+       // THEN we can use any of 3 possible colors
+       if (!color_prev && !color_next) {
+               return chooseColorNoFrom(1,2,3);
+       }
+
+       // either both neighbors have the same color,
+       // or only one of neighbors have color set
+       // THEN we can use any color except given
+       var color;
+       if (color_prev === color_next) {
+               color = color_prev; // = color_next;
+       } else if (!color_prev) {
+               color = color_next;
+       } else if (!color_next) {
+               color = color_prev;
+       }
+       if (color) {
+               return chooseColorNoFrom((color % 3) + 1, ((color+1) % 3) + 1);
+       }
+
+       // neighbors have different colors
+       // THEN there is only one color left
+       return (3 - ((color_prev + color_next) % 3));
+}
+
+/* ............................................................ */
+/* coloring rows like 'blame' after 'blame_data' finishes */
+
+/**
+ * returns true if given row element (tr) is first in commit group
+ * to be used only after 'blame_data' finishes (after processing)
+ *
+ * @param {HTMLElement} tr: table row
+ * @returns {Boolean} true if TR is first in commit group
+ */
+function isStartOfGroup(tr) {
+       return tr.firstChild.className === 'sha1';
+}
+
+/**
+ * change colors to use zebra coloring (2 colors) instead of 3 colors
+ * concatenate neighbor commit groups belonging to the same commit
+ *
+ * @globals colorRe
+ */
+function fixColorsAndGroups() {
+       var colorClasses = ['light', 'dark'];
+       var linenum = 1;
+       var tr, prev_group;
+       var colorClass = 0;
+       var table =
+               document.getElementById('blame_table') ||
+               document.getElementsByTagName('table')[0];
+
+       while ((tr = document.getElementById('l'+linenum))) {
+       // index origin is 0, which is table header; start from 1
+       //while ((tr = table.rows[linenum])) { // <- it is slower
+               if (isStartOfGroup(tr, linenum, document)) {
+                       if (prev_group &&
+                           prev_group.firstChild.firstChild.href ===
+                                   tr.firstChild.firstChild.href) {
+                               // we have to concatenate groups
+                               var prev_rows = prev_group.firstChild.rowSpan || 1;
+                               var curr_rows =         tr.firstChild.rowSpan || 1;
+                               prev_group.firstChild.rowSpan = prev_rows + curr_rows;
+                               //tr.removeChild(tr.firstChild);
+                               tr.deleteCell(0); // DOM2 HTML way
+                       } else {
+                               colorClass = (colorClass + 1) % 2;
+                               prev_group = tr;
+                       }
+               }
+               var tr_class = tr.className;
+               tr.className = tr_class.replace(colorRe, colorClasses[colorClass]);
+               linenum++;
+       }
+}
+
+
+/* ============================================================ */
+/* main part: parsing response */
+
+/**
+ * Function called for each blame entry, as soon as it finishes.
+ * It updates page via DOM manipulation, adding sha1 info, etc.
+ *
+ * @param {Commit} commit: blamed commit
+ * @param {Object} group: object representing group of lines,
+ *                        which blame the same commit (blame entry)
+ *
+ * @globals blamedLines
+ */
+function handleLine(commit, group) {
+       /*
+          This is the structure of the HTML fragment we are working
+          with:
+
+          <tr id="l123" class="">
+            <td class="sha1" title=""><a href=""> </a></td>
+            <td class="linenr"><a class="linenr" href="">123</a></td>
+            <td class="pre"># times (my ext3 doesn&#39;t).</td>
+          </tr>
+       */
+
+       var resline = group.resline;
+
+       // format date and time string only once per commit
+       if (!commit.info) {
+               /* e.g. 'Kay Sievers, 2005-08-07 21:49:46 +0200' */
+               commit.info = commit.author + ', ' +
+                       formatDateISOLocal(commit.authorTime, commit.authorTimezone);
+       }
+
+       // color depends on group of lines, not only on blamed commit
+       var colorNo = findColorNo(
+               document.getElementById('l'+(resline-1)),
+               document.getElementById('l'+(resline+group.numlines))
+       );
+
+       // loop over lines in commit group
+       for (var i = 0; i < group.numlines; i++, resline++) {
+               var tr = document.getElementById('l'+resline);
+               if (!tr) {
+                       break;
+               }
+               /*
+                       <tr id="l123" class="">
+                         <td class="sha1" title=""><a href=""> </a></td>
+                         <td class="linenr"><a class="linenr" href="">123</a></td>
+                         <td class="pre"># times (my ext3 doesn&#39;t).</td>
+                       </tr>
+               */
+               var td_sha1  = tr.firstChild;
+               var a_sha1   = td_sha1.firstChild;
+               var a_linenr = td_sha1.nextSibling.firstChild;
+
+               /* <tr id="l123" class=""> */
+               var tr_class = '';
+               if (colorNo !== null) {
+                       tr_class = 'color'+colorNo;
+               }
+               if (commit.boundary) {
+                       tr_class += ' boundary';
+               }
+               if (commit.nprevious === 0) {
+                       tr_class += ' no-previous';
+               } else if (commit.nprevious > 1) {
+                       tr_class += ' multiple-previous';
+               }
+               tr.className = tr_class;
+
+               /* <td class="sha1" title="?" rowspan="?"><a href="?">?</a></td> */
+               if (i === 0) {
+                       td_sha1.title = commit.info;
+                       td_sha1.rowSpan = group.numlines;
+
+                       a_sha1.href = projectUrl + 'a=commit;h=' + commit.sha1;
+                       if (a_sha1.firstChild) {
+                               a_sha1.firstChild.data = commit.sha1.substr(0, 8);
+                       } else {
+                               a_sha1.appendChild(
+                                       document.createTextNode(commit.sha1.substr(0, 8)));
+                       }
+                       if (group.numlines >= 2) {
+                               var fragment = document.createDocumentFragment();
+                               var br   = document.createElement("br");
+                               var match = commit.author.match(/\b([A-Z])\B/g);
+                               if (match) {
+                                       var text = document.createTextNode(
+                                                       match.join(''));
+                               }
+                               if (br && text) {
+                                       var elem = fragment || td_sha1;
+                                       elem.appendChild(br);
+                                       elem.appendChild(text);
+                                       if (fragment) {
+                                               td_sha1.appendChild(fragment);
+                                       }
+                               }
+                       }
+               } else {
+                       //tr.removeChild(td_sha1); // DOM2 Core way
+                       tr.deleteCell(0); // DOM2 HTML way
+               }
+
+               /* <td class="linenr"><a class="linenr" href="?">123</a></td> */
+               var linenr_commit =
+                       ('previous' in commit ? commit.previous : commit.sha1);
+               var linenr_filename =
+                       ('file_parent' in commit ? commit.file_parent : commit.filename);
+               a_linenr.href = projectUrl + 'a=blame_incremental' +
+                       ';hb=' + linenr_commit +
+                       ';f='  + encodeURIComponent(linenr_filename) +
+                       '#l' + (group.srcline + i);
+
+               blamedLines++;
+
+               //updateProgressInfo();
+       }
+}
+
+// ----------------------------------------------------------------------
+
+var inProgress = false;   // are we processing response
+
+/**#@+
+ * @constant
+ */
+var sha1Re = /^([0-9a-f]{40}) ([0-9]+) ([0-9]+) ([0-9]+)/;
+var infoRe = /^([a-z-]+) ?(.*)/;
+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
+ * (which updates page) as soon as blame entry is completed.
+ *
+ * @param {String[]} lines: new complete lines from blamedata server
+ *
+ * @globals commits, curCommit, curGroup, t_interval_server, cmds_server
+ * @globals sha1Re, infoRe, endRe
+ */
+function processBlameLines(lines) {
+       var match;
+
+       for (var i = 0, len = lines.length; i < len; i++) {
+
+               if ((match = sha1Re.exec(lines[i]))) {
+                       var sha1 = match[1];
+                       var srcline  = parseInt(match[2], 10);
+                       var resline  = parseInt(match[3], 10);
+                       var numlines = parseInt(match[4], 10);
+
+                       var c = commits[sha1];
+                       if (!c) {
+                               c = new Commit(sha1);
+                               commits[sha1] = c;
+                       }
+                       curCommit = c;
+
+                       curGroup.srcline = srcline;
+                       curGroup.resline = resline;
+                       curGroup.numlines = numlines;
+
+               } else if ((match = infoRe.exec(lines[i]))) {
+                       var info = match[1];
+                       var data = match[2];
+                       switch (info) {
+                       case 'filename':
+                               curCommit.filename = unquote(data);
+                               // 'filename' information terminates the entry
+                               handleLine(curCommit, curGroup);
+                               updateProgressInfo();
+                               break;
+                       case 'author':
+                               curCommit.author = data;
+                               break;
+                       case 'author-time':
+                               curCommit.authorTime = parseInt(data, 10);
+                               break;
+                       case 'author-tz':
+                               curCommit.authorTimezone = data;
+                               break;
+                       case 'previous':
+                               curCommit.nprevious++;
+                               // store only first 'previous' header
+                               if (!'previous' in curCommit) {
+                                       var parts = data.split(' ', 2);
+                                       curCommit.previous    = parts[0];
+                                       curCommit.file_parent = unquote(parts[1]);
+                               }
+                               break;
+                       case 'boundary':
+                               curCommit.boundary = true;
+                               break;
+                       } // end switch
+
+               } else if ((match = endRe.exec(lines[i]))) {
+                       t_interval_server = match[1];
+                       cmds_server = match[2];
+
+               } else if (lines[i] !== '') {
+                       // malformed line
+
+               } // end if (match)
+
+       } // end for (lines)
+}
+
+/**
+ * Process new data and return pointer to end of processed part
+ *
+ * @param {String} unprocessed: new data (from nextReadPos)
+ * @param {Number} nextReadPos: end of last processed data
+ * @return {Number} end of processed data (new value for nextReadPos)
+ */
+function processData(unprocessed, nextReadPos) {
+       var lastLineEnd = unprocessed.lastIndexOf('\n');
+       if (lastLineEnd !== -1) {
+               var lines = unprocessed.substring(0, lastLineEnd).split('\n');
+               nextReadPos += lastLineEnd + 1 /* 1 == '\n'.length */;
+
+               processBlameLines(lines);
+       } // end if
+
+       return nextReadPos;
+}
+
+/**
+ * Handle XMLHttpRequest errors
+ *
+ * @param {XMLHttpRequest} xhr: XMLHttpRequest object
+ *
+ * @globals pollTimer, commits, inProgress
+ */
+function handleError(xhr) {
+       errorInfo('Server error: ' +
+               xhr.status + ' - ' + (xhr.statusText || 'Error contacting server'));
+
+       clearInterval(pollTimer);
+       commits = {}; // free memory
+
+       inProgress = false;
+}
+
+/**
+ * Called after XMLHttpRequest finishes (loads)
+ *
+ * @param {XMLHttpRequest} xhr: XMLHttpRequest object (unused)
+ *
+ * @globals pollTimer, commits, inProgress
+ */
+function responseLoaded(xhr) {
+       clearInterval(pollTimer);
+
+       fixColorsAndGroups();
+       writeTimeInterval();
+       commits = {}; // free memory
+
+       inProgress = false;
+}
+
+/**
+ * handler for XMLHttpRequest onreadystatechange event
+ * @see startBlame
+ *
+ * @globals xhr, inProgress
+ */
+function handleResponse() {
+
+       /*
+        * xhr.readyState
+        *
+        *  Value  Constant (W3C)    Description
+        *  -------------------------------------------------------------------
+        *  0      UNSENT            open() has not been called yet.
+        *  1      OPENED            send() has not been called yet.
+        *  2      HEADERS_RECEIVED  send() has been called, and headers
+        *                           and status are available.
+        *  3      LOADING           Downloading; responseText holds partial data.
+        *  4      DONE              The operation is complete.
+        */
+
+       if (xhr.readyState !== 4 && xhr.readyState !== 3) {
+               return;
+       }
+
+       // the server returned error
+       // try ... catch block is to work around bug in IE8
+       try {
+               if (xhr.readyState === 3 && xhr.status !== 200) {
+                       return;
+               }
+       } catch (e) {
+               return;
+       }
+       if (xhr.readyState === 4 && xhr.status !== 200) {
+               handleError(xhr);
+               return;
+       }
+
+       // In konqueror xhr.responseText is sometimes null here...
+       if (xhr.responseText === null) {
+               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;
+               }
+
+               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) {
+               responseLoaded(xhr);
+       }
+
+       inProgress = false;
+}
+
+// ============================================================
+// ------------------------------------------------------------
+
+/**
+ * Incrementally update line data in blame_incremental view in gitweb.
+ *
+ * @param {String} blamedataUrl: URL to server script generating blame data.
+ * @param {String} bUrl: partial URL to project, used to generate links.
+ *
+ * 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
+*/
+function startBlame(blamedataUrl, bUrl) {
+
+       xhr = createRequestObject();
+       if (!xhr) {
+               errorInfo('ERROR: XMLHttpRequest not supported');
+               return;
+       }
+
+       t0 = new Date();
+       projectUrl = bUrl + (bUrl.indexOf('?') === -1 ? '?' : ';');
+       if ((div_progress_bar = document.getElementById('progress_bar'))) {
+               //div_progress_bar.setAttribute('style', 'width: 100%;');
+               div_progress_bar.style.cssText = 'width: 100%;';
+       }
+       totalLines = countLines();
+       updateProgressInfo();
+
+       /* add extra properties to xhr object to help processing response */
+       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.open('GET', blamedataUrl);
+       xhr.setRequestHeader('Accept', 'text/plain');
+       xhr.send(null);
+
+       // 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);
+}
+
+/* end of blame_incremental.js */
diff --git a/gitweb/static/js/javascript-detection.js b/gitweb/static/js/javascript-detection.js
new file mode 100644 (file)
index 0000000..93dd2bd
--- /dev/null
@@ -0,0 +1,43 @@
+// Copyright (C) 2007, Fredrik Kuivinen <frekui@gmail.com>
+//               2007, Petr Baudis <pasky@suse.cz>
+//          2008-2011, Jakub Narebski <jnareb@gmail.com>
+
+/**
+ * @fileOverview Detect if JavaScript is enabled, and pass it to server-side
+ * @license GPLv2 or later
+ */
+
+
+/* ============================================================ */
+/* Manipulating links */
+
+/**
+ * used to check if link has 'js' query parameter already (at end),
+ * and other reasons to not add 'js=1' param at the end of link
+ * @constant
+ */
+var jsExceptionsRe = /[;?]js=[01]$/;
+
+/**
+ * Add '?js=1' or ';js=1' to the end of every link in the document
+ * that doesn't have 'js' query parameter set already.
+ *
+ * Links with 'js=1' lead to JavaScript version of given action, if it
+ * exists (currently there is only 'blame_incremental' for 'blame')
+ *
+ * To be used as `window.onload` handler
+ *
+ * @globals jsExceptionsRe
+ */
+function fixLinks() {
+       var allLinks = document.getElementsByTagName("a") || document.links;
+       for (var i = 0, len = allLinks.length; i < len; i++) {
+               var link = allLinks[i];
+               if (!jsExceptionsRe.test(link)) { // =~ /[;?]js=[01]$/;
+                       link.href +=
+                               (link.href.indexOf('?') === -1 ? '?' : ';') + 'js=1';
+               }
+       }
+}
+
+/* end of javascript-detection.js */
diff --git a/gitweb/static/js/lib/common-lib.js b/gitweb/static/js/lib/common-lib.js
new file mode 100644 (file)
index 0000000..018bbb7
--- /dev/null
@@ -0,0 +1,224 @@
+// Copyright (C) 2007, Fredrik Kuivinen <frekui@gmail.com>
+//               2007, Petr Baudis <pasky@suse.cz>
+//          2008-2011, Jakub Narebski <jnareb@gmail.com>
+
+/**
+ * @fileOverview Generic JavaScript code (helper functions)
+ * @license GPLv2 or later
+ */
+
+
+/* ============================================================ */
+/* ............................................................ */
+/* Padding */
+
+/**
+ * pad INPUT on the left with STR that is assumed to have visible
+ * width of single character (for example nonbreakable spaces),
+ * to WIDTH characters
+ *
+ * example: padLeftStr(12, 3, '\u00A0') == '\u00A012'
+ *          ('\u00A0' is nonbreakable space)
+ *
+ * @param {Number|String} input: number to pad
+ * @param {Number} width: visible width of output
+ * @param {String} str: string to prefix to string, defaults to '\u00A0'
+ * @returns {String} INPUT prefixed with STR x (WIDTH - INPUT.length)
+ */
+function padLeftStr(input, width, str) {
+       var prefix = '';
+       if (typeof str === 'undefined') {
+               ch = '\u00A0'; // using '&nbsp;' doesn't work in all browsers
+       }
+
+       width -= input.toString().length;
+       while (width > 0) {
+               prefix += str;
+               width--;
+       }
+       return prefix + input;
+}
+
+/**
+ * Pad INPUT on the left to WIDTH, using given padding character CH,
+ * for example padLeft('a', 3, '_') is '__a'
+ *             padLeft(4, 2) is '04' (same as padLeft(4, 2, '0'))
+ *
+ * @param {String} input: input value converted to string.
+ * @param {Number} width: desired length of output.
+ * @param {String} ch: single character to prefix to string, defaults to '0'.
+ *
+ * @returns {String} Modified string, at least SIZE length.
+ */
+function padLeft(input, width, ch) {
+       var s = input + "";
+       if (typeof ch === 'undefined') {
+               ch = '0';
+       }
+
+       while (s.length < width) {
+               s = ch + s;
+       }
+       return s;
+}
+
+
+/* ............................................................ */
+/* Handling browser incompatibilities */
+
+/**
+ * Create XMLHttpRequest object in cross-browser way
+ * @returns XMLHttpRequest object, or null
+ */
+function createRequestObject() {
+       try {
+               return new XMLHttpRequest();
+       } catch (e) {}
+       try {
+               return window.createRequest();
+       } catch (e) {}
+       try {
+               return new ActiveXObject("Msxml2.XMLHTTP");
+       } catch (e) {}
+       try {
+               return new ActiveXObject("Microsoft.XMLHTTP");
+       } catch (e) {}
+
+       return null;
+}
+
+
+/**
+ * Insert rule giving specified STYLE to given SELECTOR at the end of
+ * first CSS stylesheet.
+ *
+ * @param {String} selector: CSS selector, e.g. '.class'
+ * @param {String} style: rule contents, e.g. 'background-color: red;'
+ */
+function addCssRule(selector, style) {
+       var stylesheet = document.styleSheets[0];
+
+       var theRules = [];
+       if (stylesheet.cssRules) {     // W3C way
+               theRules = stylesheet.cssRules;
+       } else if (stylesheet.rules) { // IE way
+               theRules = stylesheet.rules;
+       }
+
+       if (stylesheet.insertRule) {    // W3C way
+               stylesheet.insertRule(selector + ' { ' + style + ' }', theRules.length);
+       } else if (stylesheet.addRule) { // IE way
+               stylesheet.addRule(selector, style);
+       }
+}
+
+
+/* ............................................................ */
+/* Support for legacy browsers */
+
+/**
+ * Provides getElementsByClassName method, if there is no native
+ * implementation of this method.
+ *
+ * NOTE that there are limits and differences compared to native
+ * getElementsByClassName as defined by e.g.:
+ *   https://developer.mozilla.org/en/DOM/document.getElementsByClassName
+ *   http://www.whatwg.org/specs/web-apps/current-work/multipage/dom.html#dom-getelementsbyclassname
+ *   http://www.whatwg.org/specs/web-apps/current-work/multipage/dom.html#dom-document-getelementsbyclassname
+ *
+ * Namely, this implementation supports only single class name as
+ * argument and not set of space-separated tokens representing classes,
+ * it returns Array of nodes rather than live NodeList, and has
+ * additional optional argument where you can limit search to given tags
+ * (via getElementsByTagName).
+ *
+ * Based on
+ *   http://code.google.com/p/getelementsbyclassname/
+ *   http://www.dustindiaz.com/getelementsbyclass/
+ *   http://stackoverflow.com/questions/1818865/do-we-have-getelementsbyclassname-in-javascript
+ *
+ * See also http://ejohn.org/blog/getelementsbyclassname-speed-comparison/
+ *
+ * @param {String} class: name of _single_ class to find
+ * @param {String} [taghint] limit search to given tags
+ * @returns {Node[]} array of matching elements
+ */
+if (!('getElementsByClassName' in document)) {
+       document.getElementsByClassName = function (classname, taghint) {
+               taghint = taghint || "*";
+               var elements = (taghint === "*" && document.all) ?
+                              document.all :
+                              document.getElementsByTagName(taghint);
+               var pattern = new RegExp("(^|\\s)" + classname + "(\\s|$)");
+               var matches= [];
+               for (var i = 0, j = 0, n = elements.length; i < n; i++) {
+                       var el= elements[i];
+                       if (el.className && pattern.test(el.className)) {
+                               // matches.push(el);
+                               matches[j] = el;
+                               j++;
+                       }
+               }
+               return matches;
+       };
+} // end if
+
+
+/* ............................................................ */
+/* unquoting/unescaping filenames */
+
+/**#@+
+ * @constant
+ */
+var escCodeRe = /\\([^0-7]|[0-7]{1,3})/g;
+var octEscRe = /^[0-7]{1,3}$/;
+var maybeQuotedRe = /^\"(.*)\"$/;
+/**#@-*/
+
+/**
+ * unquote maybe C-quoted filename (as used by git, i.e. it is
+ * in double quotes '"' if there is any escape character used)
+ * e.g. 'aa' -> 'aa', '"a\ta"' -> 'a   a'
+ *
+ * @param {String} str: git-quoted string
+ * @returns {String} Unquoted and unescaped string
+ *
+ * @globals escCodeRe, octEscRe, maybeQuotedRe
+ */
+function unquote(str) {
+       function unq(seq) {
+               var es = {
+                       // character escape codes, aka escape sequences (from C)
+                       // replacements are to some extent JavaScript specific
+                       t: "\t",   // tab            (HT, TAB)
+                       n: "\n",   // newline        (NL)
+                       r: "\r",   // return         (CR)
+                       f: "\f",   // form feed      (FF)
+                       b: "\b",   // backspace      (BS)
+                       a: "\x07", // alarm (bell)   (BEL)
+                       e: "\x1B", // escape         (ESC)
+                       v: "\v"    // vertical tab   (VT)
+               };
+
+               if (seq.search(octEscRe) !== -1) {
+                       // octal char sequence
+                       return String.fromCharCode(parseInt(seq, 8));
+               } else if (seq in es) {
+                       // C escape sequence, aka character escape code
+                       return es[seq];
+               }
+               // quoted ordinary character
+               return seq;
+       }
+
+       var match = str.match(maybeQuotedRe);
+       if (match) {
+               str = match[1];
+               // perhaps str = eval('"'+str+'"'); would be enough?
+               str = str.replace(escCodeRe,
+                       function (substr, p1, offset, s) { return unq(p1); });
+       }
+       return str;
+}
+
+/* end of common-lib.js */
diff --git a/gitweb/static/js/lib/cookies.js b/gitweb/static/js/lib/cookies.js
new file mode 100644 (file)
index 0000000..72b51cd
--- /dev/null
@@ -0,0 +1,114 @@
+/**
+ * @fileOverview Accessing cookies from JavaScript
+ * @license GPLv2 or later
+ */
+
+/*
+ * Based on subsection "Cookies in JavaScript" of "Professional
+ * JavaScript for Web Developers" by Nicholas C. Zakas and cookie
+ * plugin from jQuery (dual licensed under the MIT and GPL licenses)
+ */
+
+
+/**
+ * Create a cookie with the given name and value,
+ * and other optional parameters.
+ *
+ * @example
+ *   setCookie('foo', 'bar'); // will be deleted when browser exits
+ *   setCookie('foo', 'bar', { expires: new Date(Date.parse('Jan 1, 2012')) });
+ *   setCookie('foo', 'bar', { expires: 7 }); // 7 days = 1 week
+ *   setCookie('foo', 'bar', { expires: 14, path: '/' });
+ *
+ * @param {String} sName:    Unique name of a cookie (letters, numbers, underscores).
+ * @param {String} sValue:   The string value stored in a cookie.
+ * @param {Object} [options] An object literal containing key/value pairs
+ *                           to provide optional cookie attributes.
+ * @param {String|Number|Date} [options.expires] Either literal string to be used as cookie expires,
+ *                            or an integer specifying the expiration date from now on in days,
+ *                            or a Date object to be used as cookie expiration date.
+ *                            If a negative value is specified or a date in the past),
+ *                            the cookie will be deleted.
+ *                            If set to null or omitted, the cookie will be a session cookie
+ *                            and will not be retained when the the browser exits.
+ * @param {String} [options.path] Restrict access of a cookie to particular directory
+ *                               (default: path of page that created the cookie).
+ * @param {String} [options.domain] Override what web sites are allowed to access cookie
+ *                                  (default: domain of page that created the cookie).
+ * @param {Boolean} [options.secure] If true, the secure attribute of the cookie will be set
+ *                                   and the cookie would be accessible only from secure sites
+ *                                   (cookie transmission will require secure protocol like HTTPS).
+ */
+function setCookie(sName, sValue, options) {
+       options = options || {};
+       if (sValue === null) {
+               sValue = '';
+               option.expires = 'delete';
+       }
+
+       var sCookie = sName + '=' + encodeURIComponent(sValue);
+
+       if (options.expires) {
+               var oExpires = options.expires, sDate;
+               if (oExpires === 'delete') {
+                       sDate = 'Thu, 01 Jan 1970 00:00:00 GMT';
+               } else if (typeof oExpires === 'string') {
+                       sDate = oExpires;
+               } else {
+                       var oDate;
+                       if (typeof oExpires === 'number') {
+                               oDate = new Date();
+                               oDate.setTime(oDate.getTime() + (oExpires * 24 * 60 * 60 * 1000)); // days to ms
+                       } else {
+                               oDate = oExpires;
+                       }
+                       sDate = oDate.toGMTString();
+               }
+               sCookie += '; expires=' + sDate;
+       }
+
+       if (options.path) {
+               sCookie += '; path=' + (options.path);
+       }
+       if (options.domain) {
+               sCookie += '; domain=' + (options.domain);
+       }
+       if (options.secure) {
+               sCookie += '; secure';
+       }
+       document.cookie = sCookie;
+}
+
+/**
+ * Get the value of a cookie with the given name.
+ *
+ * @param {String} sName: Unique name of a cookie (letters, numbers, underscores)
+ * @returns {String|null} The string value stored in a cookie
+ */
+function getCookie(sName) {
+       var sRE = '(?:; )?' + sName + '=([^;]*);?';
+       var oRE = new RegExp(sRE);
+       if (oRE.test(document.cookie)) {
+               return decodeURIComponent(RegExp['$1']);
+       } else {
+               return null;
+       }
+}
+
+/**
+ * Delete cookie with given name
+ *
+ * @param {String} sName:    Unique name of a cookie (letters, numbers, underscores)
+ * @param {Object} [options] An object literal containing key/value pairs
+ *                           to provide optional cookie attributes.
+ * @param {String} [options.path]   Must be the same as when setting a cookie
+ * @param {String} [options.domain] Must be the same as when setting a cookie
+ */
+function deleteCookie(sName, options) {
+       options = options || {};
+       options.expires = 'delete';
+
+       setCookie(sName, '', options);
+}
+
+/* end of cookies.js */
diff --git a/gitweb/static/js/lib/datetime.js b/gitweb/static/js/lib/datetime.js
new file mode 100644 (file)
index 0000000..f78c60a
--- /dev/null
@@ -0,0 +1,176 @@
+// Copyright (C) 2007, Fredrik Kuivinen <frekui@gmail.com>
+//               2007, Petr Baudis <pasky@suse.cz>
+//          2008-2011, Jakub Narebski <jnareb@gmail.com>
+
+/**
+ * @fileOverview Datetime manipulation: parsing and formatting
+ * @license GPLv2 or later
+ */
+
+
+/* ............................................................ */
+/* parsing and retrieving datetime related information */
+
+/**
+ * used to extract hours and minutes from timezone info, e.g '-0900'
+ * @constant
+ */
+var tzRe = /^([+\-])([0-9][0-9])([0-9][0-9])$/;
+
+/**
+ * convert numeric timezone +/-ZZZZ to offset from UTC in seconds
+ *
+ * @param {String} timezoneInfo: numeric timezone '(+|-)HHMM'
+ * @returns {Number} offset from UTC in seconds for timezone
+ *
+ * @globals tzRe
+ */
+function timezoneOffset(timezoneInfo) {
+       var match = tzRe.exec(timezoneInfo);
+       var tz_sign = (match[1] === '-' ? -1 : +1);
+       var tz_hour = parseInt(match[2],10);
+       var tz_min  = parseInt(match[3],10);
+
+       return tz_sign*(((tz_hour*60) + tz_min)*60);
+}
+
+/**
+ * return local (browser) timezone as offset from UTC in seconds
+ *
+ * @returns {Number} offset from UTC in seconds for local timezone
+ */
+function localTimezoneOffset() {
+       // getTimezoneOffset returns the time-zone offset from UTC,
+       // in _minutes_, for the current locale
+       return ((new Date()).getTimezoneOffset() * -60);
+}
+
+/**
+ * return local (browser) timezone as numeric timezone '(+|-)HHMM'
+ *
+ * @returns {String} locat timezone as -/+ZZZZ
+ */
+function localTimezoneInfo() {
+       var tzOffsetMinutes = (new Date()).getTimezoneOffset() * -1;
+
+       return formatTimezoneInfo(0, tzOffsetMinutes);
+}
+
+
+/**
+ * Parse RFC-2822 date into a Unix timestamp (into epoch)
+ *
+ * @param {String} date: date in RFC-2822 format, e.g. 'Thu, 21 Dec 2000 16:01:07 +0200'
+ * @returns {Number} epoch i.e. seconds since '00:00:00 1970-01-01 UTC'
+ */
+function parseRFC2822Date(date) {
+       // Date.parse accepts the IETF standard (RFC 1123 Section 5.2.14 and elsewhere)
+       // date syntax, which is defined in RFC 2822 (obsoletes RFC 822)
+       // and returns number of _milli_seconds since January 1, 1970, 00:00:00 UTC
+       return Date.parse(date) / 1000;
+}
+
+
+/* ............................................................ */
+/* formatting date */
+
+/**
+ * format timezone offset as numerical timezone '(+|-)HHMM' or '(+|-)HH:MM'
+ *
+ * @param {Number} hours:    offset in hours, e.g. 2 for '+0200'
+ * @param {Number} [minutes] offset in minutes, e.g. 30 for '-4030';
+ *                           it is split into hours if not 0 <= minutes < 60,
+ *                           for example 1200 would give '+0100';
+ *                           defaults to 0
+ * @param {String} [sep] separator between hours and minutes part,
+ *                       default is '', might be ':' for W3CDTF (rfc-3339)
+ * @returns {String} timezone in '(+|-)HHMM' or '(+|-)HH:MM' format
+ */
+function formatTimezoneInfo(hours, minutes, sep) {
+       minutes = minutes || 0; // to be able to use formatTimezoneInfo(hh)
+       sep = sep || ''; // default format is +/-ZZZZ
+
+       if (minutes < 0 || minutes > 59) {
+               hours = minutes > 0 ? Math.floor(minutes / 60) : Math.ceil(minutes / 60);
+               minutes = Math.abs(minutes - 60*hours); // sign of minutes is sign of hours
+               // NOTE: this works correctly because there is no UTC-00:30 timezone
+       }
+
+       var tzSign = hours >= 0 ? '+' : '-';
+       if (hours < 0) {
+               hours = -hours; // sign is stored in tzSign
+       }
+
+       return tzSign + padLeft(hours, 2, '0') + sep + padLeft(minutes, 2, '0');
+}
+
+/**
+ * translate 'utc' and 'local' to numerical timezone
+ * @param {String} timezoneInfo: might be 'utc' or 'local' (browser)
+ */
+function normalizeTimezoneInfo(timezoneInfo) {
+       switch (timezoneInfo) {
+       case 'utc':
+               return '+0000';
+       case 'local': // 'local' is browser timezone
+               return localTimezoneInfo();
+       }
+       return timezoneInfo;
+}
+
+
+/**
+ * return date in local time formatted in iso-8601 like format
+ * 'yyyy-mm-dd HH:MM:SS +/-ZZZZ' e.g. '2005-08-07 21:49:46 +0200'
+ *
+ * @param {Number} epoch: seconds since '00:00:00 1970-01-01 UTC'
+ * @param {String} timezoneInfo: numeric timezone '(+|-)HHMM'
+ * @returns {String} date in local time in iso-8601 like format
+ */
+function formatDateISOLocal(epoch, timezoneInfo) {
+       // date corrected by timezone
+       var localDate = new Date(1000 * (epoch +
+               timezoneOffset(timezoneInfo)));
+       var localDateStr = // e.g. '2005-08-07'
+               localDate.getUTCFullYear()                 + '-' +
+               padLeft(localDate.getUTCMonth()+1, 2, '0') + '-' +
+               padLeft(localDate.getUTCDate(),    2, '0');
+       var localTimeStr = // e.g. '21:49:46'
+               padLeft(localDate.getUTCHours(),   2, '0') + ':' +
+               padLeft(localDate.getUTCMinutes(), 2, '0') + ':' +
+               padLeft(localDate.getUTCSeconds(), 2, '0');
+
+       return localDateStr + ' ' + localTimeStr + ' ' + timezoneInfo;
+}
+
+/**
+ * return date in local time formatted in rfc-2822 format
+ * e.g. 'Thu, 21 Dec 2000 16:01:07 +0200'
+ *
+ * @param {Number} epoch: seconds since '00:00:00 1970-01-01 UTC'
+ * @param {String} timezoneInfo: numeric timezone '(+|-)HHMM'
+ * @param {Boolean} [padDay] e.g. 'Sun, 07 Aug' if true, 'Sun, 7 Aug' otherwise
+ * @returns {String} date in local time in rfc-2822 format
+ */
+function formatDateRFC2882(epoch, timezoneInfo, padDay) {
+       // A short textual representation of a month, three letters
+       var months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
+       // A textual representation of a day, three letters
+       var days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
+       // date corrected by timezone
+       var localDate = new Date(1000 * (epoch +
+               timezoneOffset(timezoneInfo)));
+       var localDateStr = // e.g. 'Sun, 7 Aug 2005' or 'Sun, 07 Aug 2005'
+               days[localDate.getUTCDay()] + ', ' +
+               (padDay ? padLeft(localDate.getUTCDate(),2,'0') : localDate.getUTCDate()) + ' ' +
+               months[localDate.getUTCMonth()] + ' ' +
+               localDate.getUTCFullYear();
+       var localTimeStr = // e.g. '21:49:46'
+               padLeft(localDate.getUTCHours(),   2, '0') + ':' +
+               padLeft(localDate.getUTCMinutes(), 2, '0') + ':' +
+               padLeft(localDate.getUTCSeconds(), 2, '0');
+
+       return localDateStr + ' ' + localTimeStr + ' ' + timezoneInfo;
+}
+
+/* end of datetime.js */
diff --git a/grep.c b/grep.c
index 63c4280cac9a87f867451c0d9787d1fe21ea9c2d..d03d9e24c23eff2d60ae7226a412f3ccf66670fd 100644 (file)
--- a/grep.c
+++ b/grep.c
@@ -59,6 +59,84 @@ struct grep_opt *grep_opt_dup(const struct grep_opt *opt)
        return ret;
 }
 
+static NORETURN void compile_regexp_failed(const struct grep_pat *p,
+               const char *error)
+{
+       char where[1024];
+
+       if (p->no)
+               sprintf(where, "In '%s' at %d, ", p->origin, p->no);
+       else if (p->origin)
+               sprintf(where, "%s, ", p->origin);
+       else
+               where[0] = 0;
+
+       die("%s'%s': %s", where, p->pattern, error);
+}
+
+#ifdef USE_LIBPCRE
+static void compile_pcre_regexp(struct grep_pat *p, const struct grep_opt *opt)
+{
+       const char *error;
+       int erroffset;
+       int options = 0;
+
+       if (opt->ignore_case)
+               options |= PCRE_CASELESS;
+
+       p->pcre_regexp = pcre_compile(p->pattern, options, &error, &erroffset,
+                       NULL);
+       if (!p->pcre_regexp)
+               compile_regexp_failed(p, error);
+
+       p->pcre_extra_info = pcre_study(p->pcre_regexp, 0, &error);
+       if (!p->pcre_extra_info && error)
+               die("%s", error);
+}
+
+static int pcrematch(struct grep_pat *p, const char *line, const char *eol,
+               regmatch_t *match, int eflags)
+{
+       int ovector[30], ret, flags = 0;
+
+       if (eflags & REG_NOTBOL)
+               flags |= PCRE_NOTBOL;
+
+       ret = pcre_exec(p->pcre_regexp, p->pcre_extra_info, line, eol - line,
+                       0, flags, ovector, ARRAY_SIZE(ovector));
+       if (ret < 0 && ret != PCRE_ERROR_NOMATCH)
+               die("pcre_exec failed with error code %d", ret);
+       if (ret > 0) {
+               ret = 0;
+               match->rm_so = ovector[0];
+               match->rm_eo = ovector[1];
+       }
+
+       return ret;
+}
+
+static void free_pcre_regexp(struct grep_pat *p)
+{
+       pcre_free(p->pcre_regexp);
+       pcre_free(p->pcre_extra_info);
+}
+#else /* !USE_LIBPCRE */
+static void compile_pcre_regexp(struct grep_pat *p, const struct grep_opt *opt)
+{
+       die("cannot use Perl-compatible regexes when not compiled with USE_LIBPCRE");
+}
+
+static int pcrematch(struct grep_pat *p, const char *line, const char *eol,
+               regmatch_t *match, int eflags)
+{
+       return 1;
+}
+
+static void free_pcre_regexp(struct grep_pat *p)
+{
+}
+#endif /* !USE_LIBPCRE */
+
 static void compile_regexp(struct grep_pat *p, struct grep_opt *opt)
 {
        int err;
@@ -70,20 +148,17 @@ static void compile_regexp(struct grep_pat *p, struct grep_opt *opt)
        if (p->fixed)
                return;
 
+       if (opt->pcre) {
+               compile_pcre_regexp(p, opt);
+               return;
+       }
+
        err = regcomp(&p->regexp, p->pattern, opt->regflags);
        if (err) {
                char errbuf[1024];
-               char where[1024];
-               if (p->no)
-                       sprintf(where, "In '%s' at %d, ",
-                               p->origin, p->no);
-               else if (p->origin)
-                       sprintf(where, "%s, ", p->origin);
-               else
-                       where[0] = 0;
                regerror(err, &p->regexp, errbuf, 1024);
                regfree(&p->regexp);
-               die("%s'%s': %s", where, p->pattern, errbuf);
+               compile_regexp_failed(p, errbuf);
        }
 }
 
@@ -320,7 +395,10 @@ void free_grep_patterns(struct grep_opt *opt)
                case GREP_PATTERN: /* atom */
                case GREP_PATTERN_HEAD:
                case GREP_PATTERN_BODY:
-                       regfree(&p->regexp);
+                       if (p->pcre_regexp)
+                               free_pcre_regexp(p);
+                       else
+                               regfree(&p->regexp);
                        break;
                default:
                        break;
@@ -412,6 +490,21 @@ static int regmatch(const regex_t *preg, char *line, char *eol,
        return regexec(preg, line, 1, match, eflags);
 }
 
+static int patmatch(struct grep_pat *p, char *line, char *eol,
+                   regmatch_t *match, int eflags)
+{
+       int hit;
+
+       if (p->fixed)
+               hit = !fixmatch(p, line, eol, match);
+       else if (p->pcre_regexp)
+               hit = !pcrematch(p, line, eol, match, eflags);
+       else
+               hit = !regmatch(&p->regexp, line, eol, match, eflags);
+
+       return hit;
+}
+
 static int strip_timestamp(char *bol, char **eol_p)
 {
        char *eol = *eol_p;
@@ -461,10 +554,7 @@ static int match_one_pattern(struct grep_pat *p, char *bol, char *eol,
        }
 
  again:
-       if (p->fixed)
-               hit = !fixmatch(p, bol, eol, pmatch);
-       else
-               hit = !regmatch(&p->regexp, bol, eol, pmatch, eflags);
+       hit = patmatch(p, bol, eol, pmatch, eflags);
 
        if (hit && p->word_regexp) {
                if ((pmatch[0].rm_so < 0) ||
@@ -791,10 +881,7 @@ static int look_ahead(struct grep_opt *opt,
                int hit;
                regmatch_t m;
 
-               if (p->fixed)
-                       hit = !fixmatch(p, bol, bol + *left_p, &m);
-               else
-                       hit = !regmatch(&p->regexp, bol, bol + *left_p, &m, 0);
+               hit = patmatch(p, bol, bol + *left_p, &m, 0);
                if (!hit || m.rm_so < 0 || m.rm_eo < 0)
                        continue;
                if (earliest < 0 || m.rm_so < earliest)
@@ -891,7 +978,7 @@ static int grep_buffer_1(struct grep_opt *opt, const char *name,
                int hit;
 
                /*
-                * look_ahead() skips quicly to the line that possibly
+                * look_ahead() skips quickly to the line that possibly
                 * has the next hit; don't call it if we need to do
                 * something more than just skipping the current line
                 * in response to an unmatch for the current line.  E.g.
diff --git a/grep.h b/grep.h
index 06621fe663545af52fbc42827a8374ab5bd42f38..cd055cdfa8cac903382d592f1ec7e2a22bf7f897 100644 (file)
--- a/grep.h
+++ b/grep.h
@@ -1,6 +1,12 @@
 #ifndef GREP_H
 #define GREP_H
 #include "color.h"
+#ifdef USE_LIBPCRE
+#include <pcre.h>
+#else
+typedef int pcre;
+typedef int pcre_extra;
+#endif
 
 enum grep_pat_token {
        GREP_PATTERN,
@@ -33,6 +39,8 @@ struct grep_pat {
        size_t patternlen;
        enum grep_header_field field;
        regex_t regexp;
+       pcre *pcre_regexp;
+       pcre_extra *pcre_extra_info;
        unsigned fixed:1;
        unsigned ignore_case:1;
        unsigned word_regexp:1;
@@ -83,6 +91,7 @@ struct grep_opt {
 #define GREP_BINARY_TEXT       2
        int binary;
        int extended;
+       int pcre;
        int relative;
        int pathname;
        int null_following_name;
diff --git a/ident.c b/ident.c
index 8e56b5e941e85c9634d420a3e8140c328bca9c30..35a6f264737e700ddd45714a0e966422a0d756c7 100644 (file)
--- a/ident.c
+++ b/ident.c
@@ -9,6 +9,12 @@
 
 static char git_default_date[50];
 
+#ifdef NO_GECOS_IN_PWENT
+#define get_gecos(ignored) "&"
+#else
+#define get_gecos(struct_passwd) ((struct_passwd)->pw_gecos)
+#endif
+
 static void copy_gecos(const struct passwd *w, char *name, size_t sz)
 {
        char *src, *dst;
@@ -20,7 +26,7 @@ static void copy_gecos(const struct passwd *w, char *name, size_t sz)
         * with commas.  Also & stands for capitalized form of the login name.
         */
 
-       for (len = 0, dst = name, src = w->pw_gecos; len < sz; src++) {
+       for (len = 0, dst = name, src = get_gecos(w); len < sz; src++) {
                int ch = *src;
                if (ch != '&') {
                        *dst++ = ch;
index 2a1e3a94c910cd74e41303d8b7aefa539817c831..e9457019d5ac7aff1e185e195f37b851732f6210 100644 (file)
@@ -294,8 +294,9 @@ void log_write_email_headers(struct rev_info *opt, struct commit *commit,
        if (opt->total > 0) {
                static char buffer[64];
                snprintf(buffer, sizeof(buffer),
-                        "Subject: [%s %0*d/%d] ",
+                        "Subject: [%s%s%0*d/%d] ",
                         opt->subject_prefix,
+                        *opt->subject_prefix ? " " : "",
                         digits_in_number(opt->total),
                         opt->nr, opt->total);
                subject = buffer;
@@ -484,8 +485,10 @@ void show_log(struct rev_info *opt)
        ctx.date_mode = opt->date_mode;
        ctx.abbrev = opt->diffopt.abbrev;
        ctx.after_subject = extra_headers;
+       ctx.preserve_subject = opt->preserve_subject;
        ctx.reflog_info = opt->reflog_info;
-       pretty_print_commit(opt->commit_format, commit, &msgbuf, &ctx);
+       ctx.fmt = opt->commit_format;
+       pretty_print_commit(&ctx, commit, &msgbuf);
 
        if (opt->add_signoff)
                append_signoff(&msgbuf, opt->add_signoff);
index 28046a998426e88dec9ad6ab431624bb41a81ce1..e1aaf43b438d0cfb6b7b0c723060a662a915bcea 100644 (file)
@@ -707,7 +707,7 @@ int notes_merge_commit(struct notes_merge_options *o,
                /* write file as blob, and add to partial_tree */
                if (stat(ent->name, &st))
                        die_errno("Failed to stat '%s'", ent->name);
-               if (index_path(blob_sha1, ent->name, &st, 1))
+               if (index_path(blob_sha1, ent->name, &st, HASH_WRITE_OBJECT))
                        die("Failed to write blob object from '%s'", ent->name);
                if (add_note(partial_tree, obj_sha1, blob_sha1, NULL))
                        die("Failed to add resolved note '%s' to notes tree",
index dff5c8d1831ca4019d1a502a393cb95f20ad18f7..f45eb54e4c99b8d67e4aa85f9a6218ea7a560592 100644 (file)
--- a/pretty.c
+++ b/pretty.c
@@ -318,16 +318,16 @@ static void add_rfc2047(struct strbuf *sb, const char *line, int len,
        strbuf_addstr(sb, "?=");
 }
 
-void pp_user_info(const char *what, enum cmit_fmt fmt, struct strbuf *sb,
-                 const char *line, enum date_mode dmode,
-                 const char *encoding)
+void pp_user_info(const struct pretty_print_context *pp,
+                 const char *what, struct strbuf *sb,
+                 const char *line, const char *encoding)
 {
        char *date;
        int namelen;
        unsigned long time;
        int tz;
 
-       if (fmt == CMIT_FMT_ONELINE)
+       if (pp->fmt == CMIT_FMT_ONELINE)
                return;
        date = strchr(line, '>');
        if (!date)
@@ -336,7 +336,7 @@ void pp_user_info(const char *what, enum cmit_fmt fmt, struct strbuf *sb,
        time = strtoul(date, &date, 10);
        tz = strtol(date, NULL, 10);
 
-       if (fmt == CMIT_FMT_EMAIL) {
+       if (pp->fmt == CMIT_FMT_EMAIL) {
                char *name_tail = strchr(line, '<');
                int display_name_length;
                int final_line;
@@ -366,18 +366,18 @@ void pp_user_info(const char *what, enum cmit_fmt fmt, struct strbuf *sb,
                strbuf_addch(sb, '\n');
        } else {
                strbuf_addf(sb, "%s: %.*s%.*s\n", what,
-                             (fmt == CMIT_FMT_FULLER) ? 4 : 0,
+                             (pp->fmt == CMIT_FMT_FULLER) ? 4 : 0,
                              "    ", namelen, line);
        }
-       switch (fmt) {
+       switch (pp->fmt) {
        case CMIT_FMT_MEDIUM:
-               strbuf_addf(sb, "Date:   %s\n", show_date(time, tz, dmode));
+               strbuf_addf(sb, "Date:   %s\n", show_date(time, tz, pp->date_mode));
                break;
        case CMIT_FMT_EMAIL:
                strbuf_addf(sb, "Date: %s\n", show_date(time, tz, DATE_RFC2822));
                break;
        case CMIT_FMT_FULLER:
-               strbuf_addf(sb, "%sDate: %s\n", what, show_date(time, tz, dmode));
+               strbuf_addf(sb, "%sDate: %s\n", what, show_date(time, tz, pp->date_mode));
                break;
        default:
                /* notin' */
@@ -408,12 +408,12 @@ static const char *skip_empty_lines(const char *msg)
        return msg;
 }
 
-static void add_merge_info(enum cmit_fmt fmt, struct strbuf *sb,
-                       const struct commit *commit, int abbrev)
+static void add_merge_info(const struct pretty_print_context *pp,
+                          struct strbuf *sb, const struct commit *commit)
 {
        struct commit_list *parent = commit->parents;
 
-       if ((fmt == CMIT_FMT_ONELINE) || (fmt == CMIT_FMT_EMAIL) ||
+       if ((pp->fmt == CMIT_FMT_ONELINE) || (pp->fmt == CMIT_FMT_EMAIL) ||
            !parent || !parent->next)
                return;
 
@@ -422,8 +422,8 @@ static void add_merge_info(enum cmit_fmt fmt, struct strbuf *sb,
        while (parent) {
                struct commit *p = parent->item;
                const char *hex = NULL;
-               if (abbrev)
-                       hex = find_unique_abbrev(p->object.sha1, abbrev);
+               if (pp->abbrev)
+                       hex = find_unique_abbrev(p->object.sha1, pp->abbrev);
                if (!hex)
                        hex = sha1_to_hex(p->object.sha1);
                parent = parent->next;
@@ -1084,7 +1084,7 @@ void userformat_find_requirements(const char *fmt, struct userformat_want *w)
                        return;
                fmt = user_format;
        }
-       strbuf_expand(&dummy, user_format, userformat_want_item, w);
+       strbuf_expand(&dummy, fmt, userformat_want_item, w);
        strbuf_release(&dummy);
 }
 
@@ -1116,9 +1116,7 @@ void format_commit_message(const struct commit *commit,
                free(context.message);
 }
 
-static void pp_header(enum cmit_fmt fmt,
-                     int abbrev,
-                     enum date_mode dmode,
+static void pp_header(const struct pretty_print_context *pp,
                      const char *encoding,
                      const struct commit *commit,
                      const char **msg_p,
@@ -1138,7 +1136,7 @@ static void pp_header(enum cmit_fmt fmt,
                        /* End of header */
                        return;
 
-               if (fmt == CMIT_FMT_RAW) {
+               if (pp->fmt == CMIT_FMT_RAW) {
                        strbuf_add(sb, line, linelen);
                        continue;
                }
@@ -1158,7 +1156,7 @@ static void pp_header(enum cmit_fmt fmt,
                                ;
                        /* with enough slop */
                        strbuf_grow(sb, num * 50 + 20);
-                       add_merge_info(fmt, sb, commit, abbrev);
+                       add_merge_info(pp, sb, commit);
                        parents_shown = 1;
                }
 
@@ -1169,32 +1167,31 @@ static void pp_header(enum cmit_fmt fmt,
                 */
                if (!memcmp(line, "author ", 7)) {
                        strbuf_grow(sb, linelen + 80);
-                       pp_user_info("Author", fmt, sb, line + 7, dmode, encoding);
+                       pp_user_info(pp, "Author", sb, line + 7, encoding);
                }
                if (!memcmp(line, "committer ", 10) &&
-                   (fmt == CMIT_FMT_FULL || fmt == CMIT_FMT_FULLER)) {
+                   (pp->fmt == CMIT_FMT_FULL || pp->fmt == CMIT_FMT_FULLER)) {
                        strbuf_grow(sb, linelen + 80);
-                       pp_user_info("Commit", fmt, sb, line + 10, dmode, encoding);
+                       pp_user_info(pp, "Commit", sb, line + 10, encoding);
                }
        }
 }
 
-void pp_title_line(enum cmit_fmt fmt,
+void pp_title_line(const struct pretty_print_context *pp,
                   const char **msg_p,
                   struct strbuf *sb,
-                  const char *subject,
-                  const char *after_subject,
                   const char *encoding,
                   int need_8bit_cte)
 {
        struct strbuf title;
 
        strbuf_init(&title, 80);
-       *msg_p = format_subject(&title, *msg_p, " ");
+       *msg_p = format_subject(&title, *msg_p,
+                               pp->preserve_subject ? "\n" : " ");
 
        strbuf_grow(sb, title.len + 1024);
-       if (subject) {
-               strbuf_addstr(sb, subject);
+       if (pp->subject) {
+               strbuf_addstr(sb, pp->subject);
                add_rfc2047(sb, title.buf, title.len, encoding);
        } else {
                strbuf_addbuf(sb, &title);
@@ -1208,16 +1205,16 @@ void pp_title_line(enum cmit_fmt fmt,
                        "Content-Transfer-Encoding: 8bit\n";
                strbuf_addf(sb, header_fmt, encoding);
        }
-       if (after_subject) {
-               strbuf_addstr(sb, after_subject);
+       if (pp->after_subject) {
+               strbuf_addstr(sb, pp->after_subject);
        }
-       if (fmt == CMIT_FMT_EMAIL) {
+       if (pp->fmt == CMIT_FMT_EMAIL) {
                strbuf_addch(sb, '\n');
        }
        strbuf_release(&title);
 }
 
-void pp_remainder(enum cmit_fmt fmt,
+void pp_remainder(const struct pretty_print_context *pp,
                  const char **msg_p,
                  struct strbuf *sb,
                  int indent)
@@ -1234,7 +1231,7 @@ void pp_remainder(enum cmit_fmt fmt,
                if (is_empty_line(line, &linelen)) {
                        if (first)
                                continue;
-                       if (fmt == CMIT_FMT_SHORT)
+                       if (pp->fmt == CMIT_FMT_SHORT)
                                break;
                }
                first = 0;
@@ -1259,19 +1256,19 @@ char *reencode_commit_message(const struct commit *commit, const char **encoding
        return logmsg_reencode(commit, encoding);
 }
 
-void pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit,
-                        struct strbuf *sb,
-                        const struct pretty_print_context *context)
+void pretty_print_commit(const struct pretty_print_context *pp,
+                        const struct commit *commit,
+                        struct strbuf *sb)
 {
        unsigned long beginning_of_body;
        int indent = 4;
        const char *msg = commit->buffer;
        char *reencoded;
        const char *encoding;
-       int need_8bit_cte = context->need_8bit_cte;
+       int need_8bit_cte = pp->need_8bit_cte;
 
-       if (fmt == CMIT_FMT_USERFORMAT) {
-               format_commit_message(commit, user_format, sb, context);
+       if (pp->fmt == CMIT_FMT_USERFORMAT) {
+               format_commit_message(commit, user_format, sb, pp);
                return;
        }
 
@@ -1280,14 +1277,14 @@ void pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit,
                msg = reencoded;
        }
 
-       if (fmt == CMIT_FMT_ONELINE || fmt == CMIT_FMT_EMAIL)
+       if (pp->fmt == CMIT_FMT_ONELINE || pp->fmt == CMIT_FMT_EMAIL)
                indent = 0;
 
        /*
         * We need to check and emit Content-type: to mark it
         * as 8-bit if we haven't done so.
         */
-       if (fmt == CMIT_FMT_EMAIL && need_8bit_cte == 0) {
+       if (pp->fmt == CMIT_FMT_EMAIL && need_8bit_cte == 0) {
                int i, ch, in_body;
 
                for (in_body = i = 0; (ch = msg[i]); i++) {
@@ -1306,9 +1303,8 @@ void pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit,
                }
        }
 
-       pp_header(fmt, context->abbrev, context->date_mode, encoding,
-                 commit, &msg, sb);
-       if (fmt != CMIT_FMT_ONELINE && !context->subject) {
+       pp_header(pp, encoding, commit, &msg, sb);
+       if (pp->fmt != CMIT_FMT_ONELINE && !pp->subject) {
                strbuf_addch(sb, '\n');
        }
 
@@ -1316,17 +1312,16 @@ void pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit,
        msg = skip_empty_lines(msg);
 
        /* These formats treat the title line specially. */
-       if (fmt == CMIT_FMT_ONELINE || fmt == CMIT_FMT_EMAIL)
-               pp_title_line(fmt, &msg, sb, context->subject,
-                             context->after_subject, encoding, need_8bit_cte);
+       if (pp->fmt == CMIT_FMT_ONELINE || pp->fmt == CMIT_FMT_EMAIL)
+               pp_title_line(pp, &msg, sb, encoding, need_8bit_cte);
 
        beginning_of_body = sb->len;
-       if (fmt != CMIT_FMT_ONELINE)
-               pp_remainder(fmt, &msg, sb, indent);
+       if (pp->fmt != CMIT_FMT_ONELINE)
+               pp_remainder(pp, &msg, sb, indent);
        strbuf_rtrim(sb);
 
        /* Make sure there is an EOLN for the non-oneline case */
-       if (fmt != CMIT_FMT_ONELINE)
+       if (pp->fmt != CMIT_FMT_ONELINE)
                strbuf_addch(sb, '\n');
 
        /*
@@ -1334,12 +1329,20 @@ void pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit,
         * format.  Make sure we did not strip the blank line
         * between the header and the body.
         */
-       if (fmt == CMIT_FMT_EMAIL && sb->len <= beginning_of_body)
+       if (pp->fmt == CMIT_FMT_EMAIL && sb->len <= beginning_of_body)
                strbuf_addch(sb, '\n');
 
-       if (context->show_notes)
+       if (pp->show_notes)
                format_display_notes(commit->object.sha1, sb, encoding,
                                     NOTES_SHOW_HEADER | NOTES_INDENT);
 
        free(reencoded);
 }
+
+void pp_commit_easy(enum cmit_fmt fmt, const struct commit *commit,
+                   struct strbuf *sb)
+{
+       struct pretty_print_context pp = {0};
+       pp.fmt = fmt;
+       pretty_print_commit(&pp, commit, sb);
+}
index f38471cac3ac57d8d52429cde2dcc4cf5b92557b..4ac9a037f478e769a69072c324a47876e298cae4 100644 (file)
@@ -92,7 +92,7 @@ static int ce_compare_data(struct cache_entry *ce, struct stat *st)
 
        if (fd >= 0) {
                unsigned char sha1[20];
-               if (!index_fd(sha1, fd, st, 0, OBJ_BLOB, ce->name, 0))
+               if (!index_fd(sha1, fd, st, OBJ_BLOB, ce->name, 0))
                        match = hashcmp(sha1, ce->sha1);
                /* index_fd() closed the file descriptor already */
        }
@@ -641,7 +641,7 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st,
                return 0;
        }
        if (!intent_only) {
-               if (index_path(ce->sha1, path, st, 1))
+               if (index_path(ce->sha1, path, st, HASH_WRITE_OBJECT))
                        return error("unable to index file %s", path);
        } else
                record_intent_to_add(ce);
diff --git a/refs.c b/refs.c
index e3c05110e58ca5684f9c43b1e1a5eb2587c96828..b10419a69815ef1c005915f3606c992659e60c77 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;
index 17d8a9b377265aeed9765f04d505959e1f7fb9b0..b5be25ce9623936e42566ca52b9e4d1d33153111 100644 (file)
@@ -811,19 +811,21 @@ static void parse_push(struct strbuf *buf)
 
                strbuf_reset(buf);
                if (strbuf_getline(buf, stdin, '\n') == EOF)
-                       return;
+                       goto free_specs;
                if (!*buf->buf)
                        break;
        } while (1);
 
        if (push(nr_spec, specs))
                exit(128); /* error already reported */
-       for (i = 0; i < nr_spec; i++)
-               free(specs[i]);
-       free(specs);
 
        printf("\n");
        fflush(stdout);
+
+ free_specs:
+       for (i = 0; i < nr_spec; i++)
+               free(specs[i]);
+       free(specs);
 }
 
 int main(int argc, const char **argv)
index dee2cb1514d1e97c47a4999ed66f8996035d71fe..dcb525a4d03faeba53d108d2e175a6d04f99d160 100644 (file)
--- a/rerere.c
+++ b/rerere.c
@@ -47,8 +47,14 @@ static void read_rr(struct string_list *rr)
                name = xstrdup(buf);
                if (fgetc(in) != '\t')
                        die("corrupt MERGE_RR");
-               for (i = 0; i < sizeof(buf) && (buf[i] = fgetc(in)); i++)
-                       ; /* do nothing */
+               for (i = 0; i < sizeof(buf); i++) {
+                       int c = fgetc(in);
+                       if (c < 0)
+                               die("corrupt MERGE_RR");
+                       buf[i] = c;
+                       if (c == 0)
+                                break;
+               }
                if (i == sizeof(buf))
                        die("filename too long");
                string_list_insert(rr, buf)->util = name;
@@ -739,6 +745,7 @@ void rerere_gc(struct string_list *rr)
                if (then < now - cutoff * 86400)
                        string_list_append(&to_remove, e->d_name);
        }
+       closedir(dir);
        for (i = 0; i < to_remove.nr; i++)
                unlink_rr_item(to_remove.items[i].string);
        string_list_clear(&to_remove, 0);
index cf0b001570838af00b79b5c305dc9e5da65d5653..c46cfaa3e4d2f06fd67ccd71c7ba47891796fd60 100644 (file)
@@ -133,6 +133,8 @@ void mark_parents_uninteresting(struct commit *commit)
 
 static void add_pending_object_with_mode(struct rev_info *revs, struct object *obj, const char *name, unsigned mode)
 {
+       if (!obj)
+               return;
        if (revs->no_walk && (obj->flags & UNINTERESTING))
                revs->no_walk = 0;
        if (revs->reflog_info && obj->type == OBJ_COMMIT) {
@@ -174,8 +176,11 @@ static struct object *get_reference(struct rev_info *revs, const char *name, con
        struct object *object;
 
        object = parse_object(sha1);
-       if (!object)
+       if (!object) {
+               if (revs->ignore_missing)
+                       return object;
                die("bad object %s", name);
+       }
        object->flags |= flags;
        return object;
 }
@@ -906,6 +911,8 @@ static int add_parents_only(struct rev_info *revs, const char *arg, int flags)
                return 0;
        while (1) {
                it = get_reference(revs, arg, sha1, 0);
+               if (!it && revs->ignore_missing)
+                       return 0;
                if (it->type != OBJ_TAG)
                        break;
                if (!((struct tag*)it)->tagged)
@@ -1044,6 +1051,8 @@ int handle_revision_arg(const char *arg, struct rev_info *revs,
                        a = lookup_commit_reference(from_sha1);
                        b = lookup_commit_reference(sha1);
                        if (!a || !b) {
+                               if (revs->ignore_missing)
+                                       return 0;
                                die(symmetric ?
                                    "Invalid symmetric difference expression %s...%s" :
                                    "Invalid revision range %s..%s",
@@ -1090,7 +1099,7 @@ int handle_revision_arg(const char *arg, struct rev_info *revs,
                arg++;
        }
        if (get_sha1_with_mode(arg, sha1, &mode))
-               return -1;
+               return revs->ignore_missing ? 0 : -1;
        if (!cant_be_filename)
                verify_non_filename(revs->prefix, arg);
        object = get_reference(revs, arg, sha1, flags ^ local_flags);
@@ -1428,6 +1437,9 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
                        revs->abbrev = 40;
        } else if (!strcmp(arg, "--abbrev-commit")) {
                revs->abbrev_commit = 1;
+               revs->abbrev_commit_given = 1;
+       } else if (!strcmp(arg, "--no-abbrev-commit")) {
+               revs->abbrev_commit = 0;
        } else if (!strcmp(arg, "--full-diff")) {
                revs->diff = 1;
                revs->full_diff = 1;
@@ -1474,6 +1486,8 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
        } else if (!strcmp(arg, "--children")) {
                revs->children.name = "children";
                revs->limited = 1;
+       } else if (!strcmp(arg, "--ignore-missing")) {
+               revs->ignore_missing = 1;
        } else {
                int opts = diff_opt_parse(&revs->diffopt, argv, argc);
                if (!opts)
@@ -1661,6 +1675,20 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
        }
 
        if (prune_data.nr) {
+               /*
+                * If we need to introduce the magic "a lone ':' means no
+                * pathspec whatsoever", here is the place to do so.
+                *
+                * if (prune_data.nr == 1 && !strcmp(prune_data[0], ":")) {
+                *      prune_data.nr = 0;
+                *      prune_data.alloc = 0;
+                *      free(prune_data.path);
+                *      prune_data.path = NULL;
+                * } else {
+                *      terminate prune_data.alloc with NULL and
+                *      call init_pathspec() to set revs->prune_data here.
+                * }
+                */
                ALLOC_GROW(prune_data.path, prune_data.nr+1, prune_data.alloc);
                prune_data.path[prune_data.nr++] = NULL;
                init_pathspec(&revs->prune_data,
index bca9947977c50d151b0fed90931ec52112e72bae..3d64adad18e2c889b7a7b7367f99c239f958dc70 100644 (file)
@@ -36,7 +36,8 @@ struct rev_info {
        const char *prefix;
        const char *def;
        struct pathspec prune_data;
-       unsigned int early_output;
+       unsigned int    early_output:1,
+                       ignore_missing:1;
 
        /* Traversal flags */
        unsigned int    dense:1,
@@ -90,9 +91,11 @@ struct rev_info {
                        show_notes_given:1,
                        pretty_given:1,
                        abbrev_commit:1,
+                       abbrev_commit_given:1,
                        use_terminator:1,
                        missing_newline:1,
-                       date_mode_explicit:1;
+                       date_mode_explicit:1,
+                       preserve_subject:1;
        unsigned int    disable_stdin:1;
 
        enum date_mode date_mode;
diff --git a/setup.c b/setup.c
index b6e6b5ae272b3ecae7dfa2cb446aef97e826884a..ce87900ce3c68ace0f231827bdda0e9a65ef15b3 100644 (file)
--- a/setup.c
+++ b/setup.c
@@ -85,8 +85,17 @@ static void NORETURN die_verify_filename(const char *prefix, const char *arg)
 {
        unsigned char sha1[20];
        unsigned mode;
-       /* try a detailed diagnostic ... */
-       get_sha1_with_mode_1(arg, sha1, &mode, 0, prefix);
+
+       /*
+        * Saying "'(icase)foo' does not exist in the index" when the
+        * user gave us ":(icase)foo" is just stupid.  A magic pathspec
+        * begins with a colon and is followed by a non-alnum; do not
+        * let get_sha1_with_mode_1(only_to_die=1) to even trigger.
+        */
+       if (!(arg[0] == ':' && !isalnum(arg[1])))
+               /* try a detailed diagnostic ... */
+               get_sha1_with_mode_1(arg, sha1, &mode, 1, prefix);
+
        /* ... or fall back the most general message. */
        die("ambiguous argument '%s': unknown revision or path not in the working tree.\n"
            "Use '--' to separate paths from revisions", arg);
@@ -126,6 +135,105 @@ void verify_non_filename(const char *prefix, const char *arg)
            "Use '--' to separate filenames from revisions", arg);
 }
 
+/*
+ * Magic pathspec
+ *
+ * NEEDSWORK: These need to be moved to dir.h or even to a new
+ * pathspec.h when we restructure get_pathspec() users to use the
+ * "struct pathspec" interface.
+ *
+ * Possible future magic semantics include stuff like:
+ *
+ *     { PATHSPEC_NOGLOB, '!', "noglob" },
+ *     { PATHSPEC_ICASE, '\0', "icase" },
+ *     { PATHSPEC_RECURSIVE, '*', "recursive" },
+ *     { PATHSPEC_REGEXP, '\0', "regexp" },
+ *
+ */
+#define PATHSPEC_FROMTOP    (1<<0)
+
+static struct pathspec_magic {
+       unsigned bit;
+       char mnemonic; /* this cannot be ':'! */
+       const char *name;
+} pathspec_magic[] = {
+       { PATHSPEC_FROMTOP, '/', "top" },
+};
+
+/*
+ * Take an element of a pathspec and check for magic signatures.
+ * Append the result to the prefix.
+ *
+ * For now, we only parse the syntax and throw out anything other than
+ * "top" magic.
+ *
+ * NEEDSWORK: This needs to be rewritten when we start migrating
+ * get_pathspec() users to use the "struct pathspec" interface.  For
+ * example, a pathspec element may be marked as case-insensitive, but
+ * the prefix part must always match literally, and a single stupid
+ * string cannot express such a case.
+ */
+static const char *prefix_pathspec(const char *prefix, int prefixlen, const char *elt)
+{
+       unsigned magic = 0;
+       const char *copyfrom = elt;
+       int i;
+
+       if (elt[0] != ':') {
+               ; /* nothing to do */
+       } else if (elt[1] == '(') {
+               /* longhand */
+               const char *nextat;
+               for (copyfrom = elt + 2;
+                    *copyfrom && *copyfrom != ')';
+                    copyfrom = nextat) {
+                       size_t len = strcspn(copyfrom, ",)");
+                       if (copyfrom[len] == ')')
+                               nextat = copyfrom + len;
+                       else
+                               nextat = copyfrom + len + 1;
+                       if (!len)
+                               continue;
+                       for (i = 0; i < ARRAY_SIZE(pathspec_magic); i++)
+                               if (strlen(pathspec_magic[i].name) == len &&
+                                   !strncmp(pathspec_magic[i].name, copyfrom, len)) {
+                                       magic |= pathspec_magic[i].bit;
+                                       break;
+                               }
+                       if (ARRAY_SIZE(pathspec_magic) <= i)
+                               die("Invalid pathspec magic '%.*s' in '%s'",
+                                   (int) len, copyfrom, elt);
+               }
+               if (*copyfrom == ')')
+                       copyfrom++;
+       } else {
+               /* shorthand */
+               for (copyfrom = elt + 1;
+                    *copyfrom && *copyfrom != ':';
+                    copyfrom++) {
+                       char ch = *copyfrom;
+
+                       if (!is_pathspec_magic(ch))
+                               break;
+                       for (i = 0; i < ARRAY_SIZE(pathspec_magic); i++)
+                               if (pathspec_magic[i].mnemonic == ch) {
+                                       magic |= pathspec_magic[i].bit;
+                                       break;
+                               }
+                       if (ARRAY_SIZE(pathspec_magic) <= i)
+                               die("Unimplemented pathspec magic '%c' in '%s'",
+                                   ch, elt);
+               }
+               if (*copyfrom == ':')
+                       copyfrom++;
+       }
+
+       if (magic & PATHSPEC_FROMTOP)
+               return xstrdup(copyfrom);
+       else
+               return prefix_path(prefix, prefixlen, copyfrom);
+}
+
 const char **get_pathspec(const char *prefix, const char **pathspec)
 {
        const char *entry = *pathspec;
@@ -147,8 +255,7 @@ const char **get_pathspec(const char *prefix, const char **pathspec)
        dst = pathspec;
        prefixlen = prefix ? strlen(prefix) : 0;
        while (*src) {
-               const char *p = prefix_path(prefix, prefixlen, *src);
-               *(dst++) = p;
+               *(dst++) = prefix_pathspec(prefix, prefixlen, *src);
                src++;
        }
        *dst = NULL;
@@ -275,7 +382,7 @@ const char *read_gitfile_gently(const char *path)
        const char *slash;
        struct stat st;
        int fd;
-       size_t len;
+       ssize_t len;
 
        if (stat(path, &st))
                return NULL;
diff --git a/sh-i18n--envsubst.c b/sh-i18n--envsubst.c
new file mode 100644 (file)
index 0000000..9d2e971
--- /dev/null
@@ -0,0 +1,443 @@
+/*
+ * sh-i18n--envsubst.c - a stripped-down version of gettext's envsubst(1)
+ *
+ * Copyright (C) 2010 Ã†var Arnfjörð Bjarmason
+ *
+ * This is a modified version of
+ * 67d0871a8c:gettext-runtime/src/envsubst.c from the gettext.git
+ * repository. It has been stripped down to only implement the
+ * envsubst(1) features that we need in the git-sh-i18n fallbacks.
+ *
+ * The "Close standard error" part in main() is from
+ * 8dac033df0:gnulib-local/lib/closeout.c. The copyright notices for
+ * both files are reproduced immediately below.
+ */
+
+#include "git-compat-util.h"
+
+/* Substitution of environment variables in shell format strings.
+   Copyright (C) 2003-2007 Free Software Foundation, Inc.
+   Written by Bruno Haible <bruno@clisp.org>, 2003.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation,
+   Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
+
+/* closeout.c - close standard output and standard error
+   Copyright (C) 1998-2007 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation,
+   Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* If true, substitution shall be performed on all variables.  */
+static unsigned short int all_variables;
+
+/* Forward declaration of local functions.  */
+static void print_variables (const char *string);
+static void note_variables (const char *string);
+static void subst_from_stdin (void);
+
+int
+main (int argc, char *argv[])
+{
+  /* Default values for command line options.  */
+  /* unsigned short int show_variables = 0; */
+
+  switch (argc)
+       {
+       case 1:
+         error ("we won't substitute all variables on stdin for you");
+         /*
+         all_variables = 1;
+      subst_from_stdin ();
+         */
+       case 2:
+         /* echo '$foo and $bar' | git sh-i18n--envsubst --variables '$foo and $bar' */
+         all_variables = 0;
+         note_variables (argv[1]);
+      subst_from_stdin ();
+         break;
+       case 3:
+         /* git sh-i18n--envsubst --variables '$foo and $bar' */
+         if (strcmp(argv[1], "--variables"))
+               error ("first argument must be --variables when two are given");
+         /* show_variables = 1; */
+      print_variables (argv[2]);
+         break;
+       default:
+         error ("too many arguments");
+         break;
+       }
+
+  /* Close standard error.  This is simpler than fwriteerror_no_ebadf, because
+     upon failure we don't need an errno - all we can do at this point is to
+     set an exit status.  */
+  errno = 0;
+  if (ferror (stderr) || fflush (stderr))
+    {
+      fclose (stderr);
+      exit (EXIT_FAILURE);
+    }
+  if (fclose (stderr) && errno != EBADF)
+    exit (EXIT_FAILURE);
+
+  exit (EXIT_SUCCESS);
+}
+
+/* Parse the string and invoke the callback each time a $VARIABLE or
+   ${VARIABLE} construct is seen, where VARIABLE is a nonempty sequence
+   of ASCII alphanumeric/underscore characters, starting with an ASCII
+   alphabetic/underscore character.
+   We allow only ASCII characters, to avoid dependencies w.r.t. the current
+   encoding: While "${\xe0}" looks like a variable access in ISO-8859-1
+   encoding, it doesn't look like one in the BIG5, BIG5-HKSCS, GBK, GB18030,
+   SHIFT_JIS, JOHAB encodings, because \xe0\x7d is a single character in these
+   encodings.  */
+static void
+find_variables (const char *string,
+               void (*callback) (const char *var_ptr, size_t var_len))
+{
+  for (; *string != '\0';)
+    if (*string++ == '$')
+      {
+       const char *variable_start;
+       const char *variable_end;
+       unsigned short int valid;
+       char c;
+
+       if (*string == '{')
+         string++;
+
+       variable_start = string;
+       c = *string;
+       if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '_')
+         {
+           do
+             c = *++string;
+           while ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')
+                  || (c >= '0' && c <= '9') || c == '_');
+           variable_end = string;
+
+           if (variable_start[-1] == '{')
+             {
+               if (*string == '}')
+                 {
+                   string++;
+                   valid = 1;
+                 }
+               else
+                 valid = 0;
+             }
+           else
+             valid = 1;
+
+           if (valid)
+             callback (variable_start, variable_end - variable_start);
+         }
+      }
+}
+
+
+/* Print a variable to stdout, followed by a newline.  */
+static void
+print_variable (const char *var_ptr, size_t var_len)
+{
+  fwrite (var_ptr, var_len, 1, stdout);
+  putchar ('\n');
+}
+
+/* Print the variables contained in STRING to stdout, each one followed by a
+   newline.  */
+static void
+print_variables (const char *string)
+{
+  find_variables (string, &print_variable);
+}
+
+
+/* Type describing list of immutable strings,
+   implemented using a dynamic array.  */
+typedef struct string_list_ty string_list_ty;
+struct string_list_ty
+{
+  const char **item;
+  size_t nitems;
+  size_t nitems_max;
+};
+
+/* Initialize an empty list of strings.  */
+static inline void
+string_list_init (string_list_ty *slp)
+{
+  slp->item = NULL;
+  slp->nitems = 0;
+  slp->nitems_max = 0;
+}
+
+/* Append a single string to the end of a list of strings.  */
+static inline void
+string_list_append (string_list_ty *slp, const char *s)
+{
+  /* Grow the list.  */
+  if (slp->nitems >= slp->nitems_max)
+    {
+      size_t nbytes;
+
+      slp->nitems_max = slp->nitems_max * 2 + 4;
+      nbytes = slp->nitems_max * sizeof (slp->item[0]);
+      slp->item = (const char **) xrealloc (slp->item, nbytes);
+    }
+
+  /* Add the string to the end of the list.  */
+  slp->item[slp->nitems++] = s;
+}
+
+/* Compare two strings given by reference.  */
+static int
+cmp_string (const void *pstr1, const void *pstr2)
+{
+  const char *str1 = *(const char **)pstr1;
+  const char *str2 = *(const char **)pstr2;
+
+  return strcmp (str1, str2);
+}
+
+/* Sort a list of strings.  */
+static inline void
+string_list_sort (string_list_ty *slp)
+{
+  if (slp->nitems > 0)
+    qsort (slp->item, slp->nitems, sizeof (slp->item[0]), cmp_string);
+}
+
+/* Test whether a string list contains a given string.  */
+static inline int
+string_list_member (const string_list_ty *slp, const char *s)
+{
+  size_t j;
+
+  for (j = 0; j < slp->nitems; ++j)
+    if (strcmp (slp->item[j], s) == 0)
+      return 1;
+  return 0;
+}
+
+/* Test whether a sorted string list contains a given string.  */
+static int
+sorted_string_list_member (const string_list_ty *slp, const char *s)
+{
+  size_t j1, j2;
+
+  j1 = 0;
+  j2 = slp->nitems;
+  if (j2 > 0)
+    {
+      /* Binary search.  */
+      while (j2 - j1 > 1)
+       {
+         /* Here we know that if s is in the list, it is at an index j
+            with j1 <= j < j2.  */
+         size_t j = (j1 + j2) >> 1;
+         int result = strcmp (slp->item[j], s);
+
+         if (result > 0)
+           j2 = j;
+         else if (result == 0)
+           return 1;
+         else
+           j1 = j + 1;
+       }
+      if (j2 > j1)
+       if (strcmp (slp->item[j1], s) == 0)
+         return 1;
+    }
+  return 0;
+}
+
+
+/* Set of variables on which to perform substitution.
+   Used only if !all_variables.  */
+static string_list_ty variables_set;
+
+/* Adds a variable to variables_set.  */
+static void
+note_variable (const char *var_ptr, size_t var_len)
+{
+  char *string = xmalloc (var_len + 1);
+  memcpy (string, var_ptr, var_len);
+  string[var_len] = '\0';
+
+  string_list_append (&variables_set, string);
+}
+
+/* Stores the variables occurring in the string in variables_set.  */
+static void
+note_variables (const char *string)
+{
+  string_list_init (&variables_set);
+  find_variables (string, &note_variable);
+  string_list_sort (&variables_set);
+}
+
+
+static int
+do_getc (void)
+{
+  int c = getc (stdin);
+
+  if (c == EOF)
+    {
+      if (ferror (stdin))
+       error ("error while reading standard input");
+    }
+
+  return c;
+}
+
+static inline void
+do_ungetc (int c)
+{
+  if (c != EOF)
+    ungetc (c, stdin);
+}
+
+/* Copies stdin to stdout, performing substitutions.  */
+static void
+subst_from_stdin (void)
+{
+  static char *buffer;
+  static size_t bufmax;
+  static size_t buflen;
+  int c;
+
+  for (;;)
+    {
+      c = do_getc ();
+      if (c == EOF)
+       break;
+      /* Look for $VARIABLE or ${VARIABLE}.  */
+      if (c == '$')
+       {
+         unsigned short int opening_brace = 0;
+         unsigned short int closing_brace = 0;
+
+         c = do_getc ();
+         if (c == '{')
+           {
+             opening_brace = 1;
+             c = do_getc ();
+           }
+         if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '_')
+           {
+             unsigned short int valid;
+
+             /* Accumulate the VARIABLE in buffer.  */
+             buflen = 0;
+             do
+               {
+                 if (buflen >= bufmax)
+                   {
+                     bufmax = 2 * bufmax + 10;
+                     buffer = xrealloc (buffer, bufmax);
+                   }
+                 buffer[buflen++] = c;
+
+                 c = do_getc ();
+               }
+             while ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')
+                    || (c >= '0' && c <= '9') || c == '_');
+
+             if (opening_brace)
+               {
+                 if (c == '}')
+                   {
+                     closing_brace = 1;
+                     valid = 1;
+                   }
+                 else
+                   {
+                     valid = 0;
+                     do_ungetc (c);
+                   }
+               }
+             else
+               {
+                 valid = 1;
+                 do_ungetc (c);
+               }
+
+             if (valid)
+               {
+                 /* Terminate the variable in the buffer.  */
+                 if (buflen >= bufmax)
+                   {
+                     bufmax = 2 * bufmax + 10;
+                     buffer = xrealloc (buffer, bufmax);
+                   }
+                 buffer[buflen] = '\0';
+
+                 /* Test whether the variable shall be substituted.  */
+                 if (!all_variables
+                     && !sorted_string_list_member (&variables_set, buffer))
+                   valid = 0;
+               }
+
+             if (valid)
+               {
+                 /* Substitute the variable's value from the environment.  */
+                 const char *env_value = getenv (buffer);
+
+                 if (env_value != NULL)
+                   fputs (env_value, stdout);
+               }
+             else
+               {
+                 /* Perform no substitution at all.  Since the buffered input
+                    contains no other '$' than at the start, we can just
+                    output all the buffered contents.  */
+                 putchar ('$');
+                 if (opening_brace)
+                   putchar ('{');
+                 fwrite (buffer, buflen, 1, stdout);
+                 if (closing_brace)
+                   putchar ('}');
+               }
+           }
+         else
+           {
+             do_ungetc (c);
+             putchar ('$');
+             if (opening_brace)
+               putchar ('{');
+           }
+       }
+      else
+       putchar (c);
+    }
+}
diff --git a/sha1-array.c b/sha1-array.c
new file mode 100644 (file)
index 0000000..b2f47f9
--- /dev/null
@@ -0,0 +1,59 @@
+#include "cache.h"
+#include "sha1-array.h"
+#include "sha1-lookup.h"
+
+void sha1_array_append(struct sha1_array *array, const unsigned char *sha1)
+{
+       ALLOC_GROW(array->sha1, array->nr + 1, array->alloc);
+       hashcpy(array->sha1[array->nr++], sha1);
+       array->sorted = 0;
+}
+
+static int void_hashcmp(const void *a, const void *b)
+{
+       return hashcmp(a, b);
+}
+
+void sha1_array_sort(struct sha1_array *array)
+{
+       qsort(array->sha1, array->nr, sizeof(*array->sha1), void_hashcmp);
+       array->sorted = 1;
+}
+
+static const unsigned char *sha1_access(size_t index, void *table)
+{
+       unsigned char (*array)[20] = table;
+       return array[index];
+}
+
+int sha1_array_lookup(struct sha1_array *array, const unsigned char *sha1)
+{
+       if (!array->sorted)
+               sha1_array_sort(array);
+       return sha1_pos(sha1, array->sha1, array->nr, sha1_access);
+}
+
+void sha1_array_clear(struct sha1_array *array)
+{
+       free(array->sha1);
+       array->sha1 = NULL;
+       array->nr = 0;
+       array->alloc = 0;
+       array->sorted = 0;
+}
+
+void sha1_array_for_each_unique(struct sha1_array *array,
+                               for_each_sha1_fn fn,
+                               void *data)
+{
+       int i;
+
+       if (!array->sorted)
+               sha1_array_sort(array);
+
+       for (i = 0; i < array->nr; i++) {
+               if (i > 0 && !hashcmp(array->sha1[i], array->sha1[i-1]))
+                       continue;
+               fn(array->sha1[i], data);
+       }
+}
diff --git a/sha1-array.h b/sha1-array.h
new file mode 100644 (file)
index 0000000..4499b5d
--- /dev/null
@@ -0,0 +1,24 @@
+#ifndef SHA1_ARRAY_H
+#define SHA1_ARRAY_H
+
+struct sha1_array {
+       unsigned char (*sha1)[20];
+       int nr;
+       int alloc;
+       int sorted;
+};
+
+#define SHA1_ARRAY_INIT { NULL, 0, 0, 0 }
+
+void sha1_array_append(struct sha1_array *array, const unsigned char *sha1);
+void sha1_array_sort(struct sha1_array *array);
+int sha1_array_lookup(struct sha1_array *array, const unsigned char *sha1);
+void sha1_array_clear(struct sha1_array *array);
+
+typedef void (*for_each_sha1_fn)(const unsigned char sha1[20],
+                                void *data);
+void sha1_array_for_each_unique(struct sha1_array *array,
+                               for_each_sha1_fn fn,
+                               void *data);
+
+#endif /* SHA1_ARRAY_H */
index 357f9ab7ff6aad644857986f36fd55da8348744f..064a33040812ba8782bf602c693abf08613d6ec7 100644 (file)
@@ -11,6 +11,7 @@
 #include "pack.h"
 #include "blob.h"
 #include "commit.h"
+#include "run-command.h"
 #include "tag.h"
 #include "tree.h"
 #include "tree-walk.h"
@@ -2578,10 +2579,11 @@ static void check_tag(const void *buf, size_t size)
 }
 
 static int index_mem(unsigned char *sha1, void *buf, size_t size,
-                    int write_object, enum object_type type,
-                    const char *path, int format_check)
+                    enum object_type type,
+                    const char *path, unsigned flags)
 {
        int ret, re_allocated = 0;
+       int write_object = flags & HASH_WRITE_OBJECT;
 
        if (!type)
                type = OBJ_BLOB;
@@ -2597,7 +2599,7 @@ static int index_mem(unsigned char *sha1, void *buf, size_t size,
                        re_allocated = 1;
                }
        }
-       if (format_check) {
+       if (flags & HASH_FORMAT_CHECK) {
                if (type == OBJ_TREE)
                        check_tree(buf, size);
                if (type == OBJ_COMMIT)
@@ -2615,44 +2617,141 @@ static int index_mem(unsigned char *sha1, void *buf, size_t size,
        return ret;
 }
 
+static int index_pipe(unsigned char *sha1, int fd, enum object_type type,
+                     const char *path, unsigned flags)
+{
+       struct strbuf sbuf = STRBUF_INIT;
+       int ret;
+
+       if (strbuf_read(&sbuf, fd, 4096) >= 0)
+               ret = index_mem(sha1, sbuf.buf, sbuf.len, type, path, flags);
+       else
+               ret = -1;
+       strbuf_release(&sbuf);
+       return ret;
+}
+
 #define SMALL_FILE_SIZE (32*1024)
 
-int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object,
-            enum object_type type, const char *path, int format_check)
+static int index_core(unsigned char *sha1, int fd, size_t size,
+                     enum object_type type, const char *path,
+                     unsigned flags)
 {
        int ret;
-       size_t size = xsize_t(st->st_size);
 
-       if (!S_ISREG(st->st_mode)) {
-               struct strbuf sbuf = STRBUF_INIT;
-               if (strbuf_read(&sbuf, fd, 4096) >= 0)
-                       ret = index_mem(sha1, sbuf.buf, sbuf.len, write_object,
-                                       type, path, format_check);
-               else
-                       ret = -1;
-               strbuf_release(&sbuf);
-       } else if (!size) {
-               ret = index_mem(sha1, NULL, size, write_object, type, path,
-                               format_check);
+       if (!size) {
+               ret = index_mem(sha1, NULL, size, type, path, flags);
        } else if (size <= SMALL_FILE_SIZE) {
                char *buf = xmalloc(size);
                if (size == read_in_full(fd, buf, size))
-                       ret = index_mem(sha1, buf, size, write_object, type,
-                                       path, format_check);
+                       ret = index_mem(sha1, buf, size, type, path, flags);
                else
                        ret = error("short read %s", strerror(errno));
                free(buf);
        } else {
                void *buf = xmmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
-               ret = index_mem(sha1, buf, size, write_object, type, path,
-                               format_check);
+               ret = index_mem(sha1, buf, size, type, path, flags);
                munmap(buf, size);
        }
+       return ret;
+}
+
+/*
+ * This creates one packfile per large blob, because the caller
+ * immediately wants the result sha1, and fast-import can report the
+ * object name via marks mechanism only by closing the created
+ * packfile.
+ *
+ * This also bypasses the usual "convert-to-git" dance, and that is on
+ * purpose. We could write a streaming version of the converting
+ * functions and insert that before feeding the data to fast-import
+ * (or equivalent in-core API described above), but the primary
+ * motivation for trying to stream from the working tree file and to
+ * avoid mmaping it in core is to deal with large binary blobs, and
+ * by definition they do _not_ want to get any conversion.
+ */
+static int index_stream(unsigned char *sha1, int fd, size_t size,
+                       enum object_type type, const char *path,
+                       unsigned flags)
+{
+       struct child_process fast_import;
+       char export_marks[512];
+       const char *argv[] = { "fast-import", "--quiet", export_marks, NULL };
+       char tmpfile[512];
+       char fast_import_cmd[512];
+       char buf[512];
+       int len, tmpfd;
+
+       strcpy(tmpfile, git_path("hashstream_XXXXXX"));
+       tmpfd = git_mkstemp_mode(tmpfile, 0600);
+       if (tmpfd < 0)
+               die_errno("cannot create tempfile: %s", tmpfile);
+       if (close(tmpfd))
+               die_errno("cannot close tempfile: %s", tmpfile);
+       sprintf(export_marks, "--export-marks=%s", tmpfile);
+
+       memset(&fast_import, 0, sizeof(fast_import));
+       fast_import.in = -1;
+       fast_import.argv = argv;
+       fast_import.git_cmd = 1;
+       if (start_command(&fast_import))
+               die_errno("index-stream: git fast-import failed");
+
+       len = sprintf(fast_import_cmd, "blob\nmark :1\ndata %lu\n",
+                     (unsigned long) size);
+       write_or_whine(fast_import.in, fast_import_cmd, len,
+                      "index-stream: feeding fast-import");
+       while (size) {
+               char buf[10240];
+               size_t sz = size < sizeof(buf) ? size : sizeof(buf);
+               size_t actual;
+
+               actual = read_in_full(fd, buf, sz);
+               if (actual < 0)
+                       die_errno("index-stream: reading input");
+               if (write_in_full(fast_import.in, buf, actual) != actual)
+                       die_errno("index-stream: feeding fast-import");
+               size -= actual;
+       }
+       if (close(fast_import.in))
+               die_errno("index-stream: closing fast-import");
+       if (finish_command(&fast_import))
+               die_errno("index-stream: finishing fast-import");
+
+       tmpfd = open(tmpfile, O_RDONLY);
+       if (tmpfd < 0)
+               die_errno("index-stream: cannot open fast-import mark");
+       len = read(tmpfd, buf, sizeof(buf));
+       if (len < 0)
+               die_errno("index-stream: reading fast-import mark");
+       if (close(tmpfd) < 0)
+               die_errno("index-stream: closing fast-import mark");
+       if (unlink(tmpfile))
+               die_errno("index-stream: unlinking fast-import mark");
+       if (len != 44 ||
+           memcmp(":1 ", buf, 3) ||
+           get_sha1_hex(buf + 3, sha1))
+               die_errno("index-stream: unexpected fast-import mark: <%s>", buf);
+       return 0;
+}
+
+int index_fd(unsigned char *sha1, int fd, struct stat *st,
+            enum object_type type, const char *path, unsigned flags)
+{
+       int ret;
+       size_t size = xsize_t(st->st_size);
+
+       if (!S_ISREG(st->st_mode))
+               ret = index_pipe(sha1, fd, type, path, flags);
+       else if (size <= big_file_threshold || type != OBJ_BLOB)
+               ret = index_core(sha1, fd, size, type, path, flags);
+       else
+               ret = index_stream(sha1, fd, size, type, path, flags);
        close(fd);
        return ret;
 }
 
-int index_path(unsigned char *sha1, const char *path, struct stat *st, int write_object)
+int index_path(unsigned char *sha1, const char *path, struct stat *st, unsigned flags)
 {
        int fd;
        struct strbuf sb = STRBUF_INIT;
@@ -2663,7 +2762,7 @@ int index_path(unsigned char *sha1, const char *path, struct stat *st, int write
                if (fd < 0)
                        return error("open(\"%s\"): %s", path,
                                     strerror(errno));
-               if (index_fd(sha1, fd, st, write_object, OBJ_BLOB, path, 0) < 0)
+               if (index_fd(sha1, fd, st, OBJ_BLOB, path, flags) < 0)
                        return error("%s: failed to insert into database",
                                     path);
                break;
@@ -2673,7 +2772,7 @@ int index_path(unsigned char *sha1, const char *path, struct stat *st, int write
                        return error("readlink(\"%s\"): %s", path,
                                     errstr);
                }
-               if (!write_object)
+               if (!(flags & HASH_WRITE_OBJECT))
                        hash_sha1_file(sb.buf, sb.len, blob_type, sha1);
                else if (write_sha1_file(sb.buf, sb.len, blob_type, sha1))
                        return error("%s: failed to insert into database",
index 69cd6c815d6bb43fdeda9c4b28fc138bed17c057..ff5992acc971ac5a67fa3d4def1e0c064b90519f 100644 (file)
@@ -1083,11 +1083,12 @@ static void diagnose_invalid_index_path(int stage,
 }
 
 
-int get_sha1_with_mode_1(const char *name, unsigned char *sha1, unsigned *mode, int gently, const char *prefix)
+int get_sha1_with_mode_1(const char *name, unsigned char *sha1, unsigned *mode,
+                        int only_to_die, const char *prefix)
 {
        struct object_context oc;
        int ret;
-       ret = get_sha1_with_context_1(name, sha1, &oc, gently, prefix);
+       ret = get_sha1_with_context_1(name, sha1, &oc, only_to_die, prefix);
        *mode = oc.mode;
        return ret;
 }
@@ -1111,7 +1112,7 @@ static char *resolve_relative_path(const char *rel)
 
 int get_sha1_with_context_1(const char *name, unsigned char *sha1,
                            struct object_context *oc,
-                           int gently, const char *prefix)
+                           int only_to_die, const char *prefix)
 {
        int ret, bracket_depth;
        int namelen = strlen(name);
@@ -1133,7 +1134,7 @@ int get_sha1_with_context_1(const char *name, unsigned char *sha1,
                struct cache_entry *ce;
                char *new_path = NULL;
                int pos;
-               if (namelen > 2 && name[1] == '/') {
+               if (!only_to_die && namelen > 2 && name[1] == '/') {
                        struct commit_list *list = NULL;
                        for_each_ref(handle_one_ref, &list);
                        return get_sha1_oneline(name + 2, sha1, list);
@@ -1176,7 +1177,7 @@ int get_sha1_with_context_1(const char *name, unsigned char *sha1,
                        }
                        pos++;
                }
-               if (!gently)
+               if (only_to_die && name[1] && name[1] != '/')
                        diagnose_invalid_index_path(stage, prefix, cp);
                free(new_path);
                return -1;
@@ -1192,7 +1193,7 @@ int get_sha1_with_context_1(const char *name, unsigned char *sha1,
        if (*cp == ':') {
                unsigned char tree_sha1[20];
                char *object_name = NULL;
-               if (!gently) {
+               if (only_to_die) {
                        object_name = xmalloc(cp-name+1);
                        strncpy(object_name, name, cp-name);
                        object_name[cp-name] = '\0';
@@ -1205,7 +1206,7 @@ int get_sha1_with_context_1(const char *name, unsigned char *sha1,
                        if (new_filename)
                                filename = new_filename;
                        ret = get_tree_entry(tree_sha1, filename, sha1, &oc->mode);
-                       if (!gently) {
+                       if (only_to_die) {
                                diagnose_invalid_sha1_path(prefix, filename,
                                                           tree_sha1, object_name);
                                free(object_name);
@@ -1218,7 +1219,7 @@ int get_sha1_with_context_1(const char *name, unsigned char *sha1,
                        free(new_filename);
                        return ret;
                } else {
-                       if (!gently)
+                       if (only_to_die)
                                die("Invalid object name '%s'.", object_name);
                }
        }
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 cad36dd7502b81ef8e27476e9686562547b5a1c0..c85abaffb3b8c2142e87f6e0525fa67c6b62c1a1 100644 (file)
--- a/t/README
+++ b/t/README
@@ -588,6 +588,11 @@ use these, and "test_set_prereq" for how to define your own.
    Test is not run by root user, and an attempt to write to an
    unwritable file is expected to fail correctly.
 
+ - LIBPCRE
+
+   Git was compiled with USE_LIBPCRE=YesPlease. Wrap any tests
+   that use git-grep --perl-regexp or git-grep -P in these.
+
 Tips for Writing Tests
 ----------------------
 
diff --git a/t/lib-read-tree.sh b/t/lib-read-tree.sh
new file mode 100644 (file)
index 0000000..abc2c6f
--- /dev/null
@@ -0,0 +1,43 @@
+#!/bin/sh
+#
+# Helper functions to check if read-tree would succeed/fail as expected with
+# and without the dry-run option. They also test that the dry-run does not
+# write the index and that together with -u it doesn't touch the work tree.
+#
+read_tree_must_succeed () {
+    git ls-files -s >pre-dry-run &&
+    git read-tree -n "$@" &&
+    git ls-files -s >post-dry-run &&
+    test_cmp pre-dry-run post-dry-run &&
+    git read-tree "$@"
+}
+
+read_tree_must_fail () {
+    git ls-files -s >pre-dry-run &&
+    test_must_fail git read-tree -n "$@" &&
+    git ls-files -s >post-dry-run &&
+    test_cmp pre-dry-run post-dry-run &&
+    test_must_fail git read-tree "$@"
+}
+
+read_tree_u_must_succeed () {
+    git ls-files -s >pre-dry-run &&
+    git diff-files -p >pre-dry-run-wt &&
+    git read-tree -n "$@" &&
+    git ls-files -s >post-dry-run &&
+    git diff-files -p >post-dry-run-wt &&
+    test_cmp pre-dry-run post-dry-run &&
+    test_cmp pre-dry-run-wt post-dry-run-wt &&
+    git read-tree "$@"
+}
+
+read_tree_u_must_fail () {
+    git ls-files -s >pre-dry-run &&
+    git diff-files -p >pre-dry-run-wt &&
+    test_must_fail git read-tree -n "$@" &&
+    git ls-files -s >post-dry-run &&
+    git diff-files -p >post-dry-run-wt &&
+    test_cmp pre-dry-run post-dry-run &&
+    test_cmp pre-dry-run-wt post-dry-run-wt &&
+    test_must_fail git read-tree "$@"
+}
index 8106af8fba918b8de82857d40a7f693224c84e81..ad664105646d4579a4e31ea5d230268acc33c0f5 100755 (executable)
@@ -409,7 +409,7 @@ test_expect_success SYMLINKS 're-init to move gitdir symlink' '
        cd newdir &&
        mv .git here &&
        ln -s here .git &&
-       git init -L ../realgitdir
+       git init --separate-git-dir ../realgitdir
        ) &&
        echo "gitdir: `pwd`/realgitdir" >expected &&
        test_cmp expected newdir/.git &&
index 5067d1e15b566721c1a3ae79052cbcb023a03656..bd83ed371acb5075b2b3cbb7866d36aa4bd48519 100755 (executable)
@@ -2,74 +2,14 @@
 
 test_description="Test the svn importer's input handling routines.
 
-These tests exercise the line_buffer library, but their real purpose
-is to check the assumptions that library makes of the platform's input
-routines.  Processes engaged in bi-directional communication would
-hang if fread or fgets is too greedy.
+These tests provide some simple checks that the line_buffer API
+behaves as advertised.
 
 While at it, check that input of newlines and null bytes are handled
 correctly.
 "
 . ./test-lib.sh
 
-test -n "$GIT_REMOTE_SVN_TEST_BIG_FILES" && test_set_prereq EXPENSIVE
-
-generate_tens_of_lines () {
-       tens=$1 &&
-       line=$2 &&
-
-       i=0 &&
-       while test $i -lt "$tens"
-       do
-               for j in a b c d e f g h i j
-               do
-                       echo "$line"
-               done &&
-               : $((i = $i + 1)) ||
-               return
-       done
-}
-
-long_read_test () {
-       : each line is 10 bytes, including newline &&
-       line=abcdefghi &&
-       echo "$line" >expect &&
-
-       if ! test_declared_prereq PIPE
-       then
-               echo >&4 "long_read_test: need to declare PIPE prerequisite"
-               return 127
-       fi &&
-       tens_of_lines=$(($1 / 100 + 1)) &&
-       lines=$(($tens_of_lines * 10)) &&
-       readsize=$((($lines - 1) * 10 + 3)) &&
-       copysize=7 &&
-       rm -f input &&
-       mkfifo input &&
-       {
-               (
-                       generate_tens_of_lines $tens_of_lines "$line" &&
-                       exec sleep 100
-               ) >input &
-       } &&
-       test-line-buffer input <<-EOF >output &&
-       binary $readsize
-       copy $copysize
-       EOF
-       kill $! &&
-       test_line_count = $lines output &&
-       tail -n 1 <output >actual &&
-       test_cmp expect actual
-}
-
-test_expect_success 'setup: have pipes?' '
-      rm -f frob &&
-      if mkfifo frob
-      then
-               test_set_prereq PIPE
-      fi
-'
-
 test_expect_success 'hello world' '
        echo ">HELLO" >expect &&
        test-line-buffer <<-\EOF >actual &&
@@ -79,21 +19,6 @@ test_expect_success 'hello world' '
        test_cmp expect actual
 '
 
-test_expect_success PIPE '0-length read, no input available' '
-       printf ">" >expect &&
-       rm -f input &&
-       mkfifo input &&
-       {
-               sleep 100 >input &
-       } &&
-       test-line-buffer input <<-\EOF >actual &&
-       binary 0
-       copy 0
-       EOF
-       kill $! &&
-       test_cmp expect actual
-'
-
 test_expect_success '0-length read, send along greeting' '
        echo ">HELLO" >expect &&
        test-line-buffer <<-\EOF >actual &&
@@ -104,33 +29,6 @@ test_expect_success '0-length read, send along greeting' '
        test_cmp expect actual
 '
 
-test_expect_success PIPE '1-byte read, no input available' '
-       printf ">%s" ab >expect &&
-       rm -f input &&
-       mkfifo input &&
-       {
-               (
-                       printf "%s" a &&
-                       printf "%s" b &&
-                       exec sleep 100
-               ) >input &
-       } &&
-       test-line-buffer input <<-\EOF >actual &&
-       binary 1
-       copy 1
-       EOF
-       kill $! &&
-       test_cmp expect actual
-'
-
-test_expect_success PIPE 'long read (around 8192 bytes)' '
-       long_read_test 8192
-'
-
-test_expect_success PIPE,EXPENSIVE 'longer read (around 65536 bytes)' '
-       long_read_test 65536
-'
-
 test_expect_success 'read from file descriptor' '
        rm -f input &&
        echo hello >expect &&
diff --git a/t/t0201-gettext-fallbacks.sh b/t/t0201-gettext-fallbacks.sh
new file mode 100755 (executable)
index 0000000..54d98b9
--- /dev/null
@@ -0,0 +1,51 @@
+#!/bin/sh
+#
+# Copyright (c) 2010 Ã†var Arnfjörð Bjarmason
+#
+
+test_description='Gettext Shell fallbacks'
+
+. ./test-lib.sh
+. "$GIT_BUILD_DIR"/git-sh-i18n
+
+test_expect_success 'gettext: our gettext() fallback has pass-through semantics' '
+    printf "test" >expect &&
+    gettext "test" >actual &&
+    test_i18ncmp expect actual &&
+    printf "test more words" >expect &&
+    gettext "test more words" >actual &&
+    test_i18ncmp expect actual
+'
+
+test_expect_success 'eval_gettext: our eval_gettext() fallback has pass-through semantics' '
+    printf "test" >expect &&
+    eval_gettext "test" >actual &&
+    test_i18ncmp expect actual &&
+    printf "test more words" >expect &&
+    eval_gettext "test more words" >actual &&
+    test_i18ncmp expect actual
+'
+
+test_expect_success 'eval_gettext: our eval_gettext() fallback can interpolate variables' '
+    printf "test YesPlease" >expect &&
+    GIT_INTERNAL_GETTEXT_TEST_FALLBACKS=YesPlease eval_gettext "test \$GIT_INTERNAL_GETTEXT_TEST_FALLBACKS" >actual &&
+    test_i18ncmp expect actual
+'
+
+test_expect_success 'eval_gettext: our eval_gettext() fallback can interpolate variables with spaces' '
+    cmdline="git am" &&
+    export cmdline;
+    printf "When you have resolved this problem run git am --resolved." >expect &&
+    eval_gettext "When you have resolved this problem run \$cmdline --resolved." >actual
+    test_i18ncmp expect actual
+'
+
+test_expect_success 'eval_gettext: our eval_gettext() fallback can interpolate variables with spaces and quotes' '
+    cmdline="git am" &&
+    export cmdline;
+    printf "When you have resolved this problem run \"git am --resolved\"." >expect &&
+    eval_gettext "When you have resolved this problem run \"\$cmdline --resolved\"." >actual
+    test_i18ncmp expect actual
+'
+
+test_done
index ca8a4098fa06afbdbe5c427983d3953550457bc2..babcdd23433348cd0e2397b2fb6968f7cfae4b0a 100755 (executable)
@@ -72,6 +72,7 @@ In addition:
 
 '
 . ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-read-tree.sh
 . "$TEST_DIRECTORY"/lib-read-tree-m-3way.sh
 
 ################################################################
@@ -137,7 +138,7 @@ test_expect_success \
     '3-way merge with git read-tree -m, empty cache' \
     "rm -fr [NDMALTS][NDMALTSF] Z &&
      rm .git/index &&
-     git read-tree -m $tree_O $tree_A $tree_B &&
+     read_tree_must_succeed -m $tree_O $tree_A $tree_B &&
      check_result"
 
 # This starts out with the first head, which is the normal
@@ -146,9 +147,9 @@ test_expect_success \
     '3-way merge with git read-tree -m, match H' \
     "rm -fr [NDMALTS][NDMALTSF] Z &&
      rm .git/index &&
-     git read-tree $tree_A &&
+     read_tree_must_succeed $tree_A &&
      git checkout-index -f -u -a &&
-     git read-tree -m $tree_O $tree_A $tree_B &&
+     read_tree_must_succeed -m $tree_O $tree_A $tree_B &&
      check_result"
 
 : <<\END_OF_CASE_TABLE
@@ -211,7 +212,7 @@ test_expect_success '1 - must not have an entry not in A.' "
      rm -f .git/index XX &&
      echo XX >XX &&
      git update-index --add XX &&
-     test_must_fail git read-tree -m $tree_O $tree_A $tree_B
+     read_tree_must_fail -m $tree_O $tree_A $tree_B
 "
 
 test_expect_success \
@@ -219,7 +220,7 @@ test_expect_success \
     "rm -f .git/index NA &&
      cp .orig-B/NA NA &&
      git update-index --add NA &&
-     git read-tree -m $tree_O $tree_A $tree_B"
+     read_tree_must_succeed -m $tree_O $tree_A $tree_B"
 
 test_expect_success \
     '2 - matching B alone is OK in !O && !A && B case.' \
@@ -227,14 +228,14 @@ test_expect_success \
      cp .orig-B/NA NA &&
      git update-index --add NA &&
      echo extra >>NA &&
-     git read-tree -m $tree_O $tree_A $tree_B"
+     read_tree_must_succeed -m $tree_O $tree_A $tree_B"
 
 test_expect_success \
     '3 - must match A in !O && A && !B case.' \
     "rm -f .git/index AN &&
      cp .orig-A/AN AN &&
      git update-index --add AN &&
-     git read-tree -m $tree_O $tree_A $tree_B &&
+     read_tree_must_succeed -m $tree_O $tree_A $tree_B &&
      check_result"
 
 test_expect_success \
@@ -243,7 +244,7 @@ test_expect_success \
      cp .orig-A/AN AN &&
      git update-index --add AN &&
      echo extra >>AN &&
-     git read-tree -m $tree_O $tree_A $tree_B"
+     read_tree_must_succeed -m $tree_O $tree_A $tree_B"
 
 test_expect_success \
     '3 (fail) - must match A in !O && A && !B case.' "
@@ -251,7 +252,7 @@ test_expect_success \
      cp .orig-A/AN AN &&
      echo extra >>AN &&
      git update-index --add AN &&
-     test_must_fail git read-tree -m $tree_O $tree_A $tree_B
+     read_tree_must_fail -m $tree_O $tree_A $tree_B
 "
 
 test_expect_success \
@@ -259,7 +260,7 @@ test_expect_success \
     "rm -f .git/index AA &&
      cp .orig-A/AA AA &&
      git update-index --add AA &&
-     git read-tree -m $tree_O $tree_A $tree_B &&
+     read_tree_must_succeed -m $tree_O $tree_A $tree_B &&
      check_result"
 
 test_expect_success \
@@ -268,7 +269,7 @@ test_expect_success \
      cp .orig-A/AA AA &&
      git update-index --add AA &&
      echo extra >>AA &&
-     test_must_fail git read-tree -m $tree_O $tree_A $tree_B
+     read_tree_must_fail -m $tree_O $tree_A $tree_B
 "
 
 test_expect_success \
@@ -277,7 +278,7 @@ test_expect_success \
      cp .orig-A/AA AA &&
      echo extra >>AA &&
      git update-index --add AA &&
-     test_must_fail git read-tree -m $tree_O $tree_A $tree_B
+     read_tree_must_fail -m $tree_O $tree_A $tree_B
 "
 
 test_expect_success \
@@ -285,7 +286,7 @@ test_expect_success \
     "rm -f .git/index LL &&
      cp .orig-A/LL LL &&
      git update-index --add LL &&
-     git read-tree -m $tree_O $tree_A $tree_B &&
+     read_tree_must_succeed -m $tree_O $tree_A $tree_B &&
      check_result"
 
 test_expect_success \
@@ -294,7 +295,7 @@ test_expect_success \
      cp .orig-A/LL LL &&
      git update-index --add LL &&
      echo extra >>LL &&
-     git read-tree -m $tree_O $tree_A $tree_B &&
+     read_tree_must_succeed -m $tree_O $tree_A $tree_B &&
      check_result"
 
 test_expect_success \
@@ -303,7 +304,7 @@ test_expect_success \
      cp .orig-A/LL LL &&
      echo extra >>LL &&
      git update-index --add LL &&
-     test_must_fail git read-tree -m $tree_O $tree_A $tree_B
+     read_tree_must_fail -m $tree_O $tree_A $tree_B
 "
 
 test_expect_success \
@@ -311,7 +312,7 @@ test_expect_success \
      rm -f .git/index DD &&
      echo DD >DD &&
      git update-index --add DD &&
-     test_must_fail git read-tree -m $tree_O $tree_A $tree_B
+     read_tree_must_fail -m $tree_O $tree_A $tree_B
 "
 
 test_expect_success \
@@ -319,7 +320,7 @@ test_expect_success \
      rm -f .git/index DM &&
      cp .orig-B/DM DM &&
      git update-index --add DM &&
-     test_must_fail git read-tree -m $tree_O $tree_A $tree_B
+     read_tree_must_fail -m $tree_O $tree_A $tree_B
 "
 
 test_expect_success \
@@ -327,7 +328,7 @@ test_expect_success \
      rm -f .git/index DN &&
      cp .orig-B/DN DN &&
      git update-index --add DN &&
-     test_must_fail git read-tree -m $tree_O $tree_A $tree_B
+     read_tree_must_fail -m $tree_O $tree_A $tree_B
 "
 
 test_expect_success \
@@ -335,7 +336,7 @@ test_expect_success \
     "rm -f .git/index MD &&
      cp .orig-A/MD MD &&
      git update-index --add MD &&
-     git read-tree -m $tree_O $tree_A $tree_B &&
+     read_tree_must_succeed -m $tree_O $tree_A $tree_B &&
      check_result"
 
 test_expect_success \
@@ -344,7 +345,7 @@ test_expect_success \
      cp .orig-A/MD MD &&
      git update-index --add MD &&
      echo extra >>MD &&
-     test_must_fail git read-tree -m $tree_O $tree_A $tree_B
+     read_tree_must_fail -m $tree_O $tree_A $tree_B
 "
 
 test_expect_success \
@@ -353,7 +354,7 @@ test_expect_success \
      cp .orig-A/MD MD &&
      echo extra >>MD &&
      git update-index --add MD &&
-     test_must_fail git read-tree -m $tree_O $tree_A $tree_B
+     read_tree_must_fail -m $tree_O $tree_A $tree_B
 "
 
 test_expect_success \
@@ -361,7 +362,7 @@ test_expect_success \
     "rm -f .git/index ND &&
      cp .orig-A/ND ND &&
      git update-index --add ND &&
-     git read-tree -m $tree_O $tree_A $tree_B &&
+     read_tree_must_succeed -m $tree_O $tree_A $tree_B &&
      check_result"
 
 test_expect_success \
@@ -370,7 +371,7 @@ test_expect_success \
      cp .orig-A/ND ND &&
      git update-index --add ND &&
      echo extra >>ND &&
-     test_must_fail git read-tree -m $tree_O $tree_A $tree_B
+     read_tree_must_fail -m $tree_O $tree_A $tree_B
 "
 
 test_expect_success \
@@ -379,7 +380,7 @@ test_expect_success \
      cp .orig-A/ND ND &&
      echo extra >>ND &&
      git update-index --add ND &&
-     test_must_fail git read-tree -m $tree_O $tree_A $tree_B
+     read_tree_must_fail -m $tree_O $tree_A $tree_B
 "
 
 test_expect_success \
@@ -387,7 +388,7 @@ test_expect_success \
     "rm -f .git/index MM &&
      cp .orig-A/MM MM &&
      git update-index --add MM &&
-     git read-tree -m $tree_O $tree_A $tree_B &&
+     read_tree_must_succeed -m $tree_O $tree_A $tree_B &&
      check_result"
 
 test_expect_success \
@@ -396,7 +397,7 @@ test_expect_success \
      cp .orig-A/MM MM &&
      git update-index --add MM &&
      echo extra >>MM &&
-     test_must_fail git read-tree -m $tree_O $tree_A $tree_B
+     read_tree_must_fail -m $tree_O $tree_A $tree_B
 "
 
 test_expect_success \
@@ -405,7 +406,7 @@ test_expect_success \
      cp .orig-A/MM MM &&
      echo extra >>MM &&
      git update-index --add MM &&
-     test_must_fail git read-tree -m $tree_O $tree_A $tree_B
+     read_tree_must_fail -m $tree_O $tree_A $tree_B
 "
 
 test_expect_success \
@@ -413,7 +414,7 @@ test_expect_success \
     "rm -f .git/index SS &&
      cp .orig-A/SS SS &&
      git update-index --add SS &&
-     git read-tree -m $tree_O $tree_A $tree_B &&
+     read_tree_must_succeed -m $tree_O $tree_A $tree_B &&
      check_result"
 
 test_expect_success \
@@ -422,7 +423,7 @@ test_expect_success \
      cp .orig-A/SS SS &&
      git update-index --add SS &&
      echo extra >>SS &&
-     git read-tree -m $tree_O $tree_A $tree_B &&
+     read_tree_must_succeed -m $tree_O $tree_A $tree_B &&
      check_result"
 
 test_expect_success \
@@ -431,7 +432,7 @@ test_expect_success \
      cp .orig-A/SS SS &&
      echo extra >>SS &&
      git update-index --add SS &&
-     test_must_fail git read-tree -m $tree_O $tree_A $tree_B
+     read_tree_must_fail -m $tree_O $tree_A $tree_B
 "
 
 test_expect_success \
@@ -439,7 +440,7 @@ test_expect_success \
     "rm -f .git/index MN &&
      cp .orig-A/MN MN &&
      git update-index --add MN &&
-     git read-tree -m $tree_O $tree_A $tree_B &&
+     read_tree_must_succeed -m $tree_O $tree_A $tree_B &&
      check_result"
 
 test_expect_success \
@@ -448,7 +449,7 @@ test_expect_success \
      cp .orig-A/MN MN &&
      git update-index --add MN &&
      echo extra >>MN &&
-     git read-tree -m $tree_O $tree_A $tree_B &&
+     read_tree_must_succeed -m $tree_O $tree_A $tree_B &&
      check_result"
 
 test_expect_success \
@@ -456,7 +457,7 @@ test_expect_success \
     "rm -f .git/index NM &&
      cp .orig-A/NM NM &&
      git update-index --add NM &&
-     git read-tree -m $tree_O $tree_A $tree_B &&
+     read_tree_must_succeed -m $tree_O $tree_A $tree_B &&
      check_result"
 
 test_expect_success \
@@ -465,7 +466,7 @@ test_expect_success \
      cp .orig-B/NM NM &&
      git update-index --add NM &&
      echo extra >>NM &&
-     git read-tree -m $tree_O $tree_A $tree_B &&
+     read_tree_must_succeed -m $tree_O $tree_A $tree_B &&
      check_result"
 
 test_expect_success \
@@ -474,7 +475,7 @@ test_expect_success \
      cp .orig-A/NM NM &&
      git update-index --add NM &&
      echo extra >>NM &&
-     test_must_fail git read-tree -m $tree_O $tree_A $tree_B
+     read_tree_must_fail -m $tree_O $tree_A $tree_B
 "
 
 test_expect_success \
@@ -483,7 +484,7 @@ test_expect_success \
      cp .orig-A/NM NM &&
      echo extra >>NM &&
      git update-index --add NM &&
-     test_must_fail git read-tree -m $tree_O $tree_A $tree_B
+     read_tree_must_fail -m $tree_O $tree_A $tree_B
 "
 
 test_expect_success \
@@ -491,7 +492,7 @@ test_expect_success \
     "rm -f .git/index NN &&
      cp .orig-A/NN NN &&
      git update-index --add NN &&
-     git read-tree -m $tree_O $tree_A $tree_B &&
+     read_tree_must_succeed -m $tree_O $tree_A $tree_B &&
      check_result"
 
 test_expect_success \
@@ -500,7 +501,7 @@ test_expect_success \
      cp .orig-A/NN NN &&
      git update-index --add NN &&
      echo extra >>NN &&
-     git read-tree -m $tree_O $tree_A $tree_B &&
+     read_tree_must_succeed -m $tree_O $tree_A $tree_B &&
      check_result"
 
 test_expect_success \
@@ -509,7 +510,7 @@ test_expect_success \
      cp .orig-A/NN NN &&
      echo extra >>NN &&
      git update-index --add NN &&
-     test_must_fail git read-tree -m $tree_O $tree_A $tree_B
+     read_tree_must_fail -m $tree_O $tree_A $tree_B
 "
 
 # #16
@@ -522,7 +523,7 @@ test_expect_success \
     echo E16 >F16 &&
     git update-index F16 &&
     tree1=`git write-tree` &&
-    git read-tree -m $tree0 $tree1 $tree1 $tree0 &&
+    read_tree_must_succeed -m $tree0 $tree1 $tree1 $tree0 &&
     git ls-files --stage'
 
 test_done
index 680d992f229cda9f1904240f593d66dc1cf3e32d..acaab07fac672e4b1b74161b58baf1f5e153840e 100755 (executable)
@@ -21,6 +21,7 @@ In the test, these paths are used:
         yomin   - not in H nor M
 '
 . ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-read-tree.sh
 
 read_tree_twoway () {
     git read-tree -m "$1" "$2" && git ls-files --stage
@@ -94,7 +95,7 @@ echo '+100644 X 0     yomin' >expected
 test_expect_success \
     '4 - carry forward local addition.' \
     'rm -f .git/index &&
-     git read-tree $treeH &&
+     read_tree_must_succeed $treeH &&
      git checkout-index -u -f -q -a &&
      git update-index --add yomin &&
      read_tree_twoway $treeH $treeM &&
@@ -106,7 +107,7 @@ test_expect_success \
 test_expect_success \
     '5 - carry forward local addition.' \
     'rm -f .git/index &&
-     git read-tree $treeH &&
+     read_tree_must_succeed $treeH &&
      git checkout-index -u -f -q -a &&
      echo yomin >yomin &&
      git update-index --add yomin &&
@@ -120,7 +121,7 @@ test_expect_success \
 test_expect_success \
     '6 - local addition already has the same.' \
     'rm -f .git/index &&
-     git read-tree $treeH &&
+     read_tree_must_succeed $treeH &&
      git checkout-index -u -f -q -a &&
      git update-index --add frotz &&
      read_tree_twoway $treeH $treeM &&
@@ -131,7 +132,7 @@ test_expect_success \
 test_expect_success \
     '7 - local addition already has the same.' \
     'rm -f .git/index &&
-     git read-tree $treeH &&
+     read_tree_must_succeed $treeH &&
      git checkout-index -u -f -q -a &&
      echo frotz >frotz &&
      git update-index --add frotz &&
@@ -144,7 +145,7 @@ test_expect_success \
 test_expect_success \
     '8 - conflicting addition.' \
     'rm -f .git/index &&
-     git read-tree $treeH &&
+     read_tree_must_succeed $treeH &&
      git checkout-index -u -f -q -a &&
      echo frotz frotz >frotz &&
      git update-index --add frotz &&
@@ -153,7 +154,7 @@ test_expect_success \
 test_expect_success \
     '9 - conflicting addition.' \
     'rm -f .git/index &&
-     git read-tree $treeH &&
+     read_tree_must_succeed $treeH &&
      git checkout-index -u -f -q -a &&
      echo frotz frotz >frotz &&
      git update-index --add frotz &&
@@ -163,7 +164,7 @@ test_expect_success \
 test_expect_success \
     '10 - path removed.' \
     'rm -f .git/index &&
-     git read-tree $treeH &&
+     read_tree_must_succeed $treeH &&
      git checkout-index -u -f -q -a &&
      echo rezrov >rezrov &&
      git update-index --add rezrov &&
@@ -174,7 +175,7 @@ test_expect_success \
 test_expect_success \
     '11 - dirty path removed.' \
     'rm -f .git/index &&
-     git read-tree $treeH &&
+     read_tree_must_succeed $treeH &&
      git checkout-index -u -f -q -a &&
      echo rezrov >rezrov &&
      git update-index --add rezrov &&
@@ -184,7 +185,7 @@ test_expect_success \
 test_expect_success \
     '12 - unmatching local changes being removed.' \
     'rm -f .git/index &&
-     git read-tree $treeH &&
+     read_tree_must_succeed $treeH &&
      git checkout-index -u -f -q -a &&
      echo rezrov rezrov >rezrov &&
      git update-index --add rezrov &&
@@ -193,7 +194,7 @@ test_expect_success \
 test_expect_success \
     '13 - unmatching local changes being removed.' \
     'rm -f .git/index &&
-     git read-tree $treeH &&
+     read_tree_must_succeed $treeH &&
      git checkout-index -u -f -q -a &&
      echo rezrov rezrov >rezrov &&
      git update-index --add rezrov &&
@@ -208,7 +209,7 @@ EOF
 test_expect_success \
     '14 - unchanged in two heads.' \
     'rm -f .git/index &&
-     git read-tree $treeH &&
+     read_tree_must_succeed $treeH &&
      git checkout-index -u -f -q -a &&
      echo nitfol nitfol >nitfol &&
      git update-index --add nitfol &&
@@ -221,7 +222,7 @@ test_expect_success \
 test_expect_success \
     '15 - unchanged in two heads.' \
     'rm -f .git/index &&
-     git read-tree $treeH &&
+     read_tree_must_succeed $treeH &&
      git checkout-index -u -f -q -a &&
      echo nitfol nitfol >nitfol &&
      git update-index --add nitfol &&
@@ -235,7 +236,7 @@ test_expect_success \
 test_expect_success \
     '16 - conflicting local change.' \
     'rm -f .git/index &&
-     git read-tree $treeH &&
+     read_tree_must_succeed $treeH &&
      git checkout-index -u -f -q -a &&
      echo bozbar bozbar >bozbar &&
      git update-index --add bozbar &&
@@ -244,7 +245,7 @@ test_expect_success \
 test_expect_success \
     '17 - conflicting local change.' \
     'rm -f .git/index &&
-     git read-tree $treeH &&
+     read_tree_must_succeed $treeH &&
      git checkout-index -u -f -q -a &&
      echo bozbar bozbar >bozbar &&
      git update-index --add bozbar &&
@@ -254,7 +255,7 @@ test_expect_success \
 test_expect_success \
     '18 - local change already having a good result.' \
     'rm -f .git/index &&
-     git read-tree $treeH &&
+     read_tree_must_succeed $treeH &&
      git checkout-index -u -f -q -a &&
      cat bozbar-new >bozbar &&
      git update-index --add bozbar &&
@@ -266,7 +267,7 @@ test_expect_success \
 test_expect_success \
     '19 - local change already having a good result, further modified.' \
     'rm -f .git/index &&
-     git read-tree $treeH &&
+     read_tree_must_succeed $treeH &&
      git checkout-index -u -f -q -a &&
      cat bozbar-new >bozbar &&
      git update-index --add bozbar &&
@@ -279,7 +280,7 @@ test_expect_success \
 test_expect_success \
     '20 - no local change, use new tree.' \
     'rm -f .git/index &&
-     git read-tree $treeH &&
+     read_tree_must_succeed $treeH &&
      git checkout-index -u -f -q -a &&
      cat bozbar-old >bozbar &&
      git update-index --add bozbar &&
@@ -291,7 +292,7 @@ test_expect_success \
 test_expect_success \
     '21 - no local change, dirty cache.' \
     'rm -f .git/index &&
-     git read-tree $treeH &&
+     read_tree_must_succeed $treeH &&
      git checkout-index -u -f -q -a &&
      cat bozbar-old >bozbar &&
      git update-index --add bozbar &&
@@ -302,7 +303,7 @@ test_expect_success \
 test_expect_success \
     '22 - local change cache updated.' \
     'rm -f .git/index &&
-     git read-tree $treeH &&
+     read_tree_must_succeed $treeH &&
      git checkout-index -u -f -q -a &&
      sed -e "s/such as/SUCH AS/" bozbar-old >bozbar &&
      git update-index --add bozbar &&
@@ -359,7 +360,7 @@ test_expect_success \
 
 test_expect_success \
     'a/b (untracked) vs a, plus c/d case test.' \
-    'test_must_fail git read-tree -u -m "$treeH" "$treeM" &&
+    'read_tree_u_must_fail -u -m "$treeH" "$treeM" &&
      git ls-files --stage &&
      test -f a/b'
 
@@ -386,7 +387,7 @@ test_expect_success \
 
 test_expect_success \
     'a/b vs a, plus c/d case test.' \
-    'git read-tree -u -m "$treeH" "$treeM" &&
+    'read_tree_u_must_succeed -u -m "$treeH" "$treeM" &&
      git ls-files --stage | tee >treeMcheck.out &&
      test_cmp treeM.out treeMcheck.out'
 
@@ -401,7 +402,7 @@ test_expect_success '-m references the correct modified tree' '
        echo a >file-a &&
        git add file-a &&
        git ls-tree $(git write-tree) file-a >expect &&
-       git read-tree -m HEAD initial-mod &&
+       read_tree_must_succeed -m HEAD initial-mod &&
        git ls-tree $(git write-tree) file-a >actual &&
        test_cmp expect actual
 '
index a4a17e001739a45fc7d53e76bd8bbb14426492f9..a847709a1354a824c90a13d2c9e9ab6d6bc69b6c 100755 (executable)
@@ -9,6 +9,7 @@ This is identical to t1001, but uses -u to update the work tree as well.
 
 '
 . ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-read-tree.sh
 
 compare_change () {
        sed >current \
@@ -56,8 +57,8 @@ test_expect_success \
 test_expect_success \
     '1, 2, 3 - no carry forward' \
     'rm -f .git/index nitfol bozbar rezrov frotz &&
-     git read-tree --reset -u $treeH &&
-     git read-tree -m -u $treeH $treeM &&
+     read_tree_u_must_succeed --reset -u $treeH &&
+     read_tree_u_must_succeed -m -u $treeH $treeM &&
      git ls-files --stage >1-3.out &&
      cmp M.out 1-3.out &&
      sum bozbar frotz nitfol >actual3.sum &&
@@ -69,11 +70,11 @@ test_expect_success \
 test_expect_success \
     '4 - carry forward local addition.' \
     'rm -f .git/index nitfol bozbar rezrov frotz &&
-     git read-tree --reset -u $treeH &&
+     read_tree_u_must_succeed --reset -u $treeH &&
      echo "+100644 X 0 yomin" >expected &&
      echo yomin >yomin &&
      git update-index --add yomin &&
-     git read-tree -m -u $treeH $treeM &&
+     read_tree_u_must_succeed -m -u $treeH $treeM &&
      git ls-files --stage >4.out || return 1
      git diff -U0 --no-index M.out 4.out >4diff.out
      compare_change 4diff.out expected &&
@@ -87,12 +88,12 @@ test_expect_success \
 test_expect_success \
     '5 - carry forward local addition.' \
     'rm -f .git/index nitfol bozbar rezrov frotz &&
-     git read-tree --reset -u $treeH &&
-     git read-tree -m -u $treeH &&
+     read_tree_u_must_succeed --reset -u $treeH &&
+     read_tree_u_must_succeed -m -u $treeH &&
      echo yomin >yomin &&
      git update-index --add yomin &&
      echo yomin yomin >yomin &&
-     git read-tree -m -u $treeH $treeM &&
+     read_tree_u_must_succeed -m -u $treeH $treeM &&
      git ls-files --stage >5.out || return 1
      git diff -U0 --no-index M.out 5.out >5diff.out
      compare_change 5diff.out expected &&
@@ -107,10 +108,10 @@ test_expect_success \
 test_expect_success \
     '6 - local addition already has the same.' \
     'rm -f .git/index nitfol bozbar rezrov frotz &&
-     git read-tree --reset -u $treeH &&
+     read_tree_u_must_succeed --reset -u $treeH &&
      echo frotz >frotz &&
      git update-index --add frotz &&
-     git read-tree -m -u $treeH $treeM &&
+     read_tree_u_must_succeed -m -u $treeH $treeM &&
      git ls-files --stage >6.out &&
      test_cmp M.out 6.out &&
      check_cache_at frotz clean &&
@@ -123,11 +124,11 @@ test_expect_success \
 test_expect_success \
     '7 - local addition already has the same.' \
     'rm -f .git/index nitfol bozbar rezrov frotz &&
-     git read-tree --reset -u $treeH &&
+     read_tree_u_must_succeed --reset -u $treeH &&
      echo frotz >frotz &&
      git update-index --add frotz &&
      echo frotz frotz >frotz &&
-     git read-tree -m -u $treeH $treeM &&
+     read_tree_u_must_succeed -m -u $treeH $treeM &&
      git ls-files --stage >7.out &&
      test_cmp M.out 7.out &&
      check_cache_at frotz dirty &&
@@ -141,27 +142,27 @@ test_expect_success \
 test_expect_success \
     '8 - conflicting addition.' \
     'rm -f .git/index nitfol bozbar rezrov frotz &&
-     git read-tree --reset -u $treeH &&
+     read_tree_u_must_succeed --reset -u $treeH &&
      echo frotz frotz >frotz &&
      git update-index --add frotz &&
-     if git read-tree -m -u $treeH $treeM; then false; else :; fi'
+     if read_tree_u_must_succeed -m -u $treeH $treeM; then false; else :; fi'
 
 test_expect_success \
     '9 - conflicting addition.' \
     'rm -f .git/index nitfol bozbar rezrov frotz &&
-     git read-tree --reset -u $treeH &&
+     read_tree_u_must_succeed --reset -u $treeH &&
      echo frotz frotz >frotz &&
      git update-index --add frotz &&
      echo frotz >frotz &&
-     if git read-tree -m -u $treeH $treeM; then false; else :; fi'
+     if read_tree_u_must_succeed -m -u $treeH $treeM; then false; else :; fi'
 
 test_expect_success \
     '10 - path removed.' \
     'rm -f .git/index nitfol bozbar rezrov frotz &&
-     git read-tree --reset -u $treeH &&
+     read_tree_u_must_succeed --reset -u $treeH &&
      echo rezrov >rezrov &&
      git update-index --add rezrov &&
-     git read-tree -m -u $treeH $treeM &&
+     read_tree_u_must_succeed -m -u $treeH $treeM &&
      git ls-files --stage >10.out &&
      cmp M.out 10.out &&
      sum bozbar frotz nitfol >actual10.sum &&
@@ -170,28 +171,28 @@ test_expect_success \
 test_expect_success \
     '11 - dirty path removed.' \
     'rm -f .git/index nitfol bozbar rezrov frotz &&
-     git read-tree --reset -u $treeH &&
+     read_tree_u_must_succeed --reset -u $treeH &&
      echo rezrov >rezrov &&
      git update-index --add rezrov &&
      echo rezrov rezrov >rezrov &&
-     if git read-tree -m -u $treeH $treeM; then false; else :; fi'
+     if read_tree_u_must_succeed -m -u $treeH $treeM; then false; else :; fi'
 
 test_expect_success \
     '12 - unmatching local changes being removed.' \
     'rm -f .git/index nitfol bozbar rezrov frotz &&
-     git read-tree --reset -u $treeH &&
+     read_tree_u_must_succeed --reset -u $treeH &&
      echo rezrov rezrov >rezrov &&
      git update-index --add rezrov &&
-     if git read-tree -m -u $treeH $treeM; then false; else :; fi'
+     if read_tree_u_must_succeed -m -u $treeH $treeM; then false; else :; fi'
 
 test_expect_success \
     '13 - unmatching local changes being removed.' \
     'rm -f .git/index nitfol bozbar rezrov frotz &&
-     git read-tree --reset -u $treeH &&
+     read_tree_u_must_succeed --reset -u $treeH &&
      echo rezrov rezrov >rezrov &&
      git update-index --add rezrov &&
      echo rezrov >rezrov &&
-     if git read-tree -m -u $treeH $treeM; then false; else :; fi'
+     if read_tree_u_must_succeed -m -u $treeH $treeM; then false; else :; fi'
 
 cat >expected <<EOF
 -100644 X 0    nitfol
@@ -201,10 +202,10 @@ EOF
 test_expect_success \
     '14 - unchanged in two heads.' \
     'rm -f .git/index nitfol bozbar rezrov frotz &&
-     git read-tree --reset -u $treeH &&
+     read_tree_u_must_succeed --reset -u $treeH &&
      echo nitfol nitfol >nitfol &&
      git update-index --add nitfol &&
-     git read-tree -m -u $treeH $treeM &&
+     read_tree_u_must_succeed -m -u $treeH $treeM &&
      git ls-files --stage >14.out &&
      test_must_fail git diff -U0 --no-index M.out 14.out >14diff.out &&
      compare_change 14diff.out expected &&
@@ -221,11 +222,11 @@ test_expect_success \
 test_expect_success \
     '15 - unchanged in two heads.' \
     'rm -f .git/index nitfol bozbar rezrov frotz &&
-     git read-tree --reset -u $treeH &&
+     read_tree_u_must_succeed --reset -u $treeH &&
      echo nitfol nitfol >nitfol &&
      git update-index --add nitfol &&
      echo nitfol nitfol nitfol >nitfol &&
-     git read-tree -m -u $treeH $treeM &&
+     read_tree_u_must_succeed -m -u $treeH $treeM &&
      git ls-files --stage >15.out &&
      test_must_fail git diff -U0 --no-index M.out 15.out >15diff.out &&
      compare_change 15diff.out expected &&
@@ -242,27 +243,27 @@ test_expect_success \
 test_expect_success \
     '16 - conflicting local change.' \
     'rm -f .git/index nitfol bozbar rezrov frotz &&
-     git read-tree --reset -u $treeH &&
+     read_tree_u_must_succeed --reset -u $treeH &&
      echo bozbar bozbar >bozbar &&
      git update-index --add bozbar &&
-     if git read-tree -m -u $treeH $treeM; then false; else :; fi'
+     if read_tree_u_must_succeed -m -u $treeH $treeM; then false; else :; fi'
 
 test_expect_success \
     '17 - conflicting local change.' \
     'rm -f .git/index nitfol bozbar rezrov frotz &&
-     git read-tree --reset -u $treeH &&
+     read_tree_u_must_succeed --reset -u $treeH &&
      echo bozbar bozbar >bozbar &&
      git update-index --add bozbar &&
      echo bozbar bozbar bozbar >bozbar &&
-     if git read-tree -m -u $treeH $treeM; then false; else :; fi'
+     if read_tree_u_must_succeed -m -u $treeH $treeM; then false; else :; fi'
 
 test_expect_success \
     '18 - local change already having a good result.' \
     'rm -f .git/index nitfol bozbar rezrov frotz &&
-     git read-tree --reset -u $treeH &&
+     read_tree_u_must_succeed --reset -u $treeH &&
      echo gnusto >bozbar &&
      git update-index --add bozbar &&
-     git read-tree -m -u $treeH $treeM &&
+     read_tree_u_must_succeed -m -u $treeH $treeM &&
      git ls-files --stage >18.out &&
      test_cmp M.out 18.out &&
      check_cache_at bozbar clean &&
@@ -272,11 +273,11 @@ test_expect_success \
 test_expect_success \
     '19 - local change already having a good result, further modified.' \
     'rm -f .git/index nitfol bozbar rezrov frotz &&
-     git read-tree --reset -u $treeH &&
+     read_tree_u_must_succeed --reset -u $treeH &&
      echo gnusto >bozbar &&
      git update-index --add bozbar &&
      echo gnusto gnusto >bozbar &&
-     git read-tree -m -u $treeH $treeM &&
+     read_tree_u_must_succeed -m -u $treeH $treeM &&
      git ls-files --stage >19.out &&
      test_cmp M.out 19.out &&
      check_cache_at bozbar dirty &&
@@ -292,10 +293,10 @@ test_expect_success \
 test_expect_success \
     '20 - no local change, use new tree.' \
     'rm -f .git/index nitfol bozbar rezrov frotz &&
-     git read-tree --reset -u $treeH &&
+     read_tree_u_must_succeed --reset -u $treeH &&
      echo bozbar >bozbar &&
      git update-index --add bozbar &&
-     git read-tree -m -u $treeH $treeM &&
+     read_tree_u_must_succeed -m -u $treeH $treeM &&
      git ls-files --stage >20.out &&
      test_cmp M.out 20.out &&
      check_cache_at bozbar clean &&
@@ -305,11 +306,11 @@ test_expect_success \
 test_expect_success \
     '21 - no local change, dirty cache.' \
     'rm -f .git/index nitfol bozbar rezrov frotz &&
-     git read-tree --reset -u $treeH &&
+     read_tree_u_must_succeed --reset -u $treeH &&
      echo bozbar >bozbar &&
      git update-index --add bozbar &&
      echo gnusto gnusto >bozbar &&
-     if git read-tree -m -u $treeH $treeM; then false; else :; fi'
+     if read_tree_u_must_succeed -m -u $treeH $treeM; then false; else :; fi'
 
 # Also make sure we did not break DF vs DF/DF case.
 test_expect_success \
@@ -336,7 +337,7 @@ test_expect_success \
      rm -fr DF &&
      echo DF >DF &&
      git update-index --add DF &&
-     git read-tree -m -u $treeDF $treeDFDF &&
+     read_tree_u_must_succeed -m -u $treeDF $treeDFDF &&
      git ls-files --stage >DFDFcheck.out &&
      test_cmp DFDF.out DFDFcheck.out &&
      check_cache_at DF/DF clean'
index eb8e3d447613ce7383f53a7a09039c01d72f35ef..b3ae7d52c6c76895434c1d2e8dfb9a31b12ebc62 100755 (executable)
@@ -3,6 +3,7 @@
 test_description='read-tree -m -u checks working tree files'
 
 . ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-read-tree.sh
 
 # two-tree test
 
@@ -29,7 +30,7 @@ test_expect_success 'two-way not clobbering' '
 
        echo >file2 master creates untracked file2 &&
        echo >subdir/file2 master creates untracked subdir/file2 &&
-       if err=`git read-tree -m -u master side 2>&1`
+       if err=`read_tree_u_must_succeed -m -u master side 2>&1`
        then
                echo should have complained
                false
@@ -42,7 +43,7 @@ echo file2 >.gitignore
 
 test_expect_success 'two-way with incorrect --exclude-per-directory (1)' '
 
-       if err=`git read-tree -m --exclude-per-directory=.gitignore master side 2>&1`
+       if err=`read_tree_u_must_succeed -m --exclude-per-directory=.gitignore master side 2>&1`
        then
                echo should have complained
                false
@@ -53,7 +54,7 @@ test_expect_success 'two-way with incorrect --exclude-per-directory (1)' '
 
 test_expect_success 'two-way with incorrect --exclude-per-directory (2)' '
 
-       if err=`git read-tree -m -u --exclude-per-directory=foo --exclude-per-directory=.gitignore master side 2>&1`
+       if err=`read_tree_u_must_succeed -m -u --exclude-per-directory=foo --exclude-per-directory=.gitignore master side 2>&1`
        then
                echo should have complained
                false
@@ -64,7 +65,7 @@ test_expect_success 'two-way with incorrect --exclude-per-directory (2)' '
 
 test_expect_success 'two-way clobbering a ignored file' '
 
-       git read-tree -m -u --exclude-per-directory=.gitignore master side
+       read_tree_u_must_succeed -m -u --exclude-per-directory=.gitignore master side
 '
 
 rm -f .gitignore
@@ -84,7 +85,7 @@ test_expect_success 'three-way not complaining on an untracked path in both' '
        echo >file2 file two is untracked on the master side &&
        echo >subdir/file2 file two is untracked on the master side &&
 
-       git read-tree -m -u branch-point master side
+       read_tree_u_must_succeed -m -u branch-point master side
 '
 
 test_expect_success 'three-way not clobbering a working tree file' '
@@ -94,7 +95,7 @@ test_expect_success 'three-way not clobbering a working tree file' '
        git checkout master &&
        echo >file3 file three created in master, untracked &&
        echo >subdir/file3 file three created in master, untracked &&
-       if err=`git read-tree -m -u branch-point master side 2>&1`
+       if err=`read_tree_u_must_succeed -m -u branch-point master side 2>&1`
        then
                echo should have complained
                false
@@ -113,7 +114,7 @@ test_expect_success 'three-way not complaining on an untracked file' '
        echo >file3 file three created in master, untracked &&
        echo >subdir/file3 file three created in master, untracked &&
 
-       git read-tree -m -u --exclude-per-directory=.gitignore branch-point master side
+       read_tree_u_must_succeed -m -u --exclude-per-directory=.gitignore branch-point master side
 '
 
 test_expect_success '3-way not overwriting local changes (setup)' '
@@ -137,7 +138,7 @@ test_expect_success '3-way not overwriting local changes (our side)' '
        git reset --hard &&
 
        echo >>file1 "local changes" &&
-       git read-tree -m -u branch-point side-a side-b &&
+       read_tree_u_must_succeed -m -u branch-point side-a side-b &&
        grep "new line to be kept" file1 &&
        grep "local changes" file1
 
@@ -151,7 +152,7 @@ test_expect_success '3-way not overwriting local changes (their side)' '
        git reset --hard &&
 
        echo >>file2 "local changes" &&
-       test_must_fail git read-tree -m -u branch-point side-a side-b &&
+       read_tree_u_must_fail -m -u branch-point side-a side-b &&
        ! grep "new line to be kept" file2 &&
        grep "local changes" file2
 
@@ -173,7 +174,7 @@ test_expect_success SYMLINKS 'funny symlink in work tree' '
        git add a/b &&
        git commit -m "we add a/b" &&
 
-       git read-tree -m -u sym-a sym-a sym-b
+       read_tree_u_must_succeed -m -u sym-a sym-a sym-b
 
 '
 
@@ -209,7 +210,7 @@ test_expect_success 'D/F setup' '
 test_expect_success 'D/F' '
 
        git checkout side-b &&
-       git read-tree -m -u branch-point side-b side-a &&
+       read_tree_u_must_succeed -m -u branch-point side-b side-a &&
        git ls-files -u >actual &&
        (
                a=$(git rev-parse branch-point:subdir/file2)
index 849911683a799981a565416a88fe9092b3727853..f53de79e565afcbe53a016a56a59b27831a3ce42 100755 (executable)
@@ -3,6 +3,7 @@
 test_description='read-tree -u --reset'
 
 . ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-read-tree.sh
 
 # two-tree test
 
@@ -22,13 +23,13 @@ test_expect_success 'setup' '
 '
 
 test_expect_success 'reset should work' '
-  git read-tree -u --reset HEAD^ &&
+  read_tree_u_must_succeed -u --reset HEAD^ &&
   git ls-files >actual &&
   test_cmp expect actual
 '
 
 test_expect_success 'reset should remove remnants from a failed merge' '
-  git read-tree --reset -u HEAD &&
+  read_tree_u_must_succeed --reset -u HEAD &&
   git ls-files -s >expect &&
   sha1=$(git rev-parse :new) &&
   (
@@ -37,13 +38,13 @@ test_expect_success 'reset should remove remnants from a failed merge' '
   ) | git update-index --index-info &&
   >old &&
   git ls-files -s &&
-  git read-tree --reset -u HEAD &&
+  read_tree_u_must_succeed --reset -u HEAD &&
   git ls-files -s >actual &&
   ! test -f old
 '
 
 test_expect_success 'Porcelain reset should remove remnants too' '
-  git read-tree --reset -u HEAD &&
+  read_tree_u_must_succeed --reset -u HEAD &&
   git ls-files -s >expect &&
   sha1=$(git rev-parse :new) &&
   (
@@ -58,7 +59,7 @@ test_expect_success 'Porcelain reset should remove remnants too' '
 '
 
 test_expect_success 'Porcelain checkout -f should remove remnants too' '
-  git read-tree --reset -u HEAD &&
+  read_tree_u_must_succeed --reset -u HEAD &&
   git ls-files -s >expect &&
   sha1=$(git rev-parse :new) &&
   (
@@ -73,7 +74,7 @@ test_expect_success 'Porcelain checkout -f should remove remnants too' '
 '
 
 test_expect_success 'Porcelain checkout -f HEAD should remove remnants too' '
-  git read-tree --reset -u HEAD &&
+  read_tree_u_must_succeed --reset -u HEAD &&
   git ls-files -s >expect &&
   sha1=$(git rev-parse :new) &&
   (
index f9e00285db662504b200fb25f0ed06c796346c52..4c50ed955eb18da1a60dd9d635f3c32d081cfb76 100755 (executable)
@@ -3,6 +3,7 @@
 test_description='test multi-tree read-tree without merging'
 
 . ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-read-tree.sh
 
 test_expect_success setup '
        echo one >a &&
@@ -21,7 +22,7 @@ test_expect_success setup '
 '
 
 test_expect_success 'multi-read' '
-       git read-tree initial master side &&
+       read_tree_must_succeed initial master side &&
        (echo a; echo b/c) >expect &&
        git ls-files >actual &&
        test_cmp expect actual
index 20a50eba5b77e23b6e540402b4eabb311feabd0d..018c3546b6f05228a20dfb5ec9364d0a1575e4ff 100755 (executable)
@@ -12,6 +12,7 @@ test_description='sparse checkout tests
 '
 
 . ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-read-tree.sh
 
 test_expect_success 'setup' '
        cat >expected <<-\EOF &&
@@ -43,7 +44,7 @@ test_expect_success 'setup' '
 '
 
 test_expect_success 'read-tree without .git/info/sparse-checkout' '
-       git read-tree -m -u HEAD &&
+       read_tree_u_must_succeed -m -u HEAD &&
        git ls-files --stage >result &&
        test_cmp expected result &&
        git ls-files -t >result &&
@@ -52,7 +53,7 @@ test_expect_success 'read-tree without .git/info/sparse-checkout' '
 
 test_expect_success 'read-tree with .git/info/sparse-checkout but disabled' '
        echo >.git/info/sparse-checkout &&
-       git read-tree -m -u HEAD &&
+       read_tree_u_must_succeed -m -u HEAD &&
        git ls-files -t >result &&
        test_cmp expected.swt result &&
        test -f init.t &&
@@ -62,7 +63,7 @@ test_expect_success 'read-tree with .git/info/sparse-checkout but disabled' '
 test_expect_success 'read-tree --no-sparse-checkout with empty .git/info/sparse-checkout and enabled' '
        git config core.sparsecheckout true &&
        echo >.git/info/sparse-checkout &&
-       git read-tree --no-sparse-checkout -m -u HEAD &&
+       read_tree_u_must_succeed --no-sparse-checkout -m -u HEAD &&
        git ls-files -t >result &&
        test_cmp expected.swt result &&
        test -f init.t &&
@@ -72,7 +73,7 @@ test_expect_success 'read-tree --no-sparse-checkout with empty .git/info/sparse-
 test_expect_success 'read-tree with empty .git/info/sparse-checkout' '
        git config core.sparsecheckout true &&
        echo >.git/info/sparse-checkout &&
-       test_must_fail git read-tree -m -u HEAD &&
+       read_tree_u_must_fail -m -u HEAD &&
        git ls-files --stage >result &&
        test_cmp expected result &&
        git ls-files -t >result &&
@@ -90,7 +91,7 @@ test_expect_success 'match directories with trailing slash' '
        EOF
 
        echo sub/ > .git/info/sparse-checkout &&
-       git read-tree -m -u HEAD &&
+       read_tree_u_must_succeed -m -u HEAD &&
        git ls-files -t > result &&
        test_cmp expected.swt-noinit result &&
        test ! -f init.t &&
@@ -99,7 +100,7 @@ test_expect_success 'match directories with trailing slash' '
 
 test_expect_success 'match directories without trailing slash' '
        echo sub >.git/info/sparse-checkout &&
-       git read-tree -m -u HEAD &&
+       read_tree_u_must_succeed -m -u HEAD &&
        git ls-files -t >result &&
        test_cmp expected.swt-noinit result &&
        test ! -f init.t &&
@@ -149,7 +150,7 @@ EOF
 
 test_expect_success 'match directory pattern' '
        echo "s?b" >.git/info/sparse-checkout &&
-       git read-tree -m -u HEAD &&
+       read_tree_u_must_succeed -m -u HEAD &&
        git ls-files -t >result &&
        test_cmp expected.swt-noinit result &&
        test ! -f init.t &&
@@ -165,7 +166,7 @@ test_expect_success 'checkout area changes' '
        EOF
 
        echo init.t >.git/info/sparse-checkout &&
-       git read-tree -m -u HEAD &&
+       read_tree_u_must_succeed -m -u HEAD &&
        git ls-files -t >result &&
        test_cmp expected.swt-nosub result &&
        test -f init.t &&
@@ -175,7 +176,7 @@ test_expect_success 'checkout area changes' '
 test_expect_success 'read-tree updates worktree, absent case' '
        echo sub/added >.git/info/sparse-checkout &&
        git checkout -f top &&
-       git read-tree -m -u HEAD^ &&
+       read_tree_u_must_succeed -m -u HEAD^ &&
        test ! -f init.t
 '
 
@@ -183,7 +184,7 @@ test_expect_success 'read-tree updates worktree, dirty case' '
        echo sub/added >.git/info/sparse-checkout &&
        git checkout -f top &&
        echo dirty >init.t &&
-       git read-tree -m -u HEAD^ &&
+       read_tree_u_must_succeed -m -u HEAD^ &&
        grep -q dirty init.t &&
        rm init.t
 '
@@ -192,14 +193,14 @@ test_expect_success 'read-tree removes worktree, dirty case' '
        echo init.t >.git/info/sparse-checkout &&
        git checkout -f top &&
        echo dirty >added &&
-       git read-tree -m -u HEAD^ &&
+       read_tree_u_must_succeed -m -u HEAD^ &&
        grep -q dirty added
 '
 
 test_expect_success 'read-tree adds to worktree, absent case' '
        echo init.t >.git/info/sparse-checkout &&
        git checkout -f removed &&
-       git read-tree -u -m HEAD^ &&
+       read_tree_u_must_succeed -u -m HEAD^ &&
        test ! -f sub/added
 '
 
@@ -208,7 +209,7 @@ test_expect_success 'read-tree adds to worktree, dirty case' '
        git checkout -f removed &&
        mkdir sub &&
        echo dirty >sub/added &&
-       git read-tree -u -m HEAD^ &&
+       read_tree_u_must_succeed -u -m HEAD^ &&
        grep -q dirty sub/added
 '
 
index 9811d467da5a54eefd68ecfad4c68b67e5b3734f..a6a04b6b90d290a445ed7c33e5928a9a168ed28b 100755 (executable)
@@ -3,6 +3,7 @@
 test_description='read-tree D/F conflict corner cases'
 
 . ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-read-tree.sh
 
 maketree () {
        (
@@ -53,7 +54,7 @@ test_expect_success setup '
 
 test_expect_success '3-way (1)' '
        settree A-000 &&
-       git read-tree -m -u O-000 A-000 B-000 &&
+       read_tree_u_must_succeed -m -u O-000 A-000 B-000 &&
        checkindex <<-EOF
        3 a/b
        0 a/b-2/c/d
@@ -65,7 +66,7 @@ test_expect_success '3-way (1)' '
 
 test_expect_success '3-way (2)' '
        settree A-001 &&
-       git read-tree -m -u O-000 A-001 B-000 &&
+       read_tree_u_must_succeed -m -u O-000 A-001 B-000 &&
        checkindex <<-EOF
        3 a/b
        0 a/b-2/c/d
@@ -78,7 +79,7 @@ test_expect_success '3-way (2)' '
 
 test_expect_success '3-way (3)' '
        settree A-010 &&
-       git read-tree -m -u O-010 A-010 B-010 &&
+       read_tree_u_must_succeed -m -u O-010 A-010 B-010 &&
        checkindex <<-EOF
        2 t
        1 t-0
@@ -92,7 +93,7 @@ test_expect_success '3-way (3)' '
 
 test_expect_success '2-way (1)' '
        settree O-020 &&
-       git read-tree -m -u O-020 A-020 &&
+       read_tree_u_must_succeed -m -u O-020 A-020 &&
        checkindex <<-EOF
        0 ds/dma/ioat/Makefile
        0 ds/dma/ioat/registers.h
index ddc3921ac6a009dfc706cd19ad94f2b29af4b1cc..f6a44c9ee07329c8909662d2a807acf0dd6a8e43 100755 (executable)
@@ -7,6 +7,7 @@ test_description='Try various core-level commands in subdirectory.
 '
 
 . ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-read-tree.sh
 
 test_expect_success setup '
        long="a b c d e f g h i j k l m n o p q r s t u v w x y z" &&
@@ -98,13 +99,13 @@ test_expect_success 'checkout-index' '
 test_expect_success 'read-tree' '
        rm -f one dir/two &&
        tree=`git write-tree` &&
-       git read-tree --reset -u "$tree" &&
+       read_tree_u_must_succeed --reset -u "$tree" &&
        cmp one original.one &&
        cmp dir/two original.two &&
        (
                cd dir &&
                rm -f two &&
-               git read-tree --reset -u "$tree" &&
+               read_tree_u_must_succeed --reset -u "$tree" &&
                cmp two ../original.two &&
                cmp ../one ../original.one
        )
diff --git a/t/t1050-large.sh b/t/t1050-large.sh
new file mode 100755 (executable)
index 0000000..deba111
--- /dev/null
@@ -0,0 +1,27 @@
+#!/bin/sh
+# Copyright (c) 2011, Google Inc.
+
+test_description='adding and checking out large blobs'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+       git config core.bigfilethreshold 200k &&
+       echo X | dd of=large bs=1k seek=2000
+'
+
+test_expect_success 'add a large file' '
+       git add large &&
+       # make sure we got a packfile and no loose objects
+       test -f .git/objects/pack/pack-*.pack &&
+       test ! -f .git/objects/??/??????????????????????????????????????
+'
+
+test_expect_success 'checkout a large file' '
+       large=$(git rev-parse :large) &&
+       git update-index --add --cacheinfo 100644 $large another &&
+       git checkout another &&
+       cmp large another ;# this must not be test_cmp
+'
+
+test_done
index 53fb8228cf18e2b58f3ea63e98a0220fa0fd39f2..3db56267ee89e1b585a548f7bee13386e47395f4 100755 (executable)
@@ -897,4 +897,11 @@ test_expect_success 'key sanity-checking' '
        git config foo."ba =z".bar false
 '
 
+test_expect_success 'git -c works with aliases of builtins' '
+       git config alias.checkconfig "-c foo.check=bar config foo.check" &&
+       echo bar >expect &&
+       git checkconfig >actual &&
+       test_cmp expect actual
+'
+
 test_done
index 28e17c8920cbe9f2b13ff5cc30939eab8171bae2..16de05aff941c73b3e5f5371bd4739959f679efa 100755 (executable)
@@ -435,6 +435,81 @@ test_expect_success 'removing non-existing note should not create new commit' '
        test_cmp before_commit after_commit
 '
 
+test_expect_success 'removing more than one' '
+       before=$(git rev-parse --verify refs/notes/commits) &&
+       test_when_finished "git update-ref refs/notes/commits $before" &&
+
+       # We have only two -- add another and make sure it stays
+       git notes add -m "extra" &&
+       git notes list HEAD >after-removal-expect &&
+       git notes remove HEAD^^ HEAD^^^ &&
+       git notes list | sed -e "s/ .*//" >actual &&
+       test_cmp after-removal-expect actual
+'
+
+test_expect_success 'removing is atomic' '
+       before=$(git rev-parse --verify refs/notes/commits) &&
+       test_when_finished "git update-ref refs/notes/commits $before" &&
+       test_must_fail git notes remove HEAD^^ HEAD^^^ HEAD^ &&
+       after=$(git rev-parse --verify refs/notes/commits) &&
+       test "$before" = "$after"
+'
+
+test_expect_success 'removing with --ignore-missing' '
+       before=$(git rev-parse --verify refs/notes/commits) &&
+       test_when_finished "git update-ref refs/notes/commits $before" &&
+
+       # We have only two -- add another and make sure it stays
+       git notes add -m "extra" &&
+       git notes list HEAD >after-removal-expect &&
+       git notes remove --ignore-missing HEAD^^ HEAD^^^ HEAD^ &&
+       git notes list | sed -e "s/ .*//" >actual &&
+       test_cmp after-removal-expect actual
+'
+
+test_expect_success 'removing with --ignore-missing but bogus ref' '
+       before=$(git rev-parse --verify refs/notes/commits) &&
+       test_when_finished "git update-ref refs/notes/commits $before" &&
+       test_must_fail git notes remove --ignore-missing HEAD^^ HEAD^^^ NO-SUCH-COMMIT &&
+       after=$(git rev-parse --verify refs/notes/commits) &&
+       test "$before" = "$after"
+'
+
+test_expect_success 'remove reads from --stdin' '
+       before=$(git rev-parse --verify refs/notes/commits) &&
+       test_when_finished "git update-ref refs/notes/commits $before" &&
+
+       # We have only two -- add another and make sure it stays
+       git notes add -m "extra" &&
+       git notes list HEAD >after-removal-expect &&
+       git rev-parse HEAD^^ HEAD^^^ >input &&
+       git notes remove --stdin <input &&
+       git notes list | sed -e "s/ .*//" >actual &&
+       test_cmp after-removal-expect actual
+'
+
+test_expect_success 'remove --stdin is also atomic' '
+       before=$(git rev-parse --verify refs/notes/commits) &&
+       test_when_finished "git update-ref refs/notes/commits $before" &&
+       git rev-parse HEAD^^ HEAD^^^ HEAD^ >input &&
+       test_must_fail git notes remove --stdin <input &&
+       after=$(git rev-parse --verify refs/notes/commits) &&
+       test "$before" = "$after"
+'
+
+test_expect_success 'removing with --stdin --ignore-missing' '
+       before=$(git rev-parse --verify refs/notes/commits) &&
+       test_when_finished "git update-ref refs/notes/commits $before" &&
+
+       # We have only two -- add another and make sure it stays
+       git notes add -m "extra" &&
+       git notes list HEAD >after-removal-expect &&
+       git rev-parse HEAD^^ HEAD^^^ HEAD^ >input &&
+       git notes remove --ignore-missing --stdin <input &&
+       git notes list | sed -e "s/ .*//" >actual &&
+       test_cmp after-removal-expect actual
+'
+
 test_expect_success 'list notes with "git notes list"' '
        git notes list > output &&
        test_cmp expect output
index 7d8147bb93df482cf7e11338ac96acdf43cf551e..47c8371c7edfe7945ded380bb6f96a00d26c04eb 100755 (executable)
@@ -317,7 +317,7 @@ test_expect_success '--continue tries to commit' '
 '
 
 test_expect_success 'verbose flag is heeded, even after --continue' '
-       git reset --hard HEAD@{1} &&
+       git reset --hard master@{1} &&
        test_tick &&
        test_must_fail git rebase -v -i --onto new-branch1 HEAD^ &&
        echo resolved > file1 &&
diff --git a/t/t3703-add-magic-pathspec.sh b/t/t3703-add-magic-pathspec.sh
new file mode 100755 (executable)
index 0000000..5115de7
--- /dev/null
@@ -0,0 +1,58 @@
+#!/bin/sh
+
+test_description='magic pathspec tests using git-add'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+       mkdir sub anothersub &&
+       : >sub/foo &&
+       : >anothersub/foo
+'
+
+test_expect_success 'add :/' "
+       cat >expected <<-EOF &&
+       add 'anothersub/foo'
+       add 'expected'
+       add 'sub/actual'
+       add 'sub/foo'
+       EOF
+       (cd sub && git add -n :/ >actual) &&
+       test_cmp expected sub/actual
+"
+
+cat >expected <<EOF
+add 'anothersub/foo'
+EOF
+
+test_expect_success 'add :/anothersub' '
+       (cd sub && git add -n :/anothersub >actual) &&
+       test_cmp expected sub/actual
+'
+
+test_expect_success 'add :/non-existent' '
+       (cd sub && test_must_fail git add -n :/non-existent)
+'
+
+cat >expected <<EOF
+add 'sub/foo'
+EOF
+
+if mkdir ":" 2>/dev/null
+then
+       test_set_prereq COLON_DIR
+fi
+
+test_expect_success COLON_DIR 'a file with the same (long) magic name exists' '
+       : >":(icase)ha" &&
+       test_must_fail git add -n ":(icase)ha" &&
+       git add -n "./:(icase)ha"
+'
+
+test_expect_success COLON_DIR 'a file with the same (short) magic name exists' '
+       : >":/bar" &&
+       test_must_fail git add -n :/bar &&
+       git add -n "./:/bar"
+'
+
+test_done
index 5c725406422dcedb6c6d5e5d61cfc80f87d3595c..7197aae1ed10add49aa72748188fecb4c2ef90a4 100755 (executable)
@@ -536,7 +536,7 @@ test_expect_success 'stash pop - fail early if specified stash is not a stash re
        git reset --hard HEAD
 '
 
-test_expect_success 'ref with non-existant reflog' '
+test_expect_success 'ref with non-existent reflog' '
        git stash clear &&
        echo bar5 > file &&
        echo bar6 > file2 &&
index 045cee312cdeb515869eeeda3f7b167fb5b91504..92248d24c48a65ab89a5b0ad255357f6034e6ad4 100755 (executable)
@@ -851,4 +851,22 @@ test_expect_success 'subject lines do not have 822 atom-quoting' '
        test_cmp expect actual
 '
 
+cat >expect <<'EOF'
+Subject: [PREFIX 1/1] header with . in it
+EOF
+test_expect_success 'subject prefixes have space prepended' '
+       git format-patch -n -1 --stdout --subject-prefix=PREFIX >patch &&
+       grep ^Subject: patch >actual &&
+       test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+Subject: [1/1] header with . in it
+EOF
+test_expect_success 'empty subject prefix does not have extra space' '
+       git format-patch -n -1 --stdout --subject-prefix= >patch &&
+       grep ^Subject: patch >actual &&
+       test_cmp expect actual
+'
+
 test_done
index 364693062399228cd6a18b52a21db173519d8e94..b68c56b68c0f80a0d96b513733bc5fb17db6fed4 100755 (executable)
@@ -9,8 +9,7 @@ test_description='Test custom diff function name patterns'
 
 LF='
 '
-
-cat > Beer.java << EOF
+cat >Beer.java <<\EOF
 public class Beer
 {
        int special;
@@ -29,61 +28,163 @@ public class Beer
        }
 }
 EOF
+sed 's/beer\\/beer,\\/' <Beer.java >Beer-correct.java
+cat >Beer.perl <<\EOT
+package Beer;
+
+use strict;
+use warnings;
+use parent qw(Exporter);
+our @EXPORT_OK = qw(round finalround);
+
+sub other; # forward declaration
+
+# hello
+
+sub round {
+       my ($n) = @_;
+       print "$n bottles of beer on the wall ";
+       print "$n bottles of beer\n";
+       print "Take one down, pass it around, ";
+       $n = $n - 1;
+       print "$n bottles of beer on the wall.\n";
+}
+
+sub finalround
+{
+       print "Go to the store, buy some more\n";
+       print "99 bottles of beer on the wall.\n");
+}
+
+sub withheredocument {
+       print <<"EOF"
+decoy here-doc
+EOF
+       # some lines of context
+       # to pad it out
+       print "hello\n";
+}
+
+__END__
+
+=head1 NAME
+
+Beer - subroutine to output fragment of a drinking song
+
+=head1 SYNOPSIS
+
+       use Beer qw(round finalround);
+
+       sub song {
+               for (my $i = 99; $i > 0; $i--) {
+                       round $i;
+               }
+               finalround;
+       }
 
-sed 's/beer\\/beer,\\/' < Beer.java > Beer-correct.java
+       song;
 
-builtin_patterns="bibtex cpp csharp fortran html java objc pascal perl php python ruby tex"
-for p in $builtin_patterns
+=cut
+EOT
+sed -e '
+       s/hello/goodbye/
+       s/beer\\/beer,\\/
+       s/more\\/more,\\/
+       s/song;/song();/
+' <Beer.perl >Beer-correct.perl
+
+test_config () {
+       git config "$1" "$2" &&
+       test_when_finished "git config --unset $1"
+}
+
+test_expect_funcname () {
+       lang=${2-java}
+       test_expect_code 1 git diff --no-index -U1 \
+               "Beer.$lang" "Beer-correct.$lang" >diff &&
+       grep "^@@.*@@ $1" diff
+}
+
+for p in bibtex cpp csharp fortran html java objc pascal perl php python ruby tex
 do
        test_expect_success "builtin $p pattern compiles" '
-               echo "*.java diff=$p" > .gitattributes &&
-               ! { git diff --no-index Beer.java Beer-correct.java 2>&1 |
-                       grep "fatal" > /dev/null; }
+               echo "*.java diff=$p" >.gitattributes &&
+               test_expect_code 1 git diff --no-index \
+                       Beer.java Beer-correct.java 2>msg &&
+               ! grep fatal msg &&
+               ! grep error msg
        '
        test_expect_success "builtin $p wordRegex pattern compiles" '
-               ! { git diff --no-index --word-diff \
-                       Beer.java Beer-correct.java 2>&1 |
-                       grep "fatal" > /dev/null; }
+               echo "*.java diff=$p" >.gitattributes &&
+               test_expect_code 1 git diff --no-index --word-diff \
+                       Beer.java Beer-correct.java 2>msg &&
+               ! grep fatal msg &&
+               ! grep error msg
        '
 done
 
 test_expect_success 'default behaviour' '
        rm -f .gitattributes &&
-       git diff --no-index Beer.java Beer-correct.java |
-       grep "^@@.*@@ public class Beer"
+       test_expect_funcname "public class Beer\$"
+'
+
+test_expect_success 'set up .gitattributes declaring drivers to test' '
+       cat >.gitattributes <<-\EOF
+       *.java diff=java
+       *.perl diff=perl
+       EOF
 '
 
 test_expect_success 'preset java pattern' '
-       echo "*.java diff=java" >.gitattributes &&
-       git diff --no-index Beer.java Beer-correct.java |
-       grep "^@@.*@@ public static void main("
+       test_expect_funcname "public static void main("
 '
 
-git config diff.java.funcname '!static
-!String
-[^     ].*s.*'
+test_expect_success 'preset perl pattern' '
+       test_expect_funcname "sub round {\$" perl
+'
+
+test_expect_success 'perl pattern accepts K&R style brace placement, too' '
+       test_expect_funcname "sub finalround\$" perl
+'
+
+test_expect_success 'but is not distracted by end of <<here document' '
+       test_expect_funcname "sub withheredocument {\$" perl
+'
+
+test_expect_success 'perl pattern is not distracted by sub within POD' '
+       test_expect_funcname "=head" perl
+'
+
+test_expect_success 'perl pattern gets full line of POD header' '
+       test_expect_funcname "=head1 SYNOPSIS\$" perl
+'
+
+test_expect_success 'perl pattern is not distracted by forward declaration' '
+       test_expect_funcname "package Beer;\$" perl
+'
 
 test_expect_success 'custom pattern' '
-       git diff --no-index Beer.java Beer-correct.java |
-       grep "^@@.*@@ int special;$"
+       test_config diff.java.funcname "!static
+!String
+[^     ].*s.*" &&
+       test_expect_funcname "int special;\$"
 '
 
 test_expect_success 'last regexp must not be negated' '
-       git config diff.java.funcname "!static" &&
-       git diff --no-index Beer.java Beer-correct.java 2>&1 |
-       grep "fatal: Last expression must not be negated:"
+       test_config diff.java.funcname "!static" &&
+       test_expect_code 128 git diff --no-index Beer.java Beer-correct.java 2>msg &&
+       grep ": Last expression must not be negated:" msg
 '
 
 test_expect_success 'pattern which matches to end of line' '
-       git config diff.java.funcname "Beer$" &&
-       git diff --no-index Beer.java Beer-correct.java |
-       grep "^@@.*@@ Beer"
+       test_config diff.java.funcname "Beer\$" &&
+       test_expect_funcname "Beer\$"
 '
 
 test_expect_success 'alternation in pattern' '
-       git config diff.java.xfuncname "^[      ]*((public|static).*)$" &&
-       git diff --no-index Beer.java Beer-correct.java |
-       grep "^@@.*@@ public static void main("
+       test_config diff.java.funcname "Beer$" &&
+       test_config diff.java.xfuncname "^[     ]*((public|static).*)$" &&
+       test_expect_funcname "public static void main("
 '
 
 test_done
index 37aeab0d5c7415a54384c90708626f6a5807acee..c374aa4c1c60e9a12cf1ebf5587daf3656e4851a 100755 (executable)
@@ -307,4 +307,30 @@ test_language_driver python
 test_language_driver ruby
 test_language_driver tex
 
+test_expect_success 'word-diff with diff.sbe' '
+       cat >expect <<-\EOF &&
+       diff --git a/pre b/post
+       index a1a53b5..bc8fe6d 100644
+       --- a/pre
+       +++ b/post
+       @@ -1,3 +1,3 @@
+       a
+
+       [-b-]{+c+}
+       EOF
+       cat >pre <<-\EOF &&
+       a
+
+       b
+       EOF
+       cat >post <<-\EOF &&
+       a
+
+       c
+       EOF
+       test_when_finished "git config --unset diff.suppress-blank-empty" &&
+       git config diff.suppress-blank-empty true &&
+       word_diff --word-diff=plain
+'
+
 test_done
index abc49348b196cf0fec232b6f2399356e4fe324d5..3c728a3ebf9ce52e5c24c81525d5cb749cfb2957 100755 (executable)
@@ -67,4 +67,9 @@ test_expect_success 'diff-files --diff-filter --quiet' '
        test_must_fail git diff-files --diff-filter=M --quiet
 '
 
+test_expect_success 'diff-tree --diff-filter --quiet' '
+       git commit -a -m "worktree state" &&
+       test_must_fail git diff-tree --diff-filter=M --quiet HEAD^ HEAD
+'
+
 test_done
diff --git a/t/t4152-am-subjects.sh b/t/t4152-am-subjects.sh
new file mode 100755 (executable)
index 0000000..4c68245
--- /dev/null
@@ -0,0 +1,77 @@
+#!/bin/sh
+
+test_description='test subject preservation with format-patch | am'
+. ./test-lib.sh
+
+make_patches() {
+       type=$1
+       subject=$2
+       test_expect_success "create patches with $type subject" '
+               git reset --hard baseline &&
+               echo $type >file &&
+               git commit -a -m "$subject" &&
+               git format-patch -1 --stdout >$type.patch &&
+               git format-patch -1 --stdout -k >$type-k.patch
+       '
+}
+
+check_subject() {
+       git reset --hard baseline &&
+       git am $2 $1.patch &&
+       git log -1 --pretty=format:%B >actual &&
+       test_cmp expect actual
+}
+
+test_expect_success 'setup baseline commit' '
+       test_commit baseline file
+'
+
+SHORT_SUBJECT='short subject'
+make_patches short "$SHORT_SUBJECT"
+
+LONG_SUBJECT1='this is a long subject that is virtually guaranteed'
+LONG_SUBJECT2='to require wrapping via format-patch if it is all'
+LONG_SUBJECT3='going to appear on a single line'
+LONG_SUBJECT="$LONG_SUBJECT1 $LONG_SUBJECT2 $LONG_SUBJECT3"
+make_patches long "$LONG_SUBJECT"
+
+MULTILINE_SUBJECT="$LONG_SUBJECT1
+$LONG_SUBJECT2
+$LONG_SUBJECT3"
+make_patches multiline "$MULTILINE_SUBJECT"
+
+echo "$SHORT_SUBJECT" >expect
+test_expect_success 'short subject preserved (format-patch | am)' '
+       check_subject short
+'
+test_expect_success 'short subject preserved (format-patch -k | am)' '
+       check_subject short-k
+'
+test_expect_success 'short subject preserved (format-patch -k | am -k)' '
+       check_subject short-k -k
+'
+
+echo "$LONG_SUBJECT" >expect
+test_expect_success 'long subject preserved (format-patch | am)' '
+       check_subject long
+'
+test_expect_success 'long subject preserved (format-patch -k | am)' '
+       check_subject long-k
+'
+test_expect_success 'long subject preserved (format-patch -k | am -k)' '
+       check_subject long-k -k
+'
+
+echo "$LONG_SUBJECT" >expect
+test_expect_success 'multiline subject unwrapped (format-patch | am)' '
+       check_subject multiline
+'
+test_expect_success 'multiline subject unwrapped (format-patch -k | am)' '
+       check_subject multiline-k
+'
+echo "$MULTILINE_SUBJECT" >expect
+test_expect_success 'multiline subject preserved (format-patch -k | am -k)' '
+       check_subject multiline-k -k
+'
+
+test_done
index 2fcc31a6f3d346e533b697eecf853e219d174cd0..983e34bec67a4cc4ba1182332e495377cc879ac9 100755 (executable)
@@ -448,6 +448,59 @@ test_expect_success 'log.decorate configuration' '
        git log --oneline --decorate >actual &&
        test_cmp expect.short actual
 
+       git config --unset-all log.decorate &&
+       git log --pretty=raw >expect.raw &&
+       git config log.decorate full &&
+       git log --pretty=raw >actual &&
+       test_cmp expect.raw actual
+
+'
+
+test_expect_success 'reflog is expected format' '
+       test_might_fail git config --remove-section log &&
+       git log -g --abbrev-commit --pretty=oneline >expect &&
+       git reflog >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'whatchanged is expected format' '
+       git log --no-merges --raw >expect &&
+       git whatchanged >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'log.abbrevCommit configuration' '
+       test_when_finished "git config --unset log.abbrevCommit" &&
+
+       test_might_fail git config --unset log.abbrevCommit &&
+
+       git log --abbrev-commit >expect.log.abbrev &&
+       git log --no-abbrev-commit >expect.log.full &&
+       git log --pretty=raw >expect.log.raw &&
+       git reflog --abbrev-commit >expect.reflog.abbrev &&
+       git reflog --no-abbrev-commit >expect.reflog.full &&
+       git whatchanged --abbrev-commit >expect.whatchanged.abbrev &&
+       git whatchanged --no-abbrev-commit >expect.whatchanged.full &&
+
+       git config log.abbrevCommit true &&
+
+       git log >actual &&
+       test_cmp expect.log.abbrev actual &&
+       git log --no-abbrev-commit >actual &&
+       test_cmp expect.log.full actual &&
+
+       git log --pretty=raw >actual &&
+       test_cmp expect.log.raw actual &&
+
+       git reflog >actual &&
+       test_cmp expect.reflog.abbrev actual &&
+       git reflog --no-abbrev-commit >actual &&
+       test_cmp expect.reflog.full actual &&
+
+       git whatchanged >actual &&
+       test_cmp expect.whatchanged.abbrev actual &&
+       git whatchanged --no-abbrev-commit >actual &&
+       test_cmp expect.whatchanged.full actual
 '
 
 test_expect_success 'show added path under "--follow -M"' '
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
 '
diff --git a/t/t4208-log-magic-pathspec.sh b/t/t4208-log-magic-pathspec.sh
new file mode 100755 (executable)
index 0000000..2c482b6
--- /dev/null
@@ -0,0 +1,36 @@
+#!/bin/sh
+
+test_description='magic pathspec tests using git-log'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+       test_commit initial &&
+       test_tick &&
+       git commit --allow-empty -m empty &&
+       mkdir sub
+'
+
+test_expect_success '"git log :/" should be ambiguous' '
+       test_must_fail git log :/ 2>error &&
+       grep ambiguous error
+'
+
+test_expect_success '"git log :" should be ambiguous' '
+       test_must_fail git log : 2>error &&
+       grep ambiguous error
+'
+
+test_expect_success 'git log -- :' '
+       git log -- :
+'
+
+test_expect_success 'git log HEAD -- :/' '
+       cat >expected <<-EOF &&
+       24b24cf initial
+       EOF
+       (cd sub && git log --oneline HEAD -- :/ >../actual) &&
+       test_cmp expected actual
+'
+
+test_done
index 4e69c907d810c5a82892f94f05374b824a003771..0d0222ea2ace733ffe3fbfbdee58520abcd40e92 100755 (executable)
@@ -347,6 +347,21 @@ test_expect_success 'fetch mirrors do not act as mirrors during push' '
        )
 '
 
+test_expect_success 'add fetch mirror with specific branches' '
+       git init --bare mirror-fetch/track &&
+       (cd mirror-fetch/track &&
+        git remote add --mirror=fetch -t heads/new parent ../parent
+       )
+'
+
+test_expect_success 'fetch mirror respects specific branches' '
+       (cd mirror-fetch/track &&
+        git fetch parent &&
+        git rev-parse --verify refs/heads/new &&
+        test_must_fail git rev-parse --verify refs/heads/renamed
+       )
+'
+
 test_expect_success 'add --mirror=push' '
        mkdir mirror-push &&
        git init --bare mirror-push/public &&
@@ -382,6 +397,13 @@ test_expect_success 'push mirrors do not act as mirrors during fetch' '
        )
 '
 
+test_expect_success 'push mirrors do not allow you to specify refs' '
+       git init mirror-push/track &&
+       (cd mirror-push/track &&
+        test_must_fail git remote add --mirror=push -t new public ../public
+       )
+'
+
 test_expect_success 'add alt && prune' '
        (mkdir alttst &&
         cd alttst &&
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 d1912351db7da4a7c457fd44895b5ea076c84e7e..5c546c99a58854ae0f430e469dad525af52e9292 100755 (executable)
@@ -123,4 +123,28 @@ test_expect_success 'confuses pattern as remote when no remote specified' '
 
 '
 
+test_expect_success 'die with non-2 for wrong repository even with --exit-code' '
+       git ls-remote --exit-code ./no-such-repository ;# not &&
+       status=$? &&
+       test $status != 2 && test $status != 0
+'
+
+test_expect_success 'Report success even when nothing matches' '
+       git ls-remote other.git "refs/nsn/*" >actual &&
+       >expect &&
+       test_cmp expect actual
+'
+
+test_expect_success 'Report no-match with --exit-code' '
+       test_expect_code 2 git ls-remote --exit-code other.git "refs/nsn/*" >actual &&
+       >expect &&
+       test_cmp expect actual
+'
+
+test_expect_success 'Report match with --exit-code' '
+       git ls-remote --exit-code other.git "refs/tags/*" >actual &&
+       git ls-remote . tags/mark >expect &&
+       test_cmp expect actual
+'
+
 test_done
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 cd6e2c5e871230a43f504e165498139d8ec7a3d2..1fdfbd38654a83771f677f2219403e3713abef51 100755 (executable)
@@ -780,6 +780,13 @@ test_expect_success 'status -s submodule summary (clean submodule)' '
        test_cmp expect output
 '
 
+test_expect_success 'status -z implies porcelain' '
+       git status --porcelain |
+       perl -pe "s/\012/\000/g" >expect &&
+       git status -z >output &&
+       test_cmp expect output
+'
+
 cat >expect <<EOF
 # On branch master
 # Changes to be committed:
index 8184c264cf942bcd8e6c2f75b5526ddfdc5a11e9..6379ad60bcb9b5b56eb773b5a987745356c24df6 100755 (executable)
@@ -26,6 +26,17 @@ test_expect_success setup '
                echo foo mmap bar_mmap
                echo foo_mmap bar mmap baz
        } >file &&
+       {
+               echo Hello world
+               echo HeLLo world
+               echo Hello_world
+               echo HeLLo_world
+       } >hello_world &&
+       {
+               echo "a+b*c"
+               echo "a+bc"
+               echo "abc"
+       } >ab &&
        echo vvv >v &&
        echo ww w >w &&
        echo x x xx x >x &&
@@ -221,7 +232,17 @@ do
                git grep --max-depth 0 -n -e vvv $H -- t . >actual &&
                test_cmp expected actual
        '
+       test_expect_success "grep $L with grep.extendedRegexp=false" '
+               echo "ab:a+bc" >expected &&
+               git -c grep.extendedRegexp=false grep "a+b*c" ab >actual &&
+               test_cmp expected actual
+       '
 
+       test_expect_success "grep $L with grep.extendedRegexp=true" '
+               echo "ab:abc" >expected &&
+               git -c grep.extendedRegexp=true grep "a+b*c" ab >actual &&
+               test_cmp expected actual
+       '
 done
 
 cat >expected <<EOF
@@ -599,4 +620,100 @@ test_expect_success 'grep -e -- -- path' '
        test_cmp expected actual
 '
 
+cat >expected <<EOF
+hello.c:int main(int argc, const char **argv)
+hello.c:       printf("Hello world.\n");
+EOF
+
+test_expect_success LIBPCRE 'grep --perl-regexp pattern' '
+       git grep --perl-regexp "\p{Ps}.*?\p{Pe}" hello.c >actual &&
+       test_cmp expected actual
+'
+
+test_expect_success LIBPCRE 'grep -P pattern' '
+       git grep -P "\p{Ps}.*?\p{Pe}" hello.c >actual &&
+       test_cmp expected actual
+'
+
+test_expect_success 'grep pattern with grep.extendedRegexp=true' '
+       >empty &&
+       test_must_fail git -c grep.extendedregexp=true \
+               grep "\p{Ps}.*?\p{Pe}" hello.c >actual &&
+       test_cmp empty actual
+'
+
+test_expect_success LIBPCRE 'grep -P pattern with grep.extendedRegexp=true' '
+       git -c grep.extendedregexp=true \
+               grep -P "\p{Ps}.*?\p{Pe}" hello.c >actual &&
+       test_cmp expected actual
+'
+
+test_expect_success LIBPCRE 'grep -P -v pattern' '
+       {
+               echo "ab:a+b*c"
+               echo "ab:a+bc"
+       } >expected &&
+       git grep -P -v "abc" ab >actual &&
+       test_cmp expected actual
+'
+
+test_expect_success LIBPCRE 'grep -P -i pattern' '
+       cat >expected <<-EOF &&
+       hello.c:        printf("Hello world.\n");
+       EOF
+       git grep -P -i "PRINTF\([^\d]+\)" hello.c >actual &&
+       test_cmp expected actual
+'
+
+test_expect_success LIBPCRE 'grep -P -w pattern' '
+       {
+               echo "hello_world:Hello world"
+               echo "hello_world:HeLLo world"
+       } >expected &&
+       git grep -P -w "He((?i)ll)o" hello_world >actual &&
+       test_cmp expected actual
+'
+
+test_expect_success 'grep -G invalidpattern properly dies ' '
+       test_must_fail git grep -G "a["
+'
+
+test_expect_success 'grep -E invalidpattern properly dies ' '
+       test_must_fail git grep -E "a["
+'
+
+test_expect_success LIBPCRE 'grep -P invalidpattern properly dies ' '
+       test_must_fail git grep -P "a["
+'
+
+test_expect_success 'grep -G -E -F pattern' '
+       echo "ab:a+b*c" >expected &&
+       git grep -G -E -F "a+b*c" ab >actual &&
+       test_cmp expected actual
+'
+
+test_expect_success 'grep -E -F -G pattern' '
+       echo "ab:a+bc" >expected &&
+       git grep -E -F -G "a+b*c" ab >actual &&
+       test_cmp expected actual
+'
+
+test_expect_success 'grep -F -G -E pattern' '
+       echo "ab:abc" >expected &&
+       git grep -F -G -E "a+b*c" ab >actual &&
+       test_cmp expected actual
+'
+
+test_expect_success 'grep -G -F -P -E pattern' '
+       >empty &&
+       test_must_fail git grep -G -F -P -E "a\x{2b}b\x{2a}c" ab >actual &&
+       test_cmp empty actual
+'
+
+test_expect_success LIBPCRE 'grep -G -F -E -P pattern' '
+       echo "ab:a+b*c" >expected &&
+       git grep -G -F -E -P "a\x{2b}b\x{2a}c" ab >actual &&
+       test_cmp expected actual
+'
+
 test_done
diff --git a/t/t8008-blame-formats.sh b/t/t8008-blame-formats.sh
new file mode 100755 (executable)
index 0000000..d15f8b3
--- /dev/null
@@ -0,0 +1,90 @@
+#!/bin/sh
+
+test_description='blame output in various formats on a simple case'
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+       echo a >file &&
+       git add file
+       test_tick &&
+       git commit -m one &&
+       echo b >>file &&
+       echo c >>file &&
+       echo d >>file &&
+       test_tick &&
+       git commit -a -m two
+'
+
+cat >expect <<'EOF'
+^baf5e0b (A U Thor 2005-04-07 15:13:13 -0700 1) a
+8825379d (A U Thor 2005-04-07 15:14:13 -0700 2) b
+8825379d (A U Thor 2005-04-07 15:14:13 -0700 3) c
+8825379d (A U Thor 2005-04-07 15:14:13 -0700 4) d
+EOF
+test_expect_success 'normal blame output' '
+       git blame file >actual &&
+       test_cmp expect actual
+'
+
+ID1=baf5e0b3869e0b2b2beb395a3720c7b51eac94fc
+COMMIT1='author A U Thor
+author-mail <author@example.com>
+author-time 1112911993
+author-tz -0700
+committer C O Mitter
+committer-mail <committer@example.com>
+committer-time 1112911993
+committer-tz -0700
+summary one
+boundary
+filename file'
+ID2=8825379dfb8a1267b58e8e5bcf69eec838f685ec
+COMMIT2='author A U Thor
+author-mail <author@example.com>
+author-time 1112912053
+author-tz -0700
+committer C O Mitter
+committer-mail <committer@example.com>
+committer-time 1112912053
+committer-tz -0700
+summary two
+previous baf5e0b3869e0b2b2beb395a3720c7b51eac94fc file
+filename file'
+
+cat >expect <<EOF
+$ID1 1 1 1
+$COMMIT1
+       a
+$ID2 2 2 3
+$COMMIT2
+       b
+$ID2 3 3
+       c
+$ID2 4 4
+       d
+EOF
+test_expect_success 'blame --porcelain output' '
+       git blame --porcelain file >actual &&
+       test_cmp expect actual
+'
+
+cat >expect <<EOF
+$ID1 1 1 1
+$COMMIT1
+       a
+$ID2 2 2 3
+$COMMIT2
+       b
+$ID2 3 3
+$COMMIT2
+       c
+$ID2 4 4
+$COMMIT2
+       d
+EOF
+test_expect_success 'blame --line-porcelain output' '
+       git blame --line-porcelain file >actual &&
+       test_cmp expect 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 71ef0acb1ba64d36a7909b5308ab6b6e89c31466..53297156a314a5b5e03385a3ad887eff959359c4 100755 (executable)
@@ -644,4 +644,20 @@ test_expect_success \
        'ctags: search projects by non existent tag' \
        'gitweb_run "by_tag=non-existent"'
 
+test_expect_success \
+       'ctags: malformed tag weights' \
+       'mkdir -p .git/ctags &&
+        echo "not-a-number" > .git/ctags/nan &&
+        echo "not-a-number-2" > .git/ctags/nan2 &&
+        echo "0.1" >.git/ctags/floating-point &&
+        gitweb_run'
+
+# ----------------------------------------------------------------------
+# categories
+
+test_expect_success \
+       'categories: projects list, only default category' \
+       'echo "\$projects_list_group_categories = 1;" >>gitweb_config.perl &&
+        gitweb_run'
+
 test_done
index b2ce2bc4b22f43c0b324ab3f596a7f865fa58cc1..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"
@@ -731,12 +736,11 @@ test_expect_code () {
        exit_code=$?
        if test $exit_code = $want_code
        then
-               echo >&2 "test_expect_code: command exited with $exit_code: $*"
                return 0
-       else
-               echo >&2 "test_expect_code: command exited with $exit_code, we wanted $want_code $*"
-               return 1
        fi
+
+       echo >&2 "test_expect_code: command exited with $exit_code, we wanted $want_code $*"
+       return 1
 }
 
 # test_cmp is a helper function to compare actual and expected output.
@@ -775,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="{ $*
@@ -885,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
@@ -1072,6 +1084,7 @@ esac
 
 test -z "$NO_PERL" && test_set_prereq PERL
 test -z "$NO_PYTHON" && test_set_prereq PYTHON
+test -n "$USE_LIBPCRE" && test_set_prereq LIBPCRE
 
 # Can we rely on git's output in the C locale?
 if test -n "$GETTEXT_POISON"
index a02f79aae3d91ea4109d21a9ed9ca2d962125375..c9c8056f9de69bd378cd271d70363b5560f13e07 100644 (file)
@@ -156,7 +156,7 @@ static void set_upstreams(struct transport *transport, struct ref *refs,
                        continue;
                if (!ref->peer_ref)
                        continue;
-               if (!ref->new_sha1 || is_null_sha1(ref->new_sha1))
+               if (is_null_sha1(ref->new_sha1))
                        continue;
 
                /* Follow symbolic refs (mainly for HEAD). */
@@ -1190,14 +1190,20 @@ char *transport_anonymize_url(const char *url)
        return xstrdup(url);
 }
 
-int refs_from_alternate_cb(struct alternate_object_database *e, void *cb)
+struct alternate_refs_data {
+       alternate_ref_fn *fn;
+       void *data;
+};
+
+static int refs_from_alternate_cb(struct alternate_object_database *e,
+                                 void *data)
 {
        char *other;
        size_t len;
        struct remote *remote;
        struct transport *transport;
        const struct ref *extra;
-       alternate_ref_fn *ref_fn = cb;
+       struct alternate_refs_data *cb = data;
 
        e->name[-1] = '\0';
        other = xstrdup(real_path(e->base));
@@ -1218,8 +1224,16 @@ int refs_from_alternate_cb(struct alternate_object_database *e, void *cb)
        for (extra = transport_get_remote_refs(transport);
             extra;
             extra = extra->next)
-               ref_fn(extra, NULL);
+               cb->fn(extra, cb->data);
        transport_disconnect(transport);
        free(other);
        return 0;
 }
+
+void for_each_alternate_ref(alternate_ref_fn fn, void *data)
+{
+       struct alternate_refs_data cb;
+       cb.fn = fn;
+       cb.data = data;
+       foreach_alt_odb(refs_from_alternate_cb, &cb);
+}
index efb19688692b5f1be4fc085cc36d737ace2df417..161d724bba5c132950b45866286c558148560d01 100644 (file)
@@ -167,6 +167,6 @@ void transport_print_push_status(const char *dest, struct ref *refs,
                  int verbose, int porcelain, int *nonfastforward);
 
 typedef void alternate_ref_fn(const struct ref *, void *);
-extern int refs_from_alternate_cb(struct alternate_object_database *e, void *cb);
+extern void for_each_alternate_ref(alternate_ref_fn, void *);
 
 #endif
index 3f4072525b9489a86b2231748abe13c611e05274..b3cc2e4753447d4734ed08a70e3396771257dced 100644 (file)
@@ -142,8 +142,7 @@ int diff_tree(struct tree_desc *t1, struct tree_desc *t2,
        strbuf_add(&base, base_str, baselen);
 
        for (;;) {
-               if (DIFF_OPT_TST(opt, QUICK) &&
-                   DIFF_OPT_TST(opt, HAS_CHANGES))
+               if (diff_can_quit_early(opt))
                        break;
                if (opt->pathspec.nr) {
                        skip_uninteresting(t1, &base, opt, &t1_match);
index 0bc4b2ddca2d96e16c3f7b32e8e922f426706be6..07f83642443601d107e0a2425407b3250c022dcd 100644 (file)
@@ -203,7 +203,7 @@ static int check_updates(struct unpack_trees_options *o)
 
                if (ce->ce_flags & CE_WT_REMOVE) {
                        display_progress(progress, ++cnt);
-                       if (o->update)
+                       if (o->update && !o->dry_run)
                                unlink_entry(ce);
                        continue;
                }
@@ -217,7 +217,7 @@ static int check_updates(struct unpack_trees_options *o)
                if (ce->ce_flags & CE_UPDATE) {
                        display_progress(progress, ++cnt);
                        ce->ce_flags &= ~CE_UPDATE;
-                       if (o->update) {
+                       if (o->update && !o->dry_run) {
                                errs |= checkout_entry(ce, &state, NULL);
                        }
                }
index cd11a08365ab3e27b1321b3df87bcab6b9278f90..64f02cb03ab242ac08ea0f1afbb24e71cf6664aa 100644 (file)
@@ -46,7 +46,8 @@ struct unpack_trees_options {
                     debug_unpack,
                     skip_sparse_checkout,
                     gently,
-                    show_all_errors;
+                    show_all_errors,
+                    dry_run;
        const char *prefix;
        int cache_bottom;
        struct dir_struct *dir;
index 5d62e795a2e922ec9cf4d259c5315eeeec176c4c..01d3a8b81e628b0a088e3e9fc0ca59acf7d9c58d 100644 (file)
@@ -60,10 +60,24 @@ PATTERNS("pascal",
         "|[-+0-9.e]+|0[xXbB]?[0-9a-fA-F]+"
         "|<>|<=|>=|:=|\\.\\."),
 PATTERNS("perl",
-        "^[ \t]*package .*;\n"
-        "^[ \t]*sub .* \\{\n"
-        "^[A-Z]+ \\{\n"        /* BEGIN, END, ... */
-        "^=head[0-9] ",        /* POD */
+        "^package .*\n"
+        "^sub [[:alnum:]_':]+[ \t]*"
+               "(\\([^)]*\\)[ \t]*)?" /* prototype */
+               /*
+                * Attributes.  A regex can't count nested parentheses,
+                * so just slurp up whatever we see, taking care not
+                * to accept lines like "sub foo; # defined elsewhere".
+                *
+                * An attribute could contain a semicolon, but at that
+                * point it seems reasonable enough to give up.
+                */
+               "(:[^;#]*)?"
+               "(\\{[ \t]*)?" /* brace can come here or on the next line */
+               "(#.*)?$\n" /* comment */
+        "^(BEGIN|END|INIT|CHECK|UNITCHECK|AUTOLOAD|DESTROY)[ \t]*"
+               "(\\{[ \t]*)?" /* brace can come here or on the next line */
+               "(#.*)?$\n"
+        "^=head[0-9] .*",      /* POD */
         /* -- */
         "[[:alpha:]_'][[:alnum:]_']*"
         "|0[xb]?[0-9a-fA-F_]*"
index 28290002b9f6434d716a39612f3afc9958c292af..85f09df747637b94e0488ad65984c3f97c732034 100644 (file)
--- a/wrapper.c
+++ b/wrapper.c
@@ -148,8 +148,10 @@ ssize_t read_in_full(int fd, void *buf, size_t count)
 
        while (count > 0) {
                ssize_t loaded = xread(fd, p, count);
-               if (loaded <= 0)
-                       return total ? total : loaded;
+               if (loaded < 0)
+                       return -1;
+               if (loaded == 0)
+                       return total;
                count -= loaded;
                p += loaded;
                total += loaded;