Merge branch 'maint'
authorJunio C Hamano <junkio@cox.net>
Tue, 22 May 2007 03:03:53 +0000 (20:03 -0700)
committerJunio C Hamano <junkio@cox.net>
Tue, 22 May 2007 03:03:53 +0000 (20:03 -0700)
* maint:
  git-cvsserver: fix disabling service via per-method config
  git-status: respect core.excludesFile
  SubmittingPatches: mention older C compiler compatibility
  git-daemon: don't ignore pid-file write failure

32 files changed:
Documentation/config.txt
Documentation/git-gc.txt
Documentation/git-pack-objects.txt
Documentation/git-rev-list.txt
Documentation/git-update-ref.txt
builtin-archive.c
builtin-branch.c
builtin-fetch--tool.c
builtin-gc.c
builtin-pack-objects.c
builtin-reflog.c
builtin-update-ref.c
cache.h
config.c
connect.c
csum-file.c
csum-file.h
environment.c
fast-import.c
fetch-pack.c
git-checkout.sh
git-merge.sh
git-rebase.sh
git-repack.sh
peek-remote.c
receive-pack.c
refs.c
refs.h
revision.c
send-pack.c
sha1_file.c
t/t9500-gitweb-standalone-no-errors.sh [new file with mode: 0755]

index 4bbe116..179cb17 100644 (file)
@@ -204,23 +204,16 @@ core.warnAmbiguousRefs::
        and might match multiple refs in the .git/refs/ tree. True by default.
 
 core.compression::
+       An integer -1..9, indicating a default compression level.
+       -1 is the zlib default. 0 means no compression,
+       and 1..9 are various speed/size tradeoffs, 9 being slowest.
+
+core.loosecompression::
        An integer -1..9, indicating the compression level for objects that
-       are not in a pack file. -1 is the zlib and git default. 0 means no
+       are not in a pack file. -1 is the zlib default. 0 means no
        compression, and 1..9 are various speed/size tradeoffs, 9 being
-       slowest.
-
-core.legacyheaders::
-       A boolean which
-       changes the format of loose objects so that they are more
-       efficient to pack and to send out of the repository over git
-       native protocol, since v1.4.2.  However, loose objects
-       written in the new format cannot be read by git older than
-       that version; people fetching from your repository using
-       older versions of git over dumb transports (e.g. http)
-       will also be affected.
-+
-To let git use the new loose object format, you have to
-set core.legacyheaders to false.
+       slowest.  If not set,  defaults to core.compression.  If that is
+       not set,  defaults to 0 (best speed).
 
 core.packedGitWindowSize::
        Number of bytes of a pack file to map into memory in a
@@ -389,6 +382,11 @@ format.suffix::
        `.patch`. Use this variable to change that suffix (make sure to
        include the dot if you want it).
 
+gc.aggressiveWindow::
+       The window size parameter used in the delta compression
+       algorithm used by 'git gc --aggressive'.  This defaults
+       to 10.
+
 gc.packrefs::
        `git gc` does not run `git pack-refs` in a bare repository by
        default so that older dumb-transport clients can still fetch
@@ -555,6 +553,13 @@ pack.depth::
        The maximum delta depth used by gitlink:git-pack-objects[1] when no
        maximum depth is given on the command line. Defaults to 50.
 
+pack.compression::
+       An integer -1..9, indicating the compression level for objects
+       in a pack file. -1 is the zlib default. 0 means no
+       compression, and 1..9 are various speed/size tradeoffs, 9 being
+       slowest.  If not set,  defaults to core.compression.  If that is
+       not set,  defaults to -1.
+
 pull.octopus::
        The default merge strategy to use when pulling multiple branches
        at once.
index bc16584..4ac839f 100644 (file)
@@ -8,7 +8,7 @@ git-gc - Cleanup unnecessary files and optimize the local repository
 
 SYNOPSIS
 --------
-'git-gc' [--prune]
+'git-gc' [--prune] [--aggressive]
 
 DESCRIPTION
 -----------
@@ -35,6 +35,13 @@ OPTIONS
        repository at the same time (e.g. never use this option
        in a cron script).
 
+--aggressive::
+       Usually 'git-gc' runs very quickly while providing good disk
+       space utilization and performance.   This option will cause
+       git-gc to more aggressive optimize the repository at the expense
+       of taking much more time.  The effects of this optimization are
+       persistent, so this option only needs to be sporadically; every
+       few hundred changesets or so.
 
 Configuration
 -------------
@@ -67,6 +74,13 @@ The optional configuration variable 'gc.packrefs' determines if
 is not run in bare repositories by default, to allow older dumb-transport
 clients fetch from the repository,  but this will change in the future.
 
+The optional configuration variable 'gc.aggressiveWindow' controls how
+much time is spent optimizing the delta compression of the objects in
+the repository when the --aggressive option is specified.  The larger
+the value, the more time is spent optimizing the delta compression.  See
+the documentation for the --window' option in gitlink:git-repack[1] for
+more details.  This defaults to 10.
+
 See Also
 --------
 gitlink:git-prune[1]
index bd3ee45..2531238 100644 (file)
@@ -127,6 +127,25 @@ base-name::
        This flag tells the command not to reuse existing deltas
        but compute them from scratch.
 
+--no-reuse-object::
+       This flag tells the command not to reuse existing object data at all,
+       including non deltified object, forcing recompression of everything.
+       This implies --no-reuse-delta. Useful only in the obscure case where
+       wholesale enforcement of a different compression level on the
+       packed data is desired.
+
+--compression=[N]::
+       Specifies compression level for newly-compressed data in the
+       generated pack.  If not specified,  pack compression level is
+       determined first by pack.compression,  then by core.compression,
+       and defaults to -1,  the zlib default,  if neither is set.
+       Data copied from loose objects will be recompressed
+       if core.legacyheaders was true when they were created or if
+       the loose compression level (see core.loosecompression and
+       core.compression) is now a different value than the pack
+       compression level.  Add --no-reuse-object if you want to force
+       a uniform compression level on all data no matter the source.
+
 --delta-base-offset::
        A packed archive can express base object of a delta as
        either 20-byte object name or as an offset in the
index c3c2043..0dba73f 100644 (file)
@@ -25,6 +25,7 @@ SYNOPSIS
             [ \--cherry-pick ]
             [ \--encoding[=<encoding>] ]
             [ \--(author|committer|grep)=<pattern> ]
+            [ \--regexp-ignore-case ] [ \--extended-regexp ]
             [ \--date={local|relative|default} ]
             [ [\--objects | \--objects-edge] [ \--unpacked ] ]
             [ \--pretty | \--header ]
@@ -214,6 +215,15 @@ limiting may be applied.
        Limit the commits output to ones with log message that
        matches the specified pattern (regular expression).
 
+--regexp-ignore-case::
+
+       Match the regexp limiting patterns without regard to letters case.
+
+--extended-regexp::
+
+       Consider the limiting patterns to be extended regular expressions
+       instead of the default basic regular expressions.
+
 --remove-empty::
 
        Stop when a given path disappears from the tree.
index 9424fea..f222616 100644 (file)
@@ -7,7 +7,7 @@ git-update-ref - Update the object name stored in a ref safely
 
 SYNOPSIS
 --------
-'git-update-ref' [-m <reason>] (-d <ref> <oldvalue> | <ref> <newvalue> [<oldvalue>])
+'git-update-ref' [-m <reason>] (-d <ref> <oldvalue> | [--no-deref] <ref> <newvalue> [<oldvalue>])
 
 DESCRIPTION
 -----------
@@ -36,6 +36,9 @@ them and update them as a regular file (i.e. it will allow the
 filesystem to follow them, but will overwrite such a symlink to
 somewhere else with a regular filename).
 
+If --no-deref is given, <ref> itself is overwritten, rather than
+the result of following the symbolic pointers.
+
 In general, using
 
        git-update-ref HEAD "$head"
index 7f4e409..187491b 100644 (file)
@@ -45,7 +45,7 @@ static int run_remote_archiver(const char *remote, int argc,
        }
 
        url = xstrdup(remote);
-       pid = git_connect(fd, url, exec);
+       pid = git_connect(fd, url, exec, 0);
        if (pid < 0)
                return pid;
 
index 8956d0f..a5b6bbe 100644 (file)
@@ -462,7 +462,7 @@ static void create_branch(const char *name, const char *start_name,
                die("Not a valid branch point: '%s'.", start_name);
        hashcpy(sha1, commit->object.sha1);
 
-       lock = lock_any_ref_for_update(ref, NULL);
+       lock = lock_any_ref_for_update(ref, NULL, 0);
        if (!lock)
                die("Failed to lock ref for update: %s.", strerror(errno));
 
index 12adb38..ed4d5de 100644 (file)
@@ -42,7 +42,7 @@ static int update_ref(const char *action,
        if (!rla)
                rla = "(reflog update)";
        snprintf(msg, sizeof(msg), "%s: %s", rla, action);
-       lock = lock_any_ref_for_update(refname, oldval);
+       lock = lock_any_ref_for_update(refname, oldval, 0);
        if (!lock)
                return 1;
        if (write_ref_sha1(lock, sha1, msg) < 0)
index 3b1f8c2..8ea165a 100644 (file)
 
 #define FAILED_RUN "failed to run %s"
 
-static const char builtin_gc_usage[] = "git-gc [--prune]";
+static const char builtin_gc_usage[] = "git-gc [--prune] [--aggressive]";
 
 static int pack_refs = -1;
+static int aggressive_window = -1;
 
+#define MAX_ADD 10
 static const char *argv_pack_refs[] = {"pack-refs", "--prune", NULL};
 static const char *argv_reflog[] = {"reflog", "expire", "--all", NULL};
-static const char *argv_repack[] = {"repack", "-a", "-d", "-l", NULL};
+static const char *argv_repack[MAX_ADD] = {"repack", "-a", "-d", "-l", NULL};
 static const char *argv_prune[] = {"prune", NULL};
 static const char *argv_rerere[] = {"rerere", "gc", NULL};
 
@@ -34,13 +36,31 @@ static int gc_config(const char *var, const char *value)
                        pack_refs = git_config_bool(var, value);
                return 0;
        }
+       if (!strcmp(var, "gc.aggressivewindow")) {
+               aggressive_window = git_config_int(var, value);
+               return 0;
+       }
        return git_default_config(var, value);
 }
 
+static void append_option(const char **cmd, const char *opt, int max_length)
+{
+       int i;
+
+       for (i = 0; cmd[i]; i++)
+               ;
+
+       if (i + 2 >= max_length)
+               die("Too many options specified");
+       cmd[i++] = opt;
+       cmd[i] = NULL;
+}
+
 int cmd_gc(int argc, const char **argv, const char *prefix)
 {
        int i;
        int prune = 0;
+       char buf[80];
 
        git_config(gc_config);
 
@@ -53,6 +73,14 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
                        prune = 1;
                        continue;
                }
+               if (!strcmp(arg, "--aggressive")) {
+                       append_option(argv_repack, "-f", MAX_ADD);
+                       if (aggressive_window > 0) {
+                               sprintf(buf, "--window=%d", aggressive_window);
+                               append_option(argv_repack, buf, MAX_ADD);
+                       }
+                       continue;
+               }
                /* perhaps other parameters later... */
                break;
        }
index 966f843..d165f10 100644 (file)
@@ -17,9 +17,9 @@
 static const char pack_usage[] = "\
 git-pack-objects [{ -q | --progress | --all-progress }] \n\
        [--local] [--incremental] [--window=N] [--depth=N] \n\
-       [--no-reuse-delta] [--delta-base-offset] [--non-empty] \n\
-       [--revs [--unpacked | --all]*] [--reflog] [--stdout | base-name] \n\
-       [<ref-list | <object-list]";
+       [--no-reuse-delta] [--no-reuse-object] [--delta-base-offset] \n\
+       [--non-empty] [--revs [--unpacked | --all]*] [--reflog] \n\
+       [--stdout | base-name] [<ref-list | <object-list]";
 
 struct object_entry {
        unsigned char sha1[20];
@@ -55,7 +55,7 @@ static struct object_entry *objects;
 static uint32_t nr_objects, nr_alloc, nr_result;
 
 static int non_empty;
-static int no_reuse_delta;
+static int no_reuse_delta, no_reuse_object;
 static int local;
 static int incremental;
 static int allow_ofs_delta;
@@ -68,6 +68,8 @@ static int depth = 50;
 static int pack_to_stdout;
 static int num_preferred_base;
 static struct progress progress_state;
+static int pack_compression_level = Z_DEFAULT_COMPRESSION;
+static int pack_compression_seen;
 
 /*
  * The object names in objects array are hashed with this hashtable,
@@ -346,56 +348,6 @@ static void copy_pack_data(struct sha1file *f,
        }
 }
 
-static int check_loose_inflate(unsigned char *data, unsigned long len, unsigned long expect)
-{
-       z_stream stream;
-       unsigned char fakebuf[4096];
-       int st;
-
-       memset(&stream, 0, sizeof(stream));
-       stream.next_in = data;
-       stream.avail_in = len;
-       stream.next_out = fakebuf;
-       stream.avail_out = sizeof(fakebuf);
-       inflateInit(&stream);
-
-       while (1) {
-               st = inflate(&stream, Z_FINISH);
-               if (st == Z_STREAM_END || st == Z_OK) {
-                       st = (stream.total_out == expect &&
-                             stream.total_in == len) ? 0 : -1;
-                       break;
-               }
-               if (st != Z_BUF_ERROR) {
-                       st = -1;
-                       break;
-               }
-               stream.next_out = fakebuf;
-               stream.avail_out = sizeof(fakebuf);
-       }
-       inflateEnd(&stream);
-       return st;
-}
-
-static int revalidate_loose_object(struct object_entry *entry,
-                                  unsigned char *map,
-                                  unsigned long mapsize)
-{
-       /* we already know this is a loose object with new type header. */
-       enum object_type type;
-       unsigned long size, used;
-
-       if (pack_to_stdout)
-               return 0;
-
-       used = unpack_object_header_gently(map, mapsize, &type, &size);
-       if (!used)
-               return -1;
-       map += used;
-       mapsize -= used;
-       return check_loose_inflate(map, mapsize, size);
-}
-
 static unsigned long write_object(struct sha1file *f,
                                  struct object_entry *entry)
 {
@@ -412,7 +364,9 @@ static unsigned long write_object(struct sha1file *f,
                crc32_begin(f);
 
        obj_type = entry->type;
-       if (! entry->in_pack)
+       if (no_reuse_object)
+               to_reuse = 0;   /* explicit */
+       else if (!entry->in_pack)
                to_reuse = 0;   /* can't reuse what we don't have */
        else if (obj_type == OBJ_REF_DELTA || obj_type == OBJ_OFS_DELTA)
                to_reuse = 1;   /* check_object() decided it for us */
@@ -425,25 +379,6 @@ static unsigned long write_object(struct sha1file *f,
                                 * and we do not need to deltify it.
                                 */
 
-       if (!entry->in_pack && !entry->delta) {
-               unsigned char *map;
-               unsigned long mapsize;
-               map = map_sha1_file(entry->sha1, &mapsize);
-               if (map && !legacy_loose_object(map)) {
-                       /* We can copy straight into the pack file */
-                       if (revalidate_loose_object(entry, map, mapsize))
-                               die("corrupt loose object %s",
-                                   sha1_to_hex(entry->sha1));
-                       sha1write(f, map, mapsize);
-                       munmap(map, mapsize);
-                       written++;
-                       reused++;
-                       return mapsize;
-               }
-               if (map)
-                       munmap(map, mapsize);
-       }
-
        if (!to_reuse) {
                buf = read_sha1_file(entry->sha1, &type, &size);
                if (!buf)
@@ -485,7 +420,7 @@ static unsigned long write_object(struct sha1file *f,
                        sha1write(f, entry->delta->sha1, 20);
                        hdrlen += 20;
                }
-               datalen = sha1write_compressed(f, buf, size);
+               datalen = sha1write_compressed(f, buf, size, pack_compression_level);
                free(buf);
        }
        else {
@@ -1125,8 +1060,8 @@ static void check_object(struct object_entry *entry)
                buf = use_pack(p, &w_curs, entry->in_pack_offset, &avail);
 
                /*
-                * We want in_pack_type even if we do not reuse delta.
-                * There is no point not reusing non-delta representations.
+                * We want in_pack_type even if we do not reuse delta
+                * since non-delta representations could still be reused.
                 */
                used = unpack_object_header_gently(buf, avail,
                                                   &entry->in_pack_type,
@@ -1494,6 +1429,16 @@ static int git_pack_config(const char *k, const char *v)
                depth = git_config_int(k, v);
                return 0;
        }
+       if (!strcmp(k, "pack.compression")) {
+               int level = git_config_int(k, v);
+               if (level == -1)
+                       level = Z_DEFAULT_COMPRESSION;
+               else if (level < 0 || level > Z_BEST_COMPRESSION)
+                       die("bad pack compression level %d", level);
+               pack_compression_level = level;
+               pack_compression_seen = 1;
+               return 0;
+       }
        return git_default_config(k, v);
 }
 
@@ -1605,6 +1550,8 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
        rp_ac = 2;
 
        git_config(git_pack_config);
+       if (!pack_compression_seen && core_compression_seen)
+               pack_compression_level = core_compression_level;
 
        progress = isatty(2);
        for (i = 1; i < argc; i++) {
@@ -1625,6 +1572,18 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
                        incremental = 1;
                        continue;
                }
+               if (!prefixcmp(arg, "--compression=")) {
+                       char *end;
+                       int level = strtoul(arg+14, &end, 0);
+                       if (!arg[14] || *end)
+                               usage(pack_usage);
+                       if (level == -1)
+                               level = Z_DEFAULT_COMPRESSION;
+                       else if (level < 0 || level > Z_BEST_COMPRESSION)
+                               die("bad pack compression level %d", level);
+                       pack_compression_level = level;
+                       continue;
+               }
                if (!prefixcmp(arg, "--window=")) {
                        char *end;
                        window = strtoul(arg+9, &end, 0);
@@ -1655,6 +1614,10 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
                        no_reuse_delta = 1;
                        continue;
                }
+               if (!strcmp("--no-reuse-object", arg)) {
+                       no_reuse_object = no_reuse_delta = 1;
+                       continue;
+               }
                if (!strcmp("--delta-base-offset", arg)) {
                        allow_ofs_delta = 1;
                        continue;
index 4c39f1d..ce093ca 100644 (file)
@@ -249,7 +249,7 @@ static int expire_reflog(const char *ref, const unsigned char *sha1, int unused,
        /* we take the lock for the ref itself to prevent it from
         * getting updated.
         */
-       lock = lock_any_ref_for_update(ref, sha1);
+       lock = lock_any_ref_for_update(ref, sha1, 0);
        if (!lock)
                return error("cannot lock ref '%s'", ref);
        log_file = xstrdup(git_path("logs/%s", ref));
index 5ee960b..feac2ed 100644 (file)
@@ -3,16 +3,17 @@
 #include "builtin.h"
 
 static const char git_update_ref_usage[] =
-"git-update-ref [-m <reason>] (-d <refname> <value> | <refname> <value> [<oldval>])";
+"git-update-ref [-m <reason>] (-d <refname> <value> | [--no-deref] <refname> <value> [<oldval>])";
 
 int cmd_update_ref(int argc, const char **argv, const char *prefix)
 {
        const char *refname=NULL, *value=NULL, *oldval=NULL, *msg=NULL;
        struct ref_lock *lock;
        unsigned char sha1[20], oldsha1[20];
-       int i, delete;
+       int i, delete, ref_flags;
 
        delete = 0;
+       ref_flags = 0;
        git_config(git_default_config);
 
        for (i = 1; i < argc; i++) {
@@ -30,6 +31,10 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix)
                        delete = 1;
                        continue;
                }
+               if (!strcmp("--no-deref", argv[i])) {
+                       ref_flags |= REF_NODEREF;
+                       continue;
+               }
                if (!refname) {
                        refname = argv[i];
                        continue;
@@ -59,7 +64,7 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix)
        if (oldval && *oldval && get_sha1(oldval, oldsha1))
                die("%s: not a valid old SHA1", oldval);
 
-       lock = lock_any_ref_for_update(refname, oldval ? oldsha1 : NULL);
+       lock = lock_any_ref_for_update(refname, oldval ? oldsha1 : NULL, ref_flags);
        if (!lock)
                die("%s: cannot lock the ref", refname);
        if (write_ref_sha1(lock, sha1, msg) < 0)
diff --git a/cache.h b/cache.h
index 4204bc1..65b4685 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -273,7 +273,6 @@ extern void rollback_lock_file(struct lock_file *);
 extern int delete_ref(const char *, const unsigned char *sha1);
 
 /* Environment bits from configuration mechanism */
-extern int use_legacy_headers;
 extern int trust_executable_bit;
 extern int has_symlinks;
 extern int assume_unchanged;
@@ -283,6 +282,8 @@ extern int warn_ambiguous_refs;
 extern int shared_repository;
 extern const char *apply_default_whitespace;
 extern int zlib_compression_level;
+extern int core_compression_level;
+extern int core_compression_seen;
 extern size_t packed_git_window_size;
 extern size_t packed_git_limit;
 extern size_t delta_base_cache_limit;
@@ -354,7 +355,6 @@ extern int move_temp_to_file(const char *tmpfile, const char *filename);
 extern int has_sha1_pack(const unsigned char *sha1, const char **ignore);
 extern int has_sha1_file(const unsigned char *sha1);
 extern void *map_sha1_file(const unsigned char *sha1, unsigned long *);
-extern int legacy_loose_object(unsigned char *);
 
 extern int has_pack_file(const unsigned char *sha1);
 extern int has_pack_index(const unsigned char *sha1);
@@ -463,7 +463,8 @@ struct ref {
 #define REF_HEADS      (1u << 1)
 #define REF_TAGS       (1u << 2)
 
-extern pid_t git_connect(int fd[2], char *url, const char *prog);
+#define CONNECT_VERBOSE       (1u << 0)
+extern pid_t git_connect(int fd[2], char *url, const char *prog, int flags);
 extern int finish_connect(pid_t pid);
 extern int path_match(const char *path, int nr, char **match);
 extern int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail,
index 7b655fd..0614c2b 100644 (file)
--- a/config.c
+++ b/config.c
@@ -12,6 +12,8 @@
 static FILE *config_file;
 static const char *config_file_name;
 static int config_linenr;
+static int zlib_compression_seen;
+
 static int get_next_char(void)
 {
        int c;
@@ -299,8 +301,14 @@ int git_default_config(const char *var, const char *value)
                return 0;
        }
 
-       if (!strcmp(var, "core.legacyheaders")) {
-               use_legacy_headers = git_config_bool(var, value);
+       if (!strcmp(var, "core.loosecompression")) {
+               int level = git_config_int(var, value);
+               if (level == -1)
+                       level = Z_DEFAULT_COMPRESSION;
+               else if (level < 0 || level > Z_BEST_COMPRESSION)
+                       die("bad zlib compression level %d", level);
+               zlib_compression_level = level;
+               zlib_compression_seen = 1;
                return 0;
        }
 
@@ -310,7 +318,10 @@ int git_default_config(const char *var, const char *value)
                        level = Z_DEFAULT_COMPRESSION;
                else if (level < 0 || level > Z_BEST_COMPRESSION)
                        die("bad zlib compression level %d", level);
-               zlib_compression_level = level;
+               core_compression_level = level;
+               core_compression_seen = 1;
+               if (!zlib_compression_seen)
+                       zlib_compression_level = level;
                return 0;
        }
 
index da89c9c..2a26fdb 100644 (file)
--- a/connect.c
+++ b/connect.c
@@ -394,7 +394,7 @@ static enum protocol get_protocol(const char *name)
 /*
  * Returns a connected socket() fd, or else die()s.
  */
-static int git_tcp_connect_sock(char *host)
+static int git_tcp_connect_sock(char *host, int flags)
 {
        int sockfd = -1, saved_errno = 0;
        char *colon, *end;
@@ -425,10 +425,16 @@ static int git_tcp_connect_sock(char *host)
        hints.ai_socktype = SOCK_STREAM;
        hints.ai_protocol = IPPROTO_TCP;
 
+       if (flags & CONNECT_VERBOSE)
+               fprintf(stderr, "Looking up %s ... ", host);
+
        gai = getaddrinfo(host, port, &hints, &ai);
        if (gai)
                die("Unable to look up %s (port %s) (%s)", host, port, gai_strerror(gai));
 
+       if (flags & CONNECT_VERBOSE)
+               fprintf(stderr, "done.\nConnecting to %s (port %s) ... ", host, port);
+
        for (ai0 = ai; ai; ai = ai->ai_next) {
                sockfd = socket(ai->ai_family,
                                ai->ai_socktype, ai->ai_protocol);
@@ -450,6 +456,9 @@ static int git_tcp_connect_sock(char *host)
        if (sockfd < 0)
                die("unable to connect a socket (%s)", strerror(saved_errno));
 
+       if (flags & CONNECT_VERBOSE)
+               fprintf(stderr, "done.\n");
+
        return sockfd;
 }
 
@@ -458,7 +467,7 @@ static int git_tcp_connect_sock(char *host)
 /*
  * Returns a connected socket() fd, or else die()s.
  */
-static int git_tcp_connect_sock(char *host)
+static int git_tcp_connect_sock(char *host, int flags)
 {
        int sockfd = -1, saved_errno = 0;
        char *colon, *end;
@@ -485,6 +494,9 @@ static int git_tcp_connect_sock(char *host)
                port = colon + 1;
        }
 
+       if (flags & CONNECT_VERBOSE)
+               fprintf(stderr, "Looking up %s ... ", host);
+
        he = gethostbyname(host);
        if (!he)
                die("Unable to look up %s (%s)", host, hstrerror(h_errno));
@@ -497,6 +509,9 @@ static int git_tcp_connect_sock(char *host)
                nport = se->s_port;
        }
 
+       if (flags & CONNECT_VERBOSE)
+               fprintf(stderr, "done.\nConnecting to %s (port %s) ... ", host, port);
+
        for (ap = he->h_addr_list; *ap; ap++) {
                sockfd = socket(he->h_addrtype, SOCK_STREAM, 0);
                if (sockfd < 0) {
@@ -521,15 +536,18 @@ static int git_tcp_connect_sock(char *host)
        if (sockfd < 0)
                die("unable to connect a socket (%s)", strerror(saved_errno));
 
+       if (flags & CONNECT_VERBOSE)
+               fprintf(stderr, "done.\n");
+
        return sockfd;
 }
 
 #endif /* NO_IPV6 */
 
 
-static void git_tcp_connect(int fd[2], char *host)
+static void git_tcp_connect(int fd[2], char *host, int flags)
 {
-       int sockfd = git_tcp_connect_sock(host);
+       int sockfd = git_tcp_connect_sock(host, flags);
 
        fd[0] = sockfd;
        fd[1] = dup(sockfd);
@@ -646,7 +664,7 @@ static void git_proxy_connect(int fd[2], char *host)
  *
  * Does not return a negative value on error; it just dies.
  */
-pid_t git_connect(int fd[2], char *url, const char *prog)
+pid_t git_connect(int fd[2], char *url, const char *prog, int flags)
 {
        char *host, *path = url;
        char *end;
@@ -719,7 +737,7 @@ pid_t git_connect(int fd[2], char *url, const char *prog)
                if (git_use_proxy(host))
                        git_proxy_connect(fd, host);
                else
-                       git_tcp_connect(fd, host);
+                       git_tcp_connect(fd, host, flags);
                /*
                 * Separate original protocol components prog and path
                 * from extended components with a NUL byte.
index 7c806ad..7088f6e 100644 (file)
@@ -119,14 +119,14 @@ struct sha1file *sha1fd(int fd, const char *name)
        return f;
 }
 
-int sha1write_compressed(struct sha1file *f, void *in, unsigned int size)
+int sha1write_compressed(struct sha1file *f, void *in, unsigned int size, int level)
 {
        z_stream stream;
        unsigned long maxsize;
        void *out;
 
        memset(&stream, 0, sizeof(stream));
-       deflateInit(&stream, zlib_compression_level);
+       deflateInit(&stream, level);
        maxsize = deflateBound(&stream, size);
        out = xmalloc(maxsize);
 
index 7e13391..4e8b83e 100644 (file)
@@ -16,7 +16,7 @@ extern struct sha1file *sha1fd(int fd, const char *name);
 extern struct sha1file *sha1create(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
 extern int sha1close(struct sha1file *, unsigned char *, int);
 extern int sha1write(struct sha1file *, void *, unsigned int);
-extern int sha1write_compressed(struct sha1file *, void *, unsigned int);
+extern int sha1write_compressed(struct sha1file *, void *, unsigned int, int);
 extern void crc32_begin(struct sha1file *);
 extern uint32_t crc32_end(struct sha1file *);
 
index 2231659..9d3e5eb 100644 (file)
@@ -11,7 +11,6 @@
 
 char git_default_email[MAX_GITNAME];
 char git_default_name[MAX_GITNAME];
-int use_legacy_headers = 1;
 int trust_executable_bit = 1;
 int has_symlinks = 1;
 int assume_unchanged;
@@ -24,7 +23,9 @@ const char *git_commit_encoding;
 const char *git_log_output_encoding;
 int shared_repository = PERM_UMASK;
 const char *apply_default_whitespace;
-int zlib_compression_level = Z_DEFAULT_COMPRESSION;
+int zlib_compression_level = Z_BEST_SPEED;
+int core_compression_level;
+int core_compression_seen;
 size_t packed_git_window_size = DEFAULT_PACKED_GIT_WINDOW_SIZE;
 size_t packed_git_limit = DEFAULT_PACKED_GIT_LIMIT;
 size_t delta_base_cache_limit = 16 * 1024 * 1024;
index 3a2d5ed..ffa00fd 100644 (file)
@@ -1271,7 +1271,7 @@ static int update_branch(struct branch *b)
 
        if (read_ref(b->name, old_sha1))
                hashclr(old_sha1);
-       lock = lock_any_ref_for_update(b->name, old_sha1);
+       lock = lock_any_ref_for_update(b->name, old_sha1, 0);
        if (!lock)
                return error("Unable to lock %s", b->name);
        if (!force_update && !is_null_sha1(old_sha1)) {
index 06f4aec..aa59043 100644 (file)
@@ -733,7 +733,7 @@ int main(int argc, char **argv)
        }
        if (!dest)
                usage(fetch_pack_usage);
-       pid = git_connect(fd, dest, uploadpack);
+       pid = git_connect(fd, dest, uploadpack, verbose ? CONNECT_VERBOSE : 0);
        if (pid < 0)
                return 1;
        if (heads && nr_heads)
index ed7c2c5..6b6facf 100755 (executable)
@@ -270,15 +270,7 @@ if [ "$?" -eq 0 ]; then
                fi
        elif test -n "$detached"
        then
-               # NEEDSWORK: we would want a command to detach the HEAD
-               # atomically, instead of this handcrafted command sequence.
-               # Perhaps:
-               #       git update-ref --detach HEAD $new
-               # or something like that...
-               #
-               git-rev-parse HEAD >"$GIT_DIR/HEAD.new" &&
-               mv "$GIT_DIR/HEAD.new" "$GIT_DIR/HEAD" &&
-               git-update-ref -m "checkout: moving to $arg" HEAD "$detached" ||
+               git-update-ref --no-deref -m "checkout: moving to $arg" HEAD "$detached" ||
                        die "Cannot detach HEAD"
                if test -n "$detach_warn"
                then
index 7ebbce4..351676f 100755 (executable)
@@ -90,7 +90,8 @@ finish () {
        ?*)
                case "$no_summary" in
                '')
-                       git-diff-tree --stat --summary -M "$head" "$1"
+                       # We want color (if set), but no pager
+                       GIT_PAGER='' git-diff --stat --summary -M "$head" "$1"
                        ;;
                esac
                ;;
index 2dc2c4f..61770b5 100755 (executable)
@@ -307,7 +307,8 @@ fi
 if test -n "$verbose"
 then
        echo "Changes from $mb to $onto:"
-       git-diff-tree --stat --summary "$mb" "$onto"
+       # We want color (if set), but no pager
+       GIT_PAGER='' git-diff --stat --summary "$mb" "$onto"
 fi
 
 # Rewind the head to "$onto"; this saves our current head in ORIG_HEAD.
index ddfa8b4..8bf66a4 100755 (executable)
@@ -8,7 +8,7 @@ SUBDIRECTORY_OK='Yes'
 . git-sh-setup
 
 no_update_info= all_into_one= remove_redundant=
-local= quiet= no_reuse_delta= extra=
+local= quiet= no_reuse= extra=
 while case "$#" in 0) break ;; esac
 do
        case "$1" in
@@ -16,7 +16,7 @@ do
        -a)     all_into_one=t ;;
        -d)     remove_redundant=t ;;
        -q)     quiet=-q ;;
-       -f)     no_reuse_delta=--no-reuse-delta ;;
+       -f)     no_reuse=--no-reuse-object ;;
        -l)     local=--local ;;
        --window=*) extra="$extra $1" ;;
        --depth=*) extra="$extra $1" ;;
@@ -61,7 +61,7 @@ case ",$all_into_one," in
        ;;
 esac
 
-args="$args $local $quiet $no_reuse_delta$extra"
+args="$args $local $quiet $no_reuse$extra"
 name=$(git-pack-objects --non-empty --all --reflog $args </dev/null "$PACKTMP") ||
        exit 1
 if [ -z "$name" ]; then
index 96bfac4..ceb7871 100644 (file)
@@ -64,7 +64,7 @@ int main(int argc, char **argv)
        if (!dest || i != argc - 1)
                usage(peek_remote_usage);
 
-       pid = git_connect(fd, dest, uploadpack);
+       pid = git_connect(fd, dest, uploadpack, 0);
        if (pid < 0)
                return 1;
        ret = peek_remote(fd, flags);
index 26aa26b..d3c422b 100644 (file)
@@ -209,7 +209,7 @@ static const char *update(struct command *cmd)
                return NULL; /* good */
        }
        else {
-               lock = lock_any_ref_for_update(name, old_sha1);
+               lock = lock_any_ref_for_update(name, old_sha1, 0);
                if (!lock) {
                        error("failed to lock %s", name);
                        return "failed to lock";
diff --git a/refs.c b/refs.c
index 89876bf..2ae3235 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -736,19 +736,20 @@ static int is_refname_available(const char *ref, const char *oldref,
        return 1;
 }
 
-static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char *old_sha1, int *flag)
+static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char *old_sha1, int flags, int *type_p)
 {
        char *ref_file;
        const char *orig_ref = ref;
        struct ref_lock *lock;
        struct stat st;
        int last_errno = 0;
+       int type;
        int mustexist = (old_sha1 && !is_null_sha1(old_sha1));
 
        lock = xcalloc(1, sizeof(struct ref_lock));
        lock->lock_fd = -1;
 
-       ref = resolve_ref(ref, lock->old_sha1, mustexist, flag);
+       ref = resolve_ref(ref, lock->old_sha1, mustexist, &type);
        if (!ref && errno == EISDIR) {
                /* we are trying to lock foo but we used to
                 * have foo/bar which now does not exist;
@@ -761,8 +762,10 @@ static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char
                        error("there are still refs under '%s'", orig_ref);
                        goto error_return;
                }
-               ref = resolve_ref(orig_ref, lock->old_sha1, mustexist, flag);
+               ref = resolve_ref(orig_ref, lock->old_sha1, mustexist, &type);
        }
+       if (type_p)
+           *type_p = type;
        if (!ref) {
                last_errno = errno;
                error("unable to resolve reference %s: %s",
@@ -780,10 +783,15 @@ static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char
 
        lock->lk = xcalloc(1, sizeof(struct lock_file));
 
+       if (flags & REF_NODEREF)
+               ref = orig_ref;
        lock->ref_name = xstrdup(ref);
        lock->orig_ref_name = xstrdup(orig_ref);
        ref_file = git_path("%s", ref);
-       lock->force_write = lstat(ref_file, &st) && errno == ENOENT;
+       if (lstat(ref_file, &st) && errno == ENOENT)
+               lock->force_write = 1;
+       if ((flags & REF_NODEREF) && (type & REF_ISSYMREF))
+               lock->force_write = 1;
 
        if (safe_create_leading_directories(ref_file)) {
                last_errno = errno;
@@ -806,14 +814,14 @@ struct ref_lock *lock_ref_sha1(const char *ref, const unsigned char *old_sha1)
        if (check_ref_format(ref))
                return NULL;
        strcpy(refpath, mkpath("refs/%s", ref));
-       return lock_ref_sha1_basic(refpath, old_sha1, NULL);
+       return lock_ref_sha1_basic(refpath, old_sha1, 0, NULL);
 }
 
-struct ref_lock *lock_any_ref_for_update(const char *ref, const unsigned char *old_sha1)
+struct ref_lock *lock_any_ref_for_update(const char *ref, const unsigned char *old_sha1, int flags)
 {
        if (check_ref_format(ref) == -1)
                return NULL;
-       return lock_ref_sha1_basic(ref, old_sha1, NULL);
+       return lock_ref_sha1_basic(ref, old_sha1, flags, NULL);
 }
 
 static struct lock_file packlock;
@@ -858,7 +866,7 @@ int delete_ref(const char *refname, const unsigned char *sha1)
        struct ref_lock *lock;
        int err, i, ret = 0, flag = 0;
 
-       lock = lock_ref_sha1_basic(refname, sha1, &flag);
+       lock = lock_ref_sha1_basic(refname, sha1, 0, &flag);
        if (!lock)
                return 1;
        if (!(flag & REF_ISPACKED)) {
@@ -909,7 +917,7 @@ int rename_ref(const char *oldref, const char *newref, const char *logmsg)
        if (!is_refname_available(newref, oldref, get_loose_refs(), 0))
                return 1;
 
-       lock = lock_ref_sha1_basic(renamed_ref, NULL, NULL);
+       lock = lock_ref_sha1_basic(renamed_ref, NULL, 0, NULL);
        if (!lock)
                return error("unable to lock %s", renamed_ref);
        lock->force_write = 1;
@@ -963,7 +971,7 @@ int rename_ref(const char *oldref, const char *newref, const char *logmsg)
        }
        logmoved = log;
 
-       lock = lock_ref_sha1_basic(newref, NULL, NULL);
+       lock = lock_ref_sha1_basic(newref, NULL, 0, NULL);
        if (!lock) {
                error("unable to lock %s for update", newref);
                goto rollback;
@@ -979,7 +987,7 @@ int rename_ref(const char *oldref, const char *newref, const char *logmsg)
        return 0;
 
  rollback:
-       lock = lock_ref_sha1_basic(oldref, NULL, NULL);
+       lock = lock_ref_sha1_basic(oldref, NULL, 0, NULL);
        if (!lock) {
                error("unable to lock %s for rollback", oldref);
                goto rollbacklog;
diff --git a/refs.h b/refs.h
index f61f6d9..f234eb7 100644 (file)
--- a/refs.h
+++ b/refs.h
@@ -33,7 +33,8 @@ extern int get_ref_sha1(const char *ref, unsigned char *sha1);
 extern struct ref_lock *lock_ref_sha1(const char *ref, const unsigned char *old_sha1);
 
 /** Locks any ref (for 'HEAD' type refs). */
-extern struct ref_lock *lock_any_ref_for_update(const char *ref, const unsigned char *old_sha1);
+#define REF_NODEREF    0x01
+extern struct ref_lock *lock_any_ref_for_update(const char *ref, const unsigned char *old_sha1, int flags);
 
 /** Release any lock taken but not written. **/
 extern void unlock_ref(struct ref_lock *lock);
index 0125d41..0a29b53 100644 (file)
@@ -881,6 +881,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
        const char **unrecognized = argv + 1;
        int left = 1;
        int all_match = 0;
+       int regflags = 0;
 
        /* First, search for "--" */
        seen_dashdash = 0;
@@ -1152,6 +1153,14 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
                                add_message_grep(revs, arg+7);
                                continue;
                        }
+                       if (!prefixcmp(arg, "--extended-regexp")) {
+                               regflags |= REG_EXTENDED;
+                               continue;
+                       }
+                       if (!prefixcmp(arg, "--regexp-ignore-case")) {
+                               regflags |= REG_ICASE;
+                               continue;
+                       }
                        if (!strcmp(arg, "--all-match")) {
                                all_match = 1;
                                continue;
@@ -1200,6 +1209,9 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
                }
        }
 
+       if (revs->grep_filter)
+               revs->grep_filter->regflags |= regflags;
+
        if (show_merge)
                prepare_show_merge(revs);
        if (def && !revs->pending.nr) {
index d5b5162..83ee87d 100644 (file)
@@ -393,7 +393,7 @@ int main(int argc, char **argv)
                usage(send_pack_usage);
        verify_remote_names(nr_heads, heads);
 
-       pid = git_connect(fd, dest, receivepack);
+       pid = git_connect(fd, dest, receivepack, verbose ? CONNECT_VERBOSE : 0);
        if (pid < 0)
                return 1;
        ret = send_pack(fd[0], fd[1], nr_heads, heads);
index be991ed..12d2ef2 100644 (file)
@@ -972,7 +972,7 @@ void *map_sha1_file(const unsigned char *sha1, unsigned long *size)
        return map;
 }
 
-int legacy_loose_object(unsigned char *map)
+static int legacy_loose_object(unsigned char *map)
 {
        unsigned int word;
 
@@ -1034,6 +1034,14 @@ static int unpack_sha1_header(z_stream *stream, unsigned char *map, unsigned lon
                return inflate(stream, 0);
        }
 
+
+       /*
+        * There used to be a second loose object header format which
+        * was meant to mimic the in-pack format, allowing for direct
+        * copy of the object data.  This format turned up not to be
+        * really worth it and we don't write it any longer.  But we
+        * can still read it.
+        */
        used = unpack_object_header_gently(map, mapsize, &type, &size);
        if (!used || !valid_loose_object_type[type])
                return -1;
@@ -1962,40 +1970,6 @@ static int write_buffer(int fd, const void *buf, size_t len)
        return 0;
 }
 
-static int write_binary_header(unsigned char *hdr, enum object_type type, unsigned long len)
-{
-       int hdr_len;
-       unsigned char c;
-
-       c = (type << 4) | (len & 15);
-       len >>= 4;
-       hdr_len = 1;
-       while (len) {
-               *hdr++ = c | 0x80;
-               hdr_len++;
-               c = (len & 0x7f);
-               len >>= 7;
-       }
-       *hdr = c;
-       return hdr_len;
-}
-
-static void setup_object_header(z_stream *stream, const char *type, unsigned long len)
-{
-       int obj_type, hdrlen;
-
-       if (use_legacy_headers) {
-               while (deflate(stream, 0) == Z_OK)
-                       /* nothing */;
-               return;
-       }
-       obj_type = type_from_string(type);
-       hdrlen = write_binary_header(stream->next_out, obj_type, len);
-       stream->total_out = hdrlen;
-       stream->next_out += hdrlen;
-       stream->avail_out -= hdrlen;
-}
-
 int hash_sha1_file(const void *buf, unsigned long len, const char *type,
                    unsigned char *sha1)
 {
@@ -2062,7 +2036,8 @@ int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned cha
        /* First header.. */
        stream.next_in = (unsigned char *)hdr;
        stream.avail_in = hdrlen;
-       setup_object_header(&stream, type, len);
+       while (deflate(&stream, 0) == Z_OK)
+               /* nothing */;
 
        /* Then the data itself.. */
        stream.next_in = buf;
diff --git a/t/t9500-gitweb-standalone-no-errors.sh b/t/t9500-gitweb-standalone-no-errors.sh
new file mode 100755 (executable)
index 0000000..b92ab63
--- /dev/null
@@ -0,0 +1,490 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Jakub Narebski
+#
+
+test_description='gitweb as standalone script (basic tests).
+
+This test runs gitweb (git web interface) as CGI script from
+commandline, and checks that it would not write any errors
+or warnings to log.'
+
+gitweb_init () {
+       cat >gitweb_config.perl <<EOF
+#!/usr/bin/perl
+
+# gitweb configuration for tests
+
+our \$version = "current";
+our \$GIT = "git";
+our \$projectroot = "$(pwd)";
+our \$home_link_str = "projects";
+our \$site_name = "[localhost]";
+our \$site_header = "";
+our \$site_footer = "";
+our \$home_text = "indextext.html";
+our @stylesheets = ("file:///$(pwd)/../../gitweb/gitweb.css");
+our \$logo = "file:///$(pwd)/../../gitweb/git-logo.png";
+our \$favicon = "file:///$(pwd)/../../gitweb/git-favicon.png";
+our \$projects_list = "";
+our \$export_ok = "";
+our \$strict_export = "";
+
+CGI::Carp::set_programname("gitweb/gitweb.cgi");
+EOF
+
+       cat >.git/description <<EOF
+$0 test repository
+EOF
+}
+
+gitweb_run () {
+       export GATEWAY_INTERFACE="CGI/1.1"
+       export HTTP_ACCEPT="*/*"
+       export REQUEST_METHOD="GET"
+       export QUERY_STRING=""$1""
+       export PATH_INFO=""$2""
+
+       export GITWEB_CONFIG=$(pwd)/gitweb_config.perl
+
+       # some of git commands write to STDERR on error, but this is not
+       # written to web server logs, so we are not interested in that:
+       # we are interested only in properly formatted errors/warnings
+       rm -f gitweb.log &&
+       perl -- $(pwd)/../../gitweb/gitweb.perl \
+               >/dev/null 2>gitweb.log &&
+       if grep -q -s "^[[]" gitweb.log >/dev/null; then false; else true; fi
+
+       # gitweb.log is left for debugging
+}
+
+. ./test-lib.sh
+
+gitweb_init
+
+# ----------------------------------------------------------------------
+# no commits (empty, just initialized repository)
+
+test_expect_success \
+       'no commits: projects_list (implicit)' \
+       'gitweb_run'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+       'no commits: projects_index' \
+       'gitweb_run "a=project_index"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+       'no commits: .git summary (implicit)' \
+       'gitweb_run "p=.git"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+       'no commits: .git commit (implicit HEAD)' \
+       'gitweb_run "p=.git;a=commit"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+       'no commits: .git commitdiff (implicit HEAD)' \
+       'gitweb_run "p=.git;a=commitdiff"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+       'no commits: .git tree (implicit HEAD)' \
+       'gitweb_run "p=.git;a=tree"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+       'no commits: .git heads' \
+       'gitweb_run "p=.git;a=heads"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+       'no commits: .git tags' \
+       'gitweb_run "p=.git;a=tags"'
+test_debug 'cat gitweb.log'
+
+
+# ----------------------------------------------------------------------
+# initial commit
+
+test_expect_success \
+       'Make initial commit' \
+       'echo "Not an empty file." > file &&
+        git add file &&
+        git commit -a -m "Initial commit." &&
+        git branch b'
+
+test_expect_success \
+       'projects_list (implicit)' \
+       'gitweb_run'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+       'projects_index' \
+       'gitweb_run "a=project_index"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+       '.git summary (implicit)' \
+       'gitweb_run "p=.git"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+       '.git commit (implicit HEAD)' \
+       'gitweb_run "p=.git;a=commit"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+       '.git commitdiff (implicit HEAD, root commit)' \
+       'gitweb_run "p=.git;a=commitdiff"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+       '.git commitdiff_plain (implicit HEAD, root commit)' \
+       'gitweb_run "p=.git;a=commitdiff_plain"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+       '.git commit (HEAD)' \
+       'gitweb_run "p=.git;a=commit;h=HEAD"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+       '.git tree (implicit HEAD)' \
+       'gitweb_run "p=.git;a=tree"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+       '.git blob (file)' \
+       'gitweb_run "p=.git;a=blob;f=file"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+       '.git blob_plain (file)' \
+       'gitweb_run "p=.git;a=blob_plain;f=file"'
+test_debug 'cat gitweb.log'
+
+# ----------------------------------------------------------------------
+# nonexistent objects
+
+test_expect_success \
+       '.git commit (non-existent)' \
+       'gitweb_run "p=.git;a=commit;h=non-existent"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+       '.git commitdiff (non-existent)' \
+       'gitweb_run "p=.git;a=commitdiff;h=non-existent"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+       '.git commitdiff (non-existent vs HEAD)' \
+       'gitweb_run "p=.git;a=commitdiff;hp=non-existent;h=HEAD"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+       '.git tree (0000000000000000000000000000000000000000)' \
+       'gitweb_run "p=.git;a=tree;h=0000000000000000000000000000000000000000"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+       '.git tag (0000000000000000000000000000000000000000)' \
+       'gitweb_run "p=.git;a=tag;h=0000000000000000000000000000000000000000"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+       '.git blob (non-existent)' \
+       'gitweb_run "p=.git;a=blob;f=non-existent"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+       '.git blob_plain (non-existent)' \
+       'gitweb_run "p=.git;a=blob_plain;f=non-existent"'
+test_debug 'cat gitweb.log'
+
+
+# ----------------------------------------------------------------------
+# commitdiff testing (implicit, one implicit tree-ish)
+
+test_expect_success \
+       'commitdiff(0): root' \
+       'gitweb_run "p=.git;a=commitdiff"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+       'commitdiff(0): file added' \
+       'echo "New file" > new_file &&
+        git add new_file &&
+        git commit -a -m "File added." &&
+        gitweb_run "p=.git;a=commitdiff"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+       'commitdiff(0): mode change' \
+       'chmod a+x new_file &&
+        git commit -a -m "Mode changed." &&
+        gitweb_run "p=.git;a=commitdiff"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+       'commitdiff(0): file renamed' \
+       'git mv new_file renamed_file &&
+        git commit -a -m "File renamed." &&
+        gitweb_run "p=.git;a=commitdiff"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+       'commitdiff(0): file to symlink' \
+       'rm renamed_file &&
+        ln -s file renamed_file &&
+        git commit -a -m "File to symlink." &&
+        gitweb_run "p=.git;a=commitdiff"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+       'commitdiff(0): file deleted' \
+       'git rm renamed_file &&
+        rm -f renamed_file &&
+        git commit -a -m "File removed." &&
+        gitweb_run "p=.git;a=commitdiff"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+       'commitdiff(0): file copied / new file' \
+       'cp file file2 &&
+        git add file2 &&
+        git commit -a -m "File copied." &&
+        gitweb_run "p=.git;a=commitdiff"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+       'commitdiff(0): mode change and modified' \
+       'echo "New line" >> file2 &&
+        chmod a+x file2 &&
+        git commit -a -m "Mode change and modification." &&
+        gitweb_run "p=.git;a=commitdiff"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+       'commitdiff(0): renamed and modified' \
+       'cat >file2<<EOF &&
+Dominus regit me,
+et nihil mihi deerit.
+In loco pascuae ibi me collocavit,
+super aquam refectionis educavit me;
+animam meam convertit,
+deduxit me super semitas jusitiae,
+propter nomen suum.
+EOF
+        git commit -a -m "File added." &&
+        git mv file2 file3 &&
+        echo "Propter nomen suum." >> file3 &&
+        git commit -a -m "File rename and modification." &&
+        gitweb_run "p=.git;a=commitdiff"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+       'commitdiff(0): renamed, mode change and modified' \
+       'git mv file3 file2 &&
+        echo "Propter nomen suum." >> file2 &&
+        chmod a+x file2 &&
+        git commit -a -m "File rename, mode change and modification." &&
+        gitweb_run "p=.git;a=commitdiff"'
+test_debug 'cat gitweb.log'
+
+# ----------------------------------------------------------------------
+# commitdiff testing (taken from t4114-apply-typechange.sh)
+
+test_expect_success 'setup typechange commits' '
+       echo "hello world" > foo &&
+       echo "hi planet" > bar &&
+       git update-index --add foo bar &&
+       git commit -m initial &&
+       git branch initial &&
+       rm -f foo &&
+       ln -s bar foo &&
+       git update-index foo &&
+       git commit -m "foo symlinked to bar" &&
+       git branch foo-symlinked-to-bar &&
+       rm -f foo &&
+       echo "how far is the sun?" > foo &&
+       git update-index foo &&
+       git commit -m "foo back to file" &&
+       git branch foo-back-to-file &&
+       rm -f foo &&
+       git update-index --remove foo &&
+       mkdir foo &&
+       echo "if only I knew" > foo/baz &&
+       git update-index --add foo/baz &&
+       git commit -m "foo becomes a directory" &&
+       git branch "foo-becomes-a-directory" &&
+       echo "hello world" > foo/baz &&
+       git update-index foo/baz &&
+       git commit -m "foo/baz is the original foo" &&
+       git branch foo-baz-renamed-from-foo
+       '
+
+test_expect_success \
+       'commitdiff(2): file renamed from foo to foo/baz' \
+       'gitweb_run "p=.git;a=commitdiff;hp=initial;h=foo-baz-renamed-from-foo"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+       'commitdiff(2): file renamed from foo/baz to foo' \
+       'gitweb_run "p=.git;a=commitdiff;hp=foo-baz-renamed-from-foo;h=initial"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+       'commitdiff(2): directory becomes file' \
+       'gitweb_run "p=.git;a=commitdiff;hp=foo-becomes-a-directory;h=initial"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+       'commitdiff(2): file becomes directory' \
+       'gitweb_run "p=.git;a=commitdiff;hp=initial;h=foo-becomes-a-directory"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+       'commitdiff(2): file becomes symlink' \
+       'gitweb_run "p=.git;a=commitdiff;hp=initial;h=foo-symlinked-to-bar"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+       'commitdiff(2): symlink becomes file' \
+       'gitweb_run "p=.git;a=commitdiff;hp=foo-symlinked-to-bar;h=foo-back-to-file"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+       'commitdiff(2): symlink becomes directory' \
+       'gitweb_run "p=.git;a=commitdiff;hp=foo-symlinked-to-bar;h=foo-becomes-a-directory"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+       'commitdiff(2): directory becomes symlink' \
+       'gitweb_run "p=.git;a=commitdiff;hp=foo-becomes-a-directory;h=foo-symlinked-to-bar"'
+test_debug 'cat gitweb.log'
+
+# ----------------------------------------------------------------------
+# commit, commitdiff: merge, large
+test_expect_success \
+       'Create a merge' \
+       'git checkout b &&
+        echo "Branch" >> b &&
+        git add b &&
+        git commit -a -m "On branch" &&
+        git checkout master &&
+        git pull . b'
+
+test_expect_success \
+       'commit(0): merge commit' \
+       'gitweb_run "p=.git;a=commit"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+       'commitdiff(0): merge commit' \
+       'gitweb_run "p=.git;a=commitdiff"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+       'Prepare large commit' \
+       'git checkout b &&
+        echo "To be changed" > 01-change &&
+        echo "To be renamed" > 02-pure-rename-from &&
+        echo "To be deleted" > 03-delete &&
+        echo "To be renamed and changed" > 04-rename-from &&
+        echo "To have mode changed" > 05-mode-change &&
+        echo "File to symlink" > 06-file-or-symlink &&
+        echo "To be changed and have mode changed" > 07-change-mode-change     &&
+        git add 0* &&
+        git commit -a -m "Prepare large commit" &&
+        echo "Changed" > 01-change &&
+        git mv 02-pure-rename-from 02-pure-rename-to &&
+        git rm 03-delete && rm -f 03-delete &&
+        echo "A new file" > 03-new &&
+        git add 03-new &&
+        git mv 04-rename-from 04-rename-to &&
+        echo "Changed" >> 04-rename-to &&
+        chmod a+x 05-mode-change &&
+        rm -f 06-file-or-symlink && ln -s 01-change 06-file-or-symlink &&
+        echo "Changed and have mode changed" > 07-change-mode-change   &&
+        chmod a+x 07-change-mode-change &&
+        git commit -a -m "Large commit" &&
+        git checkout master'
+
+test_expect_success \
+       'commit(1): large commit' \
+       'gitweb_run "p=.git;a=commit;h=b"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+       'commitdiff(1): large commit' \
+       'gitweb_run "p=.git;a=commitdiff;h=b"'
+test_debug 'cat gitweb.log'
+
+# ----------------------------------------------------------------------
+# tags testing
+
+test_expect_success \
+       'tags: list of different types of tags' \
+       'git checkout master &&
+        git tag -a -m "Tag commit object" tag-commit HEAD &&
+        git tag -a -m "" tag-commit-nomessage HEAD &&
+        git tag -a -m "Tag tag object" tag-tag tag-commit &&
+        git tag -a -m "Tag tree object" tag-tree HEAD^{tree} &&
+        git tag -a -m "Tag blob object" tag-blob HEAD:file &&
+        git tag lightweight/tag-commit HEAD &&
+        git tag lightweight/tag-tag tag-commit &&
+        git tag lightweight/tag-tree HEAD^{tree} &&
+        git tag lightweight/tag-blob HEAD:file &&
+        gitweb_run "p=.git;a=tags"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+       'tag: Tag to commit object' \
+       'gitweb_run "p=.git;a=tag;h=tag-commit"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+       'tag: on lightweight tag (invalid)' \
+       'gitweb_run "p=.git;a=tag;h=lightweight/tag-commit"'
+test_debug 'cat gitweb.log'
+
+# ----------------------------------------------------------------------
+# logs
+
+test_expect_success \
+       'logs: log (implicit HEAD)' \
+       'gitweb_run "p=.git;a=log"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+       'logs: shortlog (implicit HEAD)' \
+       'gitweb_run "p=.git;a=shortlog"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+       'logs: history (implicit HEAD, file)' \
+       'gitweb_run "p=.git;a=history;f=file"'
+test_debug 'cat gitweb.log'
+
+# ----------------------------------------------------------------------
+# feed generation
+
+test_expect_success \
+       'feeds: OPML' \
+       'gitweb_run "a=opml"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+       'feed: RSS' \
+       'gitweb_run "p=.git;a=rss"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+       'feed: Atom' \
+       'gitweb_run "p=.git;a=atom"'
+test_debug 'cat gitweb.log'
+
+test_done