Merge branch 'sk/svnimport'
authorJunio C Hamano <junkio@cox.net>
Mon, 25 Sep 2006 03:00:59 +0000 (20:00 -0700)
committerJunio C Hamano <junkio@cox.net>
Mon, 25 Sep 2006 03:00:59 +0000 (20:00 -0700)
* sk/svnimport:
git-svnimport: Parse log message for Signed-off-by: lines

37 files changed:
.gitignore
Documentation/config.txt
Documentation/git-daemon.txt
Documentation/git-rev-list.txt
Documentation/git-tar-tree.txt
Documentation/git-upload-tar.txt [deleted file]
Documentation/git-zip-tree.txt [deleted file]
Documentation/git.txt
Makefile
archive-tar.c [new file with mode: 0644]
archive-zip.c [new file with mode: 0644]
builtin-grep.c
builtin-pack-objects.c
builtin-rev-list.c
builtin-tar-tree.c
builtin-upload-archive.c
builtin-upload-tar.c [deleted file]
builtin-zip-tree.c [deleted file]
builtin.h
cache.h
daemon.c
diff-delta.c
git-fetch.sh
git-parse-remote.sh
git-resolve.sh
git.c
gitweb/gitweb.perl
grep.c [new file with mode: 0644]
grep.h [new file with mode: 0644]
interpolate.c [new file with mode: 0644]
interpolate.h [new file with mode: 0644]
pack-check.c
pack.h
revision.c
revision.h
sha1_file.c
t/t5510-fetch.sh [new file with mode: 0755]
index a3d9c7a11dc8e2807db377090fd1d4144aad0056..284db5dffc6605a44b08b21a677434758c63cbe5 100644 (file)
@@ -122,13 +122,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 bb2fbc324e6fa93a639b31668a7a030ece7787f0..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).
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 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 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..1bf5ef57e41eaa74836f75d089a07c3a40f2837e 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)
 -------------------------------
index 8467447da90f04cc55d1403ea102c6335331aa52..28091d6be00666b0cbfd2faaa558770c836695cd 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -234,7 +234,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 +246,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 \
@@ -298,10 +300,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-write-tree.o
 
 GITLIBS = $(LIB_FILE) $(XDIFF_LIB)
 LIBS = $(GITLIBS) -lz
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;
+}
diff --git a/archive-zip.c b/archive-zip.c
new file mode 100644 (file)
index 0000000..3ffdad6
--- /dev/null
@@ -0,0 +1,333 @@
+/*
+ * Copyright (c) 2006 Rene Scharfe
+ */
+#include <time.h>
+#include "cache.h"
+#include "commit.h"
+#include "blob.h"
+#include "tree.h"
+#include "quote.h"
+#include "builtin.h"
+#include "archive.h"
+
+static int verbose;
+static int zip_date;
+static int zip_time;
+
+static unsigned char *zip_dir;
+static unsigned int zip_dir_size;
+
+static unsigned int zip_offset;
+static unsigned int zip_dir_offset;
+static unsigned int zip_dir_entries;
+
+#define ZIP_DIRECTORY_MIN_SIZE (1024 * 1024)
+
+struct zip_local_header {
+       unsigned char magic[4];
+       unsigned char version[2];
+       unsigned char flags[2];
+       unsigned char compression_method[2];
+       unsigned char mtime[2];
+       unsigned char mdate[2];
+       unsigned char crc32[4];
+       unsigned char compressed_size[4];
+       unsigned char size[4];
+       unsigned char filename_length[2];
+       unsigned char extra_length[2];
+};
+
+struct zip_dir_header {
+       unsigned char magic[4];
+       unsigned char creator_version[2];
+       unsigned char version[2];
+       unsigned char flags[2];
+       unsigned char compression_method[2];
+       unsigned char mtime[2];
+       unsigned char mdate[2];
+       unsigned char crc32[4];
+       unsigned char compressed_size[4];
+       unsigned char size[4];
+       unsigned char filename_length[2];
+       unsigned char extra_length[2];
+       unsigned char comment_length[2];
+       unsigned char disk[2];
+       unsigned char attr1[2];
+       unsigned char attr2[4];
+       unsigned char offset[4];
+};
+
+struct zip_dir_trailer {
+       unsigned char magic[4];
+       unsigned char disk[2];
+       unsigned char directory_start_disk[2];
+       unsigned char entries_on_this_disk[2];
+       unsigned char entries[2];
+       unsigned char size[4];
+       unsigned char offset[4];
+       unsigned char comment_length[2];
+};
+
+static void copy_le16(unsigned char *dest, unsigned int n)
+{
+       dest[0] = 0xff & n;
+       dest[1] = 0xff & (n >> 010);
+}
+
+static void copy_le32(unsigned char *dest, unsigned int n)
+{
+       dest[0] = 0xff & n;
+       dest[1] = 0xff & (n >> 010);
+       dest[2] = 0xff & (n >> 020);
+       dest[3] = 0xff & (n >> 030);
+}
+
+static void *zlib_deflate(void *data, unsigned long size,
+                          unsigned long *compressed_size)
+{
+       z_stream stream;
+       unsigned long maxsize;
+       void *buffer;
+       int result;
+
+       memset(&stream, 0, sizeof(stream));
+       deflateInit(&stream, zlib_compression_level);
+       maxsize = deflateBound(&stream, size);
+       buffer = xmalloc(maxsize);
+
+       stream.next_in = data;
+       stream.avail_in = size;
+       stream.next_out = buffer;
+       stream.avail_out = maxsize;
+
+       do {
+               result = deflate(&stream, Z_FINISH);
+       } while (result == Z_OK);
+
+       if (result != Z_STREAM_END) {
+               free(buffer);
+               return NULL;
+       }
+
+       deflateEnd(&stream);
+       *compressed_size = stream.total_out;
+
+       return buffer;
+}
+
+static char *construct_path(const char *base, int baselen,
+                            const char *filename, int isdir, int *pathlen)
+{
+       int filenamelen = strlen(filename);
+       int len = baselen + filenamelen;
+       char *path, *p;
+
+       if (isdir)
+               len++;
+       p = path = xmalloc(len + 1);
+
+       memcpy(p, base, baselen);
+       p += baselen;
+       memcpy(p, filename, filenamelen);
+       p += filenamelen;
+       if (isdir)
+               *p++ = '/';
+       *p = '\0';
+
+       *pathlen = len;
+
+       return path;
+}
+
+static int write_zip_entry(const unsigned char *sha1,
+                           const char *base, int baselen,
+                           const char *filename, unsigned mode, int stage)
+{
+       struct zip_local_header header;
+       struct zip_dir_header dirent;
+       unsigned long compressed_size;
+       unsigned long uncompressed_size;
+       unsigned long crc;
+       unsigned long direntsize;
+       unsigned long size;
+       int method;
+       int result = -1;
+       int pathlen;
+       unsigned char *out;
+       char *path;
+       char type[20];
+       void *buffer = NULL;
+       void *deflated = NULL;
+
+       crc = crc32(0, Z_NULL, 0);
+
+       path = construct_path(base, baselen, filename, S_ISDIR(mode), &pathlen);
+       if (verbose)
+               fprintf(stderr, "%s\n", path);
+       if (pathlen > 0xffff) {
+               error("path too long (%d chars, SHA1: %s): %s", pathlen,
+                     sha1_to_hex(sha1), path);
+               goto out;
+       }
+
+       if (S_ISDIR(mode)) {
+               method = 0;
+               result = READ_TREE_RECURSIVE;
+               out = NULL;
+               uncompressed_size = 0;
+               compressed_size = 0;
+       } else if (S_ISREG(mode)) {
+               method = zlib_compression_level == 0 ? 0 : 8;
+               result = 0;
+               buffer = read_sha1_file(sha1, type, &size);
+               if (!buffer)
+                       die("cannot read %s", sha1_to_hex(sha1));
+               crc = crc32(crc, buffer, size);
+               out = buffer;
+               uncompressed_size = size;
+               compressed_size = size;
+       } else {
+               error("unsupported file mode: 0%o (SHA1: %s)", mode,
+                     sha1_to_hex(sha1));
+               goto out;
+       }
+
+       if (method == 8) {
+               deflated = zlib_deflate(buffer, size, &compressed_size);
+               if (deflated && compressed_size - 6 < size) {
+                       /* ZLIB --> raw compressed data (see RFC 1950) */
+                       /* CMF and FLG ... */
+                       out = (unsigned char *)deflated + 2;
+                       compressed_size -= 6;   /* ... and ADLER32 */
+               } else {
+                       method = 0;
+                       compressed_size = size;
+               }
+       }
+
+       /* make sure we have enough free space in the dictionary */
+       direntsize = sizeof(struct zip_dir_header) + pathlen;
+       while (zip_dir_size < zip_dir_offset + direntsize) {
+               zip_dir_size += ZIP_DIRECTORY_MIN_SIZE;
+               zip_dir = xrealloc(zip_dir, zip_dir_size);
+       }
+
+       copy_le32(dirent.magic, 0x02014b50);
+       copy_le16(dirent.creator_version, 0);
+       copy_le16(dirent.version, 20);
+       copy_le16(dirent.flags, 0);
+       copy_le16(dirent.compression_method, method);
+       copy_le16(dirent.mtime, zip_time);
+       copy_le16(dirent.mdate, zip_date);
+       copy_le32(dirent.crc32, crc);
+       copy_le32(dirent.compressed_size, compressed_size);
+       copy_le32(dirent.size, uncompressed_size);
+       copy_le16(dirent.filename_length, pathlen);
+       copy_le16(dirent.extra_length, 0);
+       copy_le16(dirent.comment_length, 0);
+       copy_le16(dirent.disk, 0);
+       copy_le16(dirent.attr1, 0);
+       copy_le32(dirent.attr2, 0);
+       copy_le32(dirent.offset, zip_offset);
+       memcpy(zip_dir + zip_dir_offset, &dirent, sizeof(struct zip_dir_header));
+       zip_dir_offset += sizeof(struct zip_dir_header);
+       memcpy(zip_dir + zip_dir_offset, path, pathlen);
+       zip_dir_offset += pathlen;
+       zip_dir_entries++;
+
+       copy_le32(header.magic, 0x04034b50);
+       copy_le16(header.version, 20);
+       copy_le16(header.flags, 0);
+       copy_le16(header.compression_method, method);
+       copy_le16(header.mtime, zip_time);
+       copy_le16(header.mdate, zip_date);
+       copy_le32(header.crc32, crc);
+       copy_le32(header.compressed_size, compressed_size);
+       copy_le32(header.size, uncompressed_size);
+       copy_le16(header.filename_length, pathlen);
+       copy_le16(header.extra_length, 0);
+       write_or_die(1, &header, sizeof(struct zip_local_header));
+       zip_offset += sizeof(struct zip_local_header);
+       write_or_die(1, path, pathlen);
+       zip_offset += pathlen;
+       if (compressed_size > 0) {
+               write_or_die(1, out, compressed_size);
+               zip_offset += compressed_size;
+       }
+
+out:
+       free(buffer);
+       free(deflated);
+       free(path);
+
+       return result;
+}
+
+static void write_zip_trailer(const unsigned char *sha1)
+{
+       struct zip_dir_trailer trailer;
+
+       copy_le32(trailer.magic, 0x06054b50);
+       copy_le16(trailer.disk, 0);
+       copy_le16(trailer.directory_start_disk, 0);
+       copy_le16(trailer.entries_on_this_disk, zip_dir_entries);
+       copy_le16(trailer.entries, zip_dir_entries);
+       copy_le32(trailer.size, zip_dir_offset);
+       copy_le32(trailer.offset, zip_offset);
+       copy_le16(trailer.comment_length, sha1 ? 40 : 0);
+
+       write_or_die(1, zip_dir, zip_dir_offset);
+       write_or_die(1, &trailer, sizeof(struct zip_dir_trailer));
+       if (sha1)
+               write_or_die(1, sha1_to_hex(sha1), 40);
+}
+
+static void dos_time(time_t *time, int *dos_date, int *dos_time)
+{
+       struct tm *t = localtime(time);
+
+       *dos_date = t->tm_mday + (t->tm_mon + 1) * 32 +
+                   (t->tm_year + 1900 - 1980) * 512;
+       *dos_time = t->tm_sec / 2 + t->tm_min * 32 + t->tm_hour * 2048;
+}
+
+int write_zip_archive(struct archiver_args *args)
+{
+       int plen = strlen(args->base);
+
+       dos_time(&args->time, &zip_date, &zip_time);
+
+       zip_dir = xmalloc(ZIP_DIRECTORY_MIN_SIZE);
+       zip_dir_size = ZIP_DIRECTORY_MIN_SIZE;
+       verbose = args->verbose;
+
+       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_zip_entry(args->tree->object.sha1, "", 0, base, 040777, 0);
+               free(base);
+       }
+       read_tree_recursive(args->tree, args->base, plen, 0,
+                           args->pathspec, write_zip_entry);
+       write_zip_trailer(args->commit_sha1);
+
+       free(zip_dir);
+
+       return 0;
+}
+
+void *parse_extra_zip_args(int argc, const char **argv)
+{
+       for (; argc > 0; argc--, argv++) {
+               const char *arg = argv[0];
+
+               if (arg[0] == '-' && isdigit(arg[1]) && arg[2] == '\0')
+                       zlib_compression_level = arg[1] - '0';
+               else
+                       die("Unknown argument for zip format: %s", arg);
+       }
+       return NULL;
+}
index ed87a5550c7dab6de56327b8acc0e3e2c897e39e..671878817386c72f16fe64cc62b6fa8daa62f102 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;
@@ -1055,8 +564,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 +574,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 +621,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 +639,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++) {
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 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;
-}
diff --git a/builtin-zip-tree.c b/builtin-zip-tree.c
deleted file mode 100644 (file)
index 52d4b7a..0000000
+++ /dev/null
@@ -1,398 +0,0 @@
-/*
- * Copyright (c) 2006 Rene Scharfe
- */
-#include <time.h>
-#include "cache.h"
-#include "commit.h"
-#include "blob.h"
-#include "tree.h"
-#include "quote.h"
-#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;
-
-static unsigned char *zip_dir;
-static unsigned int zip_dir_size;
-
-static unsigned int zip_offset;
-static unsigned int zip_dir_offset;
-static unsigned int zip_dir_entries;
-
-#define ZIP_DIRECTORY_MIN_SIZE (1024 * 1024)
-
-struct zip_local_header {
-       unsigned char magic[4];
-       unsigned char version[2];
-       unsigned char flags[2];
-       unsigned char compression_method[2];
-       unsigned char mtime[2];
-       unsigned char mdate[2];
-       unsigned char crc32[4];
-       unsigned char compressed_size[4];
-       unsigned char size[4];
-       unsigned char filename_length[2];
-       unsigned char extra_length[2];
-};
-
-struct zip_dir_header {
-       unsigned char magic[4];
-       unsigned char creator_version[2];
-       unsigned char version[2];
-       unsigned char flags[2];
-       unsigned char compression_method[2];
-       unsigned char mtime[2];
-       unsigned char mdate[2];
-       unsigned char crc32[4];
-       unsigned char compressed_size[4];
-       unsigned char size[4];
-       unsigned char filename_length[2];
-       unsigned char extra_length[2];
-       unsigned char comment_length[2];
-       unsigned char disk[2];
-       unsigned char attr1[2];
-       unsigned char attr2[4];
-       unsigned char offset[4];
-};
-
-struct zip_dir_trailer {
-       unsigned char magic[4];
-       unsigned char disk[2];
-       unsigned char directory_start_disk[2];
-       unsigned char entries_on_this_disk[2];
-       unsigned char entries[2];
-       unsigned char size[4];
-       unsigned char offset[4];
-       unsigned char comment_length[2];
-};
-
-static void copy_le16(unsigned char *dest, unsigned int n)
-{
-       dest[0] = 0xff & n;
-       dest[1] = 0xff & (n >> 010);
-}
-
-static void copy_le32(unsigned char *dest, unsigned int n)
-{
-       dest[0] = 0xff & n;
-       dest[1] = 0xff & (n >> 010);
-       dest[2] = 0xff & (n >> 020);
-       dest[3] = 0xff & (n >> 030);
-}
-
-static void *zlib_deflate(void *data, unsigned long size,
-                          unsigned long *compressed_size)
-{
-       z_stream stream;
-       unsigned long maxsize;
-       void *buffer;
-       int result;
-
-       memset(&stream, 0, sizeof(stream));
-       deflateInit(&stream, zlib_compression_level);
-       maxsize = deflateBound(&stream, size);
-       buffer = xmalloc(maxsize);
-
-       stream.next_in = data;
-       stream.avail_in = size;
-       stream.next_out = buffer;
-       stream.avail_out = maxsize;
-
-       do {
-               result = deflate(&stream, Z_FINISH);
-       } while (result == Z_OK);
-
-       if (result != Z_STREAM_END) {
-               free(buffer);
-               return NULL;
-       }
-
-       deflateEnd(&stream);
-       *compressed_size = stream.total_out;
-
-       return buffer;
-}
-
-static char *construct_path(const char *base, int baselen,
-                            const char *filename, int isdir, int *pathlen)
-{
-       int filenamelen = strlen(filename);
-       int len = baselen + filenamelen;
-       char *path, *p;
-
-       if (isdir)
-               len++;
-       p = path = xmalloc(len + 1);
-
-       memcpy(p, base, baselen);
-       p += baselen;
-       memcpy(p, filename, filenamelen);
-       p += filenamelen;
-       if (isdir)
-               *p++ = '/';
-       *p = '\0';
-
-       *pathlen = len;
-
-       return path;
-}
-
-static int write_zip_entry(const unsigned char *sha1,
-                           const char *base, int baselen,
-                           const char *filename, unsigned mode, int stage)
-{
-       struct zip_local_header header;
-       struct zip_dir_header dirent;
-       unsigned long compressed_size;
-       unsigned long uncompressed_size;
-       unsigned long crc;
-       unsigned long direntsize;
-       unsigned long size;
-       int method;
-       int result = -1;
-       int pathlen;
-       unsigned char *out;
-       char *path;
-       char type[20];
-       void *buffer = NULL;
-       void *deflated = NULL;
-
-       crc = crc32(0, Z_NULL, 0);
-
-       path = construct_path(base, baselen, filename, S_ISDIR(mode), &pathlen);
-       if (verbose)
-               fprintf(stderr, "%s\n", path);
-       if (pathlen > 0xffff) {
-               error("path too long (%d chars, SHA1: %s): %s", pathlen,
-                     sha1_to_hex(sha1), path);
-               goto out;
-       }
-
-       if (S_ISDIR(mode)) {
-               method = 0;
-               result = READ_TREE_RECURSIVE;
-               out = NULL;
-               uncompressed_size = 0;
-               compressed_size = 0;
-       } else if (S_ISREG(mode)) {
-               method = zlib_compression_level == 0 ? 0 : 8;
-               result = 0;
-               buffer = read_sha1_file(sha1, type, &size);
-               if (!buffer)
-                       die("cannot read %s", sha1_to_hex(sha1));
-               crc = crc32(crc, buffer, size);
-               out = buffer;
-               uncompressed_size = size;
-               compressed_size = size;
-       } else {
-               error("unsupported file mode: 0%o (SHA1: %s)", mode,
-                     sha1_to_hex(sha1));
-               goto out;
-       }
-
-       if (method == 8) {
-               deflated = zlib_deflate(buffer, size, &compressed_size);
-               if (deflated && compressed_size - 6 < size) {
-                       /* ZLIB --> raw compressed data (see RFC 1950) */
-                       /* CMF and FLG ... */
-                       out = (unsigned char *)deflated + 2;
-                       compressed_size -= 6;   /* ... and ADLER32 */
-               } else {
-                       method = 0;
-                       compressed_size = size;
-               }
-       }
-
-       /* make sure we have enough free space in the dictionary */
-       direntsize = sizeof(struct zip_dir_header) + pathlen;
-       while (zip_dir_size < zip_dir_offset + direntsize) {
-               zip_dir_size += ZIP_DIRECTORY_MIN_SIZE;
-               zip_dir = xrealloc(zip_dir, zip_dir_size);
-       }
-
-       copy_le32(dirent.magic, 0x02014b50);
-       copy_le16(dirent.creator_version, 0);
-       copy_le16(dirent.version, 20);
-       copy_le16(dirent.flags, 0);
-       copy_le16(dirent.compression_method, method);
-       copy_le16(dirent.mtime, zip_time);
-       copy_le16(dirent.mdate, zip_date);
-       copy_le32(dirent.crc32, crc);
-       copy_le32(dirent.compressed_size, compressed_size);
-       copy_le32(dirent.size, uncompressed_size);
-       copy_le16(dirent.filename_length, pathlen);
-       copy_le16(dirent.extra_length, 0);
-       copy_le16(dirent.comment_length, 0);
-       copy_le16(dirent.disk, 0);
-       copy_le16(dirent.attr1, 0);
-       copy_le32(dirent.attr2, 0);
-       copy_le32(dirent.offset, zip_offset);
-       memcpy(zip_dir + zip_dir_offset, &dirent, sizeof(struct zip_dir_header));
-       zip_dir_offset += sizeof(struct zip_dir_header);
-       memcpy(zip_dir + zip_dir_offset, path, pathlen);
-       zip_dir_offset += pathlen;
-       zip_dir_entries++;
-
-       copy_le32(header.magic, 0x04034b50);
-       copy_le16(header.version, 20);
-       copy_le16(header.flags, 0);
-       copy_le16(header.compression_method, method);
-       copy_le16(header.mtime, zip_time);
-       copy_le16(header.mdate, zip_date);
-       copy_le32(header.crc32, crc);
-       copy_le32(header.compressed_size, compressed_size);
-       copy_le32(header.size, uncompressed_size);
-       copy_le16(header.filename_length, pathlen);
-       copy_le16(header.extra_length, 0);
-       write_or_die(1, &header, sizeof(struct zip_local_header));
-       zip_offset += sizeof(struct zip_local_header);
-       write_or_die(1, path, pathlen);
-       zip_offset += pathlen;
-       if (compressed_size > 0) {
-               write_or_die(1, out, compressed_size);
-               zip_offset += compressed_size;
-       }
-
-out:
-       free(buffer);
-       free(deflated);
-       free(path);
-
-       return result;
-}
-
-static void write_zip_trailer(const unsigned char *sha1)
-{
-       struct zip_dir_trailer trailer;
-
-       copy_le32(trailer.magic, 0x06054b50);
-       copy_le16(trailer.disk, 0);
-       copy_le16(trailer.directory_start_disk, 0);
-       copy_le16(trailer.entries_on_this_disk, zip_dir_entries);
-       copy_le16(trailer.entries, zip_dir_entries);
-       copy_le32(trailer.size, zip_dir_offset);
-       copy_le32(trailer.offset, zip_offset);
-       copy_le16(trailer.comment_length, sha1 ? 40 : 0);
-
-       write_or_die(1, zip_dir, zip_dir_offset);
-       write_or_die(1, &trailer, sizeof(struct zip_dir_trailer));
-       if (sha1)
-               write_or_die(1, sha1_to_hex(sha1), 40);
-}
-
-static void dos_time(time_t *time, int *dos_date, int *dos_time)
-{
-       struct tm *t = localtime(time);
-
-       *dos_date = t->tm_mday + (t->tm_mon + 1) * 32 +
-                   (t->tm_year + 1900 - 1980) * 512;
-       *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);
-
-       dos_time(&args->time, &zip_date, &zip_time);
-
-       zip_dir = xmalloc(ZIP_DIRECTORY_MIN_SIZE);
-       zip_dir_size = ZIP_DIRECTORY_MIN_SIZE;
-       verbose = args->verbose;
-
-       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_zip_entry(args->tree->object.sha1, "", 0, base, 040777, 0);
-               free(base);
-       }
-       read_tree_recursive(args->tree, args->base, plen, 0,
-                           args->pathspec, write_zip_entry);
-       write_zip_trailer(args->commit_sha1);
-
-       free(zip_dir);
-
-       return 0;
-}
-
-void *parse_extra_zip_args(int argc, const char **argv)
-{
-       for (; argc > 0; argc--, argv++) {
-               const char *arg = argv[0];
-
-               if (arg[0] == '-' && isdigit(arg[1]) && arg[2] == '\0')
-                       zlib_compression_level = arg[1] - '0';
-               else
-                       die("Unknown argument for zip format: %s", arg);
-       }
-       return NULL;
-}
index ccade94e26b34f9005b1ed2575b4c11cb6ceab22..f9fa9ff1d245e81630438d2b321cfe04c7905905 100644 (file)
--- a/builtin.h
+++ b/builtin.h
@@ -53,7 +53,6 @@ extern int cmd_show(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 ef2e58147535c455c5be2b014af9945fb41c35d0..97debd03c51c03c6df9a96e3f7de99bf4b4313e1 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -390,10 +390,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 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;
                }
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 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 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
 
diff --git a/git.c b/git.c
index 44ab0de94d84e3b09d753b1da85679efcfd793a0..ae80e78456007e8ccb025949af82279f84cdb8fa 100644 (file)
--- a/git.c
+++ b/git.c
@@ -259,12 +259,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 3d06181229a089ad63d7a46744680a3b161a72f3..0693a833c173ab9218ea791b7678d399129a83cf 100755 (executable)
@@ -212,19 +212,9 @@ sub feature_pickaxe {
        }
 }
 
+# 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) {
@@ -305,7 +295,7 @@ sub evaluate_path_info {
                        $action  ||= "blob_plain";
                }
                $hash_base ||= validate_input($refname);
-               $file_name ||= validate_input($pathname);
+               $file_name ||= $pathname;
        } elsif (defined $refname) {
                # we got "project.git/branch"
                $action ||= "shortlog";
@@ -416,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;
@@ -1282,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 .= "/";
                                }
@@ -2430,7 +2420,7 @@ 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 =
@@ -3072,12 +3062,12 @@ sub git_blobdiff {
                if (defined $file_name) {
                        if (defined $file_parent) {
                                $diffinfo{'status'} = '2';
-                               $diffinfo{'from_file'} = $file_parent;
-                               $diffinfo{'to_file'}   = $file_name;
+                               $diffinfo{'from_file'} = esc_html($file_parent);
+                               $diffinfo{'to_file'}   = esc_html($file_name);
                        } else { # assume not renamed
                                $diffinfo{'status'} = '1';
-                               $diffinfo{'from_file'} = $file_name;
-                               $diffinfo{'to_file'}   = $file_name;
+                               $diffinfo{'from_file'} = esc_html($file_name);
+                               $diffinfo{'to_file'}   = esc_html($file_name);
                        }
                } else { # no filename given
                        $diffinfo{'status'} = '2';
@@ -3126,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";
 
@@ -3576,7 +3566,7 @@ sub git_rss {
                        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..cc8d684
--- /dev/null
+++ b/grep.c
@@ -0,0 +1,459 @@
+#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;
+
+       if (opt->fixed)
+               return;
+
+       /* First compile regexps */
+       for (p = opt->pattern_list; p; p = p->next) {
+               switch (p->token) {
+               case GREP_PATTERN: /* atom */
+               case GREP_PATTERN_HEAD:
+               case GREP_PATTERN_BODY:
+                       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 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++;
+       }
+
+       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..0b503ea
--- /dev/null
+++ b/grep.h
@@ -0,0 +1,78 @@
+#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 int grep_buffer(struct grep_opt *opt, const char *name, char *buf, unsigned long size);
+
+#endif
diff --git a/interpolate.c b/interpolate.c
new file mode 100644 (file)
index 0000000..d82f1b5
--- /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,
+               char *orig,
+               struct interp *interps, int ninterps)
+{
+       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..00c63a5
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * Copyright 2006 Jon Loeliger
+ */
+
+#ifndef INTERPOLATE_H
+#define INTERPOLATE_H
+
+
+struct interp {
+       char *name;
+       char *value;
+};
+
+extern int interpolate(char *result, int reslen,
+                      char *orig,
+                      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 6a2539b623512917479be3e6601ee8b8bef4953d..93f25130a05ccca3e3e6c65b750f256246ae16be 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;
index 0f9c2b62187ae35b07d0d678ac233e2c7125c428..27b1ebb720b1530d673f3dfb1c27862b4cf90dfd 100644 (file)
@@ -884,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) {
@@ -922,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);
 
@@ -985,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:
@@ -1066,7 +1050,6 @@ static int packed_object_info(struct pack_entry *entry,
        }
        if (sizep)
                *sizep = size;
-       unuse_packed_git(p);
        return 0;
 }
 
@@ -1103,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);
@@ -1141,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);
@@ -1149,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);
@@ -1188,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]);
@@ -1199,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
@@ -1216,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) {
@@ -1227,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;
 }
@@ -1237,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;
@@ -1259,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",
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