]> git.ipfire.org Git - thirdparty/git.git/commitdiff
Merge branch 'master' into lj/refs
authorJunio C Hamano <junkio@cox.net>
Thu, 28 Sep 2006 05:23:12 +0000 (22:23 -0700)
committerJunio C Hamano <junkio@cox.net>
Thu, 28 Sep 2006 05:23:12 +0000 (22:23 -0700)
* master: (72 commits)
  runstatus: do not recurse into subdirectories if not needed
  grep: fix --fixed-strings combined with expression.
  grep: free expressions and patterns when done.
  Corrected copy-and-paste thinko in ignore executable bit test case.
  An illustration of rev-list --parents --pretty=raw
  Allow git-checkout when on a non-existant branch.
  gitweb: Decode long title for link tooltips
  git-svn: Fix fetch --no-ignore-externals with GIT_SVN_NO_LIB=1
  Ignore executable bit when adding files if filemode=0.
  Remove empty ref directories that prevent creating a ref.
  Use const for interpolate arguments
  git-archive: update documentation
  Deprecate merge-recursive.py
  gitweb: fix over-eager application of esc_html().
  Allow '(no author)' in git-svn's authors file.
  Allow 'svn fetch' on '(no date)' revisions in Subversion.
  git-repack: allow git-repack to run in subdirectory
  Remove upload-tar and make git-tar-tree a thin wrapper to git-archive
  git-tar-tree: Move code for git-archive --format=tar to archive-tar.c
  git-tar-tree: Remove duplicate git_config() call
  ...

70 files changed:
.gitignore
Documentation/config.txt
Documentation/git-archive.txt
Documentation/git-daemon.txt
Documentation/git-init-db.txt
Documentation/git-receive-pack.txt
Documentation/git-rev-list.txt
Documentation/git-svn.txt
Documentation/git-tar-tree.txt
Documentation/git-upload-tar.txt [deleted file]
Documentation/git-zip-tree.txt [deleted file]
Documentation/git.txt
Documentation/gitk.txt
Documentation/hooks.txt
Makefile
archive-tar.c [new file with mode: 0644]
archive-zip.c [moved from builtin-zip-tree.c with 85% similarity]
builtin-apply.c
builtin-grep.c
builtin-init-db.c
builtin-mailinfo.c
builtin-pack-objects.c
builtin-rev-list.c
builtin-tar-tree.c
builtin-update-index.c
builtin-upload-archive.c
builtin-upload-tar.c [deleted file]
builtin.h
cache.h
configure.ac
daemon.c
diff-delta.c
diff.c
diff.h
dir.c
environment.c
git-branch.sh
git-checkout.sh
git-fetch.sh
git-merge-recursive-old.py [moved from git-merge-recursive.py with 100% similarity]
git-merge.sh
git-parse-remote.sh
git-rebase.sh
git-repack.sh
git-resolve.sh
git-svn.perl
git-svnimport.perl
git.c
gitweb/gitweb.perl
grep.c [new file with mode: 0644]
grep.h [new file with mode: 0644]
http.h
interpolate.c [new file with mode: 0644]
interpolate.h [new file with mode: 0644]
pack-check.c
pack.h
read-cache.c
receive-pack.c
revision.c
revision.h
setup.c
sha1_file.c
sha1_name.c
t/t3200-branch.sh
t/t3700-add.sh
t/t5400-send-pack.sh
t/t5510-fetch.sh [new file with mode: 0755]
t/t6001-rev-list-graft.sh [new file with mode: 0755]
t/t7201-co.sh
t/test-lib.sh

index 0ffe14ac4eb85ff88b3b09c50584c6a6c2c4d794..4c8c8e411500fe2ed0108c46838d50576ed15185 100644 (file)
@@ -66,6 +66,7 @@ git-merge-one-file
 git-merge-ours
 git-merge-recur
 git-merge-recursive
+git-merge-recursive-old
 git-merge-resolve
 git-merge-stupid
 git-mktag
@@ -125,13 +126,11 @@ git-update-ref
 git-update-server-info
 git-upload-archive
 git-upload-pack
-git-upload-tar
 git-var
 git-verify-pack
 git-verify-tag
 git-whatchanged
 git-write-tree
-git-zip-tree
 git-core-*/?*
 gitweb/gitweb.cgi
 test-date
index 844cae4cf024b17206021d1c55819922cf99277d..98c1f3e2e32e71047d6f0f6cf982e207117c64e2 100644 (file)
@@ -119,6 +119,13 @@ apply.whitespace::
        Tells `git-apply` how to handle whitespaces, in the same way
        as the '--whitespace' option. See gitlink:git-apply[1].
 
+branch.<name>.remote::
+       When in branch <name>, it tells `git fetch` which remote to fetch.
+
+branch.<name>.merge::
+       When in branch <name>, it tells `git fetch` the default remote branch
+       to be merged.
+
 pager.color::
        A boolean to enable/disable colored output when the pager is in
        use (default is true).
@@ -267,3 +274,10 @@ whatchanged.difftree::
 imap::
        The configuration variables in the 'imap' section are described
        in gitlink:git-imap-send[1].
+
+receive.denyNonFastforwads::
+       If set to true, git-receive-pack will deny a ref update which is
+       not a fast forward. Use this to prevent such an update via a push,
+       even if that push is forced. This configuration variable is
+       set when initializing a shared repository.
+
index 913528d3732999fd3c5a4648b8544fc80827d3dc..031fcd5190bf4baef7d87534ef5b6a39fe8985a9 100644 (file)
@@ -40,6 +40,7 @@ OPTIONS
 
 <extra>::
        This can be any options that the archiver backend understand.
+       See next section.
 
 --remote=<repo>::
        Instead of making a tar archive from local repository,
@@ -52,6 +53,18 @@ path::
        If one or more paths are specified, include only these in the
        archive, otherwise include all files and subdirectories.
 
+BACKEND EXTRA OPTIONS
+---------------------
+
+zip
+~~~
+-0::
+       Store the files instead of deflating them.
+-9::
+       Highest and slowest compression level.  You can specify any
+       number from 1 to 9 to adjust compression speed and ratio.
+
+
 CONFIGURATION
 -------------
 By default, file and directories modes are set to 0666 or 0777 in tar
index 741f2c69bdace527c57b644ed072ead4dcb46201..51d7c94d7df0c15a74855f5ef910def64045681e 100644 (file)
@@ -11,6 +11,7 @@ SYNOPSIS
 'git-daemon' [--verbose] [--syslog] [--inetd | --port=n] [--export-all]
              [--timeout=n] [--init-timeout=n] [--strict-paths]
              [--base-path=path] [--user-path | --user-path=path]
+             [--interpolated-path=pathtemplate]
              [--enable=service] [--disable=service]
             [--allow-override=service] [--forbid-override=service]
              [--reuseaddr] [--detach] [--pid-file=file]
@@ -50,6 +51,12 @@ OPTIONS
        'git://example.com/hello.git', `git-daemon` will interpret the path
        as '/srv/git/hello.git'.
 
+--interpolated-path=pathtemplate::
+       To support virtual hosting, an interpolated path template can be
+       used to dynamically construct alternate paths.  The template
+       supports %H for the target hostname as supplied by the client,
+       and %D for the absolute path of the named repository.
+
 --export-all::
        Allow pulling from all directories that look like GIT repositories
        (have the 'objects' and 'refs' subdirectories), even if they
@@ -135,6 +142,46 @@ upload-pack::
        disable it by setting `daemon.uploadpack` configuration
        item to `false`.
 
+EXAMPLES
+--------
+git-daemon as inetd server::
+       To set up `git-daemon` as an inetd service that handles any
+       repository under the whitelisted set of directories, /pub/foo
+       and /pub/bar, place an entry like the following into
+       /etc/inetd all on one line:
++
+------------------------------------------------
+       git stream tcp nowait nobody  /usr/bin/git-daemon
+               git-daemon --inetd --verbose
+               --syslog --export-all
+               /pub/foo /pub/bar
+------------------------------------------------
+
+
+git-daemon as inetd server for virtual hosts::
+       To set up `git-daemon` as an inetd service that handles
+       repositories for different virtual hosts, `www.example.com`
+       and `www.example.org`, place an entry like the following into
+       `/etc/inetd` all on one line:
++
+------------------------------------------------
+       git stream tcp nowait nobody /usr/bin/git-daemon
+               git-daemon --inetd --verbose
+               --syslog --export-all
+               --interpolated-path=/pub/%H%D
+               /pub/www.example.org/software
+               /pub/www.example.com/software
+               /software
+------------------------------------------------
++
+In this example, the root-level directory `/pub` will contain
+a subdirectory for each virtual host name supported.
+Further, both hosts advertise repositories simply as
+`git://www.example.com/software/repo.git`.  For pre-1.4.0
+clients, a symlink from `/software` into the appropriate
+default repository could be made as well.
+
+
 Author
 ------
 Written by Linus Torvalds <torvalds@osdl.org>, YOSHIFUJI Hideaki
index 63cd5dab3f82746816d6dc0b8eae32f2c9d930d4..ca7d09dc0a8563a1d8005bc83a89d19c785c1a3d 100644 (file)
@@ -48,6 +48,10 @@ is given:
  - 'all' (or 'world' or 'everybody'): Same as 'group', but make the repository
    readable by all users.
 
+By default, the configuration flag receive.denyNonFastforward is enabled
+in shared repositories, so that you cannot force a non fast-forwarding push
+into it.
+
 --
 
 
index f9457d45ed684597769124af945c184a7d03a948..0dfadc2a32a44ef1e026ae95c8cc5906ec4d4d5c 100644 (file)
@@ -73,6 +73,8 @@ packed and is served via a dumb transport.
 There are other real-world examples of using update and
 post-update hooks found in the Documentation/howto directory.
 
+git-receive-pack honours the receive.denyNonFastforwards flag, which
+tells it if updates to a ref should be denied if they are not fast-forwards.
 
 OPTIONS
 -------
index 28966adbbce60c2d3fa02f47bf0797bc29d32f60..00a95e249fe82f2eb3d53dcc541559dceb3e8709 100644 (file)
@@ -20,6 +20,7 @@ SYNOPSIS
             [ \--stdin ]
             [ \--topo-order ]
             [ \--parents ]
+            [ \--(author|committer|grep)=<pattern> ]
             [ [\--objects | \--objects-edge] [ \--unpacked ] ]
             [ \--pretty | \--header ]
             [ \--bisect ]
@@ -154,6 +155,16 @@ limiting may be applied.
 
        Limit the commits output to specified time range.
 
+--author='pattern', --committer='pattern'::
+
+       Limit the commits output to ones with author/committer
+       header lines that match the specified pattern.
+
+--grep='pattern'::
+
+       Limit the commits output to ones with log message that
+       matches the specified pattern.
+
 --remove-empty::
 
        Stop when a given path disappears from the tree.
index b7b63f7136a01d6f3383346c292e5c8aceb8ced4..1cfa3e342cfdc074b0a9a113a59bcebee8869d07 100644 (file)
@@ -244,6 +244,18 @@ doing.
 
 repo-config key: svn.noignoreexternals
 
+--ignore-nodate::
+Only used with the 'fetch' command.
+
+By default git-svn will crash if it tries to import a revision
+from SVN which has '(no date)' listed as the date of the revision.
+This is repository corruption on SVN's part, plain and simple.
+But sometimes you really need those revisions anyway.
+
+If supplied git-svn will convert '(no date)' entries to the UNIX
+epoch (midnight on Jan. 1, 1970).  Yes, that's probably very wrong.
+SVN was very wrong.
+
 --
 
 Basic Examples
index 1e1c7fa856a2b80bb839da805aa57d43f65f968e..74a6fddd9a9f9d6831922fb163240c2a2c06994e 100644 (file)
@@ -12,6 +12,9 @@ SYNOPSIS
 
 DESCRIPTION
 -----------
+THIS COMMAND IS DEPRECATED.  Use `git-archive` with `--format=tar`
+option instead.
+
 Creates a tar archive containing the tree structure for the named tree.
 When <base> is specified it is added as a leading path to the files in the
 generated tar archive.
diff --git a/Documentation/git-upload-tar.txt b/Documentation/git-upload-tar.txt
deleted file mode 100644 (file)
index 394af62..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-git-upload-tar(1)
-=================
-
-NAME
-----
-git-upload-tar - Send tar archive
-
-
-SYNOPSIS
---------
-'git-upload-tar' <directory>
-
-DESCRIPTION
------------
-Invoked by 'git-tar-tree --remote' and sends a generated tar archive
-to the other end over the git protocol.
-
-This command is usually not invoked directly by the end user.
-The UI for the protocol is on the 'git-tar-tree' side, and the
-program pair is meant to be used to get a tar archive from a
-remote repository.
-
-
-OPTIONS
--------
-<directory>::
-       The repository to get a tar archive from.
-
-Author
-------
-Written by Junio C Hamano <junio@kernel.org>
-
-Documentation
---------------
-Documentation by Junio C Hamano.
-
-GIT
----
-Part of the gitlink:git[7] suite
diff --git a/Documentation/git-zip-tree.txt b/Documentation/git-zip-tree.txt
deleted file mode 100644 (file)
index 2e9d981..0000000
+++ /dev/null
@@ -1,67 +0,0 @@
-git-zip-tree(1)
-===============
-
-NAME
-----
-git-zip-tree - Creates a ZIP archive of the files in the named tree
-
-
-SYNOPSIS
---------
-'git-zip-tree' [-0|...|-9] <tree-ish> [ <base> ]
-
-DESCRIPTION
------------
-Creates a ZIP archive containing the tree structure for the named tree.
-When <base> is specified it is added as a leading path to the files in the
-generated ZIP archive.
-
-git-zip-tree behaves differently when given a tree ID versus when given
-a commit ID or tag ID.  In the first case the current time is used as
-modification time of each file in the archive.  In the latter case the
-commit time as recorded in the referenced commit object is used instead.
-Additionally the commit ID is stored as an archive comment.
-
-Currently git-zip-tree can handle only files and directories, symbolic
-links are not supported.
-
-OPTIONS
--------
-
--0::
-       Store the files instead of deflating them.
-
--9::
-       Highest and slowest compression level.  You can specify any
-       number from 1 to 9 to adjust compression speed and ratio.
-
-<tree-ish>::
-       The tree or commit to produce ZIP archive for.  If it is
-       the object name of a commit object.
-
-<base>::
-       Leading path to the files in the resulting ZIP archive.
-
-EXAMPLES
---------
-git zip-tree v1.4.0 git-1.4.0 >git-1.4.0.zip::
-
-       Create a ZIP file for v1.4.0 release.
-
-git zip-tree HEAD:Documentation/ git-docs >docs.zip::
-
-       Put everything in the current head's Documentation/ directory
-       into 'docs.zip', with the prefix 'git-docs/'.
-
-Author
-------
-Written by Rene Scharfe.
-
-Documentation
---------------
-Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
-
-GIT
----
-Part of the gitlink:git[7] suite
-
index 744c38dee3c1835bb5e0ace4834547e6113e3bda..2135b65516b372587a9a4fa13daf21df05a5d1ce 100644 (file)
@@ -247,10 +247,6 @@ gitlink:git-upload-pack[1]::
        Invoked by 'git-fetch-pack' to push
        what are asked for.
 
-gitlink:git-upload-tar[1]::
-       Invoked by 'git-tar-tree --remote' to return the tar
-       archive the other end asked for.
-
 
 High-level commands (porcelain)
 -------------------------------
@@ -270,6 +266,9 @@ gitlink:git-am[1]::
 gitlink:git-applymbox[1]::
        Apply patches from a mailbox, original version by Linus.
 
+gitlink:git-archive[1]::
+       Creates an archive of files from a named tree.
+
 gitlink:git-bisect[1]::
        Find the change that introduced a bug by binary search.
 
index 23be0050b0023cbfecc5fa271b97a7d9855a73b0..f1aeb07f64e09735871f0a956b9ba412578a2a23 100644 (file)
@@ -37,6 +37,10 @@ frequently used options.
 
        Show commits older than a specific date.
 
+--all::
+
+       Show all branches.
+
 <revs>::
 
        Limit the revisions to show. This can be either a single revision
@@ -63,6 +67,11 @@ gitk --since="2 weeks ago" \-- gitk::
        The "--" is necessary to avoid confusion with the *branch* named
        'gitk'
 
+gitk --max-count=100 --all -- Makefile::
+
+       Show at most 100 changes made to the file 'Makefile'. Instead of only
+       looking for changes in the current branch look in all branches.
+
 See Also
 --------
 'qgit(1)'::
index 898b4aaf80aabe8caa9675c4371918a61312f1cb..517f49b5cc5016a20b171789b89f48cb6ef0ecd5 100644 (file)
@@ -5,8 +5,7 @@ Hooks are little scripts you can place in `$GIT_DIR/hooks`
 directory to trigger action at certain points.  When
 `git-init-db` is run, a handful example hooks are copied in the
 `hooks` directory of the new repository, but by default they are
-all disabled.  To enable a hook, make it executable with `chmod
-+x`.
+all disabled.  To enable a hook, make it executable with `chmod +x`.
 
 This document describes the currently defined hooks.
 
@@ -16,16 +15,16 @@ applypatch-msg
 This hook is invoked by `git-applypatch` script, which is
 typically invoked by `git-applymbox`.  It takes a single
 parameter, the name of the file that holds the proposed commit
-log message.  Exiting with non-zero status causes the
-'git-applypatch' to abort before applying the patch.
+log message.  Exiting with non-zero status causes
+`git-applypatch` to abort before applying the patch.
 
 The hook is allowed to edit the message file in place, and can
 be used to normalize the message into some project standard
 format (if the project has one). It can also be used to refuse
 the commit after inspecting the message file.
 
-The default applypatch-msg hook, when enabled, runs the
-commit-msg hook, if the latter is enabled.
+The default 'applypatch-msg' hook, when enabled, runs the
+'commit-msg' hook, if the latter is enabled.
 
 pre-applypatch
 --------------
@@ -39,8 +38,8 @@ after application of the patch not committed.
 It can be used to inspect the current working tree and refuse to
 make a commit if it does not pass certain test.
 
-The default pre-applypatch hook, when enabled, runs the
-pre-commit hook, if the latter is enabled.
+The default 'pre-applypatch' hook, when enabled, runs the
+'pre-commit' hook, if the latter is enabled.
 
 post-applypatch
 ---------------
@@ -61,9 +60,9 @@ invoked before obtaining the proposed commit log message and
 making a commit.  Exiting with non-zero status from this script
 causes the `git-commit` to abort.
 
-The default pre-commit hook, when enabled, catches introduction
+The default 'pre-commit' hook, when enabled, catches introduction
 of lines with trailing whitespaces and aborts the commit when
-a such line is found.
+such a line is found.
 
 commit-msg
 ----------
@@ -79,8 +78,8 @@ be used to normalize the message into some project standard
 format (if the project has one). It can also be used to refuse
 the commit after inspecting the message file.
 
-The default commit-msg hook, when enabled, detects duplicate
-Signed-off-by: lines, and aborts the commit when one is found.
+The default 'commit-msg' hook, when enabled, detects duplicate
+"Signed-off-by" lines, and aborts the commit if one is found.
 
 post-commit
 -----------
@@ -91,23 +90,24 @@ parameter, and is invoked after a commit is made.
 This hook is meant primarily for notification, and cannot affect
 the outcome of `git-commit`.
 
-The default post-commit hook, when enabled, demonstrates how to
+The default 'post-commit' hook, when enabled, demonstrates how to
 send out a commit notification e-mail.
 
 update
 ------
 
 This hook is invoked by `git-receive-pack` on the remote repository,
-which is happens when a `git push` is done on a local repository.
+which happens when a `git push` is done on a local repository.
 Just before updating the ref on the remote repository, the update hook
 is invoked.  Its exit status determines the success or failure of
 the ref update.
 
 The hook executes once for each ref to be updated, and takes
 three parameters:
-    - the name of the ref being updated,
-    - the old object name stored in the ref,
-    - and the new objectname to be stored in the ref.
+
+ - the name of the ref being updated,
+ - the old object name stored in the ref,
+ - and the new objectname to be stored in the ref.
 
 A zero exit from the update hook allows the ref to be updated.
 Exiting with a non-zero status prevents `git-receive-pack`
@@ -126,16 +126,16 @@ Another use suggested on the mailing list is to use this hook to
 implement access control which is finer grained than the one
 based on filesystem group.
 
-The standard output of this hook is sent to /dev/null; if you
-want to report something to the git-send-pack on the other end,
-you can redirect your output to your stderr.
+The standard output of this hook is sent to `/dev/null`; if you
+want to report something to the `git-send-pack` on the other end,
+you can redirect your output to your `stderr`.
 
 
 post-update
 -----------
 
 This hook is invoked by `git-receive-pack` on the remote repository,
-which is happens when a `git push` is done on a local repository.
+which happens when a `git push` is done on a local repository.
 It executes on the remote repository once after all the refs have
 been updated.
 
@@ -145,16 +145,16 @@ name of ref that was actually updated.
 This hook is meant primarily for notification, and cannot affect
 the outcome of `git-receive-pack`.
 
-The post-update hook can tell what are the heads that were pushed,
+The 'post-update' hook can tell what are the heads that were pushed,
 but it does not know what their original and updated values are,
 so it is a poor place to do log old..new.
 
-The default post-update hook, when enabled, runs
+When enabled, the default 'post-update' hook runs
 `git-update-server-info` to keep the information used by dumb
-transports (e.g., http) up-to-date.  If you are publishing
-a git repository that is accessible via http, you should
+transports (e.g., HTTP) up-to-date.  If you are publishing
+a git repository that is accessible via HTTP, you should
 probably enable this hook.
 
-The standard output of this hook is sent to /dev/null; if you
-want to report something to the git-send-pack on the other end,
-you can redirect your output to your stderr.
+The standard output of this hook is sent to `/dev/null`; if you
+want to report something to the `git-send-pack` on the other end,
+you can redirect your output to your `stderr`.
index af4d68fd7e7faa1d590cf0eef3f2d1a8e435a136..65fb8284f3c9ef210c08711b5ce4796593e72e75 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -81,8 +81,6 @@ all:
 # Define NO_ACCURATE_DIFF if your diff program at least sometimes misses
 # a missing newline at the end of the file.
 #
-# Define NO_PYTHON if you want to lose all benefits of the recursive merge.
-#
 # Define COLLISION_CHECK below if you believe that SHA1's
 # 1461501637330902918203684832716283019655932542976 hashes do not give you
 # sufficient guarantee that no collisions between objects will ever happen.
@@ -174,7 +172,7 @@ SCRIPT_PERL = \
        git-send-email.perl git-svn.perl
 
 SCRIPT_PYTHON = \
-       git-merge-recursive.py
+       git-merge-recursive-old.py
 
 SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) \
          $(patsubst %.perl,%,$(SCRIPT_PERL)) \
@@ -199,7 +197,7 @@ PROGRAMS = \
        git-upload-pack$X git-verify-pack$X \
        git-pack-redundant$X git-var$X \
        git-describe$X git-merge-tree$X git-blame$X git-imap-send$X \
-       git-merge-recur$X \
+       git-merge-recursive$X \
        $(EXTRA_PROGRAMS)
 
 # Empty...
@@ -234,7 +232,7 @@ LIB_FILE=libgit.a
 XDIFF_LIB=xdiff/lib.a
 
 LIB_H = \
-       archive.h blob.h cache.h commit.h csum-file.h delta.h \
+       archive.h blob.h cache.h commit.h csum-file.h delta.h grep.h \
        diff.h object.h pack.h pkt-line.h quote.h refs.h list-objects.h sideband.h \
        run-command.h strbuf.h tag.h tree.h git-compat-util.h revision.h \
        tree-walk.h log-tree.h dir.h path-list.h unpack-trees.h builtin.h
@@ -246,15 +244,17 @@ DIFF_OBJS = \
 
 LIB_OBJS = \
        blob.o commit.o connect.o csum-file.o cache-tree.o base85.o \
-       date.o diff-delta.o entry.o exec_cmd.o ident.o lockfile.o \
+       date.o diff-delta.o entry.o exec_cmd.o ident.o \
+       interpolate.o \
+       lockfile.o \
        object.o pack-check.o patch-delta.o path.o pkt-line.o sideband.o \
        quote.o read-cache.o refs.o run-command.o dir.o object-refs.o \
        server-info.o setup.o sha1_file.o sha1_name.o strbuf.o \
        tag.o tree.o usage.o config.o environment.o ctype.o copy.o \
        fetch-clone.o revision.o pager.o tree-walk.o xdiff-interface.o \
-       write_or_die.o trace.o list-objects.o \
+       write_or_die.o trace.o list-objects.o grep.o \
        alloc.o merge-file.o path-list.o help.o unpack-trees.o $(DIFF_OBJS) \
-       color.o wt-status.o
+       color.o wt-status.o archive-zip.o archive-tar.o
 
 BUILTIN_OBJS = \
        builtin-add.o \
@@ -299,10 +299,8 @@ BUILTIN_OBJS = \
        builtin-update-index.o \
        builtin-update-ref.o \
        builtin-upload-archive.o \
-       builtin-upload-tar.o \
        builtin-verify-pack.o \
        builtin-write-tree.o \
-       builtin-zip-tree.o \
        builtin-show-ref.o \
        builtin-pack-refs.o
 
@@ -573,7 +571,8 @@ LIB_OBJS += $(COMPAT_OBJS)
 export prefix TAR INSTALL DESTDIR SHELL_PATH template_dir
 ### Build rules
 
-all: $(ALL_PROGRAMS) $(BUILT_INS) git$X gitk gitweb/gitweb.cgi
+all: $(ALL_PROGRAMS) $(BUILT_INS) git$X gitk gitweb/gitweb.cgi \
+       git-merge-recur$X
 
 all:
        $(MAKE) -C templates
@@ -588,6 +587,9 @@ git$X: git.c common-cmds.h $(BUILTIN_OBJS) $(GITLIBS) GIT-CFLAGS
 
 help.o: common-cmds.h
 
+git-merge-recur$X: git-merge-recursive$X
+       rm -f $@ && ln git-merge-recursive$X $@
+
 $(BUILT_INS): git$X
        rm -f $@ && ln git$X $@
 
@@ -725,11 +727,6 @@ git-http-push$X: revision.o http.o http-push.o $(GITLIBS)
        $(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
                $(LIBS) $(CURL_LIBCURL) $(EXPAT_LIBEXPAT)
 
-merge-recursive.o path-list.o: path-list.h
-git-merge-recur$X: merge-recursive.o path-list.o $(GITLIBS)
-       $(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
-               $(LIBS)
-
 $(LIB_OBJS) $(BUILTIN_OBJS): $(LIB_H)
 $(patsubst git-%$X,%.o,$(PROGRAMS)): $(LIB_H) $(wildcard */*.h)
 $(DIFF_OBJS): diffcore.h
@@ -890,6 +887,7 @@ check-docs::
                case "$$v" in \
                git-merge-octopus | git-merge-ours | git-merge-recursive | \
                git-merge-resolve | git-merge-stupid | git-merge-recur | \
+               git-merge-recursive-old | \
                git-ssh-pull | git-ssh-push ) continue ;; \
                esac ; \
                test -f "Documentation/$$v.txt" || \
diff --git a/archive-tar.c b/archive-tar.c
new file mode 100644 (file)
index 0000000..ff0f6e2
--- /dev/null
@@ -0,0 +1,325 @@
+/*
+ * Copyright (c) 2005, 2006 Rene Scharfe
+ */
+#include <time.h>
+#include "cache.h"
+#include "commit.h"
+#include "strbuf.h"
+#include "tar.h"
+#include "builtin.h"
+#include "archive.h"
+
+#define RECORDSIZE     (512)
+#define BLOCKSIZE      (RECORDSIZE * 20)
+
+static char block[BLOCKSIZE];
+static unsigned long offset;
+
+static time_t archive_time;
+static int tar_umask;
+static int verbose;
+
+/* writes out the whole block, but only if it is full */
+static void write_if_needed(void)
+{
+       if (offset == BLOCKSIZE) {
+               write_or_die(1, block, BLOCKSIZE);
+               offset = 0;
+       }
+}
+
+/*
+ * queues up writes, so that all our write(2) calls write exactly one
+ * full block; pads writes to RECORDSIZE
+ */
+static void write_blocked(const void *data, unsigned long size)
+{
+       const char *buf = data;
+       unsigned long tail;
+
+       if (offset) {
+               unsigned long chunk = BLOCKSIZE - offset;
+               if (size < chunk)
+                       chunk = size;
+               memcpy(block + offset, buf, chunk);
+               size -= chunk;
+               offset += chunk;
+               buf += chunk;
+               write_if_needed();
+       }
+       while (size >= BLOCKSIZE) {
+               write_or_die(1, buf, BLOCKSIZE);
+               size -= BLOCKSIZE;
+               buf += BLOCKSIZE;
+       }
+       if (size) {
+               memcpy(block + offset, buf, size);
+               offset += size;
+       }
+       tail = offset % RECORDSIZE;
+       if (tail)  {
+               memset(block + offset, 0, RECORDSIZE - tail);
+               offset += RECORDSIZE - tail;
+       }
+       write_if_needed();
+}
+
+/*
+ * The end of tar archives is marked by 2*512 nul bytes and after that
+ * follows the rest of the block (if any).
+ */
+static void write_trailer(void)
+{
+       int tail = BLOCKSIZE - offset;
+       memset(block + offset, 0, tail);
+       write_or_die(1, block, BLOCKSIZE);
+       if (tail < 2 * RECORDSIZE) {
+               memset(block, 0, offset);
+               write_or_die(1, block, BLOCKSIZE);
+       }
+}
+
+static void strbuf_append_string(struct strbuf *sb, const char *s)
+{
+       int slen = strlen(s);
+       int total = sb->len + slen;
+       if (total > sb->alloc) {
+               sb->buf = xrealloc(sb->buf, total);
+               sb->alloc = total;
+       }
+       memcpy(sb->buf + sb->len, s, slen);
+       sb->len = total;
+}
+
+/*
+ * pax extended header records have the format "%u %s=%s\n".  %u contains
+ * the size of the whole string (including the %u), the first %s is the
+ * keyword, the second one is the value.  This function constructs such a
+ * string and appends it to a struct strbuf.
+ */
+static void strbuf_append_ext_header(struct strbuf *sb, const char *keyword,
+                                     const char *value, unsigned int valuelen)
+{
+       char *p;
+       int len, total, tmp;
+
+       /* "%u %s=%s\n" */
+       len = 1 + 1 + strlen(keyword) + 1 + valuelen + 1;
+       for (tmp = len; tmp > 9; tmp /= 10)
+               len++;
+
+       total = sb->len + len;
+       if (total > sb->alloc) {
+               sb->buf = xrealloc(sb->buf, total);
+               sb->alloc = total;
+       }
+
+       p = sb->buf;
+       p += sprintf(p, "%u %s=", len, keyword);
+       memcpy(p, value, valuelen);
+       p += valuelen;
+       *p = '\n';
+       sb->len = total;
+}
+
+static unsigned int ustar_header_chksum(const struct ustar_header *header)
+{
+       char *p = (char *)header;
+       unsigned int chksum = 0;
+       while (p < header->chksum)
+               chksum += *p++;
+       chksum += sizeof(header->chksum) * ' ';
+       p += sizeof(header->chksum);
+       while (p < (char *)header + sizeof(struct ustar_header))
+               chksum += *p++;
+       return chksum;
+}
+
+static int get_path_prefix(const struct strbuf *path, int maxlen)
+{
+       int i = path->len;
+       if (i > maxlen)
+               i = maxlen;
+       do {
+               i--;
+       } while (i > 0 && path->buf[i] != '/');
+       return i;
+}
+
+static void write_entry(const unsigned char *sha1, struct strbuf *path,
+                        unsigned int mode, void *buffer, unsigned long size)
+{
+       struct ustar_header header;
+       struct strbuf ext_header;
+
+       memset(&header, 0, sizeof(header));
+       ext_header.buf = NULL;
+       ext_header.len = ext_header.alloc = 0;
+
+       if (!sha1) {
+               *header.typeflag = TYPEFLAG_GLOBAL_HEADER;
+               mode = 0100666;
+               strcpy(header.name, "pax_global_header");
+       } else if (!path) {
+               *header.typeflag = TYPEFLAG_EXT_HEADER;
+               mode = 0100666;
+               sprintf(header.name, "%s.paxheader", sha1_to_hex(sha1));
+       } else {
+               if (verbose)
+                       fprintf(stderr, "%.*s\n", path->len, path->buf);
+               if (S_ISDIR(mode)) {
+                       *header.typeflag = TYPEFLAG_DIR;
+                       mode = (mode | 0777) & ~tar_umask;
+               } else if (S_ISLNK(mode)) {
+                       *header.typeflag = TYPEFLAG_LNK;
+                       mode |= 0777;
+               } else if (S_ISREG(mode)) {
+                       *header.typeflag = TYPEFLAG_REG;
+                       mode = (mode | ((mode & 0100) ? 0777 : 0666)) & ~tar_umask;
+               } else {
+                       error("unsupported file mode: 0%o (SHA1: %s)",
+                             mode, sha1_to_hex(sha1));
+                       return;
+               }
+               if (path->len > sizeof(header.name)) {
+                       int plen = get_path_prefix(path, sizeof(header.prefix));
+                       int rest = path->len - plen - 1;
+                       if (plen > 0 && rest <= sizeof(header.name)) {
+                               memcpy(header.prefix, path->buf, plen);
+                               memcpy(header.name, path->buf + plen + 1, rest);
+                       } else {
+                               sprintf(header.name, "%s.data",
+                                       sha1_to_hex(sha1));
+                               strbuf_append_ext_header(&ext_header, "path",
+                                                        path->buf, path->len);
+                       }
+               } else
+                       memcpy(header.name, path->buf, path->len);
+       }
+
+       if (S_ISLNK(mode) && buffer) {
+               if (size > sizeof(header.linkname)) {
+                       sprintf(header.linkname, "see %s.paxheader",
+                               sha1_to_hex(sha1));
+                       strbuf_append_ext_header(&ext_header, "linkpath",
+                                                buffer, size);
+               } else
+                       memcpy(header.linkname, buffer, size);
+       }
+
+       sprintf(header.mode, "%07o", mode & 07777);
+       sprintf(header.size, "%011lo", S_ISREG(mode) ? size : 0);
+       sprintf(header.mtime, "%011lo", archive_time);
+
+       /* XXX: should we provide more meaningful info here? */
+       sprintf(header.uid, "%07o", 0);
+       sprintf(header.gid, "%07o", 0);
+       strlcpy(header.uname, "git", sizeof(header.uname));
+       strlcpy(header.gname, "git", sizeof(header.gname));
+       sprintf(header.devmajor, "%07o", 0);
+       sprintf(header.devminor, "%07o", 0);
+
+       memcpy(header.magic, "ustar", 6);
+       memcpy(header.version, "00", 2);
+
+       sprintf(header.chksum, "%07o", ustar_header_chksum(&header));
+
+       if (ext_header.len > 0) {
+               write_entry(sha1, NULL, 0, ext_header.buf, ext_header.len);
+               free(ext_header.buf);
+       }
+       write_blocked(&header, sizeof(header));
+       if (S_ISREG(mode) && buffer && size > 0)
+               write_blocked(buffer, size);
+}
+
+static void write_global_extended_header(const unsigned char *sha1)
+{
+       struct strbuf ext_header;
+       ext_header.buf = NULL;
+       ext_header.len = ext_header.alloc = 0;
+       strbuf_append_ext_header(&ext_header, "comment", sha1_to_hex(sha1), 40);
+       write_entry(NULL, NULL, 0, ext_header.buf, ext_header.len);
+       free(ext_header.buf);
+}
+
+static int git_tar_config(const char *var, const char *value)
+{
+       if (!strcmp(var, "tar.umask")) {
+               if (!strcmp(value, "user")) {
+                       tar_umask = umask(0);
+                       umask(tar_umask);
+               } else {
+                       tar_umask = git_config_int(var, value);
+               }
+               return 0;
+       }
+       return git_default_config(var, value);
+}
+
+static int write_tar_entry(const unsigned char *sha1,
+                           const char *base, int baselen,
+                           const char *filename, unsigned mode, int stage)
+{
+       static struct strbuf path;
+       int filenamelen = strlen(filename);
+       void *buffer;
+       char type[20];
+       unsigned long size;
+
+       if (!path.alloc) {
+               path.buf = xmalloc(PATH_MAX);
+               path.alloc = PATH_MAX;
+               path.len = path.eof = 0;
+       }
+       if (path.alloc < baselen + filenamelen) {
+               free(path.buf);
+               path.buf = xmalloc(baselen + filenamelen);
+               path.alloc = baselen + filenamelen;
+       }
+       memcpy(path.buf, base, baselen);
+       memcpy(path.buf + baselen, filename, filenamelen);
+       path.len = baselen + filenamelen;
+       if (S_ISDIR(mode)) {
+               strbuf_append_string(&path, "/");
+               buffer = NULL;
+               size = 0;
+       } else {
+               buffer = read_sha1_file(sha1, type, &size);
+               if (!buffer)
+                       die("cannot read %s", sha1_to_hex(sha1));
+       }
+
+       write_entry(sha1, &path, mode, buffer, size);
+       free(buffer);
+
+       return READ_TREE_RECURSIVE;
+}
+
+int write_tar_archive(struct archiver_args *args)
+{
+       int plen = args->base ? strlen(args->base) : 0;
+
+       git_config(git_tar_config);
+
+       archive_time = args->time;
+       verbose = args->verbose;
+
+       if (args->commit_sha1)
+               write_global_extended_header(args->commit_sha1);
+
+       if (args->base && plen > 0 && args->base[plen - 1] == '/') {
+               char *base = xstrdup(args->base);
+               int baselen = strlen(base);
+
+               while (baselen > 0 && base[baselen - 1] == '/')
+                       base[--baselen] = '\0';
+               write_tar_entry(args->tree->object.sha1, "", 0, base, 040777, 0);
+               free(base);
+       }
+       read_tree_recursive(args->tree, args->base, plen, 0,
+                           args->pathspec, write_tar_entry);
+       write_trailer();
+
+       return 0;
+}
similarity index 85%
rename from builtin-zip-tree.c
rename to archive-zip.c
index 52d4b7a17e39831964fe7c9c9c0cb374d0d61a62..3ffdad68d130136312028b389da73af1a789232e 100644 (file)
@@ -10,9 +10,6 @@
 #include "builtin.h"
 #include "archive.h"
 
-static const char zip_tree_usage[] =
-"git-zip-tree [-0|...|-9] <tree-ish> [ <base> ]";
-
 static int verbose;
 static int zip_date;
 static int zip_time;
@@ -294,68 +291,6 @@ static void dos_time(time_t *time, int *dos_date, int *dos_time)
        *dos_time = t->tm_sec / 2 + t->tm_min * 32 + t->tm_hour * 2048;
 }
 
-int cmd_zip_tree(int argc, const char **argv, const char *prefix)
-{
-       unsigned char sha1[20];
-       struct tree *tree;
-       struct commit *commit;
-       time_t archive_time;
-       char *base;
-       int baselen;
-
-       git_config(git_default_config);
-
-       if (argc > 1 && argv[1][0] == '-') {
-               if (isdigit(argv[1][1]) && argv[1][2] == '\0') {
-                       zlib_compression_level = argv[1][1] - '0';
-                       argc--;
-                       argv++;
-               }
-       }
-
-       switch (argc) {
-       case 3:
-               base = xstrdup(argv[2]);
-               baselen = strlen(base);
-               break;
-       case 2:
-               base = xstrdup("");
-               baselen = 0;
-               break;
-       default:
-               usage(zip_tree_usage);
-       }
-
-       if (get_sha1(argv[1], sha1))
-               die("Not a valid object name %s", argv[1]);
-
-       commit = lookup_commit_reference_gently(sha1, 1);
-       archive_time = commit ? commit->date : time(NULL);
-       dos_time(&archive_time, &zip_date, &zip_time);
-
-       zip_dir = xmalloc(ZIP_DIRECTORY_MIN_SIZE);
-       zip_dir_size = ZIP_DIRECTORY_MIN_SIZE;
-
-       tree = parse_tree_indirect(sha1);
-       if (!tree)
-               die("not a tree object");
-
-       if (baselen > 0) {
-               write_zip_entry(tree->object.sha1, "", 0, base, 040777, 0);
-               base = xrealloc(base, baselen + 1);
-               base[baselen] = '/';
-               baselen++;
-               base[baselen] = '\0';
-       }
-       read_tree_recursive(tree, base, baselen, 0, NULL, write_zip_entry);
-       write_zip_trailer(commit ? commit->object.sha1 : NULL);
-
-       free(zip_dir);
-       free(base);
-
-       return 0;
-}
-
 int write_zip_archive(struct archiver_args *args)
 {
        int plen = strlen(args->base);
index 25e90d8d29a8f55b20a98425edccfe49cad4c8e2..de5f855266f6fcf3274c9648b02ab960a267bb90 100644 (file)
@@ -854,6 +854,49 @@ static int find_header(char *line, unsigned long size, int *hdrsize, struct patc
        return -1;
 }
 
+static void check_whitespace(const char *line, int len)
+{
+       const char *err = "Adds trailing whitespace";
+       int seen_space = 0;
+       int i;
+
+       /*
+        * We know len is at least two, since we have a '+' and we
+        * checked that the last character was a '\n' before calling
+        * this function.  That is, an addition of an empty line would
+        * check the '+' here.  Sneaky...
+        */
+       if (isspace(line[len-2]))
+               goto error;
+
+       /*
+        * Make sure that there is no space followed by a tab in
+        * indentation.
+        */
+       err = "Space in indent is followed by a tab";
+       for (i = 1; i < len; i++) {
+               if (line[i] == '\t') {
+                       if (seen_space)
+                               goto error;
+               }
+               else if (line[i] == ' ')
+                       seen_space = 1;
+               else
+                       break;
+       }
+       return;
+
+ error:
+       whitespace_error++;
+       if (squelch_whitespace_errors &&
+           squelch_whitespace_errors < whitespace_error)
+               ;
+       else
+               fprintf(stderr, "%s.\n%s:%d:%.*s\n",
+                       err, patch_input_file, linenr, len-2, line+1);
+}
+
+
 /*
  * Parse a unified diff. Note that this really needs to parse each
  * fragment separately, since the only way to know the difference
@@ -904,25 +947,8 @@ static int parse_fragment(char *line, unsigned long size, struct patch *patch, s
                        trailing = 0;
                        break;
                case '+':
-                       /*
-                        * We know len is at least two, since we have a '+' and
-                        * we checked that the last character was a '\n' above.
-                        * That is, an addition of an empty line would check
-                        * the '+' here.  Sneaky...
-                        */
-                       if ((new_whitespace != nowarn_whitespace) &&
-                           isspace(line[len-2])) {
-                               whitespace_error++;
-                               if (squelch_whitespace_errors &&
-                                   squelch_whitespace_errors <
-                                   whitespace_error)
-                                       ;
-                               else {
-                                       fprintf(stderr, "Adds trailing whitespace.\n%s:%d:%.*s\n",
-                                               patch_input_file,
-                                               linenr, len-2, line+1);
-                               }
-                       }
+                       if (new_whitespace != nowarn_whitespace)
+                               check_whitespace(line, len);
                        added++;
                        newlines--;
                        trailing = 0;
@@ -1494,22 +1520,68 @@ static int apply_line(char *output, const char *patch, int plen)
 {
        /* plen is number of bytes to be copied from patch,
         * starting at patch+1 (patch[0] is '+').  Typically
-        * patch[plen] is '\n'.
+        * patch[plen] is '\n', unless this is the incomplete
+        * last line.
         */
+       int i;
        int add_nl_to_tail = 0;
-       if ((new_whitespace == strip_whitespace) &&
-           1 < plen && isspace(patch[plen-1])) {
+       int fixed = 0;
+       int last_tab_in_indent = -1;
+       int last_space_in_indent = -1;
+       int need_fix_leading_space = 0;
+       char *buf;
+
+       if ((new_whitespace != strip_whitespace) || !whitespace_error) {
+               memcpy(output, patch + 1, plen);
+               return plen;
+       }
+
+       if (1 < plen && isspace(patch[plen-1])) {
                if (patch[plen] == '\n')
                        add_nl_to_tail = 1;
                plen--;
                while (0 < plen && isspace(patch[plen]))
                        plen--;
-               applied_after_stripping++;
+               fixed = 1;
        }
-       memcpy(output, patch + 1, plen);
+
+       for (i = 1; i < plen; i++) {
+               char ch = patch[i];
+               if (ch == '\t') {
+                       last_tab_in_indent = i;
+                       if (0 <= last_space_in_indent)
+                               need_fix_leading_space = 1;
+               }
+               else if (ch == ' ')
+                       last_space_in_indent = i;
+               else
+                       break;
+       }
+
+       buf = output;
+       if (need_fix_leading_space) {
+               /* between patch[1..last_tab_in_indent] strip the
+                * funny spaces, updating them to tab as needed.
+                */
+               for (i = 1; i < last_tab_in_indent; i++, plen--) {
+                       char ch = patch[i];
+                       if (ch != ' ')
+                               *output++ = ch;
+                       else if ((i % 8) == 0)
+                               *output++ = '\t';
+               }
+               fixed = 1;
+               i = last_tab_in_indent;
+       }
+       else
+               i = 1;
+
+       memcpy(output, patch + i, plen);
        if (add_nl_to_tail)
                output[plen++] = '\n';
-       return plen;
+       if (fixed)
+               applied_after_stripping++;
+       return output + plen - buf;
 }
 
 static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag, int inaccurate_eof)
index ed87a5550c7dab6de56327b8acc0e3e2c897e39e..4205e5d38dea6dee2d815a7434f87eacd089e4a9 100644 (file)
@@ -11,6 +11,7 @@
 #include "tree-walk.h"
 #include "builtin.h"
 #include <regex.h>
+#include "grep.h"
 #include <fnmatch.h>
 #include <sys/wait.h>
 
@@ -82,498 +83,6 @@ static int pathspec_matches(const char **paths, const char *name)
        return 0;
 }
 
-enum grep_pat_token {
-       GREP_PATTERN,
-       GREP_AND,
-       GREP_OPEN_PAREN,
-       GREP_CLOSE_PAREN,
-       GREP_NOT,
-       GREP_OR,
-};
-
-struct grep_pat {
-       struct grep_pat *next;
-       const char *origin;
-       int no;
-       enum grep_pat_token token;
-       const char *pattern;
-       regex_t regexp;
-};
-
-enum grep_expr_node {
-       GREP_NODE_ATOM,
-       GREP_NODE_NOT,
-       GREP_NODE_AND,
-       GREP_NODE_OR,
-};
-
-struct grep_expr {
-       enum grep_expr_node node;
-       union {
-               struct grep_pat *atom;
-               struct grep_expr *unary;
-               struct {
-                       struct grep_expr *left;
-                       struct grep_expr *right;
-               } binary;
-       } u;
-};
-
-struct grep_opt {
-       struct grep_pat *pattern_list;
-       struct grep_pat **pattern_tail;
-       struct grep_expr *pattern_expression;
-       int prefix_length;
-       regex_t regexp;
-       unsigned linenum:1;
-       unsigned invert:1;
-       unsigned name_only:1;
-       unsigned unmatch_name_only:1;
-       unsigned count:1;
-       unsigned word_regexp:1;
-       unsigned fixed:1;
-#define GREP_BINARY_DEFAULT    0
-#define GREP_BINARY_NOMATCH    1
-#define GREP_BINARY_TEXT       2
-       unsigned binary:2;
-       unsigned extended:1;
-       unsigned relative:1;
-       unsigned pathname:1;
-       int regflags;
-       unsigned pre_context;
-       unsigned post_context;
-};
-
-static void add_pattern(struct grep_opt *opt, const char *pat,
-                       const char *origin, int no, enum grep_pat_token t)
-{
-       struct grep_pat *p = xcalloc(1, sizeof(*p));
-       p->pattern = pat;
-       p->origin = origin;
-       p->no = no;
-       p->token = t;
-       *opt->pattern_tail = p;
-       opt->pattern_tail = &p->next;
-       p->next = NULL;
-}
-
-static void compile_regexp(struct grep_pat *p, struct grep_opt *opt)
-{
-       int err = regcomp(&p->regexp, p->pattern, opt->regflags);
-       if (err) {
-               char errbuf[1024];
-               char where[1024];
-               if (p->no)
-                       sprintf(where, "In '%s' at %d, ",
-                               p->origin, p->no);
-               else if (p->origin)
-                       sprintf(where, "%s, ", p->origin);
-               else
-                       where[0] = 0;
-               regerror(err, &p->regexp, errbuf, 1024);
-               regfree(&p->regexp);
-               die("%s'%s': %s", where, p->pattern, errbuf);
-       }
-}
-
-static struct grep_expr *compile_pattern_expr(struct grep_pat **);
-static struct grep_expr *compile_pattern_atom(struct grep_pat **list)
-{
-       struct grep_pat *p;
-       struct grep_expr *x;
-
-       p = *list;
-       switch (p->token) {
-       case GREP_PATTERN: /* atom */
-               x = xcalloc(1, sizeof (struct grep_expr));
-               x->node = GREP_NODE_ATOM;
-               x->u.atom = p;
-               *list = p->next;
-               return x;
-       case GREP_OPEN_PAREN:
-               *list = p->next;
-               x = compile_pattern_expr(list);
-               if (!x)
-                       return NULL;
-               if (!*list || (*list)->token != GREP_CLOSE_PAREN)
-                       die("unmatched parenthesis");
-               *list = (*list)->next;
-               return x;
-       default:
-               return NULL;
-       }
-}
-
-static struct grep_expr *compile_pattern_not(struct grep_pat **list)
-{
-       struct grep_pat *p;
-       struct grep_expr *x;
-
-       p = *list;
-       switch (p->token) {
-       case GREP_NOT:
-               if (!p->next)
-                       die("--not not followed by pattern expression");
-               *list = p->next;
-               x = xcalloc(1, sizeof (struct grep_expr));
-               x->node = GREP_NODE_NOT;
-               x->u.unary = compile_pattern_not(list);
-               if (!x->u.unary)
-                       die("--not followed by non pattern expression");
-               return x;
-       default:
-               return compile_pattern_atom(list);
-       }
-}
-
-static struct grep_expr *compile_pattern_and(struct grep_pat **list)
-{
-       struct grep_pat *p;
-       struct grep_expr *x, *y, *z;
-
-       x = compile_pattern_not(list);
-       p = *list;
-       if (p && p->token == GREP_AND) {
-               if (!p->next)
-                       die("--and not followed by pattern expression");
-               *list = p->next;
-               y = compile_pattern_and(list);
-               if (!y)
-                       die("--and not followed by pattern expression");
-               z = xcalloc(1, sizeof (struct grep_expr));
-               z->node = GREP_NODE_AND;
-               z->u.binary.left = x;
-               z->u.binary.right = y;
-               return z;
-       }
-       return x;
-}
-
-static struct grep_expr *compile_pattern_or(struct grep_pat **list)
-{
-       struct grep_pat *p;
-       struct grep_expr *x, *y, *z;
-
-       x = compile_pattern_and(list);
-       p = *list;
-       if (x && p && p->token != GREP_CLOSE_PAREN) {
-               y = compile_pattern_or(list);
-               if (!y)
-                       die("not a pattern expression %s", p->pattern);
-               z = xcalloc(1, sizeof (struct grep_expr));
-               z->node = GREP_NODE_OR;
-               z->u.binary.left = x;
-               z->u.binary.right = y;
-               return z;
-       }
-       return x;
-}
-
-static struct grep_expr *compile_pattern_expr(struct grep_pat **list)
-{
-       return compile_pattern_or(list);
-}
-
-static void compile_patterns(struct grep_opt *opt)
-{
-       struct grep_pat *p;
-
-       /* First compile regexps */
-       for (p = opt->pattern_list; p; p = p->next) {
-               if (p->token == GREP_PATTERN)
-                       compile_regexp(p, opt);
-               else
-                       opt->extended = 1;
-       }
-
-       if (!opt->extended)
-               return;
-
-       /* Then bundle them up in an expression.
-        * A classic recursive descent parser would do.
-        */
-       p = opt->pattern_list;
-       opt->pattern_expression = compile_pattern_expr(&p);
-       if (p)
-               die("incomplete pattern expression: %s", p->pattern);
-}
-
-static char *end_of_line(char *cp, unsigned long *left)
-{
-       unsigned long l = *left;
-       while (l && *cp != '\n') {
-               l--;
-               cp++;
-       }
-       *left = l;
-       return cp;
-}
-
-static int word_char(char ch)
-{
-       return isalnum(ch) || ch == '_';
-}
-
-static void show_line(struct grep_opt *opt, const char *bol, const char *eol,
-                     const char *name, unsigned lno, char sign)
-{
-       if (opt->pathname)
-               printf("%s%c", name, sign);
-       if (opt->linenum)
-               printf("%d%c", lno, sign);
-       printf("%.*s\n", (int)(eol-bol), bol);
-}
-
-/*
- * NEEDSWORK: share code with diff.c
- */
-#define FIRST_FEW_BYTES 8000
-static int buffer_is_binary(const char *ptr, unsigned long size)
-{
-       if (FIRST_FEW_BYTES < size)
-               size = FIRST_FEW_BYTES;
-       return !!memchr(ptr, 0, size);
-}
-
-static int fixmatch(const char *pattern, char *line, regmatch_t *match)
-{
-       char *hit = strstr(line, pattern);
-       if (!hit) {
-               match->rm_so = match->rm_eo = -1;
-               return REG_NOMATCH;
-       }
-       else {
-               match->rm_so = hit - line;
-               match->rm_eo = match->rm_so + strlen(pattern);
-               return 0;
-       }
-}
-
-static int match_one_pattern(struct grep_opt *opt, struct grep_pat *p, char *bol, char *eol)
-{
-       int hit = 0;
-       int at_true_bol = 1;
-       regmatch_t pmatch[10];
-
- again:
-       if (!opt->fixed) {
-               regex_t *exp = &p->regexp;
-               hit = !regexec(exp, bol, ARRAY_SIZE(pmatch),
-                              pmatch, 0);
-       }
-       else {
-               hit = !fixmatch(p->pattern, bol, pmatch);
-       }
-
-       if (hit && opt->word_regexp) {
-               if ((pmatch[0].rm_so < 0) ||
-                   (eol - bol) <= pmatch[0].rm_so ||
-                   (pmatch[0].rm_eo < 0) ||
-                   (eol - bol) < pmatch[0].rm_eo)
-                       die("regexp returned nonsense");
-
-               /* Match beginning must be either beginning of the
-                * line, or at word boundary (i.e. the last char must
-                * not be a word char).  Similarly, match end must be
-                * either end of the line, or at word boundary
-                * (i.e. the next char must not be a word char).
-                */
-               if ( ((pmatch[0].rm_so == 0 && at_true_bol) ||
-                     !word_char(bol[pmatch[0].rm_so-1])) &&
-                    ((pmatch[0].rm_eo == (eol-bol)) ||
-                     !word_char(bol[pmatch[0].rm_eo])) )
-                       ;
-               else
-                       hit = 0;
-
-               if (!hit && pmatch[0].rm_so + bol + 1 < eol) {
-                       /* There could be more than one match on the
-                        * line, and the first match might not be
-                        * strict word match.  But later ones could be!
-                        */
-                       bol = pmatch[0].rm_so + bol + 1;
-                       at_true_bol = 0;
-                       goto again;
-               }
-       }
-       return hit;
-}
-
-static int match_expr_eval(struct grep_opt *opt,
-                          struct grep_expr *x,
-                          char *bol, char *eol)
-{
-       switch (x->node) {
-       case GREP_NODE_ATOM:
-               return match_one_pattern(opt, x->u.atom, bol, eol);
-               break;
-       case GREP_NODE_NOT:
-               return !match_expr_eval(opt, x->u.unary, bol, eol);
-       case GREP_NODE_AND:
-               return (match_expr_eval(opt, x->u.binary.left, bol, eol) &&
-                       match_expr_eval(opt, x->u.binary.right, bol, eol));
-       case GREP_NODE_OR:
-               return (match_expr_eval(opt, x->u.binary.left, bol, eol) ||
-                       match_expr_eval(opt, x->u.binary.right, bol, eol));
-       }
-       die("Unexpected node type (internal error) %d\n", x->node);
-}
-
-static int match_expr(struct grep_opt *opt, char *bol, char *eol)
-{
-       struct grep_expr *x = opt->pattern_expression;
-       return match_expr_eval(opt, x, bol, eol);
-}
-
-static int match_line(struct grep_opt *opt, char *bol, char *eol)
-{
-       struct grep_pat *p;
-       if (opt->extended)
-               return match_expr(opt, bol, eol);
-       for (p = opt->pattern_list; p; p = p->next) {
-               if (match_one_pattern(opt, p, bol, eol))
-                       return 1;
-       }
-       return 0;
-}
-
-static int grep_buffer(struct grep_opt *opt, const char *name,
-                      char *buf, unsigned long size)
-{
-       char *bol = buf;
-       unsigned long left = size;
-       unsigned lno = 1;
-       struct pre_context_line {
-               char *bol;
-               char *eol;
-       } *prev = NULL, *pcl;
-       unsigned last_hit = 0;
-       unsigned last_shown = 0;
-       int binary_match_only = 0;
-       const char *hunk_mark = "";
-       unsigned count = 0;
-
-       if (buffer_is_binary(buf, size)) {
-               switch (opt->binary) {
-               case GREP_BINARY_DEFAULT:
-                       binary_match_only = 1;
-                       break;
-               case GREP_BINARY_NOMATCH:
-                       return 0; /* Assume unmatch */
-                       break;
-               default:
-                       break;
-               }
-       }
-
-       if (opt->pre_context)
-               prev = xcalloc(opt->pre_context, sizeof(*prev));
-       if (opt->pre_context || opt->post_context)
-               hunk_mark = "--\n";
-
-       while (left) {
-               char *eol, ch;
-               int hit = 0;
-
-               eol = end_of_line(bol, &left);
-               ch = *eol;
-               *eol = 0;
-
-               hit = match_line(opt, bol, eol);
-
-               /* "grep -v -e foo -e bla" should list lines
-                * that do not have either, so inversion should
-                * be done outside.
-                */
-               if (opt->invert)
-                       hit = !hit;
-               if (opt->unmatch_name_only) {
-                       if (hit)
-                               return 0;
-                       goto next_line;
-               }
-               if (hit) {
-                       count++;
-                       if (binary_match_only) {
-                               printf("Binary file %s matches\n", name);
-                               return 1;
-                       }
-                       if (opt->name_only) {
-                               printf("%s\n", 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) {
-                               unsigned from;
-                               if (opt->pre_context < lno)
-                                       from = lno - opt->pre_context;
-                               else
-                                       from = 1;
-                               if (from <= last_shown)
-                                       from = last_shown + 1;
-                               if (last_shown && from != last_shown + 1)
-                                       printf(hunk_mark);
-                               while (from < lno) {
-                                       pcl = &prev[lno-from-1];
-                                       show_line(opt, pcl->bol, pcl->eol,
-                                                 name, from, '-');
-                                       from++;
-                               }
-                               last_shown = lno-1;
-                       }
-                       if (last_shown && lno != last_shown + 1)
-                               printf(hunk_mark);
-                       if (!opt->count)
-                               show_line(opt, bol, eol, name, lno, ':');
-                       last_shown = last_hit = lno;
-               }
-               else if (last_hit &&
-                        lno <= last_hit + opt->post_context) {
-                       /* If the last hit is within the post context,
-                        * we need to show this line.
-                        */
-                       if (last_shown && lno != last_shown + 1)
-                               printf(hunk_mark);
-                       show_line(opt, bol, eol, name, lno, '-');
-                       last_shown = lno;
-               }
-               if (opt->pre_context) {
-                       memmove(prev+1, prev,
-                               (opt->pre_context-1) * sizeof(*prev));
-                       prev->bol = bol;
-                       prev->eol = eol;
-               }
-
-       next_line:
-               *eol = ch;
-               bol = eol + 1;
-               if (!left)
-                       break;
-               left--;
-               lno++;
-       }
-
-       if (opt->unmatch_name_only) {
-               /* We did not see any hit, so we want to show this */
-               printf("%s\n", name);
-               return 1;
-       }
-
-       /* NEEDSWORK:
-        * The real "grep -c foo *.c" gives many "bar.c:0" lines,
-        * which feels mostly useless but sometimes useful.  Maybe
-        * make it another option?  For now suppress them.
-        */
-       if (opt->count && count)
-               printf("%s:%u\n", name, count);
-       return !!last_hit;
-}
-
 static int grep_sha1(struct grep_opt *opt, const unsigned char *sha1, const char *name, int tree_name_len)
 {
        unsigned long size;
@@ -816,6 +325,7 @@ static int grep_cache(struct grep_opt *opt, const char **paths, int cached)
                else
                        hit |= grep_file(opt, ce->name);
        }
+       free_grep_patterns(opt);
        return hit;
 }
 
@@ -1055,8 +565,9 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
                                /* ignore empty line like grep does */
                                if (!buf[0])
                                        continue;
-                               add_pattern(&opt, xstrdup(buf), argv[1], ++lno,
-                                           GREP_PATTERN);
+                               append_grep_pattern(&opt, xstrdup(buf),
+                                                   argv[1], ++lno,
+                                                   GREP_PATTERN);
                        }
                        fclose(patterns);
                        argv++;
@@ -1064,27 +575,32 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
                        continue;
                }
                if (!strcmp("--not", arg)) {
-                       add_pattern(&opt, arg, "command line", 0, GREP_NOT);
+                       append_grep_pattern(&opt, arg, "command line", 0,
+                                           GREP_NOT);
                        continue;
                }
                if (!strcmp("--and", arg)) {
-                       add_pattern(&opt, arg, "command line", 0, GREP_AND);
+                       append_grep_pattern(&opt, arg, "command line", 0,
+                                           GREP_AND);
                        continue;
                }
                if (!strcmp("--or", arg))
                        continue; /* no-op */
                if (!strcmp("(", arg)) {
-                       add_pattern(&opt, arg, "command line", 0, GREP_OPEN_PAREN);
+                       append_grep_pattern(&opt, arg, "command line", 0,
+                                           GREP_OPEN_PAREN);
                        continue;
                }
                if (!strcmp(")", arg)) {
-                       add_pattern(&opt, arg, "command line", 0, GREP_CLOSE_PAREN);
+                       append_grep_pattern(&opt, arg, "command line", 0,
+                                           GREP_CLOSE_PAREN);
                        continue;
                }
                if (!strcmp("-e", arg)) {
                        if (1 < argc) {
-                               add_pattern(&opt, argv[1], "-e option", 0,
-                                           GREP_PATTERN);
+                               append_grep_pattern(&opt, argv[1],
+                                                   "-e option", 0,
+                                                   GREP_PATTERN);
                                argv++;
                                argc--;
                                continue;
@@ -1106,8 +622,8 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
 
                /* First unrecognized non-option token */
                if (!opt.pattern_list) {
-                       add_pattern(&opt, arg, "command line", 0,
-                                   GREP_PATTERN);
+                       append_grep_pattern(&opt, arg, "command line", 0,
+                                           GREP_PATTERN);
                        break;
                }
                else {
@@ -1124,8 +640,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
                die("no pattern given.");
        if ((opt.regflags != REG_NEWLINE) && opt.fixed)
                die("cannot mix --fixed-strings and regexp");
-       if (!opt.fixed)
-               compile_patterns(&opt);
+       compile_grep_patterns(&opt);
 
        /* Check revs and then paths */
        for (i = 1; i < argc; i++) {
@@ -1180,5 +695,6 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
                if (grep_object(&opt, paths, real_obj, list.objects[i].name))
                        hit = 1;
        }
+       free_grep_patterns(&opt);
        return !hit;
 }
index 23b7714f894019c7d59fc9ede00d83c008d6bc89..235a0ee48f2c5ce09c63a949358eb16cde05332d 100644 (file)
@@ -311,6 +311,7 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
                 */
                sprintf(buf, "%d", shared_repository);
                git_config_set("core.sharedrepository", buf);
+               git_config_set("receive.denyNonFastforwards", "true");
        }
 
        return 0;
index 0c65f9314501bc8c704d326ceeb1ae99bc974b28..b8d7dbc0b71929a95aaebd79d5912897d5eb70e7 100644 (file)
@@ -451,17 +451,6 @@ static int read_one_header_line(char *line, int sz, FILE *in)
        return ofs;
 }
 
-static unsigned hexval(int c)
-{
-       if (c >= '0' && c <= '9')
-               return c - '0';
-       if (c >= 'a' && c <= 'f')
-               return c - 'a' + 10;
-       if (c >= 'A' && c <= 'F')
-               return c - 'A' + 10;
-       return ~0;
-}
-
 static int decode_q_segment(char *in, char *ot, char *ep, int rfc2047)
 {
        int c;
index 8d7a1209d5effe83eb93ad4d0f5088806d625c70..96c069a81da643b7ee3515ca8c89734881fa3b77 100644 (file)
@@ -597,15 +597,15 @@ static int add_object_entry(const unsigned char *sha1, unsigned hash, int exclud
 
        if (!exclude) {
                for (p = packed_git; p; p = p->next) {
-                       struct pack_entry e;
-                       if (find_pack_entry_one(sha1, &e, p)) {
+                       unsigned long offset = find_pack_entry_one(sha1, p);
+                       if (offset) {
                                if (incremental)
                                        return 0;
                                if (local && !p->pack_local)
                                        return 0;
                                if (!found_pack) {
-                                       found_offset = e.offset;
-                                       found_pack = e.p;
+                                       found_offset = offset;
+                                       found_pack = p;
                                }
                        }
                }
index 1f3333da38c77a07840a8dfad052c301dbae9392..fb7fc92145b6a8baec340176abf40c6b37f45a8e 100644 (file)
@@ -269,7 +269,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
            revs.diff)
                usage(rev_list_usage);
 
-       save_commit_buffer = revs.verbose_header;
+       save_commit_buffer = revs.verbose_header || revs.grep_filter;
        track_object_refs = 0;
        if (bisect_list)
                revs.limited = 1;
index 437eb726a9b207353545726f379ab8638c0eecb9..4d4cfec878e468b8ab8a7f055c836c3569443914 100644 (file)
 #include <time.h>
 #include "cache.h"
 #include "commit.h"
-#include "strbuf.h"
 #include "tar.h"
 #include "builtin.h"
-#include "pkt-line.h"
-#include "archive.h"
-
-#define RECORDSIZE     (512)
-#define BLOCKSIZE      (RECORDSIZE * 20)
+#include "quote.h"
 
 static const char tar_tree_usage[] =
-"git-tar-tree [--remote=<repo>] <tree-ish> [basedir]";
-
-static char block[BLOCKSIZE];
-static unsigned long offset;
-
-static time_t archive_time;
-static int tar_umask;
-static int verbose;
-
-/* writes out the whole block, but only if it is full */
-static void write_if_needed(void)
-{
-       if (offset == BLOCKSIZE) {
-               write_or_die(1, block, BLOCKSIZE);
-               offset = 0;
-       }
-}
-
-/*
- * queues up writes, so that all our write(2) calls write exactly one
- * full block; pads writes to RECORDSIZE
- */
-static void write_blocked(const void *data, unsigned long size)
-{
-       const char *buf = data;
-       unsigned long tail;
-
-       if (offset) {
-               unsigned long chunk = BLOCKSIZE - offset;
-               if (size < chunk)
-                       chunk = size;
-               memcpy(block + offset, buf, chunk);
-               size -= chunk;
-               offset += chunk;
-               buf += chunk;
-               write_if_needed();
-       }
-       while (size >= BLOCKSIZE) {
-               write_or_die(1, buf, BLOCKSIZE);
-               size -= BLOCKSIZE;
-               buf += BLOCKSIZE;
-       }
-       if (size) {
-               memcpy(block + offset, buf, size);
-               offset += size;
-       }
-       tail = offset % RECORDSIZE;
-       if (tail)  {
-               memset(block + offset, 0, RECORDSIZE - tail);
-               offset += RECORDSIZE - tail;
-       }
-       write_if_needed();
-}
-
-/*
- * The end of tar archives is marked by 2*512 nul bytes and after that
- * follows the rest of the block (if any).
- */
-static void write_trailer(void)
-{
-       int tail = BLOCKSIZE - offset;
-       memset(block + offset, 0, tail);
-       write_or_die(1, block, BLOCKSIZE);
-       if (tail < 2 * RECORDSIZE) {
-               memset(block, 0, offset);
-               write_or_die(1, block, BLOCKSIZE);
-       }
-}
-
-static void strbuf_append_string(struct strbuf *sb, const char *s)
-{
-       int slen = strlen(s);
-       int total = sb->len + slen;
-       if (total > sb->alloc) {
-               sb->buf = xrealloc(sb->buf, total);
-               sb->alloc = total;
-       }
-       memcpy(sb->buf + sb->len, s, slen);
-       sb->len = total;
-}
-
-/*
- * pax extended header records have the format "%u %s=%s\n".  %u contains
- * the size of the whole string (including the %u), the first %s is the
- * keyword, the second one is the value.  This function constructs such a
- * string and appends it to a struct strbuf.
- */
-static void strbuf_append_ext_header(struct strbuf *sb, const char *keyword,
-                                     const char *value, unsigned int valuelen)
-{
-       char *p;
-       int len, total, tmp;
-
-       /* "%u %s=%s\n" */
-       len = 1 + 1 + strlen(keyword) + 1 + valuelen + 1;
-       for (tmp = len; tmp > 9; tmp /= 10)
-               len++;
-
-       total = sb->len + len;
-       if (total > sb->alloc) {
-               sb->buf = xrealloc(sb->buf, total);
-               sb->alloc = total;
-       }
-
-       p = sb->buf;
-       p += sprintf(p, "%u %s=", len, keyword);
-       memcpy(p, value, valuelen);
-       p += valuelen;
-       *p = '\n';
-       sb->len = total;
-}
-
-static unsigned int ustar_header_chksum(const struct ustar_header *header)
-{
-       char *p = (char *)header;
-       unsigned int chksum = 0;
-       while (p < header->chksum)
-               chksum += *p++;
-       chksum += sizeof(header->chksum) * ' ';
-       p += sizeof(header->chksum);
-       while (p < (char *)header + sizeof(struct ustar_header))
-               chksum += *p++;
-       return chksum;
-}
-
-static int get_path_prefix(const struct strbuf *path, int maxlen)
-{
-       int i = path->len;
-       if (i > maxlen)
-               i = maxlen;
-       do {
-               i--;
-       } while (i > 0 && path->buf[i] != '/');
-       return i;
-}
-
-static void write_entry(const unsigned char *sha1, struct strbuf *path,
-                        unsigned int mode, void *buffer, unsigned long size)
-{
-       struct ustar_header header;
-       struct strbuf ext_header;
-
-       memset(&header, 0, sizeof(header));
-       ext_header.buf = NULL;
-       ext_header.len = ext_header.alloc = 0;
-
-       if (!sha1) {
-               *header.typeflag = TYPEFLAG_GLOBAL_HEADER;
-               mode = 0100666;
-               strcpy(header.name, "pax_global_header");
-       } else if (!path) {
-               *header.typeflag = TYPEFLAG_EXT_HEADER;
-               mode = 0100666;
-               sprintf(header.name, "%s.paxheader", sha1_to_hex(sha1));
-       } else {
-               if (verbose)
-                       fprintf(stderr, "%.*s\n", path->len, path->buf);
-               if (S_ISDIR(mode)) {
-                       *header.typeflag = TYPEFLAG_DIR;
-                       mode = (mode | 0777) & ~tar_umask;
-               } else if (S_ISLNK(mode)) {
-                       *header.typeflag = TYPEFLAG_LNK;
-                       mode |= 0777;
-               } else if (S_ISREG(mode)) {
-                       *header.typeflag = TYPEFLAG_REG;
-                       mode = (mode | ((mode & 0100) ? 0777 : 0666)) & ~tar_umask;
-               } else {
-                       error("unsupported file mode: 0%o (SHA1: %s)",
-                             mode, sha1_to_hex(sha1));
-                       return;
-               }
-               if (path->len > sizeof(header.name)) {
-                       int plen = get_path_prefix(path, sizeof(header.prefix));
-                       int rest = path->len - plen - 1;
-                       if (plen > 0 && rest <= sizeof(header.name)) {
-                               memcpy(header.prefix, path->buf, plen);
-                               memcpy(header.name, path->buf + plen + 1, rest);
-                       } else {
-                               sprintf(header.name, "%s.data",
-                                       sha1_to_hex(sha1));
-                               strbuf_append_ext_header(&ext_header, "path",
-                                                        path->buf, path->len);
-                       }
-               } else
-                       memcpy(header.name, path->buf, path->len);
-       }
-
-       if (S_ISLNK(mode) && buffer) {
-               if (size > sizeof(header.linkname)) {
-                       sprintf(header.linkname, "see %s.paxheader",
-                               sha1_to_hex(sha1));
-                       strbuf_append_ext_header(&ext_header, "linkpath",
-                                                buffer, size);
-               } else
-                       memcpy(header.linkname, buffer, size);
-       }
-
-       sprintf(header.mode, "%07o", mode & 07777);
-       sprintf(header.size, "%011lo", S_ISREG(mode) ? size : 0);
-       sprintf(header.mtime, "%011lo", archive_time);
-
-       /* XXX: should we provide more meaningful info here? */
-       sprintf(header.uid, "%07o", 0);
-       sprintf(header.gid, "%07o", 0);
-       strlcpy(header.uname, "git", sizeof(header.uname));
-       strlcpy(header.gname, "git", sizeof(header.gname));
-       sprintf(header.devmajor, "%07o", 0);
-       sprintf(header.devminor, "%07o", 0);
-
-       memcpy(header.magic, "ustar", 6);
-       memcpy(header.version, "00", 2);
-
-       sprintf(header.chksum, "%07o", ustar_header_chksum(&header));
-
-       if (ext_header.len > 0) {
-               write_entry(sha1, NULL, 0, ext_header.buf, ext_header.len);
-               free(ext_header.buf);
-       }
-       write_blocked(&header, sizeof(header));
-       if (S_ISREG(mode) && buffer && size > 0)
-               write_blocked(buffer, size);
-}
-
-static void write_global_extended_header(const unsigned char *sha1)
-{
-       struct strbuf ext_header;
-       ext_header.buf = NULL;
-       ext_header.len = ext_header.alloc = 0;
-       strbuf_append_ext_header(&ext_header, "comment", sha1_to_hex(sha1), 40);
-       write_entry(NULL, NULL, 0, ext_header.buf, ext_header.len);
-       free(ext_header.buf);
-}
-
-static int git_tar_config(const char *var, const char *value)
-{
-       if (!strcmp(var, "tar.umask")) {
-               if (!strcmp(value, "user")) {
-                       tar_umask = umask(0);
-                       umask(tar_umask);
-               } else {
-                       tar_umask = git_config_int(var, value);
-               }
-               return 0;
-       }
-       return git_default_config(var, value);
-}
-
-static int generate_tar(int argc, const char **argv, const char *prefix)
-{
-       struct archiver_args args;
-       int result;
-       char *base = NULL;
-
-       git_config(git_tar_config);
-
-       memset(&args, 0, sizeof(args));
-       if (argc != 2 && argc != 3)
-               usage(tar_tree_usage);
-       if (argc == 3) {
-               int baselen = strlen(argv[2]);
-               base = xmalloc(baselen + 2);
-               memcpy(base, argv[2], baselen);
-               base[baselen] = '/';
-               base[baselen + 1] = '\0';
-       }
-       args.base = base;
-       parse_treeish_arg(argv + 1, &args, NULL);
-
-       result = write_tar_archive(&args);
-       free(base);
-
-       return result;
-}
-
-static int write_tar_entry(const unsigned char *sha1,
-                           const char *base, int baselen,
-                           const char *filename, unsigned mode, int stage)
-{
-       static struct strbuf path;
-       int filenamelen = strlen(filename);
-       void *buffer;
-       char type[20];
-       unsigned long size;
-
-       if (!path.alloc) {
-               path.buf = xmalloc(PATH_MAX);
-               path.alloc = PATH_MAX;
-               path.len = path.eof = 0;
-       }
-       if (path.alloc < baselen + filenamelen) {
-               free(path.buf);
-               path.buf = xmalloc(baselen + filenamelen);
-               path.alloc = baselen + filenamelen;
-       }
-       memcpy(path.buf, base, baselen);
-       memcpy(path.buf + baselen, filename, filenamelen);
-       path.len = baselen + filenamelen;
-       if (S_ISDIR(mode)) {
-               strbuf_append_string(&path, "/");
-               buffer = NULL;
-               size = 0;
-       } else {
-               buffer = read_sha1_file(sha1, type, &size);
-               if (!buffer)
-                       die("cannot read %s", sha1_to_hex(sha1));
-       }
-
-       write_entry(sha1, &path, mode, buffer, size);
-       free(buffer);
-
-       return READ_TREE_RECURSIVE;
-}
-
-int write_tar_archive(struct archiver_args *args)
-{
-       int plen = args->base ? strlen(args->base) : 0;
-
-       git_config(git_tar_config);
-
-       archive_time = args->time;
-       verbose = args->verbose;
-
-       if (args->commit_sha1)
-               write_global_extended_header(args->commit_sha1);
-
-       if (args->base && plen > 0 && args->base[plen - 1] == '/') {
-               char *base = xstrdup(args->base);
-               int baselen = strlen(base);
-
-               while (baselen > 0 && base[baselen - 1] == '/')
-                       base[--baselen] = '\0';
-               write_tar_entry(args->tree->object.sha1, "", 0, base, 040777, 0);
-               free(base);
-       }
-       read_tree_recursive(args->tree, args->base, plen, 0,
-                           args->pathspec, write_tar_entry);
-       write_trailer();
-
-       return 0;
-}
-
-static const char *exec = "git-upload-tar";
-
-static int remote_tar(int argc, const char **argv)
-{
-       int fd[2], ret, len;
-       pid_t pid;
-       char buf[1024];
-       char *url;
-
-       if (argc < 3 || 4 < argc)
-               usage(tar_tree_usage);
-
-       /* --remote=<repo> */
-       url = xstrdup(argv[1]+9);
-       pid = git_connect(fd, url, exec);
-       if (pid < 0)
-               return 1;
-
-       packet_write(fd[1], "want %s\n", argv[2]);
-       if (argv[3])
-               packet_write(fd[1], "base %s\n", argv[3]);
-       packet_flush(fd[1]);
-
-       len = packet_read_line(fd[0], buf, sizeof(buf));
-       if (!len)
-               die("git-tar-tree: expected ACK/NAK, got EOF");
-       if (buf[len-1] == '\n')
-               buf[--len] = 0;
-       if (strcmp(buf, "ACK")) {
-               if (5 < len && !strncmp(buf, "NACK ", 5))
-                       die("git-tar-tree: NACK %s", buf + 5);
-               die("git-tar-tree: protocol error");
-       }
-       /* expect a flush */
-       len = packet_read_line(fd[0], buf, sizeof(buf));
-       if (len)
-               die("git-tar-tree: expected a flush");
-
-       /* Now, start reading from fd[0] and spit it out to stdout */
-       ret = copy_fd(fd[0], 1);
-       close(fd[0]);
-
-       ret |= finish_connect(pid);
-       return !!ret;
-}
+"git-tar-tree [--remote=<repo>] <tree-ish> [basedir]\n"
+"*** Note that this command is now deprecated; use git-archive instead.";
 
 int cmd_tar_tree(int argc, const char **argv, const char *prefix)
 {
-       if (argc < 2)
+       /*
+        * git-tar-tree is now a wrapper around git-archive --format=tar
+        *
+        * $0 --remote=<repo> arg... ==>
+        *      git-archive --format=tar --remote=<repo> arg...
+        * $0 tree-ish ==>
+        *      git-archive --format=tar tree-ish
+        * $0 tree-ish basedir ==>
+        *      git-archive --format-tar --prefix=basedir tree-ish
+        */
+       int i;
+       const char **nargv = xcalloc(sizeof(*nargv), argc + 2);
+       char *basedir_arg;
+       int nargc = 0;
+
+       nargv[nargc++] = "git-archive";
+       nargv[nargc++] = "--format=tar";
+
+       if (2 <= argc && !strncmp("--remote=", argv[1], 9)) {
+               nargv[nargc++] = argv[1];
+               argv++;
+               argc--;
+       }
+       switch (argc) {
+       default:
                usage(tar_tree_usage);
-       if (!strncmp("--remote=", argv[1], 9))
-               return remote_tar(argc, argv);
-       return generate_tar(argc, argv, prefix);
+               break;
+       case 3:
+               /* base-path */
+               basedir_arg = xmalloc(strlen(argv[2]) + 11);
+               sprintf(basedir_arg, "--prefix=%s/", argv[2]);
+               nargv[nargc++] = basedir_arg;
+               /* fallthru */
+       case 2:
+               /* tree-ish */
+               nargv[nargc++] = argv[1];
+       }
+       nargv[nargc] = NULL;
+
+       fprintf(stderr,
+               "*** git-tar-tree is now deprecated.\n"
+               "*** Running git-archive instead.\n***");
+       for (i = 0; i < nargc; i++) {
+               fputc(' ', stderr);
+               sq_quote_print(stderr, nargv[i]);
+       }
+       fputc('\n', stderr);
+       return cmd_archive(nargc, nargv, prefix);
 }
 
 /* ustar header + extended global header content */
+#define RECORDSIZE     (512)
 #define HEADERSIZE (2 * RECORDSIZE)
 
 int cmd_get_tar_commit_id(int argc, const char **argv, const char *prefix)
index 09214c8a11e6afb927ab6da1aa3f0be5e50888c8..7f9c638466f79fee7d2914b8ec76d713c3670e7e 100644 (file)
@@ -112,11 +112,13 @@ static int add_file_to_cache(const char *path)
        ce->ce_mode = create_ce_mode(st.st_mode);
        if (!trust_executable_bit) {
                /* If there is an existing entry, pick the mode bits
-                * from it.
+                * from it, otherwise force to 644.
                 */
                int pos = cache_name_pos(path, namelen);
                if (0 <= pos)
                        ce->ce_mode = active_cache[pos]->ce_mode;
+               else
+                       ce->ce_mode = create_ce_mode(S_IFREG | 0644);
        }
 
        if (index_path(ce->sha1, path, &st, !info_only))
index 0596865679d79e1f02877ea47fb910a7c771f38f..45c92e163c477af4ec7c36c8ee6fcfd9242a0b2d 100644 (file)
@@ -2,13 +2,13 @@
  * Copyright (c) 2006 Franck Bui-Huu
  */
 #include <time.h>
+#include <sys/wait.h>
+#include <sys/poll.h>
 #include "cache.h"
 #include "builtin.h"
 #include "archive.h"
 #include "pkt-line.h"
 #include "sideband.h"
-#include <sys/wait.h>
-#include <sys/poll.h>
 
 static const char upload_archive_usage[] =
        "git-upload-archive <repo>";
diff --git a/builtin-upload-tar.c b/builtin-upload-tar.c
deleted file mode 100644 (file)
index 06a945a..0000000
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright (c) 2006 Junio C Hamano
- */
-#include "cache.h"
-#include "pkt-line.h"
-#include "exec_cmd.h"
-#include "builtin.h"
-
-static const char upload_tar_usage[] = "git-upload-tar <repo>";
-
-static int nak(const char *reason)
-{
-       packet_write(1, "NACK %s\n", reason);
-       packet_flush(1);
-       return 1;
-}
-
-int cmd_upload_tar(int argc, const char **argv, const char *prefix)
-{
-       int len;
-       const char *dir = argv[1];
-       char buf[8192];
-       unsigned char sha1[20];
-       char *base = NULL;
-       char hex[41];
-       int ac;
-       const char *av[4];
-
-       if (argc != 2)
-               usage(upload_tar_usage);
-       if (strlen(dir) < sizeof(buf)-1)
-               strcpy(buf, dir); /* enter-repo smudges its argument */
-       else
-               packet_write(1, "NACK insanely long repository name %s\n", dir);
-       if (!enter_repo(buf, 0)) {
-               packet_write(1, "NACK not a git archive %s\n", dir);
-               packet_flush(1);
-               return 1;
-       }
-
-       len = packet_read_line(0, buf, sizeof(buf));
-       if (len < 5 || strncmp("want ", buf, 5))
-               return nak("expected want");
-       if (buf[len-1] == '\n')
-               buf[--len] = 0;
-       if (get_sha1(buf + 5, sha1))
-               return nak("expected sha1");
-        strcpy(hex, sha1_to_hex(sha1));
-
-       len = packet_read_line(0, buf, sizeof(buf));
-       if (len) {
-               if (len < 5 || strncmp("base ", buf, 5))
-                       return nak("expected (optional) base");
-               if (buf[len-1] == '\n')
-                       buf[--len] = 0;
-               base = xstrdup(buf + 5);
-               len = packet_read_line(0, buf, sizeof(buf));
-       }
-       if (len)
-               return nak("expected flush");
-
-       packet_write(1, "ACK\n");
-       packet_flush(1);
-
-       ac = 0;
-       av[ac++] = "tar-tree";
-       av[ac++] = hex;
-       if (base)
-               av[ac++] = base;
-       av[ac++] = NULL;
-       execv_git_cmd(av);
-       /* should it return that is an error */
-       return 1;
-}
index 6bf0ace37579da68d8a01e928008f5d58b2e958f..721b8d8037655a0a5a5edad07838867a64bf6eda 100644 (file)
--- a/builtin.h
+++ b/builtin.h
@@ -54,7 +54,6 @@ extern int cmd_show_branch(int argc, const char **argv, const char *prefix);
 extern int cmd_stripspace(int argc, const char **argv, const char *prefix);
 extern int cmd_symbolic_ref(int argc, const char **argv, const char *prefix);
 extern int cmd_tar_tree(int argc, const char **argv, const char *prefix);
-extern int cmd_zip_tree(int argc, const char **argv, const char *prefix);
 extern int cmd_unpack_objects(int argc, const char **argv, const char *prefix);
 extern int cmd_update_index(int argc, const char **argv, const char *prefix);
 extern int cmd_update_ref(int argc, const char **argv, const char *prefix);
diff --git a/cache.h b/cache.h
index 6e004505be5091146302c75b12a4538c915092da..0565333f05e3fc95fb83d472a0590a5efb615af2 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -189,6 +189,7 @@ extern int prefer_symlink_refs;
 extern int log_all_ref_updates;
 extern int warn_ambiguous_refs;
 extern int shared_repository;
+extern int deny_non_fast_forwards;
 extern const char *apply_default_whitespace;
 extern int zlib_compression_level;
 
@@ -279,6 +280,12 @@ enum object_type {
        OBJ_BAD,
 };
 
+extern signed char hexval_table[256];
+static inline unsigned int hexval(unsigned int c)
+{
+       return hexval_table[c];
+}
+
 /* Convert to/from hex/sha1 representation */
 #define MINIMUM_ABBREV 4
 #define DEFAULT_ABBREV 7
@@ -384,10 +391,10 @@ extern void unuse_packed_git(struct packed_git *);
 extern struct packed_git *add_packed_git(char *, int, int);
 extern int num_packed_objects(const struct packed_git *p);
 extern int nth_packed_object_sha1(const struct packed_git *, int, unsigned char*);
-extern int find_pack_entry_one(const unsigned char *, struct pack_entry *, struct packed_git *);
-extern void *unpack_entry_gently(struct pack_entry *, char *, unsigned long *);
+extern unsigned long find_pack_entry_one(const unsigned char *, struct packed_git *);
+extern void *unpack_entry_gently(struct packed_git *, unsigned long, char *, unsigned long *);
 extern unsigned long unpack_object_header_gently(const unsigned char *buf, unsigned long len, enum object_type *type, unsigned long *sizep);
-extern void packed_object_info_detail(struct pack_entry *, char *, unsigned long *, unsigned long *, unsigned int *, unsigned char *);
+extern void packed_object_info_detail(struct packed_git *, unsigned long, char *, unsigned long *, unsigned long *, unsigned int *, unsigned char *);
 
 /* Dumb servers support */
 extern int update_server_info(int);
index 511cac93d6cec11f3717e810d0c80b27646a5713..b1a5833b40b905ae07b27c414833394ef63b0c99 100644 (file)
@@ -75,7 +75,6 @@ GIT_ARG_SET_PATH(shell)
 # Define PERL_PATH to provide path to Perl.
 GIT_ARG_SET_PATH(perl)
 #
-# Define NO_PYTHON if you want to lose all benefits of the recursive merge.
 # Define PYTHON_PATH to provide path to Python.
 AC_ARG_WITH(python,[AS_HELP_STRING([--with-python=PATH], [provide PATH to python])
 AS_HELP_STRING([--without-python], [don't use python scripts])],
@@ -100,7 +99,6 @@ AC_PROG_CC
 AC_CHECK_TOOL(AR, ar, :)
 AC_CHECK_PROGS(TAR, [gtar tar])
 #
-# Define NO_PYTHON if you want to lose all benefits of the recursive merge.
 # Define PYTHON_PATH to provide path to Python.
 if test -z "$NO_PYTHON"; then
        if test -z "$PYTHON_PATH"; then
index a2954a0451316197621aed152c194d398aaee624..eb4f3f1e9f08cd3a95f429a477495cf4151ddd86 100644 (file)
--- a/daemon.c
+++ b/daemon.c
@@ -12,6 +12,7 @@
 #include "pkt-line.h"
 #include "cache.h"
 #include "exec_cmd.h"
+#include "interpolate.h"
 
 static int log_syslog;
 static int verbose;
@@ -21,6 +22,7 @@ static const char daemon_usage[] =
 "git-daemon [--verbose] [--syslog] [--inetd | --port=n] [--export-all]\n"
 "           [--timeout=n] [--init-timeout=n] [--strict-paths]\n"
 "           [--base-path=path] [--user-path | --user-path=path]\n"
+"           [--interpolated-path=path]\n"
 "           [--reuseaddr] [--detach] [--pid-file=file]\n"
 "           [--[enable|disable|allow-override|forbid-override]=service]\n"
 "           [--user=user [[--group=group]] [directory...]";
@@ -34,6 +36,10 @@ static int export_all_trees;
 
 /* Take all paths relative to this one if non-NULL */
 static char *base_path;
+static char *interpolated_path;
+
+/* Flag indicating client sent extra args. */
+static int saw_extended_args;
 
 /* If defined, ~user notation is allowed and the string is inserted
  * after ~user/.  E.g. a request to git://host/~alice/frotz would
@@ -45,6 +51,21 @@ static const char *user_path;
 static unsigned int timeout;
 static unsigned int init_timeout;
 
+/*
+ * Static table for now.  Ugh.
+ * Feel free to make dynamic as needed.
+ */
+#define INTERP_SLOT_HOST       (0)
+#define INTERP_SLOT_DIR                (1)
+#define INTERP_SLOT_PERCENT    (2)
+
+static struct interp interp_table[] = {
+       { "%H", 0},
+       { "%D", 0},
+       { "%%", "%"},
+};
+
+
 static void logreport(int priority, const char *err, va_list params)
 {
        /* We should do a single write so that it is atomic and output
@@ -152,10 +173,14 @@ static int avoid_alias(char *p)
        }
 }
 
-static char *path_ok(char *dir)
+static char *path_ok(struct interp *itable)
 {
        static char rpath[PATH_MAX];
+       static char interp_path[PATH_MAX];
        char *path;
+       char *dir;
+
+       dir = itable[INTERP_SLOT_DIR].value;
 
        if (avoid_alias(dir)) {
                logerror("'%s': aliased", dir);
@@ -184,16 +209,27 @@ static char *path_ok(char *dir)
                        dir = rpath;
                }
        }
+       else if (interpolated_path && saw_extended_args) {
+               if (*dir != '/') {
+                       /* Allow only absolute */
+                       logerror("'%s': Non-absolute path denied (interpolated-path active)", dir);
+                       return NULL;
+               }
+
+               interpolate(interp_path, PATH_MAX, interpolated_path,
+                           interp_table, ARRAY_SIZE(interp_table));
+               loginfo("Interpolated dir '%s'", interp_path);
+
+               dir = interp_path;
+       }
        else if (base_path) {
                if (*dir != '/') {
                        /* Allow only absolute */
                        logerror("'%s': Non-absolute path denied (base-path active)", dir);
                        return NULL;
                }
-               else {
-                       snprintf(rpath, PATH_MAX, "%s%s", base_path, dir);
-                       dir = rpath;
-               }
+               snprintf(rpath, PATH_MAX, "%s%s", base_path, dir);
+               dir = rpath;
        }
 
        path = enter_repo(dir, strict_paths);
@@ -257,12 +293,14 @@ static int git_daemon_config(const char *var, const char *value)
        return 0;
 }
 
-static int run_service(char *dir, struct daemon_service *service)
+static int run_service(struct interp *itable, struct daemon_service *service)
 {
        const char *path;
        int enabled = service->enabled;
 
-       loginfo("Request %s for '%s'", service->name, dir);
+       loginfo("Request %s for '%s'",
+               service->name,
+               itable[INTERP_SLOT_DIR].value);
 
        if (!enabled && !service->overridable) {
                logerror("'%s': service not enabled.", service->name);
@@ -270,7 +308,7 @@ static int run_service(char *dir, struct daemon_service *service)
                return -1;
        }
 
-       if (!(path = path_ok(dir)))
+       if (!(path = path_ok(itable)))
                return -1;
 
        /*
@@ -358,6 +396,28 @@ static void make_service_overridable(const char *name, int ena) {
        die("No such service %s", name);
 }
 
+static void parse_extra_args(char *extra_args, int buflen)
+{
+       char *val;
+       int vallen;
+       char *end = extra_args + buflen;
+
+       while (extra_args < end && *extra_args) {
+               saw_extended_args = 1;
+               if (strncasecmp("host=", extra_args, 5) == 0) {
+                       val = extra_args + 5;
+                       vallen = strlen(val) + 1;
+                       if (*val) {
+                               char *save = xmalloc(vallen);
+                               interp_table[INTERP_SLOT_HOST].value = save;
+                               strlcpy(save, val, vallen);
+                       }
+                       /* On to the next one */
+                       extra_args = val + vallen;
+               }
+       }
+}
+
 static int execute(struct sockaddr *addr)
 {
        static char line[1000];
@@ -398,13 +458,18 @@ static int execute(struct sockaddr *addr)
        if (len && line[len-1] == '\n')
                line[--len] = 0;
 
+       if (len != pktlen)
+           parse_extra_args(line + len + 1, pktlen - len - 1);
+
        for (i = 0; i < ARRAY_SIZE(daemon_service); i++) {
                struct daemon_service *s = &(daemon_service[i]);
                int namelen = strlen(s->name);
                if (!strncmp("git-", line, 4) &&
                    !strncmp(s->name, line + 4, namelen) &&
-                   line[namelen + 4] == ' ')
-                       return run_service(line + namelen + 5, s);
+                   line[namelen + 4] == ' ') {
+                       interp_table[INTERP_SLOT_DIR].value = line+namelen+5;
+                       return run_service(interp_table, s);
+               }
        }
 
        logerror("Protocol error: '%s'", line);
@@ -867,6 +932,10 @@ int main(int argc, char **argv)
                        base_path = arg+12;
                        continue;
                }
+               if (!strncmp(arg, "--interpolated-path=", 20)) {
+                       interpolated_path = arg+20;
+                       continue;
+               }
                if (!strcmp(arg, "--reuseaddr")) {
                        reuseaddr = 1;
                        continue;
index fa16d06c8d1e85a458428c673cb2f589857f5424..51df4608a8186e519bcb3b4e67d421c18efb696a 100644 (file)
@@ -308,8 +308,8 @@ create_delta(const struct delta_index *index,
                                continue;
                        if (ref_size > top - src)
                                ref_size = top - src;
-                       if (ref_size > 0x10000)
-                               ref_size = 0x10000;
+                       if (ref_size > 0xffffff)
+                               ref_size = 0xffffff;
                        if (ref_size <= msize)
                                break;
                        while (ref_size-- && *src++ == *ref)
@@ -318,6 +318,8 @@ create_delta(const struct delta_index *index,
                                /* this is our best match so far */
                                msize = ref - entry->ptr;
                                moff = entry->ptr - ref_data;
+                               if (msize >= 0x10000)
+                                       break;  /* this is good enough */
                        }
                }
 
@@ -381,6 +383,8 @@ create_delta(const struct delta_index *index,
                        if (msize & 0xff) { out[outpos++] = msize; i |= 0x10; }
                        msize >>= 8;
                        if (msize & 0xff) { out[outpos++] = msize; i |= 0x20; }
+                       msize >>= 8;
+                       if (msize & 0xff) { out[outpos++] = msize; i |= 0x40; }
 
                        *op = i;
                }
diff --git a/diff.c b/diff.c
index 443e24861ba1857822dbe8e3c831f8037e4e832e..2464238ccd6c94a53a0a73de476e33f7cc935ab9 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -20,12 +20,13 @@ static int diff_use_color_default;
 
 static char diff_colors[][COLOR_MAXLEN] = {
        "\033[m",       /* reset */
-       "",             /* normal */
-       "\033[1m",      /* bold */
-       "\033[36m",     /* cyan */
-       "\033[31m",     /* red */
-       "\033[32m",     /* green */
-       "\033[33m"      /* yellow */
+       "",             /* PLAIN (normal) */
+       "\033[1m",      /* METAINFO (bold) */
+       "\033[36m",     /* FRAGINFO (cyan) */
+       "\033[31m",     /* OLD (red) */
+       "\033[32m",     /* NEW (green) */
+       "\033[33m",     /* COMMIT (yellow) */
+       "\033[41m",     /* WHITESPACE (red background) */
 };
 
 static int parse_diff_color_slot(const char *var, int ofs)
@@ -42,6 +43,8 @@ static int parse_diff_color_slot(const char *var, int ofs)
                return DIFF_FILE_NEW;
        if (!strcasecmp(var+ofs, "commit"))
                return DIFF_COMMIT;
+       if (!strcasecmp(var+ofs, "whitespace"))
+               return DIFF_WHITESPACE;
        die("bad config variable '%s'", var);
 }
 
@@ -383,9 +386,89 @@ const char *diff_get_color(int diff_use_color, enum color_diff ix)
        return "";
 }
 
+static void emit_line(const char *set, const char *reset, const char *line, int len)
+{
+       if (len > 0 && line[len-1] == '\n')
+               len--;
+       fputs(set, stdout);
+       fwrite(line, len, 1, stdout);
+       puts(reset);
+}
+
+static void emit_add_line(const char *reset, struct emit_callback *ecbdata, const char *line, int len)
+{
+       int col0 = ecbdata->nparents;
+       int last_tab_in_indent = -1;
+       int last_space_in_indent = -1;
+       int i;
+       int tail = len;
+       int need_highlight_leading_space = 0;
+       const char *ws = diff_get_color(ecbdata->color_diff, DIFF_WHITESPACE);
+       const char *set = diff_get_color(ecbdata->color_diff, DIFF_FILE_NEW);
+
+       if (!*ws) {
+               emit_line(set, reset, line, len);
+               return;
+       }
+
+       /* The line is a newly added line.  Does it have funny leading
+        * whitespaces?  In indent, SP should never precede a TAB.
+        */
+       for (i = col0; i < len; i++) {
+               if (line[i] == '\t') {
+                       last_tab_in_indent = i;
+                       if (0 <= last_space_in_indent)
+                               need_highlight_leading_space = 1;
+               }
+               else if (line[i] == ' ')
+                       last_space_in_indent = i;
+               else
+                       break;
+       }
+       fputs(set, stdout);
+       fwrite(line, col0, 1, stdout);
+       fputs(reset, stdout);
+       if (((i == len) || line[i] == '\n') && i != col0) {
+               /* The whole line was indent */
+               emit_line(ws, reset, line + col0, len - col0);
+               return;
+       }
+       i = col0;
+       if (need_highlight_leading_space) {
+               while (i < last_tab_in_indent) {
+                       if (line[i] == ' ') {
+                               fputs(ws, stdout);
+                               putchar(' ');
+                               fputs(reset, stdout);
+                       }
+                       else
+                               putchar(line[i]);
+                       i++;
+               }
+       }
+       tail = len - 1;
+       if (line[tail] == '\n' && i < tail)
+               tail--;
+       while (i < tail) {
+               if (!isspace(line[tail]))
+                       break;
+               tail--;
+       }
+       if ((i < tail && line[tail + 1] != '\n')) {
+               /* This has whitespace between tail+1..len */
+               fputs(set, stdout);
+               fwrite(line + i, tail - i + 1, 1, stdout);
+               fputs(reset, stdout);
+               emit_line(ws, reset, line + tail + 1, len - tail - 1);
+       }
+       else
+               emit_line(set, reset, line + i, len - i);
+}
+
 static void fn_out_consume(void *priv, char *line, unsigned long len)
 {
        int i;
+       int color;
        struct emit_callback *ecbdata = priv;
        const char *set = diff_get_color(ecbdata->color_diff, DIFF_METAINFO);
        const char *reset = diff_get_color(ecbdata->color_diff, DIFF_RESET);
@@ -403,45 +486,52 @@ static void fn_out_consume(void *priv, char *line, unsigned long len)
                ;
        if (2 <= i && i < len && line[i] == ' ') {
                ecbdata->nparents = i - 1;
-               set = diff_get_color(ecbdata->color_diff, DIFF_FRAGINFO);
+               emit_line(diff_get_color(ecbdata->color_diff, DIFF_FRAGINFO),
+                         reset, line, len);
+               return;
        }
-       else if (len < ecbdata->nparents)
+
+       if (len < ecbdata->nparents) {
                set = reset;
-       else {
-               int nparents = ecbdata->nparents;
-               int color = DIFF_PLAIN;
-               if (ecbdata->diff_words && nparents != 1)
-                       /* fall back to normal diff */
-                       free_diff_words_data(ecbdata);
-               if (ecbdata->diff_words) {
-                       if (line[0] == '-') {
-                               diff_words_append(line, len,
-                                               &ecbdata->diff_words->minus);
-                               return;
-                       } else if (line[0] == '+') {
-                               diff_words_append(line, len,
-                                               &ecbdata->diff_words->plus);
-                               return;
-                       }
-                       if (ecbdata->diff_words->minus.text.size ||
-                                       ecbdata->diff_words->plus.text.size)
-                               diff_words_show(ecbdata->diff_words);
-                       line++;
-                       len--;
-               } else
-                       for (i = 0; i < nparents && len; i++) {
-                               if (line[i] == '-')
-                                       color = DIFF_FILE_OLD;
-                               else if (line[i] == '+')
-                                       color = DIFF_FILE_NEW;
-                       }
-               set = diff_get_color(ecbdata->color_diff, color);
+               emit_line(reset, reset, line, len);
+               return;
        }
-       if (len > 0 && line[len-1] == '\n')
+
+       color = DIFF_PLAIN;
+       if (ecbdata->diff_words && ecbdata->nparents != 1)
+               /* fall back to normal diff */
+               free_diff_words_data(ecbdata);
+       if (ecbdata->diff_words) {
+               if (line[0] == '-') {
+                       diff_words_append(line, len,
+                                         &ecbdata->diff_words->minus);
+                       return;
+               } else if (line[0] == '+') {
+                       diff_words_append(line, len,
+                                         &ecbdata->diff_words->plus);
+                       return;
+               }
+               if (ecbdata->diff_words->minus.text.size ||
+                   ecbdata->diff_words->plus.text.size)
+                       diff_words_show(ecbdata->diff_words);
+               line++;
                len--;
-       fputs (set, stdout);
-       fwrite (line, len, 1, stdout);
-       puts (reset);
+               emit_line(set, reset, line, len);
+               return;
+       }
+       for (i = 0; i < ecbdata->nparents && len; i++) {
+               if (line[i] == '-')
+                       color = DIFF_FILE_OLD;
+               else if (line[i] == '+')
+                       color = DIFF_FILE_NEW;
+       }
+
+       if (color != DIFF_FILE_NEW) {
+               emit_line(diff_get_color(ecbdata->color_diff, color),
+                         reset, line, len);
+               return;
+       }
+       emit_add_line(reset, ecbdata, line, len);
 }
 
 static char *pprint_rename(const char *a, const char *b)
diff --git a/diff.h b/diff.h
index b60a02e627415d4edaf5056e7f95b519b7fe8fc0..3435fe7b3f868ff1cefd5a20a3409993ce0a413b 100644 (file)
--- a/diff.h
+++ b/diff.h
@@ -86,6 +86,7 @@ enum color_diff {
        DIFF_FILE_OLD = 4,
        DIFF_FILE_NEW = 5,
        DIFF_COMMIT = 6,
+       DIFF_WHITESPACE = 7,
 };
 const char *diff_get_color(int diff_use_color, enum color_diff ix);
 
diff --git a/dir.c b/dir.c
index e2f472ba7f5d3e5146f110dd3eaae9e5494854e2..96389b32e66af24896220e4738cd93bf2ac75704 100644 (file)
--- a/dir.c
+++ b/dir.c
@@ -283,7 +283,7 @@ static int dir_exists(const char *dirname, int len)
  * Also, we ignore the name ".git" (even if it is not a directory).
  * That likely will not change.
  */
-static int read_directory_recursive(struct dir_struct *dir, const char *path, const char *base, int baselen)
+static int read_directory_recursive(struct dir_struct *dir, const char *path, const char *base, int baselen, int check_only)
 {
        DIR *fdir = opendir(path);
        int contents = 0;
@@ -314,7 +314,6 @@ static int read_directory_recursive(struct dir_struct *dir, const char *path, co
 
                        switch (DTYPE(de)) {
                        struct stat st;
-                       int subdir, rewind_base;
                        default:
                                continue;
                        case DT_UNKNOWN:
@@ -328,26 +327,30 @@ static int read_directory_recursive(struct dir_struct *dir, const char *path, co
                        case DT_DIR:
                                memcpy(fullname + baselen + len, "/", 2);
                                len++;
-                               rewind_base = dir->nr;
-                               subdir = read_directory_recursive(dir, fullname, fullname,
-                                                       baselen + len);
                                if (dir->show_other_directories &&
-                                   (subdir || !dir->hide_empty_directories) &&
                                    !dir_exists(fullname, baselen + len)) {
-                                       /* Rewind the read subdirectory */
-                                       while (dir->nr > rewind_base)
-                                               free(dir->entries[--dir->nr]);
+                                       if (dir->hide_empty_directories &&
+                                           !read_directory_recursive(dir,
+                                                   fullname, fullname,
+                                                   baselen + len, 1))
+                                               continue;
                                        break;
                                }
-                               contents += subdir;
+
+                               contents += read_directory_recursive(dir,
+                                       fullname, fullname, baselen + len, 0);
                                continue;
                        case DT_REG:
                        case DT_LNK:
                                break;
                        }
-                       add_name(dir, fullname, baselen + len);
                        contents++;
+                       if (check_only)
+                               goto exit_early;
+                       else
+                               add_name(dir, fullname, baselen + len);
                }
+exit_early:
                closedir(fdir);
 
                pop_exclude_per_directory(dir, exclude_stk);
@@ -393,7 +396,7 @@ int read_directory(struct dir_struct *dir, const char *path, const char *base, i
                }
        }
 
-       read_directory_recursive(dir, path, base, baselen);
+       read_directory_recursive(dir, path, base, baselen, 0);
        qsort(dir->entries, dir->nr, sizeof(struct dir_entry *), cmp_name);
        return dir->nr;
 }
index 84d870ca4eca6e202bcbec388e9a299b887f9a12..63b1d155be1aca2f9fe7aad5f747c06b9102fd60 100644 (file)
@@ -20,6 +20,7 @@ int warn_ambiguous_refs = 1;
 int repository_format_version;
 char git_commit_encoding[MAX_ENCODING_LENGTH] = "utf-8";
 int shared_repository = PERM_UMASK;
+int deny_non_fast_forwards = 0;
 const char *apply_default_whitespace;
 int zlib_compression_level = Z_DEFAULT_COMPRESSION;
 int pager_in_use;
index 4379a07210cf30c5d6e5abdb36722985983edb46..bf84b30695fdeda5d5e18456813e0b3cc3d11275 100755 (executable)
@@ -111,6 +111,16 @@ rev=$(git-rev-parse --verify "$head") || exit
 git-check-ref-format "heads/$branchname" ||
        die "we do not like '$branchname' as a branch name."
 
+if [ -d "$GIT_DIR/refs/heads/$branchname" ]
+then
+       for refdir in `cd "$GIT_DIR" && \
+               find "refs/heads/$branchname" -type d | sort -r`
+       do
+               rmdir "$GIT_DIR/$refdir" || \
+                   die "Could not delete '$refdir', there may still be a ref there."
+       done
+fi
+
 prev=''
 if git-show-ref --verify --quiet -- "refs/heads/$branchname"
 then
index f03620b054ea81554e293dabf0884cac12bb73e4..119bca1ffbfb8fb0dae404a8e7389f577aebc42c 100755 (executable)
@@ -4,8 +4,8 @@ USAGE='[-f] [-b <new_branch>] [-m] [<branch>] [<paths>...]'
 SUBDIRECTORY_OK=Sometimes
 . git-sh-setup
 
-old=$(git-rev-parse HEAD)
 old_name=HEAD
+old=$(git-rev-parse --verify $old_name 2>/dev/null)
 new=
 new_name=
 force=
@@ -140,6 +140,13 @@ fi
        die "git checkout: to checkout the requested commit you need to specify 
               a name for a new branch which is created and switched to"
 
+if [ "X$old" = X ]
+then
+       echo "warning: You do not appear to currently be on a branch." >&2
+       echo "warning: Forcing checkout of $new_name." >&2
+       force=1
+fi
+
 if [ "$force" ]
 then
     git-read-tree --reset -u $new
index 09a5d6ceab7875f2344f84d684ea5cc250786c64..50ad101e89500af01edde00b047b91fc16fdcb81 100755 (executable)
@@ -68,11 +68,10 @@ done
 
 case "$#" in
 0)
-       test -f "$GIT_DIR/branches/origin" ||
-               test -f "$GIT_DIR/remotes/origin" ||
-                       git-repo-config --get remote.origin.url >/dev/null ||
-                               die "Where do you want to fetch from today?"
-       set origin ;;
+       origin=$(get_default_remote)
+       test -n "$(get_remote_url ${origin})" ||
+               die "Where do you want to fetch from today?"
+       set x $origin ; shift ;;
 esac
 
 remote_nick="$1"
index d049e164318a416941334b15699d52f87b00387f..5b34b4de99c33a99dfb841795baced8889f74a88 100755 (executable)
@@ -9,21 +9,15 @@ USAGE='[-n] [--no-commit] [--squash] [-s <strategy>]... <merge-message> <head> <
 LF='
 '
 
-all_strategies='recursive recur octopus resolve stupid ours'
-case "${GIT_USE_RECUR_FOR_RECURSIVE}" in
-'')
-       default_twohead_strategies=recursive ;;
-?*)
-       default_twohead_strategies=recur ;;
-esac
+all_strategies='recur recursive recursive-old octopus resolve stupid ours'
+default_twohead_strategies='recursive'
 default_octopus_strategies='octopus'
 no_trivial_merge_strategies='ours'
 use_strategies=
 
 index_merge=t
 if test "@@NO_PYTHON@@"; then
-       all_strategies='recur resolve octopus stupid ours'
-       default_twohead_strategies='resolve'
+       all_strategies='recur recursive resolve octopus stupid ours'
 fi
 
 dropsave() {
@@ -122,10 +116,6 @@ do
                        strategy="$2"
                        shift ;;
                esac
-               case "$strategy,${GIT_USE_RECUR_FOR_RECURSIVE}" in
-               recursive,?*)
-                       strategy=recur ;;
-               esac
                case " $all_strategies " in
                *" $strategy "*)
                        use_strategies="$use_strategies$strategy " ;;
index 187f0883c9136772677088ddf61228291d4b41d1..c325ef761e4c558ab5c7c560da942e127e1be040 100755 (executable)
@@ -68,6 +68,12 @@ get_remote_url () {
        esac
 }
 
+get_default_remote () {
+       curr_branch=$(git-symbolic-ref HEAD | sed -e 's|^refs/heads/||')
+       origin=$(git-repo-config --get "branch.$curr_branch.remote")
+       echo ${origin:-origin}
+}
+
 get_remote_default_refs_for_push () {
        data_source=$(get_data_source "$1")
        case "$data_source" in
@@ -86,9 +92,22 @@ get_remote_default_refs_for_push () {
 
 # Subroutine to canonicalize remote:local notation.
 canon_refs_list_for_fetch () {
-       # Leave only the first one alone; add prefix . to the rest
+       # If called from get_remote_default_refs_for_fetch
+       # leave the branches in branch.${curr_branch}.merge alone,
+       # or the first one otherwise; add prefix . to the rest
        # to prevent the secondary branches to be merged by default.
-       dot_prefix=
+       merge_branches=
+       if test "$1" = "-d"
+       then
+               shift ; remote="$1" ; shift
+               if test "$remote" = "$(get_default_remote)"
+               then
+                       curr_branch=$(git-symbolic-ref HEAD | \
+                           sed -e 's|^refs/heads/||')
+                       merge_branches=$(git-repo-config \
+                           --get-all "branch.${curr_branch}.merge")
+               fi
+       fi
        for ref
        do
                force=
@@ -101,6 +120,18 @@ canon_refs_list_for_fetch () {
                expr "z$ref" : 'z.*:' >/dev/null || ref="${ref}:"
                remote=$(expr "z$ref" : 'z\([^:]*\):')
                local=$(expr "z$ref" : 'z[^:]*:\(.*\)')
+               dot_prefix=.
+               if test -z "$merge_branches"
+               then
+                       merge_branches=$remote
+                       dot_prefix=
+               else
+                       for merge_branch in $merge_branches
+                       do
+                           [ "$remote" = "$merge_branch" ] &&
+                           dot_prefix= && break
+                       done
+               fi
                case "$remote" in
                '') remote=HEAD ;;
                refs/heads/* | refs/tags/* | refs/remotes/*) ;;
@@ -120,7 +151,6 @@ canon_refs_list_for_fetch () {
                   die "* refusing to create funny ref '$local_ref_name' locally"
                fi
                echo "${dot_prefix}${force}${remote}:${local}"
-               dot_prefix=.
        done
 }
 
@@ -131,7 +161,7 @@ get_remote_default_refs_for_fetch () {
        '' | config-partial | branches-partial)
                echo "HEAD:" ;;
        config)
-               canon_refs_list_for_fetch \
+               canon_refs_list_for_fetch -d "$1" \
                        $(git-repo-config --get-all "remote.$1.fetch") ;;
        branches)
                remote_branch=$(sed -ne '/#/s/.*#//p' "$GIT_DIR/branches/$1")
@@ -139,10 +169,7 @@ get_remote_default_refs_for_fetch () {
                echo "refs/heads/${remote_branch}:refs/heads/$1"
                ;;
        remotes)
-               # This prefixes the second and later default refspecs
-               # with a '.', to signal git-fetch to mark them
-               # not-for-merge.
-               canon_refs_list_for_fetch $(sed -ne '/^Pull: */{
+               canon_refs_list_for_fetch -d "$1" $(sed -ne '/^Pull: */{
                                                s///p
                                        }' "$GIT_DIR/remotes/$1")
                ;;
index 20f74d416732e681d3b44ea610e428529af49235..a7373c0532fad447263e7199d0b8ec2908c683c9 100755 (executable)
@@ -35,13 +35,7 @@ If you would prefer to skip this patch, instead run \"git rebase --skip\".
 To restore the original branch and stop rebasing run \"git rebase --abort\".
 "
 unset newbase
-case "${GIT_USE_RECUR_FOR_RECURSIVE}" in
-'')
-       strategy=recursive ;;
-?*)
-       strategy=recur ;;
-esac
-
+strategy=recursive
 do_merge=
 dotest=$GIT_DIR/.dotest-merge
 prec=4
@@ -206,11 +200,6 @@ do
        shift
 done
 
-case "$strategy,${GIT_USE_RECUR_FOR_RECURSIVE}" in
-recursive,?*)
-       strategy=recur ;;
-esac
-
 # Make sure we do not have .dotest
 if test -z "$do_merge"
 then
@@ -303,11 +292,11 @@ then
        exit $?
 fi
 
-if test "@@NO_PYTHON@@" && test "$strategy" = "recursive"
+if test "@@NO_PYTHON@@" && test "$strategy" = "recursive-old"
 then
-       die 'The recursive merge strategy currently relies on Python,
+       die 'The recursive-old merge strategy is written in Python,
 which this installation of git was not configured with.  Please consider
-a different merge strategy (e.g. octopus, resolve, stupid, ours)
+a different merge strategy (e.g. recursive, resolve, or stupid)
 or install Python and git with Python support.'
 
 fi
index b525fc5dfd9053a6fb63a4d38517e25e75341199..f2c9071d1109e014832f0efd8a1fd67dca44c8af 100755 (executable)
@@ -4,6 +4,7 @@
 #
 
 USAGE='[-a] [-d] [-f] [-l] [-n] [-q]'
+SUBDIRECTORY_OK='Yes'
 . git-sh-setup
 
 no_update_info= all_into_one= remove_redundant=
@@ -32,12 +33,10 @@ trap 'rm -f "$PACKTMP"-*' 0 1 2 3 15
 # There will be more repacking strategies to come...
 case ",$all_into_one," in
 ,,)
-       rev_list='--unpacked'
-       pack_objects='--incremental'
+       args='--unpacked --incremental'
        ;;
 ,t,)
-       rev_list=
-       pack_objects=
+       args=
 
        # Redundancy check in all-into-one case is trivial.
        existing=`test -d "$PACKDIR" && cd "$PACKDIR" && \
@@ -45,11 +44,8 @@ case ",$all_into_one," in
        ;;
 esac
 
-pack_objects="$pack_objects $local $quiet $no_reuse_delta$extra"
-name=$( { git-rev-list --objects --all $rev_list ||
-         echo "git-rev-list died with exit code $?"
-       } |
-       git-pack-objects --non-empty $pack_objects "$PACKTMP") ||
+args="$args $local $quiet $no_reuse_delta$extra"
+name=$(git-pack-objects --non-empty --all $args </dev/null "$PACKTMP") ||
        exit 1
 if [ -z "$name" ]; then
        echo Nothing new to pack.
index a7bc680d90cb503c50d25b15bcaba662c5f5b49e..729ec65dc9e0ddebfe81fb4d837a5f9c83b537ee 100755 (executable)
@@ -5,6 +5,10 @@
 # Resolve two trees.
 #
 
+echo 'WARNING: This command is DEPRECATED and will be removed very soon.' >&2
+echo 'WARNING: Please use git-merge or git-pull instead.' >&2
+sleep 2
+
 USAGE='<head> <remote> <merge-message>'
 . git-sh-setup
 
index 0290850b6639b4865d0941089d00a8c2ac5de366..f5c7d46341016a5ca77a52ad16fbcd8c3830678f 100755 (executable)
@@ -52,7 +52,7 @@ my ($_revision,$_stdin,$_no_ignore_ext,$_no_stop_copy,$_help,$_rmdir,$_edit,
        $_template, $_shared, $_no_default_regex, $_no_graft_copy,
        $_limit, $_verbose, $_incremental, $_oneline, $_l_fmt, $_show_commit,
        $_version, $_upgrade, $_authors, $_branch_all_refs, @_opt_m,
-       $_merge, $_strategy, $_dry_run);
+       $_merge, $_strategy, $_dry_run, $_ignore_nodate);
 my (@_branch_from, %tree_map, %users, %rusers, %equiv);
 my ($_svn_co_url_revs, $_svn_pg_peg_revs);
 my @repo_path_split_cache;
@@ -65,6 +65,7 @@ my %fc_opts = ( 'no-ignore-externals' => \$_no_ignore_ext,
                'repack:i' => \$_repack,
                'no-metadata' => \$_no_metadata,
                'quiet|q' => \$_q,
+               'ignore-nodate' => \$_ignore_nodate,
                'repack-flags|repack-args|repack-opts=s' => \$_repack_flags);
 
 my ($_trunk, $_tags, $_branches);
@@ -1246,6 +1247,7 @@ sub assert_svn_wc_clean {
        }
        my @status = grep(!/^Performing status on external/,(`svn status`));
        @status = grep(!/^\s*$/,@status);
+       @status = grep(!/^X/,@status) if $_no_ignore_ext;
        if (scalar @status) {
                print STDERR "Tree ($SVN_WC) is not clean:\n";
                print STDERR $_ foreach @status;
@@ -1734,6 +1736,8 @@ sub next_log_entry {
                        my $rev = $1;
                        my ($author, $date, $lines) = split(/\s*\|\s*/, $_, 3);
                        ($lines) = ($lines =~ /(\d+)/);
+                       $date = '1970-01-01 00:00:00 +0000'
+                               if ($_ignore_nodate && $date eq '(no date)');
                        my ($Y,$m,$d,$H,$M,$S,$tz) = ($date =~
                                        /(\d{4})\-(\d\d)\-(\d\d)\s
                                         (\d\d)\:(\d\d)\:(\d\d)\s([\-\+]\d+)/x)
@@ -2168,7 +2172,7 @@ sub load_authors {
        open my $authors, '<', $_authors or die "Can't open $_authors $!\n";
        while (<$authors>) {
                chomp;
-               next unless /^(\S+?)\s*=\s*(.+?)\s*<(.+)>\s*$/;
+               next unless /^(\S+?|\(no author\))\s*=\s*(.+?)\s*<(.+)>\s*$/;
                my ($user, $name, $email) = ($1, $2, $3);
                $users{$user} = [$name, $email];
        }
index 26dc45479532a78febb511bf36017d6106513e70..ed628974d7d5ff356d05f0cc51a8cb42046a6c1b 100755 (executable)
@@ -31,7 +31,7 @@ $SIG{'PIPE'}="IGNORE";
 $ENV{'TZ'}="UTC";
 
 our($opt_h,$opt_o,$opt_v,$opt_u,$opt_C,$opt_i,$opt_m,$opt_M,$opt_t,$opt_T,
-    $opt_b,$opt_r,$opt_I,$opt_A,$opt_s,$opt_l,$opt_d,$opt_D);
+    $opt_b,$opt_r,$opt_I,$opt_A,$opt_s,$opt_l,$opt_d,$opt_D,$opt_S);
 
 sub usage() {
        print STDERR <<END;
@@ -39,12 +39,12 @@ Usage: ${\basename $0}     # fetch/update GIT from SVN
        [-o branch-for-HEAD] [-h] [-v] [-l max_rev]
        [-C GIT_repository] [-t tagname] [-T trunkname] [-b branchname]
        [-d|-D] [-i] [-u] [-r] [-I ignorefilename] [-s start_chg]
-       [-m] [-M regex] [-A author_file] [SVN_URL]
+       [-m] [-M regex] [-A author_file] [-S] [SVN_URL]
 END
        exit(1);
 }
 
-getopts("A:b:C:dDhiI:l:mM:o:rs:t:T:uv") or usage();
+getopts("A:b:C:dDhiI:l:mM:o:rs:t:T:Suv") or usage();
 usage if $opt_h;
 
 my $tag_name = $opt_t || "tags";
@@ -531,21 +531,30 @@ sub copy_path($$$$$$$$) {
 
 sub commit {
        my($branch, $changed_paths, $revision, $author, $date, $message) = @_;
-       my($author_name,$author_email,$dest);
+       my($committer_name,$committer_email,$dest);
+       my($author_name,$author_email);
        my(@old,@new,@parents);
 
        if (not defined $author or $author eq "") {
-               $author_name = $author_email = "unknown";
+               $committer_name = $committer_email = "unknown";
        } elsif (defined $users_file) {
                die "User $author is not listed in $users_file\n"
                    unless exists $users{$author};
-               ($author_name,$author_email) = @{$users{$author}};
+               ($committer_name,$committer_email) = @{$users{$author}};
        } elsif ($author =~ /^(.*?)\s+<(.*)>$/) {
-               ($author_name, $author_email) = ($1, $2);
+               ($committer_name, $committer_email) = ($1, $2);
        } else {
                $author =~ s/^<(.*)>$/$1/;
-               $author_name = $author_email = $author;
+               $committer_name = $committer_email = $author;
+       }
+
+       if ($opt_S && $message =~ /Signed-off-by:\s+(.*?)\s+<(.*)>\s*\n/) {
+               ($author_name, $author_email) = ($1, $2);
+       } else {
+               $author_name = $committer_name;
+               $author_email = $committer_email;
        }
+
        $date = pdate($date);
 
        my $tag;
@@ -772,8 +781,8 @@ sub commit {
                                "GIT_AUTHOR_NAME=$author_name",
                                "GIT_AUTHOR_EMAIL=$author_email",
                                "GIT_AUTHOR_DATE=".strftime("+0000 %Y-%m-%d %H:%M:%S",gmtime($date)),
-                               "GIT_COMMITTER_NAME=$author_name",
-                               "GIT_COMMITTER_EMAIL=$author_email",
+                               "GIT_COMMITTER_NAME=$committer_name",
+                               "GIT_COMMITTER_EMAIL=$committer_email",
                                "GIT_COMMITTER_DATE=".strftime("+0000 %Y-%m-%d %H:%M:%S",gmtime($date)),
                                "git-commit-tree", $tree,@par);
                        die "Cannot exec git-commit-tree: $!\n";
@@ -825,7 +834,7 @@ sub commit {
                print $out ("object $cid\n".
                    "type commit\n".
                    "tag $dest\n".
-                   "tagger $author_name <$author_email>\n") and
+                   "tagger $committer_name <$committer_email>\n") and
                close($out)
                    or die "Cannot create tag object $dest: $!\n";
 
diff --git a/git.c b/git.c
index f7e784164997a3d0e6885cc57eceeacf41d433ab..9108fec808e6fa6fc78a0409f7a45aa12cd4a325 100644 (file)
--- a/git.c
+++ b/git.c
@@ -260,12 +260,10 @@ static void handle_internal_command(int argc, const char **argv, char **envp)
                { "stripspace", cmd_stripspace },
                { "symbolic-ref", cmd_symbolic_ref, RUN_SETUP },
                { "tar-tree", cmd_tar_tree, RUN_SETUP },
-               { "zip-tree", cmd_zip_tree, RUN_SETUP },
                { "unpack-objects", cmd_unpack_objects, RUN_SETUP },
                { "update-index", cmd_update_index, RUN_SETUP },
                { "update-ref", cmd_update_ref, RUN_SETUP },
                { "upload-archive", cmd_upload_archive },
-               { "upload-tar", cmd_upload_tar },
                { "version", cmd_version },
                { "whatchanged", cmd_whatchanged, RUN_SETUP | USE_PAGER },
                { "write-tree", cmd_write_tree, RUN_SETUP },
index c77270c7cd7ef478f708dbbc6165ac420891ca5a..597d29f22fc931cf13574a8d2b0fde9f6b2f9908 100755 (executable)
@@ -212,19 +212,9 @@ if (defined $project) {
        }
 }
 
+# We have to handle those containing any characters:
 our $file_name = $cgi->param('f');
-if (defined $file_name) {
-       if (!validate_input($file_name)) {
-               die_error(undef, "Invalid file parameter");
-       }
-}
-
 our $file_parent = $cgi->param('fp');
-if (defined $file_parent) {
-       if (!validate_input($file_parent)) {
-               die_error(undef, "Invalid file parent parameter");
-       }
-}
 
 our $hash = $cgi->param('h');
 if (defined $hash) {
@@ -274,13 +264,16 @@ sub evaluate_path_info {
        return if defined $project;
        my $path_info = $ENV{"PATH_INFO"};
        return if !$path_info;
-       $path_info =~ s,(^/|/$),,gs;
-       $path_info = validate_input($path_info);
+       $path_info =~ s,^/+,,;
        return if !$path_info;
+       # find which part of PATH_INFO is project
        $project = $path_info;
+       $project =~ s,/+$,,;
        while ($project && !-e "$projectroot/$project/HEAD") {
                $project =~ s,/*[^/]*$,,;
        }
+       # validate project
+       $project = validate_input($project);
        if (!$project ||
            ($export_ok && !-e "$projectroot/$project/$export_ok") ||
            ($strict_export && !project_in_list($project))) {
@@ -289,15 +282,24 @@ sub evaluate_path_info {
        }
        # do not change any parameters if an action is given using the query string
        return if $action;
-       if ($path_info =~ m,^$project/([^/]+)/(.+)$,) {
-               # we got "project.git/branch/filename"
-               $action    ||= "blob_plain";
-               $hash_base ||= validate_input($1);
-               $file_name ||= validate_input($2);
-       } elsif ($path_info =~ m,^$project/([^/]+)$,) {
+       $path_info =~ s,^$project/*,,;
+       my ($refname, $pathname) = split(/:/, $path_info, 2);
+       if (defined $pathname) {
+               # we got "project.git/branch:filename" or "project.git/branch:dir/"
+               # we could use git_get_type(branch:pathname), but it needs $git_dir
+               $pathname =~ s,^/+,,;
+               if (!$pathname || substr($pathname, -1) eq "/") {
+                       $action  ||= "tree";
+                       $pathname =~ s,/$,,;
+               } else {
+                       $action  ||= "blob_plain";
+               }
+               $hash_base ||= validate_input($refname);
+               $file_name ||= $pathname;
+       } elsif (defined $refname) {
                # we got "project.git/branch"
                $action ||= "shortlog";
-               $hash   ||= validate_input($1);
+               $hash   ||= validate_input($refname);
        }
 }
 evaluate_path_info();
@@ -341,6 +343,10 @@ if (defined $project) {
 if (!defined($actions{$action})) {
        die_error(undef, "Unknown action");
 }
+if ($action !~ m/^(opml|project_list|project_index)$/ &&
+    !$project) {
+       die_error(undef, "Project needed");
+}
 $actions{$action}->();
 exit;
 
@@ -400,7 +406,7 @@ sub validate_input {
 # correct, but quoted slashes look too horrible in bookmarks
 sub esc_param {
        my $str = shift;
-       $str =~ s/([^A-Za-z0-9\-_.~();\/;?:@&=])/sprintf("%%%02X", ord($1))/eg;
+       $str =~ s/([^A-Za-z0-9\-_.~()\/:@])/sprintf("%%%02X", ord($1))/eg;
        $str =~ s/\+/%2B/g;
        $str =~ s/ /\+/g;
        return $str;
@@ -611,7 +617,7 @@ sub format_subject_html {
 
        if (length($short) < length($long)) {
                return $cgi->a({-href => $href, -class => "list subject",
-                               -title => $long},
+                               -title => decode("utf8", $long, Encode::FB_DEFAULT)},
                       esc_html($short) . $extra);
        } else {
                return $cgi->a({-href => $href, -class => "list subject"},
@@ -702,6 +708,7 @@ sub git_get_project_config {
 sub git_get_hash_by_path {
        my $base = shift;
        my $path = shift || return undef;
+       my $type = shift;
 
        my $tree = $base;
 
@@ -712,6 +719,10 @@ sub git_get_hash_by_path {
 
        #'100644 blob 0fa3f3a66fb6a137f6ec2c19351ed4d807070ffa  panic.c'
        $line =~ m/^([0-9]+) (.+) ([0-9a-fA-F]{40})\t(.+)$/;
+       if (defined $type && $type ne $2) {
+               # type doesn't match
+               return undef;
+       }
        return $3;
 }
 
@@ -731,7 +742,7 @@ sub git_get_project_description {
 sub git_get_project_url_list {
        my $path = shift;
 
-       open my $fd, "$projectroot/$path/cloneurl" or return undef;
+       open my $fd, "$projectroot/$path/cloneurl" or return;
        my @git_project_url_list = map { chomp; $_ } <$fd>;
        close $fd;
 
@@ -828,16 +839,10 @@ sub git_get_project_owner {
 sub git_get_references {
        my $type = shift || "";
        my %refs;
-       my $fd;
        # 5dc01c595e6c6ec9ccda4f6f69c131c0dd945f8c      refs/tags/v2.6.11
        # c39ae07f393806ccf406ef966e9a15afc43cc36a      refs/tags/v2.6.11^{}
-       if (-f "$projectroot/$project/info/refs") {
-               open $fd, "$projectroot/$project/info/refs"
-                       or return;
-       } else {
-               open $fd, "-|", git_cmd(), "ls-remote", "."
-                       or return;
-       }
+       open my $fd, "-|", $GIT, "peek-remote", "$projectroot/$project/"
+               or return;
 
        while (my $line = <$fd>) {
                chomp $line;
@@ -1125,7 +1130,8 @@ sub parse_ls_tree_line ($;%) {
 ## parse to array of hashes functions
 
 sub git_get_refs_list {
-       my $ref_dir = shift;
+       my $type = shift || "";
+       my %refs;
        my @reflist;
 
        my @refs;
@@ -1133,14 +1139,21 @@ sub git_get_refs_list {
                or return;
        while (my $line = <$fd>) {
                chomp $line;
-               if ($line =~ m/^([0-9a-fA-F]{40})\t$ref_dir\/?([^\^]+)$/) {
-                       push @refs, { hash => $1, name => $2 };
-               } elsif ($line =~ m/^[0-9a-fA-F]{40}\t$ref_dir\/?(.*)\^\{\}$/ &&
-                        $1 eq $refs[-1]{'name'}) {
-                       # most likely a tag is followed by its peeled
-                       # (deref) one, and when that happens we know the
-                       # previous one was of type 'tag'.
-                       $refs[-1]{'type'} = "tag";
+               if ($line =~ m/^([0-9a-fA-F]{40})\trefs\/($type\/?([^\^]+))(\^\{\})?$/) {
+                       if (defined $refs{$1}) {
+                               push @{$refs{$1}}, $2;
+                       } else {
+                               $refs{$1} = [ $2 ];
+                       }
+
+                       if (! $4) { # unpeeled, direct reference
+                               push @refs, { hash => $1, name => $3 }; # without type
+                       } elsif ($3 eq $refs[-1]{'name'}) {
+                               # most likely a tag is followed by its peeled
+                               # (deref) one, and when that happens we know the
+                               # previous one was of type 'tag'.
+                               $refs[-1]{'type'} = "tag";
+                       }
                }
        }
        close $fd;
@@ -1156,7 +1169,7 @@ sub git_get_refs_list {
        }
        # sort refs by age
        @reflist = sort {$b->{'epoch'} <=> $a->{'epoch'}} @reflist;
-       return \@reflist;
+       return (\@reflist, \%refs);
 }
 
 ## ----------------------------------------------------------------------
@@ -1197,7 +1210,7 @@ sub mimetype_guess_file {
        }
        close(MIME);
 
-       $filename =~ /\.(.*?)$/;
+       $filename =~ /\.([^.]*)$/;
        return $mimemap{$1};
 }
 
@@ -1259,7 +1272,7 @@ sub git_header_html {
                if (defined $action) {
                        $title .= "/$action";
                        if (defined $file_name) {
-                               $title .= " - $file_name";
+                               $title .= " - " . esc_html($file_name);
                                if ($action eq "tree" && $file_name !~ m|/$|) {
                                        $title .= "/";
                                }
@@ -1496,12 +1509,15 @@ sub git_print_page_path {
                my $fullname = '';
 
                print "<div class=\"page_path\">";
+               print $cgi->a({-href => href(action=>"tree", hash_base=>$hb),
+                             -title => 'tree root'}, "[$project]");
+               print " / ";
                foreach my $dir (@dirname) {
-                       $fullname .= $dir . '/';
+                       $fullname .= ($fullname ? '/' : '') . $dir;
                        print $cgi->a({-href => href(action=>"tree", file_name=>$fullname,
                                                     hash_base=>$hb),
                                      -title => $fullname}, esc_html($dir));
-                       print "/";
+                       print " / ";
                }
                if (defined $type && $type eq 'blob') {
                        print $cgi->a({-href => href(action=>"blob_plain", file_name=>$file_name,
@@ -1511,7 +1527,6 @@ sub git_print_page_path {
                        print $cgi->a({-href => href(action=>"tree", file_name=>$file_name,
                                                     hash_base=>$hb),
                                      -title => $name}, esc_html($basename));
-                       print "/";
                } else {
                        print esc_html($basename);
                }
@@ -1950,9 +1965,6 @@ sub git_shortlog_body {
        # uses global variable $project
        my ($revlist, $from, $to, $refs, $extra) = @_;
 
-       my ($ctype, $suffix, $command) = gitweb_check_feature('snapshot');
-       my $have_snapshot = (defined $ctype && defined $suffix);
-
        $from = 0 unless defined $from;
        $to = $#{$revlist} if (!defined $to || $#{$revlist} < $to);
 
@@ -1978,10 +1990,8 @@ sub git_shortlog_body {
                print "</td>\n" .
                      "<td class=\"link\">" .
                      $cgi->a({-href => href(action=>"commit", hash=>$commit)}, "commit") . " | " .
-                     $cgi->a({-href => href(action=>"commitdiff", hash=>$commit)}, "commitdiff");
-               if ($have_snapshot) {
-                       print " | " .  $cgi->a({-href => href(action=>"snapshot", hash=>$commit)}, "snapshot");
-               }
+                     $cgi->a({-href => href(action=>"commitdiff", hash=>$commit)}, "commitdiff") . " | " .
+                     $cgi->a({-href => href(action=>"tree", hash=>$commit, hash_base=>$commit)}, "tree");
                print "</td>\n" .
                      "</tr>\n";
        }
@@ -2120,14 +2130,14 @@ sub git_tags_body {
 
 sub git_heads_body {
        # uses global variable $project
-       my ($taglist, $head, $from, $to, $extra) = @_;
+       my ($headlist, $head, $from, $to, $extra) = @_;
        $from = 0 unless defined $from;
-       $to = $#{$taglist} if (!defined $to || $#{$taglist} < $to);
+       $to = $#{$headlist} if (!defined $to || $#{$headlist} < $to);
 
        print "<table class=\"heads\" cellspacing=\"0\">\n";
        my $alternate = 0;
        for (my $i = $from; $i <= $to; $i++) {
-               my $entry = $taglist->[$i];
+               my $entry = $headlist->[$i];
                my %tag = %$entry;
                my $curr = $tag{'id'} eq $head;
                if ($alternate) {
@@ -2143,7 +2153,8 @@ sub git_heads_body {
                      "</td>\n" .
                      "<td class=\"link\">" .
                      $cgi->a({-href => href(action=>"shortlog", hash=>$tag{'name'})}, "shortlog") . " | " .
-                     $cgi->a({-href => href(action=>"log", hash=>$tag{'name'})}, "log") .
+                     $cgi->a({-href => href(action=>"log", hash=>$tag{'name'})}, "log") . " | " .
+                     $cgi->a({-href => href(action=>"tree", hash=>$tag{'name'}, hash_base=>$tag{'name'})}, "tree") .
                      "</td>\n" .
                      "</tr>";
        }
@@ -2257,7 +2268,8 @@ sub git_project_list {
                      "<td class=\"link\">" .
                      $cgi->a({-href => href(project=>$pr->{'path'}, action=>"summary")}, "summary")   . " | " .
                      $cgi->a({-href => href(project=>$pr->{'path'}, action=>"shortlog")}, "shortlog") . " | " .
-                     $cgi->a({-href => href(project=>$pr->{'path'}, action=>"log")}, "log") .
+                     $cgi->a({-href => href(project=>$pr->{'path'}, action=>"log")}, "log") . " | " .
+                     $cgi->a({-href => href(project=>$pr->{'path'}, action=>"tree")}, "tree") .
                      "</td>\n" .
                      "</tr>\n";
        }
@@ -2297,7 +2309,19 @@ sub git_summary {
 
        my $owner = git_get_project_owner($project);
 
-       my $refs = git_get_references();
+       my ($reflist, $refs) = git_get_refs_list();
+
+       my @taglist;
+       my @headlist;
+       foreach my $ref (@$reflist) {
+               if ($ref->{'name'} =~ s!^heads/!!) {
+                       push @headlist, $ref;
+               } else {
+                       $ref->{'name'} =~ s!^tags/!!;
+                       push @taglist, $ref;
+               }
+       }
+
        git_header_html();
        git_print_page_nav('summary','', $head);
 
@@ -2327,17 +2351,15 @@ sub git_summary {
        git_shortlog_body(\@revlist, 0, 15, $refs,
                          $cgi->a({-href => href(action=>"shortlog")}, "..."));
 
-       my $taglist = git_get_refs_list("refs/tags");
-       if (defined @$taglist) {
+       if (@taglist) {
                git_print_header_div('tags');
-               git_tags_body($taglist, 0, 15,
+               git_tags_body(\@taglist, 0, 15,
                              $cgi->a({-href => href(action=>"tags")}, "..."));
        }
 
-       my $headlist = git_get_refs_list("refs/heads");
-       if (defined @$headlist) {
+       if (@headlist) {
                git_print_header_div('heads');
-               git_heads_body($headlist, $head, 0, 15,
+               git_heads_body(\@headlist, $head, 0, 15,
                               $cgi->a({-href => href(action=>"heads")}, "..."));
        }
 
@@ -2398,15 +2420,18 @@ sub git_blame2 {
        if ($ftype !~ "blob") {
                die_error("400 Bad Request", "Object is not a blob");
        }
-       open ($fd, "-|", git_cmd(), "blame", '-l', $file_name, $hash_base)
+       open ($fd, "-|", git_cmd(), "blame", '-l', '--', $file_name, $hash_base)
                or die_error(undef, "Open git-blame failed");
        git_header_html();
        my $formats_nav =
                $cgi->a({-href => href(action=>"blob", hash=>$hash, hash_base=>$hash_base, file_name=>$file_name)},
                        "blob") .
                " | " .
+               $cgi->a({-href => href(action=>"history", hash=>$hash, hash_base=>$hash_base, file_name=>$file_name)},
+                       "history") .
+               " | " .
                $cgi->a({-href => href(action=>"blame", file_name=>$file_name)},
-                       "head");
+                       "HEAD");
        git_print_page_nav('','', $hash_base,$co{'tree'},$hash_base, $formats_nav);
        git_print_header_div('commit', esc_html($co{'title'}), $hash_base);
        git_print_page_path($file_name, $ftype, $hash_base);
@@ -2471,8 +2496,11 @@ sub git_blame {
                $cgi->a({-href => href(action=>"blob", hash=>$hash, hash_base=>$hash_base, file_name=>$file_name)},
                        "blob") .
                " | " .
+               $cgi->a({-href => href(action=>"history", hash=>$hash, hash_base=>$hash_base, file_name=>$file_name)},
+                       "history") .
+               " | " .
                $cgi->a({-href => href(action=>"blame", file_name=>$file_name)},
-                       "head");
+                       "HEAD");
        git_print_page_nav('','', $hash_base,$co{'tree'},$hash_base, $formats_nav);
        git_print_header_div('commit', esc_html($co{'title'}), $hash_base);
        git_print_page_path($file_name, 'blob', $hash_base);
@@ -2548,8 +2576,8 @@ sub git_tags {
        git_print_page_nav('','', $head,undef,$head);
        git_print_header_div('summary', $project);
 
-       my $taglist = git_get_refs_list("refs/tags");
-       if (defined @$taglist) {
+       my ($taglist) = git_get_refs_list("tags");
+       if (@$taglist) {
                git_tags_body($taglist);
        }
        git_footer_html();
@@ -2561,9 +2589,9 @@ sub git_heads {
        git_print_page_nav('','', $head,undef,$head);
        git_print_header_div('summary', $project);
 
-       my $taglist = git_get_refs_list("refs/heads");
-       if (defined @$taglist) {
-               git_heads_body($taglist, $head);
+       my ($headlist) = git_get_refs_list("heads");
+       if (@$headlist) {
+               git_heads_body($headlist, $head);
        }
        git_footer_html();
 }
@@ -2646,16 +2674,20 @@ sub git_blob {
                                        " | ";
                        }
                        $formats_nav .=
+                               $cgi->a({-href => href(action=>"history", hash_base=>$hash_base,
+                                                      hash=>$hash, file_name=>$file_name)},
+                                       "history") .
+                               " | " .
                                $cgi->a({-href => href(action=>"blob_plain",
                                                       hash=>$hash, file_name=>$file_name)},
-                                       "plain") .
+                                       "raw") .
                                " | " .
                                $cgi->a({-href => href(action=>"blob",
                                                       hash_base=>"HEAD", file_name=>$file_name)},
-                                       "head");
+                                       "HEAD");
                } else {
                        $formats_nav .=
-                               $cgi->a({-href => href(action=>"blob_plain", hash=>$hash)}, "plain");
+                               $cgi->a({-href => href(action=>"blob_plain", hash=>$hash)}, "raw");
                }
                git_print_page_nav('','', $hash_base,$co{'tree'},$hash_base, $formats_nav);
                git_print_header_div('commit', esc_html($co{'title'}), $hash_base);
@@ -2681,6 +2713,9 @@ sub git_blob {
 }
 
 sub git_tree {
+       my ($ctype, $suffix, $command) = gitweb_check_feature('snapshot');
+       my $have_snapshot = (defined $ctype && defined $suffix);
+
        if (!defined $hash) {
                $hash = git_get_head_hash($project);
                if (defined $file_name) {
@@ -2704,7 +2739,23 @@ sub git_tree {
        my $base = "";
        my ($have_blame) = gitweb_check_feature('blame');
        if (defined $hash_base && (my %co = parse_commit($hash_base))) {
-               git_print_page_nav('tree','', $hash_base);
+               my @views_nav = ();
+               if (defined $file_name) {
+                       push @views_nav,
+                               $cgi->a({-href => href(action=>"history", hash_base=>$hash_base,
+                                                      hash=>$hash, file_name=>$file_name)},
+                                       "history"),
+                               $cgi->a({-href => href(action=>"tree",
+                                                      hash_base=>"HEAD", file_name=>$file_name)},
+                                       "HEAD"),
+               }
+               if ($have_snapshot) {
+                       # FIXME: Should be available when we have no hash base as well.
+                       push @views_nav,
+                               $cgi->a({-href => href(action=>"snapshot", hash=>$hash)},
+                                       "snapshot");
+               }
+               git_print_page_nav('tree','', $hash_base, undef, undef, join(' | ', @views_nav));
                git_print_header_div('commit', esc_html($co{'title'}) . $ref, $hash_base);
        } else {
                undef $hash_base;
@@ -2809,6 +2860,8 @@ sub git_log {
                      $cgi->a({-href => href(action=>"commit", hash=>$commit)}, "commit") .
                      " | " .
                      $cgi->a({-href => href(action=>"commitdiff", hash=>$commit)}, "commitdiff") .
+                     " | " .
+                     $cgi->a({-href => href(action=>"tree", hash=>$commit, hash_base=>$commit)}, "tree") .
                      "<br/>\n" .
                      "</div>\n" .
                      "<i>" . esc_html($co{'author_name'}) .  " [$ad{'rfc2822'}]</i><br/>\n" .
@@ -2849,17 +2902,22 @@ sub git_commit {
        my ($ctype, $suffix, $command) = gitweb_check_feature('snapshot');
        my $have_snapshot = (defined $ctype && defined $suffix);
 
-       my $formats_nav = '';
+       my @views_nav = ();
        if (defined $file_name && defined $co{'parent'}) {
                my $parent = $co{'parent'};
-               $formats_nav .=
+               push @views_nav,
                        $cgi->a({-href => href(action=>"blame", hash_parent=>$parent, file_name=>$file_name)},
                                "blame");
        }
+       if (defined $co{'parent'}) {
+               push @views_nav,
+                       $cgi->a({-href => href(action=>"shortlog", hash=>$hash)}, "shortlog"),
+                       $cgi->a({-href => href(action=>"log", hash=>$hash)}, "log");
+       }
        git_header_html(undef, $expires);
        git_print_page_nav('commit', defined $co{'parent'} ? '' : 'commitdiff',
                           $hash, $co{'tree'}, $hash,
-                          $formats_nav);
+                          join (' | ', @views_nav));
 
        if (defined $co{'parent'}) {
                git_print_header_div('commitdiff', esc_html($co{'title'}) . $ref, $hash);
@@ -3038,7 +3096,7 @@ sub git_blobdiff {
                                               hash=>$hash, hash_parent=>$hash_parent,
                                               hash_base=>$hash_base, hash_parent_base=>$hash_parent_base,
                                               file_name=>$file_name, file_parent=>$file_parent)},
-                               "plain");
+                               "raw");
                git_header_html(undef, $expires);
                if (defined $hash_base && (my %co = parse_commit($hash_base))) {
                        git_print_page_nav('','', $hash_base,$co{'tree'},$hash_base, $formats_nav);
@@ -3058,7 +3116,7 @@ sub git_blobdiff {
                        -type => 'text/plain',
                        -charset => 'utf-8',
                        -expires => $expires,
-                       -content_disposition => qq(inline; filename="${file_name}.patch"));
+                       -content_disposition => qq(inline; filename=") . quotemeta($file_name) . qq(.patch"));
 
                print "X-Git-Url: " . $cgi->self_url() . "\n\n";
 
@@ -3078,8 +3136,8 @@ sub git_blobdiff {
 
        } else {
                while (my $line = <$fd>) {
-                       $line =~ s!a/($hash|$hash_parent)!a/$diffinfo{'from_file'}!g;
-                       $line =~ s!b/($hash|$hash_parent)!b/$diffinfo{'to_file'}!g;
+                       $line =~ s!a/($hash|$hash_parent)!'a/'.esc_html($diffinfo{'from_file'})!eg;
+                       $line =~ s!b/($hash|$hash_parent)!'b/'.esc_html($diffinfo{'to_file'})!eg;
 
                        print $line;
 
@@ -3141,7 +3199,7 @@ sub git_commitdiff {
                my $formats_nav =
                        $cgi->a({-href => href(action=>"commitdiff_plain",
                                               hash=>$hash, hash_parent=>$hash_parent)},
-                               "plain");
+                               "raw");
 
                git_header_html(undef, $expires);
                git_print_page_nav('commitdiff','', $hash,$co{'tree'},$hash, $formats_nav);
@@ -3508,7 +3566,7 @@ XML
                        if (!($line =~ m/^:([0-7]{6}) ([0-7]{6}) ([0-9a-fA-F]{40}) ([0-9a-fA-F]{40}) (.)([0-9]{0,3})\t(.*)$/)) {
                                next;
                        }
-                       my $file = validate_input(unquote($7));
+                       my $file = esc_html(unquote($7));
                        $file = decode("utf8", $file, Encode::FB_DEFAULT);
                        print "$file<br/>\n";
                }
diff --git a/grep.c b/grep.c
new file mode 100644 (file)
index 0000000..c411ddd
--- /dev/null
+++ b/grep.c
@@ -0,0 +1,498 @@
+#include "cache.h"
+#include <regex.h>
+#include "grep.h"
+
+void append_grep_pattern(struct grep_opt *opt, const char *pat,
+                        const char *origin, int no, enum grep_pat_token t)
+{
+       struct grep_pat *p = xcalloc(1, sizeof(*p));
+       p->pattern = pat;
+       p->origin = origin;
+       p->no = no;
+       p->token = t;
+       *opt->pattern_tail = p;
+       opt->pattern_tail = &p->next;
+       p->next = NULL;
+}
+
+static void compile_regexp(struct grep_pat *p, struct grep_opt *opt)
+{
+       int err = regcomp(&p->regexp, p->pattern, opt->regflags);
+       if (err) {
+               char errbuf[1024];
+               char where[1024];
+               if (p->no)
+                       sprintf(where, "In '%s' at %d, ",
+                               p->origin, p->no);
+               else if (p->origin)
+                       sprintf(where, "%s, ", p->origin);
+               else
+                       where[0] = 0;
+               regerror(err, &p->regexp, errbuf, 1024);
+               regfree(&p->regexp);
+               die("%s'%s': %s", where, p->pattern, errbuf);
+       }
+}
+
+static struct grep_expr *compile_pattern_expr(struct grep_pat **);
+static struct grep_expr *compile_pattern_atom(struct grep_pat **list)
+{
+       struct grep_pat *p;
+       struct grep_expr *x;
+
+       p = *list;
+       switch (p->token) {
+       case GREP_PATTERN: /* atom */
+       case GREP_PATTERN_HEAD:
+       case GREP_PATTERN_BODY:
+               x = xcalloc(1, sizeof (struct grep_expr));
+               x->node = GREP_NODE_ATOM;
+               x->u.atom = p;
+               *list = p->next;
+               return x;
+       case GREP_OPEN_PAREN:
+               *list = p->next;
+               x = compile_pattern_expr(list);
+               if (!x)
+                       return NULL;
+               if (!*list || (*list)->token != GREP_CLOSE_PAREN)
+                       die("unmatched parenthesis");
+               *list = (*list)->next;
+               return x;
+       default:
+               return NULL;
+       }
+}
+
+static struct grep_expr *compile_pattern_not(struct grep_pat **list)
+{
+       struct grep_pat *p;
+       struct grep_expr *x;
+
+       p = *list;
+       switch (p->token) {
+       case GREP_NOT:
+               if (!p->next)
+                       die("--not not followed by pattern expression");
+               *list = p->next;
+               x = xcalloc(1, sizeof (struct grep_expr));
+               x->node = GREP_NODE_NOT;
+               x->u.unary = compile_pattern_not(list);
+               if (!x->u.unary)
+                       die("--not followed by non pattern expression");
+               return x;
+       default:
+               return compile_pattern_atom(list);
+       }
+}
+
+static struct grep_expr *compile_pattern_and(struct grep_pat **list)
+{
+       struct grep_pat *p;
+       struct grep_expr *x, *y, *z;
+
+       x = compile_pattern_not(list);
+       p = *list;
+       if (p && p->token == GREP_AND) {
+               if (!p->next)
+                       die("--and not followed by pattern expression");
+               *list = p->next;
+               y = compile_pattern_and(list);
+               if (!y)
+                       die("--and not followed by pattern expression");
+               z = xcalloc(1, sizeof (struct grep_expr));
+               z->node = GREP_NODE_AND;
+               z->u.binary.left = x;
+               z->u.binary.right = y;
+               return z;
+       }
+       return x;
+}
+
+static struct grep_expr *compile_pattern_or(struct grep_pat **list)
+{
+       struct grep_pat *p;
+       struct grep_expr *x, *y, *z;
+
+       x = compile_pattern_and(list);
+       p = *list;
+       if (x && p && p->token != GREP_CLOSE_PAREN) {
+               y = compile_pattern_or(list);
+               if (!y)
+                       die("not a pattern expression %s", p->pattern);
+               z = xcalloc(1, sizeof (struct grep_expr));
+               z->node = GREP_NODE_OR;
+               z->u.binary.left = x;
+               z->u.binary.right = y;
+               return z;
+       }
+       return x;
+}
+
+static struct grep_expr *compile_pattern_expr(struct grep_pat **list)
+{
+       return compile_pattern_or(list);
+}
+
+void compile_grep_patterns(struct grep_opt *opt)
+{
+       struct grep_pat *p;
+
+       for (p = opt->pattern_list; p; p = p->next) {
+               switch (p->token) {
+               case GREP_PATTERN: /* atom */
+               case GREP_PATTERN_HEAD:
+               case GREP_PATTERN_BODY:
+                       if (!opt->fixed)
+                               compile_regexp(p, opt);
+                       break;
+               default:
+                       opt->extended = 1;
+                       break;
+               }
+       }
+
+       if (!opt->extended)
+               return;
+
+       /* Then bundle them up in an expression.
+        * A classic recursive descent parser would do.
+        */
+       p = opt->pattern_list;
+       opt->pattern_expression = compile_pattern_expr(&p);
+       if (p)
+               die("incomplete pattern expression: %s", p->pattern);
+}
+
+static void free_pattern_expr(struct grep_expr *x)
+{
+       switch (x->node) {
+       case GREP_NODE_ATOM:
+               break;
+       case GREP_NODE_NOT:
+               free_pattern_expr(x->u.unary);
+               break;
+       case GREP_NODE_AND:
+       case GREP_NODE_OR:
+               free_pattern_expr(x->u.binary.left);
+               free_pattern_expr(x->u.binary.right);
+               break;
+       }
+       free(x);
+}
+
+void free_grep_patterns(struct grep_opt *opt)
+{
+       struct grep_pat *p, *n;
+
+       for (p = opt->pattern_list; p; p = n) {
+               n = p->next;
+               switch (p->token) {
+               case GREP_PATTERN: /* atom */
+               case GREP_PATTERN_HEAD:
+               case GREP_PATTERN_BODY:
+                       regfree(&p->regexp);
+                       break;
+               default:
+                       break;
+               }
+               free(p);
+       }
+
+       if (!opt->extended)
+               return;
+       free_pattern_expr(opt->pattern_expression);
+}
+
+static char *end_of_line(char *cp, unsigned long *left)
+{
+       unsigned long l = *left;
+       while (l && *cp != '\n') {
+               l--;
+               cp++;
+       }
+       *left = l;
+       return cp;
+}
+
+static int word_char(char ch)
+{
+       return isalnum(ch) || ch == '_';
+}
+
+static void show_line(struct grep_opt *opt, const char *bol, const char *eol,
+                     const char *name, unsigned lno, char sign)
+{
+       if (opt->pathname)
+               printf("%s%c", name, sign);
+       if (opt->linenum)
+               printf("%d%c", lno, sign);
+       printf("%.*s\n", (int)(eol-bol), bol);
+}
+
+/*
+ * NEEDSWORK: share code with diff.c
+ */
+#define FIRST_FEW_BYTES 8000
+static int buffer_is_binary(const char *ptr, unsigned long size)
+{
+       if (FIRST_FEW_BYTES < size)
+               size = FIRST_FEW_BYTES;
+       return !!memchr(ptr, 0, size);
+}
+
+static int fixmatch(const char *pattern, char *line, regmatch_t *match)
+{
+       char *hit = strstr(line, pattern);
+       if (!hit) {
+               match->rm_so = match->rm_eo = -1;
+               return REG_NOMATCH;
+       }
+       else {
+               match->rm_so = hit - line;
+               match->rm_eo = match->rm_so + strlen(pattern);
+               return 0;
+       }
+}
+
+static int match_one_pattern(struct grep_opt *opt, struct grep_pat *p, char *bol, char *eol, enum grep_context ctx)
+{
+       int hit = 0;
+       int at_true_bol = 1;
+       regmatch_t pmatch[10];
+
+       if ((p->token != GREP_PATTERN) &&
+           ((p->token == GREP_PATTERN_HEAD) != (ctx == GREP_CONTEXT_HEAD)))
+               return 0;
+
+ again:
+       if (!opt->fixed) {
+               regex_t *exp = &p->regexp;
+               hit = !regexec(exp, bol, ARRAY_SIZE(pmatch),
+                              pmatch, 0);
+       }
+       else {
+               hit = !fixmatch(p->pattern, bol, pmatch);
+       }
+
+       if (hit && opt->word_regexp) {
+               if ((pmatch[0].rm_so < 0) ||
+                   (eol - bol) <= pmatch[0].rm_so ||
+                   (pmatch[0].rm_eo < 0) ||
+                   (eol - bol) < pmatch[0].rm_eo)
+                       die("regexp returned nonsense");
+
+               /* Match beginning must be either beginning of the
+                * line, or at word boundary (i.e. the last char must
+                * not be a word char).  Similarly, match end must be
+                * either end of the line, or at word boundary
+                * (i.e. the next char must not be a word char).
+                */
+               if ( ((pmatch[0].rm_so == 0 && at_true_bol) ||
+                     !word_char(bol[pmatch[0].rm_so-1])) &&
+                    ((pmatch[0].rm_eo == (eol-bol)) ||
+                     !word_char(bol[pmatch[0].rm_eo])) )
+                       ;
+               else
+                       hit = 0;
+
+               if (!hit && pmatch[0].rm_so + bol + 1 < eol) {
+                       /* There could be more than one match on the
+                        * line, and the first match might not be
+                        * strict word match.  But later ones could be!
+                        */
+                       bol = pmatch[0].rm_so + bol + 1;
+                       at_true_bol = 0;
+                       goto again;
+               }
+       }
+       return hit;
+}
+
+static int match_expr_eval(struct grep_opt *opt,
+                          struct grep_expr *x,
+                          char *bol, char *eol,
+                          enum grep_context ctx)
+{
+       switch (x->node) {
+       case GREP_NODE_ATOM:
+               return match_one_pattern(opt, x->u.atom, bol, eol, ctx);
+               break;
+       case GREP_NODE_NOT:
+               return !match_expr_eval(opt, x->u.unary, bol, eol, ctx);
+       case GREP_NODE_AND:
+               return (match_expr_eval(opt, x->u.binary.left, bol, eol, ctx) &&
+                       match_expr_eval(opt, x->u.binary.right, bol, eol, ctx));
+       case GREP_NODE_OR:
+               return (match_expr_eval(opt, x->u.binary.left, bol, eol, ctx) ||
+                       match_expr_eval(opt, x->u.binary.right, bol, eol, ctx));
+       }
+       die("Unexpected node type (internal error) %d\n", x->node);
+}
+
+static int match_expr(struct grep_opt *opt, char *bol, char *eol,
+                     enum grep_context ctx)
+{
+       struct grep_expr *x = opt->pattern_expression;
+       return match_expr_eval(opt, x, bol, eol, ctx);
+}
+
+static int match_line(struct grep_opt *opt, char *bol, char *eol,
+                     enum grep_context ctx)
+{
+       struct grep_pat *p;
+       if (opt->extended)
+               return match_expr(opt, bol, eol, ctx);
+       for (p = opt->pattern_list; p; p = p->next) {
+               if (match_one_pattern(opt, p, bol, eol, ctx))
+                       return 1;
+       }
+       return 0;
+}
+
+int grep_buffer(struct grep_opt *opt, const char *name, char *buf, unsigned long size)
+{
+       char *bol = buf;
+       unsigned long left = size;
+       unsigned lno = 1;
+       struct pre_context_line {
+               char *bol;
+               char *eol;
+       } *prev = NULL, *pcl;
+       unsigned last_hit = 0;
+       unsigned last_shown = 0;
+       int binary_match_only = 0;
+       const char *hunk_mark = "";
+       unsigned count = 0;
+       enum grep_context ctx = GREP_CONTEXT_HEAD;
+
+       if (buffer_is_binary(buf, size)) {
+               switch (opt->binary) {
+               case GREP_BINARY_DEFAULT:
+                       binary_match_only = 1;
+                       break;
+               case GREP_BINARY_NOMATCH:
+                       return 0; /* Assume unmatch */
+                       break;
+               default:
+                       break;
+               }
+       }
+
+       if (opt->pre_context)
+               prev = xcalloc(opt->pre_context, sizeof(*prev));
+       if (opt->pre_context || opt->post_context)
+               hunk_mark = "--\n";
+
+       while (left) {
+               char *eol, ch;
+               int hit = 0;
+
+               eol = end_of_line(bol, &left);
+               ch = *eol;
+               *eol = 0;
+
+               if ((ctx == GREP_CONTEXT_HEAD) && (eol == bol))
+                       ctx = GREP_CONTEXT_BODY;
+
+               hit = match_line(opt, bol, eol, ctx);
+               *eol = ch;
+
+               /* "grep -v -e foo -e bla" should list lines
+                * that do not have either, so inversion should
+                * be done outside.
+                */
+               if (opt->invert)
+                       hit = !hit;
+               if (opt->unmatch_name_only) {
+                       if (hit)
+                               return 0;
+                       goto next_line;
+               }
+               if (hit) {
+                       count++;
+                       if (opt->status_only)
+                               return 1;
+                       if (binary_match_only) {
+                               printf("Binary file %s matches\n", name);
+                               return 1;
+                       }
+                       if (opt->name_only) {
+                               printf("%s\n", 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) {
+                               unsigned from;
+                               if (opt->pre_context < lno)
+                                       from = lno - opt->pre_context;
+                               else
+                                       from = 1;
+                               if (from <= last_shown)
+                                       from = last_shown + 1;
+                               if (last_shown && from != last_shown + 1)
+                                       printf(hunk_mark);
+                               while (from < lno) {
+                                       pcl = &prev[lno-from-1];
+                                       show_line(opt, pcl->bol, pcl->eol,
+                                                 name, from, '-');
+                                       from++;
+                               }
+                               last_shown = lno-1;
+                       }
+                       if (last_shown && lno != last_shown + 1)
+                               printf(hunk_mark);
+                       if (!opt->count)
+                               show_line(opt, bol, eol, name, lno, ':');
+                       last_shown = last_hit = lno;
+               }
+               else if (last_hit &&
+                        lno <= last_hit + opt->post_context) {
+                       /* If the last hit is within the post context,
+                        * we need to show this line.
+                        */
+                       if (last_shown && lno != last_shown + 1)
+                               printf(hunk_mark);
+                       show_line(opt, bol, eol, name, lno, '-');
+                       last_shown = lno;
+               }
+               if (opt->pre_context) {
+                       memmove(prev+1, prev,
+                               (opt->pre_context-1) * sizeof(*prev));
+                       prev->bol = bol;
+                       prev->eol = eol;
+               }
+
+       next_line:
+               bol = eol + 1;
+               if (!left)
+                       break;
+               left--;
+               lno++;
+       }
+
+       free(prev);
+
+       if (opt->status_only)
+               return 0;
+       if (opt->unmatch_name_only) {
+               /* We did not see any hit, so we want to show this */
+               printf("%s\n", name);
+               return 1;
+       }
+
+       /* NEEDSWORK:
+        * The real "grep -c foo *.c" gives many "bar.c:0" lines,
+        * which feels mostly useless but sometimes useful.  Maybe
+        * make it another option?  For now suppress them.
+        */
+       if (opt->count && count)
+               printf("%s:%u\n", name, count);
+       return !!last_hit;
+}
+
diff --git a/grep.h b/grep.h
new file mode 100644 (file)
index 0000000..af9098c
--- /dev/null
+++ b/grep.h
@@ -0,0 +1,79 @@
+#ifndef GREP_H
+#define GREP_H
+
+enum grep_pat_token {
+       GREP_PATTERN,
+       GREP_PATTERN_HEAD,
+       GREP_PATTERN_BODY,
+       GREP_AND,
+       GREP_OPEN_PAREN,
+       GREP_CLOSE_PAREN,
+       GREP_NOT,
+       GREP_OR,
+};
+
+enum grep_context {
+       GREP_CONTEXT_HEAD,
+       GREP_CONTEXT_BODY,
+};
+
+struct grep_pat {
+       struct grep_pat *next;
+       const char *origin;
+       int no;
+       enum grep_pat_token token;
+       const char *pattern;
+       regex_t regexp;
+};
+
+enum grep_expr_node {
+       GREP_NODE_ATOM,
+       GREP_NODE_NOT,
+       GREP_NODE_AND,
+       GREP_NODE_OR,
+};
+
+struct grep_expr {
+       enum grep_expr_node node;
+       union {
+               struct grep_pat *atom;
+               struct grep_expr *unary;
+               struct {
+                       struct grep_expr *left;
+                       struct grep_expr *right;
+               } binary;
+       } u;
+};
+
+struct grep_opt {
+       struct grep_pat *pattern_list;
+       struct grep_pat **pattern_tail;
+       struct grep_expr *pattern_expression;
+       int prefix_length;
+       regex_t regexp;
+       unsigned linenum:1;
+       unsigned invert:1;
+       unsigned status_only:1;
+       unsigned name_only:1;
+       unsigned unmatch_name_only:1;
+       unsigned count:1;
+       unsigned word_regexp:1;
+       unsigned fixed:1;
+#define GREP_BINARY_DEFAULT    0
+#define GREP_BINARY_NOMATCH    1
+#define GREP_BINARY_TEXT       2
+       unsigned binary:2;
+       unsigned extended:1;
+       unsigned relative:1;
+       unsigned pathname:1;
+       int regflags;
+       unsigned pre_context;
+       unsigned post_context;
+};
+
+extern void append_grep_pattern(struct grep_opt *opt, const char *pat, const char *origin, int no, enum grep_pat_token t);
+extern void compile_grep_patterns(struct grep_opt *opt);
+extern void free_grep_patterns(struct grep_opt *opt);
+extern int grep_buffer(struct grep_opt *opt, const char *name, char *buf, unsigned long size);
+
+#endif
diff --git a/http.h b/http.h
index 9ca16acec25e3925c36bbade09b2cb7b4aa07842..6e12e416889503553ae502be31b2e9ed9e7dc4dd 100644 (file)
--- a/http.h
+++ b/http.h
 #define NO_CURL_EASY_DUPHANDLE
 #endif
 
+#if LIBCURL_VERSION_NUM < 0x070a03
+#define CURLE_HTTP_RETURNED_ERROR CURLE_HTTP_NOT_FOUND
+#endif
+
 struct slot_results
 {
        CURLcode curl_result;
diff --git a/interpolate.c b/interpolate.c
new file mode 100644 (file)
index 0000000..4570c12
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2006 Jon Loeliger
+ */
+
+#include <string.h>
+
+#include "interpolate.h"
+
+
+/*
+ * Convert a NUL-terminated string in buffer orig
+ * into the supplied buffer, result, whose length is reslen,
+ * performing substitutions on %-named sub-strings from
+ * the table, interps, with ninterps entries.
+ *
+ * Example interps:
+ *    {
+ *        { "%H", "example.org"},
+ *        { "%port", "123"},
+ *        { "%%", "%"},
+ *    }
+ *
+ * Returns 1 on a successful substitution pass that fits in result,
+ * Returns 0 on a failed or overflowing substitution pass.
+ */
+
+int interpolate(char *result, int reslen,
+               const char *orig,
+               const struct interp *interps, int ninterps)
+{
+       const char *src = orig;
+       char *dest = result;
+       int newlen = 0;
+       char *name, *value;
+       int namelen, valuelen;
+       int i;
+       char c;
+
+        memset(result, 0, reslen);
+
+       while ((c = *src) && newlen < reslen - 1) {
+               if (c == '%') {
+                       /* Try to match an interpolation string. */
+                       for (i = 0; i < ninterps; i++) {
+                               name = interps[i].name;
+                               namelen = strlen(name);
+                               if (strncmp(src, name, namelen) == 0) {
+                                       break;
+                               }
+                       }
+
+                       /* Check for valid interpolation. */
+                       if (i < ninterps) {
+                               value = interps[i].value;
+                               valuelen = strlen(value);
+
+                               if (newlen + valuelen < reslen - 1) {
+                                       /* Substitute. */
+                                       strncpy(dest, value, valuelen);
+                                       newlen += valuelen;
+                                       dest += valuelen;
+                                       src += namelen;
+                               } else {
+                                       /* Something's not fitting. */
+                                       return 0;
+                               }
+
+                       } else {
+                               /* Skip bogus interpolation. */
+                               *dest++ = *src++;
+                               newlen++;
+                       }
+
+               } else {
+                       /* Straight copy one non-interpolation character. */
+                       *dest++ = *src++;
+                       newlen++;
+               }
+       }
+
+       return newlen < reslen - 1;
+}
diff --git a/interpolate.h b/interpolate.h
new file mode 100644 (file)
index 0000000..d16f924
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2006 Jon Loeliger
+ */
+
+#ifndef INTERPOLATE_H
+#define INTERPOLATE_H
+
+/*
+ * Convert a NUL-terminated string in buffer orig,
+ * performing substitutions on %-named sub-strings from
+ * the interpretation table.
+ */
+
+struct interp {
+       char *name;
+       char *value;
+};
+
+extern int interpolate(char *result, int reslen,
+                      const char *orig,
+                      const struct interp *interps, int ninterps);
+
+#endif /* INTERPOLATE_H */
index 04c6c0082119360e307130f10972371236905747..c0caaee0933382f30cc932731e1e387cc9a03c12 100644 (file)
@@ -42,16 +42,16 @@ static int verify_packfile(struct packed_git *p)
         */
        for (i = err = 0; i < nr_objects; i++) {
                unsigned char sha1[20];
-               struct pack_entry e;
                void *data;
                char type[20];
-               unsigned long size;
+               unsigned long size, offset;
 
                if (nth_packed_object_sha1(p, i, sha1))
                        die("internal error pack-check nth-packed-object");
-               if (!find_pack_entry_one(sha1, &e, p))
+               offset = find_pack_entry_one(sha1, p);
+               if (!offset)
                        die("internal error pack-check find-pack-entry-one");
-               data = unpack_entry_gently(&e, type, &size);
+               data = unpack_entry_gently(p, offset, type, &size);
                if (!data) {
                        err = error("cannot unpack %s from %s",
                                    sha1_to_hex(sha1), p->pack_name);
@@ -84,25 +84,26 @@ static void show_pack_info(struct packed_git *p)
 
        for (i = 0; i < nr_objects; i++) {
                unsigned char sha1[20], base_sha1[20];
-               struct pack_entry e;
                char type[20];
                unsigned long size;
                unsigned long store_size;
+               unsigned long offset;
                unsigned int delta_chain_length;
 
                if (nth_packed_object_sha1(p, i, sha1))
                        die("internal error pack-check nth-packed-object");
-               if (!find_pack_entry_one(sha1, &e, p))
+               offset = find_pack_entry_one(sha1, p);
+               if (!offset)
                        die("internal error pack-check find-pack-entry-one");
 
-               packed_object_info_detail(&e, type, &size, &store_size,
+               packed_object_info_detail(p, offset, type, &size, &store_size,
                                          &delta_chain_length,
                                          base_sha1);
                printf("%s ", sha1_to_hex(sha1));
                if (!delta_chain_length)
-                       printf("%-6s %lu %u\n", type, size, e.offset);
+                       printf("%-6s %lu %lu\n", type, size, offset);
                else {
-                       printf("%-6s %lu %u %u %s\n", type, size, e.offset,
+                       printf("%-6s %lu %lu %u %s\n", type, size, offset,
                               delta_chain_length, sha1_to_hex(base_sha1));
                        if (delta_chain_length < MAX_CHAIN)
                                chain_histogram[delta_chain_length]++;
diff --git a/pack.h b/pack.h
index eb07b033ae54941d4bd00ee6bc30c7d50fd039b0..05557da1528e3185cf4d7d89a6577beb8f9e95ad 100644 (file)
--- a/pack.h
+++ b/pack.h
@@ -7,7 +7,7 @@
  * Packed object header
  */
 #define PACK_SIGNATURE 0x5041434b      /* "PACK" */
-#define PACK_VERSION 2
+#define PACK_VERSION 3
 #define pack_version_ok(v) ((v) == htonl(2) || (v) == htonl(3))
 struct pack_header {
        unsigned int hdr_signature;
index 20c9d494ac94380c9074997e9cb3c69ff1c04a3f..97c38670b45d852b2f07f3d3addb2aaf6dbd8697 100644 (file)
@@ -347,11 +347,13 @@ int add_file_to_index(const char *path, int verbose)
        ce->ce_mode = create_ce_mode(st.st_mode);
        if (!trust_executable_bit) {
                /* If there is an existing entry, pick the mode bits
-                * from it.
+                * from it, otherwise force to 644.
                 */
                int pos = cache_name_pos(path, namelen);
                if (pos >= 0)
                        ce->ce_mode = active_cache[pos]->ce_mode;
+               else
+                       ce->ce_mode = create_ce_mode(S_IFREG | 0644);
        }
 
        if (index_path(ce->sha1, path, &st, 1))
index c8aacbbdd3089fdc412da368ca7609da590e0ca3..1fcf3a91127250448a659fe0d48393c34ecd7c47 100644 (file)
@@ -2,6 +2,8 @@
 #include "refs.h"
 #include "pkt-line.h"
 #include "run-command.h"
+#include "commit.h"
+#include "object.h"
 
 static const char receive_pack_usage[] = "git-receive-pack <git-dir>";
 
@@ -94,6 +96,21 @@ static int update(struct command *cmd)
                return error("unpack should have generated %s, "
                             "but I can't find it!", new_hex);
        }
+       if (deny_non_fast_forwards && !is_null_sha1(old_sha1)) {
+               struct commit *old_commit, *new_commit;
+               struct commit_list *bases, *ent;
+
+               old_commit = (struct commit *)parse_object(old_sha1);
+               new_commit = (struct commit *)parse_object(new_sha1);
+               bases = get_merge_bases(old_commit, new_commit, 1);
+               for (ent = bases; ent; ent = ent->next)
+                       if (!hashcmp(old_sha1, ent->item->object.sha1))
+                               break;
+               free_commit_list(bases);
+               if (!ent)
+                       return error("denying non-fast forward;"
+                                    " you should pull first");
+       }
        if (run_update_hook(name, old_hex, new_hex)) {
                cmd->error_string = "hook declined";
                return error("hook declined to update %s", name);
index cb13b90776793dba8d18af4aecefb9e0dde5628b..d87cb6cd6474375c85855670fef713288af2fb34 100644 (file)
@@ -6,6 +6,8 @@
 #include "diff.h"
 #include "refs.h"
 #include "revision.h"
+#include <regex.h>
+#include "grep.h"
 
 static char *path_name(struct name_path *path, const char *name)
 {
@@ -672,6 +674,42 @@ int handle_revision_arg(const char *arg, struct rev_info *revs,
        return 0;
 }
 
+static void add_grep(struct rev_info *revs, const char *ptn, enum grep_pat_token what)
+{
+       if (!revs->grep_filter) {
+               struct grep_opt *opt = xcalloc(1, sizeof(*opt));
+               opt->status_only = 1;
+               opt->pattern_tail = &(opt->pattern_list);
+               opt->regflags = REG_NEWLINE;
+               revs->grep_filter = opt;
+       }
+       append_grep_pattern(revs->grep_filter, ptn,
+                           "command line", 0, what);
+}
+
+static void add_header_grep(struct rev_info *revs, const char *field, const char *pattern)
+{
+       char *pat;
+       const char *prefix;
+       int patlen, fldlen;
+
+       fldlen = strlen(field);
+       patlen = strlen(pattern);
+       pat = xmalloc(patlen + fldlen + 10);
+       prefix = ".*";
+       if (*pattern == '^') {
+               prefix = "";
+               pattern++;
+       }
+       sprintf(pat, "^%s %s%s", field, prefix, pattern);
+       add_grep(revs, pat, GREP_PATTERN_HEAD);
+}
+
+static void add_message_grep(struct rev_info *revs, const char *pattern)
+{
+       add_grep(revs, pattern, GREP_PATTERN_BODY);
+}
+
 static void add_ignore_packed(struct rev_info *revs, const char *name)
 {
        int num = ++revs->num_ignore_packed;
@@ -913,6 +951,23 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
                                revs->relative_date = 1;
                                continue;
                        }
+
+                       /*
+                        * Grepping the commit log
+                        */
+                       if (!strncmp(arg, "--author=", 9)) {
+                               add_header_grep(revs, "author", arg+9);
+                               continue;
+                       }
+                       if (!strncmp(arg, "--committer=", 12)) {
+                               add_header_grep(revs, "committer", arg+12);
+                               continue;
+                       }
+                       if (!strncmp(arg, "--grep=", 7)) {
+                               add_message_grep(revs, arg+7);
+                               continue;
+                       }
+
                        opts = diff_opt_parse(&revs->diffopt, argv+i, argc-i);
                        if (opts > 0) {
                                revs->diff = 1;
@@ -973,6 +1028,9 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
        if (diff_setup_done(&revs->diffopt) < 0)
                die("diff_setup_done failed");
 
+       if (revs->grep_filter)
+               compile_grep_patterns(revs->grep_filter);
+
        return left;
 }
 
@@ -1045,6 +1103,15 @@ static void mark_boundary_to_show(struct commit *commit)
        }
 }
 
+static int commit_match(struct commit *commit, struct rev_info *opt)
+{
+       if (!opt->grep_filter)
+               return 1;
+       return grep_buffer(opt->grep_filter,
+                          NULL, /* we say nothing, not even filename */
+                          commit->buffer, strlen(commit->buffer));
+}
+
 struct commit *get_revision(struct rev_info *revs)
 {
        struct commit_list *list = revs->commits;
@@ -1105,6 +1172,8 @@ struct commit *get_revision(struct rev_info *revs)
                if (revs->no_merges &&
                    commit->parents && commit->parents->next)
                        continue;
+               if (!commit_match(commit, revs))
+                       continue;
                if (revs->prune_fn && revs->dense) {
                        /* Commit without changes? */
                        if (!(commit->object.flags & TREECHANGE)) {
index a5c35d05cbd6bceb12248dff0b1e5c3f3433aeae..3adab9590a14e25c2659a1933db4af456c263a5b 100644 (file)
@@ -71,6 +71,9 @@ struct rev_info {
        const char      *add_signoff;
        const char      *extra_headers;
 
+       /* Filter by commit log message */
+       struct grep_opt *grep_filter;
+
        /* special limits */
        int max_count;
        unsigned long max_age;
diff --git a/setup.c b/setup.c
index 2afdba414a073705440f887593a1b5daa1023758..9a46a58a4a34a345eb9f9b623a255c9bcba4c757 100644 (file)
--- a/setup.c
+++ b/setup.c
@@ -244,6 +244,8 @@ int check_repository_format_version(const char *var, const char *value)
                repository_format_version = git_config_int(var, value);
        else if (strcmp(var, "core.sharedrepository") == 0)
                shared_repository = git_config_perm(var, value);
+       else if (strcmp(var, "receive.denynonfastforwards") == 0)
+               deny_non_fast_forwards = git_config_bool(var, value);
        return 0;
 }
 
index b89edb9515aa38c2c404367aa28ec5531fcc7de1..27b1ebb720b1530d673f3dfb1c27862b4cf90dfd 100644 (file)
@@ -26,44 +26,40 @@ const unsigned char null_sha1[20];
 
 static unsigned int sha1_file_open_flag = O_NOATIME;
 
-static inline unsigned int hexval(unsigned int c)
-{
-       static signed char val[256] = {
-                -1, -1, -1, -1, -1, -1, -1, -1,                /* 00-07 */
-                -1, -1, -1, -1, -1, -1, -1, -1,                /* 08-0f */
-                -1, -1, -1, -1, -1, -1, -1, -1,                /* 10-17 */
-                -1, -1, -1, -1, -1, -1, -1, -1,                /* 18-1f */
-                -1, -1, -1, -1, -1, -1, -1, -1,                /* 20-27 */
-                -1, -1, -1, -1, -1, -1, -1, -1,                /* 28-2f */
-                 0,  1,  2,  3,  4,  5,  6,  7,                /* 30-37 */
-                 8,  9, -1, -1, -1, -1, -1, -1,                /* 38-3f */
-                -1, 10, 11, 12, 13, 14, 15, -1,                /* 40-47 */
-                -1, -1, -1, -1, -1, -1, -1, -1,                /* 48-4f */
-                -1, -1, -1, -1, -1, -1, -1, -1,                /* 50-57 */
-                -1, -1, -1, -1, -1, -1, -1, -1,                /* 58-5f */
-                -1, 10, 11, 12, 13, 14, 15, -1,                /* 60-67 */
-                -1, -1, -1, -1, -1, -1, -1, -1,                /* 68-67 */
-                -1, -1, -1, -1, -1, -1, -1, -1,                /* 70-77 */
-                -1, -1, -1, -1, -1, -1, -1, -1,                /* 78-7f */
-                -1, -1, -1, -1, -1, -1, -1, -1,                /* 80-87 */
-                -1, -1, -1, -1, -1, -1, -1, -1,                /* 88-8f */
-                -1, -1, -1, -1, -1, -1, -1, -1,                /* 90-97 */
-                -1, -1, -1, -1, -1, -1, -1, -1,                /* 98-9f */
-                -1, -1, -1, -1, -1, -1, -1, -1,                /* a0-a7 */
-                -1, -1, -1, -1, -1, -1, -1, -1,                /* a8-af */
-                -1, -1, -1, -1, -1, -1, -1, -1,                /* b0-b7 */
-                -1, -1, -1, -1, -1, -1, -1, -1,                /* b8-bf */
-                -1, -1, -1, -1, -1, -1, -1, -1,                /* c0-c7 */
-                -1, -1, -1, -1, -1, -1, -1, -1,                /* c8-cf */
-                -1, -1, -1, -1, -1, -1, -1, -1,                /* d0-d7 */
-                -1, -1, -1, -1, -1, -1, -1, -1,                /* d8-df */
-                -1, -1, -1, -1, -1, -1, -1, -1,                /* e0-e7 */
-                -1, -1, -1, -1, -1, -1, -1, -1,                /* e8-ef */
-                -1, -1, -1, -1, -1, -1, -1, -1,                /* f0-f7 */
-                -1, -1, -1, -1, -1, -1, -1, -1,                /* f8-ff */
-       };
-       return val[c];
-}
+signed char hexval_table[256] = {
+        -1, -1, -1, -1, -1, -1, -1, -1,                /* 00-07 */
+        -1, -1, -1, -1, -1, -1, -1, -1,                /* 08-0f */
+        -1, -1, -1, -1, -1, -1, -1, -1,                /* 10-17 */
+        -1, -1, -1, -1, -1, -1, -1, -1,                /* 18-1f */
+        -1, -1, -1, -1, -1, -1, -1, -1,                /* 20-27 */
+        -1, -1, -1, -1, -1, -1, -1, -1,                /* 28-2f */
+         0,  1,  2,  3,  4,  5,  6,  7,                /* 30-37 */
+         8,  9, -1, -1, -1, -1, -1, -1,                /* 38-3f */
+        -1, 10, 11, 12, 13, 14, 15, -1,                /* 40-47 */
+        -1, -1, -1, -1, -1, -1, -1, -1,                /* 48-4f */
+        -1, -1, -1, -1, -1, -1, -1, -1,                /* 50-57 */
+        -1, -1, -1, -1, -1, -1, -1, -1,                /* 58-5f */
+        -1, 10, 11, 12, 13, 14, 15, -1,                /* 60-67 */
+        -1, -1, -1, -1, -1, -1, -1, -1,                /* 68-67 */
+        -1, -1, -1, -1, -1, -1, -1, -1,                /* 70-77 */
+        -1, -1, -1, -1, -1, -1, -1, -1,                /* 78-7f */
+        -1, -1, -1, -1, -1, -1, -1, -1,                /* 80-87 */
+        -1, -1, -1, -1, -1, -1, -1, -1,                /* 88-8f */
+        -1, -1, -1, -1, -1, -1, -1, -1,                /* 90-97 */
+        -1, -1, -1, -1, -1, -1, -1, -1,                /* 98-9f */
+        -1, -1, -1, -1, -1, -1, -1, -1,                /* a0-a7 */
+        -1, -1, -1, -1, -1, -1, -1, -1,                /* a8-af */
+        -1, -1, -1, -1, -1, -1, -1, -1,                /* b0-b7 */
+        -1, -1, -1, -1, -1, -1, -1, -1,                /* b8-bf */
+        -1, -1, -1, -1, -1, -1, -1, -1,                /* c0-c7 */
+        -1, -1, -1, -1, -1, -1, -1, -1,                /* c8-cf */
+        -1, -1, -1, -1, -1, -1, -1, -1,                /* d0-d7 */
+        -1, -1, -1, -1, -1, -1, -1, -1,                /* d8-df */
+        -1, -1, -1, -1, -1, -1, -1, -1,                /* e0-e7 */
+        -1, -1, -1, -1, -1, -1, -1, -1,                /* e8-ef */
+        -1, -1, -1, -1, -1, -1, -1, -1,                /* f0-f7 */
+        -1, -1, -1, -1, -1, -1, -1, -1,                /* f8-ff */
+};
 
 int get_sha1_hex(const char *hex, unsigned char *sha1)
 {
@@ -888,33 +884,32 @@ void * unpack_sha1_file(void *map, unsigned long mapsize, char *type, unsigned l
 }
 
 /* forward declaration for a mutually recursive function */
-static int packed_object_info(struct pack_entry *entry,
+static int packed_object_info(struct packed_git *p, unsigned long offset,
                              char *type, unsigned long *sizep);
 
-static int packed_delta_info(unsigned char *base_sha1,
-                            unsigned long delta_size,
-                            unsigned long left,
+static int packed_delta_info(struct packed_git *p,
+                            unsigned long offset,
                             char *type,
-                            unsigned long *sizep,
-                            struct packed_git *p)
+                            unsigned long *sizep)
 {
-       struct pack_entry base_ent;
+       unsigned long base_offset;
+       unsigned char *base_sha1 = (unsigned char *) p->pack_base + offset;
 
-       if (left < 20)
+       if (p->pack_size < offset + 20)
                die("truncated pack file");
-
        /* The base entry _must_ be in the same pack */
-       if (!find_pack_entry_one(base_sha1, &base_ent, p))
+       base_offset = find_pack_entry_one(base_sha1, p);
+       if (!base_offset)
                die("failed to find delta-pack base object %s",
                    sha1_to_hex(base_sha1));
+       offset += 20;
 
        /* We choose to only get the type of the base object and
         * ignore potentially corrupt pack file that expects the delta
         * based on a base with a wrong size.  This saves tons of
         * inflate() calls.
         */
-
-       if (packed_object_info(&base_ent, type, NULL))
+       if (packed_object_info(p, base_offset, type, NULL))
                die("cannot get info for delta-pack base");
 
        if (sizep) {
@@ -926,8 +921,8 @@ static int packed_delta_info(unsigned char *base_sha1,
 
                memset(&stream, 0, sizeof(stream));
 
-               data = stream.next_in = base_sha1 + 20;
-               stream.avail_in = left - 20;
+               stream.next_in = (unsigned char *) p->pack_base + offset;
+               stream.avail_in = p->pack_size - offset;
                stream.next_out = delta_head;
                stream.avail_out = sizeof(delta_head);
 
@@ -989,75 +984,60 @@ int check_reuse_pack_delta(struct packed_git *p, unsigned long offset,
        return status;
 }
 
-void packed_object_info_detail(struct pack_entry *e,
+void packed_object_info_detail(struct packed_git *p,
+                              unsigned long offset,
                               char *type,
                               unsigned long *size,
                               unsigned long *store_size,
                               unsigned int *delta_chain_length,
                               unsigned char *base_sha1)
 {
-       struct packed_git *p = e->p;
-       unsigned long offset;
-       unsigned char *pack;
+       unsigned long val;
+       unsigned char *next_sha1;
        enum object_type kind;
 
-       offset = unpack_object_header(p, e->offset, &kind, size);
-       pack = (unsigned char *) p->pack_base + offset;
-       if (kind != OBJ_DELTA)
-               *delta_chain_length = 0;
-       else {
-               unsigned int chain_length = 0;
-               if (p->pack_size <= offset + 20)
-                       die("pack file %s records an incomplete delta base",
-                           p->pack_name);
-               hashcpy(base_sha1, pack);
-               do {
-                       struct pack_entry base_ent;
-                       unsigned long junk;
-
-                       find_pack_entry_one(pack, &base_ent, p);
-                       offset = unpack_object_header(p, base_ent.offset,
-                                                     &kind, &junk);
-                       pack = (unsigned char *) p->pack_base + offset;
-                       chain_length++;
-               } while (kind == OBJ_DELTA);
-               *delta_chain_length = chain_length;
-       }
-       switch (kind) {
-       case OBJ_COMMIT:
-       case OBJ_TREE:
-       case OBJ_BLOB:
-       case OBJ_TAG:
-               strcpy(type, type_names[kind]);
-               break;
-       default:
-               die("corrupted pack file %s containing object of kind %d",
-                   p->pack_name, kind);
+       *delta_chain_length = 0;
+       offset = unpack_object_header(p, offset, &kind, size);
+
+       for (;;) {
+               switch (kind) {
+               default:
+                       die("corrupted pack file %s containing object of kind %d",
+                           p->pack_name, kind);
+               case OBJ_COMMIT:
+               case OBJ_TREE:
+               case OBJ_BLOB:
+               case OBJ_TAG:
+                       strcpy(type, type_names[kind]);
+                       *store_size = 0; /* notyet */
+                       return;
+               case OBJ_DELTA:
+                       if (p->pack_size <= offset + 20)
+                               die("pack file %s records an incomplete delta base",
+                                   p->pack_name);
+                       next_sha1 = (unsigned char *) p->pack_base + offset;
+                       if (*delta_chain_length == 0)
+                               hashcpy(base_sha1, next_sha1);
+                       offset = find_pack_entry_one(next_sha1, p);
+                       break;
+               }
+               offset = unpack_object_header(p, offset, &kind, &val);
+               (*delta_chain_length)++;
        }
-       *store_size = 0; /* notyet */
 }
 
-static int packed_object_info(struct pack_entry *entry,
+static int packed_object_info(struct packed_git *p, unsigned long offset,
                              char *type, unsigned long *sizep)
 {
-       struct packed_git *p = entry->p;
-       unsigned long offset, size, left;
-       unsigned char *pack;
+       unsigned long size;
        enum object_type kind;
-       int retval;
 
-       if (use_packed_git(p))
-               die("cannot map packed file");
+       offset = unpack_object_header(p, offset, &kind, &size);
 
-       offset = unpack_object_header(p, entry->offset, &kind, &size);
-       pack = (unsigned char *) p->pack_base + offset;
-       left = p->pack_size - offset;
+       if (kind == OBJ_DELTA)
+               return packed_delta_info(p, offset, type, sizep);
 
        switch (kind) {
-       case OBJ_DELTA:
-               retval = packed_delta_info(pack, size, left, type, sizep, p);
-               unuse_packed_git(p);
-               return retval;
        case OBJ_COMMIT:
        case OBJ_TREE:
        case OBJ_BLOB:
@@ -1070,7 +1050,6 @@ static int packed_object_info(struct pack_entry *entry,
        }
        if (sizep)
                *sizep = size;
-       unuse_packed_git(p);
        return 0;
 }
 
@@ -1107,25 +1086,26 @@ static void *unpack_delta_entry(struct packed_git *p,
                                char *type,
                                unsigned long *sizep)
 {
-       struct pack_entry base_ent;
        void *delta_data, *result, *base;
-       unsigned long result_size, base_size;
-       unsigned charbase_sha1;
+       unsigned long result_size, base_size, base_offset;
+       unsigned char *base_sha1;
 
-       if ((offset + 20) >= p->pack_size)
+       if (p->pack_size < offset + 20)
                die("truncated pack file");
-
        /* The base entry _must_ be in the same pack */
        base_sha1 = (unsigned char*)p->pack_base + offset;
-       if (!find_pack_entry_one(base_sha1, &base_ent, p))
+       base_offset = find_pack_entry_one(base_sha1, p);
+       if (!base_offset)
                die("failed to find delta-pack base object %s",
                    sha1_to_hex(base_sha1));
-       base = unpack_entry_gently(&base_ent, type, &base_size);
+       offset += 20;
+
+       base = unpack_entry_gently(p, base_offset, type, &base_size);
        if (!base)
-               die("failed to read delta-pack base object %s",
-                   sha1_to_hex(base_sha1));
+               die("failed to read delta base object at %lu from %s",
+                   base_offset, p->pack_name);
 
-       delta_data = unpack_compressed_entry(p, offset + 20, delta_size);
+       delta_data = unpack_compressed_entry(p, offset, delta_size);
        result = patch_delta(base, base_size,
                             delta_data, delta_size,
                             &result_size);
@@ -1145,7 +1125,7 @@ static void *unpack_entry(struct pack_entry *entry,
 
        if (use_packed_git(p))
                die("cannot map packed file");
-       retval = unpack_entry_gently(entry, type, sizep);
+       retval = unpack_entry_gently(p, entry->offset, type, sizep);
        unuse_packed_git(p);
        if (!retval)
                die("corrupted pack file %s", p->pack_name);
@@ -1153,14 +1133,13 @@ static void *unpack_entry(struct pack_entry *entry,
 }
 
 /* The caller is responsible for use_packed_git()/unuse_packed_git() pair */
-void *unpack_entry_gently(struct pack_entry *entry,
+void *unpack_entry_gently(struct packed_git *p, unsigned long offset,
                          char *type, unsigned long *sizep)
 {
-       struct packed_git *p = entry->p;
-       unsigned long offset, size;
+       unsigned long size;
        enum object_type kind;
 
-       offset = unpack_object_header(p, entry->offset, &kind, &size);
+       offset = unpack_object_header(p, offset, &kind, &size);
        switch (kind) {
        case OBJ_DELTA:
                return unpack_delta_entry(p, offset, size, type, sizep);
@@ -1192,8 +1171,8 @@ int nth_packed_object_sha1(const struct packed_git *p, int n,
        return 0;
 }
 
-int find_pack_entry_one(const unsigned char *sha1,
-                       struct pack_entry *e, struct packed_git *p)
+unsigned long find_pack_entry_one(const unsigned char *sha1,
+                                 struct packed_git *p)
 {
        unsigned int *level1_ofs = p->index_base;
        int hi = ntohl(level1_ofs[*sha1]);
@@ -1203,12 +1182,8 @@ int find_pack_entry_one(const unsigned char *sha1,
        do {
                int mi = (lo + hi) / 2;
                int cmp = hashcmp((unsigned char *)index + (24 * mi) + 4, sha1);
-               if (!cmp) {
-                       e->offset = ntohl(*((unsigned int *) ((char *) index + (24 * mi))));
-                       hashcpy(e->sha1, sha1);
-                       e->p = p;
-                       return 1;
-               }
+               if (!cmp)
+                       return ntohl(*((unsigned int *) ((char *) index + (24 * mi))));
                if (cmp > 0)
                        hi = mi;
                else
@@ -1220,6 +1195,8 @@ int find_pack_entry_one(const unsigned char *sha1,
 static int find_pack_entry(const unsigned char *sha1, struct pack_entry *e, const char **ignore_packed)
 {
        struct packed_git *p;
+       unsigned long offset;
+
        prepare_packed_git();
 
        for (p = packed_git; p; p = p->next) {
@@ -1231,8 +1208,13 @@ static int find_pack_entry(const unsigned char *sha1, struct pack_entry *e, cons
                        if (*ig)
                                continue;
                }
-               if (find_pack_entry_one(sha1, e, p))
+               offset = find_pack_entry_one(sha1, p);
+               if (offset) {
+                       e->offset = offset;
+                       e->p = p;
+                       hashcpy(e->sha1, sha1);
                        return 1;
+               }
        }
        return 0;
 }
@@ -1241,10 +1223,9 @@ struct packed_git *find_sha1_pack(const unsigned char *sha1,
                                  struct packed_git *packs)
 {
        struct packed_git *p;
-       struct pack_entry e;
 
        for (p = packs; p; p = p->next) {
-               if (find_pack_entry_one(sha1, &e, p))
+               if (find_pack_entry_one(sha1, p))
                        return p;
        }
        return NULL;
@@ -1263,12 +1244,16 @@ int sha1_object_info(const unsigned char *sha1, char *type, unsigned long *sizep
        if (!map) {
                struct pack_entry e;
 
-               if (find_pack_entry(sha1, &e, NULL))
-                       return packed_object_info(&e, type, sizep);
-               reprepare_packed_git();
-               if (find_pack_entry(sha1, &e, NULL))
-                       return packed_object_info(&e, type, sizep);
-               return error("unable to find %s", sha1_to_hex(sha1));
+               if (!find_pack_entry(sha1, &e, NULL)) {
+                       reprepare_packed_git();
+                       if (!find_pack_entry(sha1, &e, NULL))
+                               return error("unable to find %s", sha1_to_hex(sha1));
+               }
+               if (use_packed_git(e.p))
+                       die("cannot map packed file");
+               status = packed_object_info(e.p, e.offset, type, sizep);
+               unuse_packed_git(e.p);
+               return status;
        }
        if (unpack_sha1_header(&stream, map, mapsize, hdr, sizeof(hdr)) < 0)
                status = error("unable to unpack %s header",
index 84d24c6abf39f395281c7d80476575cbd482e6f0..ed711f2079752fd7ecc24b1a48b7d6fcc256659f 100644 (file)
@@ -431,6 +431,26 @@ static int peel_onion(const char *name, int len, unsigned char *sha1)
        return 0;
 }
 
+static int get_describe_name(const char *name, int len, unsigned char *sha1)
+{
+       const char *cp;
+
+       for (cp = name + len - 1; name + 2 <= cp; cp--) {
+               char ch = *cp;
+               if (hexval(ch) & ~0377) {
+                       /* We must be looking at g in "SOMETHING-g"
+                        * for it to be describe output.
+                        */
+                       if (ch == 'g' && cp[-1] == '-') {
+                               cp++;
+                               len -= cp - name;
+                               return get_short_sha1(cp, len, sha1, 1);
+                       }
+               }
+       }
+       return -1;
+}
+
 static int get_sha1_1(const char *name, int len, unsigned char *sha1)
 {
        int ret, has_suffix;
@@ -472,6 +492,12 @@ static int get_sha1_1(const char *name, int len, unsigned char *sha1)
        ret = get_sha1_basic(name, len, sha1);
        if (!ret)
                return 0;
+
+       /* It could be describe output that is "SOMETHING-gXXXX" */
+       ret = get_describe_name(name, len, sha1);
+       if (!ret)
+               return 0;
+
        return get_short_sha1(name, len, sha1, 0);
 }
 
index 5b04efc89d54c79df49495bb8fa44931729df682..6907cbcd295614bf895ef2a8d0be59e08e570f09 100755 (executable)
@@ -61,4 +61,16 @@ test_expect_success \
         test -f .git/logs/refs/heads/g/h/i &&
         diff expect .git/logs/refs/heads/g/h/i'
 
+test_expect_success \
+    'git branch j/k should work after branch j has been deleted' \
+       'git-branch j &&
+        git-branch -d j &&
+        git-branch j/k'
+
+test_expect_success \
+    'git branch l should work after branch l/m has been deleted' \
+       'git-branch l/m &&
+        git-branch -d l/m &&
+        git-branch l'
+
 test_done
index 6cd05c3d9096e8017a6bfb23982ea46e0ae93630..c20e4c29fcc864dff80e5ae73a8ad7ede1f794f0 100755 (executable)
@@ -19,4 +19,26 @@ test_expect_success \
     'Test that "git-add -- -q" works' \
     'touch -- -q && git-add -- -q'
 
+test_expect_success \
+       'git-add: Test that executable bit is not used if core.filemode=0' \
+       'git repo-config core.filemode 0 &&
+        echo foo >xfoo1 &&
+        chmod 755 xfoo1 &&
+        git-add xfoo1 &&
+        case "`git-ls-files --stage xfoo1`" in
+        100644" "*xfoo1) echo ok;;
+        *) echo fail; git-ls-files --stage xfoo1; exit 1;;
+        esac'
+
+test_expect_success \
+       'git-update-index --add: Test that executable bit is not used...' \
+       'git repo-config core.filemode 0 &&
+        echo foo >xfoo2 &&
+        chmod 755 xfoo2 &&
+        git-update-index --add xfoo2 &&
+        case "`git-ls-files --stage xfoo2`" in
+        100644" "*xfoo2) echo ok;;
+        *) echo fail; git-ls-files --stage xfoo2; exit 1;;
+        esac'
+
 test_done
index f3694ac3c78e64b6b77ebfef4cb3e90f9fb6c32c..8afb89971752fe4a91b9b7c62b4b0a6b69a8272b 100755 (executable)
@@ -64,4 +64,18 @@ test_expect_success \
        cmp victim/.git/refs/heads/master .git/refs/heads/master
 '
 
+unset GIT_CONFIG GIT_CONFIG_LOCAL
+HOME=`pwd`/no-such-directory
+export HOME ;# this way we force the victim/.git/config to be used.
+
+test_expect_success \
+        'pushing with --force should be denied with denyNonFastforwards' '
+       cd victim &&
+       git-repo-config receive.denyNonFastforwards true &&
+       cd .. &&
+       git-update-ref refs/heads/master master^ &&
+       git-send-pack --force ./victim/.git/ master &&
+       ! diff -u .git/refs/heads/master victim/.git/refs/heads/master
+'
+
 test_done
diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh
new file mode 100755 (executable)
index 0000000..df0ae48
--- /dev/null
@@ -0,0 +1,69 @@
+#!/bin/sh
+# Copyright (c) 2006, Junio C Hamano.
+
+test_description='Per branch config variables affects "git fetch".
+
+'
+
+. ./test-lib.sh
+
+D=`pwd`
+
+test_expect_success setup '
+       echo >file original &&
+       git add file &&
+       git commit -a -m original'
+
+test_expect_success "clone and setup child repos" '
+       git clone . one &&
+       cd one &&
+       echo >file updated by one &&
+       git commit -a -m "updated by one" &&
+       cd .. &&
+       git clone . two &&
+       cd two &&
+       git repo-config branch.master.remote one &&
+       {
+               echo "URL: ../one/.git/"
+               echo "Pull: refs/heads/master:refs/heads/one"
+       } >.git/remotes/one
+       cd .. &&
+       git clone . three &&
+       cd three &&
+       git repo-config branch.master.remote two &&
+       git repo-config branch.master.merge refs/heads/one &&
+       {
+               echo "URL: ../two/.git/"
+               echo "Pull: refs/heads/master:refs/heads/two"
+               echo "Pull: refs/heads/one:refs/heads/one"
+       } >.git/remotes/two
+'
+
+test_expect_success "fetch test" '
+       cd "$D" &&
+       echo >file updated by origin &&
+       git commit -a -m "updated by origin" &&
+       cd two &&
+       git fetch &&
+       test -f .git/refs/heads/one &&
+       mine=`git rev-parse refs/heads/one` &&
+       his=`cd ../one && git rev-parse refs/heads/master` &&
+       test "z$mine" = "z$his"
+'
+
+test_expect_success "fetch test for-merge" '
+       cd "$D" &&
+       cd three &&
+       git fetch &&
+       test -f .git/refs/heads/two &&
+       test -f .git/refs/heads/one &&
+       master_in_two=`cd ../two && git rev-parse master` &&
+       one_in_two=`cd ../two && git rev-parse one` &&
+       {
+               echo "$master_in_two    not-for-merge"
+               echo "$one_in_two       "
+       } >expected &&
+       cut -f -2 .git/FETCH_HEAD >actual &&
+       diff expected actual'
+
+test_done
diff --git a/t/t6001-rev-list-graft.sh b/t/t6001-rev-list-graft.sh
new file mode 100755 (executable)
index 0000000..b2131cd
--- /dev/null
@@ -0,0 +1,113 @@
+#!/bin/sh
+
+test_description='Revision traversal vs grafts and path limiter'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+       mkdir subdir &&
+       echo >fileA fileA &&
+       echo >subdir/fileB fileB &&
+       git add fileA subdir/fileB &&
+       git commit -a -m "Initial in one history." &&
+       A0=`git rev-parse --verify HEAD` &&
+
+       echo >fileA fileA modified &&
+       git commit -a -m "Second in one history." &&
+       A1=`git rev-parse --verify HEAD` &&
+
+       echo >subdir/fileB fileB modified &&
+       git commit -a -m "Third in one history." &&
+       A2=`git rev-parse --verify HEAD` &&
+
+       rm -f .git/refs/heads/master .git/index &&
+
+       echo >fileA fileA again &&
+       echo >subdir/fileB fileB again &&
+       git add fileA subdir/fileB &&
+       git commit -a -m "Initial in alternate history." &&
+       B0=`git rev-parse --verify HEAD` &&
+
+       echo >fileA fileA modified in alternate history &&
+       git commit -a -m "Second in alternate history." &&
+       B1=`git rev-parse --verify HEAD` &&
+
+       echo >subdir/fileB fileB modified in alternate history &&
+       git commit -a -m "Third in alternate history." &&
+       B2=`git rev-parse --verify HEAD` &&
+       : done
+'
+
+check () {
+       type=$1
+       shift
+
+       arg=
+       which=arg
+       rm -f test.expect
+       for a
+       do
+               if test "z$a" = z--
+               then
+                       which=expect
+                       child=
+                       continue
+               fi
+               if test "$which" = arg
+               then
+                       arg="$arg$a "
+                       continue
+               fi
+               if test "$type" = basic
+               then
+                       echo "$a"
+               else
+                       if test "z$child" != z
+                       then
+                               echo "$child $a"
+                       fi
+                       child="$a"
+               fi
+       done >test.expect
+       if test "$type" != basic && test "z$child" != z
+       then
+               echo >>test.expect $child
+       fi
+       if test $type = basic
+       then
+               git rev-list $arg >test.actual
+       elif test $type = parents
+       then
+               git rev-list --parents $arg >test.actual
+       elif test $type = parents-raw
+       then
+               git rev-list --parents --pretty=raw $arg |
+               sed -n -e 's/^commit //p' >test.actual
+       fi
+       diff test.expect test.actual
+}
+
+for type in basic parents parents-raw
+do
+       test_expect_success 'without grafts' "
+               rm -f .git/info/grafts
+               check $type $B2 -- $B2 $B1 $B0
+       "
+
+       test_expect_success 'with grafts' "
+               echo '$B0 $A2' >.git/info/grafts
+               check $type $B2 -- $B2 $B1 $B0 $A2 $A1 $A0
+       "
+
+       test_expect_success 'without grafts, with pathlimit' "
+               rm -f .git/info/grafts
+               check $type $B2 subdir -- $B2 $B0
+       "
+
+       test_expect_success 'with grafts, with pathlimit' "
+               echo '$B0 $A2' >.git/info/grafts
+               check $type $B2 subdir -- $B2 $B0 $A2 $A0
+       "
+
+done
+test_done
index b64e8b7d773f9503c7a5b0e80945be60f71e345f..085d4a096b90e076afef70a7e8b7746f58a49060 100755 (executable)
@@ -31,6 +31,15 @@ test_expect_success setup '
        git checkout master
 '
 
+test_expect_success "checkout from non-existing branch" '
+
+       git checkout -b delete-me master &&
+       rm .git/refs/heads/delete-me &&
+       test refs/heads/delete-me = "$(git symbolic-ref HEAD)" &&
+       git checkout master &&
+       test refs/heads/master = "$(git symbolic-ref HEAD)"
+'
+
 test_expect_success "checkout with dirty tree without -m" '
 
        fill 0 1 2 3 4 5 >one &&
index e2629339d4b1c9acf8cdf002f8ac59ef736eacf3..0fe2718845fa8ad66dfa850ac2921e33b10b51c1 100755 (executable)
@@ -34,7 +34,7 @@ export GIT_AUTHOR_EMAIL GIT_AUTHOR_NAME
 export GIT_COMMITTER_EMAIL GIT_COMMITTER_NAME
 export EDITOR VISUAL
 
-case $(echo $GIT_TRACE |tr [A-Z] [a-z]) in
+case $(echo $GIT_TRACE |tr "[A-Z]" "[a-z]") in
        1|2|true)
                echo "* warning: Some tests will not work if GIT_TRACE" \
                        "is set as to trace on STDERR ! *"
@@ -211,7 +211,7 @@ export PATH GIT_EXEC_PATH
 PYTHON=`sed -e '1{
        s/^#!//
        q
-}' ../git-merge-recursive` || {
+}' ../git-merge-recursive-old` || {
        error "You haven't built things yet, have you?"
 }
 "$PYTHON" -c 'import subprocess' 2>/dev/null || {