Code

Merge branch 'gs/usage-to-stdout'
authorJunio C Hamano <gitster@pobox.com>
Mon, 21 Jun 2010 13:02:45 +0000 (06:02 -0700)
committerJunio C Hamano <gitster@pobox.com>
Mon, 21 Jun 2010 13:02:45 +0000 (06:02 -0700)
* gs/usage-to-stdout:
  parseopt: wrap rev-parse --parseopt usage for eval consumption
  print the usage string on stdout instead of stderr

Conflicts:
parse-options.h

192 files changed:
Documentation/RelNotes-1.7.1.1.txt [new file with mode: 0644]
Documentation/RelNotes-1.7.2.txt
Documentation/SubmittingPatches
Documentation/config.txt
Documentation/diff-options.txt
Documentation/git-add.txt
Documentation/git-checkout.txt
Documentation/git-commit.txt
Documentation/git-cvsserver.txt
Documentation/git-instaweb.txt
Documentation/git-log.txt
Documentation/git-mailinfo.txt
Documentation/git-merge.txt
Documentation/git-notes.txt
Documentation/git-rebase.txt
Documentation/git-remote.txt
Documentation/git-status.txt
Documentation/git-submodule.txt
Documentation/git-update-index.txt
Documentation/gitignore.txt
Documentation/install-webdoc.sh
Documentation/pretty-formats.txt
Documentation/pretty-options.txt
Documentation/technical/api-run-command.txt
Makefile
aclocal.m4 [new file with mode: 0644]
attr.h
builtin.h
builtin/add.c
builtin/apply.c
builtin/blame.c
builtin/branch.c
builtin/cat-file.c
builtin/checkout.c
builtin/clone.c
builtin/commit.c
builtin/fetch.c
builtin/fmt-merge-msg.c
builtin/for-each-ref.c
builtin/grep.c
builtin/help.c
builtin/ls-files.c
builtin/ls-remote.c
builtin/mailinfo.c
builtin/merge-file.c
builtin/merge-tree.c
builtin/merge.c
builtin/notes.c
builtin/pack-objects.c
builtin/receive-pack.c
builtin/remote.c
builtin/rerere.c
cache.h
combine-diff.c
commit.h
compat/mingw.h
compat/win32/pthread.c
compat/win32/pthread.h
config.mak.in
configure.ac
connect.c
contrib/completion/git-completion.bash
convert.c
ctype.c
daemon.c
date.c
diff.c
diff.h
dir.c
fast-import.c
fsck.c
git-add--interactive.perl
git-am.sh
git-compat-util.h
git-cvsserver.perl
git-instaweb.sh
git-merge-one-file.sh
git-pull.sh
git-rebase.sh
git-remote-testgit.py
git-request-pull.sh
git-submodule.sh
git-svn.perl
git-web--browse.sh
git_remote_helpers/git/exporter.py
git_remote_helpers/git/importer.py
git_remote_helpers/git/non_local.py
git_remote_helpers/git/repo.py
gitweb/INSTALL
gitweb/Makefile
gitweb/README
gitweb/git-favicon.png [deleted file]
gitweb/git-logo.png [deleted file]
gitweb/gitweb.css [deleted file]
gitweb/gitweb.js [deleted file]
gitweb/gitweb.perl
gitweb/static/git-favicon.png [new file with mode: 0644]
gitweb/static/git-logo.png [new file with mode: 0644]
gitweb/static/gitweb.css [new file with mode: 0644]
gitweb/static/gitweb.js [new file with mode: 0644]
graph.c
grep.c
grep.h
http-backend.c
http-push.c
http-walker.c
imap-send.c
ll-merge.c
log-tree.c
merge-file.c
merge-recursive.h
notes.c
notes.h
parse-options.h
pretty.c
quote.c
quote.h
refs.c
refs.h
remote.c
remote.h
rerere.c
revision.c
run-command.c
run-command.h
setup.c
sha1_file.c
t/Makefile
t/README
t/aggregate-results.sh
t/gitweb-lib.sh
t/lib-t6000.sh [new file with mode: 0644]
t/t0000-basic.sh
t/t0021-conversion.sh
t/t1450-fsck.sh
t/t2007-checkout-symlink.sh
t/t2017-checkout-orphan.sh
t/t2106-update-index-assume-unchanged.sh [new file with mode: 0755]
t/t3030-merge-recursive.sh
t/t3200-branch.sh
t/t3210-pack-refs.sh
t/t3306-notes-prune.sh
t/t3307-notes-man.sh [new file with mode: 0755]
t/t3400-rebase.sh
t/t3903-stash.sh
t/t4002-diff-basic.sh
t/t4015-diff-whitespace.sh
t/t4043-diff-rename-binary.sh [new file with mode: 0755]
t/t4044-diff-index-unique-abbrev.sh [new file with mode: 0755]
t/t4124-apply-ws-rule.sh
t/t4127-apply-same-fn.sh
t/t4205-log-pretty-formats.sh [new file with mode: 0755]
t/t5150-request-pull.sh
t/t5300-pack-object.sh
t/t5505-remote.sh
t/t5510-fetch.sh
t/t5512-ls-remote.sh
t/t5520-pull.sh
t/t5530-upload-pack-error.sh
t/t5601-clone.sh
t/t5700-clone-reference.sh
t/t5800-remote-helpers.sh
t/t6000lib.sh [deleted file]
t/t6001-rev-list-graft.sh
t/t6002-rev-list-bisect.sh
t/t6003-rev-list-topo-order.sh
t/t6022-merge-rename.sh
t/t6101-rev-parse-parents.sh
t/t7002-grep.sh
t/t7005-editor.sh
t/t7006-pager.sh
t/t7008-grep-binary.sh [new file with mode: 0755]
t/t7010-setup.sh
t/t7407-submodule-foreach.sh
t/t7508-status.sh
t/t7509-commit.sh
t/t7604-merge-custom-message.sh
t/t9129-git-svn-i18n-commitencoding.sh
t/t9200-git-cvsexportcommit.sh
t/t9400-git-cvsserver-server.sh
t/t9500-gitweb-standalone-no-errors.sh
t/test-lib.sh
trace.c
transport.c
unpack-trees.c
url.c [new file with mode: 0644]
url.h [new file with mode: 0644]
usage.c
userdiff.c
wrapper.c
wt-status.c
wt-status.h

diff --git a/Documentation/RelNotes-1.7.1.1.txt b/Documentation/RelNotes-1.7.1.1.txt
new file mode 100644 (file)
index 0000000..e0386a4
--- /dev/null
@@ -0,0 +1,67 @@
+Git v1.7.1.1 Release Notes (draft)
+==================================
+
+Fixes since v1.7.1
+------------------
+
+ * Authentication over http transport can now be made lazily, in that the
+   request can first go to a URL without username, get a 401 response and
+   then the client will ask for the username to use.
+
+ * We used to mistakenly think "../work" is a subdirectory of the current
+   directory when we are in "../work-xyz".
+
+ * The attribute mechanism now allows an entry that uses an attribute
+   macro that set/unset one attribute, immediately followed by an
+   overriding setting; this makes attribute macros much easier to use.
+
+ * We didn't recognize timezone "Z" as a synonym for "UTC" (75b37e70).
+
+ * "git am -3" did not show diagnosis when the patch in the message was corrupt.
+
+ * After "git apply --whitespace=fix" removed trailing blank lines in an
+   patch in a patch series, it failed to apply later patches that depend
+   on the presense of such blank lines.
+
+ * "git bundle --stdin" segfaulted.
+
+ * "git checkout" and "git rebase" overwrote paths that are marked "assume
+   unchanged".
+
+ * "git describe" did not tie-break tags that point at the same commit
+   correctly; newer ones are preferred by paying attention to the
+   tagger date now.
+
+ * "git diff" used to tell underlying xdiff machinery to work very hard to
+   minimize the output, but this often was spending too many extra cycles
+   for very little gain.
+
+ * "git diff --color" did not paint extended diff headers per line
+   (i.e. the coloring escape sequence didn't end at the end of line),
+   which confused "less -R".
+
+ * "git fetch" over HTTP verifies the downloaded packfiles more robustly.
+
+ * The memory usage by "git index-pack" (run during "git fetch" and "git
+   push") got leaner.
+
+ * "GIT_DIR=foo.git git init --bare bar.git" created foo.git instead of bar.git.
+
+ * "git log --abbrev=$num --format='%h' ignored --abbrev=$num.
+
+ * "git pull" accepted "--dry-run", gave it to underlying "git fetch" but
+   ignored the option itself, resulting in a bogus attempt to merge
+   unrelated commit.
+
+ * "git send-email" lacked a way to specify the domainname used in the
+   EHLO/HELO exchange, causing rejected connection from picky servers.
+   It learned --smtp-domain option to solve this issue.
+
+ * "git stash" incorrectly lost paths in the working tree that were
+   previously removed from the index.
+
+ * "git status" stopped refreshing the index by mistake in 1.7.1.
+
+ * "git status" showed excess "hints" even when advice.statusHints is set to false.
+
+And other minor fixes and documentation updates.
index 37781b4f149a0af5a1c61a538ac320879f4470f2..a1b5428df4843165290b82ea1b9bee4b7b294f84 100644 (file)
@@ -4,37 +4,112 @@ Git v1.7.2 Release Notes (draft)
 Updates since v1.7.1
 --------------------
 
- * After "git apply --whitespace=fix" removed trailing blank lines in an
-   patch in a patch series, it failed to apply later patches that depend
-   on the presense of such blank lines.
+ * The whitespace rules used in "git apply --whitespace" and "git diff"
+   gained a new member in the family (tab-in-indent) to help projects with
+   policy to indent only with spaces.
+
+ * When working from a subdirectory, by default, git does not look for its
+   metadirectory ".git" across filesystems, primarily to help people who
+   have invocations of git in their custom PS1 prompts, as being outside
+   of a git repository would look for ".git" all the way up to the root
+   directory, and NFS mounts are often slow.  DISCOVERY_ACROSS_FILESYSTEM
+   environment variable can be used to tell git not to stop at a
+   filesystem boundary.
+
+ * "git" wrapper learned "-c name=value" option to override configuration
+   variable from the command line.
+
+ * The message from "git am -3" has been improved when conflict
+   resolution ended up making the patch a no-op.
+
+ * "git checkout --orphan newbranch" is similar to "-b newbranch" but
+   prepares to create a root commit that is not connected to any existing
+   commit.
+
+ * "git commit --amend" on a commit with an invalid author-name line that
+   lacks the display name didn't work (fb7749e4).
+
+ * "git cvsserver" can be told to use pserver; its password file can be
+   stored outside the repository.
 
  * The output from the textconv filter used by "git diff" can be cached to
    speed up their reuse.
 
- * "git send-email" learned --smtp-domain option to specify the domainname
-   used in the EHLO/HELO exchange.
+ * "git diff --word-diff=<mode>" extends the existing "--color-words"
+   option, making it more useful in color-challenged environments.
+
+ * The regexp to detect function headers used by "git diff" for PHP has
+   been enhanced for visibility modifiers (public, protected, etc.) to
+   better support PHP5.
+
+ * "diff.noprefix" configuration variable can be used to implicitly
+   ask for "diff --no-prefix" behaviour.
+
+ * "git for-each-ref" learned "%(objectname:short)" that gives the object
+   name abbreviated.
+
+ * Various options to "git grep" (e.g. --count, --name-only) work better
+   with binary files.
+
+ * "git help -w" learned "chrome" and "chromium" browsers.
+
+ * "git log --follow <path>" follows across copies (it used to only follow
+   renames).  This may make the processing more expensive.
+
+ * "git ls-files ../out/side/cwd" works now.
+
+ * "git notes prune" learned "-n" (dry-run) and "-v" options, similar to
+   what "git prune" has.
+
+ * "git patch-id" can be fed a mbox without getting confused by the
+   signature line in the format-patch output.
+
+ * "git remote" learned "set-branches" subcommand.
 
  * "git revert" learned --strategy option to specify the merge strategy.
 
- * The whitespace rules used in "git apply --whitespace" and "git diff"
-   gained a new member in the family (tab-in-indent) to help projects with
-   policy to indent only with spaces.
+ * "git status [-s] --ignored" can be used to list ignored paths.
 
- * Authentication over http transport can now be made lazily, in that the
-   request can first go to a URL without username, get a 401 response and
-   then the client will ask for the username to use.
+ * "git status -s -b" shows the current branch in the output.
+
+ * Various "gitweb" enhancements and clean-ups, including syntax
+   highlighting, "plackup" support for instaweb, etc.
 
 
 Fixes since v1.7.1
 ------------------
 
- * In 1.7.1, "git status" stopped refreshing the index by mistake.
-
 All of the fixes in v1.7.1.X maintenance series are included in this
 release, unless otherwise noted.
 
+ * We didn't URL decode "file:///path/to/repo" correctly when path/to/repo
+   had percent-encoded characters (638794c, 9d2e942).
+
+ * "git clone/fetch/pull" issued an incorrect error message when a ref and
+   a symref that points to the ref were updated at the same time.  This
+   obviously would update them to the same value, and should not result in
+   an error condition (7223dcaf).
+
+ * "git clone" did not configure remote.origin.url correctly for bare
+   clones (df61c889).
+
+ * "git diff --graph" works better with "--color-words" and other options
+   (81fa024..4297c0a).
+
+ * "git diff" could show ambiguous abbreviation of blob object names on
+   its "index" line (3e5a188).
+
+ * "git merge --log" used to replace the custom message given by "-m" with
+   the shortlog, instead of appending to it (tc/merge-m-log).
+
+ * "git reset --hard" started from a wrong directory and a working tree in
+   a nonstandard location is in use got confused (560fb6a1).
+
+ * "git show -C -C" and other corner cases lost diff metainfo output
+   in 1.7.0 (296c6bb).
+
 --
 exec >/var/tmp/1
-O=v1.7.1-77-gb751157
-echo O=$(git describe master)
-git shortlog --no-merges master ^maint ^$O
+O=v1.7.1-423-gae391ec
+echo O=$(git describe HEAD)
+git shortlog --no-merges HEAD ^maint ^$O
index b9204c77d3b6070ec27b297cbd3e35d85f40e442..eb53e0636e3c3bab06e88ce3371945f5602c5756 100644 (file)
@@ -547,7 +547,7 @@ Gmail
 
 GMail does not appear to have any way to turn off line wrapping in the web
 interface, so this will mangle any emails that you send.  You can however
-use "git send e-mail" and send your patches through the GMail SMTP server, or
+use "git send-email" and send your patches through the GMail SMTP server, or
 use any IMAP email client to connect to the google IMAP server and forward
 the emails through that.
 
index 87f397ed962debf97c00b944a5f8e9207c3d5181..7afd0a333f5e67fcfcc5c5277c3a871f6825ea83 100644 (file)
@@ -520,18 +520,12 @@ check that makes sure that existing object files will not get overwritten.
 
 core.notesRef::
        When showing commit messages, also show notes which are stored in
-       the given ref.  This ref is expected to contain files named
-       after the full SHA-1 of the commit they annotate.  The ref
-       must be fully qualified.
+       the given ref.  The ref must be fully qualified.  If the given
+       ref does not exist, it is not an error but means that no
+       notes should be printed.
 +
-If such a file exists in the given ref, the referenced blob is read, and
-appended to the commit message, separated by a "Notes (<refname>):"
-line (shortened to "Notes:" in the case of "refs/notes/commits").  If the
-given ref itself does not exist, it is not an error, but means that no
-notes should be printed.
-+
-This setting defaults to "refs/notes/commits", and can be overridden by
-the `GIT_NOTES_REF` environment variable.
+This setting defaults to "refs/notes/commits", and it can be overridden by
+the 'GIT_NOTES_REF' environment variable.  See linkgit:git-notes[1].
 
 core.sparseCheckout::
        Enable "sparse checkout" feature. See section "Sparse checkout" in
@@ -798,6 +792,8 @@ diff.mnemonicprefix::
        standard "a/" and "b/" depending on what is being compared.  When
        this configuration is in effect, reverse diff output also swaps
        the order of the prefixes:
+diff.noprefix::
+       If set, 'git diff' does not show any source or destination prefix.
 `git diff`;;
        compares the (i)ndex and the (w)ork tree;
 `git diff HEAD`;;
@@ -1481,6 +1477,16 @@ pager.<cmd>::
        it takes precedence over this option.  To disable pagination for
        all commands, set `core.pager` or `GIT_PAGER` to `cat`.
 
+pretty.<name>::
+       Alias for a --pretty= format string, as specified in
+       linkgit:git-log[1]. Any aliases defined here can be used just
+       as the built-in pretty formats could. For example,
+       running `git config pretty.changelog "format:{asterisk} %H %s"`
+       would cause the invocation `git log --pretty=changelog`
+       to be equivalent to running `git log "--pretty=format:{asterisk} %H %s"`.
+       Note that an alias with the same name as a built-in format
+       will be silently ignored.
+
 pull.octopus::
        The default merge strategy to use when pulling multiple branches
        at once.
@@ -1699,6 +1705,13 @@ If this variable is not specified, it defaults to 'normal'.
 This variable can be overridden with the -u|--untracked-files option
 of linkgit:git-status[1] and linkgit:git-commit[1].
 
+status.submodulesummary::
+       Defaults to false.
+       If this is set to a non zero number or true (identical to -1 or an
+       unlimited number), the submodule summary will be enabled and a
+       summary of commits for modified submodules will be shown (see
+       --summary-limit option of linkgit:git-submodule[1]).
+
 tar.umask::
        This variable can be used to restrict the permission bits of
        tar archive entries.  The default is 0002, which turns off the
index 0d89aaaf2aac365f78823c05b57b02ecffa95928..e745a3ccdc923b068e791a778a7c73b664b230d2 100644 (file)
@@ -210,7 +210,14 @@ endif::git-format-patch[]
        Break complete rewrite changes into pairs of delete and create.
 
 -M::
+ifndef::git-log[]
        Detect renames.
+endif::git-log[]
+ifdef::git-log[]
+       If generating diffs, detect and report renames for each commit.
+       For following files across renames while traversing history, see
+       `--follow`.
+endif::git-log[]
 
 -C::
        Detect copies as well as renames.  See also `--find-copies-harder`.
index 51cbeb7032865599317fd9d7d36a9c9e2f8cc0c5..74741a42f409a7af5c60584a0d3b7a0be851811c 100644 (file)
@@ -266,9 +266,9 @@ patch::
 
        y - stage this hunk
        n - do not stage this hunk
-       q - quit, do not stage this hunk nor any of the remaining ones
-       a - stage this and all the remaining hunks in the file
-       d - do not stage this hunk nor any of the remaining hunks in the file
+       q - quit; do not stage this hunk nor any of the remaining ones
+       a - stage this hunk and all later hunks in the file
+       d - do not stage this hunk nor any of the later hunks in the file
        g - select a hunk to go to
        / - search for a hunk matching the given regex
        j - leave this hunk undecided, see next undecided hunk
index 4505eb6d84990944009d0b5e560de4bd4d2022e8..261dd90c381eccbd87d73325478e1d5abcf05e15 100644 (file)
@@ -15,33 +15,41 @@ SYNOPSIS
 
 DESCRIPTION
 -----------
-
-When <paths> are not given, this command switches branches by
-updating the index, working tree, and HEAD to reflect the specified
+Updates files in the working tree to match the version in the index
+or the specified tree.  If no paths are given, 'git checkout' will
+also update `HEAD` to set the specified branch as the current
 branch.
 
-If `-b` is given, a new branch is created and checked out, as if
-linkgit:git-branch[1] were called; in this case you can
-use the --track or --no-track options, which will be passed to `git
-branch`.  As a convenience, --track without `-b` implies branch
-creation; see the description of --track below.
-
-When <paths> or --patch are given, this command does *not* switch
-branches.  It updates the named paths in the working tree from
-the index file, or from a named <tree-ish> (most often a commit).  In
-this case, the `-b` and `--track` options are meaningless and giving
-either of them results in an error. The <tree-ish> argument can be
-used to specify a specific tree-ish (i.e. commit, tag or tree)
-to update the index for the given paths before updating the
-working tree.
-
-The index may contain unmerged entries after a failed merge.  By
-default, if you try to check out such an entry from the index, the
+'git checkout' [<branch>]::
+'git checkout' -b <new branch> [<start point>]::
+
+       This form switches branches by updating the index, working
+       tree, and HEAD to reflect the specified branch.
++
+If `-b` is given, a new branch is created as if linkgit:git-branch[1]
+were called and then checked out; in this case you can
+use the `--track` or `--no-track` options, which will be passed to
+'git branch'.  As a convenience, `--track` without `-b` implies branch
+creation; see the description of `--track` below.
+
+'git checkout' [--patch] [<tree-ish>] [--] <pathspec>...::
+
+       When <paths> or `--patch` are given, 'git checkout' *not* switch
+       branches.  It updates the named paths in the working tree from
+       the index file or from a named <tree-ish> (most often a commit).  In
+       this case, the `-b` and `--track` options are meaningless and giving
+       either of them results in an error. The <tree-ish> argument can be
+       used to specify a specific tree-ish (i.e. commit, tag or tree)
+       to update the index for the given paths before updating the
+       working tree.
++
+The index may contain unmerged entries because of a previous failed merge.
+By default, if you try to check out such an entry from the index, the
 checkout operation will fail and nothing will be checked out.
-Using -f will ignore these unmerged entries.  The contents from a
+Using `-f` will ignore these unmerged entries.  The contents from a
 specific side of the merge can be checked out of the index by
-using --ours or --theirs.  With -m, changes made to the working tree
-file can be discarded to recreate the original conflicted merge result.
+using `--ours` or `--theirs`.  With `-m`, changes made to the working tree
+file can be discarded to re-create the original conflicted merge result.
 
 OPTIONS
 -------
@@ -91,22 +99,29 @@ explicitly give a name with '-b' in such a case.
        details.
 
 --orphan::
-       Create a new branch named <new_branch>, unparented to any other
-       branch.  The new branch you switch to does not have any commit
-       and after the first one it will become the root of a new history
-       completely unconnected from all the other branches.
+       Create a new 'orphan' branch, named <new_branch>, started from
+       <start_point> and switch to it.  The first commit made on this
+       new branch will have no parents and it will be the root of a new
+       history totally disconnected from all the other branches and
+       commits.
++
+The index and the working tree are adjusted as if you had previously run
+"git checkout <start_point>".  This allows you to start a new history
+that records a set of paths similar to <start_point> by easily running
+"git commit -a" to make the root commit.
 +
-When you use "--orphan", the index and the working tree are kept intact.
-This allows you to start a new history that records set of paths similar
-to that of the start-point commit, which is useful when you want to keep
-different branches for different audiences you are working to like when
-you have an open source and commercial versions of a software, for example.
+This can be useful when you want to publish the tree from a commit
+without exposing its full history. You might want to do this to publish
+an open source branch of a project whose current tree is "clean", but
+whose full history contains proprietary or otherwise encumbered bits of
+code.
 +
-If you want to start a disconnected history that records set of paths
-totally different from the original branch, you may want to first clear
-the index and the working tree, by running "git rm -rf ." from the
-top-level of the working tree, before preparing your files (by copying
-from elsewhere, extracting a tarball, etc.) in the working tree.
+If you want to start a disconnected history that records a set of paths
+that is totally different from the one of <start_point>, then you should
+clear the index and the working tree right after creating the orphan
+branch by running "git rm -rf ." from the top level of the working tree.
+Afterwards you will be ready to prepare your new files, repopulating the
+working tree, by copying them from elsewhere, extracting a tarball, etc.
 
 -m::
 --merge::
@@ -154,6 +169,10 @@ edits from your current working tree.
 As a special case, the `"@\{-N\}"` syntax for the N-th last branch
 checks out the branch (instead of detaching).  You may also specify
 `-` which is synonymous with `"@\{-1\}"`.
++
+As a further special case, you may use `"A...B"` as a shortcut for the
+merge base of `A` and `B` if there is exactly one merge base. You can
+leave out at most one of `A` and `B`, in which case it defaults to `HEAD`.
 
 <new_branch>::
        Name for the new branch.
index 32c482f33ff331dbae9c8072a7050d10d875454a..c28603ecf58bd065cc8f7c3c4436f1c0f82a7d2e 100644 (file)
@@ -95,10 +95,11 @@ OPTIONS
        read the message from the standard input.
 
 --author=<author>::
-       Override the author name used in the commit.  You can use the
-       standard `A U Thor <author@example.com>` format.  Otherwise,
-       an existing commit that matches the given string and its author
-       name is used.
+       Override the commit author. Specify an explicit author using the
+       standard `A U Thor <author@example.com>` format. Otherwise <author>
+       is assumed to be a pattern and is used to search for an existing
+       commit by that author (i.e. rev-list --all -i --author=<author>);
+       the commit author is then copied from the first such commit found.
 
 --date=<date>::
        Override the author date used in the commit.
index dbb053ee17fbc9e254aac42bf87d204655e00614..c27ca4350e675e396c72e30022b26f8efff53a7f 100644 (file)
@@ -72,9 +72,6 @@ plugin. Most functionality works fine with both of these clients.
 LIMITATIONS
 -----------
 
-Currently cvsserver works over SSH connections for read/write clients, and
-over pserver for anonymous CVS access.
-
 CVS clients cannot tag, branch or perform GIT merges.
 
 'git-cvsserver' maps GIT branches to CVS modules. This is very different
@@ -84,7 +81,7 @@ one or more directories.
 INSTALLATION
 ------------
 
-1. If you are going to offer anonymous CVS access via pserver, add a line in
+1. If you are going to offer CVS access via pserver, add a line in
    /etc/inetd.conf like
 +
 --
@@ -101,6 +98,38 @@ looks like
    cvspserver stream tcp nowait nobody /usr/bin/git-cvsserver git-cvsserver pserver
 
 ------
+
+Only anonymous access is provided by pserve by default. To commit you
+will have to create pserver accounts, simply add a gitcvs.authdb
+setting in the config file of the repositories you want the cvsserver
+to allow writes to, for example:
+
+------
+
+   [gitcvs]
+       authdb = /etc/cvsserver/passwd
+
+------
+The format of these files is username followed by the crypted password,
+for example:
+
+------
+   myuser:$1Oyx5r9mdGZ2
+   myuser:$1$BA)@$vbnMJMDym7tA32AamXrm./
+------
+You can use the 'htpasswd' facility that comes with Apache to make these
+files, but Apache's MD5 crypt method differs from the one used by most C
+library's crypt() function, so don't use the -m option.
+
+Alternatively you can produce the password with perl's crypt() operator:
+-----
+   perl -e 'my ($user, $pass) = @ARGV; printf "%s:%s\n", $user, crypt($user, $pass)' $USER password
+-----
+
+Then provide your password via the pserver method, for example:
+------
+   cvs -d:pserver:someuser:somepassword <at> server/path/repo.git co <HEAD_name>
+------
 No special setup is needed for SSH access, other than having GIT tools
 in the PATH. If you have clients that do not accept the CVS_SERVER
 environment variable, you can rename 'git-cvsserver' to `cvs`.
index a1f17df0744361ab999314b1834c2e2384af90bd..2c3c4d299472a265bfc4486816933ca3dc69b44f 100644 (file)
@@ -29,7 +29,7 @@ OPTIONS
        The HTTP daemon command-line that will be executed.
        Command-line options may be specified here, and the
        configuration file will be added at the end of the command-line.
-       Currently apache2, lighttpd, mongoose and webrick are supported.
+       Currently apache2, lighttpd, mongoose, plackup and webrick are supported.
        (Default: lighttpd)
 
 -m::
index a2d55f974525c59f3dd51684531861bf0dc49a23..0e6ff3182347c03a9ae9c3992fd1e8a6db2867f9 100644 (file)
@@ -133,6 +133,48 @@ Discussion
 
 include::i18n.txt[]
 
+Configuration
+-------------
+
+See linkgit:git-config[1] for core variables and linkgit:git-diff[1]
+for settings related to diff generation.
+
+format.pretty::
+       Default for the `--format` option.  (See "PRETTY FORMATS" above.)
+       Defaults to "medium".
+
+i18n.logOutputEncoding::
+       Encoding to use when displaying logs.  (See "Discussion", above.)
+       Defaults to the value of `i18n.commitEncoding` if set, UTF-8
+       otherwise.
+
+log.date::
+       Default format for human-readable dates.  (Compare the
+       `--date` option.)  Defaults to "default", which means to write
+       dates like `Sat May 8 19:35:34 2010 -0500`.
+
+log.showroot::
+       If `false`, 'git log' and related commands will not treat the
+       initial commit as a big creation event.  Any root commits in
+       `git log -p` output would be shown without a diff attached.
+       The default is `true`.
+
+mailmap.file::
+       See linkgit:git-shortlog[1].
+
+notes.displayRef::
+       Which refs, in addition to the default set by `core.notesRef`
+       or 'GIT_NOTES_REF', to read notes from when showing commit
+       messages with the 'log' family of commands.  See
+       linkgit:git-notes[1].
++
+May be an unabbreviated ref name or a glob and may be specified
+multiple times.  A warning will be issued for refs that do not exist,
+but a glob that does not match any refs is silently ignored.
++
+This setting can be disabled by the `--no-standard-notes` option,
+overridden by the 'GIT_NOTES_DISPLAY_REF' environment variable,
+and supplemented by the `--show-notes` option.
 
 Author
 ------
index e3d58cbac3f162cc01d8731485f220fd70fed17b..3ea5aad56c5e1cec7fc2ca01ba9b5ea6badebe2f 100644 (file)
@@ -40,16 +40,16 @@ OPTIONS
 -u::
        The commit log message, author name and author email are
        taken from the e-mail, and after minimally decoding MIME
-       transfer encoding, re-coded in UTF-8 by transliterating
+       transfer encoding, re-coded in the charset specified by
+       i18n.commitencoding (defaulting to UTF-8) by transliterating
        them.  This used to be optional but now it is the default.
 +
 Note that the patch is always used as-is without charset
 conversion, even with this flag.
 
 --encoding=<encoding>::
-       Similar to -u but if the local convention is different
-       from what is specified by i18n.commitencoding, this flag
-       can be used to override it.
+       Similar to -u.  But when re-coding, the charset specified here is
+       used instead of the one specified by i18n.commitencoding or UTF-8.
 
 -n::
        Disable all charset re-coding of the metadata.
index c2325ef90e336a37df42eb7f71f95e3581c83127..84043cc5b26db7d34c93a70ec005df913c345d85 100644 (file)
@@ -58,7 +58,12 @@ include::merge-options.txt[]
 
 -m <msg>::
        Set the commit message to be used for the merge commit (in
-       case one is created). The 'git fmt-merge-msg' command can be
+       case one is created).
+
+       If `--log` is specified, a shortlog of the commits being merged
+       will be appended to the specified message.
+
+       The 'git fmt-merge-msg' command can be
        used to give a good default for automated 'git merge'
        invocations.
 
index 4e5113b837230c8f050cf4bbe10e8de4b3271bd0..5540af5d1667aad16ac11c53593921733c4af56f 100644 (file)
@@ -3,7 +3,7 @@ git-notes(1)
 
 NAME
 ----
-git-notes - Add/inspect object notes
+git-notes - Add or inspect object notes
 
 SYNOPSIS
 --------
@@ -15,29 +15,31 @@ SYNOPSIS
 'git notes' edit [<object>]
 'git notes' show [<object>]
 'git notes' remove [<object>]
-'git notes' prune
+'git notes' prune [-n | -v]
 
 
 DESCRIPTION
 -----------
-This command allows you to add/remove notes to/from objects, without
-changing the objects themselves.
+Adds, removes, or reads notes attached to objects, without touching
+the objects themselves.
 
-A typical use of notes is to extend a commit message without having
-to change the commit itself. Such commit notes can be shown by `git log`
-along with the original commit message. To discern these notes from the
+By default, notes are saved to and read from `refs/notes/commits`, but
+this default can be overridden.  See the OPTIONS, CONFIGURATION, and
+ENVIRONMENT sections below.  If this ref does not exist, it will be
+quietly created when it is first needed to store a note.
+
+A typical use of notes is to supplement a commit message without
+changing the commit itself. Notes can be shown by 'git log' along with
+the original commit message. To distinguish these notes from the
 message stored in the commit object, the notes are indented like the
 message, after an unindented line saying "Notes (<refname>):" (or
-"Notes:" for the default setting).
+"Notes:" for `refs/notes/commits`).
 
-This command always manipulates the notes specified in "core.notesRef"
-(see linkgit:git-config[1]), which can be overridden by GIT_NOTES_REF.
-To change which notes are shown by 'git-log', see the
-"notes.displayRef" configuration.
+To change which notes are shown by 'git log', see the
+"notes.displayRef" configuration in linkgit:git-log[1].
 
-See the description of "notes.rewrite.<command>" in
-linkgit:git-config[1] for a way of carrying your notes across commands
-that rewrite commits.
+See the "notes.rewrite.<command>" configuration for a way to carry
+notes across commands that rewrite commits.
 
 
 SUBCOMMANDS
@@ -101,15 +103,20 @@ OPTIONS
        Use the given note message (instead of prompting).
        If multiple `-m` options are given, their values
        are concatenated as separate paragraphs.
+       Lines starting with `#` and empty lines other than a
+       single line between paragraphs will be stripped out.
 
 -F <file>::
 --file=<file>::
        Take the note message from the given file.  Use '-' to
        read the note message from the standard input.
+       Lines starting with `#` and empty lines other than a
+       single line between paragraphs will be stripped out.
 
 -C <object>::
 --reuse-message=<object>::
-       Reuse the note message from the given note object.
+       Take the note message from the given blob object (for
+       example, another note).
 
 -c <object>::
 --reedit-message=<object>::
@@ -117,22 +124,151 @@ OPTIONS
        the user can further edit the note message.
 
 --ref <ref>::
-       Manipulate the notes tree in <ref>.  This overrides both
-       GIT_NOTES_REF and the "core.notesRef" configuration.  The ref
+       Manipulate the notes tree in <ref>.  This overrides
+       'GIT_NOTES_REF' and the "core.notesRef" configuration.  The ref
        is taken to be in `refs/notes/` if it is not qualified.
 
+-n::
+       Do not remove anything; just report the object names whose notes
+       would be removed.
+
+-v::
+       Report all object names whose notes are removed.
+
+
+DISCUSSION
+----------
 
-NOTES
------
+Commit notes are blobs containing extra information about an object
+(usually information to supplement a commit's message).  These blobs
+are taken from notes refs.  A notes ref is usually a branch which
+contains "files" whose paths are the object names for the objects
+they describe, with some directory separators included for performance
+reasons footnote:[Permitted pathnames have the form
+'ab'`/`'cd'`/`'ef'`/`'...'`/`'abcdef...': a sequence of directory
+names of two hexadecimal digits each followed by a filename with the
+rest of the object ID.].
 
 Every notes change creates a new commit at the specified notes ref.
 You can therefore inspect the history of the notes by invoking, e.g.,
-`git log -p notes/commits`.
+`git log -p notes/commits`.  Currently the commit message only records
+which operation triggered the update, and the commit authorship is
+determined according to the usual rules (see linkgit:git-commit[1]).
+These details may change in the future.
+
+It is also permitted for a notes ref to point directly to a tree
+object, in which case the history of the notes can be read with
+`git log -p -g <refname>`.
+
+
+EXAMPLES
+--------
+
+You can use notes to add annotations with information that was not
+available at the time a commit was written.
+
+------------
+$ git notes add -m 'Tested-by: Johannes Sixt <j6t@kdbg.org>' 72a144e2
+$ git show -s 72a144e
+[...]
+    Signed-off-by: Junio C Hamano <gitster@pobox.com>
+
+Notes:
+    Tested-by: Johannes Sixt <j6t@kdbg.org>
+------------
+
+In principle, a note is a regular Git blob, and any kind of
+(non-)format is accepted.  You can binary-safely create notes from
+arbitrary files using 'git hash-object':
+
+------------
+$ cc *.c
+$ blob=$(git hash-object -w a.out)
+$ git notes --ref=built add -C "$blob" HEAD
+------------
+
+Of course, it doesn't make much sense to display non-text-format notes
+with 'git log', so if you use such notes, you'll probably need to write
+some special-purpose tools to do something useful with them.
+
+
+CONFIGURATION
+-------------
 
-Currently the commit message only records which operation triggered
-the update, and the commit authorship is determined according to the
-usual rules (see linkgit:git-commit[1]).  These details may change in
-the future.
+core.notesRef::
+       Notes ref to read and manipulate instead of
+       `refs/notes/commits`.  Must be an unabbreviated ref name.
+       This setting can be overridden through the environment and
+       command line.
+
+notes.displayRef::
+       Which ref (or refs, if a glob or specified more than once), in
+       addition to the default set by `core.notesRef` or
+       'GIT_NOTES_REF', to read notes from when showing commit
+       messages with the 'git log' family of commands.
+       This setting can be overridden on the command line or by the
+       'GIT_NOTES_DISPLAY_REF' environment variable.
+       See linkgit:git-log[1].
+
+notes.rewrite.<command>::
+       When rewriting commits with <command> (currently `amend` or
+       `rebase`), if this variable is `false`, git will not copy
+       notes from the original to the rewritten commit.  Defaults to
+       `true`.  See also "`notes.rewriteRef`" below.
++
+This setting can be overridden by the 'GIT_NOTES_REWRITE_REF'
+environment variable.
+
+notes.rewriteMode::
+       When copying notes during a rewrite, what to do if the target
+       commit already has a note.  Must be one of `overwrite`,
+       `concatenate`, and `ignore`.  Defaults to `concatenate`.
++
+This setting can be overridden with the `GIT_NOTES_REWRITE_MODE`
+environment variable.
+
+notes.rewriteRef::
+       When copying notes during a rewrite, specifies the (fully
+       qualified) ref whose notes should be copied.  May be a glob,
+       in which case notes in all matching refs will be copied.  You
+       may also specify this configuration several times.
++
+Does not have a default value; you must configure this variable to
+enable note rewriting.
++
+Can be overridden with the 'GIT_NOTES_REWRITE_REF' environment variable.
+
+
+ENVIRONMENT
+-----------
+
+'GIT_NOTES_REF'::
+       Which ref to manipulate notes from, instead of `refs/notes/commits`.
+       This overrides the `core.notesRef` setting.
+
+'GIT_NOTES_DISPLAY_REF'::
+       Colon-delimited list of refs or globs indicating which refs,
+       in addition to the default from `core.notesRef` or
+       'GIT_NOTES_REF', to read notes from when showing commit
+       messages.
+       This overrides the `notes.displayRef` setting.
++
+A warning will be issued for refs that do not exist, but a glob that
+does not match any refs is silently ignored.
+
+'GIT_NOTES_REWRITE_MODE'::
+       When copying notes during a rewrite, what to do if the target
+       commit already has a note.
+       Must be one of `overwrite`, `concatenate`, and `ignore`.
+       This overrides the `core.rewriteMode` setting.
+
+'GIT_NOTES_REWRITE_REF'::
+       When rewriting commits, which notes to copy from the original
+       to the rewritten commit.  Must be a colon-delimited list of
+       refs or globs.
++
+If not set in the environment, the list of notes to copy depends
+on the `notes.rewrite.<command>` and `notes.rewriteRef` settings.
 
 
 Author
index 5863decdc9f96669e839b3e16cc265ddb699fce7..50ba2e469f48f1bc16c3f6e3b6f6451a9e29637a 100644 (file)
@@ -206,6 +206,10 @@ OPTIONS
        --onto option is not specified, the starting point is
        <upstream>.  May be any valid commit, and not just an
        existing branch name.
++
+As a special case, you may use "A...B" as a shortcut for the
+merge base of A and B if there is exactly one merge base. You can
+leave out at most one of A and B, in which case it defaults to HEAD.
 
 <upstream>::
        Upstream branch to compare against.  May be any valid commit,
index ebaaadc1786b3581f70764aff5d0af4038c86644..aa021b0cb803a9cd9f08566f6a6fe407eb025e4b 100644 (file)
@@ -14,6 +14,7 @@ SYNOPSIS
 'git remote rename' <old> <new>
 'git remote rm' <name>
 'git remote set-head' <name> (-a | -d | <branch>)
+'git remote set-branches' <name> [--add] <branch>...
 'git remote set-url' [--push] <name> <newurl> [<oldurl>]
 'git remote set-url --add' [--push] <name> <newurl>
 'git remote set-url --delete' [--push] <name> <url>
@@ -110,6 +111,18 @@ remote set-head origin master" will set `$GIT_DIR/refs/remotes/origin/HEAD` to
 `refs/remotes/origin/master` already exists; if not it must be fetched first.
 +
 
+'set-branches'::
+
+Changes the list of branches tracked by the named remote.
+This can be used to track a subset of the available remote branches
+after the initial setup for a remote.
++
+The named branches will be interpreted as if specified with the
+`-t` option on the 'git remote add' command line.
++
+With `--add`, instead of replacing the list of currently tracked
+branches, adds to that list.
+
 'set-url'::
 
 Changes URL remote points to. Sets first URL remote points to matching
index 2d4bbfcaf4cc2d2b92ad827662dc3b4b4ef355c0..fd0fe7cb56cf3ba163cef467f5fc9c35b0ea963b 100644 (file)
@@ -27,6 +27,10 @@ OPTIONS
 --short::
        Give the output in the short-format.
 
+-b::
+--branch::
+       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
@@ -120,6 +124,10 @@ Ignored files are not listed.
     ?           ?    untracked
     -------------------------------------------------
 
+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
 that format, the status field is the same, but some other things
 change.  First, the '->' is omitted from rename entries and the field
@@ -128,7 +136,7 @@ order is reversed (e.g 'from -> to' becomes 'to from'). Second, a NUL
 and the terminating newline (but a space still separates the status
 field from the first filename).  Third, filenames containing special
 characters are not specially formatted; no quoting or
-backslash-escaping is performed.
+backslash-escaping is performed. Fourth, there is no branch line.
 
 CONFIGURATION
 -------------
index 2502531a3dfd0cee67f6d1d9fc1db1d69cf20e30..cdabfd29add5752caab24a1715427c0e4eba9a11 100644 (file)
@@ -145,10 +145,12 @@ summary::
 
 foreach::
        Evaluates an arbitrary shell command in each checked out submodule.
-       The command has access to the variables $name, $path and $sha1:
+       The command has access to the variables $name, $path, $sha1 and
+       $toplevel:
        $name is the name of the relevant submodule section in .gitmodules,
        $path is the name of the submodule directory relative to the
-       superproject, and $sha1 is the commit as recorded in the superproject.
+       superproject, $sha1 is the commit as recorded in the superproject,
+       and $toplevel is the absolute path to the top-level of the superproject.
        Any submodules defined in the superproject but not checked out are
        ignored by this command. Unless given --quiet, foreach prints the name
        of each submodule before evaluating the command.
index 68dc1879fe912b1a01b3caa6b1c0168f6a7b8072..765d4b312ed6f062180e83fa0d931e6bd2eef24f 100644 (file)
@@ -93,8 +93,6 @@ OPTIONS
 This option can be also used as a coarse file-level mechanism
 to ignore uncommitted changes in tracked files (akin to what
 `.gitignore` does for untracked files).
-You should remember that an explicit 'git add' operation will
-still cause the file to be refreshed from the working tree.
 Git will fail (gracefully) in case it needs to modify this file
 in the index e.g. when merging in a commit;
 thus, in case the assumed-untracked file is changed upstream,
index 98c459dc82be46be706b1d7545d7ec95efd8790c..e10fa88b8cf98b796fc9354e7671dc7bec25840d 100644 (file)
@@ -83,16 +83,20 @@ Patterns have the following format:
 
  - If the pattern does not contain a slash '/', git treats it as
    a shell glob pattern and checks for a match against the
-   pathname without leading directories.
+   pathname relative to the location of the `.gitignore` file
+   (relative to the toplevel of the work tree if not from a
+   `.gitignore` file).
 
  - Otherwise, git treats the pattern as a shell glob suitable
    for consumption by fnmatch(3) with the FNM_PATHNAME flag:
    wildcards in the pattern will not match a / in the pathname.
    For example, "Documentation/\*.html" matches
-   "Documentation/git.html" but not
-   "Documentation/ppc/ppc.html".  A leading slash matches the
-   beginning of the pathname; for example, "/*.c" matches
-   "cat-file.c" but not "mozilla-sha1/sha1.c".
+   "Documentation/git.html" but not "Documentation/ppc/ppc.html"
+   or "tools/perf/Documentation/perf.html".
+
+ - A leading slash matches the beginning of the pathname.
+   For example, "/*.c" matches "cat-file.c" but not
+   "mozilla-sha1/sha1.c".
 
 An example:
 
index 2135a8ee1f4f56a8c799437949ba76d7526164c0..34d02a24188921ebac1b70f7cf0c6517a45e8fb1 100755 (executable)
@@ -12,7 +12,7 @@ do
        then
                : did not match
        elif test -f "$T/$h" &&
-          diff -u -I'Last updated [0-9][0-9]-[A-Z][a-z][a-z]-' "$T/$h" "$h"
+          $DIFF -u -I'Last updated [0-9][0-9]-[A-Z][a-z][a-z]-' "$T/$h" "$h"
        then
                :; # up to date
        else
index bd760d3bd107d2f1101efbc75693e3ac5e81139b..8c68ce94f990b0c7f3bc909cc3dc0c4e2a37cf3b 100644 (file)
@@ -11,7 +11,12 @@ have limited your view of history: for example, if you are
 only interested in changes related to a certain directory or
 file.
 
-Here are some additional details for each format:
+There are several built-in formats, and you can define
+additional formats by setting a pretty.<name>
+config option to either another format name, or a
+'format:' string, as described below (see
+linkgit:git-config[1]). Here are the details of the
+built-in formats:
 
 * 'oneline'
 
@@ -76,9 +81,9 @@ displayed in full, regardless of whether --abbrev or
 true parent commits, without taking grafts nor history
 simplification into account.
 
-* 'format:'
+* 'format:<string>'
 +
-The 'format:' format allows you to specify which information
+The 'format:<string>' format allows you to specify which information
 you want to show. It works a little bit like printf format,
 with the notable exception that you get a newline with '%n'
 instead of '\n'.
index af6d2b995a050007ae088dec8e2c3474e9f06ad5..d78e121c76ef12b2d72fbdf1558fa967dfdba85b 100644 (file)
@@ -3,8 +3,9 @@
 
        Pretty-print the contents of the commit logs in a given format,
        where '<format>' can be one of 'oneline', 'short', 'medium',
-       'full', 'fuller', 'email', 'raw' and 'format:<string>'.
-       When omitted, the format defaults to 'medium'.
+       'full', 'fuller', 'email', 'raw' and 'format:<string>'.  See
+       the "PRETTY FORMATS" section for some additional details for each
+       format.  When omitted, the format defaults to 'medium'.
 +
 Note: you can specify the default pretty format in the repository
 configuration (see linkgit:git-config[1]).
index 44876fa703578f4952d6e928993e15ddec70439c..f18b4f4817448530a5adbe2c8835bb7791add42a 100644 (file)
@@ -231,8 +231,9 @@ The function pointer in .proc has the following signature:
 
 
 There are serious restrictions on what the asynchronous function can do
-because this facility is implemented by a pipe to a forked process on
-UNIX, but by a thread in the same address space on Windows:
+because this facility is implemented by a thread in the same address
+space on most platforms (when pthreads is available), but by a pipe to
+a forked process otherwise:
 
 . It cannot change the program's state (global variables, environment,
   etc.) in a way that the caller notices; in other words, .in and .out
index d5d6565c86242a10e5f0c10365375d2b8ba990c4..3dc072fc8162be1b1d57db207a976b8b78928f04 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -8,6 +8,12 @@ all::
 # Define SANE_TOOL_PATH to a colon-separated list of paths to prepend
 # to PATH if your tools in /usr/bin are broken.
 #
+# Define SOCKLEN_T to a suitable type (such as 'size_t') if your
+# system headers do not define a socklen_t type.
+#
+# Define INLINE to a suitable substitute (such as '__inline' or '') if git
+# fails to compile with errors about undefined inline functions or similar.
+#
 # Define SNPRINTF_RETURNS_BOGUS if your are on a system which snprintf()
 # or vsnprintf() return -1 instead of number of characters which would
 # have been written to the final string if enough space had been available.
@@ -249,7 +255,7 @@ endif
 
 CFLAGS = -g -O2 -Wall
 LDFLAGS =
-ALL_CFLAGS = $(CFLAGS)
+ALL_CFLAGS = $(CPPFLAGS) $(CFLAGS)
 ALL_LDFLAGS = $(LDFLAGS)
 STRIP ?= strip
 
@@ -272,6 +278,7 @@ mandir = share/man
 infodir = share/info
 gitexecdir = libexec/git-core
 sharedir = $(prefix)/share
+gitwebdir = $(sharedir)/gitweb
 template_dir = share/git-core/templates
 htmldir = share/doc/git-doc
 ifeq ($(prefix),/usr)
@@ -285,11 +292,12 @@ lib = lib
 # DESTDIR=
 pathsep = :
 
-export prefix bindir sharedir sysconfdir
+export prefix bindir sharedir sysconfdir gitwebdir
 
 CC = gcc
 AR = ar
 RM = rm -f
+DIFF = diff
 TAR = tar
 FIND = find
 INSTALL = install
@@ -297,6 +305,7 @@ RPMBUILD = rpmbuild
 TCL_PATH = tclsh
 TCLTK_PATH = wish
 PTHREAD_LIBS = -lpthread
+PTHREAD_CFLAGS =
 
 export TCL_PATH TCLTK_PATH
 
@@ -627,6 +636,7 @@ LIB_OBJS += tree-diff.o
 LIB_OBJS += tree.o
 LIB_OBJS += tree-walk.o
 LIB_OBJS += unpack-trees.o
+LIB_OBJS += url.o
 LIB_OBJS += usage.o
 LIB_OBJS += userdiff.o
 LIB_OBJS += utf8.o
@@ -739,6 +749,13 @@ EXTLIBS =
 # because maintaining the nesting to match is a pain.  If
 # we had "elif" things would have been much nicer...
 
+ifeq ($(uname_S),OSF1)
+       # Need this for u_short definitions et al
+       BASIC_CFLAGS += -D_OSF_SOURCE
+       SOCKLEN_T = int
+       NO_STRTOULL = YesPlease
+       NO_NSEC = YesPlease
+endif
 ifeq ($(uname_S),Linux)
        NO_STRLCPY = YesPlease
        NO_MKSTEMPS = YesPlease
@@ -813,6 +830,18 @@ ifeq ($(uname_S),SunOS)
        NO_MKDTEMP = YesPlease
        NO_MKSTEMPS = YesPlease
        NO_REGEX = YesPlease
+       ifeq ($(uname_R),5.6)
+               SOCKLEN_T = int
+               NO_HSTRERROR = YesPlease
+               NO_IPV6 = YesPlease
+               NO_SOCKADDR_STORAGE = YesPlease
+               NO_UNSETENV = YesPlease
+               NO_SETENV = YesPlease
+               NO_STRLCPY = YesPlease
+               NO_C99_FORMAT = YesPlease
+               NO_STRTOUMAX = YesPlease
+               GIT_TEST_CMP = cmp
+       endif
        ifeq ($(uname_R),5.7)
                NEEDS_RESOLV = YesPlease
                NO_IPV6 = YesPlease
@@ -822,18 +851,21 @@ ifeq ($(uname_S),SunOS)
                NO_STRLCPY = YesPlease
                NO_C99_FORMAT = YesPlease
                NO_STRTOUMAX = YesPlease
+               GIT_TEST_CMP = cmp
        endif
        ifeq ($(uname_R),5.8)
                NO_UNSETENV = YesPlease
                NO_SETENV = YesPlease
                NO_C99_FORMAT = YesPlease
                NO_STRTOUMAX = YesPlease
+               GIT_TEST_CMP = cmp
        endif
        ifeq ($(uname_R),5.9)
                NO_UNSETENV = YesPlease
                NO_SETENV = YesPlease
                NO_C99_FORMAT = YesPlease
                NO_STRTOUMAX = YesPlease
+               GIT_TEST_CMP = cmp
        endif
        INSTALL = /usr/ucb/install
        TAR = gtar
@@ -898,6 +930,7 @@ ifeq ($(uname_S),NetBSD)
        HAVE_PATHS_H = YesPlease
 endif
 ifeq ($(uname_S),AIX)
+       DEFAULT_PAGER = more
        NO_STRCASESTR=YesPlease
        NO_MEMMEM = YesPlease
        NO_MKDTEMP = YesPlease
@@ -910,7 +943,13 @@ ifeq ($(uname_S),AIX)
        BASIC_CFLAGS += -D_LARGE_FILES
        ifeq ($(shell expr "$(uname_V)" : '[1234]'),1)
                NO_PTHREADS = YesPlease
+       else
+               PTHREAD_LIBS = -lpthread
+       endif
+       ifeq ($(shell expr "$(uname_V).$(uname_R)" : '5\.1'),3)
+               INLINE=''
        endif
+       GIT_TEST_CMP = cmp
 endif
 ifeq ($(uname_S),GNU)
        # GNU/Hurd
@@ -955,6 +994,7 @@ ifeq ($(uname_S),IRIX64)
        NEEDS_LIBGEN = YesPlease
 endif
 ifeq ($(uname_S),HP-UX)
+       INLINE = __inline
        NO_IPV6=YesPlease
        NO_SETENV=YesPlease
        NO_STRCASESTR=YesPlease
@@ -966,6 +1006,20 @@ ifeq ($(uname_S),HP-UX)
        NO_HSTRERROR = YesPlease
        NO_SYS_SELECT_H = YesPlease
        SNPRINTF_RETURNS_BOGUS = YesPlease
+       NO_NSEC = YesPlease
+       ifeq ($(uname_R),B.11.00)
+               NO_INET_NTOP = YesPlease
+               NO_INET_PTON = YesPlease
+       endif
+       ifeq ($(uname_R),B.10.20)
+               # Override HP-UX 11.x setting:
+               INLINE =
+               SOCKLEN_T = size_t
+               NO_PREAD = YesPlease
+               NO_INET_NTOP = YesPlease
+               NO_INET_PTON = YesPlease
+       endif
+       GIT_TEST_CMP = cmp
 endif
 ifeq ($(uname_S),Windows)
        GIT_VERSION := $(GIT_VERSION).MSVC
@@ -1039,7 +1093,6 @@ ifneq (,$(findstring MINGW,$(uname_S)))
        NO_STRTOUMAX = YesPlease
        NO_MKDTEMP = YesPlease
        NO_MKSTEMPS = YesPlease
-       SNPRINTF_RETURNS_BOGUS = YesPlease
        NO_SVN_TESTS = YesPlease
        NO_PERL_MAKEMAKER = YesPlease
        RUNTIME_PREFIX = YesPlease
@@ -1076,6 +1129,7 @@ endif
 -include config.mak
 
 ifdef CHECK_HEADER_DEPENDENCIES
+COMPUTE_HEADER_DEPENDENCIES =
 USE_COMPUTED_HEADER_DEPENDENCIES =
 endif
 
@@ -1091,6 +1145,14 @@ else
 BROKEN_PATH_FIX = '/^\# @@BROKEN_PATH_FIX@@$$/d'
 endif
 
+ifneq (,$(INLINE))
+       BASIC_CFLAGS += -Dinline=$(INLINE)
+endif
+
+ifneq (,$(SOCKLEN_T))
+       BASIC_CFLAGS += -Dsocklen_t=$(SOCKLEN_T)
+endif
+
 ifeq ($(uname_S),Darwin)
        ifndef NO_FINK
                ifeq ($(shell test -d /sw/lib && echo y),y)
@@ -1362,6 +1424,7 @@ endif
 ifdef NO_PTHREADS
        BASIC_CFLAGS += -DNO_PTHREADS
 else
+       BASIC_CFLAGS += $(PTHREAD_CFLAGS)
        EXTLIBS += $(PTHREAD_LIBS)
        LIB_OBJS += thread-utils.o
 endif
@@ -1386,6 +1449,10 @@ ifdef USE_NED_ALLOCATOR
        COMPAT_OBJS += compat/nedmalloc/nedmalloc.o
 endif
 
+ifdef GIT_TEST_CMP_USE_COPIED_CONTEXT
+       export GIT_TEST_CMP_USE_COPIED_CONTEXT
+endif
+
 ifeq ($(TCLTK_PATH),)
 NO_TCLTK=NoThanks
 endif
@@ -1442,11 +1509,13 @@ gitexecdir_SQ = $(subst ','\'',$(gitexecdir))
 template_dir_SQ = $(subst ','\'',$(template_dir))
 htmldir_SQ = $(subst ','\'',$(htmldir))
 prefix_SQ = $(subst ','\'',$(prefix))
+gitwebdir_SQ = $(subst ','\'',$(gitwebdir))
 
 SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH))
 PERL_PATH_SQ = $(subst ','\'',$(PERL_PATH))
 PYTHON_PATH_SQ = $(subst ','\'',$(PYTHON_PATH))
 TCLTK_PATH_SQ = $(subst ','\'',$(TCLTK_PATH))
+DIFF_SQ = $(subst ','\'',$(DIFF))
 
 LIBS = $(GITLIBS) $(EXTLIBS)
 
@@ -1473,7 +1542,7 @@ endif
 ALL_CFLAGS += $(BASIC_CFLAGS)
 ALL_LDFLAGS += $(BASIC_LDFLAGS)
 
-export TAR INSTALL DESTDIR SHELL_PATH
+export DIFF TAR INSTALL DESTDIR SHELL_PATH
 
 
 ### Build rules
@@ -1535,6 +1604,7 @@ define cmd_munge_script
 $(RM) $@ $@+ && \
 sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
     -e 's|@SHELL_PATH@|$(SHELL_PATH_SQ)|' \
+    -e 's|@@DIFF@@|$(DIFF_SQ)|' \
     -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
     -e 's/@@NO_CURL@@/$(NO_CURL)/g' \
     -e $(BROKEN_PATH_FIX) \
@@ -1562,11 +1632,10 @@ $(patsubst %.perl,%,$(SCRIPT_PERL)): % : %.perl
        sed -e '1{' \
            -e '        s|#!.*perl|#!$(PERL_PATH_SQ)|' \
            -e '        h' \
-           -e '        s=.*=use lib (split(/$(pathsep)/, $$ENV{GITPERLLIB} || "@@INSTLIBDIR@@"));=' \
+           -e '        s=.*=use lib (split(/$(pathsep)/, $$ENV{GITPERLLIB} || "'"$$INSTLIBDIR"'"));=' \
            -e '        H' \
            -e '        x' \
            -e '}' \
-           -e 's|@@INSTLIBDIR@@|'"$$INSTLIBDIR"'|g' \
            -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
            $@.perl >$@+ && \
        chmod +x $@+ && \
@@ -1578,45 +1647,38 @@ gitweb:
        $(QUIET_SUBDIR0)gitweb $(QUIET_SUBDIR1) all
 
 ifdef JSMIN
-GITWEB_PROGRAMS += gitweb/gitweb.min.js
-GITWEB_JS = gitweb/gitweb.min.js
+GITWEB_PROGRAMS += gitweb/static/gitweb.min.js
+GITWEB_JS = gitweb/static/gitweb.min.js
 else
-GITWEB_JS = gitweb/gitweb.js
+GITWEB_JS = gitweb/static/gitweb.js
 endif
 ifdef CSSMIN
-GITWEB_PROGRAMS += gitweb/gitweb.min.css
-GITWEB_CSS = gitweb/gitweb.min.css
+GITWEB_PROGRAMS += gitweb/static/gitweb.min.css
+GITWEB_CSS = gitweb/static/gitweb.min.css
 else
-GITWEB_CSS = gitweb/gitweb.css
+GITWEB_CSS = gitweb/static/gitweb.css
 endif
 OTHER_PROGRAMS +=  gitweb/gitweb.cgi  $(GITWEB_PROGRAMS)
 gitweb/gitweb.cgi: gitweb/gitweb.perl $(GITWEB_PROGRAMS)
        $(QUIET_SUBDIR0)gitweb $(QUIET_SUBDIR1) $(patsubst gitweb/%,%,$@)
 
 ifdef JSMIN
-gitweb/gitweb.min.js: gitweb/gitweb.js
+gitweb/static/gitweb.min.js: gitweb/static/gitweb.js
        $(QUIET_SUBDIR0)gitweb $(QUIET_SUBDIR1) $(patsubst gitweb/%,%,$@)
 endif # JSMIN
 ifdef CSSMIN
-gitweb/gitweb.min.css: gitweb/gitweb.css
+gitweb/static/gitweb.min.css: gitweb/static/gitweb.css
        $(QUIET_SUBDIR0)gitweb $(QUIET_SUBDIR1) $(patsubst gitweb/%,%,$@)
 endif # CSSMIN
 
 
-git-instaweb: git-instaweb.sh gitweb/gitweb.cgi gitweb/gitweb.css gitweb/gitweb.js
+git-instaweb: git-instaweb.sh gitweb/gitweb.cgi gitweb/static/gitweb.css gitweb/static/gitweb.js
        $(QUIET_GEN)$(RM) $@ $@+ && \
        sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
            -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
            -e 's/@@NO_CURL@@/$(NO_CURL)/g' \
-           -e '/@@GITWEB_CGI@@/r gitweb/gitweb.cgi' \
-           -e '/@@GITWEB_CGI@@/d' \
-           -e '/@@GITWEB_CSS@@/r $(GITWEB_CSS)' \
-           -e '/@@GITWEB_CSS@@/d' \
-           -e '/@@GITWEB_JS@@/r $(GITWEB_JS)' \
-           -e '/@@GITWEB_JS@@/d' \
+           -e 's|@@GITWEBDIR@@|$(gitwebdir_SQ)|g' \
            -e 's|@@PERL@@|$(PERL_PATH_SQ)|g' \
-            -e 's|@@GITWEB_CSS_NAME@@|$(GITWEB_CSS)|' \
-            -e 's|@@GITWEB_JS_NAME@@|$(GITWEB_JS)|' \
            $@.sh > $@+ && \
        chmod +x $@+ && \
        mv $@+ $@
@@ -1668,7 +1730,10 @@ git.o git.spec \
 
 TEST_OBJS := $(patsubst test-%$X,test-%.o,$(TEST_PROGRAMS))
 GIT_OBJS := $(LIB_OBJS) $(BUILTIN_OBJS) $(PROGRAM_OBJS) $(TEST_OBJS) \
-       git.o http.o http-walker.o remote-curl.o
+       git.o
+ifndef NO_CURL
+       GIT_OBJS += http.o http-walker.o remote-curl.o
+endif
 XDIFF_OBJS = xdiff/xdiffi.o xdiff/xprepare.o xdiff/xutils.o xdiff/xemit.o \
        xdiff/xmerge.o xdiff/xpatience.o
 OBJECTS := $(GIT_OBJS) $(XDIFF_OBJS)
@@ -1885,10 +1950,18 @@ GIT-CFLAGS: FORCE
 GIT-BUILD-OPTIONS: FORCE
        @echo SHELL_PATH=\''$(subst ','\'',$(SHELL_PATH_SQ))'\' >$@
        @echo PERL_PATH=\''$(subst ','\'',$(PERL_PATH_SQ))'\' >>$@
+       @echo DIFF=\''$(subst ','\'',$(subst ','\'',$(DIFF)))'\' >>$@
+       @echo PYTHON_PATH=\''$(subst ','\'',$(PYTHON_PATH_SQ))'\' >>$@
        @echo TAR=\''$(subst ','\'',$(subst ','\'',$(TAR)))'\' >>$@
        @echo NO_CURL=\''$(subst ','\'',$(subst ','\'',$(NO_CURL)))'\' >>$@
        @echo NO_PERL=\''$(subst ','\'',$(subst ','\'',$(NO_PERL)))'\' >>$@
        @echo NO_PYTHON=\''$(subst ','\'',$(subst ','\'',$(NO_PYTHON)))'\' >>$@
+ifdef GIT_TEST_CMP
+       @echo GIT_TEST_CMP=\''$(subst ','\'',$(subst ','\'',$(GIT_TEST_CMP)))'\' >>$@
+endif
+ifdef GIT_TEST_CMP_USE_COPIED_CONTEXT
+       @echo GIT_TEST_CMP_USE_COPIED_CONTEXT=YesPlease >>$@
+endif
 
 ### Detect Tck/Tk interpreter path changes
 ifndef NO_TCLTK
@@ -1983,6 +2056,7 @@ install: all
        $(MAKE) -C templates DESTDIR='$(DESTDIR_SQ)' install
 ifndef NO_PERL
        $(MAKE) -C perl prefix='$(prefix_SQ)' DESTDIR='$(DESTDIR_SQ)' install
+       $(MAKE) -C gitweb install
 endif
 ifndef NO_PYTHON
        $(MAKE) -C git_remote_helpers prefix='$(prefix_SQ)' DESTDIR='$(DESTDIR_SQ)' install
diff --git a/aclocal.m4 b/aclocal.m4
new file mode 100644 (file)
index 0000000..d399de2
--- /dev/null
@@ -0,0 +1,40 @@
+dnl Check for socklen_t: historically on BSD it is an int, and in
+dnl POSIX 1g it is a type of its own, but some platforms use different
+dnl types for the argument to getsockopt, getpeername, etc.  So we
+dnl have to test to find something that will work.
+AC_DEFUN([TYPE_SOCKLEN_T],
+[
+   AC_CHECK_TYPE([socklen_t], ,[
+      AC_MSG_CHECKING([for socklen_t equivalent])
+      AC_CACHE_VAL([git_cv_socklen_t_equiv],
+      [
+         # Systems have either "struct sockaddr *" or
+         # "void *" as the second argument to getpeername
+         git_cv_socklen_t_equiv=
+         for arg2 in "struct sockaddr" void; do
+            for t in int size_t unsigned long "unsigned long"; do
+               AC_TRY_COMPILE([
+                  #include <sys/types.h>
+                  #include <sys/socket.h>
+
+                  int getpeername (int, $arg2 *, $t *);
+               ],[
+                  $t len;
+                  getpeername(0,0,&len);
+               ],[
+                  git_cv_socklen_t_equiv="$t"
+                  break 2
+               ])
+            done
+         done
+
+         if test "x$git_cv_socklen_t_equiv" = x; then
+            AC_MSG_ERROR([Cannot find a type to use in place of socklen_t])
+         fi
+      ])
+      AC_MSG_RESULT($git_cv_socklen_t_equiv)
+      AC_DEFINE_UNQUOTED(socklen_t, $git_cv_socklen_t_equiv,
+                       [type to use in place of socklen_t if not defined])],
+      [#include <sys/types.h>
+#include <sys/socket.h>])
+])
diff --git a/attr.h b/attr.h
index 450f49d648a013ffddc6321b7fd79b3fc1b66f7a..8b3f19be67f17c1fbf6edb37ac3ea27002ca6415 100644 (file)
--- a/attr.h
+++ b/attr.h
@@ -34,7 +34,7 @@ int git_checkattr(const char *path, int, struct git_attr_check *);
 enum git_attr_direction {
        GIT_ATTR_CHECKIN,
        GIT_ATTR_CHECKOUT,
-       GIT_ATTR_INDEX,
+       GIT_ATTR_INDEX
 };
 void git_attr_set_direction(enum git_attr_direction, struct index_state *);
 
index 5c887ef61bbf67411a81691c67fc14cc4e0bc682..b614d12b9f3464eff9ecfa90c2eb5ecf43a82b7e 100644 (file)
--- a/builtin.h
+++ b/builtin.h
@@ -16,6 +16,7 @@ extern const char *help_unknown_cmd(const char *cmd);
 extern void prune_packed_objects(int);
 extern int fmt_merge_msg(int merge_summary, struct strbuf *in,
        struct strbuf *out);
+extern int fmt_merge_msg_shortlog(struct strbuf *in, struct strbuf *out);
 extern int commit_notes(struct notes_tree *t, const char *msg);
 
 struct notes_rewrite_cfg {
index 87d2980313e71afc08df3eacde4a99a8468ff874..17149cfeedcc39956dc55f8b72acc1227fed5213 100644 (file)
@@ -261,12 +261,14 @@ static int edit_patch(int argc, const char **argv, const char *prefix)
 {
        char *file = xstrdup(git_path("ADD_EDIT.patch"));
        const char *apply_argv[] = { "apply", "--recount", "--cached",
-               file, NULL };
+               NULL, NULL };
        struct child_process child;
        struct rev_info rev;
        int out;
        struct stat st;
 
+       apply_argv[3] = file;
+
        git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
 
        if (read_cache() < 0)
index 8fc5ec31deae63122878bdb776921bcf656fb791..562e5345fc969a5ad03b3ade4952668bcec09136 100644 (file)
@@ -56,7 +56,7 @@ static enum ws_error_action {
        nowarn_ws_error,
        warn_on_ws_error,
        die_on_ws_error,
-       correct_ws_error,
+       correct_ws_error
 } ws_error_action = warn_on_ws_error;
 static int whitespace_error;
 static int squelch_whitespace_errors = 5;
@@ -64,7 +64,7 @@ static int applied_after_fixing_ws;
 
 static enum ws_ignore {
        ignore_ws_none,
-       ignore_ws_change,
+       ignore_ws_change
 } ws_ignore_action = ignore_ws_none;
 
 
index fc1586350f94ae48e7e48a51818517b465e7a40d..729b43058a8afe01b03a463d9c11f5d74d9c8d43 100644 (file)
@@ -39,7 +39,7 @@ static int show_root;
 static int reverse;
 static int blank_boundary;
 static int incremental;
-static int xdl_opts = XDF_NEED_MINIMAL;
+static int xdl_opts;
 
 static enum date_mode blame_date_mode = DATE_ISO8601;
 static size_t blame_date_width;
@@ -733,10 +733,11 @@ static int pass_blame_to_parent(struct scoreboard *sb,
 {
        int last_in_target;
        mmfile_t file_p, file_o;
-       struct blame_chunk_cb_data d = { sb, target, parent, 0, 0 };
+       struct blame_chunk_cb_data d;
        xpparam_t xpp;
        xdemitconf_t xecfg;
-
+       memset(&d, 0, sizeof(d));
+       d.sb = sb; d.target = target; d.parent = parent;
        last_in_target = find_last_in_target(sb, target);
        if (last_in_target < 0)
                return 1; /* nothing remains for this target */
@@ -875,10 +876,11 @@ static void find_copy_in_blob(struct scoreboard *sb,
        const char *cp;
        int cnt;
        mmfile_t file_o;
-       struct handle_split_cb_data d = { sb, ent, parent, split, 0, 0 };
+       struct handle_split_cb_data d;
        xpparam_t xpp;
        xdemitconf_t xecfg;
-
+       memset(&d, 0, sizeof(d));
+       d.sb = sb; d.ent = ent; d.parent = parent; d.split = split;
        /*
         * Prepare mmfile that contains only the lines in ent.
         */
@@ -1589,7 +1591,7 @@ static void emit_porcelain(struct scoreboard *sb, struct blame_entry *ent)
        strcpy(hex, sha1_to_hex(suspect->commit->object.sha1));
        printf("%s%c%d %d %d\n",
               hex,
-              ent->guilty ? ' ' : '*', // purely for debugging
+              ent->guilty ? ' ' : '*', /* purely for debugging */
               ent->s_lno + 1,
               ent->lno + 1,
               ent->num_lines);
index 6cf7e721e6b59f50c7a8018296aa49848deecda3..f594af0b3b88e5c44545022fb8ed66cb8e302fe0 100644 (file)
@@ -43,13 +43,13 @@ enum color_branch {
        BRANCH_COLOR_PLAIN = 1,
        BRANCH_COLOR_REMOTE = 2,
        BRANCH_COLOR_LOCAL = 3,
-       BRANCH_COLOR_CURRENT = 4,
+       BRANCH_COLOR_CURRENT = 4
 };
 
 static enum merge_filter {
        NO_FILTER = 0,
        SHOW_NOT_MERGED,
-       SHOW_MERGED,
+       SHOW_MERGED
 } merge_filter;
 static unsigned char merge_filter_ref[20];
 
index a933eaa043257c84f35e1d86728ca91be035caff..e5118c57da2b69963894ff2994ac361ab7f6b837 100644 (file)
@@ -118,7 +118,9 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name)
 
                /* custom pretty-print here */
                if (type == OBJ_TREE) {
-                       const char *ls_args[3] = {"ls-tree", obj_name, NULL};
+                       const char *ls_args[3] = { NULL };
+                       ls_args[0] =  "ls-tree";
+                       ls_args[1] =  obj_name;
                        return cmd_ls_tree(2, ls_args, NULL);
                }
 
index c3825219c1efbd4939af6d18bcad6efcfa74f406..1994be92c66257da18c31502bda605be170fd092 100644 (file)
@@ -493,7 +493,24 @@ static void update_refs_for_switch(struct checkout_opts *opts,
        struct strbuf msg = STRBUF_INIT;
        const char *old_desc;
        if (opts->new_branch) {
-               if (!opts->new_orphan_branch)
+               if (opts->new_orphan_branch) {
+                       if (opts->new_branch_log && !log_all_ref_updates) {
+                               int temp;
+                               char log_file[PATH_MAX];
+                               char *ref_name = mkpath("refs/heads/%s", opts->new_orphan_branch);
+
+                               temp = log_all_ref_updates;
+                               log_all_ref_updates = 1;
+                               if (log_ref_setup(ref_name, log_file, sizeof(log_file))) {
+                                       fprintf(stderr, "Can not do reflog for '%s'\n",
+                                           opts->new_orphan_branch);
+                                       log_all_ref_updates = temp;
+                                       return;
+                               }
+                               log_all_ref_updates = temp;
+                       }
+               }
+               else
                        create_branch(old->name, opts->new_branch, new->name, 0,
                                      opts->new_branch_log, opts->track);
                new->name = opts->new_branch;
@@ -517,6 +534,14 @@ static void update_refs_for_switch(struct checkout_opts *opts,
                                        opts->new_branch ? " a new" : "",
                                        new->name);
                }
+               if (old->path && old->name) {
+                       char log_file[PATH_MAX], ref_file[PATH_MAX];
+
+                       git_snpath(log_file, sizeof(log_file), "logs/%s", old->path);
+                       git_snpath(ref_file, sizeof(ref_file), "%s", old->path);
+                       if (!file_exists(ref_file) && file_exists(log_file))
+                               remove_path(log_file);
+               }
        } else if (strcmp(new->name, "HEAD")) {
                update_ref(msg.buf, "HEAD", new->commit->object.sha1, NULL,
                           REF_NODEREF, DIE_ON_ERR);
@@ -611,7 +636,8 @@ static int check_tracking_name(const char *refname, const unsigned char *sha1,
 
 static const char *unique_tracking_name(const char *name)
 {
-       struct tracking_name_data cb_data = { name, NULL, 1 };
+       struct tracking_name_data cb_data = { NULL, NULL, 1 };
+       cb_data.name = name;
        for_each_ref(check_tracking_name, &cb_data);
        if (cb_data.unique)
                return cb_data.remote;
@@ -684,8 +710,8 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
        if (opts.new_orphan_branch) {
                if (opts.new_branch)
                        die("--orphan and -b are mutually exclusive");
-               if (opts.track > 0 || opts.new_branch_log)
-                       die("--orphan cannot be used with -t or -l");
+               if (opts.track > 0)
+                       die("--orphan cannot be used with -t");
                opts.new_branch = opts.new_orphan_branch;
        }
 
index 44579224270c194a0ab4a90280515ec55e3b6842..efb1e6faa539935de0e9dc590237185a1d222322 100644 (file)
@@ -464,7 +464,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
        set_git_dir(make_absolute_path(git_dir));
 
        if (0 <= option_verbosity)
-               printf("Cloning into %s...\n", get_git_dir());
+               printf("Cloning into %s%s...\n",
+                      option_bare ? "bare repository " : "", dir);
        init_db(option_template, INIT_DB_QUIET);
 
        /*
index ddf77e48e1d9fa1e6db0ac49008857b85a9b5e41..0e3ae3c11d7d3a582f3cb549d7d17aa1aea67d56 100644 (file)
@@ -57,7 +57,7 @@ static struct lock_file false_lock; /* used only for partial commits */
 static enum {
        COMMIT_AS_IS = 1,
        COMMIT_NORMAL,
-       COMMIT_PARTIAL,
+       COMMIT_PARTIAL
 } commit_style;
 
 static const char *logfile, *force_author;
@@ -78,7 +78,7 @@ static char *untracked_files_arg, *force_date;
 static enum {
        CLEANUP_SPACE,
        CLEANUP_NONE,
-       CLEANUP_ALL,
+       CLEANUP_ALL
 } cleanup_mode;
 static char *cleanup_arg;
 
@@ -91,8 +91,9 @@ static int null_termination;
 static enum {
        STATUS_FORMAT_LONG,
        STATUS_FORMAT_SHORT,
-       STATUS_FORMAT_PORCELAIN,
+       STATUS_FORMAT_PORCELAIN
 } status_format = STATUS_FORMAT_LONG;
+static int status_show_branch;
 
 static int opt_parse_m(const struct option *opt, const char *arg, int unset)
 {
@@ -134,6 +135,7 @@ static struct option builtin_commit_options[] = {
        OPT_BOOLEAN(0, "dry-run", &dry_run, "show what would be committed"),
        OPT_SET_INT(0, "short", &status_format, "show status concisely",
                    STATUS_FORMAT_SHORT),
+       OPT_BOOLEAN(0, "branch", &status_show_branch, "show branch information"),
        OPT_SET_INT(0, "porcelain", &status_format,
                    "show porcelain output format", STATUS_FORMAT_PORCELAIN),
        OPT_BOOLEAN('z', "null", &null_termination,
@@ -424,7 +426,7 @@ static int run_status(FILE *fp, const char *index_file, const char *prefix, int
 
        switch (status_format) {
        case STATUS_FORMAT_SHORT:
-               wt_shortstatus_print(s, null_termination);
+               wt_shortstatus_print(s, null_termination, status_show_branch);
                break;
        case STATUS_FORMAT_PORCELAIN:
                wt_porcelain_print(s, null_termination);
@@ -462,15 +464,21 @@ static void determine_author_info(void)
                if (!a)
                        die("invalid commit: %s", use_message);
 
-               lb = strstr(a + 8, " <");
-               rb = strstr(a + 8, "> ");
-               eol = strchr(a + 8, '\n');
-               if (!lb || !rb || !eol)
+               lb = strchrnul(a + strlen("\nauthor "), '<');
+               rb = strchrnul(lb, '>');
+               eol = strchrnul(rb, '\n');
+               if (!*lb || !*rb || !*eol)
                        die("invalid commit: %s", use_message);
 
-               name = xstrndup(a + 8, lb - (a + 8));
-               email = xstrndup(lb + 2, rb - (lb + 2));
-               date = xstrndup(rb + 2, eol - (rb + 2));
+               if (lb == a + strlen("\nauthor "))
+                       /* \nauthor <foo@example.com> */
+                       name = xcalloc(1, 1);
+               else
+                       name = xmemdupz(a + strlen("\nauthor "),
+                                       (lb - strlen(" ") -
+                                        (a + strlen("\nauthor "))));
+               email = xmemdupz(lb + strlen("<"), rb - (lb + strlen("<")));
+               date = xmemdupz(rb + strlen("> "), eol - (rb + strlen("> ")));
        }
 
        if (force_author) {
@@ -724,7 +732,8 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
 
        if (use_editor) {
                char index[PATH_MAX];
-               const char *env[2] = { index, NULL };
+               const char *env[2] = { NULL };
+               env[0] =  index;
                snprintf(index, sizeof(index), "GIT_INDEX_FILE=%s", index_file);
                if (launch_editor(git_path(commit_editmsg), NULL, env)) {
                        fprintf(stderr,
@@ -1030,6 +1039,8 @@ int cmd_status(int argc, const char **argv, const char *prefix)
                OPT__VERBOSE(&verbose),
                OPT_SET_INT('s', "short", &status_format,
                            "show status concisely", STATUS_FORMAT_SHORT),
+               OPT_BOOLEAN('b', "branch", &status_show_branch,
+                           "show branch information"),
                OPT_SET_INT(0, "porcelain", &status_format,
                            "show porcelain output format",
                            STATUS_FORMAT_PORCELAIN),
@@ -1082,7 +1093,7 @@ int cmd_status(int argc, const char **argv, const char *prefix)
 
        switch (status_format) {
        case STATUS_FORMAT_SHORT:
-               wt_shortstatus_print(&s, null_termination);
+               wt_shortstatus_print(&s, null_termination, status_show_branch);
                break;
        case STATUS_FORMAT_PORCELAIN:
                wt_porcelain_print(&s, null_termination);
index 8470850415c14cad8ceeca9f6baef46ab6feabc3..5cb369cfd1fbca1ceeee06b751c2b047f8a0e5b5 100644 (file)
@@ -574,9 +574,10 @@ static void find_non_local_tags(struct transport *transport,
 {
        struct string_list existing_refs = { NULL, 0, 0, 0 };
        struct string_list remote_refs = { NULL, 0, 0, 0 };
-       struct tag_data data = {head, tail};
+       struct tag_data data;
        const struct ref *ref;
        struct string_list_item *item = NULL;
+       data.head = head; data.tail = tail;
 
        for_each_ref(add_existing, &existing_refs);
        for (ref = transport_get_remote_refs(transport); ref; ref = ref->next) {
@@ -778,7 +779,8 @@ static int get_remote_group(const char *key, const char *value, void *priv)
 static int add_remote_or_group(const char *name, struct string_list *list)
 {
        int prev_nr = list->nr;
-       struct remote_group_data g = { name, list };
+       struct remote_group_data g;
+       g.name = name; g.list = list;
 
        git_config(get_remote_group, &g);
        if (list->nr == prev_nr) {
index 379a03131fbd84fb2b45a6b05e62daf82b7e4947..44204257c72afd3a6a24966b9a11cf9f2374e9ce 100644 (file)
@@ -202,35 +202,10 @@ static void shortlog(const char *name, unsigned char *sha1,
        string_list_clear(&subjects, 0);
 }
 
-int fmt_merge_msg(int merge_summary, struct strbuf *in, struct strbuf *out) {
-       int limit = 20, i = 0, pos = 0;
+static void do_fmt_merge_msg_title(struct strbuf *out,
+       const char *current_branch) {
+       int i = 0;
        char *sep = "";
-       unsigned char head_sha1[20];
-       const char *current_branch;
-
-       /* get current branch */
-       current_branch = resolve_ref("HEAD", head_sha1, 1, NULL);
-       if (!current_branch)
-               die("No current branch");
-       if (!prefixcmp(current_branch, "refs/heads/"))
-               current_branch += 11;
-
-       /* get a line */
-       while (pos < in->len) {
-               int len;
-               char *newline, *p = in->buf + pos;
-
-               newline = strchr(p, '\n');
-               len = newline ? newline - p : strlen(p);
-               pos += len + !!newline;
-               i++;
-               p[len] = 0;
-               if (handle_line(p))
-                       die ("Error in line %d: %.*s", i, len, p);
-       }
-
-       if (!srcs.nr)
-               return 0;
 
        strbuf_addstr(out, "Merge ");
        for (i = 0; i < srcs.nr; i++) {
@@ -278,6 +253,40 @@ int fmt_merge_msg(int merge_summary, struct strbuf *in, struct strbuf *out) {
                strbuf_addch(out, '\n');
        else
                strbuf_addf(out, " into %s\n", current_branch);
+}
+
+static int do_fmt_merge_msg(int merge_title, int merge_summary,
+       struct strbuf *in, struct strbuf *out) {
+       int limit = 20, i = 0, pos = 0;
+       unsigned char head_sha1[20];
+       const char *current_branch;
+
+       /* get current branch */
+       current_branch = resolve_ref("HEAD", head_sha1, 1, NULL);
+       if (!current_branch)
+               die("No current branch");
+       if (!prefixcmp(current_branch, "refs/heads/"))
+               current_branch += 11;
+
+       /* get a line */
+       while (pos < in->len) {
+               int len;
+               char *newline, *p = in->buf + pos;
+
+               newline = strchr(p, '\n');
+               len = newline ? newline - p : strlen(p);
+               pos += len + !!newline;
+               i++;
+               p[len] = 0;
+               if (handle_line(p))
+                       die ("Error in line %d: %.*s", i, len, p);
+       }
+
+       if (!srcs.nr)
+               return 0;
+
+       if (merge_title)
+               do_fmt_merge_msg_title(out, current_branch);
 
        if (merge_summary) {
                struct commit *head;
@@ -289,6 +298,9 @@ int fmt_merge_msg(int merge_summary, struct strbuf *in, struct strbuf *out) {
                rev.ignore_merges = 1;
                rev.limited = 1;
 
+               if (suffixcmp(out->buf, "\n"))
+                       strbuf_addch(out, '\n');
+
                for (i = 0; i < origins.nr; i++)
                        shortlog(origins.items[i].string, origins.items[i].util,
                                        head, &rev, limit, out);
@@ -296,6 +308,14 @@ int fmt_merge_msg(int merge_summary, struct strbuf *in, struct strbuf *out) {
        return 0;
 }
 
+int fmt_merge_msg(int merge_summary, struct strbuf *in, struct strbuf *out) {
+       return do_fmt_merge_msg(1, merge_summary, in, out);
+}
+
+int fmt_merge_msg_shortlog(struct strbuf *in, struct strbuf *out) {
+       return do_fmt_merge_msg(0, 1, in, out);
+}
+
 int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix)
 {
        const char *inpath = NULL;
index 3a97953177eee234c310bbf37bab5f2d7e03e5aa..a2b28c6962be8ea6f8882bae3d4fde9d972e78d2 100644 (file)
@@ -552,10 +552,10 @@ static void grab_values(struct atom_value *val, int deref, struct object *obj, v
                grab_person("committer", val, deref, obj, buf, sz);
                break;
        case OBJ_TREE:
-               // grab_tree_values(val, deref, obj, buf, sz);
+               /* grab_tree_values(val, deref, obj, buf, sz); */
                break;
        case OBJ_BLOB:
-               // grab_blob_values(val, deref, obj, buf, sz);
+               /* grab_blob_values(val, deref, obj, buf, sz); */
                break;
        default:
                die("Eh?  Object of type %d?", obj->type);
index b194ea3cea531f3a6c81d6d27b5b3028641ba9ce..d0a73da07a0d7d8d4b01adcfcda88a98130910a0 100644 (file)
@@ -724,11 +724,15 @@ static int file_callback(const struct option *opt, const char *arg, int unset)
        if (!patterns)
                die_errno("cannot open '%s'", arg);
        while (strbuf_getline(&sb, patterns, '\n') == 0) {
+               char *s;
+               size_t len;
+
                /* ignore empty line like grep does */
                if (sb.len == 0)
                        continue;
-               append_grep_pattern(grep_opt, strbuf_detach(&sb, NULL), arg,
-                                   ++lno, GREP_PATTERN);
+
+               s = strbuf_detach(&sb, &len);
+               append_grep_pat(grep_opt, s, len, arg, ++lno, GREP_PATTERN);
        }
        fclose(patterns);
        strbuf_release(&sb);
index 3182a2bec466c50a9a1db1e91888bad3335414b1..a9836b00aec8df68e7a615dce83d3c60f73e242f 100644 (file)
@@ -26,7 +26,7 @@ enum help_format {
        HELP_FORMAT_NONE,
        HELP_FORMAT_MAN,
        HELP_FORMAT_INFO,
-       HELP_FORMAT_WEB,
+       HELP_FORMAT_WEB
 };
 
 static int show_all = 0;
index c0fbcdcf4f3447c29c4058907ea2e25fa6d225a6..080404769365fef8176c75fff5f526cf6138df6a 100644 (file)
@@ -26,8 +26,9 @@ static int show_killed;
 static int show_valid_bit;
 static int line_terminator = '\n';
 
+static const char *prefix;
+static int max_prefix_len;
 static int prefix_len;
-static int prefix_offset;
 static const char **pathspec;
 static int error_unmatch;
 static char *ps_matched;
@@ -43,10 +44,15 @@ static const char *tag_modified = "";
 static const char *tag_skip_worktree = "";
 static const char *tag_resolve_undo = "";
 
+static void write_name(const char* name, size_t len)
+{
+       write_name_quoted_relative(name, len, prefix, prefix_len, stdout,
+                       line_terminator);
+}
+
 static void show_dir_entry(const char *tag, struct dir_entry *ent)
 {
-       int len = prefix_len;
-       int offset = prefix_offset;
+       int len = max_prefix_len;
 
        if (len >= ent->len)
                die("git ls-files: internal error - directory entry not superset of prefix");
@@ -55,7 +61,7 @@ static void show_dir_entry(const char *tag, struct dir_entry *ent)
                return;
 
        fputs(tag, stdout);
-       write_name_quoted(ent->name + offset, stdout, line_terminator);
+       write_name(ent->name, ent->len);
 }
 
 static void show_other_files(struct dir_struct *dir)
@@ -121,8 +127,7 @@ static void show_killed_files(struct dir_struct *dir)
 
 static void show_ce_entry(const char *tag, struct cache_entry *ce)
 {
-       int len = prefix_len;
-       int offset = prefix_offset;
+       int len = max_prefix_len;
 
        if (len >= ce_namelen(ce))
                die("git ls-files: internal error - cache entry not superset of prefix");
@@ -156,20 +161,19 @@ static void show_ce_entry(const char *tag, struct cache_entry *ce)
                       find_unique_abbrev(ce->sha1,abbrev),
                       ce_stage(ce));
        }
-       write_name_quoted(ce->name + offset, stdout, line_terminator);
+       write_name(ce->name, ce_namelen(ce));
 }
 
 static int show_one_ru(struct string_list_item *item, void *cbdata)
 {
-       int offset = prefix_offset;
        const char *path = item->string;
        struct resolve_undo_info *ui = item->util;
        int i, len;
 
        len = strlen(path);
-       if (len < prefix_len)
+       if (len < max_prefix_len)
                return 0; /* outside of the prefix */
-       if (!match_pathspec(pathspec, path, len, prefix_len, ps_matched))
+       if (!match_pathspec(pathspec, path, len, max_prefix_len, ps_matched))
                return 0; /* uninterested */
        for (i = 0; i < 3; i++) {
                if (!ui->mode[i])
@@ -177,19 +181,19 @@ static int show_one_ru(struct string_list_item *item, void *cbdata)
                printf("%s%06o %s %d\t", tag_resolve_undo, ui->mode[i],
                       find_unique_abbrev(ui->sha1[i], abbrev),
                       i + 1);
-               write_name_quoted(path + offset, stdout, line_terminator);
+               write_name(path, len);
        }
        return 0;
 }
 
-static void show_ru_info(const char *prefix)
+static void show_ru_info(void)
 {
        if (!the_index.resolve_undo)
                return;
        for_each_string_list(show_one_ru, the_index.resolve_undo, NULL);
 }
 
-static void show_files(struct dir_struct *dir, const char *prefix)
+static void show_files(struct dir_struct *dir)
 {
        int i;
 
@@ -243,7 +247,7 @@ static void show_files(struct dir_struct *dir, const char *prefix)
  */
 static void prune_cache(const char *prefix)
 {
-       int pos = cache_name_pos(prefix, prefix_len);
+       int pos = cache_name_pos(prefix, max_prefix_len);
        unsigned int first, last;
 
        if (pos < 0)
@@ -256,7 +260,7 @@ static void prune_cache(const char *prefix)
        while (last > first) {
                int next = (last + first) >> 1;
                struct cache_entry *ce = active_cache[next];
-               if (!strncmp(ce->name, prefix, prefix_len)) {
+               if (!strncmp(ce->name, prefix, max_prefix_len)) {
                        first = next+1;
                        continue;
                }
@@ -265,11 +269,16 @@ static void prune_cache(const char *prefix)
        active_nr = last;
 }
 
-static const char *verify_pathspec(const char *prefix)
+static const char *pathspec_prefix(const char *prefix)
 {
        const char **p, *n, *prev;
        unsigned long max;
 
+       if (!pathspec) {
+               max_prefix_len = prefix ? strlen(prefix) : 0;
+               return prefix;
+       }
+
        prev = NULL;
        max = PATH_MAX;
        for (p = pathspec; (n = *p) != NULL; p++) {
@@ -291,10 +300,7 @@ static const char *verify_pathspec(const char *prefix)
                }
        }
 
-       if (prefix_offset > max || memcmp(prev, prefix, prefix_offset))
-               die("git ls-files: cannot generate relative filenames containing '..'");
-
-       prefix_len = max;
+       max_prefix_len = max;
        return max ? xmemdupz(prev, max) : NULL;
 }
 
@@ -374,7 +380,7 @@ void overlay_tree_on_cache(const char *tree_name, const char *prefix)
        }
 }
 
-int report_path_error(const char *ps_matched, const char **pathspec, int prefix_offset)
+int report_path_error(const char *ps_matched, const char **pathspec, int prefix_len)
 {
        /*
         * Make sure all pathspec matched; otherwise it is an error.
@@ -404,7 +410,7 @@ int report_path_error(const char *ps_matched, const char **pathspec, int prefix_
                        continue;
 
                error("pathspec '%s' did not match any file(s) known to git.",
-                     pathspec[num] + prefix_offset);
+                     pathspec[num] + prefix_len);
                errors++;
        }
        return errors;
@@ -456,9 +462,10 @@ static int option_parse_exclude_standard(const struct option *opt,
        return 0;
 }
 
-int cmd_ls_files(int argc, const char **argv, const char *prefix)
+int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
 {
        int require_work_tree = 0, show_tag = 0;
+       const char *max_prefix;
        struct dir_struct dir;
        struct option builtin_ls_files_options[] = {
                { OPTION_CALLBACK, 'z', NULL, NULL, NULL,
@@ -504,7 +511,7 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)
                { OPTION_CALLBACK, 0, "exclude-standard", &dir, NULL,
                        "add the standard git exclusions",
                        PARSE_OPT_NOARG, option_parse_exclude_standard },
-               { OPTION_SET_INT, 0, "full-name", &prefix_offset, NULL,
+               { OPTION_SET_INT, 0, "full-name", &prefix_len, NULL,
                        "make the output relative to the project top directory",
                        PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL },
                OPT_BOOLEAN(0, "error-unmatch", &error_unmatch,
@@ -516,8 +523,9 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)
        };
 
        memset(&dir, 0, sizeof(dir));
+       prefix = cmd_prefix;
        if (prefix)
-               prefix_offset = strlen(prefix);
+               prefix_len = strlen(prefix);
        git_config(git_default_config, NULL);
 
        if (read_cache() < 0)
@@ -555,9 +563,8 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)
        if (pathspec)
                strip_trailing_slash_from_submodules();
 
-       /* Verify that the pathspec matches the prefix */
-       if (pathspec)
-               prefix = verify_pathspec(prefix);
+       /* Find common prefix for all pathspec's */
+       max_prefix = pathspec_prefix(prefix);
 
        /* Treat unmatching pathspec elements as errors */
        if (pathspec && error_unmatch) {
@@ -575,8 +582,8 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)
              show_killed | show_modified | show_resolve_undo))
                show_cached = 1;
 
-       if (prefix)
-               prune_cache(prefix);
+       if (max_prefix)
+               prune_cache(max_prefix);
        if (with_tree) {
                /*
                 * Basic sanity check; show-stages and show-unmerged
@@ -584,15 +591,15 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)
                 */
                if (show_stage || show_unmerged)
                        die("ls-files --with-tree is incompatible with -s or -u");
-               overlay_tree_on_cache(with_tree, prefix);
+               overlay_tree_on_cache(with_tree, max_prefix);
        }
-       show_files(&dir, prefix);
+       show_files(&dir);
        if (show_resolve_undo)
-               show_ru_info(prefix);
+               show_ru_info();
 
        if (ps_matched) {
                int bad;
-               bad = report_path_error(ps_matched, pathspec, prefix_offset);
+               bad = report_path_error(ps_matched, pathspec, prefix_len);
                if (bad)
                        fprintf(stderr, "Did you forget to 'git add'?\n");
 
index 8ee91eb547c2bafedd573045c977cc7d0469c95f..34480cfad6a8842d65ae61f6257c597a2a987b33 100644 (file)
@@ -5,7 +5,7 @@
 
 static const char ls_remote_usage[] =
 "git ls-remote [--heads] [--tags]  [-u <exec> | --upload-pack <exec>]\n"
-"                     [<repository> [<refs>...]]";
+"                     [-q|--quiet] [<repository> [<refs>...]]";
 
 /*
  * Is there one among the list of patterns that match the tail part
@@ -34,6 +34,7 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)
        const char *dest = NULL;
        int nongit;
        unsigned flags = 0;
+       int quiet = 0;
        const char *uploadpack = NULL;
        const char **pattern = NULL;
 
@@ -67,6 +68,10 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)
                                flags |= REF_NORMAL;
                                continue;
                        }
+                       if (!strcmp("--quiet", arg) || !strcmp("-q", arg)) {
+                               quiet = 1;
+                               continue;
+                       }
                        usage(ls_remote_usage);
                }
                dest = arg;
@@ -99,6 +104,9 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)
        ref = transport_get_remote_refs(transport);
        if (transport_disconnect(transport))
                return 1;
+
+       if (!dest && !quiet)
+               fprintf(stderr, "From %s\n", *remote->url);
        for ( ; ref; ref = ref->next) {
                if (!check_ref_type(ref, flags))
                        continue;
index 4a9729b9b388a1e589e9250d8157e723c68140de..2320d981ceab220e287e42c21f3c9f3be5b3722f 100644 (file)
@@ -17,10 +17,10 @@ static struct strbuf name = STRBUF_INIT;
 static struct strbuf email = STRBUF_INIT;
 
 static enum  {
-       TE_DONTCARE, TE_QP, TE_BASE64,
+       TE_DONTCARE, TE_QP, TE_BASE64
 } transfer_encoding;
 static enum  {
-       TYPE_TEXT, TYPE_OTHER,
+       TYPE_TEXT, TYPE_OTHER
 } message_type;
 
 static struct strbuf charset = STRBUF_INIT;
index 610849a6533c6fd2d3d2e8d3f8d233757174aabd..b8e9e5ba01658bbdd6e9d712b00da902b6f33fbe 100644 (file)
@@ -25,7 +25,7 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix)
        const char *names[3] = { NULL, NULL, NULL };
        mmfile_t mmfs[3];
        mmbuffer_t result = {NULL, 0};
-       xmparam_t xmp = {{XDF_NEED_MINIMAL}};
+       xmparam_t xmp = {{0}};
        int ret = 0, i = 0, to_stdout = 0;
        int quiet = 0;
        int nongit;
index a4a4f2ce4c3f147062070c2acc08eaf9f4d40be8..fc00d794d641c146023a78d7d8e4143f0fdac302 100644 (file)
@@ -106,7 +106,7 @@ static void show_diff(struct merge_list *entry)
        xdemitconf_t xecfg;
        xdemitcb_t ecb;
 
-       xpp.flags = XDF_NEED_MINIMAL;
+       xpp.flags = 0;
        memset(&xecfg, 0, sizeof(xecfg));
        xecfg.ctxlen = 3;
        ecb.outf = show_outf;
index 37d414b3ba95162953aec001e6036956eb1a97f6..37ce4f589f6582abf0d1ef5bd263f590a16853d5 100644 (file)
@@ -982,7 +982,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                reset_hard(remote_head->sha1, 0);
                return 0;
        } else {
-               struct strbuf msg = STRBUF_INIT;
+               struct strbuf merge_names = STRBUF_INIT;
 
                /* We are invoked directly as the first-class UI. */
                head_arg = "HEAD";
@@ -995,13 +995,17 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                 * codepath so we discard the error in this
                 * loop.
                 */
-               if (!have_message) {
-                       for (i = 0; i < argc; i++)
-                               merge_name(argv[i], &msg);
-                       fmt_merge_msg(option_log, &msg, &merge_msg);
-                       if (merge_msg.len)
-                               strbuf_setlen(&merge_msg, merge_msg.len-1);
-               }
+               for (i = 0; i < argc; i++)
+                       merge_name(argv[i], &merge_names);
+
+               if (have_message && option_log)
+                       fmt_merge_msg_shortlog(&merge_names, &merge_msg);
+               else if (!have_message)
+                       fmt_merge_msg(option_log, &merge_names, &merge_msg);
+
+
+               if (!(have_message && !option_log) && merge_msg.len)
+                       strbuf_setlen(&merge_msg, merge_msg.len-1);
        }
 
        if (head_invalid || !argc)
index 52b72fca687d42dc09d1d79a8e76584a55c0c546..648033c27e75e9a19e9fd3a2fa21b2e26b19d5ac 100644 (file)
@@ -26,7 +26,7 @@ static const char * const git_notes_usage[] = {
        "git notes [--ref <notes_ref>] edit [<object>]",
        "git notes [--ref <notes_ref>] show [<object>]",
        "git notes [--ref <notes_ref>] remove [<object>]",
-       "git notes [--ref <notes_ref>] prune",
+       "git notes [--ref <notes_ref>] prune [-n | -v]",
        NULL
 };
 
@@ -67,7 +67,7 @@ static const char * const git_notes_remove_usage[] = {
 };
 
 static const char * const git_notes_prune_usage[] = {
-       "git notes prune",
+       "git notes prune [<options>]",
        NULL
 };
 
@@ -416,7 +416,7 @@ int notes_copy_from_stdin(int force, const char *rewrite_cmd)
 {
        struct strbuf buf = STRBUF_INIT;
        struct notes_rewrite_cfg *c = NULL;
-       struct notes_tree *t;
+       struct notes_tree *t = NULL;
        int ret = 0;
 
        if (rewrite_cmd) {
@@ -792,7 +792,10 @@ static int remove_cmd(int argc, const char **argv, const char *prefix)
 static int prune(int argc, const char **argv, const char *prefix)
 {
        struct notes_tree *t;
+       int show_only = 0, verbose = 0;
        struct option options[] = {
+               OPT_BOOLEAN('n', NULL, &show_only, "do not remove, show only"),
+               OPT_BOOLEAN('v', NULL, &verbose, "report pruned notes"),
                OPT_END()
        };
 
@@ -806,8 +809,10 @@ static int prune(int argc, const char **argv, const char *prefix)
 
        t = init_notes_check("prune");
 
-       prune_notes(t);
-       commit_notes(t, "Notes removed by 'git notes prune'");
+       prune_notes(t, (verbose ? NOTES_PRUNE_VERBOSE : 0) |
+               (show_only ? NOTES_PRUNE_VERBOSE|NOTES_PRUNE_DRYRUN : 0) );
+       if (!show_only)
+               commit_notes(t, "Notes removed by 'git notes prune'");
        free_notes(t);
        return 0;
 }
index 214d7ef2b12d41e8d70539fe4a4b16fde71a780a..0e8167311884d38c48a9421bf8aede89e7f2c22d 100644 (file)
@@ -1529,6 +1529,8 @@ static void try_to_free_from_threads(size_t size)
        read_unlock();
 }
 
+try_to_free_t old_try_to_free_routine;
+
 /*
  * The main thread waits on the condition that (at least) one of the workers
  * has stopped working (which is indicated in the .working member of
@@ -1563,12 +1565,12 @@ static void init_threaded_search(void)
        pthread_mutex_init(&cache_mutex, NULL);
        pthread_mutex_init(&progress_mutex, NULL);
        pthread_cond_init(&progress_cond, NULL);
-       set_try_to_free_routine(try_to_free_from_threads);
+       old_try_to_free_routine = set_try_to_free_routine(try_to_free_from_threads);
 }
 
 static void cleanup_threaded_search(void)
 {
-       set_try_to_free_routine(NULL);
+       set_try_to_free_routine(old_try_to_free_routine);
        pthread_cond_destroy(&progress_cond);
        pthread_mutex_destroy(&read_mutex);
        pthread_mutex_destroy(&cache_mutex);
index bb34757d27f32fff8cb15a8f99d8295b5e38cb9e..5a75af415562b6f8f60c9d89ba6042888211170d 100644 (file)
@@ -17,7 +17,7 @@ enum deny_action {
        DENY_UNCONFIGURED,
        DENY_IGNORE,
        DENY_WARN,
-       DENY_REFUSE,
+       DENY_REFUSE
 };
 
 static int deny_deletes;
index 0e99a9957dad39ded17ce98de49b164fdb70eacd..0a52667e0f7cc14d3a760bf0ec5f6857a0ce9051 100644 (file)
@@ -16,6 +16,7 @@ static const char * const builtin_remote_usage[] = {
        "git remote [-v | --verbose] show [-n] <name>",
        "git remote prune [-n | --dry-run] <name>",
        "git remote [-v | --verbose] update [-p | --prune] [group | remote]",
+       "git remote set-branches <name> [--add] <branch>...",
        "git remote set-url <name> <newurl> [<oldurl>]",
        "git remote set-url --add <name> <newurl>",
        "git remote set-url --delete <name> <url>",
@@ -42,6 +43,12 @@ static const char * const builtin_remote_sethead_usage[] = {
        NULL
 };
 
+static const char * const builtin_remote_setbranches_usage[] = {
+       "git remote set-branches <name> <branch>...",
+       "git remote set-branches --add <name> <branch>...",
+       NULL
+};
+
 static const char * const builtin_remote_show_usage[] = {
        "git remote show [<options>] <name>",
        NULL
@@ -110,6 +117,20 @@ enum {
        TAGS_SET = 2
 };
 
+static int add_branch(const char *key, const char *branchname,
+               const char *remotename, int mirror, struct strbuf *tmp)
+{
+       strbuf_reset(tmp);
+       strbuf_addch(tmp, '+');
+       if (mirror)
+               strbuf_addf(tmp, "refs/%s:refs/%s",
+                               branchname, branchname);
+       else
+               strbuf_addf(tmp, "refs/heads/%s:refs/remotes/%s/%s",
+                               branchname, remotename, branchname);
+       return git_config_set_multivar(key, tmp->buf, "^$", 0);
+}
+
 static int add(int argc, const char **argv)
 {
        int fetch = 0, mirror = 0, fetch_tags = TAGS_DEFAULT;
@@ -162,17 +183,8 @@ static int add(int argc, const char **argv)
        if (track.nr == 0)
                string_list_append("*", &track);
        for (i = 0; i < track.nr; i++) {
-               struct string_list_item *item = track.items + i;
-
-               strbuf_reset(&buf2);
-               strbuf_addch(&buf2, '+');
-               if (mirror)
-                       strbuf_addf(&buf2, "refs/%s:refs/%s",
-                                       item->string, item->string);
-               else
-                       strbuf_addf(&buf2, "refs/heads/%s:refs/remotes/%s/%s",
-                                       item->string, name, item->string);
-               if (git_config_set_multivar(buf.buf, buf2.buf, "^$", 0))
+               if (add_branch(buf.buf, track.items[i].string,
+                               name, mirror, &buf2))
                        return 1;
        }
 
@@ -336,7 +348,7 @@ struct push_info {
                PUSH_STATUS_UPTODATE,
                PUSH_STATUS_FASTFORWARD,
                PUSH_STATUS_OUTOFDATE,
-               PUSH_STATUS_NOTQUERIED,
+               PUSH_STATUS_NOTQUERIED
        } status;
 };
 
@@ -724,11 +736,14 @@ static int rm(int argc, const char **argv)
        struct known_remotes known_remotes = { NULL, NULL };
        struct string_list branches = { NULL, 0, 0, 1 };
        struct string_list skipped = { NULL, 0, 0, 1 };
-       struct branches_for_remote cb_data = {
-               NULL, &branches, &skipped, &known_remotes
-       };
+       struct branches_for_remote cb_data;
        int i, result;
 
+       memset(&cb_data, 0, sizeof(cb_data));
+       cb_data.branches = &branches;
+       cb_data.skipped = &skipped;
+       cb_data.keep = &known_remotes;
+
        if (argc != 2)
                usage_with_options(builtin_remote_rm_usage, options);
 
@@ -1284,6 +1299,72 @@ static int update(int argc, const char **argv)
        return run_command_v_opt(fetch_argv, RUN_GIT_CMD);
 }
 
+static int remove_all_fetch_refspecs(const char *remote, const char *key)
+{
+       return git_config_set_multivar(key, NULL, NULL, 1);
+}
+
+static int add_branches(struct remote *remote, const char **branches,
+                       const char *key)
+{
+       const char *remotename = remote->name;
+       int mirror = remote->mirror;
+       struct strbuf refspec = STRBUF_INIT;
+
+       for (; *branches; branches++)
+               if (add_branch(key, *branches, remotename, mirror, &refspec)) {
+                       strbuf_release(&refspec);
+                       return 1;
+               }
+
+       strbuf_release(&refspec);
+       return 0;
+}
+
+static int set_remote_branches(const char *remotename, const char **branches,
+                               int add_mode)
+{
+       struct strbuf key = STRBUF_INIT;
+       struct remote *remote;
+
+       strbuf_addf(&key, "remote.%s.fetch", remotename);
+
+       if (!remote_is_configured(remotename))
+               die("No such remote '%s'", remotename);
+       remote = remote_get(remotename);
+
+       if (!add_mode && remove_all_fetch_refspecs(remotename, key.buf)) {
+               strbuf_release(&key);
+               return 1;
+       }
+       if (add_branches(remote, branches, key.buf)) {
+               strbuf_release(&key);
+               return 1;
+       }
+
+       strbuf_release(&key);
+       return 0;
+}
+
+static int set_branches(int argc, const char **argv)
+{
+       int add_mode = 0;
+       struct option options[] = {
+               OPT_BOOLEAN('\0', "add", &add_mode, "add branch"),
+               OPT_END()
+       };
+
+       argc = parse_options(argc, argv, NULL, options,
+                            builtin_remote_setbranches_usage, 0);
+       if (argc == 0) {
+               error("no remote specified");
+               usage_with_options(builtin_remote_seturl_usage, options);
+       }
+       argv[argc] = NULL;
+
+       return set_remote_branches(argv[0], argv + 1, add_mode);
+}
+
 static int set_url(int argc, const char **argv)
 {
        int i, push_mode = 0, add_mode = 0, delete_mode = 0;
@@ -1449,6 +1530,8 @@ int cmd_remote(int argc, const char **argv, const char *prefix)
                result = rm(argc, argv);
        else if (!strcmp(argv[0], "set-head"))
                result = set_head(argc, argv);
+       else if (!strcmp(argv[0], "set-branches"))
+               result = set_branches(argc, argv);
        else if (!strcmp(argv[0], "set-url"))
                result = set_url(argc, argv);
        else if (!strcmp(argv[0], "show"))
index 34f9acee910406c7ba0bf9eed76267a0cf8f46c2..0048f9ef7fee24e5e058ef226f3b0fc93703fcf1 100644 (file)
@@ -89,7 +89,7 @@ static int diff_two(const char *file1, const char *label1,
        printf("--- a/%s\n+++ b/%s\n", label1, label2);
        fflush(stdout);
        memset(&xpp, 0, sizeof(xpp));
-       xpp.flags = XDF_NEED_MINIMAL;
+       xpp.flags = 0;
        memset(&xecfg, 0, sizeof(xecfg));
        xecfg.ctxlen = 3;
        ecb.outf = outf;
diff --git a/cache.h b/cache.h
index c96602305f94b50e3f39be124c5e8d5af5a36f51..5e55367bf596d49a0e507d820aa3c104b9a7b6d5 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -361,7 +361,7 @@ enum object_type {
        OBJ_OFS_DELTA = 6,
        OBJ_REF_DELTA = 7,
        OBJ_ANY,
-       OBJ_MAX,
+       OBJ_MAX
 };
 
 static inline enum object_type object_type(unsigned int mode)
@@ -556,7 +556,7 @@ extern int core_apply_sparse_checkout;
 enum safe_crlf {
        SAFE_CRLF_FALSE = 0,
        SAFE_CRLF_FAIL = 1,
-       SAFE_CRLF_WARN = 2,
+       SAFE_CRLF_WARN = 2
 };
 
 extern enum safe_crlf safe_crlf;
@@ -567,21 +567,21 @@ enum branch_track {
        BRANCH_TRACK_REMOTE,
        BRANCH_TRACK_ALWAYS,
        BRANCH_TRACK_EXPLICIT,
-       BRANCH_TRACK_OVERRIDE,
+       BRANCH_TRACK_OVERRIDE
 };
 
 enum rebase_setup_type {
        AUTOREBASE_NEVER = 0,
        AUTOREBASE_LOCAL,
        AUTOREBASE_REMOTE,
-       AUTOREBASE_ALWAYS,
+       AUTOREBASE_ALWAYS
 };
 
 enum push_default_type {
        PUSH_DEFAULT_NOTHING = 0,
        PUSH_DEFAULT_MATCHING,
        PUSH_DEFAULT_TRACKING,
-       PUSH_DEFAULT_CURRENT,
+       PUSH_DEFAULT_CURRENT
 };
 
 extern enum branch_track git_branch_track;
@@ -590,7 +590,7 @@ extern enum push_default_type push_default;
 
 enum object_creation_mode {
        OBJECT_CREATION_USES_HARDLINKS = 0,
-       OBJECT_CREATION_USES_RENAMES = 1,
+       OBJECT_CREATION_USES_RENAMES = 1
 };
 
 extern enum object_creation_mode object_creation_mode;
@@ -670,7 +670,7 @@ enum sharedrepo {
        OLD_PERM_GROUP      = 1,
        OLD_PERM_EVERYBODY  = 2,
        PERM_GROUP          = 0660,
-       PERM_EVERYBODY      = 0664,
+       PERM_EVERYBODY      = 0664
 };
 int git_config_perm(const char *var, const char *value);
 int set_shared_perm(const char *path, int mode);
@@ -882,7 +882,7 @@ struct ref {
                REF_STATUS_REJECT_NODELETE,
                REF_STATUS_UPTODATE,
                REF_STATUS_REMOTE_REJECT,
-               REF_STATUS_EXPECTING_REPORT,
+               REF_STATUS_EXPECTING_REPORT
        } status;
        char *remote_status;
        struct ref *peer_ref; /* when renaming */
index 7557136c820a10570e72f6870ecc7a45c4c7ce36..655fa89d8a7ffe3c3823080f5af3e5e385e95440 100644 (file)
@@ -220,7 +220,7 @@ static void combine_diff(const unsigned char *parent, unsigned int mode,
        parent_file.ptr = grab_blob(parent, mode, &sz);
        parent_file.size = sz;
        memset(&xpp, 0, sizeof(xpp));
-       xpp.flags = XDF_NEED_MINIMAL;
+       xpp.flags = 0;
        memset(&xecfg, 0, sizeof(xecfg));
        memset(&state, 0, sizeof(state));
        state.nmask = nmask;
index 6ef88dcf45d6c5b2cd0f26397db2fc7b8859d581..e958a7c3dfbb3ff76410dc83e47d0ffa950af9ea 100644 (file)
--- a/commit.h
+++ b/commit.h
@@ -60,7 +60,7 @@ enum cmit_fmt {
        CMIT_FMT_EMAIL,
        CMIT_FMT_USERFORMAT,
 
-       CMIT_FMT_UNSPECIFIED,
+       CMIT_FMT_UNSPECIFIED
 };
 
 struct pretty_print_context
index 0e3e74304138ab2f279c74599ee934ade72ae37d..3b2477be5f658be665f19a12b48cc47fa07d1c6b 100644 (file)
@@ -80,7 +80,7 @@ static inline int fork(void)
 static inline unsigned int alarm(unsigned int seconds)
 { return 0; }
 static inline int fsync(int fd)
-{ return 0; }
+{ return _commit(fd); }
 static inline int getppid(void)
 { return 1; }
 static inline void sync(void)
@@ -89,7 +89,7 @@ static inline int getuid()
 { return 1; }
 static inline struct passwd *getpwnam(const char *name)
 { return NULL; }
-static inline int fcntl(int fd, int cmd, long arg)
+static inline int fcntl(int fd, int cmd, ...)
 {
        if (cmd == F_GETFD || cmd == F_SETFD)
                return 0;
index 0f949fc4250b5de4a3545931506fc48373ad8c6b..010e875ec4dd8d7154a0911661570165ac1ae874 100644 (file)
@@ -16,6 +16,7 @@
 static unsigned __stdcall win32_start_routine(void *arg)
 {
        pthread_t *thread = arg;
+       thread->tid = GetCurrentThreadId();
        thread->arg = thread->start_routine(thread->arg);
        return 0;
 }
@@ -49,6 +50,13 @@ int win32_pthread_join(pthread_t *thread, void **value_ptr)
        }
 }
 
+pthread_t pthread_self(void)
+{
+       pthread_t t = { 0 };
+       t.tid = GetCurrentThreadId();
+       return t;
+}
+
 int pthread_cond_init(pthread_cond_t *cond, const void *unused)
 {
        cond->waiters = 0;
index a45f8d66df8d1e452d9392945bf12a74c32bbca9..2e205485570bf62a11112c665624203207c724a9 100644 (file)
@@ -58,6 +58,7 @@ typedef struct {
        HANDLE handle;
        void *(*start_routine)(void*);
        void *arg;
+       DWORD tid;
 } pthread_t;
 
 extern int pthread_create(pthread_t *thread, const void *unused,
@@ -71,4 +72,28 @@ extern int pthread_create(pthread_t *thread, const void *unused,
 
 extern int win32_pthread_join(pthread_t *thread, void **value_ptr);
 
+#define pthread_equal(t1, t2) ((t1).tid == (t2).tid)
+extern pthread_t pthread_self(void);
+
+static inline int pthread_exit(void *ret)
+{
+       ExitThread((DWORD)ret);
+}
+
+typedef DWORD pthread_key_t;
+static inline int pthread_key_create(pthread_key_t *keyp, void (*destructor)(void *value))
+{
+       return (*keyp = TlsAlloc()) == TLS_OUT_OF_INDEXES ? EAGAIN : 0;
+}
+
+static inline int pthread_setspecific(pthread_key_t key, const void *value)
+{
+       return TlsSetValue(key, (void *)value) ? 0 : EINVAL;
+}
+
+static inline void *pthread_getspecific(pthread_key_t key)
+{
+       return TlsGetValue(key);
+}
+
 #endif /* PTHREAD_H */
index 0d4b64d076b8041a3701715a83ca46a4675ac9d6..b4e65c32b235eafafefeed1c755be7e1ad5c71ae 100644 (file)
@@ -3,10 +3,12 @@
 
 CC = @CC@
 CFLAGS = @CFLAGS@
+CPPFLAGS = @CPPFLAGS@
 LDFLAGS = @LDFLAGS@
 CC_LD_DYNPATH = @CC_LD_DYNPATH@
 AR = @AR@
 TAR = @TAR@
+DIFF = @DIFF@
 #INSTALL = @INSTALL@           # needs install-sh or install.sh in sources
 TCLTK_PATH = @TCLTK_PATH@
 
@@ -42,6 +44,7 @@ NO_D_TYPE_IN_DIRENT=@NO_D_TYPE_IN_DIRENT@
 NO_SOCKADDR_STORAGE=@NO_SOCKADDR_STORAGE@
 NO_IPV6=@NO_IPV6@
 NO_C99_FORMAT=@NO_C99_FORMAT@
+NO_HSTRERROR=@NO_HSTRERROR@
 NO_STRCASESTR=@NO_STRCASESTR@
 NO_MEMMEM=@NO_MEMMEM@
 NO_STRLCPY=@NO_STRLCPY@
@@ -51,10 +54,15 @@ NO_SETENV=@NO_SETENV@
 NO_UNSETENV=@NO_UNSETENV@
 NO_MKDTEMP=@NO_MKDTEMP@
 NO_MKSTEMPS=@NO_MKSTEMPS@
+NO_INET_NTOP=@NO_INET_NTOP@
+NO_INET_PTON=@NO_INET_PTON@
 NO_ICONV=@NO_ICONV@
 OLD_ICONV=@OLD_ICONV@
 NO_DEFLATE_BOUND=@NO_DEFLATE_BOUND@
+INLINE=@INLINE@
+SOCKLEN_T=@SOCKLEN_T@
 FREAD_READS_DIRECTORIES=@FREAD_READS_DIRECTORIES@
 SNPRINTF_RETURNS_BOGUS=@SNPRINTF_RETURNS_BOGUS@
 NO_PTHREADS=@NO_PTHREADS@
+PTHREAD_CFLAGS=@PTHREAD_CFLAGS@
 PTHREAD_LIBS=@PTHREAD_LIBS@
index 71038fcf1cd04fdfa3bcd133b6fc82974a135141..5601e8bac953c670e35f32ffe48d157dd5694ce7 100644 (file)
@@ -327,6 +327,12 @@ GIT_PARSE_WITH(tcltk))
 AC_MSG_NOTICE([CHECKS for programs])
 #
 AC_PROG_CC([cc gcc])
+AC_C_INLINE
+case $ac_cv_c_inline in
+  inline | yes | no)   ;;
+  *)                   AC_SUBST([INLINE], [$ac_cv_c_inline]) ;;
+esac
+
 # which switch to pass runtime path to dynamic libraries to the linker
 AC_CACHE_CHECK([if linker supports -R], git_cv_ld_dashr, [
    SAVE_LDFLAGS="${LDFLAGS}"
@@ -362,6 +368,7 @@ fi
 #AC_PROG_INSTALL               # needs install-sh or install.sh in sources
 AC_CHECK_TOOLS(AR, [gar ar], :)
 AC_CHECK_PROGS(TAR, [gtar tar])
+AC_CHECK_PROGS(DIFF, [gnudiff gdiff diff])
 # TCLTK_PATH will be set to some value if we want Tcl/Tk
 # or will be empty otherwise.
 if test -z "$NO_TCLTK"; then
@@ -544,13 +551,47 @@ AC_SUBST(NEEDS_SOCKET)
 test -n "$NEEDS_SOCKET" && LIBS="$LIBS -lsocket"
 
 #
-# Define NEEDS_RESOLV if linking with -lnsl and/or -lsocket is not enough.
-# Notably on Solaris hstrerror resides in libresolv and on Solaris 7
-# inet_ntop and inet_pton additionally reside there.
-AC_CHECK_LIB([c], [hstrerror],
-[NEEDS_RESOLV=],
-[NEEDS_RESOLV=YesPlease])
+# The next few tests will define NEEDS_RESOLV if linking with
+# libresolv provides some of the functions we would normally get
+# from libc.
+NEEDS_RESOLV=
 AC_SUBST(NEEDS_RESOLV)
+#
+# Define NO_INET_NTOP if linking with -lresolv is not enough.
+# Solaris 2.7 in particular hos inet_ntop in -lresolv.
+NO_INET_NTOP=
+AC_SUBST(NO_INET_NTOP)
+AC_CHECK_FUNC([inet_ntop],
+       [],
+    [AC_CHECK_LIB([resolv], [inet_ntop],
+           [NEEDS_RESOLV=YesPlease],
+       [NO_INET_NTOP=YesPlease])
+])
+#
+# Define NO_INET_PTON if linking with -lresolv is not enough.
+# Solaris 2.7 in particular hos inet_pton in -lresolv.
+NO_INET_PTON=
+AC_SUBST(NO_INET_PTON)
+AC_CHECK_FUNC([inet_pton],
+       [],
+    [AC_CHECK_LIB([resolv], [inet_pton],
+           [NEEDS_RESOLV=YesPlease],
+       [NO_INET_PTON=YesPlease])
+])
+#
+# Define NO_HSTRERROR if linking with -lresolv is not enough.
+# Solaris 2.6 in particular has no hstrerror, even in -lresolv.
+NO_HSTRERROR=
+AC_CHECK_FUNC([hstrerror],
+       [],
+    [AC_CHECK_LIB([resolv], [hstrerror],
+           [NEEDS_RESOLV=YesPlease],
+       [NO_HSTRERROR=YesPlease])
+])
+AC_SUBST(NO_HSTRERROR)
+#
+# If any of the above tests determined that -lresolv is needed at
+# build-time, also set it here for remaining configure-time checks.
 test -n "$NEEDS_RESOLV" && LIBS="$LIBS -lresolv"
 
 AC_CHECK_LIB([c], [basename],
@@ -598,6 +639,12 @@ AC_SUBST(OLD_ICONV)
 ## Checks for typedefs, structures, and compiler characteristics.
 AC_MSG_NOTICE([CHECKS for typedefs, structures, and compiler characteristics])
 #
+TYPE_SOCKLEN_T
+case $ac_cv_type_socklen_t in
+  yes) ;;
+  *)   AC_SUBST([SOCKLEN_T], [$git_cv_socklen_t_equiv]) ;;
+esac
+
 # Define NO_D_INO_IN_DIRENT if you don't have d_ino in your struct dirent.
 AC_CHECK_MEMBER(struct dirent.d_ino,
 [NO_D_INO_IN_DIRENT=],
@@ -808,7 +855,11 @@ AC_DEFUN([PTHREADTEST_SRC], [
 int main(void)
 {
        pthread_mutex_t test_mutex;
-       return (0);
+       int retcode = 0;
+       retcode |= pthread_mutex_init(&test_mutex,(void *)0);
+       retcode |= pthread_mutex_lock(&test_mutex);
+       retcode |= pthread_mutex_unlock(&test_mutex);
+       return retcode;
 }
 ])
 
@@ -825,7 +876,8 @@ if test -n "$USER_NOPTHREAD"; then
 # handle these separately since PTHREAD_CFLAGS could be '-lpthreads
 # -D_REENTRANT' or some such.
 elif test -z "$PTHREAD_CFLAGS"; then
-  for opt in -pthread -lpthread; do
+  threads_found=no
+  for opt in -mt -pthread -lpthread; do
      old_CFLAGS="$CFLAGS"
      CFLAGS="$opt $CFLAGS"
      AC_MSG_CHECKING([Checking for POSIX Threads with '$opt'])
@@ -833,11 +885,18 @@ elif test -z "$PTHREAD_CFLAGS"; then
        [AC_MSG_RESULT([yes])
                NO_PTHREADS=
                PTHREAD_LIBS="$opt"
+               PTHREAD_CFLAGS="$opt"
+               threads_found=yes
                break
        ],
        [AC_MSG_RESULT([no])])
       CFLAGS="$old_CFLAGS"
   done
+  if test $threads_found != yes; then
+    AC_CHECK_LIB([pthread], [pthread_create],
+       [PTHREAD_LIBS="-lpthread"],
+       [NO_PTHREADS=UnfortunatelyYes])
+  fi
 else
   old_CFLAGS="$CFLAGS"
   CFLAGS="$PTHREAD_CFLAGS $CFLAGS"
@@ -854,6 +913,7 @@ fi
 
 CFLAGS="$old_CFLAGS"
 
+AC_SUBST(PTHREAD_CFLAGS)
 AC_SUBST(PTHREAD_LIBS)
 AC_SUBST(NO_PTHREADS)
 
index 9ae991ac42544716599ff8bf3ebaaa376c8119e4..02e738a0146a5c46aaf3f1d8edc3c055a99e98b9 100644 (file)
--- a/connect.c
+++ b/connect.c
@@ -5,6 +5,7 @@
 #include "refs.h"
 #include "run-command.h"
 #include "remote.h"
+#include "url.h"
 
 static char *server_capabilities;
 
@@ -131,7 +132,7 @@ int path_match(const char *path, int nr, char **match)
 enum protocol {
        PROTO_LOCAL = 1,
        PROTO_SSH,
-       PROTO_GIT,
+       PROTO_GIT
 };
 
 static enum protocol get_protocol(const char *name)
@@ -450,7 +451,7 @@ static struct child_process no_fork;
 struct child_process *git_connect(int fd[2], const char *url_orig,
                                  const char *prog, int flags)
 {
-       char *url = xstrdup(url_orig);
+       char *url;
        char *host, *path;
        char *end;
        int c;
@@ -466,6 +467,11 @@ struct child_process *git_connect(int fd[2], const char *url_orig,
         */
        signal(SIGCHLD, SIG_DFL);
 
+       if (is_url(url_orig))
+               url = url_decode(url_orig);
+       else
+               url = xstrdup(url_orig);
+
        host = strstr(url, "://");
        if (host) {
                *host = '\0';
index 57245a8c01fa3aba4f9e3f2bc258b40f38f446c0..aebb0b689040c3f1c1023e3ddf07779178a6bdfb 100755 (executable)
@@ -842,7 +842,7 @@ _git_checkout ()
        --*)
                __gitcomp "
                        --quiet --ours --theirs --track --no-track --merge
-                       --conflict= --patch
+                       --conflict= --orphan --patch
                        "
                ;;
        *)
index 4f8fcb7bbb00b66f1eaa354119784e6ef57e1eb4..824bd047a548800d51abd7ae9d6f3e2eef9b160c 100644 (file)
--- a/convert.c
+++ b/convert.c
@@ -249,7 +249,9 @@ static int filter_buffer(int in, int out, void *data)
        struct child_process child_process;
        struct filter_params *params = (struct filter_params *)data;
        int write_err, status;
-       const char *argv[] = { params->cmd, NULL };
+       const char *argv[] = { NULL, NULL };
+
+       argv[0] = params->cmd;
 
        memset(&child_process, 0, sizeof(child_process));
        child_process.argv = argv;
@@ -425,6 +427,8 @@ static int count_ident(const char *cp, unsigned long size)
                                cnt++;
                                break;
                        }
+                       if (ch == '\n')
+                               break;
                }
        }
        return cnt;
@@ -455,6 +459,11 @@ static int ident_to_git(const char *path, const char *src, size_t len,
                        dollar = memchr(src + 3, '$', len - 3);
                        if (!dollar)
                                break;
+                       if (memchr(src + 3, '\n', dollar - src - 3)) {
+                               /* Line break before the next dollar. */
+                               continue;
+                       }
+
                        memcpy(dst, "Id$", 3);
                        dst += 3;
                        len -= dollar + 1 - src;
@@ -470,7 +479,7 @@ static int ident_to_worktree(const char *path, const char *src, size_t len,
                              struct strbuf *buf, int ident)
 {
        unsigned char sha1[20];
-       char *to_free = NULL, *dollar;
+       char *to_free = NULL, *dollar, *spc;
        int cnt;
 
        if (!ident)
@@ -506,7 +515,10 @@ static int ident_to_worktree(const char *path, const char *src, size_t len,
                } else if (src[2] == ':') {
                        /*
                         * It's possible that an expanded Id has crept its way into the
-                        * repository, we cope with that by stripping the expansion out
+                        * repository, we cope with that by stripping the expansion out.
+                        * This is probably not a good idea, since it will cause changes
+                        * on checkout, which won't go away by stash, but let's keep it
+                        * for git-style ids.
                         */
                        dollar = memchr(src + 3, '$', len - 3);
                        if (!dollar) {
@@ -514,6 +526,20 @@ static int ident_to_worktree(const char *path, const char *src, size_t len,
                                break;
                        }
 
+                       if (memchr(src + 3, '\n', dollar - src - 3)) {
+                               /* Line break before the next dollar. */
+                               continue;
+                       }
+
+                       spc = memchr(src + 4, ' ', dollar - src - 4);
+                       if (spc && spc < dollar-1) {
+                               /* There are spaces in unexpected places.
+                                * This is probably an id from some other
+                                * versioning system. Keep it for now.
+                                */
+                               continue;
+                       }
+
                        len -= dollar + 1 - src;
                        src  = dollar + 1;
                } else {
diff --git a/ctype.c b/ctype.c
index 7ee64c7d77dd4a5665f70d80ffba1bcdecb9a408..de600279eef4765db497599e6654c2bedd978129 100644 (file)
--- a/ctype.c
+++ b/ctype.c
@@ -10,7 +10,7 @@ enum {
        A = GIT_ALPHA,
        D = GIT_DIGIT,
        G = GIT_GLOB_SPECIAL,   /* *, ?, [, \\ */
-       R = GIT_REGEX_SPECIAL,  /* $, (, ), +, ., ^, {, | */
+       R = GIT_REGEX_SPECIAL   /* $, (, ), +, ., ^, {, | */
 };
 
 unsigned char sane_ctype[256] = {
index a90ab10505a3694de83a0ffd8fc472518f12cf2b..e22a2b7fa5f6fcfc5b26541a21c10d3c8b7eda81 100644 (file)
--- a/daemon.c
+++ b/daemon.c
@@ -141,15 +141,14 @@ static char *path_ok(char *directory)
        }
        else if (interpolated_path && saw_extended_args) {
                struct strbuf expanded_path = STRBUF_INIT;
-               struct strbuf_expand_dict_entry dict[] = {
-                       { "H", hostname },
-                       { "CH", canon_hostname },
-                       { "IP", ip_address },
-                       { "P", tcp_port },
-                       { "D", directory },
-                       { NULL }
-               };
-
+               struct strbuf_expand_dict_entry dict[6];
+
+               dict[0].placeholder = "H"; dict[0].value = hostname;
+               dict[1].placeholder = "CH"; dict[1].value = canon_hostname;
+               dict[2].placeholder = "IP"; dict[2].value = ip_address;
+               dict[3].placeholder = "P"; dict[3].value = tcp_port;
+               dict[4].placeholder = "D"; dict[4].value = directory;
+               dict[5].placeholder = NULL; dict[5].value = NULL;
                if (*dir != '/') {
                        /* Allow only absolute */
                        logerror("'%s': Non-absolute path denied (interpolated-path active)", dir);
@@ -343,7 +342,9 @@ static int upload_pack(void)
 {
        /* Timeout as string */
        char timeout_buf[64];
-       const char *argv[] = { "upload-pack", "--strict", timeout_buf, ".", NULL };
+       const char *argv[] = { "upload-pack", "--strict", NULL, ".", NULL };
+
+       argv[2] = timeout_buf;
 
        snprintf(timeout_buf, sizeof timeout_buf, "--timeout=%u", timeout);
        return run_service_command(argv);
diff --git a/date.c b/date.c
index 002aa3c8d6d4ff08d8790a155b8979bc117a2b95..6bae49ca330a69df5e2cf29e1e461f2490d99b2f 100644 (file)
--- a/date.c
+++ b/date.c
@@ -229,6 +229,7 @@ static const struct {
 
        { "GMT",    0, 0, },    /* Greenwich Mean */
        { "UTC",    0, 0, },    /* Universal (Coordinated) */
+       { "Z",      0, 0, },    /* Zulu, alias for UTC */
 
        { "WET",    0, 0, },    /* Western European */
        { "BST",    0, 1, },    /* British Summer */
@@ -305,7 +306,7 @@ static int match_alpha(const char *date, struct tm *tm, int *offset)
 
        for (i = 0; i < ARRAY_SIZE(timezone_names); i++) {
                int match = match_string(date, timezone_names[i].name);
-               if (match >= 3) {
+               if (match >= 3 || match == strlen(timezone_names[i].name)) {
                        int off = timezone_names[i].offset;
 
                        /* This is bogus, but we like summer */
diff --git a/diff.c b/diff.c
index 494f5601e97e7395e647296bc7f8090331a47c43..2327cea5b2c9ebe8d545b5a648910077ee67cd64 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -30,6 +30,7 @@ static const char *diff_word_regex_cfg;
 static const char *external_diff_cmd_cfg;
 int diff_auto_refresh_index = 1;
 static int diff_mnemonic_prefix;
+static int diff_no_prefix;
 
 static char diff_colors[][COLOR_MAXLEN] = {
        GIT_COLOR_RESET,
@@ -101,6 +102,10 @@ int git_diff_ui_config(const char *var, const char *value, void *cb)
                diff_mnemonic_prefix = git_config_bool(var, value);
                return 0;
        }
+       if (!strcmp(var, "diff.noprefix")) {
+               diff_no_prefix = git_config_bool(var, value);
+               return 0;
+       }
        if (!strcmp(var, "diff.external"))
                return git_config_string(&external_diff_cmd_cfg, var, value);
        if (!strcmp(var, "diff.wordregex"))
@@ -194,8 +199,8 @@ struct emit_callback {
        sane_truncate_fn truncate;
        const char **label_path;
        struct diff_words_data *diff_words;
+       struct diff_options *opt;
        int *found_changesp;
-       FILE *file;
        struct strbuf *header;
 };
 
@@ -282,11 +287,19 @@ static void check_blank_at_eof(mmfile_t *mf1, mmfile_t *mf2,
        ecbdata->blank_at_eof_in_postimage = (at - l2) + 1;
 }
 
-static void emit_line_0(FILE *file, const char *set, const char *reset,
+static void emit_line_0(struct diff_options *o, const char *set, const char *reset,
                        int first, const char *line, int len)
 {
        int has_trailing_newline, has_trailing_carriage_return;
        int nofirst;
+       FILE *file = o->file;
+
+       if (o->output_prefix) {
+               struct strbuf *msg = NULL;
+               msg = o->output_prefix(o, o->output_prefix_data);
+               assert(msg);
+               fwrite(msg->buf, msg->len, 1, file);
+       }
 
        if (len == 0) {
                has_trailing_newline = (first == '\n');
@@ -316,10 +329,10 @@ static void emit_line_0(FILE *file, const char *set, const char *reset,
                fputc('\n', file);
 }
 
-static void emit_line(FILE *file, const char *set, const char *reset,
+static void emit_line(struct diff_options *o, const char *set, const char *reset,
                      const char *line, int len)
 {
-       emit_line_0(file, set, reset, line[0], line+1, len-1);
+       emit_line_0(o, set, reset, line[0], line+1, len-1);
 }
 
 static int new_blank_line_at_eof(struct emit_callback *ecbdata, const char *line, int len)
@@ -341,15 +354,15 @@ static void emit_add_line(const char *reset,
        const char *set = diff_get_color(ecbdata->color_diff, DIFF_FILE_NEW);
 
        if (!*ws)
-               emit_line_0(ecbdata->file, set, reset, '+', line, len);
+               emit_line_0(ecbdata->opt, set, reset, '+', line, len);
        else if (new_blank_line_at_eof(ecbdata, line, len))
                /* Blank line at EOF - paint '+' as well */
-               emit_line_0(ecbdata->file, ws, reset, '+', line, len);
+               emit_line_0(ecbdata->opt, ws, reset, '+', line, len);
        else {
                /* Emit just the prefix, then the rest. */
-               emit_line_0(ecbdata->file, set, reset, '+', "", 0);
+               emit_line_0(ecbdata->opt, set, reset, '+', "", 0);
                ws_check_emit(line, len, ecbdata->ws_rule,
-                             ecbdata->file, set, reset, ws);
+                             ecbdata->opt->file, set, reset, ws);
        }
 }
 
@@ -362,6 +375,9 @@ static void emit_hunk_header(struct emit_callback *ecbdata,
        const char *reset = diff_get_color(ecbdata->color_diff, DIFF_RESET);
        static const char atat[2] = { '@', '@' };
        const char *cp, *ep;
+       struct strbuf msgbuf = STRBUF_INIT;
+       int org_len = len;
+       int i = 1;
 
        /*
         * As a hunk header must begin with "@@ -<old>, +<new> @@",
@@ -370,23 +386,42 @@ static void emit_hunk_header(struct emit_callback *ecbdata,
        if (len < 10 ||
            memcmp(line, atat, 2) ||
            !(ep = memmem(line + 2, len - 2, atat, 2))) {
-               emit_line(ecbdata->file, plain, reset, line, len);
+               emit_line(ecbdata->opt, plain, reset, line, len);
                return;
        }
        ep += 2; /* skip over @@ */
 
        /* The hunk header in fraginfo color */
-       emit_line(ecbdata->file, frag, reset, line, ep - line);
+       strbuf_add(&msgbuf, frag, strlen(frag));
+       strbuf_add(&msgbuf, line, ep - line);
+       strbuf_add(&msgbuf, reset, strlen(reset));
+
+       /*
+        * trailing "\r\n"
+        */
+       for ( ; i < 3; i++)
+               if (line[len - i] == '\r' || line[len - i] == '\n')
+                       len--;
 
        /* blank before the func header */
        for (cp = ep; ep - line < len; ep++)
                if (*ep != ' ' && *ep != '\t')
                        break;
-       if (ep != cp)
-               emit_line(ecbdata->file, plain, reset, cp, ep - cp);
+       if (ep != cp) {
+               strbuf_add(&msgbuf, plain, strlen(plain));
+               strbuf_add(&msgbuf, cp, ep - cp);
+               strbuf_add(&msgbuf, reset, strlen(reset));
+       }
+
+       if (ep < line + len) {
+               strbuf_add(&msgbuf, func, strlen(func));
+               strbuf_add(&msgbuf, ep, line + len - ep);
+               strbuf_add(&msgbuf, reset, strlen(reset));
+       }
 
-       if (ep < line + len)
-               emit_line(ecbdata->file, func, reset, ep, line + len - ep);
+       strbuf_add(&msgbuf, line + len, org_len - len);
+       emit_line(ecbdata->opt, "", "", msgbuf.buf, msgbuf.len);
+       strbuf_release(&msgbuf);
 }
 
 static struct diff_tempfile *claim_diff_tempfile(void) {
@@ -446,7 +481,7 @@ static void emit_rewrite_lines(struct emit_callback *ecb,
                len = endp ? (endp - data + 1) : size;
                if (prefix != '+') {
                        ecb->lno_in_preimage++;
-                       emit_line_0(ecb->file, old, reset, '-',
+                       emit_line_0(ecb->opt, old, reset, '-',
                                    data, len);
                } else {
                        ecb->lno_in_postimage++;
@@ -458,7 +493,7 @@ static void emit_rewrite_lines(struct emit_callback *ecb,
        if (!endp) {
                const char *plain = diff_get_color(ecb->color_diff,
                                                   DIFF_PLAIN);
-               emit_line_0(ecb->file, plain, reset, '\\',
+               emit_line_0(ecb->opt, plain, reset, '\\',
                            nneof, strlen(nneof));
        }
 }
@@ -482,6 +517,13 @@ static void emit_rewrite_diff(const char *name_a,
        char *data_one, *data_two;
        size_t size_one, size_two;
        struct emit_callback ecbdata;
+       char *line_prefix = "";
+       struct strbuf *msgbuf;
+
+       if (o && o->output_prefix) {
+               msgbuf = o->output_prefix(o, o->output_prefix_data);
+               line_prefix = msgbuf->buf;
+       }
 
        if (diff_mnemonic_prefix && DIFF_OPT_TST(o, REVERSE_DIFF)) {
                a_prefix = o->b_prefix;
@@ -508,7 +550,7 @@ static void emit_rewrite_diff(const char *name_a,
        ecbdata.color_diff = color_diff;
        ecbdata.found_changesp = &o->found_changes;
        ecbdata.ws_rule = whitespace_rule(name_b ? name_b : name_a);
-       ecbdata.file = o->file;
+       ecbdata.opt = o;
        if (ecbdata.ws_rule & WS_BLANK_AT_EOF) {
                mmfile_t mf1, mf2;
                mf1.ptr = (char *)data_one;
@@ -523,9 +565,10 @@ static void emit_rewrite_diff(const char *name_a,
        lc_a = count_lines(data_one, size_one);
        lc_b = count_lines(data_two, size_two);
        fprintf(o->file,
-               "%s--- %s%s%s\n%s+++ %s%s%s\n%s@@ -",
-               metainfo, a_name.buf, name_a_tab, reset,
-               metainfo, b_name.buf, name_b_tab, reset, fraginfo);
+               "%s%s--- %s%s%s\n%s%s+++ %s%s%s\n%s%s@@ -",
+               line_prefix, metainfo, a_name.buf, name_a_tab, reset,
+               line_prefix, metainfo, b_name.buf, name_b_tab, reset,
+               line_prefix, fraginfo);
        print_line_count(o->file, lc_a);
        fprintf(o->file, " +");
        print_line_count(o->file, lc_b);
@@ -584,7 +627,8 @@ struct diff_words_style diff_words_styles[] = {
 struct diff_words_data {
        struct diff_words_buffer minus, plus;
        const char *current_plus;
-       FILE *file;
+       int last_minus;
+       struct diff_options *opt;
        regex_t *word_regex;
        enum diff_words_type type;
        struct diff_words_style *style;
@@ -593,10 +637,15 @@ struct diff_words_data {
 static int fn_out_diff_words_write_helper(FILE *fp,
                                          struct diff_words_style_elem *st_el,
                                          const char *newline,
-                                         size_t count, const char *buf)
+                                         size_t count, const char *buf,
+                                         const char *line_prefix)
 {
+       int print = 0;
+
        while (count) {
                char *p = memchr(buf, '\n', count);
+               if (print)
+                       fputs(line_prefix, fp);
                if (p != buf) {
                        if (st_el->color && fputs(st_el->color, fp) < 0)
                                return -1;
@@ -614,21 +663,74 @@ static int fn_out_diff_words_write_helper(FILE *fp,
                        return -1;
                count -= p + 1 - buf;
                buf = p + 1;
+               print = 1;
        }
        return 0;
 }
 
+/*
+ * '--color-words' algorithm can be described as:
+ *
+ *   1. collect a the minus/plus lines of a diff hunk, divided into
+ *      minus-lines and plus-lines;
+ *
+ *   2. break both minus-lines and plus-lines into words and
+ *      place them into two mmfile_t with one word for each line;
+ *
+ *   3. use xdiff to run diff on the two mmfile_t to get the words level diff;
+ *
+ * And for the common parts of the both file, we output the plus side text.
+ * diff_words->current_plus is used to trace the current position of the plus file
+ * which printed. diff_words->last_minus is used to trace the last minus word
+ * printed.
+ *
+ * For '--graph' to work with '--color-words', we need to output the graph prefix
+ * on each line of color words output. Generally, there are two conditions on
+ * which we should output the prefix.
+ *
+ *   1. diff_words->last_minus == 0 &&
+ *      diff_words->current_plus == diff_words->plus.text.ptr
+ *
+ *      that is: the plus text must start as a new line, and if there is no minus
+ *      word printed, a graph prefix must be printed.
+ *
+ *   2. diff_words->current_plus > diff_words->plus.text.ptr &&
+ *      *(diff_words->current_plus - 1) == '\n'
+ *
+ *      that is: a graph prefix must be printed following a '\n'
+ */
+static int color_words_output_graph_prefix(struct diff_words_data *diff_words)
+{
+       if ((diff_words->last_minus == 0 &&
+               diff_words->current_plus == diff_words->plus.text.ptr) ||
+               (diff_words->current_plus > diff_words->plus.text.ptr &&
+               *(diff_words->current_plus - 1) == '\n')) {
+               return 1;
+       } else {
+               return 0;
+       }
+}
+
 static void fn_out_diff_words_aux(void *priv, char *line, unsigned long len)
 {
        struct diff_words_data *diff_words = priv;
        struct diff_words_style *style = diff_words->style;
        int minus_first, minus_len, plus_first, plus_len;
        const char *minus_begin, *minus_end, *plus_begin, *plus_end;
+       struct diff_options *opt = diff_words->opt;
+       struct strbuf *msgbuf;
+       char *line_prefix = "";
 
        if (line[0] != '@' || parse_hunk_header(line, len,
                        &minus_first, &minus_len, &plus_first, &plus_len))
                return;
 
+       assert(opt);
+       if (opt->output_prefix) {
+               msgbuf = opt->output_prefix(opt, opt->output_prefix_data);
+               line_prefix = msgbuf->buf;
+       }
+
        /* POSIX requires that first be decremented by one if len == 0... */
        if (minus_len) {
                minus_begin = diff_words->minus.orig[minus_first].begin;
@@ -644,21 +746,32 @@ static void fn_out_diff_words_aux(void *priv, char *line, unsigned long len)
        } else
                plus_begin = plus_end = diff_words->plus.orig[plus_first].end;
 
-       if (diff_words->current_plus != plus_begin)
-               fn_out_diff_words_write_helper(diff_words->file,
+       if (color_words_output_graph_prefix(diff_words)) {
+               fputs(line_prefix, diff_words->opt->file);
+       }
+       if (diff_words->current_plus != plus_begin) {
+               fn_out_diff_words_write_helper(diff_words->opt->file,
                                &style->ctx, style->newline,
                                plus_begin - diff_words->current_plus,
-                               diff_words->current_plus);
-       if (minus_begin != minus_end)
-               fn_out_diff_words_write_helper(diff_words->file,
+                               diff_words->current_plus, line_prefix);
+               if (*(plus_begin - 1) == '\n')
+                       fputs(line_prefix, diff_words->opt->file);
+       }
+       if (minus_begin != minus_end) {
+               fn_out_diff_words_write_helper(diff_words->opt->file,
                                &style->old, style->newline,
-                               minus_end - minus_begin, minus_begin);
-       if (plus_begin != plus_end)
-               fn_out_diff_words_write_helper(diff_words->file,
+                               minus_end - minus_begin, minus_begin,
+                               line_prefix);
+       }
+       if (plus_begin != plus_end) {
+               fn_out_diff_words_write_helper(diff_words->opt->file,
                                &style->new, style->newline,
-                               plus_end - plus_begin, plus_begin);
+                               plus_end - plus_begin, plus_begin,
+                               line_prefix);
+       }
 
        diff_words->current_plus = plus_end;
+       diff_words->last_minus = minus_first;
 }
 
 /* This function starts looking at *begin, and returns 0 iff a word was found. */
@@ -739,22 +852,35 @@ static void diff_words_show(struct diff_words_data *diff_words)
        mmfile_t minus, plus;
        struct diff_words_style *style = diff_words->style;
 
+       struct diff_options *opt = diff_words->opt;
+       struct strbuf *msgbuf;
+       char *line_prefix = "";
+
+       assert(opt);
+       if (opt->output_prefix) {
+               msgbuf = opt->output_prefix(opt, opt->output_prefix_data);
+               line_prefix = msgbuf->buf;
+       }
+
        /* special case: only removal */
        if (!diff_words->plus.text.size) {
-               fn_out_diff_words_write_helper(diff_words->file,
+               fputs(line_prefix, diff_words->opt->file);
+               fn_out_diff_words_write_helper(diff_words->opt->file,
                        &style->old, style->newline,
-                       diff_words->minus.text.size, diff_words->minus.text.ptr);
+                       diff_words->minus.text.size,
+                       diff_words->minus.text.ptr, line_prefix);
                diff_words->minus.text.size = 0;
                return;
        }
 
        diff_words->current_plus = diff_words->plus.text.ptr;
+       diff_words->last_minus = 0;
 
        memset(&xpp, 0, sizeof(xpp));
        memset(&xecfg, 0, sizeof(xecfg));
        diff_words_fill(&diff_words->minus, &minus, diff_words->word_regex);
        diff_words_fill(&diff_words->plus, &plus, diff_words->word_regex);
-       xpp.flags = XDF_NEED_MINIMAL;
+       xpp.flags = 0;
        /* as only the hunk header will be parsed, we need a 0-context */
        xecfg.ctxlen = 0;
        xdi_diff_outf(&minus, &plus, fn_out_diff_words_aux, diff_words,
@@ -762,11 +888,15 @@ static void diff_words_show(struct diff_words_data *diff_words)
        free(minus.ptr);
        free(plus.ptr);
        if (diff_words->current_plus != diff_words->plus.text.ptr +
-                       diff_words->plus.text.size)
-               fn_out_diff_words_write_helper(diff_words->file,
+                       diff_words->plus.text.size) {
+               if (color_words_output_graph_prefix(diff_words))
+                       fputs(line_prefix, diff_words->opt->file);
+               fn_out_diff_words_write_helper(diff_words->opt->file,
                        &style->ctx, style->newline,
                        diff_words->plus.text.ptr + diff_words->plus.text.size
-                       - diff_words->current_plus, diff_words->current_plus);
+                       - diff_words->current_plus, diff_words->current_plus,
+                       line_prefix);
+       }
        diff_words->minus.text.size = diff_words->plus.text.size = 0;
 }
 
@@ -838,9 +968,17 @@ static void fn_out_consume(void *priv, char *line, unsigned long len)
        const char *meta = diff_get_color(ecbdata->color_diff, DIFF_METAINFO);
        const char *plain = diff_get_color(ecbdata->color_diff, DIFF_PLAIN);
        const char *reset = diff_get_color(ecbdata->color_diff, DIFF_RESET);
+       struct diff_options *o = ecbdata->opt;
+       char *line_prefix = "";
+       struct strbuf *msgbuf;
+
+       if (o && o->output_prefix) {
+               msgbuf = o->output_prefix(o, o->output_prefix_data);
+               line_prefix = msgbuf->buf;
+       }
 
        if (ecbdata->header) {
-               fprintf(ecbdata->file, "%s", ecbdata->header->buf);
+               fprintf(ecbdata->opt->file, "%s", ecbdata->header->buf);
                strbuf_reset(ecbdata->header);
                ecbdata->header = NULL;
        }
@@ -852,10 +990,10 @@ static void fn_out_consume(void *priv, char *line, unsigned long len)
                name_a_tab = strchr(ecbdata->label_path[0], ' ') ? "\t" : "";
                name_b_tab = strchr(ecbdata->label_path[1], ' ') ? "\t" : "";
 
-               fprintf(ecbdata->file, "%s--- %s%s%s\n",
-                       meta, ecbdata->label_path[0], reset, name_a_tab);
-               fprintf(ecbdata->file, "%s+++ %s%s%s\n",
-                       meta, ecbdata->label_path[1], reset, name_b_tab);
+               fprintf(ecbdata->opt->file, "%s%s--- %s%s%s\n",
+                       line_prefix, meta, ecbdata->label_path[0], reset, name_a_tab);
+               fprintf(ecbdata->opt->file, "%s%s+++ %s%s%s\n",
+                       line_prefix, meta, ecbdata->label_path[1], reset, name_b_tab);
                ecbdata->label_path[0] = ecbdata->label_path[1] = NULL;
        }
 
@@ -872,15 +1010,15 @@ static void fn_out_consume(void *priv, char *line, unsigned long len)
                find_lno(line, ecbdata);
                emit_hunk_header(ecbdata, line, len);
                if (line[len-1] != '\n')
-                       putc('\n', ecbdata->file);
+                       putc('\n', ecbdata->opt->file);
                return;
        }
 
        if (len < 1) {
-               emit_line(ecbdata->file, reset, reset, line, len);
+               emit_line(ecbdata->opt, reset, reset, line, len);
                if (ecbdata->diff_words
                    && ecbdata->diff_words->type == DIFF_WORDS_PORCELAIN)
-                       fputs("~\n", ecbdata->file);
+                       fputs("~\n", ecbdata->opt->file);
                return;
        }
 
@@ -896,11 +1034,11 @@ static void fn_out_consume(void *priv, char *line, unsigned long len)
                }
                diff_words_flush(ecbdata);
                if (ecbdata->diff_words->type == DIFF_WORDS_PORCELAIN) {
-                       emit_line(ecbdata->file, plain, reset, line, len);
-                       fputs("~\n", ecbdata->file);
+                       emit_line(ecbdata->opt, plain, reset, line, len);
+                       fputs("~\n", ecbdata->opt->file);
                } else {
                        /* don't print the prefix character */
-                       emit_line(ecbdata->file, plain, reset, line+1, len-1);
+                       emit_line(ecbdata->opt, plain, reset, line+1, len-1);
                }
                return;
        }
@@ -912,7 +1050,7 @@ static void fn_out_consume(void *priv, char *line, unsigned long len)
                ecbdata->lno_in_preimage++;
                if (line[0] == ' ')
                        ecbdata->lno_in_postimage++;
-               emit_line(ecbdata->file, color, reset, line, len);
+               emit_line(ecbdata->opt, color, reset, line, len);
        } else {
                ecbdata->lno_in_postimage++;
                emit_add_line(reset, ecbdata, line + 1, len - 1);
@@ -1092,10 +1230,17 @@ static void show_stats(struct diffstat_t *data, struct diff_options *options)
        int total_files = data->nr;
        int width, name_width;
        const char *reset, *set, *add_c, *del_c;
+       const char *line_prefix = "";
+       struct strbuf *msg = NULL;
 
        if (data->nr == 0)
                return;
 
+       if (options->output_prefix) {
+               msg = options->output_prefix(options, options->output_prefix_data);
+               line_prefix = msg->buf;
+       }
+
        width = options->stat_width ? options->stat_width : 80;
        name_width = options->stat_name_width ? options->stat_name_width : 50;
 
@@ -1165,6 +1310,7 @@ static void show_stats(struct diffstat_t *data, struct diff_options *options)
                }
 
                if (data->files[i]->is_binary) {
+                       fprintf(options->file, "%s", line_prefix);
                        show_name(options->file, prefix, name, len);
                        fprintf(options->file, "  Bin ");
                        fprintf(options->file, "%s%"PRIuMAX"%s",
@@ -1177,6 +1323,7 @@ static void show_stats(struct diffstat_t *data, struct diff_options *options)
                        continue;
                }
                else if (data->files[i]->is_unmerged) {
+                       fprintf(options->file, "%s", line_prefix);
                        show_name(options->file, prefix, name, len);
                        fprintf(options->file, "  Unmerged\n");
                        continue;
@@ -1199,6 +1346,7 @@ static void show_stats(struct diffstat_t *data, struct diff_options *options)
                        add = scale_linear(add, width, max_change);
                        del = scale_linear(del, width, max_change);
                }
+               fprintf(options->file, "%s", line_prefix);
                show_name(options->file, prefix, name, len);
                fprintf(options->file, "%5"PRIuMAX"%s", added + deleted,
                                added + deleted ? " " : "");
@@ -1206,6 +1354,7 @@ static void show_stats(struct diffstat_t *data, struct diff_options *options)
                show_graph(options->file, '-', del, del_c, reset);
                fprintf(options->file, "\n");
        }
+       fprintf(options->file, "%s", line_prefix);
        fprintf(options->file,
               " %d files changed, %d insertions(+), %d deletions(-)\n",
               total_files, adds, dels);
@@ -1232,6 +1381,12 @@ static void show_shortstats(struct diffstat_t *data, struct diff_options *option
                        }
                }
        }
+       if (options->output_prefix) {
+               struct strbuf *msg = NULL;
+               msg = options->output_prefix(options,
+                               options->output_prefix_data);
+               fprintf(options->file, "%s", msg->buf);
+       }
        fprintf(options->file, " %d files changed, %d insertions(+), %d deletions(-)\n",
               total_files, adds, dels);
 }
@@ -1246,6 +1401,13 @@ static void show_numstat(struct diffstat_t *data, struct diff_options *options)
        for (i = 0; i < data->nr; i++) {
                struct diffstat_file *file = data->files[i];
 
+               if (options->output_prefix) {
+                       struct strbuf *msg = NULL;
+                       msg = options->output_prefix(options,
+                                       options->output_prefix_data);
+                       fprintf(options->file, "%s", msg->buf);
+               }
+
                if (file->is_binary)
                        fprintf(options->file, "-\t-\t");
                else
@@ -1281,10 +1443,18 @@ struct dirstat_dir {
        int alloc, nr, percent, cumulative;
 };
 
-static long gather_dirstat(FILE *file, struct dirstat_dir *dir, unsigned long changed, const char *base, int baselen)
+static long gather_dirstat(struct diff_options *opt, struct dirstat_dir *dir,
+               unsigned long changed, const char *base, int baselen)
 {
        unsigned long this_dir = 0;
        unsigned int sources = 0;
+       const char *line_prefix = "";
+       struct strbuf *msg = NULL;
+
+       if (opt->output_prefix) {
+               msg = opt->output_prefix(opt, opt->output_prefix_data);
+               line_prefix = msg->buf;
+       }
 
        while (dir->nr) {
                struct dirstat_file *f = dir->files;
@@ -1299,7 +1469,7 @@ static long gather_dirstat(FILE *file, struct dirstat_dir *dir, unsigned long ch
                slash = strchr(f->name + baselen, '/');
                if (slash) {
                        int newbaselen = slash + 1 - f->name;
-                       this = gather_dirstat(file, dir, changed, f->name, newbaselen);
+                       this = gather_dirstat(opt, dir, changed, f->name, newbaselen);
                        sources++;
                } else {
                        this = f->changed;
@@ -1321,7 +1491,8 @@ static long gather_dirstat(FILE *file, struct dirstat_dir *dir, unsigned long ch
                if (permille) {
                        int percent = permille / 10;
                        if (percent >= dir->percent) {
-                               fprintf(file, "%4d.%01d%% %.*s\n", percent, permille % 10, baselen, base);
+                               fprintf(opt->file, "%s%4d.%01d%% %.*s\n", line_prefix,
+                                       percent, permille % 10, baselen, base);
                                if (!dir->cumulative)
                                        return 0;
                        }
@@ -1401,7 +1572,7 @@ static void show_dirstat(struct diff_options *options)
 
        /* Show all directories with more than x% of the changes */
        qsort(dir.files, dir.nr, sizeof(dir.files[0]), dirstat_compare);
-       gather_dirstat(options->file, &dir, changed, "", 0);
+       gather_dirstat(options, &dir, changed, "", 0);
 }
 
 static void free_diffstat_info(struct diffstat_t *diffstat)
@@ -1459,6 +1630,15 @@ static void checkdiff_consume(void *priv, char *line, unsigned long len)
        const char *reset = diff_get_color(color_diff, DIFF_RESET);
        const char *set = diff_get_color(color_diff, DIFF_FILE_NEW);
        char *err;
+       char *line_prefix = "";
+       struct strbuf *msgbuf;
+
+       assert(data->o);
+       if (data->o->output_prefix) {
+               msgbuf = data->o->output_prefix(data->o,
+                       data->o->output_prefix_data);
+               line_prefix = msgbuf->buf;
+       }
 
        if (line[0] == '+') {
                unsigned bad;
@@ -1466,18 +1646,18 @@ static void checkdiff_consume(void *priv, char *line, unsigned long len)
                if (is_conflict_marker(line + 1, marker_size, len - 1)) {
                        data->status |= 1;
                        fprintf(data->o->file,
-                               "%s:%d: leftover conflict marker\n",
-                               data->filename, data->lineno);
+                               "%s%s:%d: leftover conflict marker\n",
+                               line_prefix, data->filename, data->lineno);
                }
                bad = ws_check(line + 1, len - 1, data->ws_rule);
                if (!bad)
                        return;
                data->status |= bad;
                err = whitespace_error_string(bad);
-               fprintf(data->o->file, "%s:%d: %s.\n",
-                       data->filename, data->lineno, err);
+               fprintf(data->o->file, "%s%s:%d: %s.\n",
+                       line_prefix, data->filename, data->lineno, err);
                free(err);
-               emit_line(data->o->file, set, reset, line, 1);
+               emit_line(data->o, set, reset, line, 1);
                ws_check_emit(line + 1, len - 1, data->ws_rule,
                              data->o->file, set, reset, ws);
        } else if (line[0] == ' ') {
@@ -1515,7 +1695,7 @@ static unsigned char *deflate_it(char *data,
        return deflated;
 }
 
-static void emit_binary_diff_body(FILE *file, mmfile_t *one, mmfile_t *two)
+static void emit_binary_diff_body(FILE *file, mmfile_t *one, mmfile_t *two, char *prefix)
 {
        void *cp;
        void *delta;
@@ -1544,13 +1724,13 @@ static void emit_binary_diff_body(FILE *file, mmfile_t *one, mmfile_t *two)
        }
 
        if (delta && delta_size < deflate_size) {
-               fprintf(file, "delta %lu\n", orig_size);
+               fprintf(file, "%sdelta %lu\n", prefix, orig_size);
                free(deflated);
                data = delta;
                data_size = delta_size;
        }
        else {
-               fprintf(file, "literal %lu\n", two->size);
+               fprintf(file, "%sliteral %lu\n", prefix, two->size);
                free(delta);
                data = deflated;
                data_size = deflate_size;
@@ -1568,18 +1748,19 @@ static void emit_binary_diff_body(FILE *file, mmfile_t *one, mmfile_t *two)
                        line[0] = bytes - 26 + 'a' - 1;
                encode_85(line + 1, cp, bytes);
                cp = (char *) cp + bytes;
+               fprintf(file, "%s", prefix);
                fputs(line, file);
                fputc('\n', file);
        }
-       fprintf(file, "\n");
+       fprintf(file, "%s\n", prefix);
        free(data);
 }
 
-static void emit_binary_diff(FILE *file, mmfile_t *one, mmfile_t *two)
+static void emit_binary_diff(FILE *file, mmfile_t *one, mmfile_t *two, char *prefix)
 {
-       fprintf(file, "GIT binary patch\n");
-       emit_binary_diff_body(file, one, two);
-       emit_binary_diff_body(file, two, one);
+       fprintf(file, "%sGIT binary patch\n", prefix);
+       emit_binary_diff_body(file, one, two, prefix);
+       emit_binary_diff_body(file, two, one, prefix);
 }
 
 static void diff_filespec_load_driver(struct diff_filespec *one)
@@ -1656,6 +1837,7 @@ static void builtin_diff(const char *name_a,
                         struct diff_filespec *one,
                         struct diff_filespec *two,
                         const char *xfrm_msg,
+                        int must_show_header,
                         struct diff_options *o,
                         int complete_rewrite)
 {
@@ -1668,6 +1850,13 @@ static void builtin_diff(const char *name_a,
        struct userdiff_driver *textconv_one = NULL;
        struct userdiff_driver *textconv_two = NULL;
        struct strbuf header = STRBUF_INIT;
+       struct strbuf *msgbuf;
+       char *line_prefix = "";
+
+       if (o->output_prefix) {
+               msgbuf = o->output_prefix(o, o->output_prefix_data);
+               line_prefix = msgbuf->buf;
+       }
 
        if (DIFF_OPT_TST(o, SUBMODULE_LOG) &&
                        (!one->mode || S_ISGITLINK(one->mode)) &&
@@ -1702,25 +1891,28 @@ static void builtin_diff(const char *name_a,
        b_two = quote_two(b_prefix, name_b + (*name_b == '/'));
        lbl[0] = DIFF_FILE_VALID(one) ? a_one : "/dev/null";
        lbl[1] = DIFF_FILE_VALID(two) ? b_two : "/dev/null";
-       strbuf_addf(&header, "%sdiff --git %s %s%s\n", set, a_one, b_two, reset);
+       strbuf_addf(&header, "%s%sdiff --git %s %s%s\n", line_prefix, set, a_one, b_two, reset);
        if (lbl[0][0] == '/') {
                /* /dev/null */
-               strbuf_addf(&header, "%snew file mode %06o%s\n", set, two->mode, reset);
-               if (xfrm_msg && xfrm_msg[0])
-                       strbuf_addf(&header, "%s%s%s\n", set, xfrm_msg, reset);
+               strbuf_addf(&header, "%s%snew file mode %06o%s\n", line_prefix, set, two->mode, reset);
+               if (xfrm_msg)
+                       strbuf_addstr(&header, xfrm_msg);
+               must_show_header = 1;
        }
        else if (lbl[1][0] == '/') {
-               strbuf_addf(&header, "%sdeleted file mode %06o%s\n", set, one->mode, reset);
-               if (xfrm_msg && xfrm_msg[0])
-                       strbuf_addf(&header, "%s%s%s\n", set, xfrm_msg, reset);
+               strbuf_addf(&header, "%s%sdeleted file mode %06o%s\n", line_prefix, set, one->mode, reset);
+               if (xfrm_msg)
+                       strbuf_addstr(&header, xfrm_msg);
+               must_show_header = 1;
        }
        else {
                if (one->mode != two->mode) {
-                       strbuf_addf(&header, "%sold mode %06o%s\n", set, one->mode, reset);
-                       strbuf_addf(&header, "%snew mode %06o%s\n", set, two->mode, reset);
+                       strbuf_addf(&header, "%s%sold mode %06o%s\n", line_prefix, set, one->mode, reset);
+                       strbuf_addf(&header, "%s%snew mode %06o%s\n", line_prefix, set, two->mode, reset);
+                       must_show_header = 1;
                }
-               if (xfrm_msg && xfrm_msg[0])
-                       strbuf_addf(&header, "%s%s%s\n", set, xfrm_msg, reset);
+               if (xfrm_msg)
+                       strbuf_addstr(&header, xfrm_msg);
 
                /*
                 * we do not run diff between different kind
@@ -1747,15 +1939,18 @@ static void builtin_diff(const char *name_a,
                        die("unable to read files to diff");
                /* Quite common confusing case */
                if (mf1.size == mf2.size &&
-                   !memcmp(mf1.ptr, mf2.ptr, mf1.size))
+                   !memcmp(mf1.ptr, mf2.ptr, mf1.size)) {
+                       if (must_show_header)
+                               fprintf(o->file, "%s", header.buf);
                        goto free_ab_and_return;
+               }
                fprintf(o->file, "%s", header.buf);
                strbuf_reset(&header);
                if (DIFF_OPT_TST(o, BINARY))
-                       emit_binary_diff(o->file, &mf1, &mf2);
+                       emit_binary_diff(o->file, &mf1, &mf2, line_prefix);
                else
-                       fprintf(o->file, "Binary files %s and %s differ\n",
-                               lbl[0], lbl[1]);
+                       fprintf(o->file, "%sBinary files %s and %s differ\n",
+                               line_prefix, lbl[0], lbl[1]);
                o->found_changes = 1;
        }
        else {
@@ -1766,7 +1961,7 @@ static void builtin_diff(const char *name_a,
                struct emit_callback ecbdata;
                const struct userdiff_funcname *pe;
 
-               if (!DIFF_XDL_TST(o, WHITESPACE_FLAGS)) {
+               if (!DIFF_XDL_TST(o, WHITESPACE_FLAGS) || must_show_header) {
                        fprintf(o->file, "%s", header.buf);
                        strbuf_reset(&header);
                }
@@ -1787,9 +1982,9 @@ static void builtin_diff(const char *name_a,
                ecbdata.ws_rule = whitespace_rule(name_b ? name_b : name_a);
                if (ecbdata.ws_rule & WS_BLANK_AT_EOF)
                        check_blank_at_eof(&mf1, &mf2, &ecbdata);
-               ecbdata.file = o->file;
+               ecbdata.opt = o;
                ecbdata.header = header.len ? &header : NULL;
-               xpp.flags = XDF_NEED_MINIMAL | o->xdl_opts;
+               xpp.flags = o->xdl_opts;
                xecfg.ctxlen = o->context;
                xecfg.interhunkctxlen = o->interhunkcontext;
                xecfg.flags = XDL_EMIT_FUNCNAMES;
@@ -1806,8 +2001,8 @@ static void builtin_diff(const char *name_a,
 
                        ecbdata.diff_words =
                                xcalloc(1, sizeof(struct diff_words_data));
-                       ecbdata.diff_words->file = o->file;
                        ecbdata.diff_words->type = o->word_diff;
+                       ecbdata.diff_words->opt = o;
                        if (!o->word_regex)
                                o->word_regex = userdiff_word_regex(one);
                        if (!o->word_regex)
@@ -1894,7 +2089,7 @@ static void builtin_diffstat(const char *name_a, const char *name_b,
 
                memset(&xpp, 0, sizeof(xpp));
                memset(&xecfg, 0, sizeof(xecfg));
-               xpp.flags = XDF_NEED_MINIMAL | o->xdl_opts;
+               xpp.flags = o->xdl_opts;
                xdi_diff_outf(&mf1, &mf2, diffstat_consume, diffstat,
                              &xpp, &xecfg);
        }
@@ -1942,7 +2137,7 @@ static void builtin_checkdiff(const char *name_a, const char *name_b,
                memset(&xpp, 0, sizeof(xpp));
                memset(&xecfg, 0, sizeof(xecfg));
                xecfg.ctxlen = 1; /* at least one context line */
-               xpp.flags = XDF_NEED_MINIMAL;
+               xpp.flags = 0;
                xdi_diff_outf(&mf1, &mf2, checkdiff_consume, &data,
                              &xpp, &xecfg);
 
@@ -2379,36 +2574,54 @@ static void fill_metainfo(struct strbuf *msg,
                          struct diff_filespec *one,
                          struct diff_filespec *two,
                          struct diff_options *o,
-                         struct diff_filepair *p)
+                         struct diff_filepair *p,
+                         int *must_show_header,
+                         int use_color)
 {
+       const char *set = diff_get_color(use_color, DIFF_METAINFO);
+       const char *reset = diff_get_color(use_color, DIFF_RESET);
+       struct strbuf *msgbuf;
+       char *line_prefix = "";
+
+       *must_show_header = 1;
+       if (o->output_prefix) {
+               msgbuf = o->output_prefix(o, o->output_prefix_data);
+               line_prefix = msgbuf->buf;
+       }
        strbuf_init(msg, PATH_MAX * 2 + 300);
        switch (p->status) {
        case DIFF_STATUS_COPIED:
-               strbuf_addf(msg, "similarity index %d%%", similarity_index(p));
-               strbuf_addstr(msg, "\ncopy from ");
+               strbuf_addf(msg, "%s%ssimilarity index %d%%",
+                           line_prefix, set, similarity_index(p));
+               strbuf_addf(msg, "%s\n%s%scopy from ",
+                           reset,  line_prefix, set);
                quote_c_style(name, msg, NULL, 0);
-               strbuf_addstr(msg, "\ncopy to ");
+               strbuf_addf(msg, "%s\n%s%scopy to ", reset, line_prefix, set);
                quote_c_style(other, msg, NULL, 0);
-               strbuf_addch(msg, '\n');
+               strbuf_addf(msg, "%s\n", reset);
                break;
        case DIFF_STATUS_RENAMED:
-               strbuf_addf(msg, "similarity index %d%%", similarity_index(p));
-               strbuf_addstr(msg, "\nrename from ");
+               strbuf_addf(msg, "%s%ssimilarity index %d%%",
+                           line_prefix, set, similarity_index(p));
+               strbuf_addf(msg, "%s\n%s%srename from ",
+                           reset, line_prefix, set);
                quote_c_style(name, msg, NULL, 0);
-               strbuf_addstr(msg, "\nrename to ");
+               strbuf_addf(msg, "%s\n%s%srename to ",
+                           reset, line_prefix, set);
                quote_c_style(other, msg, NULL, 0);
-               strbuf_addch(msg, '\n');
+               strbuf_addf(msg, "%s\n", reset);
                break;
        case DIFF_STATUS_MODIFIED:
                if (p->score) {
-                       strbuf_addf(msg, "dissimilarity index %d%%\n",
-                                   similarity_index(p));
+                       strbuf_addf(msg, "%s%sdissimilarity index %d%%%s\n",
+                                   line_prefix,
+                                   set, similarity_index(p), reset);
                        break;
                }
                /* fallthru */
        default:
                /* nothing */
-               ;
+               *must_show_header = 0;
        }
        if (one && two && hashcmp(one->sha1, two->sha1)) {
                int abbrev = DIFF_OPT_TST(o, FULL_INDEX) ? 40 : DEFAULT_ABBREV;
@@ -2419,15 +2632,14 @@ static void fill_metainfo(struct strbuf *msg,
                            (!fill_mmfile(&mf, two) && diff_filespec_is_binary(two)))
                                abbrev = 40;
                }
-               strbuf_addf(msg, "index %.*s..%.*s",
-                           abbrev, sha1_to_hex(one->sha1),
-                           abbrev, sha1_to_hex(two->sha1));
+               strbuf_addf(msg, "%s%sindex %s..", set,
+                           line_prefix,
+                           find_unique_abbrev(one->sha1, abbrev));
+               strbuf_addstr(msg, find_unique_abbrev(two->sha1, abbrev));
                if (one->mode == two->mode)
                        strbuf_addf(msg, " %06o", one->mode);
-               strbuf_addch(msg, '\n');
+               strbuf_addf(msg, "%s\n", reset);
        }
-       if (msg->len)
-               strbuf_setlen(msg, msg->len - 1);
 }
 
 static void run_diff_cmd(const char *pgm,
@@ -2442,11 +2654,7 @@ static void run_diff_cmd(const char *pgm,
 {
        const char *xfrm_msg = NULL;
        int complete_rewrite = (p->status == DIFF_STATUS_MODIFIED) && p->score;
-
-       if (msg) {
-               fill_metainfo(msg, name, other, one, two, o, p);
-               xfrm_msg = msg->len ? msg->buf : NULL;
-       }
+       int must_show_header = 0;
 
        if (!DIFF_OPT_TST(o, ALLOW_EXTERNAL))
                pgm = NULL;
@@ -2456,6 +2664,17 @@ static void run_diff_cmd(const char *pgm,
                        pgm = drv->external;
        }
 
+       if (msg) {
+               /*
+                * don't use colors when the header is intended for an
+                * external diff driver
+                */
+               fill_metainfo(msg, name, other, one, two, o, p,
+                             &must_show_header,
+                             DIFF_OPT_TST(o, COLOR_DIFF) && !pgm);
+               xfrm_msg = msg->len ? msg->buf : NULL;
+       }
+
        if (pgm) {
                run_external_diff(pgm, name, other, one, two, xfrm_msg,
                                  complete_rewrite);
@@ -2463,7 +2682,8 @@ static void run_diff_cmd(const char *pgm,
        }
        if (one && two)
                builtin_diff(name, other ? other : name,
-                            one, two, xfrm_msg, o, complete_rewrite);
+                            one, two, xfrm_msg, must_show_header,
+                            o, complete_rewrite);
        else
                fprintf(o->file, "* Unmerged path %s\n", name);
 }
@@ -2616,7 +2836,9 @@ void diff_setup(struct diff_options *options)
                DIFF_OPT_SET(options, COLOR_DIFF);
        options->detect_rename = diff_detect_rename_default;
 
-       if (!diff_mnemonic_prefix) {
+       if (diff_no_prefix) {
+               options->a_prefix = options->b_prefix = "";
+       } else if (!diff_mnemonic_prefix) {
                options->a_prefix = "a/";
                options->b_prefix = "b/";
        }
@@ -3124,6 +3346,11 @@ static void diff_flush_raw(struct diff_filepair *p, struct diff_options *opt)
 {
        int line_termination = opt->line_termination;
        int inter_name_termination = line_termination ? '\t' : '\0';
+       if (opt->output_prefix) {
+               struct strbuf *msg = NULL;
+               msg = opt->output_prefix(opt, opt->output_prefix_data);
+               fprintf(opt->file, "%s", msg->buf);
+       }
 
        if (!(opt->output_format & DIFF_FORMAT_NAME_STATUS)) {
                fprintf(opt->file, ":%06o %06o %s ", p->one->mode, p->two->mode,
@@ -3369,48 +3596,62 @@ static void show_file_mode_name(FILE *file, const char *newdelete, struct diff_f
 }
 
 
-static void show_mode_change(FILE *file, struct diff_filepair *p, int show_name)
+static void show_mode_change(FILE *file, struct diff_filepair *p, int show_name,
+               const char *line_prefix)
 {
        if (p->one->mode && p->two->mode && p->one->mode != p->two->mode) {
-               fprintf(file, " mode change %06o => %06o%c", p->one->mode, p->two->mode,
-                       show_name ? ' ' : '\n');
+               fprintf(file, "%s mode change %06o => %06o%c", line_prefix, p->one->mode,
+                       p->two->mode, show_name ? ' ' : '\n');
                if (show_name) {
                        write_name_quoted(p->two->path, file, '\n');
                }
        }
 }
 
-static void show_rename_copy(FILE *file, const char *renamecopy, struct diff_filepair *p)
+static void show_rename_copy(FILE *file, const char *renamecopy, struct diff_filepair *p,
+                       const char *line_prefix)
 {
        char *names = pprint_rename(p->one->path, p->two->path);
 
        fprintf(file, " %s %s (%d%%)\n", renamecopy, names, similarity_index(p));
        free(names);
-       show_mode_change(file, p, 0);
+       show_mode_change(file, p, 0, line_prefix);
 }
 
-static void diff_summary(FILE *file, struct diff_filepair *p)
+static void diff_summary(struct diff_options *opt, struct diff_filepair *p)
 {
+       FILE *file = opt->file;
+       char *line_prefix = "";
+
+       if (opt->output_prefix) {
+               struct strbuf *buf = opt->output_prefix(opt, opt->output_prefix_data);
+               line_prefix = buf->buf;
+       }
+
        switch(p->status) {
        case DIFF_STATUS_DELETED:
+               fputs(line_prefix, file);
                show_file_mode_name(file, "delete", p->one);
                break;
        case DIFF_STATUS_ADDED:
+               fputs(line_prefix, file);
                show_file_mode_name(file, "create", p->two);
                break;
        case DIFF_STATUS_COPIED:
-               show_rename_copy(file, "copy", p);
+               fputs(line_prefix, file);
+               show_rename_copy(file, "copy", p, line_prefix);
                break;
        case DIFF_STATUS_RENAMED:
-               show_rename_copy(file, "rename", p);
+               fputs(line_prefix, file);
+               show_rename_copy(file, "rename", p, line_prefix);
                break;
        default:
                if (p->score) {
-                       fputs(" rewrite ", file);
+                       fprintf(file, "%s rewrite ", line_prefix);
                        write_name_quoted(p->two->path, file, ' ');
                        fprintf(file, "(%d%%)\n", similarity_index(p));
                }
-               show_mode_change(file, p, !p->score);
+               show_mode_change(file, p, !p->score, line_prefix);
                break;
        }
 }
@@ -3521,7 +3762,7 @@ static int diff_get_patch_id(struct diff_options *options, unsigned char *sha1)
                                        len2, p->two->path);
                git_SHA1_Update(&ctx, buffer, len1);
 
-               xpp.flags = XDF_NEED_MINIMAL;
+               xpp.flags = 0;
                xecfg.ctxlen = 3;
                xecfg.flags = XDL_EMIT_FUNCNAMES;
                xdi_diff_outf(&mf1, &mf2, patch_id_consume, &data,
@@ -3619,8 +3860,9 @@ void diff_flush(struct diff_options *options)
                show_dirstat(options);
 
        if (output_format & DIFF_FORMAT_SUMMARY && !is_summary_empty(q)) {
-               for (i = 0; i < q->nr; i++)
-                       diff_summary(options->file, q->queue[i]);
+               for (i = 0; i < q->nr; i++) {
+                       diff_summary(options, q->queue[i]);
+               }
                separator++;
        }
 
diff --git a/diff.h b/diff.h
index 9ace08cbae0651d53dd558f80a3d88777b5521c3..48abe7a96a96df98ef91d5f5d7ba9f57cace777f 100644 (file)
--- a/diff.h
+++ b/diff.h
@@ -9,6 +9,7 @@
 struct rev_info;
 struct diff_options;
 struct diff_queue_struct;
+struct strbuf;
 
 typedef void (*change_fn_t)(struct diff_options *options,
                 unsigned old_mode, unsigned new_mode,
@@ -25,6 +26,8 @@ typedef void (*add_remove_fn_t)(struct diff_options *options,
 typedef void (*diff_format_fn_t)(struct diff_queue_struct *q,
                struct diff_options *options, void *data);
 
+typedef struct strbuf *(*diff_prefix_fn_t)(struct diff_options *opt, void *data);
+
 #define DIFF_FORMAT_RAW                0x0001
 #define DIFF_FORMAT_DIFFSTAT   0x0002
 #define DIFF_FORMAT_NUMSTAT    0x0004
@@ -130,6 +133,8 @@ struct diff_options {
        add_remove_fn_t add_remove;
        diff_format_fn_t format_callback;
        void *format_callback_data;
+       diff_prefix_fn_t output_prefix;
+       void *output_prefix_data;
 };
 
 enum color_diff {
@@ -141,7 +146,7 @@ enum color_diff {
        DIFF_FILE_NEW = 5,
        DIFF_COMMIT = 6,
        DIFF_WHITESPACE = 7,
-       DIFF_FUNCINFO = 8,
+       DIFF_FUNCINFO = 8
 };
 const char *diff_get_color(int diff_use_color, enum color_diff ix);
 #define diff_get_color_opt(o, ix) \
diff --git a/dir.c b/dir.c
index 5615f33af187f381f8c2dfe7ab53910fe165fd59..5e36f8e616d63f8a9c5950897e0f06235d8e9acf 100644 (file)
--- a/dir.c
+++ b/dir.c
@@ -465,7 +465,7 @@ static struct dir_entry *dir_add_ignored(struct dir_struct *dir, const char *pat
 enum exist_status {
        index_nonexistent = 0,
        index_directory,
-       index_gitdir,
+       index_gitdir
 };
 
 /*
@@ -533,7 +533,7 @@ static enum exist_status directory_exists_in_index(const char *dirname, int len)
 enum directory_treatment {
        show_directory,
        ignore_directory,
-       recurse_into_directory,
+       recurse_into_directory
 };
 
 static enum directory_treatment treat_directory(struct dir_struct *dir,
@@ -684,7 +684,7 @@ static int get_dtype(struct dirent *de, const char *path, int len)
 enum path_treatment {
        path_ignored,
        path_handled,
-       path_recurse,
+       path_recurse
 };
 
 static enum path_treatment treat_one_path(struct dir_struct *dir,
index 129a786832c2dc863e33ae2fc039a0212c1751d5..1e5d66ed0ab3b605de670758887b13f146bdd8a5 100644 (file)
@@ -267,7 +267,7 @@ struct hash_list
 typedef enum {
        WHENSPEC_RAW = 1,
        WHENSPEC_RFC2822,
-       WHENSPEC_NOW,
+       WHENSPEC_NOW
 } whenspec_type;
 
 struct recent_command
diff --git a/fsck.c b/fsck.c
index ae9ae1abee8e406e838fc32802aeb2c50ba4ca7d..3d05d4a794a4158a421ce1265422d241e28a5278 100644 (file)
--- a/fsck.c
+++ b/fsck.c
@@ -311,8 +311,6 @@ static int fsck_commit(struct commit *commit, fsck_error error_func)
        err = fsck_ident(&buffer, &commit->object, error_func);
        if (err)
                return err;
-       if (*buffer != '\n')
-               return error_func(&commit->object, FSCK_ERROR, "invalid format - expected blank line");
        if (!commit->tree)
                return error_func(&commit->object, FSCK_ERROR, "could not load commit's tree %s", sha1_to_hex(tree_sha1));
 
index 21f1330a5bf26c955051ce9cf263289d90c7667b..27fc79347af428dd39daa5b550814cc6cf980510 100755 (executable)
@@ -1111,9 +1111,9 @@ sub help_patch_cmd {
        print colored $help_color, <<EOF ;
 y - $verb this hunk$target
 n - do not $verb this hunk$target
-q - quit, do not $verb this hunk nor any of the remaining ones
-a - $verb this and all the remaining hunks in the file
-d - do not $verb this hunk nor any of the remaining hunks in the file
+q - quit; do not $verb this hunk nor any of the remaining ones
+a - $verb this hunk and all later hunks in the file
+d - do not $verb this hunk nor any of the later hunks in the file
 g - select a hunk to go to
 / - search for a hunk matching the given regex
 j - leave this hunk undecided, see next undecided hunk
index 87ffae252b3f2ff88646d142ea9c2dfb38a28953..ef2d51a2b80d77dc5fe28d76b5047a97dc4c0fae 100755 (executable)
--- a/git-am.sh
+++ b/git-am.sh
@@ -52,6 +52,16 @@ else
        HAS_HEAD=
 fi
 
+cmdline="git am"
+if test '' != "$interactive"
+then
+       cmdline="$cmdline -i"
+fi
+if test '' != "$threeway"
+then
+       cmdline="$cmdline -3"
+fi
+
 sq () {
        git rev-parse --sq-quote "$@"
 }
@@ -66,15 +76,6 @@ stop_here_user_resolve () {
            printf '%s\n' "$resolvemsg"
            stop_here $1
     fi
-    cmdline="git am"
-    if test '' != "$interactive"
-    then
-        cmdline="$cmdline -i"
-    fi
-    if test '' != "$threeway"
-    then
-        cmdline="$cmdline -3"
-    fi
     echo "When you have resolved this problem run \"$cmdline --resolved\"."
     echo "If you would prefer to skip this patch, instead run \"$cmdline --skip\"."
     echo "To restore the original branch and stop patching run \"$cmdline --abort\"."
@@ -591,6 +592,8 @@ do
 
                test -s "$dotest/patch" || {
                        echo "Patch is empty.  Was it split wrong?"
+                       echo "If you would prefer to skip this patch, instead run \"$cmdline --skip\"."
+                       echo "To restore the original branch and stop patching run \"$cmdline --abort\"."
                        stop_here $this
                }
                rm -f "$dotest/original-commit"
@@ -690,7 +693,13 @@ do
        else
            action=yes
        fi
-       FIRSTLINE=$(sed 1q "$dotest/final-commit")
+
+       if test -f "$dotest/final-commit"
+       then
+               FIRSTLINE=$(sed 1q "$dotest/final-commit")
+       else
+               FIRSTLINE=""
+       fi
 
        if test $action = skip
        then
@@ -726,6 +735,8 @@ do
                resolved=
                git diff-index --quiet --cached HEAD -- && {
                        echo "No changes - did you forget to use 'git add'?"
+                       echo "If there is nothing left to stage, chances are that something else"
+                       echo "already introduced the same changes; you might want to skip this patch."
                        stop_here_user_resolve $this
                }
                unmerged=$(git ls-files -u)
index edf352dee2271bcc1e328c43330ca9a8248c01b3..02a73eeb667e798fca29de25fed6b0b2900f6912 100644 (file)
@@ -56,7 +56,7 @@
 # define _XOPEN_SOURCE 500
 # endif
 #elif !defined(__APPLE__) && !defined(__FreeBSD__) && !defined(__USLC__) && \
-      !defined(_M_UNIX) && !defined(sgi) && !defined(__DragonFly__)
+      !defined(_M_UNIX) && !defined(__sgi) && !defined(__DragonFly__)
 #define _XOPEN_SOURCE 600 /* glibc2 and AIX 5.3L need 500, OpenBSD needs 600 for S_ISLNK() */
 #define _XOPEN_SOURCE_EXTENDED 1 /* AIX 5.3L needs this */
 #endif
@@ -200,6 +200,7 @@ extern char *gitbasename(char *);
 #include "compat/bswap.h"
 
 /* General helper functions */
+extern void vreportf(const char *prefix, const char *err, va_list params);
 extern NORETURN void usage(const char *err);
 extern NORETURN void usagef(const char *err, ...) __attribute__((format (printf, 1, 2)));
 extern NORETURN void die(const char *err, ...) __attribute__((format (printf, 1, 2)));
@@ -224,7 +225,6 @@ static inline const char *skip_prefix(const char *str, const char *prefix)
 #define PROT_READ 1
 #define PROT_WRITE 2
 #define MAP_PRIVATE 1
-#define MAP_FAILED ((void*)-1)
 #endif
 
 #define mmap git_mmap
@@ -253,6 +253,10 @@ extern int git_munmap(void *start, size_t length);
 
 #endif /* NO_MMAP */
 
+#ifndef MAP_FAILED
+#define MAP_FAILED ((void *)-1)
+#endif
+
 #ifdef NO_ST_BLOCKS_IN_STRUCT_STAT
 #define on_disk_bytes(st) ((st).st_size)
 #else
@@ -363,7 +367,8 @@ static inline void *gitmempcpy(void *dest, const void *src, size_t n)
 
 extern void release_pack_memory(size_t, int);
 
-extern void set_try_to_free_routine(void (*routine)(size_t));
+typedef void (*try_to_free_t)(size_t);
+extern try_to_free_t set_try_to_free_routine(try_to_free_t);
 
 extern char *xstrdup(const char *str);
 extern void *xmalloc(size_t size);
index 13751db882dd2c40e2c78503eba0d3f941297644..0f45c395098f2e19b7ce93ac17c45d128f05ff0d 100755 (executable)
@@ -183,12 +183,58 @@ if ($state->{method} eq 'pserver') {
        exit 1;
     }
     $line = <STDIN>; chomp $line;
-    unless ($line eq 'anonymous') {
-       print "E Only anonymous user allowed via pserver\n";
-       print "I HATE YOU\n";
-       exit 1;
+    my $user = $line;
+    $line = <STDIN>; chomp $line;
+    my $password = $line;
+
+    if ($user eq 'anonymous') {
+        # "A" will be 1 byte, use length instead in case the
+        # encryption method ever changes (yeah, right!)
+        if (length($password) > 1 ) {
+            print "E Don't supply a password for the `anonymous' user\n";
+            print "I HATE YOU\n";
+            exit 1;
+        }
+
+        # Fall through to LOVE
+    } else {
+        # Trying to authenticate a user
+        if (not exists $cfg->{gitcvs}->{authdb}) {
+            print "E the repo config file needs a [gitcvs] section with an 'authdb' parameter set to the filename of the authentication database\n";
+            print "I HATE YOU\n";
+            exit 1;
+        }
+
+        my $authdb = $cfg->{gitcvs}->{authdb};
+
+        unless (-e $authdb) {
+            print "E The authentication database specified in [gitcvs.authdb] does not exist\n";
+            print "I HATE YOU\n";
+            exit 1;
+        }
+
+        my $auth_ok;
+        open my $passwd, "<", $authdb or die $!;
+        while (<$passwd>) {
+            if (m{^\Q$user\E:(.*)}) {
+                if (crypt($user, descramble($password)) eq $1) {
+                    $auth_ok = 1;
+                }
+            };
+        }
+        close $passwd;
+
+        unless ($auth_ok) {
+            print "I HATE YOU\n";
+            exit 1;
+        }
+
+        # Fall through to LOVE
     }
-    $line = <STDIN>; chomp $line;    # validate the password?
+
+    # For checking whether the user is anonymous on commit
+    $state->{user} = $user;
+
     $line = <STDIN>; chomp $line;
     unless ($line eq "END $request REQUEST") {
        die "E Do not understand $line -- expecting END $request REQUEST\n";
@@ -1271,9 +1317,9 @@ sub req_ci
 
     $log->info("req_ci : " . ( defined($data) ? $data : "[NULL]" ));
 
-    if ( $state->{method} eq 'pserver')
+    if ( $state->{method} eq 'pserver' and $state->{user} eq 'anonymous' )
     {
-        print "error 1 pserver access cannot commit\n";
+        print "error 1 anonymous user cannot commit via pserver\n";
         cleanupWorkTree();
         exit;
     }
@@ -2586,6 +2632,43 @@ sub cvs_author
     $author;
 }
 
+
+sub descramble
+{
+    # This table is from src/scramble.c in the CVS source
+    my @SHIFTS = (
+        0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15,
+        16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+        114,120, 53, 79, 96,109, 72,108, 70, 64, 76, 67,116, 74, 68, 87,
+        111, 52, 75,119, 49, 34, 82, 81, 95, 65,112, 86,118,110,122,105,
+        41, 57, 83, 43, 46,102, 40, 89, 38,103, 45, 50, 42,123, 91, 35,
+        125, 55, 54, 66,124,126, 59, 47, 92, 71,115, 78, 88,107,106, 56,
+        36,121,117,104,101,100, 69, 73, 99, 63, 94, 93, 39, 37, 61, 48,
+        58,113, 32, 90, 44, 98, 60, 51, 33, 97, 62, 77, 84, 80, 85,223,
+        225,216,187,166,229,189,222,188,141,249,148,200,184,136,248,190,
+        199,170,181,204,138,232,218,183,255,234,220,247,213,203,226,193,
+        174,172,228,252,217,201,131,230,197,211,145,238,161,179,160,212,
+        207,221,254,173,202,146,224,151,140,196,205,130,135,133,143,246,
+        192,159,244,239,185,168,215,144,139,165,180,157,147,186,214,176,
+        227,231,219,169,175,156,206,198,129,164,150,210,154,177,134,127,
+        182,128,158,208,162,132,167,209,149,241,153,251,237,236,171,195,
+        243,233,253,240,194,250,191,155,142,137,245,235,163,242,178,152
+    );
+    my ($str) = @_;
+
+    # This should never happen, the same password format (A) has been
+    # used by CVS since the beginning of time
+    {
+        my $fmt = substr($str, 0, 1);
+        die "invalid password format `$fmt'" unless $fmt eq 'A';
+    }
+
+    my @str = unpack "C*", substr($str, 1);
+    my $ret = join '', map { chr $SHIFTS[$_] } @str;
+    return $ret;
+}
+
+
 package GITCVS::log;
 
 ####
index f6080149c22bad8b3434b874b9e4079ac508ca69..6635fbefdf2a06e556716f91a70181ecf4f3252a 100755 (executable)
@@ -24,6 +24,7 @@ restart        restart the web server
 fqgitdir="$GIT_DIR"
 local="$(git config --bool --get instaweb.local)"
 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)"
 
@@ -34,6 +35,9 @@ conf="$GIT_DIR/gitweb/httpd.conf"
 # if installed, it doesn't need further configuration (module_path)
 test -z "$httpd" && httpd='lighttpd -f'
 
+# Default is @@GITWEBDIR@@
+test -z "$root" && root='@@GITWEBDIR@@'
+
 # any untaken local port will do...
 test -z "$port" && port=1234
 
@@ -46,6 +50,12 @@ resolve_full_httpd () {
                        httpd="$httpd -f"
                fi
                ;;
+       *plackup*)
+               # server is started by running via generated gitweb.psgi in $fqgitdir/gitweb
+               full_httpd="$fqgitdir/gitweb/gitweb.psgi"
+               httpd_only="${httpd%% *}" # cut on first space
+               return
+               ;;
        esac
 
        httpd_only="$(echo $httpd | cut -f1 -d' ')"
@@ -57,7 +67,7 @@ resolve_full_httpd () {
                # these days and those are not in most users $PATHs
                # in addition, we may have generated a server script
                # in $fqgitdir/gitweb.
-               for i in /usr/local/sbin /usr/sbin "$fqgitdir/gitweb"
+               for i in /usr/local/sbin /usr/sbin "$root" "$fqgitdir/gitweb"
                do
                        if test -x "$i/$httpd_only"
                        then
@@ -83,8 +93,8 @@ start_httpd () {
 
        # don't quote $full_httpd, there can be arguments to it (-f)
        case "$httpd" in
-       *mongoose*)
-               #The mongoose server doesn't have a daemon mode so we'll have to fork it
+       *mongoose*|*plackup*)
+               #These servers don't have a daemon mode so we'll have to fork it
                $full_httpd "$fqgitdir/gitweb/httpd.conf" &
                #Save the pid before doing anything else (we'll print it later)
                pid=$!
@@ -110,6 +120,20 @@ EOF
 
 stop_httpd () {
        test -f "$fqgitdir/pid" && kill $(cat "$fqgitdir/pid")
+       rm -f "$fqgitdir/pid"
+}
+
+httpd_is_ready () {
+       "$PERL" -MIO::Socket::INET -e "
+local \$| = 1; # turn on autoflush
+exit if (IO::Socket::INET->new('127.0.0.1:$port'));
+print 'Waiting for \'$httpd\' to start ..';
+do {
+       print '.';
+       sleep(1);
+} until (IO::Socket::INET->new('127.0.0.1:$port'));
+print qq! (done)\n!;
+"
 }
 
 while test $# != 0
@@ -159,8 +183,8 @@ done
 mkdir -p "$GIT_DIR/gitweb/tmp"
 GIT_EXEC_PATH="$(git --exec-path)"
 GIT_DIR="$fqgitdir"
-export GIT_EXEC_PATH GIT_DIR
-
+GITWEB_CONFIG="$fqgitdir/gitweb/gitweb_config.perl"
+export GIT_EXEC_PATH GIT_DIR GITWEB_CONFIG
 
 webrick_conf () {
        # generate a standalone server script in $fqgitdir/gitweb.
@@ -192,7 +216,7 @@ EOF
 
        cat >"$conf" <<EOF
 :Port: $port
-:DocumentRoot: "$fqgitdir/gitweb"
+:DocumentRoot: "$root"
 :DirectoryIndex: ["gitweb.cgi"]
 :PidFile: "$fqgitdir/pid"
 EOF
@@ -201,18 +225,18 @@ EOF
 
 lighttpd_conf () {
        cat > "$conf" <<EOF
-server.document-root = "$fqgitdir/gitweb"
+server.document-root = "$root"
 server.port = $port
 server.modules = ( "mod_setenv", "mod_cgi" )
 server.indexfiles = ( "gitweb.cgi" )
 server.pid-file = "$fqgitdir/pid"
-server.errorlog = "$fqgitdir/gitweb/error.log"
+server.errorlog = "$fqgitdir/gitweb/$httpd_only/error.log"
 
 # to enable, add "mod_access", "mod_accesslog" to server.modules
 # variable above and uncomment this
-#accesslog.filename = "$fqgitdir/gitweb/access.log"
+#accesslog.filename = "$fqgitdir/gitweb/$httpd_only/access.log"
 
-setenv.add-environment = ( "PATH" => env.PATH )
+setenv.add-environment = ( "PATH" => env.PATH, "GITWEB_CONFIG" => env.GITWEB_CONFIG )
 
 cgi.assign = ( ".cgi" => "" )
 
@@ -277,14 +301,15 @@ EOF
 
 apache2_conf () {
        test -z "$module_path" && module_path=/usr/lib/apache2/modules
-       mkdir -p "$GIT_DIR/gitweb/logs"
        bind=
        test x"$local" = xtrue && bind='127.0.0.1:'
        echo 'text/css css' > "$fqgitdir/mime.types"
        cat > "$conf" <<EOF
 ServerName "git-instaweb"
-ServerRoot "$fqgitdir/gitweb"
-DocumentRoot "$fqgitdir/gitweb"
+ServerRoot "$root"
+DocumentRoot "$root"
+ErrorLog "$fqgitdir/gitweb/$httpd_only/error.log"
+CustomLog "$fqgitdir/gitweb/$httpd_only/access.log" combined
 PidFile "$fqgitdir/pid"
 Listen $bind$port
 EOF
@@ -303,13 +328,14 @@ EOF
        # check to see if Dennis Stosberg's mod_perl compatibility patch
        # (<20060621130708.Gcbc6e5c@leonov.stosberg.net>) has been applied
        if test -f "$module_path/mod_perl.so" &&
-          sane_grep 'MOD_PERL' "$GIT_DIR/gitweb/gitweb.cgi" >/dev/null
+          sane_grep 'MOD_PERL' "$root/gitweb.cgi" >/dev/null
        then
                # favor mod_perl if available
                cat >> "$conf" <<EOF
 LoadModule perl_module $module_path/mod_perl.so
 PerlPassEnv GIT_DIR
 PerlPassEnv GIT_EXEC_DIR
+PerlPassEnv GITWEB_CONFIG
 <Location /gitweb.cgi>
        SetHandler perl-script
        PerlResponseHandler ModPerl::Registry
@@ -353,15 +379,15 @@ mongoose_conf() {
 # For detailed description of every option, visit
 # http://code.google.com/p/mongoose/wiki/MongooseManual
 
-root           $fqgitdir/gitweb
+root           $root
 ports          $port
 index_files    gitweb.cgi
 #ssl_cert      $fqgitdir/gitweb/ssl_cert.pem
-error_log      $fqgitdir/gitweb/error.log
-access_log     $fqgitdir/gitweb/access.log
+error_log      $fqgitdir/gitweb/$httpd_only/error.log
+access_log     $fqgitdir/gitweb/$httpd_only/access.log
 
 #cgi setup
-cgi_env                PATH=$PATH,GIT_DIR=$GIT_DIR,GIT_EXEC_PATH=$GIT_EXEC_PATH
+cgi_env                PATH=$PATH,GIT_DIR=$GIT_DIR,GIT_EXEC_PATH=$GIT_EXEC_PATH,GITWEB_CONFIG=$GITWEB_CONFIG
 cgi_interp     $PERL
 cgi_ext                cgi,pl
 
@@ -370,41 +396,165 @@ mime_types       .gz=application/x-gzip,.tar.gz=application/x-tgz,.tgz=application/x-t
 EOF
 }
 
-
-script='
-s#^(my|our) \$projectroot =.*#$1 \$projectroot = "'$(dirname "$fqgitdir")'";#;
-s#(my|our) \$gitbin =.*#$1 \$gitbin = "'$GIT_EXEC_PATH'";#;
-s#(my|our) \$projects_list =.*#$1 \$projects_list = \$projectroot;#;
-s#(my|our) \$git_temp =.*#$1 \$git_temp = "'$fqgitdir/gitweb/tmp'";#;'
-
-gitweb_cgi () {
-       cat > "$1.tmp" <<\EOFGITWEB
-@@GITWEB_CGI@@
-EOFGITWEB
-       # Use the configured full path to perl to match the generated
-       # scripts' 'hashpling' line
-       "$PERL" -p -e "$script" "$1.tmp"  > "$1"
-       chmod +x "$1"
-       rm -f "$1.tmp"
+plackup_conf () {
+       # generate a standalone 'plackup' server script in $fqgitdir/gitweb
+       # with embedded configuration; it does not use "$conf" file
+       cat > "$fqgitdir/gitweb/gitweb.psgi" <<EOF
+#!$PERL
+
+# gitweb - simple web interface to track changes in git repositories
+#          PSGI wrapper and server starter (see http://plackperl.org)
+
+use strict;
+
+use IO::Handle;
+use Plack::MIME;
+use Plack::Builder;
+use Plack::App::WrapCGI;
+use CGI::Emulate::PSGI 0.07; # minimum version required to work with gitweb
+
+# mimetype mapping (from lighttpd_conf)
+Plack::MIME->add_type(
+       ".pdf"          =>      "application/pdf",
+       ".sig"          =>      "application/pgp-signature",
+       ".spl"          =>      "application/futuresplash",
+       ".class"        =>      "application/octet-stream",
+       ".ps"           =>      "application/postscript",
+       ".torrent"      =>      "application/x-bittorrent",
+       ".dvi"          =>      "application/x-dvi",
+       ".gz"           =>      "application/x-gzip",
+       ".pac"          =>      "application/x-ns-proxy-autoconfig",
+       ".swf"          =>      "application/x-shockwave-flash",
+       ".tar.gz"       =>      "application/x-tgz",
+       ".tgz"          =>      "application/x-tgz",
+       ".tar"          =>      "application/x-tar",
+       ".zip"          =>      "application/zip",
+       ".mp3"          =>      "audio/mpeg",
+       ".m3u"          =>      "audio/x-mpegurl",
+       ".wma"          =>      "audio/x-ms-wma",
+       ".wax"          =>      "audio/x-ms-wax",
+       ".ogg"          =>      "application/ogg",
+       ".wav"          =>      "audio/x-wav",
+       ".gif"          =>      "image/gif",
+       ".jpg"          =>      "image/jpeg",
+       ".jpeg"         =>      "image/jpeg",
+       ".png"          =>      "image/png",
+       ".xbm"          =>      "image/x-xbitmap",
+       ".xpm"          =>      "image/x-xpixmap",
+       ".xwd"          =>      "image/x-xwindowdump",
+       ".css"          =>      "text/css",
+       ".html"         =>      "text/html",
+       ".htm"          =>      "text/html",
+       ".js"           =>      "text/javascript",
+       ".asc"          =>      "text/plain",
+       ".c"            =>      "text/plain",
+       ".cpp"          =>      "text/plain",
+       ".log"          =>      "text/plain",
+       ".conf"         =>      "text/plain",
+       ".text"         =>      "text/plain",
+       ".txt"          =>      "text/plain",
+       ".dtd"          =>      "text/xml",
+       ".xml"          =>      "text/xml",
+       ".mpeg"         =>      "video/mpeg",
+       ".mpg"          =>      "video/mpeg",
+       ".mov"          =>      "video/quicktime",
+       ".qt"           =>      "video/quicktime",
+       ".avi"          =>      "video/x-msvideo",
+       ".asf"          =>      "video/x-ms-asf",
+       ".asx"          =>      "video/x-ms-asf",
+       ".wmv"          =>      "video/x-ms-wmv",
+       ".bz2"          =>      "application/x-bzip",
+       ".tbz"          =>      "application/x-bzip-compressed-tar",
+       ".tar.bz2"      =>      "application/x-bzip-compressed-tar",
+       ""              =>      "text/plain"
+);
+
+my \$app = builder {
+       # to be able to override \$SIG{__WARN__} to log build time warnings
+       use CGI::Carp; # it sets \$SIG{__WARN__} itself
+
+       my \$logdir = "$fqgitdir/gitweb/$httpd_only";
+       open my \$access_log_fh, '>>', "\$logdir/access.log"
+               or die "Couldn't open access log '\$logdir/access.log': \$!";
+       open my \$error_log_fh,  '>>', "\$logdir/error.log"
+               or die "Couldn't open error log '\$logdir/error.log': \$!";
+
+       \$access_log_fh->autoflush(1);
+       \$error_log_fh->autoflush(1);
+
+       # redirect build time warnings to error.log
+       \$SIG{'__WARN__'} = sub {
+               my \$msg = shift;
+               # timestamp warning like in CGI::Carp::warn
+               my \$stamp = CGI::Carp::stamp();
+               \$msg =~ s/^/\$stamp/gm;
+               print \$error_log_fh \$msg;
+       };
+
+       # write errors to error.log, access to access.log
+       enable 'AccessLog',
+               format => "combined",
+               logger => sub { print \$access_log_fh @_; };
+       enable sub {
+               my \$app = shift;
+               sub {
+                       my \$env = shift;
+                       \$env->{'psgi.errors'} = \$error_log_fh;
+                       \$app->(\$env);
+               }
+       };
+       # gitweb currently doesn't work with $SIG{CHLD} set to 'IGNORE',
+       # because it uses 'close $fd or die...' on piped filehandle $fh
+       # (which causes the parent process to wait for child to finish).
+       enable_if { \$SIG{'CHLD'} eq 'IGNORE' } sub {
+               my \$app = shift;
+               sub {
+                       my \$env = shift;
+                       local \$SIG{'CHLD'} = 'DEFAULT';
+                       local \$SIG{'CLD'}  = 'DEFAULT';
+                       \$app->(\$env);
+               }
+       };
+       # serve static files, i.e. stylesheet, images, script
+       enable 'Static',
+               path => sub { m!\.(js|css|png)\$! && s!^/gitweb/!! },
+               root => "$root/",
+               encoding => 'utf-8'; # encoding for 'text/plain' files
+       # convert CGI application to PSGI app
+       Plack::App::WrapCGI->new(script => "$root/gitweb.cgi")->to_app;
+};
+
+# make it runnable as standalone app,
+# like it would be run via 'plackup' utility
+if (__FILE__ eq \$0) {
+       require Plack::Runner;
+
+       my \$runner = Plack::Runner->new();
+       \$runner->parse_options(qw(--env deployment --port $port),
+                              "$local" ? qw(--host 127.0.0.1) : ());
+       \$runner->run(\$app);
 }
+__END__
+EOF
 
-gitweb_css () {
-       cat > "$1" <<\EOFGITWEB
-@@GITWEB_CSS@@
-
-EOFGITWEB
+       chmod a+x "$fqgitdir/gitweb/gitweb.psgi"
+       # configuration is embedded in server script file, gitweb.psgi
+       rm -f "$conf"
 }
 
-gitweb_js () {
-       cat > "$1" <<\EOFGITWEB
-@@GITWEB_JS@@
-
-EOFGITWEB
+gitweb_conf() {
+       cat > "$fqgitdir/gitweb/gitweb_config.perl" <<EOF
+#!/usr/bin/perl
+our \$projectroot = "$(dirname "$fqgitdir")";
+our \$git_temp = "$fqgitdir/gitweb/tmp";
+our \$projects_list = \$projectroot;
+EOF
 }
 
-gitweb_cgi "$GIT_DIR/gitweb/gitweb.cgi"
-gitweb_css "$GIT_DIR/@@GITWEB_CSS_NAME@@"
-gitweb_js  "$GIT_DIR/@@GITWEB_JS_NAME@@"
+gitweb_conf
+
+resolve_full_httpd
+mkdir -p "$fqgitdir/gitweb/$httpd_only"
 
 case "$httpd" in
 *lighttpd*)
@@ -419,6 +569,9 @@ webrick)
 *mongoose*)
        mongoose_conf
        ;;
+*plackup*)
+       plackup_conf
+       ;;
 *)
        echo "Unknown httpd specified: $httpd"
        exit 1
@@ -429,7 +582,7 @@ start_httpd
 url=http://127.0.0.1:$port
 
 if test -n "$browser"; then
-       git web--browse -b "$browser" $url || echo $url
+       httpd_is_ready && git web--browse -b "$browser" $url || echo $url
 else
-       git web--browse -c "instaweb.browser" $url || echo $url
+       httpd_is_ready && git web--browse -c "instaweb.browser" $url || echo $url
 fi
index d067894bf45fd0a50513e196ea2a5e671d901681..b86402afa5d079df8d1cf6eebb15e5727cc8a5de 100755 (executable)
@@ -107,7 +107,7 @@ case "${1:-.}${2:-.}${3:-.}" in
                # remove lines that are unique to ours.
                orig=`git-unpack-file $2`
                sz0=`wc -c <"$orig"`
-               diff -u -La/$orig -Lb/$orig $orig $src2 | git apply --no-add
+               @@DIFF@@ -u -La/$orig -Lb/$orig $orig $src2 | git apply --no-add
                sz1=`wc -c <"$orig"`
 
                # If we do not have enough common material, it is not
index 1a4729f7bb29205fb7bc251887dcd4f5237f1659..a09a44ec4ce3d82cebc5f972f09e94c7c35dc948 100755 (executable)
@@ -43,6 +43,7 @@ merge_args=
 curr_branch=$(git symbolic-ref -q HEAD)
 curr_branch_short="${curr_branch#refs/heads/}"
 rebase=$(git config --bool branch.$curr_branch_short.rebase)
+dry_run=
 while :
 do
        case "$1" in
@@ -104,6 +105,9 @@ do
        --no-r|--no-re|--no-reb|--no-reba|--no-rebas|--no-rebase)
                rebase=false
                ;;
+       --d|--dr|--dry|--dry-|--dry-r|--dry-ru|--dry-run)
+               dry_run=--dry-run
+               ;;
        -h|--h|--he|--hel|--help)
                usage
                ;;
@@ -216,7 +220,8 @@ test true = "$rebase" && {
        done
 }
 orig_head=$(git rev-parse -q --verify HEAD)
-git fetch $verbosity $progress --update-head-ok "$@" || exit 1
+git fetch $verbosity $progress $dry_run --update-head-ok "$@" || exit 1
+test -z "$dry_run" || exit 0
 
 curr_head=$(git rev-parse -q --verify HEAD)
 if test -n "$orig_head" && test "$curr_head" != "$orig_head"
index 44f5c65fdb5e81c4b660a666c2edd71aa75c5c1a..ab4afa7dee377707484e7f76c7d44ac955f09397 100755 (executable)
@@ -3,7 +3,7 @@
 # Copyright (c) 2005 Junio C Hamano.
 #
 
-USAGE='[--interactive | -i] [-v] [--force-rebase | -f] [--no-ff] [--onto <newbase>] [<upstream>|--root] [<branch>] [--quiet | -q]'
+USAGE='[--interactive | -i] [-v] [--force-rebase | -f] [--no-ff] [--onto <newbase>] (<upstream>|--root) [<branch>] [--quiet | -q]'
 LONG_USAGE='git-rebase replaces <branch> with a new branch of the
 same name.  When the --onto option is provided the new branch starts
 out with a HEAD equal to <newbase>, otherwise it is equal to <upstream>
@@ -198,14 +198,6 @@ test -f "$GIT_DIR"/rebase-apply/applying &&
 
 is_interactive "$@" && exec git-rebase--interactive "$@"
 
-if test $# -eq 0
-then
-       test -d "$dotest" -o -d "$GIT_DIR"/rebase-apply || usage
-       test -d "$dotest" -o -f "$GIT_DIR"/rebase-apply/rebasing &&
-               die 'A rebase is in progress, try --continue, --skip or --abort.'
-       die "No arguments given and $GIT_DIR/rebase-apply already exists."
-fi
-
 while test $# != 0
 do
        case "$1" in
@@ -370,6 +362,13 @@ do
 done
 test $# -gt 2 && usage
 
+if test $# -eq 0 && test -z "$rebase_root"
+then
+       test -d "$dotest" -o -d "$GIT_DIR"/rebase-apply || usage
+       test -d "$dotest" -o -f "$GIT_DIR"/rebase-apply/rebasing &&
+               die 'A rebase is in progress, try --continue, --skip or --abort.'
+fi
+
 # Make sure we do not have $GIT_DIR/rebase-apply
 if test -z "$do_merge"
 then
index 92539222c57d1966f847a641c976754f1941dc2b..df9d512f1a966635828cb7a8dadde3b0c2b7b9d8 100644 (file)
@@ -1,6 +1,12 @@
 #!/usr/bin/env python
 
-import hashlib
+# hashlib is only available in python >= 2.5
+try:
+    import hashlib
+    _digest = hashlib.sha1
+except ImportError:
+    import sha
+    _digest = sha.new
 import sys
 import os
 sys.path.insert(0, os.getenv("GITPYTHONLIB","."))
@@ -19,7 +25,7 @@ def get_repo(alias, url):
     repo.get_revs()
     repo.get_head()
 
-    hasher = hashlib.sha1()
+    hasher = _digest()
     hasher.update(repo.path)
     repo.hash = hasher.hexdigest()
 
@@ -133,7 +139,10 @@ def do_export(repo, args):
 
     path = os.path.join(dirname, 'testgit.marks')
     print path
-    print path if os.path.exists(path) else ""
+    if os.path.exists(path):
+        print path
+    else:
+        print ""
     sys.stdout.flush()
 
     update_local_repo(repo)
index 74238b031370a02d8456aeee9003977f001bd9c7..6fdea397ddec3cfa4ff37cdf672e7cced840bb60 100755 (executable)
@@ -1,4 +1,4 @@
-#!/bin/sh -e
+#!/bin/sh
 # Copyright 2005, Ryan Anderson <ryan@michonline.com>
 #
 # This file is licensed under the GPL v2, or a later version
@@ -70,10 +70,10 @@ git show -s --format='The following changes since commit %H:
 
   %s (%ci)
 
-are available in the git repository at:' $baserev
-echo "  $url $branch"
-echo
+are available in the git repository at:' $baserev &&
+echo "  $url $branch" &&
+echo &&
 
-git shortlog ^$baserev $headrev
-git diff -M --stat --summary $patch $merge_base..$headrev
+git shortlog ^$baserev $headrev &&
+git diff -M --stat --summary $patch $merge_base..$headrev || exit
 exit $status
index 8175cb279d38a663fc0481e2b11f4b5ea60f8a32..8c562a72e6e95fce60b78c015623212b7fce1dab 100755 (executable)
@@ -271,6 +271,8 @@ cmd_foreach()
                shift
        done
 
+       toplevel=$(pwd)
+
        module_list |
        while read mode sha1 stage path
        do
index b3b6964f95976ba885a277de9d463a2443dd3147..19d6848d0e7754b9afb9fdbbac6fce31286468a5 100755 (executable)
@@ -963,6 +963,7 @@ sub cmd_multi_init {
        }
        do_git_init_db();
        if (defined $_trunk) {
+               $_trunk =~ s#^/+##;
                my $trunk_ref = 'refs/remotes/' . $_prefix . 'trunk';
                # try both old-style and new-style lookups:
                my $gs_trunk = eval { Git::SVN->new($trunk_ref) };
@@ -2054,6 +2055,9 @@ sub new {
                         "\":$ref_id\$\" in config\n";
                ($self->{path}, undef) = split(/\s*:\s*/, $fetch);
        }
+       $self->{path} =~ s{/+}{/}g;
+       $self->{path} =~ s{\A/}{};
+       $self->{path} =~ s{/\z}{};
        $self->{url} = command_oneline('config', '--get',
                                       "svn-remote.$repo_id.url") or
                   die "Failed to read \"svn-remote.$repo_id.url\" in config\n";
@@ -2836,8 +2840,9 @@ sub mkemptydirs {
        foreach my $d (sort keys %empty_dirs) {
                $d = uri_decode($d);
                $d =~ s/$strip//;
+               next unless length($d);
                next if -d $d;
-               if (-e _) {
+               if (-e $d) {
                        warn "$d exists but is not a directory\n";
                } else {
                        print "creating empty directory: $d\n";
index a578c3a73203fbf1bf4abfb024b1e83c45f2b2ce..dbded76aaf5499dd116e5ad22f95d4518b79c0d8 100755 (executable)
@@ -31,7 +31,7 @@ valid_custom_tool()
 
 valid_tool() {
        case "$1" in
-               firefox | iceweasel | konqueror | w3m | links | lynx | dillo | open | start)
+               firefox | iceweasel | chrome | chromium | konqueror | w3m | links | lynx | dillo | open | start)
                        ;; # happy
                *)
                        valid_custom_tool "$1" || return 1
@@ -103,7 +103,7 @@ fi
 
 if test -z "$browser" ; then
     if test -n "$DISPLAY"; then
-       browser_candidates="firefox iceweasel konqueror w3m links lynx dillo"
+       browser_candidates="firefox iceweasel chrome chromium konqueror w3m links lynx dillo"
        if test "$KDE_FULL_SESSION" = "true"; then
            browser_candidates="konqueror $browser_candidates"
        fi
@@ -146,6 +146,11 @@ case "$browser" in
        test "$vers" -lt 2 && NEWTAB=''
        "$browser_path" $NEWTAB "$@" &
        ;;
+    chrome|chromium)
+       # Actual command for chromium is chromium-browser.
+       # No need to specify newTab. It's default in chromium
+       eval "$browser_path" "$@" &
+       ;;
     konqueror)
        case "$(basename "$browser_path")" in
            konqueror)
index dfaab00b5fe971a6d2379710795b3038dd851e90..f40f9d6a29cda39ee7f989e11ef66cdf3d54d625 100644 (file)
@@ -48,4 +48,6 @@ class GitExporter(object):
 
         args = ["sed", "s_refs/heads/_" + self.repo.prefix + "_g"]
 
-        subprocess.check_call(args, stdin=p1.stdout)
+        child = subprocess.Popen(args, stdin=p1.stdout)
+        if child.wait() != 0:
+            raise CalledProcessError
index af2919d92cee77b5e806dfdcb8361187d86e4b0e..70a712729b63047b9760b72d3e2b0a45ff176646 100644 (file)
@@ -35,4 +35,6 @@ class GitImporter(object):
         if os.path.exists(path):
             args.append("--import-marks=" + path)
 
-        subprocess.check_call(args)
+        child = subprocess.Popen(args)
+        if child.wait() != 0:
+            raise CalledProcessError
index d75ef8f214fda0748d7f8d690d3a811aa07d76c1..f27389bb945ef423dc412c2368762439086f593e 100644 (file)
@@ -29,7 +29,9 @@ class NonLocalGit(object):
         os.makedirs(path)
         args = ["git", "clone", "--bare", "--quiet", self.repo.gitpath, path]
 
-        subprocess.check_call(args)
+        child = subprocess.Popen(args)
+        if child.wait() != 0:
+            raise CalledProcessError
 
         return path
 
@@ -43,10 +45,14 @@ class NonLocalGit(object):
             die("could not find repo at %s", path)
 
         args = ["git", "--git-dir=" + path, "fetch", "--quiet", self.repo.gitpath]
-        subprocess.check_call(args)
+        child = subprocess.Popen(args)
+        if child.wait() != 0:
+            raise CalledProcessError
 
         args = ["git", "--git-dir=" + path, "update-ref", "refs/heads/master", "FETCH_HEAD"]
-        subprocess.check_call(args)
+        child = subprocess.Popen(args)
+        if child.wait() != 0:
+            raise CalledProcessError
 
     def push(self, base):
         """Pushes from the non-local repo to base.
@@ -58,4 +64,6 @@ class NonLocalGit(object):
             die("could not find repo at %s", path)
 
         args = ["git", "--git-dir=" + path, "push", "--quiet", self.repo.gitpath]
-        subprocess.check_call(args)
+        child = subprocess.Popen(args)
+        if child.wait() != 0:
+            raise CalledProcessError
index 82d5f78c7eeb3eef7e307d654b424b4c831bf2ba..58e1cdb560fa0fe1a4745f971064e5e967408502 100644 (file)
@@ -19,7 +19,10 @@ def is_remote(url):
 
     prefixes = ["http", "file", "git"]
 
-    return any(url.startswith(i) for i in prefixes)
+    for prefix in prefixes:
+        if url.startswith(prefix):
+            return True
+    return False
 
 class GitRepo(object):
     """Repo object representing a repo.
@@ -50,7 +53,9 @@ class GitRepo(object):
         path = ".cached_revs"
         ofile = open(path, "w")
 
-        subprocess.check_call(args, stdout=ofile)
+        child = subprocess.Popen(args, stdout=ofile)
+        if child.wait() != 0:
+            raise CalledProcessError
         output = open(path).readlines()
         self.revmap = dict(sanitize(i) for i in output)
         if "HEAD" in self.revmap:
index d484d76b753ef3d4ca0d75725e44f3c58ab2b482..823053173c92e4097dad54945db16f42b4b5d994 100644 (file)
@@ -2,9 +2,10 @@ GIT web Interface (gitweb) Installation
 =======================================
 
 First you have to generate gitweb.cgi from gitweb.perl using
-"make gitweb", then copy appropriate files (gitweb.cgi, gitweb.js,
-gitweb.css, git-logo.png and git-favicon.png) to their destination.
-For example if git was (or is) installed with /usr prefix, you can do
+"make gitweb", then "make install-gitweb" appropriate files
+(gitweb.cgi, gitweb.js, gitweb.css, git-logo.png and git-favicon.png)
+to their destination. For example if git was (or is) installed with
+/usr prefix and gitwebdir is /var/www/cgi-bin, you can do
 
        $ make prefix=/usr gitweb                            ;# as yourself
        # make gitwebdir=/var/www/cgi-bin install-gitweb     ;# as root
@@ -81,16 +82,14 @@ Build example
   minifiers, you can do
 
        make GITWEB_PROJECTROOT="/home/local/scm" \
-            GITWEB_JS="/gitweb/gitweb.js" \
-            GITWEB_CSS="/gitweb/gitweb.css" \
-            GITWEB_LOGO="/gitweb/git-logo.png" \
-            GITWEB_FAVICON="/gitweb/git-favicon.png" \
+            GITWEB_JS="gitweb/static/gitweb.js" \
+            GITWEB_CSS="gitweb/static/gitweb.css" \
+            GITWEB_LOGO="gitweb/static/git-logo.png" \
+            GITWEB_FAVICON="gitweb/static/git-favicon.png" \
             bindir=/usr/local/bin \
             gitweb
 
-       cp -fv gitweb/gitweb.{cgi,js,css} \
-              gitweb/git-{favicon,logo}.png \
-            /var/www/cgi-bin/gitweb/
+       make gitwebdir=/var/www/cgi-bin/gitweb install-gitweb
 
 
 Gitweb config file
index 935d2d2e0775234fc99a29fd5b7610978a00099f..2fb7c2d77bbd5f2041341822859dce51ae504d83 100644 (file)
@@ -4,10 +4,10 @@ all::
 # Define V=1 to have a more verbose compile.
 #
 # Define JSMIN to point to JavaScript minifier that functions as
-# a filter to have gitweb.js minified.
+# a filter to have static/gitweb.js minified.
 #
 # Define CSSMIN to point to a CSS minifier in order to generate a minified
-# version of gitweb.css
+# version of static/gitweb.css
 #
 
 prefix ?= $(HOME)
@@ -29,10 +29,10 @@ GITWEB_STRICT_EXPORT =
 GITWEB_BASE_URL =
 GITWEB_LIST =
 GITWEB_HOMETEXT = indextext.html
-GITWEB_CSS = gitweb.css
-GITWEB_LOGO = git-logo.png
-GITWEB_FAVICON = git-favicon.png
-GITWEB_JS = gitweb.js
+GITWEB_CSS = static/gitweb.css
+GITWEB_LOGO = static/git-logo.png
+GITWEB_FAVICON = static/git-favicon.png
+GITWEB_JS = static/gitweb.js
 GITWEB_SITE_HEADER =
 GITWEB_SITE_FOOTER =
 
@@ -54,6 +54,7 @@ PERL_PATH  ?= /usr/bin/perl
 # Shell quote;
 bindir_SQ = $(subst ','\'',$(bindir))#'
 gitwebdir_SQ = $(subst ','\'',$(gitwebdir))#'
+gitwebstaticdir_SQ = $(subst ','\'',$(gitwebdir)/static)#'
 SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH))#'
 PERL_PATH_SQ  = $(subst ','\'',$(PERL_PATH))#'
 DESTDIR_SQ    = $(subst ','\'',$(DESTDIR))#'
@@ -88,26 +89,26 @@ all:: gitweb.cgi
 GITWEB_PROGRAMS = gitweb.cgi
 
 ifdef JSMIN
-GITWEB_FILES += gitweb.min.js
-GITWEB_JS = gitweb.min.js
-all:: gitweb.min.js
-gitweb.min.js: gitweb.js GITWEB-BUILD-OPTIONS
+GITWEB_FILES += static/gitweb.min.js
+GITWEB_JS = static/gitweb.min.js
+all:: static/gitweb.min.js
+static/gitweb.min.js: static/gitweb.js GITWEB-BUILD-OPTIONS
        $(QUIET_GEN)$(JSMIN) <$< >$@
 else
-GITWEB_FILES += gitweb.js
+GITWEB_FILES += static/gitweb.js
 endif
 
 ifdef CSSMIN
-GITWEB_FILES += gitweb.min.css
-GITWEB_CSS = gitweb.min.css
-all:: gitweb.min.css
-gitweb.min.css: gitweb.css GITWEB-BUILD-OPTIONS
-       $(QUIET_GEN)$(CSSMIN) <$ >$@
+GITWEB_FILES += static/gitweb.min.css
+GITWEB_CSS = static/gitweb.min.css
+all:: static/gitweb.min.css
+static/gitweb.min.css: static/gitweb.css GITWEB-BUILD-OPTIONS
+       $(QUIET_GEN)$(CSSMIN) <$< >$@
 else
-GITWEB_FILES += gitweb.css
+GITWEB_FILES += static/gitweb.css
 endif
 
-GITWEB_FILES += git-logo.png git-favicon.png
+GITWEB_FILES += static/git-logo.png static/git-favicon.png
 
 GITWEB_REPLACE = \
        -e 's|++GIT_VERSION++|$(GIT_VERSION)|g' \
@@ -147,12 +148,13 @@ gitweb.cgi: gitweb.perl GITWEB-BUILD-OPTIONS
 install: all
        $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(gitwebdir_SQ)'
        $(INSTALL) -m 755 $(GITWEB_PROGRAMS) '$(DESTDIR_SQ)$(gitwebdir_SQ)'
-       $(INSTALL) -m 644 $(GITWEB_FILES)    '$(DESTDIR_SQ)$(gitwebdir_SQ)'
+       $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(gitwebstaticdir_SQ)'
+       $(INSTALL) -m 644 $(GITWEB_FILES) '$(DESTDIR_SQ)$(gitwebstaticdir_SQ)'
 
 ### Cleaning rules
 
 clean:
-       $(RM) gitweb.cgi gitweb.min.js gitweb.min.css GITWEB-BUILD-OPTIONS
+       $(RM) gitweb.cgi static/gitweb.min.js static/gitweb.min.css GITWEB-BUILD-OPTIONS
 
 .PHONY: all clean install .FORCE-GIT-VERSION-FILE FORCE
 
index 71742b335dd786a24699b06b17519e264724ea5b..0e19be8d216aa813221c8663d6424f96f80bfb14 100644 (file)
@@ -80,24 +80,26 @@ You can specify the following configuration variables when building GIT:
    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: gitweb.css (or gitweb.min.css if the
-   CSSMIN variable is defined / CSS minifier is used)]
+   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: git-logo.png]
+   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: git-favicon.png]
+   to base URI of gitweb.  [Default: static/git-favicon.png]
  * GITWEB_JS
    Points to the localtion 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: gitweb.js (or gitweb.min.js
-   if JSMIN build variable is defined / JavaScript minifier is used)]
+   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
diff --git a/gitweb/git-favicon.png b/gitweb/git-favicon.png
deleted file mode 100644 (file)
index aae35a7..0000000
Binary files a/gitweb/git-favicon.png and /dev/null differ
diff --git a/gitweb/git-logo.png b/gitweb/git-logo.png
deleted file mode 100644 (file)
index f4ede2e..0000000
Binary files a/gitweb/git-logo.png and /dev/null differ
diff --git a/gitweb/gitweb.css b/gitweb/gitweb.css
deleted file mode 100644 (file)
index 50067f2..0000000
+++ /dev/null
@@ -1,574 +0,0 @@
-body {
-       font-family: sans-serif;
-       font-size: small;
-       border: solid #d9d8d1;
-       border-width: 1px;
-       margin: 10px;
-       background-color: #ffffff;
-       color: #000000;
-}
-
-a {
-       color: #0000cc;
-}
-
-a:hover, a:visited, a:active {
-       color: #880000;
-}
-
-span.cntrl {
-       border: dashed #aaaaaa;
-       border-width: 1px;
-       padding: 0px 2px 0px 2px;
-       margin:  0px 2px 0px 2px;
-}
-
-img.logo {
-       float: right;
-       border-width: 0px;
-}
-
-img.avatar {
-       vertical-align: middle;
-}
-
-a.list img.avatar {
-       border-style: none;
-}
-
-div.page_header {
-       height: 25px;
-       padding: 8px;
-       font-size: 150%;
-       font-weight: bold;
-       background-color: #d9d8d1;
-}
-
-div.page_header a:visited, a.header {
-       color: #0000cc;
-}
-
-div.page_header a:hover {
-       color: #880000;
-}
-
-div.page_nav {
-       padding: 8px;
-}
-
-div.page_nav a:visited {
-       color: #0000cc;
-}
-
-div.page_path {
-       padding: 8px;
-       font-weight: bold;
-       border: solid #d9d8d1;
-       border-width: 0px 0px 1px;
-}
-
-div.page_footer {
-       height: 17px;
-       padding: 4px 8px;
-       background-color: #d9d8d1;
-}
-
-div.page_footer_text {
-       float: left;
-       color: #555555;
-       font-style: italic;
-}
-
-div#generating_info {
-       margin: 4px;
-       font-size: smaller;
-       text-align: center;
-       color: #505050;
-}
-
-div.page_body {
-       padding: 8px;
-       font-family: monospace;
-}
-
-div.title, a.title {
-       display: block;
-       padding: 6px 8px;
-       font-weight: bold;
-       background-color: #edece6;
-       text-decoration: none;
-       color: #000000;
-}
-
-div.readme {
-       padding: 8px;
-}
-
-a.title:hover {
-       background-color: #d9d8d1;
-}
-
-div.title_text {
-       padding: 6px 0px;
-       border: solid #d9d8d1;
-       border-width: 0px 0px 1px;
-       font-family: monospace;
-}
-
-div.log_body {
-       padding: 8px 8px 8px 150px;
-}
-
-span.age {
-       position: relative;
-       float: left;
-       width: 142px;
-       font-style: italic;
-}
-
-span.signoff {
-       color: #888888;
-}
-
-div.log_link {
-       padding: 0px 8px;
-       font-size: 70%;
-       font-family: sans-serif;
-       font-style: normal;
-       position: relative;
-       float: left;
-       width: 136px;
-}
-
-div.list_head {
-       padding: 6px 8px 4px;
-       border: solid #d9d8d1;
-       border-width: 1px 0px 0px;
-       font-style: italic;
-}
-
-.author_date, .author {
-       font-style: italic;
-}
-
-div.author_date {
-       padding: 8px;
-       border: solid #d9d8d1;
-       border-width: 0px 0px 1px 0px;
-}
-
-a.list {
-       text-decoration: none;
-       color: #000000;
-}
-
-a.subject, a.name {
-       font-weight: bold;
-}
-
-table.tags a.subject {
-       font-weight: normal;
-}
-
-a.list:hover {
-       text-decoration: underline;
-       color: #880000;
-}
-
-a.text {
-       text-decoration: none;
-       color: #0000cc;
-}
-
-a.text:visited {
-       text-decoration: none;
-       color: #880000;
-}
-
-a.text:hover {
-       text-decoration: underline;
-       color: #880000;
-}
-
-table {
-       padding: 8px 4px;
-       border-spacing: 0;
-}
-
-table.diff_tree {
-       font-family: monospace;
-}
-
-table.combined.diff_tree th {
-       text-align: center;
-}
-
-table.combined.diff_tree td {
-       padding-right: 24px;
-}
-
-table.combined.diff_tree th.link,
-table.combined.diff_tree td.link {
-       padding: 0px 2px;
-}
-
-table.combined.diff_tree td.nochange a {
-       color: #6666ff;
-}
-
-table.combined.diff_tree td.nochange a:hover,
-table.combined.diff_tree td.nochange a:visited {
-       color: #d06666;
-}
-
-table.blame {
-       border-collapse: collapse;
-}
-
-table.blame td {
-       padding: 0px 5px;
-       font-size: 100%;
-       vertical-align: top;
-}
-
-th {
-       padding: 2px 5px;
-       font-size: 100%;
-       text-align: left;
-}
-
-/* do not change row style on hover for 'blame' view */
-tr.light,
-table.blame .light:hover {
-       background-color: #ffffff;
-}
-
-tr.dark,
-table.blame .dark:hover {
-       background-color: #f6f6f0;
-}
-
-/* currently both use the same, but it can change */
-tr.light:hover,
-tr.dark:hover {
-       background-color: #edece6;
-}
-
-/* boundary commits in 'blame' view */
-/* and commits without "previous" */
-tr.boundary td.sha1,
-tr.no-previous td.linenr {
-       font-weight: bold;
-}
-
-/* for 'blame_incremental', during processing */
-tr.color1 { background-color: #f6fff6; }
-tr.color2 { background-color: #f6f6ff; }
-tr.color3 { background-color: #fff6f6; }
-
-td {
-       padding: 2px 5px;
-       font-size: 100%;
-       vertical-align: top;
-}
-
-td.link, td.selflink {
-       padding: 2px 5px;
-       font-family: sans-serif;
-       font-size: 70%;
-}
-
-td.selflink {
-       padding-right: 0px;
-}
-
-td.sha1 {
-       font-family: monospace;
-}
-
-.error {
-       color: red;
-       background-color: yellow;
-}
-
-td.current_head {
-       text-decoration: underline;
-}
-
-table.diff_tree span.file_status.new {
-       color: #008000;
-}
-
-table.diff_tree span.file_status.deleted {
-       color: #c00000;
-}
-
-table.diff_tree span.file_status.moved,
-table.diff_tree span.file_status.mode_chnge {
-       color: #777777;
-}
-
-table.diff_tree span.file_status.copied {
-  color: #70a070;
-}
-
-/* noage: "No commits" */
-table.project_list td.noage {
-       color: #808080;
-       font-style: italic;
-}
-
-/* age2: 60*60*24*2 <= age */
-table.project_list td.age2, table.blame td.age2 {
-       font-style: italic;
-}
-
-/* age1: 60*60*2 <= age < 60*60*24*2 */
-table.project_list td.age1 {
-       color: #009900;
-       font-style: italic;
-}
-
-table.blame td.age1 {
-       color: #009900;
-       background: transparent;
-}
-
-/* age0: age < 60*60*2 */
-table.project_list td.age0 {
-       color: #009900;
-       font-style: italic;
-       font-weight: bold;
-}
-
-table.blame td.age0 {
-       color: #009900;
-       background: transparent;
-       font-weight: bold;
-}
-
-td.pre, div.pre, div.diff {
-       font-family: monospace;
-       font-size: 12px;
-       white-space: pre;
-}
-
-td.mode {
-       font-family: monospace;
-}
-
-/* progress of blame_interactive */
-div#progress_bar {
-       height: 2px;
-       margin-bottom: -2px;
-       background-color: #d8d9d0;
-}
-div#progress_info {
-       float: right;
-       text-align: right;
-}
-
-/* format of (optional) objects size in 'tree' view */
-td.size {
-       font-family: monospace;
-       text-align: right;
-}
-
-/* styling of diffs (patchsets): commitdiff and blobdiff views */
-div.diff.header,
-div.diff.extended_header {
-       white-space: normal;
-}
-
-div.diff.header {
-       font-weight: bold;
-
-       background-color: #edece6;
-
-       margin-top: 4px;
-       padding: 4px 0px 2px 0px;
-       border: solid #d9d8d1;
-       border-width: 1px 0px 1px 0px;
-}
-
-div.diff.header a.path {
-       text-decoration: underline;
-}
-
-div.diff.extended_header,
-div.diff.extended_header a.path,
-div.diff.extended_header a.hash {
-       color: #777777;
-}
-
-div.diff.extended_header .info {
-       color: #b0b0b0;
-}
-
-div.diff.extended_header {
-       background-color: #f6f5ee;
-       padding: 2px 0px 2px 0px;
-}
-
-div.diff a.list,
-div.diff a.path,
-div.diff a.hash {
-       text-decoration: none;
-}
-
-div.diff a.list:hover,
-div.diff a.path:hover,
-div.diff a.hash:hover {
-       text-decoration: underline;
-}
-
-div.diff.to_file a.path,
-div.diff.to_file {
-       color: #007000;
-}
-
-div.diff.add {
-       color: #008800;
-}
-
-div.diff.from_file a.path,
-div.diff.from_file {
-       color: #aa0000;
-}
-
-div.diff.rem {
-       color: #cc0000;
-}
-
-div.diff.chunk_header a,
-div.diff.chunk_header {
-       color: #990099;
-}
-
-div.diff.chunk_header {
-       border: dotted #ffe0ff;
-       border-width: 1px 0px 0px 0px;
-       margin-top: 2px;
-}
-
-div.diff.chunk_header span.chunk_info {
-       background-color: #ffeeff;
-}
-
-div.diff.chunk_header span.section {
-       color: #aa22aa;
-}
-
-div.diff.incomplete {
-       color: #cccccc;
-}
-
-div.diff.nodifferences {
-       font-weight: bold;
-       color: #600000;
-}
-
-div.index_include {
-       border: solid #d9d8d1;
-       border-width: 0px 0px 1px;
-       padding: 12px 8px;
-}
-
-div.search {
-       font-size: 100%;
-       font-weight: normal;
-       margin: 4px 8px;
-       float: right;
-       top: 56px;
-       right: 12px
-}
-
-p.projsearch {
-       text-align: center;
-}
-
-td.linenr {
-       text-align: right;
-}
-
-a.linenr {
-       color: #999999;
-       text-decoration: none
-}
-
-a.rss_logo {
-       float: right;
-       padding: 3px 0px;
-       width: 35px;
-       line-height: 10px;
-       border: 1px solid;
-       border-color: #fcc7a5 #7d3302 #3e1a01 #ff954e;
-       color: #ffffff;
-       background-color: #ff6600;
-       font-weight: bold;
-       font-family: sans-serif;
-       font-size: 70%;
-       text-align: center;
-       text-decoration: none;
-}
-
-a.rss_logo:hover {
-       background-color: #ee5500;
-}
-
-a.rss_logo.generic {
-       background-color: #ff8800;
-}
-
-a.rss_logo.generic:hover {
-       background-color: #ee7700;
-}
-
-span.refs span {
-       padding: 0px 4px;
-       font-size: 70%;
-       font-weight: normal;
-       border: 1px solid;
-       background-color: #ffaaff;
-       border-color: #ffccff #ff00ee #ff00ee #ffccff;
-}
-
-span.refs span a {
-       text-decoration: none;
-       color: inherit;
-}
-
-span.refs span a:hover {
-       text-decoration: underline;
-}
-
-span.refs span.indirect {
-       font-style: italic;
-}
-
-span.refs span.ref {
-       background-color: #aaaaff;
-       border-color: #ccccff #0033cc #0033cc #ccccff;
-}
-
-span.refs span.tag {
-       background-color: #ffffaa;
-       border-color: #ffffcc #ffee00 #ffee00 #ffffcc;
-}
-
-span.refs span.head {
-       background-color: #aaffaa;
-       border-color: #ccffcc #00cc33 #00cc33 #ccffcc;
-}
-
-span.atnight {
-       color: #cc0000;
-}
-
-span.match {
-       color: #e00000;
-}
-
-div.binary {
-       font-style: italic;
-}
diff --git a/gitweb/gitweb.js b/gitweb/gitweb.js
deleted file mode 100644 (file)
index 9c66928..0000000
+++ /dev/null
@@ -1,875 +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])$/;
-
-/**
- * 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
- *
- * @globals tzRe
- */
-function formatDateISOLocal(epoch, timezoneInfo) {
-       var match = tzRe.exec(timezoneInfo);
-       // date corrected by timezone
-       var localDate = new Date(1000 * (epoch +
-               (parseInt(match[1],10)*3600 + parseInt(match[2],10)*60)));
-       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
index 934aacb61494a666ed0bdf9a9f1ef77dc6147c23..9e5e0bf3ee6a86df6329da145f7f597e7abf491f 100755 (executable)
@@ -28,34 +28,42 @@ BEGIN {
        CGI->compile() if $ENV{'MOD_PERL'};
 }
 
-our $cgi = new CGI;
 our $version = "++GIT_VERSION++";
-our $my_url = $cgi->url();
-our $my_uri = $cgi->url(-absolute => 1);
 
-# Base URL for relative URLs in gitweb ($logo, $favicon, ...),
-# needed and used only for URLs with nonempty PATH_INFO
-our $base_url = $my_url;
+our ($my_url, $my_uri, $base_url, $path_info, $home_link);
+sub evaluate_uri {
+       our $cgi;
 
-# When the script is used as DirectoryIndex, the URL does not contain the name
-# of the script file itself, and $cgi->url() fails to strip PATH_INFO, so we
-# have to do it ourselves. We make $path_info global because it's also used
-# later on.
-#
-# Another issue with the script being the DirectoryIndex is that the resulting
-# $my_url data is not the full script URL: this is good, because we want
-# generated links to keep implying the script name if it wasn't explicitly
-# indicated in the URL we're handling, but it means that $my_url cannot be used
-# as base URL.
-# Therefore, if we needed to strip PATH_INFO, then we know that we have
-# to build the base URL ourselves:
-our $path_info = $ENV{"PATH_INFO"};
-if ($path_info) {
-       if ($my_url =~ s,\Q$path_info\E$,, &&
-           $my_uri =~ s,\Q$path_info\E$,, &&
-           defined $ENV{'SCRIPT_NAME'}) {
-               $base_url = $cgi->url(-base => 1) . $ENV{'SCRIPT_NAME'};
+       our $my_url = $cgi->url();
+       our $my_uri = $cgi->url(-absolute => 1);
+
+       # Base URL for relative URLs in gitweb ($logo, $favicon, ...),
+       # needed and used only for URLs with nonempty PATH_INFO
+       our $base_url = $my_url;
+
+       # When the script is used as DirectoryIndex, the URL does not contain the name
+       # of the script file itself, and $cgi->url() fails to strip PATH_INFO, so we
+       # have to do it ourselves. We make $path_info global because it's also used
+       # later on.
+       #
+       # Another issue with the script being the DirectoryIndex is that the resulting
+       # $my_url data is not the full script URL: this is good, because we want
+       # generated links to keep implying the script name if it wasn't explicitly
+       # indicated in the URL we're handling, but it means that $my_url cannot be used
+       # as base URL.
+       # Therefore, if we needed to strip PATH_INFO, then we know that we have
+       # to build the base URL ourselves:
+       our $path_info = $ENV{"PATH_INFO"};
+       if ($path_info) {
+               if ($my_url =~ s,\Q$path_info\E$,, &&
+                   $my_uri =~ s,\Q$path_info\E$,, &&
+                   defined $ENV{'SCRIPT_NAME'}) {
+                       $base_url = $cgi->url(-base => 1) . $ENV{'SCRIPT_NAME'};
+               }
        }
+
+       # target of the home link on top of all pages
+       our $home_link = $my_uri || "/";
 }
 
 # core git executable to use
@@ -70,9 +78,6 @@ our $projectroot = "++GITWEB_PROJECTROOT++";
 # the number is relative to the projectroot
 our $project_maxdepth = "++GITWEB_PROJECT_MAXDEPTH++";
 
-# target of the home link on top of all pages
-our $home_link = $my_uri || "/";
-
 # string of the home link on top of all pages
 our $home_link_str = "++GITWEB_HOME_LINK_STR++";
 
@@ -445,6 +450,19 @@ our %feature = (
        'javascript-actions' => {
                'override' => 0,
                'default' => [0]},
+
+       # 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,
+       # and therefore is disabled by default.
+
+       # To enable system wide have in $GITWEB_CONFIG
+       # $feature{'highlight'}{'default'} = [1];
+
+       'highlight' => {
+               'sub' => sub { feature_bool('highlight', @_) },
+               'override' => 0,
+               'default' => [0]},
 );
 
 sub gitweb_get_feature {
@@ -553,15 +571,18 @@ sub filter_snapshot_fmts {
                !$known_snapshot_formats{$_}{'disabled'}} @fmts;
 }
 
-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 $@;
+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 $@;
+       }
 }
 
 # Get loadavg of system, to compare against $maxload.
@@ -587,13 +608,16 @@ sub get_loadavg {
 }
 
 # version of the core git binary
-our $git_version = qx("$GIT" --version) =~ m/git version (.*)$/ ? $1 : "unknown";
-$number_of_git_cmds++;
-
-$projects_list ||= $projectroot;
+our $git_version;
+sub evaluate_git_version {
+       our $git_version = qx("$GIT" --version) =~ m/git version (.*)$/ ? $1 : "unknown";
+       $number_of_git_cmds++;
+}
 
-if (defined $maxload && get_loadavg() > $maxload) {
-       die_error(503, "The load average on the server is too high");
+sub check_loadavg {
+       if (defined $maxload && get_loadavg() > $maxload) {
+               die_error(503, "The load average on the server is too high");
+       }
 }
 
 # ======================================================================
@@ -680,11 +704,15 @@ our %allowed_options = (
 # should be single values, but opt can be an array. We should probably
 # build an array of parameters that can be multi-valued, but since for the time
 # being it's only this one, we just single it out
-while (my ($name, $symbol) = each %cgi_param_mapping) {
-       if ($symbol eq 'opt') {
-               $input_params{$name} = [ $cgi->param($symbol) ];
-       } else {
-               $input_params{$name} = $cgi->param($symbol);
+sub evaluate_query_params {
+       our $cgi;
+
+       while (my ($name, $symbol) = each %cgi_param_mapping) {
+               if ($symbol eq 'opt') {
+                       $input_params{$name} = [ $cgi->param($symbol) ];
+               } else {
+                       $input_params{$name} = $cgi->param($symbol);
+               }
        }
 }
 
@@ -831,125 +859,134 @@ sub evaluate_path_info {
                }
        }
 }
-evaluate_path_info();
 
-our $action = $input_params{'action'};
-if (defined $action) {
-       if (!validate_action($action)) {
-               die_error(400, "Invalid action parameter");
+our ($action, $project, $file_name, $file_parent, $hash, $hash_parent, $hash_base,
+     $hash_parent_base, @extra_options, $page, $searchtype, $search_use_regexp,
+     $searchtext, $search_regexp);
+sub evaluate_and_validate_params {
+       our $action = $input_params{'action'};
+       if (defined $action) {
+               if (!validate_action($action)) {
+                       die_error(400, "Invalid action parameter");
+               }
        }
-}
 
-# parameters which are pathnames
-our $project = $input_params{'project'};
-if (defined $project) {
-       if (!validate_project($project)) {
-               undef $project;
-               die_error(404, "No such project");
+       # parameters which are pathnames
+       our $project = $input_params{'project'};
+       if (defined $project) {
+               if (!validate_project($project)) {
+                       undef $project;
+                       die_error(404, "No such project");
+               }
        }
-}
 
-our $file_name = $input_params{'file_name'};
-if (defined $file_name) {
-       if (!validate_pathname($file_name)) {
-               die_error(400, "Invalid file parameter");
+       our $file_name = $input_params{'file_name'};
+       if (defined $file_name) {
+               if (!validate_pathname($file_name)) {
+                       die_error(400, "Invalid file parameter");
+               }
        }
-}
 
-our $file_parent = $input_params{'file_parent'};
-if (defined $file_parent) {
-       if (!validate_pathname($file_parent)) {
-               die_error(400, "Invalid file parent parameter");
+       our $file_parent = $input_params{'file_parent'};
+       if (defined $file_parent) {
+               if (!validate_pathname($file_parent)) {
+                       die_error(400, "Invalid file parent parameter");
+               }
        }
-}
 
-# parameters which are refnames
-our $hash = $input_params{'hash'};
-if (defined $hash) {
-       if (!validate_refname($hash)) {
-               die_error(400, "Invalid hash parameter");
+       # parameters which are refnames
+       our $hash = $input_params{'hash'};
+       if (defined $hash) {
+               if (!validate_refname($hash)) {
+                       die_error(400, "Invalid hash parameter");
+               }
        }
-}
 
-our $hash_parent = $input_params{'hash_parent'};
-if (defined $hash_parent) {
-       if (!validate_refname($hash_parent)) {
-               die_error(400, "Invalid hash parent parameter");
+       our $hash_parent = $input_params{'hash_parent'};
+       if (defined $hash_parent) {
+               if (!validate_refname($hash_parent)) {
+                       die_error(400, "Invalid hash parent parameter");
+               }
        }
-}
 
-our $hash_base = $input_params{'hash_base'};
-if (defined $hash_base) {
-       if (!validate_refname($hash_base)) {
-               die_error(400, "Invalid hash base parameter");
+       our $hash_base = $input_params{'hash_base'};
+       if (defined $hash_base) {
+               if (!validate_refname($hash_base)) {
+                       die_error(400, "Invalid hash base parameter");
+               }
        }
-}
 
-our @extra_options = @{$input_params{'extra_options'}};
-# @extra_options is always defined, since it can only be (currently) set from
-# CGI, and $cgi->param() returns the empty array in array context if the param
-# is not set
-foreach my $opt (@extra_options) {
-       if (not exists $allowed_options{$opt}) {
-               die_error(400, "Invalid option parameter");
-       }
-       if (not grep(/^$action$/, @{$allowed_options{$opt}})) {
-               die_error(400, "Invalid option parameter for this action");
+       our @extra_options = @{$input_params{'extra_options'}};
+       # @extra_options is always defined, since it can only be (currently) set from
+       # CGI, and $cgi->param() returns the empty array in array context if the param
+       # is not set
+       foreach my $opt (@extra_options) {
+               if (not exists $allowed_options{$opt}) {
+                       die_error(400, "Invalid option parameter");
+               }
+               if (not grep(/^$action$/, @{$allowed_options{$opt}})) {
+                       die_error(400, "Invalid option parameter for this action");
+               }
        }
-}
 
-our $hash_parent_base = $input_params{'hash_parent_base'};
-if (defined $hash_parent_base) {
-       if (!validate_refname($hash_parent_base)) {
-               die_error(400, "Invalid hash parent base parameter");
+       our $hash_parent_base = $input_params{'hash_parent_base'};
+       if (defined $hash_parent_base) {
+               if (!validate_refname($hash_parent_base)) {
+                       die_error(400, "Invalid hash parent base parameter");
+               }
        }
-}
 
-# other parameters
-our $page = $input_params{'page'};
-if (defined $page) {
-       if ($page =~ m/[^0-9]/) {
-               die_error(400, "Invalid page parameter");
+       # other parameters
+       our $page = $input_params{'page'};
+       if (defined $page) {
+               if ($page =~ m/[^0-9]/) {
+                       die_error(400, "Invalid page parameter");
+               }
        }
-}
 
-our $searchtype = $input_params{'searchtype'};
-if (defined $searchtype) {
-       if ($searchtype =~ m/[^a-z]/) {
-               die_error(400, "Invalid searchtype parameter");
+       our $searchtype = $input_params{'searchtype'};
+       if (defined $searchtype) {
+               if ($searchtype =~ m/[^a-z]/) {
+                       die_error(400, "Invalid searchtype parameter");
+               }
        }
-}
 
-our $search_use_regexp = $input_params{'search_use_regexp'};
+       our $search_use_regexp = $input_params{'search_use_regexp'};
 
-our $searchtext = $input_params{'searchtext'};
-our $search_regexp;
-if (defined $searchtext) {
-       if (length($searchtext) < 2) {
-               die_error(403, "At least two characters are required for search parameter");
+       our $searchtext = $input_params{'searchtext'};
+       our $search_regexp;
+       if (defined $searchtext) {
+               if (length($searchtext) < 2) {
+                       die_error(403, "At least two characters are required for search parameter");
+               }
+               $search_regexp = $search_use_regexp ? $searchtext : quotemeta $searchtext;
        }
-       $search_regexp = $search_use_regexp ? $searchtext : quotemeta $searchtext;
 }
 
 # path to the current git repository
 our $git_dir;
-$git_dir = "$projectroot/$project" if $project;
+sub evaluate_git_dir {
+       our $git_dir = "$projectroot/$project" if $project;
+}
 
-# list of supported snapshot formats
-our @snapshot_fmts = gitweb_get_feature('snapshot');
-@snapshot_fmts = filter_snapshot_fmts(@snapshot_fmts);
+our (@snapshot_fmts, $git_avatar);
+sub configure_gitweb_features {
+       # list of supported snapshot formats
+       our @snapshot_fmts = gitweb_get_feature('snapshot');
+       @snapshot_fmts = filter_snapshot_fmts(@snapshot_fmts);
 
-# check that the avatar feature is set to a known provider name,
-# and for each provider check if the dependencies are satisfied.
-# if the provider name is invalid or the dependencies are not met,
-# reset $git_avatar to the empty string.
-our ($git_avatar) = gitweb_get_feature('avatar');
-if ($git_avatar eq 'gravatar') {
-       $git_avatar = '' unless (eval { require Digest::MD5; 1; });
-} elsif ($git_avatar eq 'picon') {
-       # no dependencies
-} else {
-       $git_avatar = '';
+       # check that the avatar feature is set to a known provider name,
+       # and for each provider check if the dependencies are satisfied.
+       # if the provider name is invalid or the dependencies are not met,
+       # reset $git_avatar to the empty string.
+       our ($git_avatar) = gitweb_get_feature('avatar');
+       if ($git_avatar eq 'gravatar') {
+               $git_avatar = '' unless (eval { require Digest::MD5; 1; });
+       } elsif ($git_avatar eq 'picon') {
+               # no dependencies
+       } else {
+               $git_avatar = '';
+       }
 }
 
 # custom error handler: 'die <message>' is Internal Server Error
@@ -968,27 +1005,109 @@ sub handle_errors_html {
 set_message(\&handle_errors_html);
 
 # dispatch
-if (!defined $action) {
-       if (defined $hash) {
-               $action = git_get_type($hash);
-       } elsif (defined $hash_base && defined $file_name) {
-               $action = git_get_type("$hash_base:$file_name");
-       } elsif (defined $project) {
-               $action = 'summary';
-       } else {
-               $action = 'project_list';
+sub dispatch {
+       if (!defined $action) {
+               if (defined $hash) {
+                       $action = git_get_type($hash);
+               } elsif (defined $hash_base && defined $file_name) {
+                       $action = git_get_type("$hash_base:$file_name");
+               } elsif (defined $project) {
+                       $action = 'summary';
+               } else {
+                       $action = 'project_list';
+               }
+       }
+       if (!defined($actions{$action})) {
+               die_error(400, "Unknown action");
+       }
+       if ($action !~ m/^(?:opml|project_list|project_index)$/ &&
+           !$project) {
+               die_error(400, "Project needed");
        }
+       $actions{$action}->();
 }
-if (!defined($actions{$action})) {
-       die_error(400, "Unknown action");
+
+sub run_request {
+       our $t0 = [Time::HiRes::gettimeofday()]
+               if defined $t0;
+
+       evaluate_uri();
+       evaluate_gitweb_config();
+       evaluate_git_version();
+       check_loadavg();
+
+       # $projectroot and $projects_list might be set in gitweb config file
+       $projects_list ||= $projectroot;
+
+       evaluate_query_params();
+       evaluate_path_info();
+       evaluate_and_validate_params();
+       evaluate_git_dir();
+
+       configure_gitweb_features();
+
+       dispatch();
+}
+
+our $is_last_request = sub { 1 };
+our ($pre_dispatch_hook, $post_dispatch_hook, $pre_listen_hook);
+our $CGI = 'CGI';
+our $cgi;
+sub configure_as_fcgi {
+       require CGI::Fast;
+       our $CGI = 'CGI::Fast';
+
+       my $request_number = 0;
+       # let each child service 100 requests
+       our $is_last_request = sub { ++$request_number > 100 };
+}
+sub evaluate_argv {
+       my $script_name = $ENV{'SCRIPT_NAME'} || $ENV{'SCRIPT_FILENAME'} || __FILE__;
+       configure_as_fcgi()
+               if $script_name =~ /\.fcgi$/;
+
+       return unless (@ARGV);
+
+       require Getopt::Long;
+       Getopt::Long::GetOptions(
+               'fastcgi|fcgi|f' => \&configure_as_fcgi,
+               'nproc|n=i' => sub {
+                       my ($arg, $val) = @_;
+                       return unless eval { require FCGI::ProcManager; 1; };
+                       my $proc_manager = FCGI::ProcManager->new({
+                               n_processes => $val,
+                       });
+                       our $pre_listen_hook    = sub { $proc_manager->pm_manage()        };
+                       our $pre_dispatch_hook  = sub { $proc_manager->pm_pre_dispatch()  };
+                       our $post_dispatch_hook = sub { $proc_manager->pm_post_dispatch() };
+               },
+       );
 }
-if ($action !~ m/^(?:opml|project_list|project_index)$/ &&
-    !$project) {
-       die_error(400, "Project needed");
+
+sub run {
+       evaluate_argv();
+
+       $pre_listen_hook->()
+               if $pre_listen_hook;
+
+ REQUEST:
+       while ($cgi = $CGI->new()) {
+               $pre_dispatch_hook->()
+                       if $pre_dispatch_hook;
+
+               run_request();
+
+               $pre_dispatch_hook->()
+                       if $post_dispatch_hook;
+
+               last REQUEST if ($is_last_request->());
+       }
+
+ DONE_GITWEB:
+       1;
 }
-$actions{$action}->();
-DONE_GITWEB:
-1;
+
+run();
 
 ## ======================================================================
 ## action links
@@ -3179,6 +3298,61 @@ sub blob_contenttype {
        return $type;
 }
 
+# guess file syntax for syntax highlighting; return undef if no highlighting
+# the name of syntax can (in the future) depend on syntax highlighter used
+sub guess_file_syntax {
+       my ($highlight, $mimetype, $file_name) = @_;
+       return undef unless ($highlight && defined $file_name);
+
+       # configuration for 'highlight' (http://www.andre-simon.de/)
+       # match by basename
+       my %highlight_basename = (
+               #'Program' => 'py',
+               #'Library' => 'py',
+               'SConstruct' => 'py', # SCons equivalent of Makefile
+               'Makefile' => 'make',
+       );
+       # match by extension
+       my %highlight_ext = (
+               # main extensions, defining name of syntax;
+               # see files in /usr/share/highlight/langDefs/ directory
+               map { $_ => $_ }
+                       qw(py c cpp rb java css php sh pl js tex bib xml awk bat ini spec tcl),
+               # alternate extensions, see /etc/highlight/filetypes.conf
+               'h' => 'c',
+               map { $_ => 'cpp' } qw(cxx c++ cc),
+               map { $_ => 'php' } qw(php3 php4),
+               map { $_ => 'pl'  } qw(perl pm), # perhaps also 'cgi'
+               'mak' => 'make',
+               map { $_ => 'xml' } qw(xhtml html htm),
+       );
+
+       my $basename = basename($file_name, '.in');
+       return $highlight_basename{$basename}
+               if exists $highlight_basename{$basename};
+
+       $basename =~ /\.([^.]*)$/;
+       my $ext = $1 or return undef;
+       return $highlight_ext{$ext}
+               if exists $highlight_ext{$ext};
+
+       return undef;
+}
+
+# run highlighter and return FD of its output,
+# or return original FD if no highlighting
+sub run_highlighter {
+       my ($fd, $highlight, $syntax) = @_;
+       return $fd unless ($highlight && defined $syntax);
+
+       close $fd
+               or die_error(404, "Reading blob failed");
+       open $fd, quote_command(git_cmd(), "cat-file", "blob", $hash)." | ".
+                 "highlight --xhtml --fragment --syntax $syntax |"
+               or die_error(500, "Couldn't open file or run syntax highlighter");
+       return $fd;
+}
+
 ## ======================================================================
 ## functions printing HTML: header, footer, error page
 
@@ -3220,7 +3394,7 @@ sub git_header_html {
        }
        print $cgi->header(-type=>$content_type, -charset => 'utf-8',
                           -status=> $status, -expires => $expires)
-               unless ($opts{'-no_http_headers'});
+               unless ($opts{'-no_http_header'});
        my $mod_perl_version = $ENV{'MOD_PERL'} ? " $ENV{'MOD_PERL'}" : '';
        print <<EOF;
 <?xml version="1.0" encoding="utf-8"?>
@@ -5380,6 +5554,7 @@ sub git_blob {
        open my $fd, "-|", git_cmd(), "cat-file", "blob", $hash
                or die_error(500, "Couldn't cat $file_name, $hash");
        my $mimetype = blob_mimetype($fd, $file_name);
+       # use 'blob_plain' (aka 'raw') view for files that cannot be displayed
        if ($mimetype !~ m!^(?:text/|image/(?:gif|png|jpeg)$)! && -B $fd) {
                close $fd;
                return git_blob_plain($mimetype);
@@ -5387,6 +5562,11 @@ sub git_blob {
        # we can have blame only for text/* mimetype
        $have_blame &&= ($mimetype =~ m!^text/!);
 
+       my $highlight = gitweb_check_feature('highlight');
+       my $syntax = guess_file_syntax($highlight, $mimetype, $file_name);
+       $fd = run_highlighter($fd, $highlight, $syntax)
+               if $syntax;
+
        git_header_html(undef, $expires);
        my $formats_nav = '';
        if (defined $hash_base && (my %co = parse_commit($hash_base))) {
@@ -5436,9 +5616,8 @@ sub git_blob {
                        chomp $line;
                        $nr++;
                        $line = untabify($line);
-                       printf "<div class=\"pre\"><a id=\"l%i\" href=\"" . href(-replay => 1)
-                               . "#l%i\" class=\"linenr\">%4i</a> %s</div>\n",
-                              $nr, $nr, $nr, esc_html($line, -nbsp=>1);
+                       printf qq!<div class="pre"><a id="l%i" href="%s#l%i" class="linenr">%4i</a> %s</div>\n!,
+                              $nr, href(-replay => 1), $nr, $nr, $syntax ? $line : esc_html($line, -nbsp=>1);
                }
        }
        close $fd
diff --git a/gitweb/static/git-favicon.png b/gitweb/static/git-favicon.png
new file mode 100644 (file)
index 0000000..aae35a7
Binary files /dev/null and b/gitweb/static/git-favicon.png differ
diff --git a/gitweb/static/git-logo.png b/gitweb/static/git-logo.png
new file mode 100644 (file)
index 0000000..f4ede2e
Binary files /dev/null and b/gitweb/static/git-logo.png differ
diff --git a/gitweb/static/gitweb.css b/gitweb/static/gitweb.css
new file mode 100644 (file)
index 0000000..4132aab
--- /dev/null
@@ -0,0 +1,592 @@
+body {
+       font-family: sans-serif;
+       font-size: small;
+       border: solid #d9d8d1;
+       border-width: 1px;
+       margin: 10px;
+       background-color: #ffffff;
+       color: #000000;
+}
+
+a {
+       color: #0000cc;
+}
+
+a:hover, a:visited, a:active {
+       color: #880000;
+}
+
+span.cntrl {
+       border: dashed #aaaaaa;
+       border-width: 1px;
+       padding: 0px 2px 0px 2px;
+       margin:  0px 2px 0px 2px;
+}
+
+img.logo {
+       float: right;
+       border-width: 0px;
+}
+
+img.avatar {
+       vertical-align: middle;
+}
+
+a.list img.avatar {
+       border-style: none;
+}
+
+div.page_header {
+       height: 25px;
+       padding: 8px;
+       font-size: 150%;
+       font-weight: bold;
+       background-color: #d9d8d1;
+}
+
+div.page_header a:visited, a.header {
+       color: #0000cc;
+}
+
+div.page_header a:hover {
+       color: #880000;
+}
+
+div.page_nav {
+       padding: 8px;
+}
+
+div.page_nav a:visited {
+       color: #0000cc;
+}
+
+div.page_path {
+       padding: 8px;
+       font-weight: bold;
+       border: solid #d9d8d1;
+       border-width: 0px 0px 1px;
+}
+
+div.page_footer {
+       height: 17px;
+       padding: 4px 8px;
+       background-color: #d9d8d1;
+}
+
+div.page_footer_text {
+       float: left;
+       color: #555555;
+       font-style: italic;
+}
+
+div#generating_info {
+       margin: 4px;
+       font-size: smaller;
+       text-align: center;
+       color: #505050;
+}
+
+div.page_body {
+       padding: 8px;
+       font-family: monospace;
+}
+
+div.title, a.title {
+       display: block;
+       padding: 6px 8px;
+       font-weight: bold;
+       background-color: #edece6;
+       text-decoration: none;
+       color: #000000;
+}
+
+div.readme {
+       padding: 8px;
+}
+
+a.title:hover {
+       background-color: #d9d8d1;
+}
+
+div.title_text {
+       padding: 6px 0px;
+       border: solid #d9d8d1;
+       border-width: 0px 0px 1px;
+       font-family: monospace;
+}
+
+div.log_body {
+       padding: 8px 8px 8px 150px;
+}
+
+span.age {
+       position: relative;
+       float: left;
+       width: 142px;
+       font-style: italic;
+}
+
+span.signoff {
+       color: #888888;
+}
+
+div.log_link {
+       padding: 0px 8px;
+       font-size: 70%;
+       font-family: sans-serif;
+       font-style: normal;
+       position: relative;
+       float: left;
+       width: 136px;
+}
+
+div.list_head {
+       padding: 6px 8px 4px;
+       border: solid #d9d8d1;
+       border-width: 1px 0px 0px;
+       font-style: italic;
+}
+
+.author_date, .author {
+       font-style: italic;
+}
+
+div.author_date {
+       padding: 8px;
+       border: solid #d9d8d1;
+       border-width: 0px 0px 1px 0px;
+}
+
+a.list {
+       text-decoration: none;
+       color: #000000;
+}
+
+a.subject, a.name {
+       font-weight: bold;
+}
+
+table.tags a.subject {
+       font-weight: normal;
+}
+
+a.list:hover {
+       text-decoration: underline;
+       color: #880000;
+}
+
+a.text {
+       text-decoration: none;
+       color: #0000cc;
+}
+
+a.text:visited {
+       text-decoration: none;
+       color: #880000;
+}
+
+a.text:hover {
+       text-decoration: underline;
+       color: #880000;
+}
+
+table {
+       padding: 8px 4px;
+       border-spacing: 0;
+}
+
+table.diff_tree {
+       font-family: monospace;
+}
+
+table.combined.diff_tree th {
+       text-align: center;
+}
+
+table.combined.diff_tree td {
+       padding-right: 24px;
+}
+
+table.combined.diff_tree th.link,
+table.combined.diff_tree td.link {
+       padding: 0px 2px;
+}
+
+table.combined.diff_tree td.nochange a {
+       color: #6666ff;
+}
+
+table.combined.diff_tree td.nochange a:hover,
+table.combined.diff_tree td.nochange a:visited {
+       color: #d06666;
+}
+
+table.blame {
+       border-collapse: collapse;
+}
+
+table.blame td {
+       padding: 0px 5px;
+       font-size: 100%;
+       vertical-align: top;
+}
+
+th {
+       padding: 2px 5px;
+       font-size: 100%;
+       text-align: left;
+}
+
+/* do not change row style on hover for 'blame' view */
+tr.light,
+table.blame .light:hover {
+       background-color: #ffffff;
+}
+
+tr.dark,
+table.blame .dark:hover {
+       background-color: #f6f6f0;
+}
+
+/* currently both use the same, but it can change */
+tr.light:hover,
+tr.dark:hover {
+       background-color: #edece6;
+}
+
+/* boundary commits in 'blame' view */
+/* and commits without "previous" */
+tr.boundary td.sha1,
+tr.no-previous td.linenr {
+       font-weight: bold;
+}
+
+/* for 'blame_incremental', during processing */
+tr.color1 { background-color: #f6fff6; }
+tr.color2 { background-color: #f6f6ff; }
+tr.color3 { background-color: #fff6f6; }
+
+td {
+       padding: 2px 5px;
+       font-size: 100%;
+       vertical-align: top;
+}
+
+td.link, td.selflink {
+       padding: 2px 5px;
+       font-family: sans-serif;
+       font-size: 70%;
+}
+
+td.selflink {
+       padding-right: 0px;
+}
+
+td.sha1 {
+       font-family: monospace;
+}
+
+.error {
+       color: red;
+       background-color: yellow;
+}
+
+td.current_head {
+       text-decoration: underline;
+}
+
+table.diff_tree span.file_status.new {
+       color: #008000;
+}
+
+table.diff_tree span.file_status.deleted {
+       color: #c00000;
+}
+
+table.diff_tree span.file_status.moved,
+table.diff_tree span.file_status.mode_chnge {
+       color: #777777;
+}
+
+table.diff_tree span.file_status.copied {
+  color: #70a070;
+}
+
+/* noage: "No commits" */
+table.project_list td.noage {
+       color: #808080;
+       font-style: italic;
+}
+
+/* age2: 60*60*24*2 <= age */
+table.project_list td.age2, table.blame td.age2 {
+       font-style: italic;
+}
+
+/* age1: 60*60*2 <= age < 60*60*24*2 */
+table.project_list td.age1 {
+       color: #009900;
+       font-style: italic;
+}
+
+table.blame td.age1 {
+       color: #009900;
+       background: transparent;
+}
+
+/* age0: age < 60*60*2 */
+table.project_list td.age0 {
+       color: #009900;
+       font-style: italic;
+       font-weight: bold;
+}
+
+table.blame td.age0 {
+       color: #009900;
+       background: transparent;
+       font-weight: bold;
+}
+
+td.pre, div.pre, div.diff {
+       font-family: monospace;
+       font-size: 12px;
+       white-space: pre;
+}
+
+td.mode {
+       font-family: monospace;
+}
+
+/* progress of blame_interactive */
+div#progress_bar {
+       height: 2px;
+       margin-bottom: -2px;
+       background-color: #d8d9d0;
+}
+div#progress_info {
+       float: right;
+       text-align: right;
+}
+
+/* format of (optional) objects size in 'tree' view */
+td.size {
+       font-family: monospace;
+       text-align: right;
+}
+
+/* styling of diffs (patchsets): commitdiff and blobdiff views */
+div.diff.header,
+div.diff.extended_header {
+       white-space: normal;
+}
+
+div.diff.header {
+       font-weight: bold;
+
+       background-color: #edece6;
+
+       margin-top: 4px;
+       padding: 4px 0px 2px 0px;
+       border: solid #d9d8d1;
+       border-width: 1px 0px 1px 0px;
+}
+
+div.diff.header a.path {
+       text-decoration: underline;
+}
+
+div.diff.extended_header,
+div.diff.extended_header a.path,
+div.diff.extended_header a.hash {
+       color: #777777;
+}
+
+div.diff.extended_header .info {
+       color: #b0b0b0;
+}
+
+div.diff.extended_header {
+       background-color: #f6f5ee;
+       padding: 2px 0px 2px 0px;
+}
+
+div.diff a.list,
+div.diff a.path,
+div.diff a.hash {
+       text-decoration: none;
+}
+
+div.diff a.list:hover,
+div.diff a.path:hover,
+div.diff a.hash:hover {
+       text-decoration: underline;
+}
+
+div.diff.to_file a.path,
+div.diff.to_file {
+       color: #007000;
+}
+
+div.diff.add {
+       color: #008800;
+}
+
+div.diff.from_file a.path,
+div.diff.from_file {
+       color: #aa0000;
+}
+
+div.diff.rem {
+       color: #cc0000;
+}
+
+div.diff.chunk_header a,
+div.diff.chunk_header {
+       color: #990099;
+}
+
+div.diff.chunk_header {
+       border: dotted #ffe0ff;
+       border-width: 1px 0px 0px 0px;
+       margin-top: 2px;
+}
+
+div.diff.chunk_header span.chunk_info {
+       background-color: #ffeeff;
+}
+
+div.diff.chunk_header span.section {
+       color: #aa22aa;
+}
+
+div.diff.incomplete {
+       color: #cccccc;
+}
+
+div.diff.nodifferences {
+       font-weight: bold;
+       color: #600000;
+}
+
+div.index_include {
+       border: solid #d9d8d1;
+       border-width: 0px 0px 1px;
+       padding: 12px 8px;
+}
+
+div.search {
+       font-size: 100%;
+       font-weight: normal;
+       margin: 4px 8px;
+       float: right;
+       top: 56px;
+       right: 12px
+}
+
+p.projsearch {
+       text-align: center;
+}
+
+td.linenr {
+       text-align: right;
+}
+
+a.linenr {
+       color: #999999;
+       text-decoration: none
+}
+
+a.rss_logo {
+       float: right;
+       padding: 3px 0px;
+       width: 35px;
+       line-height: 10px;
+       border: 1px solid;
+       border-color: #fcc7a5 #7d3302 #3e1a01 #ff954e;
+       color: #ffffff;
+       background-color: #ff6600;
+       font-weight: bold;
+       font-family: sans-serif;
+       font-size: 70%;
+       text-align: center;
+       text-decoration: none;
+}
+
+a.rss_logo:hover {
+       background-color: #ee5500;
+}
+
+a.rss_logo.generic {
+       background-color: #ff8800;
+}
+
+a.rss_logo.generic:hover {
+       background-color: #ee7700;
+}
+
+span.refs span {
+       padding: 0px 4px;
+       font-size: 70%;
+       font-weight: normal;
+       border: 1px solid;
+       background-color: #ffaaff;
+       border-color: #ffccff #ff00ee #ff00ee #ffccff;
+}
+
+span.refs span a {
+       text-decoration: none;
+       color: inherit;
+}
+
+span.refs span a:hover {
+       text-decoration: underline;
+}
+
+span.refs span.indirect {
+       font-style: italic;
+}
+
+span.refs span.ref {
+       background-color: #aaaaff;
+       border-color: #ccccff #0033cc #0033cc #ccccff;
+}
+
+span.refs span.tag {
+       background-color: #ffffaa;
+       border-color: #ffffcc #ffee00 #ffee00 #ffffcc;
+}
+
+span.refs span.head {
+       background-color: #aaffaa;
+       border-color: #ccffcc #00cc33 #00cc33 #ccffcc;
+}
+
+span.atnight {
+       color: #cc0000;
+}
+
+span.match {
+       color: #e00000;
+}
+
+div.binary {
+       font-style: italic;
+}
+
+/* Style definition generated by highlight 2.4.5, http://www.andre-simon.de/ */
+
+/* Highlighting theme definition: */
+
+.num    { color:#2928ff; }
+.esc    { color:#ff00ff; }
+.str    { color:#ff0000; }
+.dstr   { color:#818100; }
+.slc    { color:#838183; font-style:italic; }
+.com    { color:#838183; font-style:italic; }
+.dir    { color:#008200; }
+.sym    { color:#000000; }
+.line   { color:#555555; }
+.kwa    { color:#000000; font-weight:bold; }
+.kwb    { color:#830000; }
+.kwc    { color:#000000; font-weight:bold; }
+.kwd    { color:#010181; }
diff --git a/gitweb/static/gitweb.js b/gitweb/static/gitweb.js
new file mode 100644 (file)
index 0000000..9c66928
--- /dev/null
@@ -0,0 +1,875 @@
+// 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])$/;
+
+/**
+ * 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
+ *
+ * @globals tzRe
+ */
+function formatDateISOLocal(epoch, timezoneInfo) {
+       var match = tzRe.exec(timezoneInfo);
+       // date corrected by timezone
+       var localDate = new Date(1000 * (epoch +
+               (parseInt(match[1],10)*3600 + parseInt(match[2],10)*60)));
+       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/graph.c b/graph.c
index e6bbcaa8c4655add3ecaca578e948355795e36ca..ac7c60540645761d3c99fb405a69cb2d06aaddc2 100644 (file)
--- a/graph.c
+++ b/graph.c
@@ -211,6 +211,18 @@ struct git_graph {
        unsigned short default_column_color;
 };
 
+static struct strbuf *diff_output_prefix_callback(struct diff_options *opt, void *data)
+{
+       struct git_graph *graph = data;
+       static struct strbuf msgbuf = STRBUF_INIT;
+
+       assert(graph);
+
+       strbuf_reset(&msgbuf);
+       graph_padding_line(graph, &msgbuf);
+       return &msgbuf;
+}
+
 struct git_graph *graph_init(struct rev_info *opt)
 {
        struct git_graph *graph = xmalloc(sizeof(struct git_graph));
@@ -244,6 +256,13 @@ struct git_graph *graph_init(struct rev_info *opt)
        graph->mapping = xmalloc(sizeof(int) * 2 * graph->column_capacity);
        graph->new_mapping = xmalloc(sizeof(int) * 2 * graph->column_capacity);
 
+       /*
+        * The diff output prefix callback, with this we can make
+        * all the diff output to align with the graph lines.
+        */
+       opt->diffopt.output_prefix = diff_output_prefix_callback;
+       opt->diffopt.output_prefix_data = graph;
+
        return graph;
 }
 
diff --git a/grep.c b/grep.c
index 543b1d53784c16020dea089f23c431c7f2608425..82fb349be6f6ef8211d4828101cd8e88bcbb304f 100644 (file)
--- a/grep.c
+++ b/grep.c
@@ -7,6 +7,7 @@ void append_header_grep_pattern(struct grep_opt *opt, enum grep_header_field fie
 {
        struct grep_pat *p = xcalloc(1, sizeof(*p));
        p->pattern = pat;
+       p->patternlen = strlen(pat);
        p->origin = "header";
        p->no = 0;
        p->token = GREP_PATTERN_HEAD;
@@ -18,9 +19,16 @@ void append_header_grep_pattern(struct grep_opt *opt, enum grep_header_field fie
 
 void append_grep_pattern(struct grep_opt *opt, const char *pat,
                         const char *origin, int no, enum grep_pat_token t)
+{
+       append_grep_pat(opt, pat, strlen(pat), origin, no, t);
+}
+
+void append_grep_pat(struct grep_opt *opt, const char *pat, size_t patlen,
+                    const char *origin, int no, enum grep_pat_token t)
 {
        struct grep_pat *p = xcalloc(1, sizeof(*p));
        p->pattern = pat;
+       p->patternlen = patlen;
        p->origin = origin;
        p->no = no;
        p->token = t;
@@ -44,8 +52,8 @@ struct grep_opt *grep_opt_dup(const struct grep_opt *opt)
                        append_header_grep_pattern(ret, pat->field,
                                                   pat->pattern);
                else
-                       append_grep_pattern(ret, pat->pattern, pat->origin,
-                                           pat->no, pat->token);
+                       append_grep_pat(ret, pat->pattern, pat->patternlen,
+                                       pat->origin, pat->no, pat->token);
        }
 
        return ret;
@@ -329,14 +337,21 @@ static void show_name(struct grep_opt *opt, const char *name)
        opt->output(opt, opt->null_following_name ? "\0" : "\n", 1);
 }
 
-
-static int fixmatch(const char *pattern, char *line, int ignore_case, regmatch_t *match)
+static int fixmatch(struct grep_pat *p, char *line, char *eol,
+                   regmatch_t *match)
 {
        char *hit;
-       if (ignore_case)
-               hit = strcasestr(line, pattern);
-       else
-               hit = strstr(line, pattern);
+
+       if (p->ignore_case) {
+               char *s = line;
+               do {
+                       hit = strcasestr(s, p->pattern);
+                       if (hit)
+                               break;
+                       s += strlen(s) + 1;
+               } while (s < eol);
+       } else
+               hit = memmem(line, eol - line, p->pattern, p->patternlen);
 
        if (!hit) {
                match->rm_so = match->rm_eo = -1;
@@ -344,11 +359,22 @@ static int fixmatch(const char *pattern, char *line, int ignore_case, regmatch_t
        }
        else {
                match->rm_so = hit - line;
-               match->rm_eo = match->rm_so + strlen(pattern);
+               match->rm_eo = match->rm_so + p->patternlen;
                return 0;
        }
 }
 
+static int regmatch(const regex_t *preg, char *line, char *eol,
+                   regmatch_t *match, int eflags)
+{
+#ifdef REG_STARTEND
+       match->rm_so = 0;
+       match->rm_eo = eol - line;
+       eflags |= REG_STARTEND;
+#endif
+       return regexec(preg, line, 1, match, eflags);
+}
+
 static int strip_timestamp(char *bol, char **eol_p)
 {
        char *eol = *eol_p;
@@ -399,9 +425,9 @@ static int match_one_pattern(struct grep_pat *p, char *bol, char *eol,
 
  again:
        if (p->fixed)
-               hit = !fixmatch(p->pattern, bol, p->ignore_case, pmatch);
+               hit = !fixmatch(p, bol, eol, pmatch);
        else
-               hit = !regexec(&p->regexp, bol, 1, pmatch, eflags);
+               hit = !regmatch(&p->regexp, bol, eol, pmatch, eflags);
 
        if (hit && p->word_regexp) {
                if ((pmatch[0].rm_so < 0) ||
@@ -726,16 +752,9 @@ static int look_ahead(struct grep_opt *opt,
                regmatch_t m;
 
                if (p->fixed)
-                       hit = !fixmatch(p->pattern, bol, p->ignore_case, &m);
-               else {
-#ifdef REG_STARTEND
-                       m.rm_so = 0;
-                       m.rm_eo = *left_p;
-                       hit = !regexec(&p->regexp, bol, 1, &m, REG_STARTEND);
-#else
-                       hit = !regexec(&p->regexp, bol, 1, &m, 0);
-#endif
-               }
+                       hit = !fixmatch(p, bol, bol + *left_p, &m);
+               else
+                       hit = !regmatch(&p->regexp, bol, bol + *left_p, &m, 0);
                if (!hit || m.rm_so < 0 || m.rm_eo < 0)
                        continue;
                if (earliest < 0 || m.rm_so < earliest)
@@ -800,17 +819,19 @@ static int grep_buffer_1(struct grep_opt *opt, const char *name,
                opt->show_hunk_mark = 1;
        opt->last_shown = 0;
 
-       if (buffer_is_binary(buf, size)) {
-               switch (opt->binary) {
-               case GREP_BINARY_DEFAULT:
+       switch (opt->binary) {
+       case GREP_BINARY_DEFAULT:
+               if (buffer_is_binary(buf, size))
                        binary_match_only = 1;
-                       break;
-               case GREP_BINARY_NOMATCH:
+               break;
+       case GREP_BINARY_NOMATCH:
+               if (buffer_is_binary(buf, size))
                        return 0; /* Assume unmatch */
-                       break;
-               default:
-                       break;
-               }
+               break;
+       case GREP_BINARY_TEXT:
+               break;
+       default:
+               die("bug: unknown binary handling mode");
        }
 
        memset(&xecfg, 0, sizeof(xecfg));
@@ -871,6 +892,12 @@ static int grep_buffer_1(struct grep_opt *opt, const char *name,
                        count++;
                        if (opt->status_only)
                                return 1;
+                       if (opt->name_only) {
+                               show_name(opt, name);
+                               return 1;
+                       }
+                       if (opt->count)
+                               goto next_line;
                        if (binary_match_only) {
                                opt->output(opt, "Binary file ", 12);
                                output_color(opt, name, strlen(name),
@@ -878,22 +905,14 @@ static int grep_buffer_1(struct grep_opt *opt, const char *name,
                                opt->output(opt, " matches\n", 9);
                                return 1;
                        }
-                       if (opt->name_only) {
-                               show_name(opt, name);
-                               return 1;
-                       }
                        /* Hit at this line.  If we haven't shown the
                         * pre-context lines, we would need to show them.
-                        * When asked to do "count", this still show
-                        * the context which is nonsense, but the user
-                        * deserves to get that ;-).
                         */
                        if (opt->pre_context)
                                show_pre_context(opt, name, buf, bol, lno);
                        else if (opt->funcname)
                                show_funcname_line(opt, name, buf, bol, lno);
-                       if (!opt->count)
-                               show_line(opt, bol, eol, name, lno, ':');
+                       show_line(opt, bol, eol, name, lno, ':');
                        last_hit = lno;
                }
                else if (last_hit &&
@@ -937,6 +956,7 @@ static int grep_buffer_1(struct grep_opt *opt, const char *name,
                output_sep(opt, ':');
                snprintf(buf, sizeof(buf), "%u\n", count);
                opt->output(opt, buf, strlen(buf));
+               return 1;
        }
        return !!last_hit;
 }
diff --git a/grep.h b/grep.h
index 89342e5b47f6d63dd546e738e4cbf023c447b382..efa8cff980af2b2c06dad080876637d16b5c4985 100644 (file)
--- a/grep.h
+++ b/grep.h
@@ -10,17 +10,17 @@ enum grep_pat_token {
        GREP_OPEN_PAREN,
        GREP_CLOSE_PAREN,
        GREP_NOT,
-       GREP_OR,
+       GREP_OR
 };
 
 enum grep_context {
        GREP_CONTEXT_HEAD,
-       GREP_CONTEXT_BODY,
+       GREP_CONTEXT_BODY
 };
 
 enum grep_header_field {
        GREP_HEADER_AUTHOR = 0,
-       GREP_HEADER_COMMITTER,
+       GREP_HEADER_COMMITTER
 };
 
 struct grep_pat {
@@ -29,6 +29,7 @@ struct grep_pat {
        int no;
        enum grep_pat_token token;
        const char *pattern;
+       size_t patternlen;
        enum grep_header_field field;
        regex_t regexp;
        unsigned fixed:1;
@@ -40,7 +41,7 @@ enum grep_expr_node {
        GREP_NODE_ATOM,
        GREP_NODE_NOT,
        GREP_NODE_AND,
-       GREP_NODE_OR,
+       GREP_NODE_OR
 };
 
 struct grep_expr {
@@ -104,6 +105,7 @@ struct grep_opt {
        void *output_priv;
 };
 
+extern void append_grep_pat(struct grep_opt *opt, const char *pat, size_t patlen, const char *origin, int no, enum grep_pat_token t);
 extern void append_grep_pattern(struct grep_opt *opt, const char *pat, const char *origin, int no, enum grep_pat_token t);
 extern void append_header_grep_pattern(struct grep_opt *, enum grep_header_field, const char *);
 extern void compile_grep_patterns(struct grep_opt *opt);
index d1e83d0906dbc9d677630cdf74615ec3f9dfc46d..44ce6bb32b0e98681f7c523537082065e8cb5fc1 100644 (file)
@@ -6,6 +6,7 @@
 #include "exec_cmd.h"
 #include "run-command.h"
 #include "string-list.h"
+#include "url.h"
 
 static const char content_type[] = "Content-Type";
 static const char content_length[] = "Content-Length";
@@ -25,60 +26,6 @@ static struct rpc_service rpc_service[] = {
        { "receive-pack", "receivepack", -1 },
 };
 
-static int decode_char(const char *q)
-{
-       int i;
-       unsigned char val = 0;
-       for (i = 0; i < 2; i++) {
-               unsigned char c = *q++;
-               val <<= 4;
-               if (c >= '0' && c <= '9')
-                       val += c - '0';
-               else if (c >= 'a' && c <= 'f')
-                       val += c - 'a' + 10;
-               else if (c >= 'A' && c <= 'F')
-                       val += c - 'A' + 10;
-               else
-                       return -1;
-       }
-       return val;
-}
-
-static char *decode_parameter(const char **query, int is_name)
-{
-       const char *q = *query;
-       struct strbuf out;
-
-       strbuf_init(&out, 16);
-       do {
-               unsigned char c = *q;
-
-               if (!c)
-                       break;
-               if (c == '&' || (is_name && c == '=')) {
-                       q++;
-                       break;
-               }
-
-               if (c == '%') {
-                       int val = decode_char(q + 1);
-                       if (0 <= val) {
-                               strbuf_addch(&out, val);
-                               q += 3;
-                               continue;
-                       }
-               }
-
-               if (c == '+')
-                       strbuf_addch(&out, ' ');
-               else
-                       strbuf_addch(&out, c);
-               q++;
-       } while (1);
-       *query = q;
-       return strbuf_detach(&out, NULL);
-}
-
 static struct string_list *get_parameters(void)
 {
        if (!query_params) {
@@ -86,8 +33,8 @@ static struct string_list *get_parameters(void)
 
                query_params = xcalloc(1, sizeof(*query_params));
                while (query && *query) {
-                       char *name = decode_parameter(&query, 1);
-                       char *value = decode_parameter(&query, 0);
+                       char *name = url_decode_parameter_name(&query);
+                       char *value = url_decode_parameter_value(&query);
                        struct string_list_item *i;
 
                        i = string_list_lookup(name, query_params);
@@ -541,14 +488,12 @@ static NORETURN void die_webcgi(const char *err, va_list params)
        static int dead;
 
        if (!dead) {
-               char buffer[1000];
                dead = 1;
-
-               vsnprintf(buffer, sizeof(buffer), err, params);
-               fprintf(stderr, "fatal: %s\n", buffer);
                http_status(500, "Internal Server Error");
                hdr_nocache();
                end_headers();
+
+               vreportf("fatal: ", err, params);
        }
        exit(0); /* we successfully reported a failure ;-) */
 }
index 415b1ab0a7f0a98e3a16f82c39bbcc9b04f85ac2..c9bcd116975635cabf30eceb7909abfc7f341948 100644 (file)
@@ -105,7 +105,7 @@ enum transfer_state {
        RUN_PUT,
        RUN_MOVE,
        ABORTED,
-       COMPLETE,
+       COMPLETE
 };
 
 struct transfer_request
index 8ca76d0507bdc1d95283e1f5fee3f88180cdce26..18bd6504beb99ab68f360c4fa93011efae42fdfb 100644 (file)
@@ -15,7 +15,7 @@ enum object_request_state {
        WAITING,
        ABORTED,
        ACTIVE,
-       COMPLETE,
+       COMPLETE
 };
 
 struct object_request
index 9d0097ca02960460ff3a104f1739982fee453987..1a577a0a094342315bd3f68e8d13ef7f57a22f50 100644 (file)
@@ -230,7 +230,7 @@ enum CAPABILITY {
        LITERALPLUS,
        NAMESPACE,
        STARTTLS,
-       AUTH_CRAM_MD5,
+       AUTH_CRAM_MD5
 };
 
 static const char *cap_list[] = {
index f9b3d854a921ab6fa3eed6f4dbf01af1a8657602..3764a1ab72354a3ce643c899c1e8cf5443d4e7cb 100644 (file)
@@ -139,17 +139,17 @@ static int ll_ext_merge(const struct ll_merge_driver *fn,
 {
        char temp[4][50];
        struct strbuf cmd = STRBUF_INIT;
-       struct strbuf_expand_dict_entry dict[] = {
-               { "O", temp[0] },
-               { "A", temp[1] },
-               { "B", temp[2] },
-               { "L", temp[3] },
-               { NULL }
-       };
+       struct strbuf_expand_dict_entry dict[5];
        const char *args[] = { NULL, NULL };
        int status, fd, i;
        struct stat st;
 
+       dict[0].placeholder = "O"; dict[0].value = temp[0];
+       dict[1].placeholder = "A"; dict[1].value = temp[1];
+       dict[2].placeholder = "B"; dict[2].value = temp[2];
+       dict[3].placeholder = "L"; dict[3].value = temp[3];
+       dict[4].placeholder = NULL; dict[4].value = NULL;
+
        if (fn->cmdline == NULL)
                die("custom merge driver %s lacks command line.", fn->name);
 
index d3ae969f608b50a469aa60b2e925558a6bb437e5..2e2be7c40f996949a69f597168b620976cf55b2f 100644 (file)
@@ -469,6 +469,12 @@ int log_tree_diff_flush(struct rev_info *opt)
                        int pch = DIFF_FORMAT_DIFFSTAT | DIFF_FORMAT_PATCH;
                        if ((pch & opt->diffopt.output_format) == pch)
                                printf("---");
+                       if (opt->diffopt.output_prefix) {
+                               struct strbuf *msg = NULL;
+                               msg = opt->diffopt.output_prefix(&opt->diffopt,
+                                       opt->diffopt.output_prefix_data);
+                               fwrite(msg->buf, msg->len, 1, stdout);
+                       }
                        putchar('\n');
                }
        }
index c336c93c01c0bad76d6189065f0e6630d0b7f5af..db4d0d50d32d8852d1cb5173125e4173c3badb49 100644 (file)
@@ -66,7 +66,7 @@ static int generate_common_file(mmfile_t *res, mmfile_t *f1, mmfile_t *f2)
        xdemitcb_t ecb;
 
        memset(&xpp, 0, sizeof(xpp));
-       xpp.flags = XDF_NEED_MINIMAL;
+       xpp.flags = 0;
        memset(&xecfg, 0, sizeof(xecfg));
        xecfg.ctxlen = 3;
        xecfg.flags = XDL_EMIT_COMMON;
index 0cc465ec5d13385dd8ecdbd64387cf937c82bce3..b831293b3865ae52584565039e4a893c3c6f45ff 100644 (file)
@@ -10,7 +10,7 @@ struct merge_options {
        enum {
                MERGE_RECURSIVE_NORMAL = 0,
                MERGE_RECURSIVE_OURS,
-               MERGE_RECURSIVE_THEIRS,
+               MERGE_RECURSIVE_THEIRS
        } recursive_variant;
        const char *subtree_shift;
        unsigned buffer_output : 1;
diff --git a/notes.c b/notes.c
index e425e198278bfb5c6a039dc88825568f1518e875..6ee04e79e903901db3594feae69ceca4374994e2 100644 (file)
--- a/notes.c
+++ b/notes.c
@@ -1083,7 +1083,7 @@ int write_notes_tree(struct notes_tree *t, unsigned char *result)
        return ret;
 }
 
-void prune_notes(struct notes_tree *t)
+void prune_notes(struct notes_tree *t, int flags)
 {
        struct note_delete_list *l = NULL;
 
@@ -1094,7 +1094,10 @@ void prune_notes(struct notes_tree *t)
        for_each_note(t, 0, prune_notes_helper, &l);
 
        while (l) {
-               remove_note(t, l->sha1);
+               if (flags & NOTES_PRUNE_VERBOSE)
+                       printf("%s\n", sha1_to_hex(l->sha1));
+               if (!(flags & NOTES_PRUNE_DRYRUN))
+                       remove_note(t, l->sha1);
                l = l->next;
        }
 }
diff --git a/notes.h b/notes.h
index 9f59277c516f7ac78bcc66f91a7d7013b2a77f65..cc2dff22a12f2f7e78d37fb316e70f0a1ebaa2cd 100644 (file)
--- a/notes.h
+++ b/notes.h
@@ -171,6 +171,9 @@ int for_each_note(struct notes_tree *t, int flags, each_note_fn fn,
  */
 int write_notes_tree(struct notes_tree *t, unsigned char *result);
 
+/* Flags controlling the operation of prune */
+#define NOTES_PRUNE_VERBOSE 1
+#define NOTES_PRUNE_DRYRUN 2
 /*
  * Remove all notes annotating non-existing objects from the given notes tree
  *
@@ -181,7 +184,7 @@ int write_notes_tree(struct notes_tree *t, unsigned char *result);
  * structure are not persistent until a subsequent call to write_notes_tree()
  * returns zero.
  */
-void prune_notes(struct notes_tree *t);
+void prune_notes(struct notes_tree *t, int flags);
 
 /*
  * Free (and de-initialize) the given notes_tree structure
index e16b4d201a998390672cdade90bacde64d9ad29c..7435cdbf1d94378cf1c3408ae17cb610165a00ba 100644 (file)
@@ -25,7 +25,7 @@ enum parse_opt_flags {
        PARSE_OPT_STOP_AT_NON_OPTION = 2,
        PARSE_OPT_KEEP_ARGV0 = 4,
        PARSE_OPT_KEEP_UNKNOWN = 8,
-       PARSE_OPT_NO_INTERNAL_HELP = 16,
+       PARSE_OPT_NO_INTERNAL_HELP = 16
 };
 
 enum parse_opt_option_flags {
@@ -161,7 +161,7 @@ extern NORETURN void usage_msg_opt(const char *msg,
 enum {
        PARSE_OPT_HELP = -1,
        PARSE_OPT_DONE,
-       PARSE_OPT_UNKNOWN,
+       PARSE_OPT_UNKNOWN
 };
 
 /*
index 74cda1b44e37f414d9d61326bbc5c548d56ba780..4b85373ba6797069eefa96a63141438bd5de8803 100644 (file)
--- a/pretty.c
+++ b/pretty.c
 #include "reflog-walk.h"
 
 static char *user_format;
+static struct cmt_fmt_map {
+       const char *name;
+       enum cmit_fmt format;
+       int is_tformat;
+       int is_alias;
+       const char *user_format;
+} *commit_formats;
+static size_t builtin_formats_len;
+static size_t commit_formats_len;
+static size_t commit_formats_alloc;
+static struct cmt_fmt_map *find_commit_format(const char *sought);
 
 static void save_user_format(struct rev_info *rev, const char *cp, int is_tformat)
 {
@@ -21,22 +32,118 @@ static void save_user_format(struct rev_info *rev, const char *cp, int is_tforma
        rev->commit_format = CMIT_FMT_USERFORMAT;
 }
 
-void get_commit_format(const char *arg, struct rev_info *rev)
+static int git_pretty_formats_config(const char *var, const char *value, void *cb)
 {
+       struct cmt_fmt_map *commit_format = NULL;
+       const char *name;
+       const char *fmt;
        int i;
-       static struct cmt_fmt_map {
-               const char *n;
-               size_t cmp_len;
-               enum cmit_fmt v;
-       } cmt_fmts[] = {
-               { "raw",        1,      CMIT_FMT_RAW },
-               { "medium",     1,      CMIT_FMT_MEDIUM },
-               { "short",      1,      CMIT_FMT_SHORT },
-               { "email",      1,      CMIT_FMT_EMAIL },
-               { "full",       5,      CMIT_FMT_FULL },
-               { "fuller",     5,      CMIT_FMT_FULLER },
-               { "oneline",    1,      CMIT_FMT_ONELINE },
+
+       if (prefixcmp(var, "pretty."))
+               return 0;
+
+       name = var + strlen("pretty.");
+       for (i = 0; i < builtin_formats_len; i++) {
+               if (!strcmp(commit_formats[i].name, name))
+                       return 0;
+       }
+
+       for (i = builtin_formats_len; i < commit_formats_len; i++) {
+               if (!strcmp(commit_formats[i].name, name)) {
+                       commit_format = &commit_formats[i];
+                       break;
+               }
+       }
+
+       if (!commit_format) {
+               ALLOC_GROW(commit_formats, commit_formats_len+1,
+                          commit_formats_alloc);
+               commit_format = &commit_formats[commit_formats_len];
+               memset(commit_format, 0, sizeof(*commit_format));
+               commit_formats_len++;
+       }
+
+       commit_format->name = xstrdup(name);
+       commit_format->format = CMIT_FMT_USERFORMAT;
+       git_config_string(&fmt, var, value);
+       if (!prefixcmp(fmt, "format:") || !prefixcmp(fmt, "tformat:")) {
+               commit_format->is_tformat = fmt[0] == 't';
+               fmt = strchr(fmt, ':') + 1;
+       } else if (strchr(fmt, '%'))
+               commit_format->is_tformat = 1;
+       else
+               commit_format->is_alias = 1;
+       commit_format->user_format = fmt;
+
+       return 0;
+}
+
+static void setup_commit_formats(void)
+{
+       struct cmt_fmt_map builtin_formats[] = {
+               { "raw",        CMIT_FMT_RAW,           0 },
+               { "medium",     CMIT_FMT_MEDIUM,        0 },
+               { "short",      CMIT_FMT_SHORT,         0 },
+               { "email",      CMIT_FMT_EMAIL,         0 },
+               { "fuller",     CMIT_FMT_FULLER,        0 },
+               { "full",       CMIT_FMT_FULL,          0 },
+               { "oneline",    CMIT_FMT_ONELINE,       1 }
        };
+       commit_formats_len = ARRAY_SIZE(builtin_formats);
+       builtin_formats_len = commit_formats_len;
+       ALLOC_GROW(commit_formats, commit_formats_len, commit_formats_alloc);
+       memcpy(commit_formats, builtin_formats,
+              sizeof(*builtin_formats)*ARRAY_SIZE(builtin_formats));
+
+       git_config(git_pretty_formats_config, NULL);
+}
+
+static struct cmt_fmt_map *find_commit_format_recursive(const char *sought,
+                                                       const char *original,
+                                                       int num_redirections)
+{
+       struct cmt_fmt_map *found = NULL;
+       size_t found_match_len = 0;
+       int i;
+
+       if (num_redirections >= commit_formats_len)
+               die("invalid --pretty format: "
+                   "'%s' references an alias which points to itself",
+                   original);
+
+       for (i = 0; i < commit_formats_len; i++) {
+               size_t match_len;
+
+               if (prefixcmp(commit_formats[i].name, sought))
+                       continue;
+
+               match_len = strlen(commit_formats[i].name);
+               if (found == NULL || found_match_len > match_len) {
+                       found = &commit_formats[i];
+                       found_match_len = match_len;
+               }
+       }
+
+       if (found && found->is_alias) {
+               found = find_commit_format_recursive(found->user_format,
+                                                    original,
+                                                    num_redirections+1);
+       }
+
+       return found;
+}
+
+static struct cmt_fmt_map *find_commit_format(const char *sought)
+{
+       if (!commit_formats)
+               setup_commit_formats();
+
+       return find_commit_format_recursive(sought, sought, 0);
+}
+
+void get_commit_format(const char *arg, struct rev_info *rev)
+{
+       struct cmt_fmt_map *commit_format;
 
        rev->use_terminator = 0;
        if (!arg || !*arg) {
@@ -47,21 +154,22 @@ void get_commit_format(const char *arg, struct rev_info *rev)
                save_user_format(rev, strchr(arg, ':') + 1, arg[0] == 't');
                return;
        }
-       for (i = 0; i < ARRAY_SIZE(cmt_fmts); i++) {
-               if (!strncmp(arg, cmt_fmts[i].n, cmt_fmts[i].cmp_len) &&
-                   !strncmp(arg, cmt_fmts[i].n, strlen(arg))) {
-                       if (cmt_fmts[i].v == CMIT_FMT_ONELINE)
-                               rev->use_terminator = 1;
-                       rev->commit_format = cmt_fmts[i].v;
-                       return;
-               }
-       }
+
        if (strchr(arg, '%')) {
                save_user_format(rev, arg, 1);
                return;
        }
 
-       die("invalid --pretty format: %s", arg);
+       commit_format = find_commit_format(arg);
+       if (!commit_format)
+               die("invalid --pretty format: %s", arg);
+
+       rev->commit_format = commit_format->format;
+       rev->use_terminator = commit_format->is_tformat;
+       if (commit_format->format == CMIT_FMT_USERFORMAT) {
+               save_user_format(rev, commit_format->user_format,
+                                commit_format->is_tformat);
+       }
 }
 
 /*
@@ -833,7 +941,7 @@ static size_t format_commit_item(struct strbuf *sb, const char *placeholder,
        enum {
                NO_MAGIC,
                ADD_LF_BEFORE_NON_EMPTY,
-               DEL_LF_BEFORE_EMPTY,
+               DEL_LF_BEFORE_EMPTY
        } magic = NO_MAGIC;
 
        switch (placeholder[0]) {
diff --git a/quote.c b/quote.c
index fc93435727db3b0634c390965258200c61d8b59b..63d3b018183abc05a5231dfd7e134dd7394f7a9b 100644 (file)
--- a/quote.c
+++ b/quote.c
@@ -295,42 +295,75 @@ void write_name_quotedpfx(const char *pfx, size_t pfxlen,
        fputc(terminator, fp);
 }
 
-/* quote path as relative to the given prefix */
-char *quote_path_relative(const char *in, int len,
-                         struct strbuf *out, const char *prefix)
+static const char *path_relative(const char *in, int len,
+                                struct strbuf *sb, const char *prefix,
+                                int prefix_len);
+
+void write_name_quoted_relative(const char *name, size_t len,
+                               const char *prefix, size_t prefix_len,
+                               FILE *fp, int terminator)
 {
-       int needquote;
+       struct strbuf sb = STRBUF_INIT;
+
+       name = path_relative(name, len, &sb, prefix, prefix_len);
+       write_name_quoted(name, fp, terminator);
+
+       strbuf_release(&sb);
+}
+
+/*
+ * Give path as relative to prefix.
+ *
+ * The strbuf may or may not be used, so do not assume it contains the
+ * returned path.
+ */
+static const char *path_relative(const char *in, int len,
+                                struct strbuf *sb, const char *prefix,
+                                int prefix_len)
+{
+       int off, i;
 
        if (len < 0)
                len = strlen(in);
+       if (prefix && prefix_len < 0)
+               prefix_len = strlen(prefix);
+
+       off = 0;
+       i = 0;
+       while (i < prefix_len && i < len && prefix[i] == in[i]) {
+               if (prefix[i] == '/')
+                       off = i + 1;
+               i++;
+       }
+       in += off;
+       len -= off;
+
+       if (i >= prefix_len)
+               return in;
 
-       /* "../" prefix itself does not need quoting, but "in" might. */
-       needquote = next_quote_pos(in, len) < len;
-       strbuf_setlen(out, 0);
-       strbuf_grow(out, len);
-
-       if (needquote)
-               strbuf_addch(out, '"');
-       if (prefix) {
-               int off = 0;
-               while (prefix[off] && off < len && prefix[off] == in[off])
-                       if (prefix[off] == '/') {
-                               prefix += off + 1;
-                               in += off + 1;
-                               len -= off + 1;
-                               off = 0;
-                       } else
-                               off++;
-
-               for (; *prefix; prefix++)
-                       if (*prefix == '/')
-                               strbuf_addstr(out, "../");
+       strbuf_reset(sb);
+       strbuf_grow(sb, len);
+
+       while (i < prefix_len) {
+               if (prefix[i] == '/')
+                       strbuf_addstr(sb, "../");
+               i++;
        }
+       strbuf_add(sb, in, len);
+
+       return sb->buf;
+}
 
-       quote_c_style_counted (in, len, out, NULL, 1);
+/* quote path as relative to the given prefix */
+char *quote_path_relative(const char *in, int len,
+                         struct strbuf *out, const char *prefix)
+{
+       struct strbuf sb = STRBUF_INIT;
+       const char *rel = path_relative(in, len, &sb, prefix, -1);
+       strbuf_reset(out);
+       quote_c_style_counted(rel, strlen(rel), out, NULL, 0);
+       strbuf_release(&sb);
 
-       if (needquote)
-               strbuf_addch(out, '"');
        if (!out->len)
                strbuf_addstr(out, "./");
 
diff --git a/quote.h b/quote.h
index f83eb233c4b153c4c073b3ccd5bf2f5dd926b739..38003bff5f97a11e051b106522a8548d43d24f6c 100644 (file)
--- a/quote.h
+++ b/quote.h
@@ -54,9 +54,12 @@ extern void quote_two_c_style(struct strbuf *, const char *, const char *, int);
 extern void write_name_quoted(const char *name, FILE *, int terminator);
 extern void write_name_quotedpfx(const char *pfx, size_t pfxlen,
                                  const char *name, FILE *, int terminator);
+extern void write_name_quoted_relative(const char *name, size_t len,
+               const char *prefix, size_t prefix_len,
+               FILE *fp, int terminator);
 
 /* quote path as relative to the given prefix */
-char *quote_path_relative(const char *in, int len,
+extern char *quote_path_relative(const char *in, int len,
                          struct strbuf *out, const char *prefix);
 
 /* quoting as a string literal for other languages */
diff --git a/refs.c b/refs.c
index d3db15a76cc46f6f6a31d4448816c09e6c48e543..6f486ae62d8b4605520e75286ca36217d0715363 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -314,7 +314,11 @@ static int warn_if_dangling_symref(const char *refname, const unsigned char *sha
 
 void warn_dangling_symref(FILE *fp, const char *msg_fmt, const char *refname)
 {
-       struct warn_if_dangling_data data = { fp, refname, msg_fmt };
+       struct warn_if_dangling_data data;
+
+       data.fp = fp;
+       data.refname = refname;
+       data.msg_fmt = msg_fmt;
        for_each_rawref(warn_if_dangling_symref, &data);
 }
 
@@ -1258,52 +1262,65 @@ static int copy_msg(char *buf, const char *msg)
        return cp - buf;
 }
 
-static int log_ref_write(const char *ref_name, const unsigned char *old_sha1,
-                        const unsigned char *new_sha1, const char *msg)
+int log_ref_setup(const char *ref_name, char *logfile, int bufsize)
 {
-       int logfd, written, oflags = O_APPEND | O_WRONLY;
-       unsigned maxlen, len;
-       int msglen;
-       char log_file[PATH_MAX];
-       char *logrec;
-       const char *committer;
-
-       if (log_all_ref_updates < 0)
-               log_all_ref_updates = !is_bare_repository();
-
-       git_snpath(log_file, sizeof(log_file), "logs/%s", ref_name);
+       int logfd, oflags = O_APPEND | O_WRONLY;
 
+       git_snpath(logfile, bufsize, "logs/%s", ref_name);
        if (log_all_ref_updates &&
            (!prefixcmp(ref_name, "refs/heads/") ||
             !prefixcmp(ref_name, "refs/remotes/") ||
             !prefixcmp(ref_name, "refs/notes/") ||
             !strcmp(ref_name, "HEAD"))) {
-               if (safe_create_leading_directories(log_file) < 0)
+               if (safe_create_leading_directories(logfile) < 0)
                        return error("unable to create directory for %s",
-                                    log_file);
+                                    logfile);
                oflags |= O_CREAT;
        }
 
-       logfd = open(log_file, oflags, 0666);
+       logfd = open(logfile, oflags, 0666);
        if (logfd < 0) {
                if (!(oflags & O_CREAT) && errno == ENOENT)
                        return 0;
 
                if ((oflags & O_CREAT) && errno == EISDIR) {
-                       if (remove_empty_directories(log_file)) {
+                       if (remove_empty_directories(logfile)) {
                                return error("There are still logs under '%s'",
-                                            log_file);
+                                            logfile);
                        }
-                       logfd = open(log_file, oflags, 0666);
+                       logfd = open(logfile, oflags, 0666);
                }
 
                if (logfd < 0)
                        return error("Unable to append to %s: %s",
-                                    log_file, strerror(errno));
+                                    logfile, strerror(errno));
        }
 
-       adjust_shared_perm(log_file);
+       adjust_shared_perm(logfile);
+       close(logfd);
+       return 0;
+}
+
+static int log_ref_write(const char *ref_name, const unsigned char *old_sha1,
+                        const unsigned char *new_sha1, const char *msg)
+{
+       int logfd, result, written, oflags = O_APPEND | O_WRONLY;
+       unsigned maxlen, len;
+       int msglen;
+       char log_file[PATH_MAX];
+       char *logrec;
+       const char *committer;
+
+       if (log_all_ref_updates < 0)
+               log_all_ref_updates = !is_bare_repository();
 
+       result = log_ref_setup(ref_name, log_file, sizeof(log_file));
+       if (result)
+               return result;
+
+       logfd = open(log_file, oflags);
+       if (logfd < 0)
+               return 0;
        msglen = msg ? strlen(msg) : 0;
        committer = git_committer_info(0);
        maxlen = strlen(committer) + msglen + 100;
diff --git a/refs.h b/refs.h
index 4a18b083f52a15e5216d583644e590d666cae097..762ce504b5eba3aacff204321a7c41db2b1e6053 100644 (file)
--- a/refs.h
+++ b/refs.h
@@ -68,6 +68,9 @@ extern void unlock_ref(struct ref_lock *lock);
 /** Writes sha1 into the ref specified by the lock. **/
 extern int write_ref_sha1(struct ref_lock *lock, const unsigned char *sha1, const char *msg);
 
+/** Setup reflog before using. **/
+int log_ref_setup(const char *ref_name, char *logfile, int bufsize);
+
 /** Reads log for the value of ref during at_time. **/
 extern int read_ref_at(const char *ref, unsigned long at_time, int cnt, unsigned char *sha1, char **msg, unsigned long *cutoff_time, int *cutoff_tz, int *cutoff_cnt);
 
index 0f073e0b5f9f08510af3a9e0ab5aed106475b30f..e51cd22d6bcbd29bd47392434d37fb50d7b97588 100644 (file)
--- a/remote.c
+++ b/remote.c
@@ -478,7 +478,7 @@ static void read_config(void)
        unsigned char sha1[20];
        const char *head_ref;
        int flag;
-       if (default_remote_name) // did this already
+       if (default_remote_name) /* did this already */
                return;
        default_remote_name = xstrdup("origin");
        current_branch = NULL;
@@ -659,10 +659,9 @@ static struct refspec *parse_refspec_internal(int nr_refspec, const char **refsp
 
 int valid_fetch_refspec(const char *fetch_refspec_str)
 {
-       const char *fetch_refspec[] = { fetch_refspec_str };
        struct refspec *refspec;
 
-       refspec = parse_refspec_internal(1, fetch_refspec, 1, 1);
+       refspec = parse_refspec_internal(1, &fetch_refspec_str, 1, 1);
        free_refspecs(refspec, 1);
        return !!refspec;
 }
index 6e13643cabb6fa9a5b619f53dd148345d9161ad4..888d7c15de2eacc56d869a96d4463aefca7a7a06 100644 (file)
--- a/remote.h
+++ b/remote.h
@@ -145,7 +145,7 @@ int branch_merge_matches(struct branch *, int n, const char *);
 enum match_refs_flags {
        MATCH_REFS_NONE         = 0,
        MATCH_REFS_ALL          = (1 << 0),
-       MATCH_REFS_MIRROR       = (1 << 1),
+       MATCH_REFS_MIRROR       = (1 << 1)
 };
 
 /* Reporting of tracking info */
index f221bed1e97a0f1b41d1845edac270a109e3a4dd..2197890982e55d7ff0832b6c59b6661874fa4223 100644 (file)
--- a/rerere.c
+++ b/rerere.c
@@ -153,7 +153,7 @@ static int handle_path(unsigned char *sha1, struct rerere_io *io, int marker_siz
        git_SHA_CTX ctx;
        int hunk_no = 0;
        enum {
-               RR_CONTEXT = 0, RR_SIDE_1, RR_SIDE_2, RR_ORIGINAL,
+               RR_CONTEXT = 0, RR_SIDE_1, RR_SIDE_2, RR_ORIGINAL
        } hunk = RR_CONTEXT;
        struct strbuf one = STRBUF_INIT, two = STRBUF_INIT;
        struct strbuf buf = STRBUF_INIT;
index f4b8b383153b2330be1729fa29488bab1848e019..b209d493e169ae58130a998f7dc1239f5a385c44 100644 (file)
@@ -1781,7 +1781,7 @@ int prepare_revision_walk(struct rev_info *revs)
 enum rewrite_result {
        rewrite_one_ok,
        rewrite_one_noparents,
-       rewrite_one_error,
+       rewrite_one_error
 };
 
 static enum rewrite_result rewrite_one(struct rev_info *revs, struct commit **pp)
index c7793f50fbe0a43495c2b2d36a47c0b5aac37483..2a1041ef6599c84fff6a8d9faf5dea23a2af3ab0 100644 (file)
@@ -84,6 +84,7 @@ static NORETURN void die_child(const char *err, va_list params)
        unused = write(child_err, "\n", 1);
        exit(128);
 }
+#endif
 
 static inline void set_cloexec(int fd)
 {
@@ -91,7 +92,6 @@ static inline void set_cloexec(int fd)
        if (flags >= 0)
                fcntl(fd, F_SETFD, flags | FD_CLOEXEC);
 }
-#endif
 
 static int wait_or_whine(pid_t pid, const char *argv0, int silent_exec_failure)
 {
@@ -449,11 +449,35 @@ int run_command_v_opt_cd_env(const char **argv, int opt, const char *dir, const
        return run_command(&cmd);
 }
 
-#ifdef WIN32
-static unsigned __stdcall run_thread(void *data)
+#ifndef NO_PTHREADS
+static pthread_t main_thread;
+static int main_thread_set;
+static pthread_key_t async_key;
+
+static void *run_thread(void *data)
 {
        struct async *async = data;
-       return async->proc(async->proc_in, async->proc_out, async->data);
+       intptr_t ret;
+
+       pthread_setspecific(async_key, async);
+       ret = async->proc(async->proc_in, async->proc_out, async->data);
+       return (void *)ret;
+}
+
+static NORETURN void die_async(const char *err, va_list params)
+{
+       vreportf("fatal: ", err, params);
+
+       if (!pthread_equal(main_thread, pthread_self())) {
+               struct async *async = pthread_getspecific(async_key);
+               if (async->proc_in >= 0)
+                       close(async->proc_in);
+               if (async->proc_out >= 0)
+                       close(async->proc_out);
+               pthread_exit((void *)128);
+       }
+
+       exit(128);
 }
 #endif
 
@@ -499,7 +523,7 @@ int start_async(struct async *async)
        else
                proc_out = -1;
 
-#ifndef WIN32
+#ifdef NO_PTHREADS
        /* Flush stdio before fork() to avoid cloning buffers */
        fflush(NULL);
 
@@ -526,12 +550,29 @@ int start_async(struct async *async)
        else if (async->out)
                close(async->out);
 #else
+       if (!main_thread_set) {
+               /*
+                * We assume that the first time that start_async is called
+                * it is from the main thread.
+                */
+               main_thread_set = 1;
+               main_thread = pthread_self();
+               pthread_key_create(&async_key, NULL);
+               set_die_routine(die_async);
+       }
+
+       if (proc_in >= 0)
+               set_cloexec(proc_in);
+       if (proc_out >= 0)
+               set_cloexec(proc_out);
        async->proc_in = proc_in;
        async->proc_out = proc_out;
-       async->tid = (HANDLE) _beginthreadex(NULL, 0, run_thread, async, 0, NULL);
-       if (!async->tid) {
-               error("cannot create thread: %s", strerror(errno));
-               goto error;
+       {
+               int err = pthread_create(&async->tid, NULL, run_thread, async);
+               if (err) {
+                       error("cannot create thread: %s", strerror(err));
+                       goto error;
+               }
        }
 #endif
        return 0;
@@ -551,17 +592,15 @@ error:
 
 int finish_async(struct async *async)
 {
-#ifndef WIN32
-       int ret = wait_or_whine(async->pid, "child process", 0);
+#ifdef NO_PTHREADS
+       return wait_or_whine(async->pid, "child process", 0);
 #else
-       DWORD ret = 0;
-       if (WaitForSingleObject(async->tid, INFINITE) != WAIT_OBJECT_0)
-               ret = error("waiting for thread failed: %lu", GetLastError());
-       else if (!GetExitCodeThread(async->tid, &ret))
-               ret = error("cannot get thread exit code: %lu", GetLastError());
-       CloseHandle(async->tid);
+       void *ret = (void *)(intptr_t)(-1);
+
+       if (pthread_join(async->tid, &ret))
+               error("pthread_join failed");
+       return (int)(intptr_t)ret;
 #endif
-       return ret;
 }
 
 int run_hook(const char *index_file, const char *name, ...)
index 94619f52d95888b320664b7f19db3eeb7d6d8cca..56491b9f2344541c02bd0da2928a535f11193bd8 100644 (file)
@@ -1,6 +1,10 @@
 #ifndef RUN_COMMAND_H
 #define RUN_COMMAND_H
 
+#ifndef NO_PTHREADS
+#include <pthread.h>
+#endif
+
 struct child_process {
        const char **argv;
        pid_t pid;
@@ -74,10 +78,10 @@ struct async {
        void *data;
        int in;         /* caller writes here and closes it */
        int out;        /* caller reads from here and closes it */
-#ifndef WIN32
+#ifdef NO_PTHREADS
        pid_t pid;
 #else
-       HANDLE tid;
+       pthread_t tid;
        int proc_in;
        int proc_out;
 #endif
diff --git a/setup.c b/setup.c
index 5a083fa77dda016d0f3f511787ebf771aaf1be08..7e0460205dc1d15007849f367d21e999f2da044c 100644 (file)
--- a/setup.c
+++ b/setup.c
@@ -546,6 +546,12 @@ int check_repository_format(void)
        return check_repository_format_gently(NULL);
 }
 
+/*
+ * Returns the "prefix", a path to the current working directory
+ * relative to the work tree root, or NULL, if the current working
+ * directory is not a strict subdirectory of the work tree root. The
+ * prefix always ends with a '/' character.
+ */
 const char *setup_git_directory(void)
 {
        const char *retval = setup_git_directory_gently(NULL);
index d8e61a65d10ed6fa792d207e36fb6b245ef86970..e42ef96d457f6aa12ab7d0057acf936db0335d40 100644 (file)
@@ -102,20 +102,22 @@ static void fill_sha1_path(char *pathbuf, const unsigned char *sha1)
  */
 char *sha1_file_name(const unsigned char *sha1)
 {
-       static char *name, *base;
+       static char buf[PATH_MAX];
+       const char *objdir;
+       int len;
 
-       if (!base) {
-               const char *sha1_file_directory = get_object_directory();
-               int len = strlen(sha1_file_directory);
-               base = xmalloc(len + 60);
-               memcpy(base, sha1_file_directory, len);
-               memset(base+len, 0, 60);
-               base[len] = '/';
-               base[len+3] = '/';
-               name = base + len + 1;
-       }
-       fill_sha1_path(name, sha1);
-       return base;
+       objdir = get_object_directory();
+       len = strlen(objdir);
+
+       /* '/' + sha1(2) + '/' + sha1(38) + '\0' */
+       if (len + 43 > PATH_MAX)
+               die("insanely long object directory %s", objdir);
+       memcpy(buf, objdir, len);
+       buf[len] = '/';
+       buf[len+3] = '/';
+       buf[len+42] = '\0';
+       fill_sha1_path(buf + len + 1, sha1);
+       return buf;
 }
 
 static char *sha1_get_pack_name(const unsigned char *sha1,
index 25c559bb49d04586c69242cd7ef03713f0939e9d..f9de24b4d2baa9fad1d6caa72fea651da78d6878 100644 (file)
@@ -3,6 +3,7 @@
 # Copyright (c) 2005 Junio C Hamano
 #
 
+-include ../config.mak.autogen
 -include ../config.mak
 
 #GIT_TEST_OPTS=--verbose --debug
@@ -35,7 +36,9 @@ aggregate-results-and-cleanup: $(T)
        $(MAKE) clean
 
 aggregate-results:
-       '$(SHELL_PATH_SQ)' ./aggregate-results.sh test-results/t*-*
+       for f in test-results/t*-*; do \
+               echo "$$f"; \
+       done | '$(SHELL_PATH_SQ)' ./aggregate-results.sh
 
 # we can test NO_OPTIMIZE_COMMITS independently of LC_ALL
 full-svn-test:
index dcd3ebb5f2dcdbf15ca0e4a043b45cd2fc36cbb5..0e4e8d8862c96383a6f6f22a1b6bb01044925620 100644 (file)
--- a/t/README
+++ b/t/README
@@ -84,6 +84,12 @@ appropriately before running "make".
        implied by other options like --valgrind and
        GIT_TEST_INSTALLED.
 
+--root=<directory>::
+       Create "trash" directories used to store all temporary data during
+       testing under <directory>, instead of the t/ directory.
+       Using this option with a RAM-based filesystem (such as tmpfs)
+       can massively speed up the test suite.
+
 You can also set the GIT_TEST_INSTALLED environment variable to
 the bindir of an existing git installation to test that installation.
 You still need to have built this git sandbox, from which various
index d5bab75d7da49ebb53e368d67f6b867f5417a125..d206b7c4cfa4f7d61d151cebc35a0f296fbe4c6b 100755 (executable)
@@ -6,7 +6,7 @@ failed=0
 broken=0
 total=0
 
-for file
+while read file
 do
        while read type value
        do
index 5a734b1b7b2df4a5c5c35f5347c618f3735317ac..b70b891b628d1e5d7fe68b4d303c17fdd0416981 100644 (file)
@@ -19,9 +19,9 @@ our \$site_name = '[localhost]';
 our \$site_header = '';
 our \$site_footer = '';
 our \$home_text = 'indextext.html';
-our @stylesheets = ('file:///$TEST_DIRECTORY/../gitweb/gitweb.css');
-our \$logo = 'file:///$TEST_DIRECTORY/../gitweb/git-logo.png';
-our \$favicon = 'file:///$TEST_DIRECTORY/../gitweb/git-favicon.png';
+our @stylesheets = ('file:///$TEST_DIRECTORY/../gitweb/static/gitweb.css');
+our \$logo = 'file:///$TEST_DIRECTORY/../gitweb/static/git-logo.png';
+our \$favicon = 'file:///$TEST_DIRECTORY/../gitweb/static/git-favicon.png';
 our \$projects_list = '';
 our \$export_ok = '';
 our \$strict_export = '';
diff --git a/t/lib-t6000.sh b/t/lib-t6000.sh
new file mode 100644 (file)
index 0000000..ea25dd8
--- /dev/null
@@ -0,0 +1,127 @@
+: included from 6002 and others
+
+[ -d .git/refs/tags ] || mkdir -p .git/refs/tags
+
+:> sed.script
+
+# Answer the sha1 has associated with the tag. The tag must exist in .git or .git/refs/tags
+tag()
+{
+       _tag=$1
+       [ -f .git/refs/tags/$_tag ] || error "tag: \"$_tag\" does not exist"
+       cat .git/refs/tags/$_tag
+}
+
+# Generate a commit using the text specified to make it unique and the tree
+# named by the tag specified.
+unique_commit()
+{
+       _text=$1
+        _tree=$2
+       shift 2
+       echo $_text | git commit-tree $(tag $_tree) "$@"
+}
+
+# Save the output of a command into the tag specified. Prepend
+# a substitution script for the tag onto the front of sed.script
+save_tag()
+{
+       _tag=$1
+       [ -n "$_tag" ] || error "usage: save_tag tag commit-args ..."
+       shift 1
+       "$@" >.git/refs/tags/$_tag
+
+        echo "s/$(tag $_tag)/$_tag/g" > sed.script.tmp
+       cat sed.script >> sed.script.tmp
+       rm sed.script
+       mv sed.script.tmp sed.script
+}
+
+# Replace unhelpful sha1 hashses with their symbolic equivalents
+entag()
+{
+       sed -f sed.script
+}
+
+# Execute a command after first saving, then setting the GIT_AUTHOR_EMAIL
+# tag to a specified value. Restore the original value on return.
+as_author()
+{
+       _author=$1
+       shift 1
+        _save=$GIT_AUTHOR_EMAIL
+
+       GIT_AUTHOR_EMAIL="$_author"
+       export GIT_AUTHOR_EMAIL
+       "$@"
+       if test -z "$_save"
+       then
+               unset GIT_AUTHOR_EMAIL
+       else
+               GIT_AUTHOR_EMAIL="$_save"
+               export GIT_AUTHOR_EMAIL
+       fi
+}
+
+commit_date()
+{
+        _commit=$1
+       git cat-file commit $_commit | sed -n "s/^committer .*> \([0-9]*\) .*/\1/p"
+}
+
+on_committer_date()
+{
+    _date=$1
+    shift 1
+    GIT_COMMITTER_DATE="$_date"
+    export GIT_COMMITTER_DATE
+    "$@"
+    unset GIT_COMMITTER_DATE
+}
+
+# Execute a command and suppress any error output.
+hide_error()
+{
+       "$@" 2>/dev/null
+}
+
+check_output()
+{
+       _name=$1
+       shift 1
+       if eval "$*" | entag > $_name.actual
+       then
+               test_cmp $_name.expected $_name.actual
+       else
+               return 1;
+       fi
+}
+
+# Turn a reasonable test description into a reasonable test name.
+# All alphanums translated into -'s which are then compressed and stripped
+# from front and back.
+name_from_description()
+{
+       perl -pe '
+               s/[^A-Za-z0-9.]/-/g;
+               s/-+/-/g;
+               s/-$//;
+               s/^-//;
+               y/A-Z/a-z/;
+       '
+}
+
+
+# Execute the test described by the first argument, by eval'ing
+# command line specified in the 2nd argument. Check the status code
+# is zero and that the output matches the stream read from
+# stdin.
+test_output_expect_success()
+{
+       _description=$1
+        _test=$2
+        [ $# -eq 2 ] || error "usage: test_output_expect_success description test <<EOF ... EOF"
+        _name=$(echo $_description | name_from_description)
+       cat > $_name.expected
+       test_expect_success "$_description" "check_output $_name \"$_test\""
+}
index 3ec9cbef2c88f65e5fb254d10cc551c6c4062c88..f2c73369a5b93544ee837229da965d7efbdc45bf 100755 (executable)
@@ -301,7 +301,7 @@ $expectfilter >expected <<\EOF
 EOF
 test_expect_success \
     'validate git diff-files output for a know cache/work tree state.' \
-    'git diff-files >current && diff >/dev/null -b current expected'
+    'git diff-files >current && test_cmp current expected >/dev/null'
 
 test_expect_success \
     'git update-index --refresh should succeed.' \
index 6cb8d60ea2649495c0e3c8bbb8b7cc75c36799b7..828e35baf72d94908ad1f30dbd2e1aa6f9376e69 100755 (executable)
@@ -65,17 +65,21 @@ test_expect_success expanded_in_repo '
                echo "\$Id:NoSpaceAtFront \$"
                echo "\$Id:NoSpaceAtEitherEnd\$"
                echo "\$Id: NoTerminatingSymbol"
+               echo "\$Id: Foreign Commit With Spaces \$"
+               echo "\$Id: NoTerminatingSymbolAtEOF"
        } > expanded-keywords &&
 
        {
                echo "File with expanded keywords"
-               echo "\$Id: 4f21723e7b15065df7de95bd46c8ba6fb1818f4c \$"
-               echo "\$Id: 4f21723e7b15065df7de95bd46c8ba6fb1818f4c \$"
-               echo "\$Id: 4f21723e7b15065df7de95bd46c8ba6fb1818f4c \$"
-               echo "\$Id: 4f21723e7b15065df7de95bd46c8ba6fb1818f4c \$"
-               echo "\$Id: 4f21723e7b15065df7de95bd46c8ba6fb1818f4c \$"
-               echo "\$Id: 4f21723e7b15065df7de95bd46c8ba6fb1818f4c \$"
+               echo "\$Id: fd0478f5f1486f3d5177d4c3f6eb2765e8fc56b9 \$"
+               echo "\$Id: fd0478f5f1486f3d5177d4c3f6eb2765e8fc56b9 \$"
+               echo "\$Id: fd0478f5f1486f3d5177d4c3f6eb2765e8fc56b9 \$"
+               echo "\$Id: fd0478f5f1486f3d5177d4c3f6eb2765e8fc56b9 \$"
+               echo "\$Id: fd0478f5f1486f3d5177d4c3f6eb2765e8fc56b9 \$"
+               echo "\$Id: fd0478f5f1486f3d5177d4c3f6eb2765e8fc56b9 \$"
                echo "\$Id: NoTerminatingSymbol"
+               echo "\$Id: Foreign Commit With Spaces \$"
+               echo "\$Id: NoTerminatingSymbolAtEOF"
        } > expected-output &&
 
        git add expanded-keywords &&
index 22a80c8268b368963d58fd26fffde5cc8d084ee6..759cf12e16bcddc3dab9d6b3ad8abd5590245a94 100755 (executable)
@@ -5,7 +5,9 @@ test_description='git fsck random collection of tests'
 . ./test-lib.sh
 
 test_expect_success setup '
+       git config i18n.commitencoding ISO-8859-1 &&
        test_commit A fileA one &&
+       git config --unset i18n.commitencoding &&
        git checkout HEAD^0 &&
        test_commit B fileB two &&
        git tag -d A B &&
@@ -28,6 +30,12 @@ test_expect_success 'loose objects borrowed from alternate are not missing' '
        )
 '
 
+test_expect_success 'valid objects appear valid' '
+       { git fsck 2>out; true; } &&
+       ! grep error out &&
+       ! grep fatal out
+'
+
 # Corruption tests follow.  Make sure to remove all traces of the
 # specific corruption you test afterwards, lest a later test trip over
 # it.
index 20f33436d00077b64dcc855fc263cd5d0efcca38..27e2127afeeb0dd462a686254e60be74e9dd9c28 100755 (executable)
@@ -44,8 +44,10 @@ test_expect_success 'switch from symlink to dir' '
 
 '
 
-rm -fr frotz xyzzy nitfol &&
-git checkout -f master || exit
+test_expect_success 'Remove temporary directories & switch to master' '
+       rm -fr frotz xyzzy nitfol &&
+       git checkout -f master
+'
 
 test_expect_success 'switch from dir to symlink' '
 
index a8297c61bd7402423eec6af1ba92f46cd13dd1bd..be88d4b5ee86b75eb0e5654404064f712192d0a9 100755 (executable)
@@ -49,6 +49,62 @@ test_expect_success '--orphan must be rejected with -b' '
        test refs/heads/master = "$(git symbolic-ref HEAD)"
 '
 
+test_expect_success '--orphan must be rejected with -t' '
+       git checkout master &&
+       test_must_fail git checkout --orphan new -t master &&
+       test refs/heads/master = "$(git symbolic-ref HEAD)"
+'
+
+test_expect_success '--orphan ignores branch.autosetupmerge' '
+       git checkout master &&
+       git config branch.autosetupmerge always &&
+       git checkout --orphan gamma &&
+       test -z "$(git config branch.gamma.merge)" &&
+       test refs/heads/gamma = "$(git symbolic-ref HEAD)" &&
+       test_must_fail git rev-parse --verify HEAD^
+'
+
+test_expect_success '--orphan makes reflog by default' '
+       git checkout master &&
+       git config --unset core.logAllRefUpdates &&
+       git checkout --orphan delta &&
+       ! test -f .git/logs/refs/heads/delta &&
+       test_must_fail PAGER= git reflog show delta &&
+       git commit -m Delta &&
+       test -f .git/logs/refs/heads/delta &&
+       PAGER= git reflog show delta
+'
+
+test_expect_success '--orphan does not make reflog when core.logAllRefUpdates = false' '
+       git checkout master &&
+       git config core.logAllRefUpdates false &&
+       git checkout --orphan epsilon &&
+       ! test -f .git/logs/refs/heads/epsilon &&
+       test_must_fail PAGER= git reflog show epsilon &&
+       git commit -m Epsilon &&
+       ! test -f .git/logs/refs/heads/epsilon &&
+       test_must_fail PAGER= git reflog show epsilon
+'
+
+test_expect_success '--orphan with -l makes reflog when core.logAllRefUpdates = false' '
+       git checkout master &&
+       git checkout -l --orphan zeta &&
+       test -f .git/logs/refs/heads/zeta &&
+       test_must_fail PAGER= git reflog show zeta &&
+       git commit -m Zeta &&
+       PAGER= git reflog show zeta
+'
+
+test_expect_success 'giving up --orphan not committed when -l and core.logAllRefUpdates = false deletes reflog' '
+       git checkout master &&
+       git checkout -l --orphan eta &&
+       test -f .git/logs/refs/heads/eta &&
+       test_must_fail PAGER= git reflog show eta &&
+       git checkout master &&
+       ! test -f .git/logs/refs/heads/eta &&
+       test_must_fail PAGER= git reflog show eta
+'
+
 test_expect_success '--orphan is rejected with an existing name' '
        git checkout master &&
        test_must_fail git checkout --orphan master &&
@@ -60,31 +116,11 @@ test_expect_success '--orphan refuses to switch if a merge is needed' '
        git reset --hard &&
        echo local >>"$TEST_FILE" &&
        cat "$TEST_FILE" >"$TEST_FILE.saved" &&
-       test_must_fail git checkout --orphan gamma master^ &&
+       test_must_fail git checkout --orphan new master^ &&
        test refs/heads/master = "$(git symbolic-ref HEAD)" &&
        test_cmp "$TEST_FILE" "$TEST_FILE.saved" &&
        git diff-index --quiet --cached HEAD &&
        git reset --hard
 '
 
-test_expect_success '--orphan does not mix well with -t' '
-       git checkout master &&
-       test_must_fail git checkout -t master --orphan gamma &&
-       test refs/heads/master = "$(git symbolic-ref HEAD)"
-'
-
-test_expect_success '--orphan ignores branch.autosetupmerge' '
-       git checkout -f master &&
-       git config branch.autosetupmerge always &&
-       git checkout --orphan delta &&
-       test -z "$(git config branch.delta.merge)" &&
-       test refs/heads/delta = "$(git symbolic-ref HEAD)" &&
-       test_must_fail git rev-parse --verify HEAD^
-'
-
-test_expect_success '--orphan does not mix well with -l' '
-       git checkout -f master &&
-       test_must_fail git checkout -l --orphan gamma
-'
-
 test_done
diff --git a/t/t2106-update-index-assume-unchanged.sh b/t/t2106-update-index-assume-unchanged.sh
new file mode 100755 (executable)
index 0000000..99d858c
--- /dev/null
@@ -0,0 +1,24 @@
+#!/bin/sh
+
+test_description='git update-index --assume-unchanged test.
+'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' \
+       ': >file &&
+        git add file &&
+        git commit -m initial &&
+        git branch other &&
+        echo upstream >file &&
+        git add file &&
+        git commit -m upstream'
+
+test_expect_success 'do not switch branches with dirty file' \
+       'git reset --hard &&
+        git checkout other &&
+        echo dirt >file &&
+        git update-index --assume-unchanged file &&
+        test_must_fail git checkout master'
+
+test_done
index 9929f82021deeb20358016b487400b80e27b596f..d541544537d47587abad8d27565cf9197387cfd6 100755 (executable)
@@ -22,6 +22,7 @@ test_expect_success 'setup 1' '
        git branch df-2 &&
        git branch df-3 &&
        git branch remove &&
+       git branch submod &&
 
        echo hello >>a &&
        cp a d/e &&
@@ -236,6 +237,17 @@ test_expect_success 'setup 6' '
        test_cmp expected actual
 '
 
+test_expect_success 'setup 7' '
+
+       git checkout submod &&
+       git rm d/e &&
+       test_tick &&
+       git commit -m "remove d/e" &&
+       git update-index --add --cacheinfo 160000 $c1 d &&
+       test_tick &&
+       git commit -m "make d/ a submodule"
+'
+
 test_expect_success 'merge-recursive simple' '
 
        rm -fr [abcd] &&
@@ -551,4 +563,21 @@ test_expect_success 'merge removes empty directories' '
        test_must_fail test -d d
 '
 
+test_expect_failure 'merge-recursive simple w/submodule' '
+
+       git checkout submod &&
+       git merge remove
+'
+
+test_expect_failure 'merge-recursive simple w/submodule result' '
+
+       git ls-files -s >actual &&
+       (
+               echo "100644 $o5 0      a"
+               echo "100644 $o0 0      c"
+               echo "160000 $c1 0      d"
+       ) >expected &&
+       test_cmp expected actual
+'
+
 test_done
index e0b760513cfc065126cecd6e273180826c8f6bc9..859b99abf1cc62c966322fcfc552c0a7eb7356df 100755 (executable)
@@ -43,7 +43,7 @@ test_expect_success \
      git branch -l d/e/f &&
         test -f .git/refs/heads/d/e/f &&
         test -f .git/logs/refs/heads/d/e/f &&
-        diff expect .git/logs/refs/heads/d/e/f'
+        test_cmp expect .git/logs/refs/heads/d/e/f'
 
 test_expect_success \
     'git branch -d d/e/f should delete a branch and a log' \
@@ -222,7 +222,31 @@ test_expect_success \
      git checkout -b g/h/i -l master &&
         test -f .git/refs/heads/g/h/i &&
         test -f .git/logs/refs/heads/g/h/i &&
-        diff expect .git/logs/refs/heads/g/h/i'
+        test_cmp expect .git/logs/refs/heads/g/h/i'
+
+test_expect_success 'checkout -b makes reflog by default' '
+       git checkout master &&
+       git config --unset core.logAllRefUpdates &&
+       git checkout -b alpha &&
+       test -f .git/logs/refs/heads/alpha &&
+       PAGER= git reflog show alpha
+'
+
+test_expect_success 'checkout -b does not make reflog when core.logAllRefUpdates = false' '
+       git checkout master &&
+       git config core.logAllRefUpdates false &&
+       git checkout -b beta &&
+       ! test -f .git/logs/refs/heads/beta &&
+       test_must_fail PAGER= git reflog show beta
+'
+
+test_expect_success 'checkout -b with -l makes reflog when core.logAllRefUpdates = false' '
+       git checkout master &&
+       git checkout -lb gamma &&
+       git config --unset core.logAllRefUpdates &&
+       test -f .git/logs/refs/heads/gamma &&
+       PAGER= git reflog show gamma
+'
 
 test_expect_success 'avoid ambiguous track' '
        git config branch.autosetupmerge true &&
index 413019acafc98646a61e77960527f042a8f96ac6..525174013c4c33eab5bdbde8831d43f1ddbaeaae 100755 (executable)
@@ -28,7 +28,7 @@ test_expect_success \
      SHA1=`cat .git/refs/heads/a` &&
      echo "$SHA1 refs/heads/a" >expect &&
      git show-ref a >result &&
-     diff expect result'
+     test_cmp expect result'
 
 test_expect_success \
     'see if a branch still exists when packed' \
@@ -37,7 +37,7 @@ test_expect_success \
      rm -f .git/refs/heads/b &&
      echo "$SHA1 refs/heads/b" >expect &&
      git show-ref b >result &&
-     diff expect result'
+     test_cmp expect result'
 
 test_expect_success 'git branch c/d should barf if branch c exists' '
      git branch c &&
@@ -52,7 +52,7 @@ test_expect_success \
      git pack-refs --all --prune &&
      echo "$SHA1 refs/heads/e" >expect &&
      git show-ref e >result &&
-     diff expect result'
+     test_cmp expect result'
 
 test_expect_success 'see if git pack-refs --prune remove ref files' '
      git branch f &&
@@ -109,7 +109,7 @@ test_expect_success 'pack, prune and repack' '
        git show-ref >all-of-them &&
        git pack-refs &&
        git show-ref >again &&
-       diff all-of-them again
+       test_cmp all-of-them again
 '
 
 test_done
index a0ed0353e69dd6dbc96f1a8e3c9251757b4f408f..b4554041b49d1ab7b56430f2e85b76a23d360fb4 100755 (executable)
@@ -60,7 +60,7 @@ test_expect_success 'verify commits and notes' '
 
 test_expect_success 'remove some commits' '
 
-       git reset --hard HEAD~2 &&
+       git reset --hard HEAD~1 &&
        git reflog expire --expire=now HEAD &&
        git gc --prune=now
 '
@@ -68,7 +68,7 @@ test_expect_success 'remove some commits' '
 test_expect_success 'verify that commits are gone' '
 
        ! git cat-file -p 5ee1c35e83ea47cd3cc4f8cbee0568915fbbbd29 &&
-       git cat-file -p 08341ad9e94faa089d60fd3f523affb25c6da189 &&
+       git cat-file -p 08341ad9e94faa089d60fd3f523affb25c6da189 &&
        git cat-file -p ab5f302035f2e7aaf04265f08b42034c23256e1f
 '
 
@@ -79,11 +79,55 @@ test_expect_success 'verify that notes are still present' '
        git notes show ab5f302035f2e7aaf04265f08b42034c23256e1f
 '
 
+test_expect_success 'prune -n does not remove notes' '
+
+       git notes list > expect &&
+       git notes prune -n &&
+       git notes list > actual &&
+       test_cmp expect actual
+'
+
+cat > expect <<EOF
+5ee1c35e83ea47cd3cc4f8cbee0568915fbbbd29
+EOF
+
+test_expect_success 'prune -n lists prunable notes' '
+
+
+       git notes prune -n > actual &&
+       test_cmp expect actual
+'
+
+
 test_expect_success 'prune notes' '
 
        git notes prune
 '
 
+test_expect_success 'verify that notes are gone' '
+
+       ! git notes show 5ee1c35e83ea47cd3cc4f8cbee0568915fbbbd29 &&
+       git notes show 08341ad9e94faa089d60fd3f523affb25c6da189 &&
+       git notes show ab5f302035f2e7aaf04265f08b42034c23256e1f
+'
+
+test_expect_success 'remove some commits' '
+
+       git reset --hard HEAD~1 &&
+       git reflog expire --expire=now HEAD &&
+       git gc --prune=now
+'
+
+cat > expect <<EOF
+08341ad9e94faa089d60fd3f523affb25c6da189
+EOF
+
+test_expect_success 'prune -v notes' '
+
+       git notes prune -v > actual &&
+       test_cmp expect actual
+'
+
 test_expect_success 'verify that notes are gone' '
 
        ! git notes show 5ee1c35e83ea47cd3cc4f8cbee0568915fbbbd29 &&
diff --git a/t/t3307-notes-man.sh b/t/t3307-notes-man.sh
new file mode 100755 (executable)
index 0000000..3269f2e
--- /dev/null
@@ -0,0 +1,38 @@
+#!/bin/sh
+
+test_description='Examples from the git-notes man page
+
+Make sure the manual is not full of lies.'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+       test_commit A &&
+       test_commit B &&
+       test_commit C
+'
+
+test_expect_success 'example 1: notes to add an Acked-by line' '
+       cat <<-\EOF >expect &&
+           B
+
+       Notes:
+           Acked-by: A C Ker <acker@example.com>
+       EOF
+       git notes add -m "Acked-by: A C Ker <acker@example.com>" B &&
+       git show -s B^{commit} >log &&
+       tail -n 4 log >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'example 2: binary notes' '
+       cp "$TEST_DIRECTORY"/test4012.png .
+       git checkout B &&
+       blob=$(git hash-object -w test4012.png) &&
+       git notes --ref=logo add -C "$blob" &&
+       git notes --ref=logo copy B C &&
+       git notes --ref=logo show C >actual &&
+       test_cmp test4012.png actual
+'
+
+test_done
index dbf7dfba9b55e906d44a35d7b11ca2ceaad7e6f5..e5691bc5edc8d139c7238b59b0690834ba9e85f4 100755 (executable)
@@ -126,9 +126,20 @@ test_expect_success 'Show verbose error when HEAD could not be detached' '
      test_must_fail git rebase topic 2> output.err > output.out &&
      grep "Untracked working tree file .B. would be overwritten" output.err
 '
+rm -f B
+
+test_expect_success 'dump usage when upstream arg is missing' '
+     git checkout -b usage topic &&
+     test_must_fail git rebase 2>error1 &&
+     grep "[Uu]sage" error1 &&
+     test_must_fail git rebase --abort 2>error2 &&
+     grep "No rebase in progress" error2 &&
+     test_must_fail git rebase --onto master 2>error3 &&
+     grep "[Uu]sage" error3 &&
+     ! grep "can.t shift" error3
+'
 
 test_expect_success 'rebase -q is quiet' '
-     rm B &&
      git checkout -b quiet topic &&
      git rebase -q master > output.out 2>&1 &&
      test ! -s output.out
index 8fe14ccc5444df95960bee88e5b79eb68156572b..62e208aadd592ddaa5c519cf328e636d948221c0 100755 (executable)
@@ -81,7 +81,7 @@ test_expect_success 'drop top stash' '
        git stash &&
        git stash drop &&
        git stash list > stashlist2 &&
-       diff stashlist1 stashlist2 &&
+       test_cmp stashlist1 stashlist2 &&
        git stash apply &&
        test 3 = $(cat file) &&
        test 1 = $(git show :file) &&
index 18695ce8218a7b383258eeb0bad84b4d4bde45be..73441a516572dcf826582c04f3fe9d3ad0c1a88c 100755 (executable)
@@ -135,7 +135,7 @@ cmp_diff_files_output () {
     # filesystem.
     sed <"$2" >.test-tmp \
        -e '/^:000000 /d;s/'$x40'\( [MCRNDU][0-9]*\)    /'$z40'\1       /' &&
-    diff "$1" .test-tmp
+    test_cmp "$1" .test-tmp
 }
 
 test_expect_success \
index e92eab09cb3155e602027ce4104fe7ceb7af245e..935d101fe8d880ff426452ca6f611ce8d30b8bc1 100755 (executable)
@@ -438,6 +438,43 @@ test_expect_success 'whitespace-only changes not reported' '
        test_cmp expect actual
 '
 
+cat <<EOF >expect
+diff --git a/x b/z
+similarity index NUM%
+rename from x
+rename to z
+index 380c32a..a97b785 100644
+EOF
+test_expect_success 'whitespace-only changes reported across renames' '
+       git reset --hard &&
+       for i in 1 2 3 4 5 6 7 8 9; do echo "$i$i$i$i$i$i"; done >x &&
+       git add x &&
+       git commit -m "base" &&
+       sed -e "5s/^/ /" x >z &&
+       git rm x &&
+       git add z &&
+       git diff -w -M --cached |
+       sed -e "/^similarity index /s/[0-9][0-9]*/NUM/" >actual &&
+       test_cmp expect actual
+'
+
+cat >expected <<\EOF
+diff --git a/empty b/void
+similarity index 100%
+rename from empty
+rename to void
+EOF
+
+test_expect_success 'rename empty' '
+       git reset --hard &&
+       >empty &&
+       git add empty &&
+       git commit -m empty &&
+       git mv empty void &&
+       git diff -w --cached -M >current &&
+       test_cmp expected current
+'
+
 test_expect_success 'combined diff with autocrlf conversion' '
 
        git reset --hard &&
diff --git a/t/t4043-diff-rename-binary.sh b/t/t4043-diff-rename-binary.sh
new file mode 100755 (executable)
index 0000000..0601281
--- /dev/null
@@ -0,0 +1,45 @@
+#!/bin/sh
+#
+# Copyright (c) 2010 Jakub Narebski, Christian Couder
+#
+
+test_description='Move a binary file'
+
+. ./test-lib.sh
+
+
+test_expect_success 'prepare repository' '
+       git init &&
+       echo foo > foo &&
+       echo "barQ" | q_to_nul > bar &&
+       git add . &&
+       git commit -m "Initial commit"
+'
+
+test_expect_success 'move the files into a "sub" directory' '
+       mkdir sub &&
+       git mv bar foo sub/ &&
+       git commit -m "Moved to sub/"
+'
+
+cat > expected <<\EOF
+ bar => sub/bar |  Bin 5 -> 5 bytes
+ foo => sub/foo |    0
+ 2 files changed, 0 insertions(+), 0 deletions(-)
+
+diff --git a/bar b/sub/bar
+similarity index 100%
+rename from bar
+rename to sub/bar
+diff --git a/foo b/sub/foo
+similarity index 100%
+rename from foo
+rename to sub/foo
+EOF
+
+test_expect_success 'git show -C -C report renames' '
+       git show -C -C --raw --binary --stat | tail -n 12 > current &&
+       test_cmp expected current
+'
+
+test_done
diff --git a/t/t4044-diff-index-unique-abbrev.sh b/t/t4044-diff-index-unique-abbrev.sh
new file mode 100755 (executable)
index 0000000..d5ce72b
--- /dev/null
@@ -0,0 +1,35 @@
+#!/bin/sh
+
+test_description='test unique sha1 abbreviation on "index from..to" line'
+. ./test-lib.sh
+
+cat >expect_initial <<EOF
+100644 blob 51d2738463ea4ca66f8691c91e33ce64b7d41bb1   foo
+EOF
+
+cat >expect_update <<EOF
+100644 blob 51d2738efb4ad8a1e40bed839ab8e116f0a15e47   foo
+EOF
+
+test_expect_success 'setup' '
+       echo 4827 > foo &&
+       git add foo &&
+       git commit -m "initial" &&
+       git cat-file -p HEAD: > actual &&
+       test_cmp expect_initial actual &&
+       echo 11742 > foo &&
+       git commit -a -m "update" &&
+       git cat-file -p HEAD: > actual &&
+       test_cmp expect_update actual
+'
+
+cat >expect <<EOF
+index 51d27384..51d2738e 100644
+EOF
+
+test_expect_success 'diff does not produce ambiguous index line' '
+       git diff HEAD^..HEAD | grep index > actual &&
+       test_cmp expect actual
+'
+
+test_done
index d0af697aa129466172e27456c597f329f9c4f027..8a676a5dcd113418c2bd4ea4aa885fddd5951a3a 100755 (executable)
@@ -44,7 +44,7 @@ test_fix () {
        apply_patch --whitespace=fix || return 1
 
        # find touched lines
-       diff file target | sed -n -e "s/^> //p" >fixed
+       $DIFF file target | sed -n -e "s/^> //p" >fixed
 
        # the changed lines are all expeced to change
        fixed_cnt=$(wc -l <fixed)
@@ -85,14 +85,14 @@ test_expect_success setup '
 test_expect_success 'whitespace=nowarn, default rule' '
 
        apply_patch --whitespace=nowarn &&
-       diff file target
+       test_cmp file target
 
 '
 
 test_expect_success 'whitespace=warn, default rule' '
 
        apply_patch --whitespace=warn &&
-       diff file target
+       test_cmp file target
 
 '
 
@@ -108,7 +108,7 @@ test_expect_success 'whitespace=error-all, no rule' '
 
        git config core.whitespace -trailing,-space-before,-indent &&
        apply_patch --whitespace=error-all &&
-       diff file target
+       test_cmp file target
 
 '
 
@@ -117,7 +117,7 @@ test_expect_success 'whitespace=error-all, no rule (attribute)' '
        git config --unset core.whitespace &&
        echo "target -whitespace" >.gitattributes &&
        apply_patch --whitespace=error-all &&
-       diff file target
+       test_cmp file target
 
 '
 
index 3a8202ea9311b1c90158ad0d115dda985060fdeb..77200c0b2d969d621623f3be8621a3f9925d50a5 100755 (executable)
@@ -27,7 +27,7 @@ test_expect_success 'apply same filename with independent changes' '
        cp same_fn same_fn2 &&
        git reset --hard &&
        git apply patch0 &&
-       diff same_fn same_fn2
+       test_cmp same_fn same_fn2
 '
 
 test_expect_success 'apply same filename with overlapping changes' '
@@ -40,7 +40,7 @@ test_expect_success 'apply same filename with overlapping changes' '
        cp same_fn same_fn2 &&
        git reset --hard &&
        git apply patch0 &&
-       diff same_fn same_fn2
+       test_cmp same_fn same_fn2
 '
 
 test_expect_success 'apply same new filename after rename' '
@@ -54,7 +54,7 @@ test_expect_success 'apply same new filename after rename' '
        cp new_fn new_fn2 &&
        git reset --hard &&
        git apply --index patch1 &&
-       diff new_fn new_fn2
+       test_cmp new_fn new_fn2
 '
 
 test_expect_success 'apply same old filename after rename -- should fail.' '
diff --git a/t/t4205-log-pretty-formats.sh b/t/t4205-log-pretty-formats.sh
new file mode 100755 (executable)
index 0000000..cb9f2bd
--- /dev/null
@@ -0,0 +1,74 @@
+#!/bin/sh
+#
+# Copyright (c) 2010, Will Palmer
+#
+
+test_description='Test pretty formats'
+. ./test-lib.sh
+
+test_expect_success 'set up basic repos' '
+       >foo &&
+       >bar &&
+       git add foo &&
+       test_tick &&
+       git commit -m initial &&
+       git add bar &&
+       test_tick &&
+       git commit -m "add bar"
+'
+
+test_expect_success 'alias builtin format' '
+       git log --pretty=oneline >expected &&
+       git config pretty.test-alias oneline &&
+       git log --pretty=test-alias >actual &&
+       test_cmp expected actual
+'
+
+test_expect_success 'alias masking builtin format' '
+       git log --pretty=oneline >expected &&
+       git config pretty.oneline "%H" &&
+       git log --pretty=oneline >actual &&
+       test_cmp expected actual
+'
+
+test_expect_success 'alias user-defined format' '
+       git log --pretty="format:%h" >expected &&
+       git config pretty.test-alias "format:%h" &&
+       git log --pretty=test-alias >actual &&
+       test_cmp expected actual
+'
+
+test_expect_success 'alias user-defined tformat' '
+       git log --pretty="tformat:%h" >expected &&
+       git config pretty.test-alias "tformat:%h" &&
+       git log --pretty=test-alias >actual &&
+       test_cmp expected actual
+'
+
+test_expect_success 'alias non-existant format' '
+       git config pretty.test-alias format-that-will-never-exist &&
+       test_must_fail git log --pretty=test-alias
+'
+
+test_expect_success 'alias of an alias' '
+       git log --pretty="tformat:%h" >expected &&
+       git config pretty.test-foo "tformat:%h" &&
+       git config pretty.test-bar test-foo &&
+       git log --pretty=test-bar >actual && test_cmp expected actual
+'
+
+test_expect_success 'alias masking an alias' '
+       git log --pretty=format:"Two %H" >expected &&
+       git config pretty.duplicate "format:One %H" &&
+       git config --add pretty.duplicate "format:Two %H" &&
+       git log --pretty=duplicate >actual &&
+       test_cmp expected actual
+'
+
+test_expect_success 'alias loop' '
+       git config pretty.test-foo test-bar &&
+       git config pretty.test-bar test-foo &&
+       test_must_fail git log --pretty=test-foo
+'
+
+test_done
index 169d3ea376f68a0924920b52748ba64356084eab..9cc0a42ea977e184be7af02e05352da8155c4728 100755 (executable)
@@ -67,7 +67,7 @@ test_expect_success 'setup: two scripts for reading pull requests' '
 
        cat <<-\EOT >read-request.sed &&
        #!/bin/sed -nf
-       / in the git repository at:$/! d
+       / in the git repository at:$/!d
        n
        /^$/ n
        s/^[    ]*\(.*\) \([^ ]*\)/please pull\
@@ -102,7 +102,7 @@ test_expect_success 'setup: two scripts for reading pull requests' '
        /^        [a-zA-Z]/ n
        /^[a-zA-Z]* ([0-9]*):\$/ n
        /^\$/ N
-       /^\n[a-zA-Z]* ([0-9]*):\$/! {
+       /^\n[a-zA-Z]* ([0-9]*):\$/!{
                a\\
        SHORTLOG
                D
index 7649b810b1469724ff738fb0bf8b23ea61b37bda..bbb9c1251d97bdaf5bca8f8260e481c3cac56dea 100755 (executable)
@@ -147,7 +147,7 @@ test_expect_success \
            git cat-file $t $object || return 1
         done <obj-list
     } >current &&
-    diff expect current'
+    test_cmp expect current'
 
 test_expect_success \
     'use packed deltified (REF_DELTA) objects' \
@@ -162,7 +162,7 @@ test_expect_success \
            git cat-file $t $object || return 1
         done <obj-list
     } >current &&
-    diff expect current'
+    test_cmp expect current'
 
 test_expect_success \
     'use packed deltified (OFS_DELTA) objects' \
@@ -177,7 +177,7 @@ test_expect_success \
            git cat-file $t $object || return 1
         done <obj-list
     } >current &&
-    diff expect current'
+    test_cmp expect current'
 
 unset GIT_OBJECT_DIRECTORY
 
index 41f17e76939ff0470878018cfbc11b45a8e92137..4c498b1902e4b0c74ea8d6bfc039d583bcc4364e 100755 (executable)
@@ -597,6 +597,94 @@ test_expect_success 'show empty remote' '
        )
 '
 
+test_expect_success 'remote set-branches requires a remote' '
+       test_must_fail git remote set-branches &&
+       test_must_fail git remote set-branches --add
+'
+
+test_expect_success 'remote set-branches' '
+       echo "+refs/heads/*:refs/remotes/scratch/*" >expect.initial &&
+       sort <<-\EOF >expect.add &&
+       +refs/heads/*:refs/remotes/scratch/*
+       +refs/heads/other:refs/remotes/scratch/other
+       EOF
+       sort <<-\EOF >expect.replace &&
+       +refs/heads/maint:refs/remotes/scratch/maint
+       +refs/heads/master:refs/remotes/scratch/master
+       +refs/heads/next:refs/remotes/scratch/next
+       EOF
+       sort <<-\EOF >expect.add-two &&
+       +refs/heads/maint:refs/remotes/scratch/maint
+       +refs/heads/master:refs/remotes/scratch/master
+       +refs/heads/next:refs/remotes/scratch/next
+       +refs/heads/pu:refs/remotes/scratch/pu
+       +refs/heads/t/topic:refs/remotes/scratch/t/topic
+       EOF
+       sort <<-\EOF >expect.setup-ffonly &&
+       refs/heads/master:refs/remotes/scratch/master
+       +refs/heads/next:refs/remotes/scratch/next
+       EOF
+       sort <<-\EOF >expect.respect-ffonly &&
+       refs/heads/master:refs/remotes/scratch/master
+       +refs/heads/next:refs/remotes/scratch/next
+       +refs/heads/pu:refs/remotes/scratch/pu
+       EOF
+
+       git clone .git/ setbranches &&
+       (
+               cd setbranches &&
+               git remote rename origin scratch &&
+               git config --get-all remote.scratch.fetch >config-result &&
+               sort <config-result >../actual.initial &&
+
+               git remote set-branches scratch --add other &&
+               git config --get-all remote.scratch.fetch >config-result &&
+               sort <config-result >../actual.add &&
+
+               git remote set-branches scratch maint master next &&
+               git config --get-all remote.scratch.fetch >config-result &&
+               sort <config-result >../actual.replace &&
+
+               git remote set-branches --add scratch pu t/topic &&
+               git config --get-all remote.scratch.fetch >config-result &&
+               sort <config-result >../actual.add-two &&
+
+               git config --unset-all remote.scratch.fetch &&
+               git config remote.scratch.fetch \
+                       refs/heads/master:refs/remotes/scratch/master &&
+               git config --add remote.scratch.fetch \
+                       +refs/heads/next:refs/remotes/scratch/next &&
+               git config --get-all remote.scratch.fetch >config-result &&
+               sort <config-result >../actual.setup-ffonly &&
+
+               git remote set-branches --add scratch pu &&
+               git config --get-all remote.scratch.fetch >config-result &&
+               sort <config-result >../actual.respect-ffonly
+       ) &&
+       test_cmp expect.initial actual.initial &&
+       test_cmp expect.add actual.add &&
+       test_cmp expect.replace actual.replace &&
+       test_cmp expect.add-two actual.add-two &&
+       test_cmp expect.setup-ffonly actual.setup-ffonly &&
+       test_cmp expect.respect-ffonly actual.respect-ffonly
+'
+
+test_expect_success 'remote set-branches with --mirror' '
+       echo "+refs/*:refs/*" >expect.initial &&
+       echo "+refs/heads/master:refs/heads/master" >expect.replace &&
+       git clone --mirror .git/ setbranches-mirror &&
+       (
+               cd setbranches-mirror &&
+               git remote rename origin scratch &&
+               git config --get-all remote.scratch.fetch >../actual.initial &&
+
+               git remote set-branches scratch heads/master &&
+               git config --get-all remote.scratch.fetch >../actual.replace
+       ) &&
+       test_cmp expect.initial actual.initial &&
+       test_cmp expect.replace actual.replace
+'
+
 test_expect_success 'new remote' '
        git remote add someremote foo &&
        echo foo >expect &&
index 721821ec92e476ed9a16222bc49b72f2fd9b2c68..4eb10f602fdcba354c6d140ed6b910ee89641708 100755 (executable)
@@ -71,7 +71,7 @@ test_expect_success "fetch test for-merge" '
                echo "$one_in_two       "
        } >expected &&
        cut -f -2 .git/FETCH_HEAD >actual &&
-       diff expected actual'
+       test_cmp expected actual'
 
 test_expect_success 'fetch tags when there is no tags' '
 
index 3cf1b3da40a8d99631fabaa2cce4ff2b6d1b52ac..d1912351db7da4a7c457fd44895b5ea076c84e7e 100755 (executable)
@@ -57,12 +57,24 @@ test_expect_success 'dies when no remote specified and no default remotes found'
 
 test_expect_success 'use "origin" when no remote specified' '
 
-       git remote add origin "$(pwd)/.git" &&
-       git ls-remote >actual &&
+       URL="$(pwd)/.git" &&
+       echo "From $URL" >exp_err &&
+
+       git remote add origin "$URL" &&
+       git ls-remote 2>actual_err >actual &&
+
+       test_cmp exp_err actual_err &&
        test_cmp expected.all actual
 
 '
 
+test_expect_success 'suppress "From <url>" with -q' '
+
+       git ls-remote -q 2>actual_err &&
+       test_must_fail test_cmp exp_err actual_err
+
+'
+
 test_expect_success 'use branch.<name>.remote if possible' '
 
        #
@@ -78,10 +90,14 @@ test_expect_success 'use branch.<name>.remote if possible' '
                git show-ref    | sed -e "s/ /  /"
        ) >exp &&
 
-       git remote add other other.git &&
+       URL="other.git" &&
+       echo "From $URL" >exp_err &&
+
+       git remote add other $URL &&
        git config branch.master.remote other &&
 
-       git ls-remote >actual &&
+       git ls-remote 2>actual_err >actual &&
+       test_cmp exp_err actual_err &&
        test_cmp exp actual
 
 '
index dd2ee842e020c23b49ed4e2070c4e31cdb7ac055..319e389ed0dbb9c920d8d619cdf94ab52145e9f4 100755 (executable)
@@ -26,7 +26,7 @@ cd "$D"
 test_expect_success 'checking the results' '
        test -f file &&
        test -f cloned/file &&
-       diff file cloned/file
+       test_cmp file cloned/file
 '
 
 test_expect_success 'pulling into void using master:master' '
index a696b8791b7caa44ae2bd16d6970a791f3a28d3d..044603c26ed62e3ddf03ebb2542f783e4dd7d9ff 100755 (executable)
@@ -32,9 +32,9 @@ test_expect_success 'fsck fails' '
 
 test_expect_success 'upload-pack fails due to error in pack-objects packing' '
 
-       ! echo "0032want $(git rev-parse HEAD)
-00000009done
-0000" | git upload-pack . > /dev/null 2> output.err &&
+       printf "0032want %s\n00000009done\n0000" \
+               $(git rev-parse HEAD) >input &&
+       test_must_fail git upload-pack . <input >/dev/null 2>output.err &&
        grep "unable to read" output.err &&
        grep "pack-objects died" output.err
 '
@@ -51,9 +51,9 @@ test_expect_success 'fsck fails' '
 '
 test_expect_success 'upload-pack fails due to error in rev-list' '
 
-       ! echo "0032want $(git rev-parse HEAD)
-0034shallow $(git rev-parse HEAD^)00000009done
-0000" | git upload-pack . > /dev/null 2> output.err &&
+       printf "0032want %s\n0034shallow %s00000009done\n0000" \
+               $(git rev-parse HEAD) $(git rev-parse HEAD^) >input &&
+       test_must_fail git upload-pack . <input >/dev/null 2>output.err &&
        # pack-objects survived
        grep "Total.*, reused" output.err &&
        # but there was an error, which must have been in rev-list
@@ -62,9 +62,9 @@ test_expect_success 'upload-pack fails due to error in rev-list' '
 
 test_expect_success 'upload-pack fails due to error in pack-objects enumeration' '
 
-       ! echo "0032want $(git rev-parse HEAD)
-00000009done
-0000" | git upload-pack . > /dev/null 2> output.err &&
+       printf "0032want %s\n00000009done\n0000" \
+               $(git rev-parse HEAD) >input &&
+       test_must_fail git upload-pack . <input >/dev/null 2>output.err &&
        grep "bad tree object" output.err &&
        grep "pack-objects died" output.err
 '
index 678cee502de54e5a9c18a43f114446d3392ec7f3..8abb71afcd4d7389260baa6f82ecb9b53bb9524c 100755 (executable)
@@ -176,4 +176,16 @@ test_expect_success 'clone respects global branch.autosetuprebase' '
        )
 '
 
+test_expect_success 'respect url-encoding of file://' '
+       git init x+y &&
+       test_must_fail git clone "file://$PWD/x+y" xy-url &&
+       git clone "file://$PWD/x%2By" xy-url
+'
+
+test_expect_success 'do not respect url-encoding of non-url path' '
+       git init x+y &&
+       test_must_fail git clone x%2By xy-regular &&
+       git clone x+y xy-regular
+'
+
 test_done
index 1c109160690d273451f7a089be42e45f36a3b5bb..895f5595aee9341276e79497b9c4a8736c78e5e7 100755 (executable)
@@ -48,7 +48,7 @@ test_expect_success 'that reference gets used' \
 'cd C &&
 echo "0 objects, 0 kilobytes" > expected &&
 git count-objects > current &&
-diff expected current'
+test_cmp expected current'
 
 cd "$base_dir"
 
@@ -75,7 +75,7 @@ cd "$base_dir"
 test_expect_success 'that reference gets used' \
 'cd D && echo "0 objects, 0 kilobytes" > expected &&
 git count-objects > current &&
-diff expected current'
+test_cmp expected current'
 
 cd "$base_dir"
 
@@ -100,7 +100,7 @@ test_expect_success 'that alternate to origin gets used' \
 'cd C &&
 echo "2 objects" > expected &&
 git count-objects | cut -d, -f1 > current &&
-diff expected current'
+test_cmp expected current'
 
 cd "$base_dir"
 
@@ -116,7 +116,7 @@ test_expect_success 'check objects expected to exist locally' \
 'cd D &&
 echo "5 objects" > expected &&
 git count-objects | cut -d, -f1 > current &&
-diff expected current'
+test_cmp expected current'
 
 cd "$base_dir"
 
index 75a0163c074724513accc9f2a482d72d9e51532c..4ee7b65ce6d86be5f4debec6ca5021231e8d69de 100755 (executable)
@@ -7,9 +7,15 @@ test_description='Test remote-helper import and export commands'
 
 . ./test-lib.sh
 
-if ! test_have_prereq PYTHON
+if test_have_prereq PYTHON && "$PYTHON_PATH" -c '
+import sys
+if sys.hexversion < 0x02040000:
+    sys.exit(1)
+'
 then
-       say 'skipping git remote-testgit tests: requires Python support'
+       :
+else
+       say 'skipping git remote-testgit tests: requires Python 2.4 or newer'
        test_done
 fi
 
diff --git a/t/t6000lib.sh b/t/t6000lib.sh
deleted file mode 100644 (file)
index 985d517..0000000
+++ /dev/null
@@ -1,127 +0,0 @@
-: included from 6002 and others
-
-[ -d .git/refs/tags ] || mkdir -p .git/refs/tags
-
-:> sed.script
-
-# Answer the sha1 has associated with the tag. The tag must exist in .git or .git/refs/tags
-tag()
-{
-       _tag=$1
-       [ -f .git/refs/tags/$_tag ] || error "tag: \"$_tag\" does not exist"
-       cat .git/refs/tags/$_tag
-}
-
-# Generate a commit using the text specified to make it unique and the tree
-# named by the tag specified.
-unique_commit()
-{
-       _text=$1
-        _tree=$2
-       shift 2
-       echo $_text | git commit-tree $(tag $_tree) "$@"
-}
-
-# Save the output of a command into the tag specified. Prepend
-# a substitution script for the tag onto the front of sed.script
-save_tag()
-{
-       _tag=$1
-       [ -n "$_tag" ] || error "usage: save_tag tag commit-args ..."
-       shift 1
-       "$@" >.git/refs/tags/$_tag
-
-        echo "s/$(tag $_tag)/$_tag/g" > sed.script.tmp
-       cat sed.script >> sed.script.tmp
-       rm sed.script
-       mv sed.script.tmp sed.script
-}
-
-# Replace unhelpful sha1 hashses with their symbolic equivalents
-entag()
-{
-       sed -f sed.script
-}
-
-# Execute a command after first saving, then setting the GIT_AUTHOR_EMAIL
-# tag to a specified value. Restore the original value on return.
-as_author()
-{
-       _author=$1
-       shift 1
-        _save=$GIT_AUTHOR_EMAIL
-
-       GIT_AUTHOR_EMAIL="$_author"
-       export GIT_AUTHOR_EMAIL
-       "$@"
-       if test -z "$_save"
-       then
-               unset GIT_AUTHOR_EMAIL
-       else
-               GIT_AUTHOR_EMAIL="$_save"
-               export GIT_AUTHOR_EMAIL
-       fi
-}
-
-commit_date()
-{
-        _commit=$1
-       git cat-file commit $_commit | sed -n "s/^committer .*> \([0-9]*\) .*/\1/p"
-}
-
-on_committer_date()
-{
-    _date=$1
-    shift 1
-    GIT_COMMITTER_DATE="$_date"
-    export GIT_COMMITTER_DATE
-    "$@"
-    unset GIT_COMMITTER_DATE
-}
-
-# Execute a command and suppress any error output.
-hide_error()
-{
-       "$@" 2>/dev/null
-}
-
-check_output()
-{
-       _name=$1
-       shift 1
-       if eval "$*" | entag > $_name.actual
-       then
-               diff $_name.expected $_name.actual
-       else
-               return 1;
-       fi
-}
-
-# Turn a reasonable test description into a reasonable test name.
-# All alphanums translated into -'s which are then compressed and stripped
-# from front and back.
-name_from_description()
-{
-       perl -pe '
-               s/[^A-Za-z0-9.]/-/g;
-               s/-+/-/g;
-               s/-$//;
-               s/^-//;
-               y/A-Z/a-z/;
-       '
-}
-
-
-# Execute the test described by the first argument, by eval'ing
-# command line specified in the 2nd argument. Check the status code
-# is zero and that the output matches the stream read from
-# stdin.
-test_output_expect_success()
-{
-       _description=$1
-        _test=$2
-        [ $# -eq 2 ] || error "usage: test_output_expect_success description test <<EOF ... EOF"
-        _name=$(echo $_description | name_from_description)
-       cat > $_name.expected
-       test_expect_success "$_description" "check_output $_name \"$_test\""
-}
index b2131cdacd93e0b62f4ef8fdc62b6a81c6aef6fc..fc57e7d3fd69c60144ee3fb3f66b252a67369b42 100755 (executable)
@@ -84,7 +84,7 @@ check () {
                git rev-list --parents --pretty=raw $arg |
                sed -n -e 's/^commit //p' >test.actual
        fi
-       diff test.expect test.actual
+       test_cmp test.expect test.actual
 }
 
 for type in basic parents parents-raw
index b4e8fbaa5e6f2a56094c05ca505630669a51e101..fb07536a0f5b7e1000cf4945f55be3305df88e90 100755 (executable)
@@ -5,7 +5,7 @@
 test_description='Tests git rev-list --bisect functionality'
 
 . ./test-lib.sh
-. "$TEST_DIRECTORY"/t6000lib.sh # t6xxx specific functions
+. "$TEST_DIRECTORY"/lib-t6000.sh # t6xxx specific functions
 
 # usage: test_bisection max-diff bisect-option head ^prune...
 #
index 2c73f2da7b0a1f560bfd41376b587d1c91b18615..e4c52b0214b5028e8c3db035dc96ca3285e61506 100755 (executable)
@@ -6,7 +6,7 @@
 test_description='Tests git rev-list --topo-order functionality'
 
 . ./test-lib.sh
-. "$TEST_DIRECTORY"/t6000lib.sh # t6xxx specific functions
+. "$TEST_DIRECTORY"/lib-t6000.sh # t6xxx specific functions
 
 list_duplicates()
 {
index e3f7ae8120aa2a46b25dd3830597cb863a5f5e20..b66544b76d545a396ef068438f3980b3f544efdd 100755 (executable)
@@ -280,7 +280,7 @@ test_expect_success 'updated working tree file should prevent the merge' '
                echo "BAD: should have complained"
                return 1
        }
-       diff M M.saved || {
+       test_cmp M M.saved || {
                echo "BAD: should have left M intact"
                return 1
        }
@@ -301,7 +301,7 @@ test_expect_success 'updated working tree file should prevent the merge' '
                echo "BAD: should have complained"
                return 1
        }
-       diff M M.saved || {
+       test_cmp M M.saved || {
                echo "BAD: should have left M intact"
                return 1
        }
index f105fab98e2d493ab489d345676101fc13096c22..e673c25e943f77430d7f61b0ab9e0b21e38e6915 100755 (executable)
@@ -6,7 +6,7 @@
 test_description='Test git rev-parse with different parent options'
 
 . ./test-lib.sh
-. "$TEST_DIRECTORY"/t6000lib.sh # t6xxx specific functions
+. "$TEST_DIRECTORY"/lib-t6000.sh # t6xxx specific functions
 
 date >path0
 git update-index --add path0
index e249c3ed4176b4934ae57541ab8b671801968eae..8a6322765c965bfc3a9158cc14312fdf03295090 100755 (executable)
@@ -60,7 +60,7 @@ do
                        echo ${HC}file:5:foo_mmap bar mmap baz
                } >expected &&
                git grep -n -w -e mmap $H >actual &&
-               diff expected actual
+               test_cmp expected actual
        '
 
        test_expect_success "grep -w $L (w)" '
@@ -74,7 +74,7 @@ do
                        echo ${HC}x:1:x x xx x
                } >expected &&
                git grep -n -w -e "x xx* x" $H >actual &&
-               diff expected actual
+               test_cmp expected actual
        '
 
        test_expect_success "grep -w $L (y-1)" '
@@ -82,7 +82,7 @@ do
                        echo ${HC}y:1:y yy
                } >expected &&
                git grep -n -w -e "^y" $H >actual &&
-               diff expected actual
+               test_cmp expected actual
        '
 
        test_expect_success "grep -w $L (y-2)" '
@@ -93,7 +93,7 @@ do
                        cat actual
                        false
                else
-                       diff expected actual
+                       test_cmp expected actual
                fi
        '
 
@@ -105,14 +105,14 @@ do
                        cat actual
                        false
                else
-                       diff expected actual
+                       test_cmp expected actual
                fi
        '
 
        test_expect_success "grep $L (t-1)" '
                echo "${HC}t/t:1:test" >expected &&
                git grep -n -e test $H >actual &&
-               diff expected actual
+               test_cmp expected actual
        '
 
        test_expect_success "grep $L (t-2)" '
@@ -121,7 +121,7 @@ do
                        cd t &&
                        git grep -n -e test $H
                ) >actual &&
-               diff expected actual
+               test_cmp expected actual
        '
 
        test_expect_success "grep $L (t-3)" '
@@ -130,7 +130,7 @@ do
                        cd t &&
                        git grep --full-name -n -e test $H
                ) >actual &&
-               diff expected actual
+               test_cmp expected actual
        '
 
        test_expect_success "grep -c $L (no /dev/null)" '
index 5257f4d261c2060b881d2649034232f76f4ed9b7..25b6d2e67c443efbc9e20edb43ef2df89a40db3c 100755 (executable)
@@ -38,7 +38,7 @@ test_expect_success setup '
        test_commit "$msg" &&
        echo "$msg" >expect &&
        git show -s --format=%s > actual &&
-       diff actual expect
+       test_cmp actual expect
 
 '
 
@@ -85,7 +85,7 @@ do
                git --exec-path=. commit --amend &&
                git show -s --pretty=oneline |
                sed -e "s/^[0-9a-f]* //" >actual &&
-               diff actual expect
+               test_cmp actual expect
        '
 done
 
@@ -107,7 +107,7 @@ do
                git --exec-path=. commit --amend &&
                git show -s --pretty=oneline |
                sed -e "s/^[0-9a-f]* //" >actual &&
-               diff actual expect
+               test_cmp actual expect
        '
 done
 
index 3bc7a2a796bdb97702eaaf92e26b189cc4204c90..a6f3677731af38293e4bbae2b14e46d7e3a9107e 100755 (executable)
@@ -40,7 +40,7 @@ else
 fi
 
 test_expect_success 'setup' '
-       unset GIT_PAGER GIT_PAGER_IN_USE &&
+       unset GIT_PAGER GIT_PAGER_IN_USE;
        test_might_fail git config --unset core.pager &&
 
        PAGER="cat >paginated.out" &&
@@ -159,7 +159,7 @@ test_expect_success 'color when writing to a file intended for a pager' '
 '
 
 test_expect_success 'determine default pager' '
-       unset PAGER GIT_PAGER &&
+       unset PAGER GIT_PAGER;
        test_might_fail git config --unset core.pager ||
        cleanup_fail &&
 
@@ -173,7 +173,7 @@ then
 fi
 
 test_expect_success SIMPLEPAGER 'default pager is used by default' '
-       unset PAGER GIT_PAGER &&
+       unset PAGER GIT_PAGER;
        test_might_fail git config --unset core.pager &&
        rm -f default_pager_used ||
        cleanup_fail &&
@@ -192,7 +192,7 @@ test_expect_success SIMPLEPAGER 'default pager is used by default' '
 '
 
 test_expect_success TTY 'PAGER overrides default pager' '
-       unset GIT_PAGER &&
+       unset GIT_PAGER;
        test_might_fail git config --unset core.pager &&
        rm -f PAGER_used ||
        cleanup_fail &&
@@ -204,7 +204,7 @@ test_expect_success TTY 'PAGER overrides default pager' '
 '
 
 test_expect_success TTY 'core.pager overrides PAGER' '
-       unset GIT_PAGER &&
+       unset GIT_PAGER;
        rm -f core.pager_used ||
        cleanup_fail &&
 
diff --git a/t/t7008-grep-binary.sh b/t/t7008-grep-binary.sh
new file mode 100755 (executable)
index 0000000..eb8ca88
--- /dev/null
@@ -0,0 +1,102 @@
+#!/bin/sh
+
+test_description='git grep in binary files'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' "
+       printf 'binary\000file\n' >a &&
+       git add a &&
+       git commit -m.
+"
+
+test_expect_success 'git grep ina a' '
+       echo Binary file a matches >expect &&
+       git grep ina a >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'git grep -ah ina a' '
+       git grep -ah ina a >actual &&
+       test_cmp a actual
+'
+
+test_expect_success 'git grep -I ina a' '
+       : >expect &&
+       test_must_fail git grep -I ina a >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'git grep -c ina a' '
+       echo a:1 >expect &&
+       git grep -c ina a >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'git grep -l ina a' '
+       echo a >expect &&
+       git grep -l ina a >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'git grep -L bar a' '
+       echo a >expect &&
+       git grep -L bar a >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'git grep -q ina a' '
+       : >expect &&
+       git grep -q ina a >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'git grep -F ile a' '
+       git grep -F ile a
+'
+
+test_expect_success 'git grep -Fi iLE a' '
+       git grep -Fi iLE a
+'
+
+# This test actually passes on platforms where regexec() supports the
+# flag REG_STARTEND.
+test_expect_failure 'git grep ile a' '
+       git grep ile a
+'
+
+test_expect_failure 'git grep .fi a' '
+       git grep .fi a
+'
+
+test_expect_success 'git grep -F y<NUL>f a' "
+       printf 'y\000f' >f &&
+       git grep -f f -F a
+"
+
+test_expect_success 'git grep -F y<NUL>x a' "
+       printf 'y\000x' >f &&
+       test_must_fail git grep -f f -F a
+"
+
+test_expect_success 'git grep -Fi Y<NUL>f a' "
+       printf 'Y\000f' >f &&
+       git grep -f f -Fi a
+"
+
+test_expect_failure 'git grep -Fi Y<NUL>x a' "
+       printf 'Y\000x' >f &&
+       test_must_fail git grep -f f -Fi a
+"
+
+test_expect_success 'git grep y<NUL>f a' "
+       printf 'y\000f' >f &&
+       git grep -f f a
+"
+
+test_expect_failure 'git grep y<NUL>x a' "
+       printf 'y\000x' >f &&
+       test_must_fail git grep -f f a
+"
+
+test_done
index d8a7c798525728ddc8fc5fa9bd8335d8d1f0a710..0335a9a158ab507b2e37e4d7642a69ba4344c0ad 100755 (executable)
@@ -103,14 +103,10 @@ test_expect_success 'git ls-files (relative #3)' '
        git add a &&
        (
                cd a/b &&
-               if git ls-files "../e/f"
-               then
-                       echo Gaah, should have failed
-                       exit 1
-               else
-                       : happy
-               fi
-       )
+               git ls-files "../e/f"
+       )  >current &&
+       echo ../e/f >expect &&
+       test_cmp expect current
 
 '
 
index 2a527750ce4a81bc168b8ad3d8b09174f487b142..db9365b6454b18eb07f5c0798e13e18d696498c6 100755 (executable)
@@ -59,11 +59,13 @@ test_expect_success 'setup a submodule tree' '
 sub1sha1=$(cd super/sub1 && git rev-parse HEAD)
 sub3sha1=$(cd super/sub3 && git rev-parse HEAD)
 
+pwd=$(pwd)
+
 cat > expect <<EOF
 Entering 'sub1'
-foo1-sub1-$sub1sha1
+$pwd/clone-foo1-sub1-$sub1sha1
 Entering 'sub3'
-foo3-sub3-$sub3sha1
+$pwd/clone-foo3-sub3-$sub3sha1
 EOF
 
 test_expect_success 'test basic "submodule foreach" usage' '
@@ -71,7 +73,9 @@ test_expect_success 'test basic "submodule foreach" usage' '
        (
                cd clone &&
                git submodule update --init -- sub1 sub3 &&
-               git submodule foreach "echo \$name-\$path-\$sha1" > ../actual
+               git submodule foreach "echo \$toplevel-\$name-\$path-\$sha1" > ../actual &&
+               git config foo.bar zar &&
+               git submodule foreach "git config --file \"\$toplevel/.git/config\" foo.bar"
        ) &&
        test_cmp expect actual
 '
index 008d5711b818a315136290c141c830cdf5af80a0..9e081073fbed00da362137446d78a7be7f86350b 100755 (executable)
@@ -107,13 +107,32 @@ A  dir2/added
 ?? untracked
 EOF
 
-test_expect_success 'status -s (2)' '
+test_expect_success 'status -s' '
 
        git status -s >output &&
        test_cmp expect output
 
 '
 
+cat >expect <<\EOF
+## master
+ M dir1/modified
+A  dir2/added
+?? dir1/untracked
+?? dir2/modified
+?? dir2/untracked
+?? expect
+?? output
+?? untracked
+EOF
+
+test_expect_success 'status -s -b' '
+
+       git status -s -b >output &&
+       test_cmp expect output
+
+'
+
 cat >expect <<EOF
 # On branch master
 # Changes to be committed:
@@ -436,6 +455,25 @@ test_expect_success 'status -s with color.status' '
 
 '
 
+cat >expect <<\EOF
+## <GREEN>master<RESET>
+ <RED>M<RESET> dir1/modified
+<GREEN>A<RESET>  dir2/added
+<BLUE>??<RESET> dir1/untracked
+<BLUE>??<RESET> dir2/modified
+<BLUE>??<RESET> dir2/untracked
+<BLUE>??<RESET> expect
+<BLUE>??<RESET> output
+<BLUE>??<RESET> untracked
+EOF
+
+test_expect_success 'status -s -b with color.status' '
+
+       git status -s -b | test_decode_color >output &&
+       test_cmp expect output
+
+'
+
 cat >expect <<\EOF
  M dir1/modified
 A  dir2/added
@@ -469,6 +507,13 @@ test_expect_success 'status --porcelain ignores color.status' '
 git config --unset color.status
 git config --unset color.ui
 
+test_expect_success 'status --porcelain ignores -b' '
+
+       git status --porcelain -b >output &&
+       test_cmp expect output
+
+'
+
 cat >expect <<\EOF
 # On branch master
 # Changes to be committed:
index d52c060b061b3e0af9d9779366f4cc85a8009851..3ea33db6c73df763c22ccfab9f0cdcb2bbaef4d7 100755 (executable)
@@ -83,6 +83,52 @@ test_expect_success '--amend option copies authorship' '
        test_cmp expect actual
 '
 
+sha1_file() {
+       echo "$*" | sed "s#..#.git/objects/&/#"
+}
+remove_object() {
+       rm -f $(sha1_file "$*")
+}
+no_reflog() {
+       cp .git/config .git/config.saved &&
+       echo "[core] logallrefupdates = false" >>.git/config &&
+       test_when_finished "mv -f .git/config.saved .git/config" &&
+
+       if test -e .git/logs
+       then
+               mv .git/logs . &&
+               test_when_finished "mv logs .git/"
+       fi
+}
+
+test_expect_success '--amend option with empty author' '
+       git cat-file commit Initial >tmp &&
+       sed "s/author [^<]* </author  </" tmp >empty-author &&
+       no_reflog &&
+       sha=$(git hash-object -t commit -w empty-author) &&
+       test_when_finished "remove_object $sha" &&
+       git checkout $sha &&
+       test_when_finished "git checkout Initial" &&
+       echo "Empty author test" >>foo &&
+       test_tick &&
+       ! git commit -a -m "empty author" --amend 2>err &&
+       grep "empty ident" err
+'
+
+test_expect_success '--amend option with missing author' '
+       git cat-file commit Initial >tmp &&
+       sed "s/author [^<]* </author </" tmp >malformed &&
+       no_reflog &&
+       sha=$(git hash-object -t commit -w malformed) &&
+       test_when_finished "remove_object $sha" &&
+       git checkout $sha &&
+       test_when_finished "git checkout Initial" &&
+       echo "Missing author test" >>foo &&
+       test_tick &&
+       ! git commit -a -m "malformed author" --amend 2>err &&
+       grep "empty ident" err
+'
+
 test_expect_success '--reset-author makes the commit ours even with --amend option' '
        git checkout Initial &&
        echo "Test 6" >>foo &&
index 269cfdf267443f67ef8c6c921610bc856f4e506b..9114785ef7c850ae2d393bed3565e13f11339ba0 100755 (executable)
@@ -6,6 +6,15 @@ Testing merge when using a custom message for the merge commit.'
 
 . ./test-lib.sh
 
+create_merge_msgs() {
+       echo >exp.subject "custom message"
+
+       cp exp.subject exp.log &&
+       echo >>exp.log "" &&
+       echo >>exp.log "* commit 'c2':" &&
+       echo >>exp.log "  c2"
+}
+
 test_expect_success 'setup' '
        echo c0 > c0.c &&
        git add c0.c &&
@@ -19,16 +28,23 @@ test_expect_success 'setup' '
        echo c2 > c2.c &&
        git add c2.c &&
        git commit -m c2 &&
-       git tag c2
+       git tag c2 &&
+       create_merge_msgs
 '
 
 
 test_expect_success 'merge c2 with a custom message' '
        git reset --hard c1 &&
-       echo >expected "custom message" &&
-       git merge -m "custom message" c2 &&
+       git merge -m "$(cat exp.subject)" c2 &&
+       git cat-file commit HEAD | sed -e "1,/^$/d" >actual &&
+       test_cmp exp.subject actual
+'
+
+test_expect_success 'merge --log appends to custom message' '
+       git reset --hard c1 &&
+       git merge --log -m "$(cat exp.subject)" c2 &&
        git cat-file commit HEAD | sed -e "1,/^$/d" >actual &&
-       test_cmp expected actual
+       test_cmp exp.log actual
 '
 
 test_done
index b9224bdb20a397f40f1504684e1144428cf8a24e..1e9a2eb12bee55474f9bc10a7e31f5c01d6403f8 100755 (executable)
@@ -14,10 +14,22 @@ compare_git_head_with () {
        test_cmp current "$1"
 }
 
+a_utf8_locale=$(locale -a | sed -n '/\.[uU][tT][fF]-*8$/{
+       p
+       q
+}')
+
+if test -n "$a_utf8_locale"
+then
+       test_set_prereq UTF8
+else
+       say "UTF-8 locale not available, some tests are skipped"
+fi
+
 compare_svn_head_with () {
        # extract just the log message and strip out committer info.
        # don't use --limit here since svn 1.1.x doesn't have it,
-       LC_ALL=en_US.UTF-8 svn log `git svn info --url` | perl -w -e '
+       LC_ALL="$a_utf8_locale" svn log `git svn info --url` | perl -w -e '
                use bytes;
                $/ = ("-"x72) . "\n";
                my @x = <STDIN>;
@@ -69,12 +81,6 @@ do
        '
 done
 
-if locale -a |grep -q en_US.utf8; then
-       test_set_prereq UTF8
-else
-       say "UTF-8 locale not available, test skipped"
-fi
-
 test_expect_success UTF8 'ISO-8859-1 should match UTF-8 in svn' '
        (
                cd ISO8859-1 &&
index fc3795dc98803bd98e2ebd6f38a249c331038d54..61bcb8fc86bc8b771a4843197a40fa38f350a1ff 100755 (executable)
@@ -63,10 +63,10 @@ test_expect_success \
      check_entries B "newfile2.txt/1.1/" &&
      check_entries C "newfile3.png/1.1/-kb" &&
      check_entries D "newfile4.png/1.1/-kb" &&
-     diff A/newfile1.txt ../A/newfile1.txt &&
-     diff B/newfile2.txt ../B/newfile2.txt &&
-     diff C/newfile3.png ../C/newfile3.png &&
-     diff D/newfile4.png ../D/newfile4.png
+     test_cmp A/newfile1.txt ../A/newfile1.txt &&
+     test_cmp B/newfile2.txt ../B/newfile2.txt &&
+     test_cmp C/newfile3.png ../C/newfile3.png &&
+     test_cmp D/newfile4.png ../D/newfile4.png
      )'
 
 test_expect_success \
@@ -89,10 +89,10 @@ test_expect_success \
      check_entries D "newfile4.png/1.2/-kb" &&
      check_entries E "newfile5.txt/1.1/" &&
      check_entries F "newfile6.png/1.1/-kb" &&
-     diff A/newfile1.txt ../A/newfile1.txt &&
-     diff D/newfile4.png ../D/newfile4.png &&
-     diff E/newfile5.txt ../E/newfile5.txt &&
-     diff F/newfile6.png ../F/newfile6.png
+     test_cmp A/newfile1.txt ../A/newfile1.txt &&
+     test_cmp D/newfile4.png ../D/newfile4.png &&
+     test_cmp E/newfile5.txt ../E/newfile5.txt &&
+     test_cmp F/newfile6.png ../F/newfile6.png
      )'
 
 # Should fail (but only on the git cvsexportcommit stage)
@@ -137,9 +137,9 @@ test_expect_success \
      check_entries D "" &&
      check_entries E "newfile5.txt/1.1/" &&
      check_entries F "newfile6.png/1.1/-kb" &&
-     diff A/newfile1.txt ../A/newfile1.txt &&
-     diff E/newfile5.txt ../E/newfile5.txt &&
-     diff F/newfile6.png ../F/newfile6.png
+     test_cmp A/newfile1.txt ../A/newfile1.txt &&
+     test_cmp E/newfile5.txt ../E/newfile5.txt &&
+     test_cmp F/newfile6.png ../F/newfile6.png
      )'
 
 test_expect_success \
@@ -155,8 +155,8 @@ test_expect_success \
      check_entries D "" &&
      check_entries E "newfile5.txt/1.1/" &&
      check_entries F "newfile6.png/1.1/-kb" &&
-     diff E/newfile5.txt ../E/newfile5.txt &&
-     diff F/newfile6.png ../F/newfile6.png
+     test_cmp E/newfile5.txt ../E/newfile5.txt &&
+     test_cmp F/newfile6.png ../F/newfile6.png
      )'
 
 test_expect_success \
index daef2d6c230fb617395feeff6cf330b74ad57832..86395065cfb8827ba5a999aa97cd5552a72322e3 100755 (executable)
@@ -48,7 +48,9 @@ test_expect_success 'setup' '
   git pull secondroot master &&
   git clone -q --bare "$WORKDIR/.git" "$SERVERDIR" >/dev/null 2>&1 &&
   GIT_DIR="$SERVERDIR" git config --bool gitcvs.enabled true &&
-  GIT_DIR="$SERVERDIR" git config gitcvs.logfile "$SERVERDIR/gitcvs.log"
+  GIT_DIR="$SERVERDIR" git config gitcvs.logfile "$SERVERDIR/gitcvs.log" &&
+  GIT_DIR="$SERVERDIR" git config gitcvs.authdb "$SERVERDIR/auth.db" &&
+  echo cvsuser:cvGVEarMLnhlA > "$SERVERDIR/auth.db"
 '
 
 # note that cvs doesn't accept absolute pathnames
@@ -94,6 +96,14 @@ git
 END VERIFICATION REQUEST
 EOF
 
+cat >login-git-ok <<EOF
+BEGIN VERIFICATION REQUEST
+$SERVERDIR
+cvsuser
+Ah<Z:yZZ30 e
+END VERIFICATION REQUEST
+EOF
+
 test_expect_success 'pserver authentication' \
   'cat request-anonymous | git-cvsserver pserver >log 2>&1 &&
    sed -ne \$p log | grep "^I LOVE YOU\$"'
@@ -107,6 +117,10 @@ test_expect_success 'pserver authentication failure (non-anonymous user)' \
    fi &&
    sed -ne \$p log | grep "^I HATE YOU\$"'
 
+test_expect_success 'pserver authentication success (non-anonymous user with password)' \
+  'cat login-git-ok | git-cvsserver pserver >log 2>&1 &&
+   sed -ne \$p log | grep "^I LOVE YOU\$"'
+
 test_expect_success 'pserver authentication (login)' \
   'cat login-anonymous | git-cvsserver pserver >log 2>&1 &&
    sed -ne \$p log | grep "^I LOVE YOU\$"'
@@ -435,7 +449,7 @@ test_expect_success 'cvs update (-p)' '
     rm -f failures &&
     for i in merge no-lf empty really-empty; do
         GIT_CONFIG="$git_config" cvs update -p "$i" >$i.out
-        diff $i.out ../$i >>failures 2>&1
+       test_cmp $i.out ../$i >>failures 2>&1
     done &&
     test -z "$(cat failures)"
 '
index 63b6b060e64948771319294251838394264c84a9..4f2b9b062b08302aedab42dcdd0a33776559c05e 100755 (executable)
@@ -647,4 +647,33 @@ test_expect_success \
         gitweb_run "p=.git;a=summary"'
 test_debug 'cat gitweb.log'
 
+# ----------------------------------------------------------------------
+# syntax highlighting
+
+cat >>gitweb_config.perl <<\EOF
+$feature{'highlight'}{'override'} = 1;
+EOF
+
+highlight --version >/dev/null 2>&1
+if [ $? -eq 127 ]; then
+       say "Skipping syntax highlighting test, because 'highlight' was not found"
+else
+       test_set_prereq HIGHLIGHT
+fi
+
+test_expect_success HIGHLIGHT \
+       'syntax highlighting (no highlight)' \
+       'git config gitweb.highlight yes &&
+        gitweb_run "p=.git;a=blob;f=file"'
+test_debug 'cat gitweb.log'
+
+test_expect_success HIGHLIGHT \
+       'syntax highlighting (highlighted)' \
+       'git config gitweb.highlight yes &&
+        echo "#!/usr/bin/sh" > test.sh &&
+        git add test.sh &&
+        git commit -m "Add test.sh" &&
+        gitweb_run "p=.git;a=blob;f=test.sh"'
+test_debug 'cat gitweb.log'
+
 test_done
index 454880ac7d281d901156136900814dee9aae46c5..367f0537cd87c29bfdca15b4744f1aef3ddd8d7e 100644 (file)
@@ -75,7 +75,6 @@ export GIT_MERGE_VERBOSITY
 export GIT_AUTHOR_EMAIL GIT_AUTHOR_NAME
 export GIT_COMMITTER_EMAIL GIT_COMMITTER_NAME
 export EDITOR
-GIT_TEST_CMP=${GIT_TEST_CMP:-diff -u}
 
 # Protect ourselves from common misconfiguration to export
 # CDPATH into the environment
@@ -740,6 +739,16 @@ export PATH GIT_EXEC_PATH GIT_TEMPLATE_DIR GIT_CONFIG_NOSYSTEM GIT_CONFIG_NOGLOB
 
 . ../GIT-BUILD-OPTIONS
 
+if test -z "$GIT_TEST_CMP"
+then
+       if test -n "$GIT_TEST_CMP_USE_COPIED_CONTEXT"
+       then
+               GIT_TEST_CMP="$DIFF -c"
+       else
+               GIT_TEST_CMP="$DIFF -u"
+       fi
+fi
+
 GITPERLLIB=$(pwd)/../perl/blib/lib:$(pwd)/../perl/blib/arch/auto/Git
 export GITPERLLIB
 test -d ../templates/blt || {
diff --git a/trace.c b/trace.c
index 4229ae1231d69aedd9f1aa8350989ddbe2bdb845..1e560cb0b977e19beeb095497c8e4f6280b7f0d6 100644 (file)
--- a/trace.c
+++ b/trace.c
 #include "cache.h"
 #include "quote.h"
 
+void do_nothing(size_t unused)
+{
+}
+
 /* Get a trace file descriptor from GIT_TRACE env variable. */
 static int get_trace_fd(int *need_close)
 {
@@ -72,6 +76,7 @@ void trace_printf(const char *fmt, ...)
        if (!fd)
                return;
 
+       set_try_to_free_routine(do_nothing);    /* is never reset */
        strbuf_init(&buf, 64);
        va_start(ap, fmt);
        len = vsnprintf(buf.buf, strbuf_avail(&buf), fmt, ap);
@@ -103,6 +108,7 @@ void trace_argv_printf(const char **argv, const char *fmt, ...)
        if (!fd)
                return;
 
+       set_try_to_free_routine(do_nothing);    /* is never reset */
        strbuf_init(&buf, 64);
        va_start(ap, fmt);
        len = vsnprintf(buf.buf, strbuf_avail(&buf), fmt, ap);
index 8ce39364a1e58b338bc45a4eb524b637ce3a8881..4dba6f8815a80093a8ac9edc226d99bba1bb6394 100644 (file)
@@ -9,6 +9,7 @@
 #include "dir.h"
 #include "refs.h"
 #include "branch.h"
+#include "url.h"
 
 /* rsync support */
 
@@ -871,54 +872,6 @@ static int is_file(const char *url)
        return S_ISREG(buf.st_mode);
 }
 
-static int isurlschemechar(int first_flag, int ch)
-{
-       /*
-        * The set of valid URL schemes, as per STD66 (RFC3986) is
-        * '[A-Za-z][A-Za-z0-9+.-]*'. But use sightly looser check
-        * of '[A-Za-z0-9][A-Za-z0-9+.-]*' because earlier version
-        * of check used '[A-Za-z0-9]+' so not to break any remote
-        * helpers.
-        */
-       int alphanumeric, special;
-       alphanumeric = ch > 0 && isalnum(ch);
-       special = ch == '+' || ch == '-' || ch == '.';
-       return alphanumeric || (!first_flag && special);
-}
-
-static int is_url(const char *url)
-{
-       const char *url2, *first_slash;
-
-       if (!url)
-               return 0;
-       url2 = url;
-       first_slash = strchr(url, '/');
-
-       /* Input with no slash at all or slash first can't be URL. */
-       if (!first_slash || first_slash == url)
-               return 0;
-       /* Character before must be : and next must be /. */
-       if (first_slash[-1] != ':' || first_slash[1] != '/')
-               return 0;
-       /* There must be something before the :// */
-       if (first_slash == url + 1)
-               return 0;
-       /*
-        * Check all characters up to first slash - 1. Only alphanum
-        * is allowed.
-        */
-       url2 = url;
-       while (url2 < first_slash - 1) {
-               if (!isurlschemechar(url2 == url, (unsigned char)*url2))
-                       return 0;
-               url2++;
-       }
-
-       /* Valid enough. */
-       return 1;
-}
-
 static int external_specification_len(const char *url)
 {
        return strchr(url, ':') - url;
@@ -946,7 +899,7 @@ struct transport *transport_get(struct remote *remote, const char *url)
        if (url) {
                const char *p = url;
 
-               while (isurlschemechar(p == url, *p))
+               while (is_urlschemechar(p == url, *p))
                        p++;
                if (!prefixcmp(p, "::"))
                        helper = xstrndup(url, p - url);
index c29a9e067ff362063d6626e8e4d1e4466d63b8af..85045fd03fd7ab2a2ad8daf5ec584e010372673b 100644 (file)
@@ -279,9 +279,11 @@ static void add_same_unmerged(struct cache_entry *ce,
 static int unpack_index_entry(struct cache_entry *ce,
                              struct unpack_trees_options *o)
 {
-       struct cache_entry *src[5] = { ce, NULL, };
+       struct cache_entry *src[5] = { NULL };
        int ret;
 
+       src[0] = ce;
+
        mark_ce_used(ce, o);
        if (ce_stage(ce)) {
                if (o->skip_unmerged) {
@@ -854,7 +856,7 @@ static int verify_uptodate_1(struct cache_entry *ce,
 {
        struct stat st;
 
-       if (o->index_only || (!ce_skip_worktree(ce) && (o->reset || ce_uptodate(ce))))
+       if (o->index_only || (!((ce->ce_flags & CE_VALID) || ce_skip_worktree(ce)) && (o->reset || ce_uptodate(ce))))
                return 0;
 
        if (!lstat(ce->name, &st)) {
diff --git a/url.c b/url.c
new file mode 100644 (file)
index 0000000..cd32b92
--- /dev/null
+++ b/url.c
@@ -0,0 +1,118 @@
+#include "cache.h"
+
+int is_urlschemechar(int first_flag, int ch)
+{
+       /*
+        * The set of valid URL schemes, as per STD66 (RFC3986) is
+        * '[A-Za-z][A-Za-z0-9+.-]*'. But use sightly looser check
+        * of '[A-Za-z0-9][A-Za-z0-9+.-]*' because earlier version
+        * of check used '[A-Za-z0-9]+' so not to break any remote
+        * helpers.
+        */
+       int alphanumeric, special;
+       alphanumeric = ch > 0 && isalnum(ch);
+       special = ch == '+' || ch == '-' || ch == '.';
+       return alphanumeric || (!first_flag && special);
+}
+
+int is_url(const char *url)
+{
+       const char *url2, *first_slash;
+
+       if (!url)
+               return 0;
+       url2 = url;
+       first_slash = strchr(url, '/');
+
+       /* Input with no slash at all or slash first can't be URL. */
+       if (!first_slash || first_slash == url)
+               return 0;
+       /* Character before must be : and next must be /. */
+       if (first_slash[-1] != ':' || first_slash[1] != '/')
+               return 0;
+       /* There must be something before the :// */
+       if (first_slash == url + 1)
+               return 0;
+       /*
+        * Check all characters up to first slash - 1. Only alphanum
+        * is allowed.
+        */
+       url2 = url;
+       while (url2 < first_slash - 1) {
+               if (!is_urlschemechar(url2 == url, (unsigned char)*url2))
+                       return 0;
+               url2++;
+       }
+
+       /* Valid enough. */
+       return 1;
+}
+
+static int url_decode_char(const char *q)
+{
+       int i;
+       unsigned char val = 0;
+       for (i = 0; i < 2; i++) {
+               unsigned char c = *q++;
+               val <<= 4;
+               if (c >= '0' && c <= '9')
+                       val += c - '0';
+               else if (c >= 'a' && c <= 'f')
+                       val += c - 'a' + 10;
+               else if (c >= 'A' && c <= 'F')
+                       val += c - 'A' + 10;
+               else
+                       return -1;
+       }
+       return val;
+}
+
+static char *url_decode_internal(const char **query, const char *stop_at)
+{
+       const char *q = *query;
+       struct strbuf out;
+
+       strbuf_init(&out, 16);
+       do {
+               unsigned char c = *q;
+
+               if (!c)
+                       break;
+               if (stop_at && strchr(stop_at, c)) {
+                       q++;
+                       break;
+               }
+
+               if (c == '%') {
+                       int val = url_decode_char(q + 1);
+                       if (0 <= val) {
+                               strbuf_addch(&out, val);
+                               q += 3;
+                               continue;
+                       }
+               }
+
+               if (c == '+')
+                       strbuf_addch(&out, ' ');
+               else
+                       strbuf_addch(&out, c);
+               q++;
+       } while (1);
+       *query = q;
+       return strbuf_detach(&out, NULL);
+}
+
+char *url_decode(const char *url)
+{
+       return url_decode_internal(&url, NULL);
+}
+
+char *url_decode_parameter_name(const char **query)
+{
+       return url_decode_internal(query, "&=");
+}
+
+char *url_decode_parameter_value(const char **query)
+{
+       return url_decode_internal(query, "&");
+}
diff --git a/url.h b/url.h
new file mode 100644 (file)
index 0000000..15817f8
--- /dev/null
+++ b/url.h
@@ -0,0 +1,10 @@
+#ifndef URL_H
+#define URL_H
+
+extern int is_url(const char *url);
+extern int is_urlschemechar(int first_flag, int ch);
+extern char *url_decode(const char *url);
+extern char *url_decode_parameter_name(const char **query);
+extern char *url_decode_parameter_value(const char **query);
+
+#endif /* URL_H */
diff --git a/usage.c b/usage.c
index 79856a2b9f5bc4603252cb10b471a0815416a617..ec4cf53b6bf16f7f9b3528a430891d6c68ef64cc 100644 (file)
--- a/usage.c
+++ b/usage.c
@@ -5,7 +5,7 @@
  */
 #include "git-compat-util.h"
 
-static void report(const char *prefix, const char *err, va_list params)
+void vreportf(const char *prefix, const char *err, va_list params)
 {
        char msg[4096];
        vsnprintf(msg, sizeof(msg), err, params);
@@ -14,24 +14,24 @@ static void report(const char *prefix, const char *err, va_list params)
 
 static NORETURN void usage_builtin(const char *err, va_list params)
 {
-       report("usage: ", err, params);
+       vreportf("usage: ", err, params);
        exit(129);
 }
 
 static NORETURN void die_builtin(const char *err, va_list params)
 {
-       report("fatal: ", err, params);
+       vreportf("fatal: ", err, params);
        exit(128);
 }
 
 static void error_builtin(const char *err, va_list params)
 {
-       report("error: ", err, params);
+       vreportf("error: ", err, params);
 }
 
 static void warn_builtin(const char *warn, va_list params)
 {
-       report("warning: ", warn, params);
+       vreportf("warning: ", warn, params);
 }
 
 /* If we are in a dlopen()ed .so write to a global variable would segfault
index 67003fbb232cd85627f61c02a616b3d6a9ccd5d2..c49cc1b67e1164c561fb20839272bd8b1f7dde6d 100644 (file)
@@ -45,7 +45,9 @@ PATTERNS("pascal",
         "|[-+0-9.e]+|0[xXbB]?[0-9a-fA-F]+"
         "|<>|<=|>=|:=|\\.\\."
         "|[^[:space:]]|[\x80-\xff]+"),
-PATTERNS("php", "^[\t ]*((function|class).*)",
+PATTERNS("php",
+        "^[\t ]*(((public|protected|private|static)[\t ]+)*function.*)$\n"
+        "^[\t ]*(class.*)$",
         /* -- */
         "[a-zA-Z_][a-zA-Z0-9_]*"
         "|[-+0-9.e]+|0[xXbB]?[0-9a-fA-F]+"
index 58201b6bcb8d362cffd1222bcb8cd77072c34cf5..afb4f6f773a3ed909433778f4b4da3740ded2b18 100644 (file)
--- a/wrapper.c
+++ b/wrapper.c
@@ -10,9 +10,11 @@ static void try_to_free_builtin(size_t size)
 
 static void (*try_to_free_routine)(size_t size) = try_to_free_builtin;
 
-void set_try_to_free_routine(void (*routine)(size_t))
+try_to_free_t set_try_to_free_routine(try_to_free_t routine)
 {
-       try_to_free_routine = (routine) ? routine : try_to_free_builtin;
+       try_to_free_t old = try_to_free_routine;
+       try_to_free_routine = routine;
+       return old;
 }
 
 char *xstrdup(const char *str)
index 14e0acce8cc92f332a5b88482621119ec4dc1ded..9d9cb9556225301c98e8cc98d51bc516881afd73 100644 (file)
@@ -9,6 +9,7 @@
 #include "quote.h"
 #include "run-command.h"
 #include "remote.h"
+#include "refs.h"
 
 static char default_wt_status_colors[][COLOR_MAXLEN] = {
        GIT_COLOR_NORMAL, /* WT_STATUS_HEADER */
@@ -17,6 +18,8 @@ static char default_wt_status_colors[][COLOR_MAXLEN] = {
        GIT_COLOR_RED,    /* WT_STATUS_UNTRACKED */
        GIT_COLOR_RED,    /* WT_STATUS_NOBRANCH */
        GIT_COLOR_RED,    /* WT_STATUS_UNMERGED */
+       GIT_COLOR_GREEN,  /* WT_STATUS_LOCAL_BRANCH */
+       GIT_COLOR_RED,    /* WT_STATUS_REMOTE_BRANCH */
 };
 
 static const char *color(int slot, struct wt_status *s)
@@ -518,17 +521,18 @@ static void wt_status_print_submodule_summary(struct wt_status *s, int uncommitt
        struct child_process sm_summary;
        char summary_limit[64];
        char index[PATH_MAX];
-       const char *env[] = { index, NULL };
-       const char *argv[] = {
-               "submodule",
-               "summary",
-               uncommitted ? "--files" : "--cached",
-               "--for-status",
-               "--summary-limit",
-               summary_limit,
-               uncommitted ? NULL : (s->amend ? "HEAD^" : "HEAD"),
-               NULL
-       };
+       const char *env[] = { NULL, NULL };
+       const char *argv[8];
+
+       env[0] =        index;
+       argv[0] =       "submodule";
+       argv[1] =       "summary";
+       argv[2] =       uncommitted ? "--files" : "--cached";
+       argv[3] =       "--for-status";
+       argv[4] =       "--summary-limit";
+       argv[5] =       summary_limit;
+       argv[6] =       uncommitted ? NULL : (s->amend ? "HEAD^" : "HEAD");
+       argv[7] =       NULL;
 
        sprintf(summary_limit, "%d", s->submodule_summary);
        snprintf(index, sizeof(index), "GIT_INDEX_FILE=%s", s->index_file);
@@ -756,9 +760,69 @@ static void wt_shortstatus_other(int null_termination, struct string_list_item *
        }
 }
 
-void wt_shortstatus_print(struct wt_status *s, int null_termination)
+static void wt_shortstatus_print_tracking(struct wt_status *s)
+{
+       struct branch *branch;
+       const char *header_color = color(WT_STATUS_HEADER, s);
+       const char *branch_color_local = color(WT_STATUS_LOCAL_BRANCH, s);
+       const char *branch_color_remote = color(WT_STATUS_REMOTE_BRANCH, s);
+
+       const char *base;
+       const char *branch_name;
+       int num_ours, num_theirs;
+
+       color_fprintf(s->fp, color(WT_STATUS_HEADER, s), "## ");
+
+       if (!s->branch)
+               return;
+       branch_name = s->branch;
+
+       if (!prefixcmp(branch_name, "refs/heads/"))
+               branch_name += 11;
+       else if (!strcmp(branch_name, "HEAD")) {
+               branch_name = "HEAD (no branch)";
+               branch_color_local = color(WT_STATUS_NOBRANCH, s);
+       }
+
+       branch = branch_get(s->branch + 11);
+       if (s->is_initial)
+               color_fprintf(s->fp, header_color, "Initial commit on ");
+       if (!stat_tracking_info(branch, &num_ours, &num_theirs)) {
+               color_fprintf_ln(s->fp, branch_color_local,
+                       "%s", branch_name);
+               return;
+       }
+
+       base = branch->merge[0]->dst;
+       base = shorten_unambiguous_ref(base, 0);
+       color_fprintf(s->fp, branch_color_local, "%s", branch_name);
+       color_fprintf(s->fp, header_color, "...");
+       color_fprintf(s->fp, branch_color_remote, "%s", base);
+
+       color_fprintf(s->fp, header_color, " [");
+       if (!num_ours) {
+               color_fprintf(s->fp, header_color, "behind ");
+               color_fprintf(s->fp, branch_color_remote, "%d", num_theirs);
+       } else if (!num_theirs) {
+               color_fprintf(s->fp, header_color, "ahead ");
+               color_fprintf(s->fp, branch_color_local, "%d", num_ours);
+       } else {
+               color_fprintf(s->fp, header_color, "ahead ");
+               color_fprintf(s->fp, branch_color_local, "%d", num_ours);
+               color_fprintf(s->fp, header_color, ", behind ");
+               color_fprintf(s->fp, branch_color_remote, "%d", num_theirs);
+       }
+
+       color_fprintf_ln(s->fp, header_color, "]");
+}
+
+void wt_shortstatus_print(struct wt_status *s, int null_termination, int show_branch)
 {
        int i;
+
+       if (show_branch)
+               wt_shortstatus_print_tracking(s);
+
        for (i = 0; i < s->change.nr; i++) {
                struct wt_status_change_data *d;
                struct string_list_item *it;
@@ -789,5 +853,5 @@ void wt_porcelain_print(struct wt_status *s, int null_termination)
        s->use_color = 0;
        s->relative_paths = 0;
        s->prefix = NULL;
-       wt_shortstatus_print(s, null_termination);
+       wt_shortstatus_print(s, null_termination, 0);
 }
index 1093e65ae00e60c171c740d06628705cfcfb68c2..4cd74c4b32f51dbe575b0a04a894a520df79e9bf 100644 (file)
@@ -12,6 +12,8 @@ enum color_wt_status {
        WT_STATUS_UNTRACKED,
        WT_STATUS_NOBRANCH,
        WT_STATUS_UNMERGED,
+       WT_STATUS_LOCAL_BRANCH,
+       WT_STATUS_REMOTE_BRANCH
 };
 
 enum untracked_status_type {
@@ -43,7 +45,7 @@ struct wt_status {
        int submodule_summary;
        int show_ignored_files;
        enum untracked_status_type show_untracked_files;
-       char color_palette[WT_STATUS_UNMERGED+1][COLOR_MAXLEN];
+       char color_palette[WT_STATUS_REMOTE_BRANCH+1][COLOR_MAXLEN];
 
        /* These are computed during processing of the individual sections */
        int commitable;
@@ -60,7 +62,7 @@ void wt_status_prepare(struct wt_status *s);
 void wt_status_print(struct wt_status *s);
 void wt_status_collect(struct wt_status *s);
 
-void wt_shortstatus_print(struct wt_status *s, int null_termination);
+void wt_shortstatus_print(struct wt_status *s, int null_termination, int show_branch);
 void wt_porcelain_print(struct wt_status *s, int null_termination);
 
 #endif /* STATUS_H */