Code

Merge branch 'jc/upload-corrupt' into next
authorJunio C Hamano <junkio@cox.net>
Wed, 21 Jun 2006 23:38:11 +0000 (16:38 -0700)
committerJunio C Hamano <junkio@cox.net>
Wed, 21 Jun 2006 23:38:11 +0000 (16:38 -0700)
* jc/upload-corrupt:
  daemon: send stderr to /dev/null instead of closing.

67 files changed:
Documentation/git-rebase.txt
Documentation/git-repo-config.txt
Makefile
blame.c
builtin-apply.c
builtin-diff-files.c
builtin-diff-index.c
builtin-diff-tree.c
builtin-diff.c
builtin-grep.c
builtin-log.c
builtin-mailinfo.c [new file with mode: 0644]
builtin-mailsplit.c [new file with mode: 0644]
builtin-read-tree.c
builtin-rev-list.c
builtin-stripspace.c [new file with mode: 0644]
builtin-update-index.c [new file with mode: 0644]
builtin-update-ref.c [new file with mode: 0644]
builtin-write-tree.c [new file with mode: 0644]
builtin.h
config.c
connect.c
convert-objects.c
csum-file.c
daemon.c
diff-delta.c
diff-lib.c
diff.c
diff.h
diffcore-order.c
git-rebase.sh
git.c
gitweb/gitweb.cgi
gitweb/gitweb.css [new file with mode: 0644]
http-fetch.c
http-push.c
http.c
imap-send.c
mailinfo.c [deleted file]
mailsplit.c [deleted file]
merge-index.c
name-rev.c
object-refs.c
object.c
object.h
pack-check.c
pack-objects.c
pack-redundant.c
pager.c
patch-delta.c
pkt-line.c
quote.c
read-cache.c
repo-config.c
revision.c
revision.h
sha1_file.c
ssh-fetch.c
stripspace.c [deleted file]
t/Makefile
t/t1300-repo-config.sh
t/t3402-rebase-merge.sh [new file with mode: 0755]
tag.c
tree-walk.c
update-index.c [deleted file]
update-ref.c [deleted file]
write-tree.c [deleted file]

index 08ee4aabaf74c8691a67c974bf3dca1c28cf25bc..c339c4525c500f29d51063a34f1c7d238b37c2e6 100644 (file)
@@ -7,7 +7,7 @@ git-rebase - Rebase local commits to a new head
 
 SYNOPSIS
 --------
-'git-rebase' [--onto <newbase>] <upstream> [<branch>]
+'git-rebase' [--merge] [--onto <newbase>] <upstream> [<branch>]
 
 'git-rebase' --continue | --skip | --abort
 
@@ -106,6 +106,24 @@ OPTIONS
 --abort::
        Restore the original branch and abort the rebase operation.
 
+--skip::
+       Restart the rebasing process by skipping the current patch.
+       This does not work with the --merge option.
+
+--merge::
+       Use merging strategies to rebase.  When the recursive (default) merge
+       strategy is used, this allows rebase to be aware of renames on the
+       upstream side.
+
+-s <strategy>, \--strategy=<strategy>::
+       Use the given merge strategy; can be supplied more than
+       once to specify them in the order they should be tried.
+       If there is no `-s` option, a built-in list of strategies
+       is used instead (`git-merge-recursive` when merging a single
+       head, `git-merge-octopus` otherwise).  This implies --merge.
+
+include::merge-strategies.txt[]
+
 NOTES
 -----
 When you rebase a branch, you are changing its history in a way that
index d5142e0dcd6799b8a94c9cd5a3f2bd17c3cb7289..803c0d5cae6db0a953380adc6ee3980a0afa72e0 100644 (file)
@@ -73,6 +73,18 @@ OPTIONS
        List all variables set in .git/config.
 
 
+ENVIRONMENT
+-----------
+
+GIT_CONFIG::
+       Take the configuration from the given file instead of .git/config.
+
+GIT_CONFIG_LOCAL::
+       Currently the same as $GIT_CONFIG; when Git will support global
+       configuration files, this will cause it to take the configuration
+       from the global configuration file in addition to the given file.
+
+
 EXAMPLE
 -------
 
index ae5e8d790a8b148b95cfce03afab94e97315110a..de7614174215feaafb20bdaa350b916e76f7cc43 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -144,34 +144,33 @@ SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) \
 
 # The ones that do not have to link with lcrypto, lz nor xdiff.
 SIMPLE_PROGRAMS = \
-       git-mailsplit$X \
-       git-stripspace$X git-daemon$X
+       git-daemon$X
 
 # ... and all the rest that could be moved out of bindir to gitexecdir
 PROGRAMS = \
        git-checkout-index$X \
        git-convert-objects$X git-fetch-pack$X git-fsck-objects$X \
        git-hash-object$X git-index-pack$X git-local-fetch$X \
-       git-mailinfo$X git-merge-base$X \
+       git-merge-base$X \
        git-merge-index$X git-mktag$X git-mktree$X git-pack-objects$X git-patch-id$X \
        git-peek-remote$X git-prune-packed$X git-receive-pack$X \
        git-send-pack$X git-shell$X \
        git-show-index$X git-ssh-fetch$X \
        git-ssh-upload$X git-unpack-file$X \
-       git-unpack-objects$X git-update-index$X git-update-server-info$X \
-       git-upload-pack$X git-verify-pack$X git-write-tree$X \
-       git-update-ref$X git-symbolic-ref$X \
+       git-unpack-objects$X git-update-server-info$X \
+       git-upload-pack$X git-verify-pack$X \
+       git-symbolic-ref$X \
        git-name-rev$X git-pack-redundant$X git-repo-config$X git-var$X \
        git-describe$X git-merge-tree$X git-blame$X git-imap-send$X
 
-BUILT_INS = git-log$X git-whatchanged$X git-show$X \
-       git-count-objects$X git-diff$X git-push$X \
-       git-grep$X git-add$X git-rm$X git-rev-list$X \
-       git-check-ref-format$X git-rev-parse$X \
+BUILT_INS = git-log$X git-whatchanged$X git-show$X git-update-ref$X \
+       git-count-objects$X git-diff$X git-push$X git-mailsplit$X \
+       git-grep$X git-add$X git-rm$X git-rev-list$X git-stripspace$X \
+       git-check-ref-format$X git-rev-parse$X git-mailinfo$X \
        git-init-db$X git-tar-tree$X git-upload-tar$X git-format-patch$X \
        git-ls-files$X git-ls-tree$X git-get-tar-commit-id$X \
-       git-read-tree$X git-commit-tree$X \
-       git-apply$X git-show-branch$X git-diff-files$X \
+       git-read-tree$X git-commit-tree$X git-write-tree$X \
+       git-apply$X git-show-branch$X git-diff-files$X git-update-index$X \
        git-diff-index$X git-diff-stages$X git-diff-tree$X git-cat-file$X
 
 # what 'all' will build and 'install' will install, in gitexecdir
@@ -222,12 +221,13 @@ BUILTIN_OBJS = \
        builtin-log.o builtin-help.o builtin-count.o builtin-diff.o builtin-push.o \
        builtin-grep.o builtin-add.o builtin-rev-list.o builtin-check-ref-format.o \
        builtin-rm.o builtin-init-db.o builtin-rev-parse.o \
-       builtin-tar-tree.o builtin-upload-tar.o \
-       builtin-ls-files.o builtin-ls-tree.o \
-       builtin-read-tree.o builtin-commit-tree.o \
+       builtin-tar-tree.o builtin-upload-tar.o builtin-update-index.o \
+       builtin-ls-files.o builtin-ls-tree.o builtin-write-tree.o \
+       builtin-read-tree.o builtin-commit-tree.o builtin-mailinfo.o \
        builtin-apply.o builtin-show-branch.o builtin-diff-files.o \
        builtin-diff-index.o builtin-diff-stages.o builtin-diff-tree.o \
-       builtin-cat-file.o
+       builtin-cat-file.o builtin-mailsplit.o builtin-stripspace.o \
+       builtin-update-ref.o
 
 GITLIBS = $(LIB_FILE) $(XDIFF_LIB)
 LIBS = $(GITLIBS) -lz
@@ -380,9 +380,7 @@ ifdef NEEDS_LIBICONV
        else
                ICONV_LINK =
        endif
-       LIB_4_ICONV = $(ICONV_LINK) -liconv
-else
-       LIB_4_ICONV =
+       LIBS += $(ICONV_LINK) -liconv
 endif
 ifdef NEEDS_SOCKET
        LIBS += -lsocket
@@ -565,10 +563,6 @@ $(SIMPLE_PROGRAMS) : git-%$X : %.o
        $(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
                $(LIB_FILE) $(SIMPLE_LIB)
 
-git-mailinfo$X: mailinfo.o $(LIB_FILE)
-       $(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
-               $(LIB_FILE) $(SIMPLE_LIB) $(LIB_4_ICONV)
-
 git-local-fetch$X: fetch.o
 git-ssh-fetch$X: rsh.o fetch.o
 git-ssh-upload$X: rsh.o
diff --git a/blame.c b/blame.c
index 7c0b62cf3f5ff78f95cf96c2582f693b5d124752..c86e2fd4b31fc75adf1c315fda166aabc38f0f43 100644 (file)
--- a/blame.c
+++ b/blame.c
@@ -301,9 +301,9 @@ static void fill_line_map(struct commit *commit, struct commit *other,
                                if (DEBUG)
                                        printf("map: i1: %d %d %p i2: %d %d %p\n",
                                               i1, map[i1],
-                                              i1 != -1 ? blame_lines[map[i1]] : NULL,
+                                              (void *) (i1 != -1 ? blame_lines[map[i1]] : NULL),
                                               i2, map2[i2],
-                                              i2 != -1 ? blame_lines[map2[i2]] : NULL);
+                                              (void *) (i2 != -1 ? blame_lines[map2[i2]] : NULL));
                                if (map2[i2] != -1 &&
                                    blame_lines[map[i1]] &&
                                    !blame_lines[map2[i2]])
index e113c74dd80d458116788500baca2740cfaa9a00..6dd0472ae07cac291d26b9e547a92c7c51d569df 100644 (file)
@@ -148,7 +148,7 @@ static void *read_patch_file(int fd, unsigned long *sizep)
                        buffer = xrealloc(buffer, alloc);
                        nr = alloc - size;
                }
-               nr = xread(fd, buffer + size, nr);
+               nr = xread(fd, (char *) buffer + size, nr);
                if (!nr)
                        break;
                if (nr < 0)
@@ -164,7 +164,7 @@ static void *read_patch_file(int fd, unsigned long *sizep)
         */
        if (alloc < size + SLOP)
                buffer = xrealloc(buffer, size + SLOP);
-       memset(buffer + size, 0, SLOP);
+       memset((char *) buffer + size, 0, SLOP);
        return buffer;
 }
 
@@ -1194,7 +1194,7 @@ static int read_old_data(struct stat *st, const char *path, void *buf, unsigned
                        return error("unable to open %s", path);
                got = 0;
                for (;;) {
-                       int ret = xread(fd, buf + got, size - got);
+                       int ret = xread(fd, (char *) buf + got, size - got);
                        if (ret <= 0)
                                break;
                        got += ret;
index cebda828eec4e90f3d0af050e07a53071b39d5b7..5afc1d7208f8a13b7363be2127cad3b43baa95dd 100644 (file)
@@ -41,7 +41,7 @@ int cmd_diff_files(int argc, const char **argv, char **envp)
         * rev.max_count is reasonable (0 <= n <= 3),
         * there is no other revision filtering parameters.
         */
-       if (rev.pending_objects ||
+       if (rev.pending.nr ||
            rev.min_age != -1 || rev.max_age != -1)
                usage(diff_files_usage);
        /*
index 1958580d82e54ab5a5b1e054263eab7264c4bcb2..c42ef9a7a76e7d8f6bc25d593865b9343d4e74b5 100644 (file)
@@ -32,7 +32,7 @@ int cmd_diff_index(int argc, const char **argv, char **envp)
         * Make sure there is one revision (i.e. pending object),
         * and there is no revision filtering parameters.
         */
-       if (!rev.pending_objects || rev.pending_objects->next ||
+       if (rev.pending.nr != 1 ||
            rev.max_count != -1 || rev.min_age != -1 || rev.max_age != -1)
                usage(diff_cache_usage);
        return run_diff_index(&rev, cached);
index 58cf65856d1e9e0ab088dab2c051058cf48cb840..3409a39a9f73f9ed67547f5cc79619c1e402f47b 100644 (file)
@@ -65,7 +65,6 @@ int cmd_diff_tree(int argc, const char **argv, char **envp)
        char line[1000];
        struct object *tree1, *tree2;
        static struct rev_info *opt = &log_tree_opt;
-       struct object_list *list;
        int read_stdin = 0;
 
        git_config(git_diff_config);
@@ -86,45 +85,28 @@ int cmd_diff_tree(int argc, const char **argv, char **envp)
        }
 
        /*
-        * NOTE! "setup_revisions()" will have inserted the revisions
-        * it parsed in reverse order. So if you do
-        *
-        *      git-diff-tree a b
-        *
-        * the commit list will be "b" -> "a" -> NULL, so we reverse
-        * the order of the objects if the first one is not marked
-        * UNINTERESTING.
+        * NOTE! We expect "a ^b" to be equal to "a..b", so we
+        * reverse the order of the objects if the second one
+        * is marked UNINTERESTING.
         */
-       nr_sha1 = 0;
-       list = opt->pending_objects;
-       if (list) {
-               nr_sha1++;
-               tree1 = list->item;
-               list = list->next;
-               if (list) {
-                       nr_sha1++;
-                       tree2 = tree1;
-                       tree1 = list->item;
-                       if (list->next)
-                               usage(diff_tree_usage);
-                       /* Switch them around if the second one was uninteresting.. */
-                       if (tree2->flags & UNINTERESTING) {
-                               struct object *tmp = tree2;
-                               tree2 = tree1;
-                               tree1 = tmp;
-                       }
-               }
-       }
-
+       nr_sha1 = opt->pending.nr;
        switch (nr_sha1) {
        case 0:
                if (!read_stdin)
                        usage(diff_tree_usage);
                break;
        case 1:
+               tree1 = opt->pending.objects[0].item;
                diff_tree_commit_sha1(tree1->sha1);
                break;
        case 2:
+               tree1 = opt->pending.objects[0].item;
+               tree2 = opt->pending.objects[1].item;
+               if (tree2->flags & UNINTERESTING) {
+                       struct object *tmp = tree2;
+                       tree2 = tree1;
+                       tree1 = tmp;
+               }
                diff_tree_sha1(tree1->sha1,
                               tree2->sha1,
                               "", &opt->diffopt);
index 6ac3d4b8ca23e7c4d724b22fd3222005d8fe3df8..99a2f766055d6c8c3231e38bd369169d3c742782 100644 (file)
@@ -50,7 +50,7 @@ static int builtin_diff_files(struct rev_info *revs,
         * specified rev.max_count is reasonable (0 <= n <= 3), and
         * there is no other revision filtering parameter.
         */
-       if (revs->pending_objects ||
+       if (revs->pending.nr ||
            revs->min_age != -1 ||
            revs->max_age != -1 ||
            3 < revs->max_count)
@@ -172,7 +172,7 @@ static int builtin_diff_index(struct rev_info *revs,
         * Make sure there is one revision (i.e. pending object),
         * and there is no revision filtering parameters.
         */
-       if (!revs->pending_objects || revs->pending_objects->next ||
+       if (revs->pending.nr != 1 ||
            revs->max_count != -1 || revs->min_age != -1 ||
            revs->max_age != -1)
                usage(builtin_diff_usage);
@@ -181,10 +181,10 @@ static int builtin_diff_index(struct rev_info *revs,
 
 static int builtin_diff_tree(struct rev_info *revs,
                             int argc, const char **argv,
-                            struct object_list *ent)
+                            struct object_array_entry *ent)
 {
        const unsigned char *(sha1[2]);
-       int swap = 1;
+       int swap = 0;
        while (1 < argc) {
                const char *arg = argv[1];
                if (!strcmp(arg, "--raw"))
@@ -195,10 +195,10 @@ static int builtin_diff_tree(struct rev_info *revs,
        }
 
        /* We saw two trees, ent[0] and ent[1].
-        * unless ent[0] is unintesting, they are swapped
+        * if ent[1] is unintesting, they are swapped
         */
-       if (ent[0].item->flags & UNINTERESTING)
-               swap = 0;
+       if (ent[1].item->flags & UNINTERESTING)
+               swap = 1;
        sha1[swap] = ent[0].item->sha1;
        sha1[1-swap] = ent[1].item->sha1;
        diff_tree_sha1(sha1[0], sha1[1], "", &revs->diffopt);
@@ -208,7 +208,7 @@ static int builtin_diff_tree(struct rev_info *revs,
 
 static int builtin_diff_combined(struct rev_info *revs,
                                 int argc, const char **argv,
-                                struct object_list *ent,
+                                struct object_array_entry *ent,
                                 int ents)
 {
        const unsigned char (*parent)[20];
@@ -242,13 +242,14 @@ void add_head(struct rev_info *revs)
        obj = parse_object(sha1);
        if (!obj)
                return;
-       add_object(obj, &revs->pending_objects, NULL, "HEAD");
+       add_pending_object(revs, obj, "HEAD");
 }
 
 int cmd_diff(int argc, const char **argv, char **envp)
 {
+       int i;
        struct rev_info rev;
-       struct object_list *list, ent[100];
+       struct object_array_entry ent[100];
        int ents = 0, blobs = 0, paths = 0;
        const char *path = NULL;
        struct blobinfo blob[2];
@@ -281,7 +282,7 @@ int cmd_diff(int argc, const char **argv, char **envp)
        /* Do we have --cached and not have a pending object, then
         * default to HEAD by hand.  Eek.
         */
-       if (!rev.pending_objects) {
+       if (!rev.pending.nr) {
                int i;
                for (i = 1; i < argc; i++) {
                        const char *arg = argv[i];
@@ -294,7 +295,8 @@ int cmd_diff(int argc, const char **argv, char **envp)
                }
        }
 
-       for (list = rev.pending_objects; list; list = list->next) {
+       for (i = 0; i < rev.pending.nr; i++) {
+               struct object_array_entry *list = rev.pending.objects+i;
                struct object *obj = list->item;
                const char *name = list->name;
                int flags = (obj->flags & UNINTERESTING);
index f7767bb4eeacf4d4c06ef54e6bb3f4099e96d198..2e7986cecefc964f665b10d363569951c1f40293 100644 (file)
@@ -658,7 +658,7 @@ int cmd_grep(int argc, const char **argv, char **envp)
        int cached = 0;
        int seen_dashdash = 0;
        struct grep_opt opt;
-       struct object_list *list, **tail, *object_list = NULL;
+       struct object_array list = { 0, 0, NULL };
        const char *prefix = setup_git_directory();
        const char **paths = NULL;
        int i;
@@ -678,7 +678,6 @@ int cmd_grep(int argc, const char **argv, char **envp)
         * that continues up to the -- (if exists), and then paths.
         */
 
-       tail = &object_list;
        while (1 < argc) {
                const char *arg = argv[1];
                argc--; argv++;
@@ -852,12 +851,9 @@ int cmd_grep(int argc, const char **argv, char **envp)
                /* Is it a rev? */
                if (!get_sha1(arg, sha1)) {
                        struct object *object = parse_object(sha1);
-                       struct object_list *elem;
                        if (!object)
                                die("bad object %s", arg);
-                       elem = object_list_insert(object, tail);
-                       elem->name = arg;
-                       tail = &elem->next;
+                       add_object_array(object, arg, &list);
                        continue;
                }
                if (!strcmp(arg, "--")) {
@@ -882,16 +878,16 @@ int cmd_grep(int argc, const char **argv, char **envp)
                paths[1] = NULL;
        }
 
-       if (!object_list)
+       if (!list.nr)
                return !grep_cache(&opt, paths, cached);
 
        if (cached)
                die("both --cached and trees are given.");
 
-       for (list = object_list; list; list = list->next) {
+       for (i = 0; i < list.nr; i++) {
                struct object *real_obj;
-               real_obj = deref_tag(list->item, NULL, 0);
-               if (grep_object(&opt, paths, real_obj, list->name))
+               real_obj = deref_tag(list.objects[i].item, NULL, 0);
+               if (grep_object(&opt, paths, real_obj, list.objects[i].name))
                        hit = 1;
        }
        return !hit;
index 9187fd337ba767384abab26224b95610aa531807..5a8a50b81dabea4ccd9b0ba0fcb5d925a00560d0 100644 (file)
@@ -257,8 +257,8 @@ int cmd_format_patch(int argc, const char **argv, char **envp)
                            output_directory);
        }
 
-       if (rev.pending_objects && rev.pending_objects->next == NULL) {
-               rev.pending_objects->item->flags |= UNINTERESTING;
+       if (rev.pending.nr == 1) {
+               rev.pending.objects[0].item->flags |= UNINTERESTING;
                add_head(&rev);
        }
 
diff --git a/builtin-mailinfo.c b/builtin-mailinfo.c
new file mode 100644 (file)
index 0000000..821642a
--- /dev/null
@@ -0,0 +1,846 @@
+/*
+ * Another stupid program, this one parsing the headers of an
+ * email to figure out authorship and subject
+ */
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#ifndef NO_ICONV
+#include <iconv.h>
+#endif
+#include "git-compat-util.h"
+#include "cache.h"
+#include "builtin.h"
+
+static FILE *cmitmsg, *patchfile, *fin, *fout;
+
+static int keep_subject = 0;
+static const char *metainfo_charset = NULL;
+static char line[1000];
+static char date[1000];
+static char name[1000];
+static char email[1000];
+static char subject[1000];
+
+static enum  {
+       TE_DONTCARE, TE_QP, TE_BASE64,
+} transfer_encoding;
+static char charset[256];
+
+static char multipart_boundary[1000];
+static int multipart_boundary_len;
+static int patch_lines = 0;
+
+static char *sanity_check(char *name, char *email)
+{
+       int len = strlen(name);
+       if (len < 3 || len > 60)
+               return email;
+       if (strchr(name, '@') || strchr(name, '<') || strchr(name, '>'))
+               return email;
+       return name;
+}
+
+static int bogus_from(char *line)
+{
+       /* John Doe <johndoe> */
+       char *bra, *ket, *dst, *cp;
+
+       /* This is fallback, so do not bother if we already have an
+        * e-mail address.
+        */
+       if (*email)
+               return 0;
+
+       bra = strchr(line, '<');
+       if (!bra)
+               return 0;
+       ket = strchr(bra, '>');
+       if (!ket)
+               return 0;
+
+       for (dst = email, cp = bra+1; cp < ket; )
+               *dst++ = *cp++;
+       *dst = 0;
+       for (cp = line; isspace(*cp); cp++)
+               ;
+       for (bra--; isspace(*bra); bra--)
+               *bra = 0;
+       cp = sanity_check(cp, email);
+       strcpy(name, cp);
+       return 1;
+}
+
+static int handle_from(char *in_line)
+{
+       char line[1000];
+       char *at;
+       char *dst;
+
+       strcpy(line, in_line);
+       at = strchr(line, '@');
+       if (!at)
+               return bogus_from(line);
+
+       /*
+        * If we already have one email, don't take any confusing lines
+        */
+       if (*email && strchr(at+1, '@'))
+               return 0;
+
+       /* Pick up the string around '@', possibly delimited with <>
+        * pair; that is the email part.  White them out while copying.
+        */
+       while (at > line) {
+               char c = at[-1];
+               if (isspace(c))
+                       break;
+               if (c == '<') {
+                       at[-1] = ' ';
+                       break;
+               }
+               at--;
+       }
+       dst = email;
+       for (;;) {
+               unsigned char c = *at;
+               if (!c || c == '>' || isspace(c)) {
+                       if (c == '>')
+                               *at = ' ';
+                       break;
+               }
+               *at++ = ' ';
+               *dst++ = c;
+       }
+       *dst++ = 0;
+
+       /* The remainder is name.  It could be "John Doe <john.doe@xz>"
+        * or "john.doe@xz (John Doe)", but we have whited out the
+        * email part, so trim from both ends, possibly removing
+        * the () pair at the end.
+        */
+       at = line + strlen(line);
+       while (at > line) {
+               unsigned char c = *--at;
+               if (!isspace(c)) {
+                       at[(c == ')') ? 0 : 1] = 0;
+                       break;
+               }
+       }
+
+       at = line;
+       for (;;) {
+               unsigned char c = *at;
+               if (!c || !isspace(c)) {
+                       if (c == '(')
+                               at++;
+                       break;
+               }
+               at++;
+       }
+       at = sanity_check(at, email);
+       strcpy(name, at);
+       return 1;
+}
+
+static int handle_date(char *line)
+{
+       strcpy(date, line);
+       return 0;
+}
+
+static int handle_subject(char *line)
+{
+       strcpy(subject, line);
+       return 0;
+}
+
+/* NOTE NOTE NOTE.  We do not claim we do full MIME.  We just attempt
+ * to have enough heuristics to grok MIME encoded patches often found
+ * on our mailing lists.  For example, we do not even treat header lines
+ * case insensitively.
+ */
+
+static int slurp_attr(const char *line, const char *name, char *attr)
+{
+       char *ends, *ap = strcasestr(line, name);
+       size_t sz;
+
+       if (!ap) {
+               *attr = 0;
+               return 0;
+       }
+       ap += strlen(name);
+       if (*ap == '"') {
+               ap++;
+               ends = "\"";
+       }
+       else
+               ends = "; \t";
+       sz = strcspn(ap, ends);
+       memcpy(attr, ap, sz);
+       attr[sz] = 0;
+       return 1;
+}
+
+static int handle_subcontent_type(char *line)
+{
+       /* We do not want to mess with boundary.  Note that we do not
+        * handle nested multipart.
+        */
+       if (strcasestr(line, "boundary=")) {
+               fprintf(stderr, "Not handling nested multipart message.\n");
+               exit(1);
+       }
+       slurp_attr(line, "charset=", charset);
+       if (*charset) {
+               int i, c;
+               for (i = 0; (c = charset[i]) != 0; i++)
+                       charset[i] = tolower(c);
+       }
+       return 0;
+}
+
+static int handle_content_type(char *line)
+{
+       *multipart_boundary = 0;
+       if (slurp_attr(line, "boundary=", multipart_boundary + 2)) {
+               memcpy(multipart_boundary, "--", 2);
+               multipart_boundary_len = strlen(multipart_boundary);
+       }
+       slurp_attr(line, "charset=", charset);
+       return 0;
+}
+
+static int handle_content_transfer_encoding(char *line)
+{
+       if (strcasestr(line, "base64"))
+               transfer_encoding = TE_BASE64;
+       else if (strcasestr(line, "quoted-printable"))
+               transfer_encoding = TE_QP;
+       else
+               transfer_encoding = TE_DONTCARE;
+       return 0;
+}
+
+static int is_multipart_boundary(const char *line)
+{
+       return (!memcmp(line, multipart_boundary, multipart_boundary_len));
+}
+
+static int eatspace(char *line)
+{
+       int len = strlen(line);
+       while (len > 0 && isspace(line[len-1]))
+               line[--len] = 0;
+       return len;
+}
+
+#define SEEN_FROM 01
+#define SEEN_DATE 02
+#define SEEN_SUBJECT 04
+#define SEEN_BOGUS_UNIX_FROM 010
+#define SEEN_PREFIX  020
+
+/* First lines of body can have From:, Date:, and Subject: or empty */
+static void handle_inbody_header(int *seen, char *line)
+{
+       if (*seen & SEEN_PREFIX)
+               return;
+       if (isspace(*line)) {
+               char *cp;
+               for (cp = line + 1; *cp; cp++) {
+                       if (!isspace(*cp))
+                               break;
+               }
+               if (!*cp)
+                       return;
+       }
+       if (!memcmp(">From", line, 5) && isspace(line[5])) {
+               if (!(*seen & SEEN_BOGUS_UNIX_FROM)) {
+                       *seen |= SEEN_BOGUS_UNIX_FROM;
+                       return;
+               }
+       }
+       if (!memcmp("From:", line, 5) && isspace(line[5])) {
+               if (!(*seen & SEEN_FROM) && handle_from(line+6)) {
+                       *seen |= SEEN_FROM;
+                       return;
+               }
+       }
+       if (!memcmp("Date:", line, 5) && isspace(line[5])) {
+               if (!(*seen & SEEN_DATE)) {
+                       handle_date(line+6);
+                       *seen |= SEEN_DATE;
+                       return;
+               }
+       }
+       if (!memcmp("Subject:", line, 8) && isspace(line[8])) {
+               if (!(*seen & SEEN_SUBJECT)) {
+                       handle_subject(line+9);
+                       *seen |= SEEN_SUBJECT;
+                       return;
+               }
+       }
+       if (!memcmp("[PATCH]", line, 7) && isspace(line[7])) {
+               if (!(*seen & SEEN_SUBJECT)) {
+                       handle_subject(line);
+                       *seen |= SEEN_SUBJECT;
+                       return;
+               }
+       }
+       *seen |= SEEN_PREFIX;
+}
+
+static char *cleanup_subject(char *subject)
+{
+       if (keep_subject)
+               return subject;
+       for (;;) {
+               char *p;
+               int len, remove;
+               switch (*subject) {
+               case 'r': case 'R':
+                       if (!memcmp("e:", subject+1, 2)) {
+                               subject +=3;
+                               continue;
+                       }
+                       break;
+               case ' ': case '\t': case ':':
+                       subject++;
+                       continue;
+
+               case '[':
+                       p = strchr(subject, ']');
+                       if (!p) {
+                               subject++;
+                               continue;
+                       }
+                       len = strlen(p);
+                       remove = p - subject;
+                       if (remove <= len *2) {
+                               subject = p+1;
+                               continue;
+                       }
+                       break;
+               }
+               eatspace(subject);
+               return subject;
+       }
+}
+
+static void cleanup_space(char *buf)
+{
+       unsigned char c;
+       while ((c = *buf) != 0) {
+               buf++;
+               if (isspace(c)) {
+                       buf[-1] = ' ';
+                       c = *buf;
+                       while (isspace(c)) {
+                               int len = strlen(buf);
+                               memmove(buf, buf+1, len);
+                               c = *buf;
+                       }
+               }
+       }
+}
+
+static void decode_header_bq(char *it);
+typedef int (*header_fn_t)(char *);
+struct header_def {
+       const char *name;
+       header_fn_t func;
+       int namelen;
+};
+
+static void check_header(char *line, struct header_def *header)
+{
+       int i;
+
+       if (header[0].namelen <= 0) {
+               for (i = 0; header[i].name; i++)
+                       header[i].namelen = strlen(header[i].name);
+       }
+       for (i = 0; header[i].name; i++) {
+               int len = header[i].namelen;
+               if (!strncasecmp(line, header[i].name, len) &&
+                   line[len] == ':' && isspace(line[len + 1])) {
+                       /* Unwrap inline B and Q encoding, and optionally
+                        * normalize the meta information to utf8.
+                        */
+                       decode_header_bq(line + len + 2);
+                       header[i].func(line + len + 2);
+                       break;
+               }
+       }
+}
+
+static void check_subheader_line(char *line)
+{
+       static struct header_def header[] = {
+               { "Content-Type", handle_subcontent_type },
+               { "Content-Transfer-Encoding",
+                 handle_content_transfer_encoding },
+               { NULL },
+       };
+       check_header(line, header);
+}
+static void check_header_line(char *line)
+{
+       static struct header_def header[] = {
+               { "From", handle_from },
+               { "Date", handle_date },
+               { "Subject", handle_subject },
+               { "Content-Type", handle_content_type },
+               { "Content-Transfer-Encoding",
+                 handle_content_transfer_encoding },
+               { NULL },
+       };
+       check_header(line, header);
+}
+
+static int is_rfc2822_header(char *line)
+{
+       /*
+        * The section that defines the loosest possible
+        * field name is "3.6.8 Optional fields".
+        *
+        * optional-field = field-name ":" unstructured CRLF
+        * field-name = 1*ftext
+        * ftext = %d33-57 / %59-126
+        */
+       int ch;
+       char *cp = line;
+       while ((ch = *cp++)) {
+               if (ch == ':')
+                       return cp != line;
+               if ((33 <= ch && ch <= 57) ||
+                   (59 <= ch && ch <= 126))
+                       continue;
+               break;
+       }
+       return 0;
+}
+
+static int read_one_header_line(char *line, int sz, FILE *in)
+{
+       int ofs = 0;
+       while (ofs < sz) {
+               int peek, len;
+               if (fgets(line + ofs, sz - ofs, in) == NULL)
+                       break;
+               len = eatspace(line + ofs);
+               if ((len == 0) || !is_rfc2822_header(line)) {
+                       /* Re-add the newline */
+                       line[ofs + len] = '\n';
+                       line[ofs + len + 1] = '\0';
+                       break;
+               }
+               ofs += len;
+               /* Yuck, 2822 header "folding" */
+               peek = fgetc(in); ungetc(peek, in);
+               if (peek != ' ' && peek != '\t')
+                       break;
+       }
+       /* Count mbox From headers as headers */
+       if (!ofs && !memcmp(line, "From ", 5))
+               ofs = 1;
+       return ofs;
+}
+
+static unsigned hexval(int c)
+{
+       if (c >= '0' && c <= '9')
+               return c - '0';
+       if (c >= 'a' && c <= 'f')
+               return c - 'a' + 10;
+       if (c >= 'A' && c <= 'F')
+               return c - 'A' + 10;
+       return ~0;
+}
+
+static int decode_q_segment(char *in, char *ot, char *ep, int rfc2047)
+{
+       int c;
+       while ((c = *in++) != 0 && (in <= ep)) {
+               if (c == '=') {
+                       int d = *in++;
+                       if (d == '\n' || !d)
+                               break; /* drop trailing newline */
+                       *ot++ = ((hexval(d) << 4) | hexval(*in++));
+                       continue;
+               }
+               if (rfc2047 && c == '_') /* rfc2047 4.2 (2) */
+                       c = 0x20;
+               *ot++ = c;
+       }
+       *ot = 0;
+       return 0;
+}
+
+static int decode_b_segment(char *in, char *ot, char *ep)
+{
+       /* Decode in..ep, possibly in-place to ot */
+       int c, pos = 0, acc = 0;
+
+       while ((c = *in++) != 0 && (in <= ep)) {
+               if (c == '+')
+                       c = 62;
+               else if (c == '/')
+                       c = 63;
+               else if ('A' <= c && c <= 'Z')
+                       c -= 'A';
+               else if ('a' <= c && c <= 'z')
+                       c -= 'a' - 26;
+               else if ('0' <= c && c <= '9')
+                       c -= '0' - 52;
+               else if (c == '=') {
+                       /* padding is almost like (c == 0), except we do
+                        * not output NUL resulting only from it;
+                        * for now we just trust the data.
+                        */
+                       c = 0;
+               }
+               else
+                       continue; /* garbage */
+               switch (pos++) {
+               case 0:
+                       acc = (c << 2);
+                       break;
+               case 1:
+                       *ot++ = (acc | (c >> 4));
+                       acc = (c & 15) << 4;
+                       break;
+               case 2:
+                       *ot++ = (acc | (c >> 2));
+                       acc = (c & 3) << 6;
+                       break;
+               case 3:
+                       *ot++ = (acc | c);
+                       acc = pos = 0;
+                       break;
+               }
+       }
+       *ot = 0;
+       return 0;
+}
+
+static void convert_to_utf8(char *line, char *charset)
+{
+#ifndef NO_ICONV
+       char *in, *out;
+       size_t insize, outsize, nrc;
+       char outbuf[4096]; /* cheat */
+       static char latin_one[] = "latin1";
+       char *input_charset = *charset ? charset : latin_one;
+       iconv_t conv = iconv_open(metainfo_charset, input_charset);
+
+       if (conv == (iconv_t) -1) {
+               static int warned_latin1_once = 0;
+               if (input_charset != latin_one) {
+                       fprintf(stderr, "cannot convert from %s to %s\n",
+                               input_charset, metainfo_charset);
+                       *charset = 0;
+               }
+               else if (!warned_latin1_once) {
+                       warned_latin1_once = 1;
+                       fprintf(stderr, "tried to convert from %s to %s, "
+                               "but your iconv does not work with it.\n",
+                               input_charset, metainfo_charset);
+               }
+               return;
+       }
+       in = line;
+       insize = strlen(in);
+       out = outbuf;
+       outsize = sizeof(outbuf);
+       nrc = iconv(conv, &in, &insize, &out, &outsize);
+       iconv_close(conv);
+       if (nrc == (size_t) -1)
+               return;
+       *out = 0;
+       strcpy(line, outbuf);
+#endif
+}
+
+static void decode_header_bq(char *it)
+{
+       char *in, *out, *ep, *cp, *sp;
+       char outbuf[1000];
+
+       in = it;
+       out = outbuf;
+       while ((ep = strstr(in, "=?")) != NULL) {
+               int sz, encoding;
+               char charset_q[256], piecebuf[256];
+               if (in != ep) {
+                       sz = ep - in;
+                       memcpy(out, in, sz);
+                       out += sz;
+                       in += sz;
+               }
+               /* E.g.
+                * ep : "=?iso-2022-jp?B?GyR...?= foo"
+                * ep : "=?ISO-8859-1?Q?Foo=FCbar?= baz"
+                */
+               ep += 2;
+               cp = strchr(ep, '?');
+               if (!cp)
+                       return; /* no munging */
+               for (sp = ep; sp < cp; sp++)
+                       charset_q[sp - ep] = tolower(*sp);
+               charset_q[cp - ep] = 0;
+               encoding = cp[1];
+               if (!encoding || cp[2] != '?')
+                       return; /* no munging */
+               ep = strstr(cp + 3, "?=");
+               if (!ep)
+                       return; /* no munging */
+               switch (tolower(encoding)) {
+               default:
+                       return; /* no munging */
+               case 'b':
+                       sz = decode_b_segment(cp + 3, piecebuf, ep);
+                       break;
+               case 'q':
+                       sz = decode_q_segment(cp + 3, piecebuf, ep, 1);
+                       break;
+               }
+               if (sz < 0)
+                       return;
+               if (metainfo_charset)
+                       convert_to_utf8(piecebuf, charset_q);
+               strcpy(out, piecebuf);
+               out += strlen(out);
+               in = ep + 2;
+       }
+       strcpy(out, in);
+       strcpy(it, outbuf);
+}
+
+static void decode_transfer_encoding(char *line)
+{
+       char *ep;
+
+       switch (transfer_encoding) {
+       case TE_QP:
+               ep = line + strlen(line);
+               decode_q_segment(line, line, ep, 0);
+               break;
+       case TE_BASE64:
+               ep = line + strlen(line);
+               decode_b_segment(line, line, ep);
+               break;
+       case TE_DONTCARE:
+               break;
+       }
+}
+
+static void handle_info(void)
+{
+       char *sub;
+
+       sub = cleanup_subject(subject);
+       cleanup_space(name);
+       cleanup_space(date);
+       cleanup_space(email);
+       cleanup_space(sub);
+
+       fprintf(fout, "Author: %s\nEmail: %s\nSubject: %s\nDate: %s\n\n",
+              name, email, sub, date);
+}
+
+/* We are inside message body and have read line[] already.
+ * Spit out the commit log.
+ */
+static int handle_commit_msg(int *seen)
+{
+       if (!cmitmsg)
+               return 0;
+       do {
+               if (!memcmp("diff -", line, 6) ||
+                   !memcmp("---", line, 3) ||
+                   !memcmp("Index: ", line, 7))
+                       break;
+               if ((multipart_boundary[0] && is_multipart_boundary(line))) {
+                       /* We come here when the first part had only
+                        * the commit message without any patch.  We
+                        * pretend we have not seen this line yet, and
+                        * go back to the loop.
+                        */
+                       return 1;
+               }
+
+               /* Unwrap transfer encoding and optionally
+                * normalize the log message to UTF-8.
+                */
+               decode_transfer_encoding(line);
+               if (metainfo_charset)
+                       convert_to_utf8(line, charset);
+
+               handle_inbody_header(seen, line);
+               if (!(*seen & SEEN_PREFIX))
+                       continue;
+
+               fputs(line, cmitmsg);
+       } while (fgets(line, sizeof(line), fin) != NULL);
+       fclose(cmitmsg);
+       cmitmsg = NULL;
+       return 0;
+}
+
+/* We have done the commit message and have the first
+ * line of the patch in line[].
+ */
+static void handle_patch(void)
+{
+       do {
+               if (multipart_boundary[0] && is_multipart_boundary(line))
+                       break;
+               /* Only unwrap transfer encoding but otherwise do not
+                * do anything.  We do *NOT* want UTF-8 conversion
+                * here; we are dealing with the user payload.
+                */
+               decode_transfer_encoding(line);
+               fputs(line, patchfile);
+               patch_lines++;
+       } while (fgets(line, sizeof(line), fin) != NULL);
+}
+
+/* multipart boundary and transfer encoding are set up for us, and we
+ * are at the end of the sub header.  do equivalent of handle_body up
+ * to the next boundary without closing patchfile --- we will expect
+ * that the first part to contain commit message and a patch, and
+ * handle other parts as pure patches.
+ */
+static int handle_multipart_one_part(int *seen)
+{
+       int n = 0;
+
+       while (fgets(line, sizeof(line), fin) != NULL) {
+       again:
+               n++;
+               if (is_multipart_boundary(line))
+                       break;
+               if (handle_commit_msg(seen))
+                       goto again;
+               handle_patch();
+               break;
+       }
+       if (n == 0)
+               return -1;
+       return 0;
+}
+
+static void handle_multipart_body(void)
+{
+       int seen = 0;
+       int part_num = 0;
+
+       /* Skip up to the first boundary */
+       while (fgets(line, sizeof(line), fin) != NULL)
+               if (is_multipart_boundary(line)) {
+                       part_num = 1;
+                       break;
+               }
+       if (!part_num)
+               return;
+       /* We are on boundary line.  Start slurping the subhead. */
+       while (1) {
+               int hdr = read_one_header_line(line, sizeof(line), fin);
+               if (!hdr) {
+                       if (handle_multipart_one_part(&seen) < 0)
+                               return;
+                       /* Reset per part headers */
+                       transfer_encoding = TE_DONTCARE;
+                       charset[0] = 0;
+               }
+               else
+                       check_subheader_line(line);
+       }
+       fclose(patchfile);
+       if (!patch_lines) {
+               fprintf(stderr, "No patch found\n");
+               exit(1);
+       }
+}
+
+/* Non multipart message */
+static void handle_body(void)
+{
+       int seen = 0;
+
+       handle_commit_msg(&seen);
+       handle_patch();
+       fclose(patchfile);
+       if (!patch_lines) {
+               fprintf(stderr, "No patch found\n");
+               exit(1);
+       }
+}
+
+int mailinfo(FILE *in, FILE *out, int ks, const char *encoding,
+            const char *msg, const char *patch)
+{
+       keep_subject = ks;
+       metainfo_charset = encoding;
+       fin = in;
+       fout = out;
+
+       cmitmsg = fopen(msg, "w");
+       if (!cmitmsg) {
+               perror(msg);
+               return -1;
+       }
+       patchfile = fopen(patch, "w");
+       if (!patchfile) {
+               perror(patch);
+               fclose(cmitmsg);
+               return -1;
+       }
+       while (1) {
+               int hdr = read_one_header_line(line, sizeof(line), fin);
+               if (!hdr) {
+                       if (multipart_boundary[0])
+                               handle_multipart_body();
+                       else
+                               handle_body();
+                       handle_info();
+                       break;
+               }
+               check_header_line(line);
+       }
+
+       return 0;
+}
+
+static const char mailinfo_usage[] =
+       "git-mailinfo [-k] [-u | --encoding=<encoding>] msg patch <mail >info";
+
+int cmd_mailinfo(int argc, const char **argv, char **envp)
+{
+       /* NEEDSWORK: might want to do the optional .git/ directory
+        * discovery
+        */
+       git_config(git_default_config);
+
+       while (1 < argc && argv[1][0] == '-') {
+               if (!strcmp(argv[1], "-k"))
+                       keep_subject = 1;
+               else if (!strcmp(argv[1], "-u"))
+                       metainfo_charset = git_commit_encoding;
+               else if (!strncmp(argv[1], "--encoding=", 11))
+                       metainfo_charset = argv[1] + 11;
+               else
+                       usage(mailinfo_usage);
+               argc--; argv++;
+       }
+
+       if (argc != 3)
+               usage(mailinfo_usage);
+
+       return !!mailinfo(stdin, stdout, keep_subject, metainfo_charset, argv[1], argv[2]);
+}
diff --git a/builtin-mailsplit.c b/builtin-mailsplit.c
new file mode 100644 (file)
index 0000000..e2a0058
--- /dev/null
@@ -0,0 +1,201 @@
+/*
+ * Totally braindamaged mbox splitter program.
+ *
+ * It just splits a mbox into a list of files: "0001" "0002" ..
+ * so you can process them further from there.
+ */
+#include <unistd.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <stdio.h>
+#include "cache.h"
+#include "builtin.h"
+
+static const char git_mailsplit_usage[] =
+"git-mailsplit [-d<prec>] [-f<n>] [-b] -o<directory> <mbox>...";
+
+static int is_from_line(const char *line, int len)
+{
+       const char *colon;
+
+       if (len < 20 || memcmp("From ", line, 5))
+               return 0;
+
+       colon = line + len - 2;
+       line += 5;
+       for (;;) {
+               if (colon < line)
+                       return 0;
+               if (*--colon == ':')
+                       break;
+       }
+
+       if (!isdigit(colon[-4]) ||
+           !isdigit(colon[-2]) ||
+           !isdigit(colon[-1]) ||
+           !isdigit(colon[ 1]) ||
+           !isdigit(colon[ 2]))
+               return 0;
+
+       /* year */
+       if (strtol(colon+3, NULL, 10) <= 90)
+               return 0;
+
+       /* Ok, close enough */
+       return 1;
+}
+
+/* Could be as small as 64, enough to hold a Unix "From " line. */
+static char buf[4096];
+
+/* Called with the first line (potentially partial)
+ * already in buf[] -- normally that should begin with
+ * the Unix "From " line.  Write it into the specified
+ * file.
+ */
+static int split_one(FILE *mbox, const char *name, int allow_bare)
+{
+       FILE *output = NULL;
+       int len = strlen(buf);
+       int fd;
+       int status = 0;
+       int is_bare = !is_from_line(buf, len);
+
+       if (is_bare && !allow_bare)
+               goto corrupt;
+
+       fd = open(name, O_WRONLY | O_CREAT | O_EXCL, 0666);
+       if (fd < 0)
+               die("cannot open output file %s", name);
+       output = fdopen(fd, "w");
+
+       /* Copy it out, while searching for a line that begins with
+        * "From " and having something that looks like a date format.
+        */
+       for (;;) {
+               int is_partial = (buf[len-1] != '\n');
+
+               if (fputs(buf, output) == EOF)
+                       die("cannot write output");
+
+               if (fgets(buf, sizeof(buf), mbox) == NULL) {
+                       if (feof(mbox)) {
+                               status = 1;
+                               break;
+                       }
+                       die("cannot read mbox");
+               }
+               len = strlen(buf);
+               if (!is_partial && !is_bare && is_from_line(buf, len))
+                       break; /* done with one message */
+       }
+       fclose(output);
+       return status;
+
+ corrupt:
+       if (output)
+               fclose(output);
+       unlink(name);
+       fprintf(stderr, "corrupt mailbox\n");
+       exit(1);
+}
+
+int split_mbox(const char **mbox, const char *dir, int allow_bare, int nr_prec, int skip)
+{
+       char *name = xmalloc(strlen(dir) + 2 + 3 * sizeof(skip));
+       int ret = -1;
+
+       while (*mbox) {
+               const char *file = *mbox++;
+               FILE *f = !strcmp(file, "-") ? stdin : fopen(file, "r");
+               int file_done = 0;
+
+               if ( !f ) {
+                       error("cannot open mbox %s", file);
+                       goto out;
+               }
+
+               if (fgets(buf, sizeof(buf), f) == NULL) {
+                       if (f == stdin)
+                               break; /* empty stdin is OK */
+                       error("cannot read mbox %s", file);
+                       goto out;
+               }
+
+               while (!file_done) {
+                       sprintf(name, "%s/%0*d", dir, nr_prec, ++skip);
+                       file_done = split_one(f, name, allow_bare);
+               }
+
+               if (f != stdin)
+                       fclose(f);
+       }
+       ret = skip;
+out:
+       free(name);
+       return ret;
+}
+int cmd_mailsplit(int argc, const char **argv, char **envp)
+{
+       int nr = 0, nr_prec = 4, ret;
+       int allow_bare = 0;
+       const char *dir = NULL;
+       const char **argp;
+       static const char *stdin_only[] = { "-", NULL };
+
+       for (argp = argv+1; *argp; argp++) {
+               const char *arg = *argp;
+
+               if (arg[0] != '-')
+                       break;
+               /* do flags here */
+               if ( arg[1] == 'd' ) {
+                       nr_prec = strtol(arg+2, NULL, 10);
+                       if (nr_prec < 3 || 10 <= nr_prec)
+                               usage(git_mailsplit_usage);
+                       continue;
+               } else if ( arg[1] == 'f' ) {
+                       nr = strtol(arg+2, NULL, 10);
+               } else if ( arg[1] == 'b' && !arg[2] ) {
+                       allow_bare = 1;
+               } else if ( arg[1] == 'o' && arg[2] ) {
+                       dir = arg+2;
+               } else if ( arg[1] == '-' && !arg[2] ) {
+                       argp++; /* -- marks end of options */
+                       break;
+               } else {
+                       die("unknown option: %s", arg);
+               }
+       }
+
+       if ( !dir ) {
+               /* Backwards compatibility: if no -o specified, accept
+                  <mbox> <dir> or just <dir> */
+               switch (argc - (argp-argv)) {
+               case 1:
+                       dir = argp[0];
+                       argp = stdin_only;
+                       break;
+               case 2:
+                       stdin_only[0] = argp[0];
+                       dir = argp[1];
+                       argp = stdin_only;
+                       break;
+               default:
+                       usage(git_mailsplit_usage);
+               }
+       } else {
+               /* New usage: if no more argument, parse stdin */
+               if ( !*argp )
+                       argp = stdin_only;
+       }
+
+       ret = split_mbox(argp, dir, allow_bare, nr_prec, nr);
+       if (ret != -1)
+               printf("%d\n", ret);
+
+       return ret == -1;
+}
index 04506da892c1bfb198c706e166efe8a2bf6cf1be..9a2099d7307903d62c85f3483a22969ab5094c84 100644 (file)
@@ -31,8 +31,7 @@ static int merge_size = 0;
 
 static struct object_list *trees = NULL;
 
-static struct cache_entry df_conflict_entry = {
-};
+static struct cache_entry df_conflict_entry;
 
 struct tree_entry_list {
        struct tree_entry_list *next;
index 71353eb19db158a60a5ae94f83f53059adc3399b..63bad0e96ade657bef21d9ca0e0de2e0ba8af61c 100644 (file)
@@ -99,26 +99,26 @@ static void show_commit(struct commit *commit)
        }
 }
 
-static struct object_list **process_blob(struct blob *blob,
-                                        struct object_list **p,
-                                        struct name_path *path,
-                                        const char *name)
+static void process_blob(struct blob *blob,
+                        struct object_array *p,
+                        struct name_path *path,
+                        const char *name)
 {
        struct object *obj = &blob->object;
 
        if (!revs.blob_objects)
-               return p;
+               return;
        if (obj->flags & (UNINTERESTING | SEEN))
-               return p;
+               return;
        obj->flags |= SEEN;
        name = strdup(name);
-       return add_object(obj, p, path, name);
+       add_object(obj, p, path, name);
 }
 
-static struct object_list **process_tree(struct tree *tree,
-                                        struct object_list **p,
-                                        struct name_path *path,
-                                        const char *name)
+static void process_tree(struct tree *tree,
+                        struct object_array *p,
+                        struct name_path *path,
+                        const char *name)
 {
        struct object *obj = &tree->object;
        struct tree_desc desc;
@@ -126,14 +126,14 @@ static struct object_list **process_tree(struct tree *tree,
        struct name_path me;
 
        if (!revs.tree_objects)
-               return p;
+               return;
        if (obj->flags & (UNINTERESTING | SEEN))
-               return p;
+               return;
        if (parse_tree(tree) < 0)
                die("bad tree object %s", sha1_to_hex(obj->sha1));
        obj->flags |= SEEN;
        name = strdup(name);
-       p = add_object(obj, p, path, name);
+       add_object(obj, p, path, name);
        me.up = path;
        me.elem = name;
        me.elem_len = strlen(name);
@@ -143,57 +143,59 @@ static struct object_list **process_tree(struct tree *tree,
 
        while (tree_entry(&desc, &entry)) {
                if (S_ISDIR(entry.mode))
-                       p = process_tree(lookup_tree(entry.sha1), p, &me, entry.path);
+                       process_tree(lookup_tree(entry.sha1), p, &me, entry.path);
                else
-                       p = process_blob(lookup_blob(entry.sha1), p, &me, entry.path);
+                       process_blob(lookup_blob(entry.sha1), p, &me, entry.path);
        }
        free(tree->buffer);
        tree->buffer = NULL;
-       return p;
 }
 
 static void show_commit_list(struct rev_info *revs)
 {
+       int i;
        struct commit *commit;
-       struct object_list *objects = NULL, **p = &objects, *pending;
+       struct object_array objects = { 0, 0, NULL };
 
        while ((commit = get_revision(revs)) != NULL) {
-               p = process_tree(commit->tree, p, NULL, "");
+               process_tree(commit->tree, &objects, NULL, "");
                show_commit(commit);
        }
-       for (pending = revs->pending_objects; pending; pending = pending->next) {
+       for (i = 0; i < revs->pending.nr; i++) {
+               struct object_array_entry *pending = revs->pending.objects + i;
                struct object *obj = pending->item;
                const char *name = pending->name;
                if (obj->flags & (UNINTERESTING | SEEN))
                        continue;
                if (obj->type == TYPE_TAG) {
                        obj->flags |= SEEN;
-                       p = add_object(obj, p, NULL, name);
+                       add_object_array(obj, name, &objects);
                        continue;
                }
                if (obj->type == TYPE_TREE) {
-                       p = process_tree((struct tree *)obj, p, NULL, name);
+                       process_tree((struct tree *)obj, &objects, NULL, name);
                        continue;
                }
                if (obj->type == TYPE_BLOB) {
-                       p = process_blob((struct blob *)obj, p, NULL, name);
+                       process_blob((struct blob *)obj, &objects, NULL, name);
                        continue;
                }
                die("unknown pending object %s (%s)", sha1_to_hex(obj->sha1), name);
        }
-       while (objects) {
+       for (i = 0; i < objects.nr; i++) {
+               struct object_array_entry *p = objects.objects + i;
+
                /* An object with name "foo\n0000000..." can be used to
                 * confuse downstream git-pack-objects very badly.
                 */
-               const char *ep = strchr(objects->name, '\n');
+               const char *ep = strchr(p->name, '\n');
                if (ep) {
-                       printf("%s %.*s\n", sha1_to_hex(objects->item->sha1),
-                              (int) (ep - objects->name),
-                              objects->name);
+                       printf("%s %.*s\n", sha1_to_hex(p->item->sha1),
+                              (int) (ep - p->name),
+                              p->name);
                }
                else
-                       printf("%s %s\n", sha1_to_hex(objects->item->sha1), objects->name);
-               objects = objects->next;
+                       printf("%s %s\n", sha1_to_hex(p->item->sha1), p->name);
        }
 }
 
@@ -348,7 +350,7 @@ int cmd_rev_list(int argc, const char **argv, char **envp)
 
        if ((!list &&
             (!(revs.tag_objects||revs.tree_objects||revs.blob_objects) &&
-             !revs.pending_objects)) ||
+             !revs.pending.nr)) ||
            revs.diff)
                usage(rev_list_usage);
 
diff --git a/builtin-stripspace.c b/builtin-stripspace.c
new file mode 100644 (file)
index 0000000..2ce1264
--- /dev/null
@@ -0,0 +1,61 @@
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include "builtin.h"
+
+/*
+ * Remove empty lines from the beginning and end.
+ *
+ * Turn multiple consecutive empty lines into just one
+ * empty line.  Return true if it is an incomplete line.
+ */
+static int cleanup(char *line)
+{
+       int len = strlen(line);
+
+       if (len && line[len-1] == '\n') {
+               if (len == 1)
+                       return 0;
+               do {
+                       unsigned char c = line[len-2];
+                       if (!isspace(c))
+                               break;
+                       line[len-2] = '\n';
+                       len--;
+                       line[len] = 0;
+               } while (len > 1);
+               return 0;
+       }
+       return 1;
+}
+
+void stripspace(FILE *in, FILE *out)
+{
+       int empties = -1;
+       int incomplete = 0;
+       char line[1024];
+
+       while (fgets(line, sizeof(line), in)) {
+               incomplete = cleanup(line);
+
+               /* Not just an empty line? */
+               if (line[0] != '\n') {
+                       if (empties > 0)
+                               fputc('\n', out);
+                       empties = 0;
+                       fputs(line, out);
+                       continue;
+               }
+               if (empties < 0)
+                       continue;
+               empties++;
+       }
+       if (incomplete)
+               fputc('\n', out);
+}
+
+int cmd_stripspace(int argc, const char **argv, char **envp)
+{
+       stripspace(stdin, stdout);
+       return 0;
+}
diff --git a/builtin-update-index.c b/builtin-update-index.c
new file mode 100644 (file)
index 0000000..ef50243
--- /dev/null
@@ -0,0 +1,658 @@
+/*
+ * GIT - The information manager from hell
+ *
+ * Copyright (C) Linus Torvalds, 2005
+ */
+#include "cache.h"
+#include "strbuf.h"
+#include "quote.h"
+#include "cache-tree.h"
+#include "tree-walk.h"
+#include "builtin.h"
+
+/*
+ * Default to not allowing changes to the list of files. The
+ * tool doesn't actually care, but this makes it harder to add
+ * files to the revision control by mistake by doing something
+ * like "git-update-index *" and suddenly having all the object
+ * files be revision controlled.
+ */
+static int allow_add;
+static int allow_remove;
+static int allow_replace;
+static int info_only;
+static int force_remove;
+static int verbose;
+static int mark_valid_only = 0;
+#define MARK_VALID 1
+#define UNMARK_VALID 2
+
+static void report(const char *fmt, ...)
+{
+       va_list vp;
+
+       if (!verbose)
+               return;
+
+       va_start(vp, fmt);
+       vprintf(fmt, vp);
+       putchar('\n');
+       va_end(vp);
+}
+
+static int mark_valid(const char *path)
+{
+       int namelen = strlen(path);
+       int pos = cache_name_pos(path, namelen);
+       if (0 <= pos) {
+               switch (mark_valid_only) {
+               case MARK_VALID:
+                       active_cache[pos]->ce_flags |= htons(CE_VALID);
+                       break;
+               case UNMARK_VALID:
+                       active_cache[pos]->ce_flags &= ~htons(CE_VALID);
+                       break;
+               }
+               cache_tree_invalidate_path(active_cache_tree, path);
+               active_cache_changed = 1;
+               return 0;
+       }
+       return -1;
+}
+
+static int add_file_to_cache(const char *path)
+{
+       int size, namelen, option, status;
+       struct cache_entry *ce;
+       struct stat st;
+
+       status = lstat(path, &st);
+
+       /* We probably want to do this in remove_file_from_cache() and
+        * add_cache_entry() instead...
+        */
+       cache_tree_invalidate_path(active_cache_tree, path);
+
+       if (status < 0 || S_ISDIR(st.st_mode)) {
+               /* When we used to have "path" and now we want to add
+                * "path/file", we need a way to remove "path" before
+                * being able to add "path/file".  However,
+                * "git-update-index --remove path" would not work.
+                * --force-remove can be used but this is more user
+                * friendly, especially since we can do the opposite
+                * case just fine without --force-remove.
+                */
+               if (status == 0 || (errno == ENOENT || errno == ENOTDIR)) {
+                       if (allow_remove) {
+                               if (remove_file_from_cache(path))
+                                       return error("%s: cannot remove from the index",
+                                                    path);
+                               else
+                                       return 0;
+                       } else if (status < 0) {
+                               return error("%s: does not exist and --remove not passed",
+                                            path);
+                       }
+               }
+               if (0 == status)
+                       return error("%s: is a directory - add files inside instead",
+                                    path);
+               else
+                       return error("lstat(\"%s\"): %s", path,
+                                    strerror(errno));
+       }
+
+       namelen = strlen(path);
+       size = cache_entry_size(namelen);
+       ce = xcalloc(1, size);
+       memcpy(ce->name, path, namelen);
+       ce->ce_flags = htons(namelen);
+       fill_stat_cache_info(ce, &st);
+
+       ce->ce_mode = create_ce_mode(st.st_mode);
+       if (!trust_executable_bit) {
+               /* If there is an existing entry, pick the mode bits
+                * from it.
+                */
+               int pos = cache_name_pos(path, namelen);
+               if (0 <= pos)
+                       ce->ce_mode = active_cache[pos]->ce_mode;
+       }
+
+       if (index_path(ce->sha1, path, &st, !info_only))
+               return -1;
+       option = allow_add ? ADD_CACHE_OK_TO_ADD : 0;
+       option |= allow_replace ? ADD_CACHE_OK_TO_REPLACE : 0;
+       if (add_cache_entry(ce, option))
+               return error("%s: cannot add to the index - missing --add option?",
+                            path);
+       return 0;
+}
+
+static int add_cacheinfo(unsigned int mode, const unsigned char *sha1,
+                        const char *path, int stage)
+{
+       int size, len, option;
+       struct cache_entry *ce;
+
+       if (!verify_path(path))
+               return -1;
+
+       len = strlen(path);
+       size = cache_entry_size(len);
+       ce = xcalloc(1, size);
+
+       memcpy(ce->sha1, sha1, 20);
+       memcpy(ce->name, path, len);
+       ce->ce_flags = create_ce_flags(len, stage);
+       ce->ce_mode = create_ce_mode(mode);
+       if (assume_unchanged)
+               ce->ce_flags |= htons(CE_VALID);
+       option = allow_add ? ADD_CACHE_OK_TO_ADD : 0;
+       option |= allow_replace ? ADD_CACHE_OK_TO_REPLACE : 0;
+       if (add_cache_entry(ce, option))
+               return error("%s: cannot add to the index - missing --add option?",
+                            path);
+       report("add '%s'", path);
+       cache_tree_invalidate_path(active_cache_tree, path);
+       return 0;
+}
+
+static void chmod_path(int flip, const char *path)
+{
+       int pos;
+       struct cache_entry *ce;
+       unsigned int mode;
+
+       pos = cache_name_pos(path, strlen(path));
+       if (pos < 0)
+               goto fail;
+       ce = active_cache[pos];
+       mode = ntohl(ce->ce_mode);
+       if (!S_ISREG(mode))
+               goto fail;
+       switch (flip) {
+       case '+':
+               ce->ce_mode |= htonl(0111); break;
+       case '-':
+               ce->ce_mode &= htonl(~0111); break;
+       default:
+               goto fail;
+       }
+       cache_tree_invalidate_path(active_cache_tree, path);
+       active_cache_changed = 1;
+       report("chmod %cx '%s'", flip, path);
+       return;
+ fail:
+       die("git-update-index: cannot chmod %cx '%s'", flip, path);
+}
+
+static void update_one(const char *path, const char *prefix, int prefix_length)
+{
+       const char *p = prefix_path(prefix, prefix_length, path);
+       if (!verify_path(p)) {
+               fprintf(stderr, "Ignoring path %s\n", path);
+               goto free_return;
+       }
+       if (mark_valid_only) {
+               if (mark_valid(p))
+                       die("Unable to mark file %s", path);
+               goto free_return;
+       }
+       cache_tree_invalidate_path(active_cache_tree, path);
+
+       if (force_remove) {
+               if (remove_file_from_cache(p))
+                       die("git-update-index: unable to remove %s", path);
+               report("remove '%s'", path);
+               goto free_return;
+       }
+       if (add_file_to_cache(p))
+               die("Unable to process file %s", path);
+       report("add '%s'", path);
+ free_return:
+       if (p < path || p > path + strlen(path))
+               free((char*)p);
+}
+
+static void read_index_info(int line_termination)
+{
+       struct strbuf buf;
+       strbuf_init(&buf);
+       while (1) {
+               char *ptr, *tab;
+               char *path_name;
+               unsigned char sha1[20];
+               unsigned int mode;
+               int stage;
+
+               /* This reads lines formatted in one of three formats:
+                *
+                * (1) mode         SP sha1          TAB path
+                * The first format is what "git-apply --index-info"
+                * reports, and used to reconstruct a partial tree
+                * that is used for phony merge base tree when falling
+                * back on 3-way merge.
+                *
+                * (2) mode SP type SP sha1          TAB path
+                * The second format is to stuff git-ls-tree output
+                * into the index file.
+                *
+                * (3) mode         SP sha1 SP stage TAB path
+                * This format is to put higher order stages into the
+                * index file and matches git-ls-files --stage output.
+                */
+               read_line(&buf, stdin, line_termination);
+               if (buf.eof)
+                       break;
+
+               mode = strtoul(buf.buf, &ptr, 8);
+               if (ptr == buf.buf || *ptr != ' ')
+                       goto bad_line;
+
+               tab = strchr(ptr, '\t');
+               if (!tab || tab - ptr < 41)
+                       goto bad_line;
+
+               if (tab[-2] == ' ' && '0' <= tab[-1] && tab[-1] <= '3') {
+                       stage = tab[-1] - '0';
+                       ptr = tab + 1; /* point at the head of path */
+                       tab = tab - 2; /* point at tail of sha1 */
+               }
+               else {
+                       stage = 0;
+                       ptr = tab + 1; /* point at the head of path */
+               }
+
+               if (get_sha1_hex(tab - 40, sha1) || tab[-41] != ' ')
+                       goto bad_line;
+
+               if (line_termination && ptr[0] == '"')
+                       path_name = unquote_c_style(ptr, NULL);
+               else
+                       path_name = ptr;
+
+               if (!verify_path(path_name)) {
+                       fprintf(stderr, "Ignoring path %s\n", path_name);
+                       if (path_name != ptr)
+                               free(path_name);
+                       continue;
+               }
+               cache_tree_invalidate_path(active_cache_tree, path_name);
+
+               if (!mode) {
+                       /* mode == 0 means there is no such path -- remove */
+                       if (remove_file_from_cache(path_name))
+                               die("git-update-index: unable to remove %s",
+                                   ptr);
+               }
+               else {
+                       /* mode ' ' sha1 '\t' name
+                        * ptr[-1] points at tab,
+                        * ptr[-41] is at the beginning of sha1
+                        */
+                       ptr[-42] = ptr[-1] = 0;
+                       if (add_cacheinfo(mode, sha1, path_name, stage))
+                               die("git-update-index: unable to update %s",
+                                   path_name);
+               }
+               if (path_name != ptr)
+                       free(path_name);
+               continue;
+
+       bad_line:
+               die("malformed index info %s", buf.buf);
+       }
+}
+
+static const char update_index_usage[] =
+"git-update-index [-q] [--add] [--replace] [--remove] [--unmerged] [--refresh] [--really-refresh] [--cacheinfo] [--chmod=(+|-)x] [--assume-unchanged] [--info-only] [--force-remove] [--stdin] [--index-info] [--unresolve] [--again] [--ignore-missing] [-z] [--verbose] [--] <file>...";
+
+static unsigned char head_sha1[20];
+static unsigned char merge_head_sha1[20];
+
+static struct cache_entry *read_one_ent(const char *which,
+                                       unsigned char *ent, const char *path,
+                                       int namelen, int stage)
+{
+       unsigned mode;
+       unsigned char sha1[20];
+       int size;
+       struct cache_entry *ce;
+
+       if (get_tree_entry(ent, path, sha1, &mode)) {
+               if (which)
+                       error("%s: not in %s branch.", path, which);
+               return NULL;
+       }
+       if (mode == S_IFDIR) {
+               if (which)
+                       error("%s: not a blob in %s branch.", path, which);
+               return NULL;
+       }
+       size = cache_entry_size(namelen);
+       ce = xcalloc(1, size);
+
+       memcpy(ce->sha1, sha1, 20);
+       memcpy(ce->name, path, namelen);
+       ce->ce_flags = create_ce_flags(namelen, stage);
+       ce->ce_mode = create_ce_mode(mode);
+       return ce;
+}
+
+static int unresolve_one(const char *path)
+{
+       int namelen = strlen(path);
+       int pos;
+       int ret = 0;
+       struct cache_entry *ce_2 = NULL, *ce_3 = NULL;
+
+       /* See if there is such entry in the index. */
+       pos = cache_name_pos(path, namelen);
+       if (pos < 0) {
+               /* If there isn't, either it is unmerged, or
+                * resolved as "removed" by mistake.  We do not
+                * want to do anything in the former case.
+                */
+               pos = -pos-1;
+               if (pos < active_nr) {
+                       struct cache_entry *ce = active_cache[pos];
+                       if (ce_namelen(ce) == namelen &&
+                           !memcmp(ce->name, path, namelen)) {
+                               fprintf(stderr,
+                                       "%s: skipping still unmerged path.\n",
+                                       path);
+                               goto free_return;
+                       }
+               }
+       }
+
+       /* Grab blobs from given path from HEAD and MERGE_HEAD,
+        * stuff HEAD version in stage #2,
+        * stuff MERGE_HEAD version in stage #3.
+        */
+       ce_2 = read_one_ent("our", head_sha1, path, namelen, 2);
+       ce_3 = read_one_ent("their", merge_head_sha1, path, namelen, 3);
+
+       if (!ce_2 || !ce_3) {
+               ret = -1;
+               goto free_return;
+       }
+       if (!memcmp(ce_2->sha1, ce_3->sha1, 20) &&
+           ce_2->ce_mode == ce_3->ce_mode) {
+               fprintf(stderr, "%s: identical in both, skipping.\n",
+                       path);
+               goto free_return;
+       }
+
+       cache_tree_invalidate_path(active_cache_tree, path);
+       remove_file_from_cache(path);
+       if (add_cache_entry(ce_2, ADD_CACHE_OK_TO_ADD)) {
+               error("%s: cannot add our version to the index.", path);
+               ret = -1;
+               goto free_return;
+       }
+       if (!add_cache_entry(ce_3, ADD_CACHE_OK_TO_ADD))
+               return 0;
+       error("%s: cannot add their version to the index.", path);
+       ret = -1;
+ free_return:
+       free(ce_2);
+       free(ce_3);
+       return ret;
+}
+
+static void read_head_pointers(void)
+{
+       if (read_ref(git_path("HEAD"), head_sha1))
+               die("No HEAD -- no initial commit yet?\n");
+       if (read_ref(git_path("MERGE_HEAD"), merge_head_sha1)) {
+               fprintf(stderr, "Not in the middle of a merge.\n");
+               exit(0);
+       }
+}
+
+static int do_unresolve(int ac, const char **av,
+                       const char *prefix, int prefix_length)
+{
+       int i;
+       int err = 0;
+
+       /* Read HEAD and MERGE_HEAD; if MERGE_HEAD does not exist, we
+        * are not doing a merge, so exit with success status.
+        */
+       read_head_pointers();
+
+       for (i = 1; i < ac; i++) {
+               const char *arg = av[i];
+               const char *p = prefix_path(prefix, prefix_length, arg);
+               err |= unresolve_one(p);
+               if (p < arg || p > arg + strlen(arg))
+                       free((char*)p);
+       }
+       return err;
+}
+
+static int do_reupdate(int ac, const char **av,
+                      const char *prefix, int prefix_length)
+{
+       /* Read HEAD and run update-index on paths that are
+        * merged and already different between index and HEAD.
+        */
+       int pos;
+       int has_head = 1;
+       const char **pathspec = get_pathspec(prefix, av + 1);
+
+       if (read_ref(git_path("HEAD"), head_sha1))
+               /* If there is no HEAD, that means it is an initial
+                * commit.  Update everything in the index.
+                */
+               has_head = 0;
+ redo:
+       for (pos = 0; pos < active_nr; pos++) {
+               struct cache_entry *ce = active_cache[pos];
+               struct cache_entry *old = NULL;
+               int save_nr;
+
+               if (ce_stage(ce) || !ce_path_match(ce, pathspec))
+                       continue;
+               if (has_head)
+                       old = read_one_ent(NULL, head_sha1,
+                                          ce->name, ce_namelen(ce), 0);
+               if (old && ce->ce_mode == old->ce_mode &&
+                   !memcmp(ce->sha1, old->sha1, 20)) {
+                       free(old);
+                       continue; /* unchanged */
+               }
+               /* Be careful.  The working tree may not have the
+                * path anymore, in which case, under 'allow_remove',
+                * or worse yet 'allow_replace', active_nr may decrease.
+                */
+               save_nr = active_nr;
+               update_one(ce->name + prefix_length, prefix, prefix_length);
+               if (save_nr != active_nr)
+                       goto redo;
+       }
+       return 0;
+}
+
+int cmd_update_index(int argc, const char **argv, char **envp)
+{
+       int i, newfd, entries, has_errors = 0, line_termination = '\n';
+       int allow_options = 1;
+       int read_from_stdin = 0;
+       const char *prefix = setup_git_directory();
+       int prefix_length = prefix ? strlen(prefix) : 0;
+       char set_executable_bit = 0;
+       unsigned int refresh_flags = 0;
+       struct lock_file *lock_file;
+
+       git_config(git_default_config);
+
+       /* We can't free this memory, it becomes part of a linked list parsed atexit() */
+       lock_file = xcalloc(1, sizeof(struct lock_file));
+
+       newfd = hold_lock_file_for_update(lock_file, get_index_file());
+       if (newfd < 0)
+               die("unable to create new cachefile");
+
+       entries = read_cache();
+       if (entries < 0)
+               die("cache corrupted");
+
+       for (i = 1 ; i < argc; i++) {
+               const char *path = argv[i];
+
+               if (allow_options && *path == '-') {
+                       if (!strcmp(path, "--")) {
+                               allow_options = 0;
+                               continue;
+                       }
+                       if (!strcmp(path, "-q")) {
+                               refresh_flags |= REFRESH_QUIET;
+                               continue;
+                       }
+                       if (!strcmp(path, "--add")) {
+                               allow_add = 1;
+                               continue;
+                       }
+                       if (!strcmp(path, "--replace")) {
+                               allow_replace = 1;
+                               continue;
+                       }
+                       if (!strcmp(path, "--remove")) {
+                               allow_remove = 1;
+                               continue;
+                       }
+                       if (!strcmp(path, "--unmerged")) {
+                               refresh_flags |= REFRESH_UNMERGED;
+                               continue;
+                       }
+                       if (!strcmp(path, "--refresh")) {
+                               has_errors |= refresh_cache(refresh_flags);
+                               continue;
+                       }
+                       if (!strcmp(path, "--really-refresh")) {
+                               has_errors |= refresh_cache(REFRESH_REALLY | refresh_flags);
+                               continue;
+                       }
+                       if (!strcmp(path, "--cacheinfo")) {
+                               unsigned char sha1[20];
+                               unsigned int mode;
+
+                               if (i+3 >= argc)
+                                       die("git-update-index: --cacheinfo <mode> <sha1> <path>");
+
+                               if ((sscanf(argv[i+1], "%o", &mode) != 1) ||
+                                   get_sha1_hex(argv[i+2], sha1) ||
+                                   add_cacheinfo(mode, sha1, argv[i+3], 0))
+                                       die("git-update-index: --cacheinfo"
+                                           " cannot add %s", argv[i+3]);
+                               i += 3;
+                               continue;
+                       }
+                       if (!strcmp(path, "--chmod=-x") ||
+                           !strcmp(path, "--chmod=+x")) {
+                               if (argc <= i+1)
+                                       die("git-update-index: %s <path>", path);
+                               set_executable_bit = path[8];
+                               continue;
+                       }
+                       if (!strcmp(path, "--assume-unchanged")) {
+                               mark_valid_only = MARK_VALID;
+                               continue;
+                       }
+                       if (!strcmp(path, "--no-assume-unchanged")) {
+                               mark_valid_only = UNMARK_VALID;
+                               continue;
+                       }
+                       if (!strcmp(path, "--info-only")) {
+                               info_only = 1;
+                               continue;
+                       }
+                       if (!strcmp(path, "--force-remove")) {
+                               force_remove = 1;
+                               continue;
+                       }
+                       if (!strcmp(path, "-z")) {
+                               line_termination = 0;
+                               continue;
+                       }
+                       if (!strcmp(path, "--stdin")) {
+                               if (i != argc - 1)
+                                       die("--stdin must be at the end");
+                               read_from_stdin = 1;
+                               break;
+                       }
+                       if (!strcmp(path, "--index-info")) {
+                               if (i != argc - 1)
+                                       die("--index-info must be at the end");
+                               allow_add = allow_replace = allow_remove = 1;
+                               read_index_info(line_termination);
+                               break;
+                       }
+                       if (!strcmp(path, "--unresolve")) {
+                               has_errors = do_unresolve(argc - i, argv + i,
+                                                         prefix, prefix_length);
+                               if (has_errors)
+                                       active_cache_changed = 0;
+                               goto finish;
+                       }
+                       if (!strcmp(path, "--again")) {
+                               has_errors = do_reupdate(argc - i, argv + i,
+                                                        prefix, prefix_length);
+                               if (has_errors)
+                                       active_cache_changed = 0;
+                               goto finish;
+                       }
+                       if (!strcmp(path, "--ignore-missing")) {
+                               refresh_flags |= REFRESH_IGNORE_MISSING;
+                               continue;
+                       }
+                       if (!strcmp(path, "--verbose")) {
+                               verbose = 1;
+                               continue;
+                       }
+                       if (!strcmp(path, "-h") || !strcmp(path, "--help"))
+                               usage(update_index_usage);
+                       die("unknown option %s", path);
+               }
+               update_one(path, prefix, prefix_length);
+               if (set_executable_bit)
+                       chmod_path(set_executable_bit, path);
+       }
+       if (read_from_stdin) {
+               struct strbuf buf;
+               strbuf_init(&buf);
+               while (1) {
+                       char *path_name;
+                       const char *p;
+                       read_line(&buf, stdin, line_termination);
+                       if (buf.eof)
+                               break;
+                       if (line_termination && buf.buf[0] == '"')
+                               path_name = unquote_c_style(buf.buf, NULL);
+                       else
+                               path_name = buf.buf;
+                       p = prefix_path(prefix, prefix_length, path_name);
+                       update_one(p, NULL, 0);
+                       if (set_executable_bit)
+                               chmod_path(set_executable_bit, p);
+                       if (p < path_name || p > path_name + strlen(path_name))
+                               free((char*) p);
+                       if (path_name != buf.buf)
+                               free(path_name);
+               }
+       }
+
+ finish:
+       if (active_cache_changed) {
+               if (write_cache(newfd, active_cache, active_nr) ||
+                   commit_lock_file(lock_file))
+                       die("Unable to write new index file");
+       }
+
+       rollback_lock_file(lock_file);
+
+       return has_errors ? 1 : 0;
+}
diff --git a/builtin-update-ref.c b/builtin-update-ref.c
new file mode 100644 (file)
index 0000000..00333c7
--- /dev/null
@@ -0,0 +1,59 @@
+#include "cache.h"
+#include "refs.h"
+#include "builtin.h"
+
+static const char git_update_ref_usage[] =
+"git-update-ref <refname> <value> [<oldval>] [-m <reason>]";
+
+int cmd_update_ref(int argc, const char **argv, char **envp)
+{
+       const char *refname=NULL, *value=NULL, *oldval=NULL, *msg=NULL;
+       struct ref_lock *lock;
+       unsigned char sha1[20], oldsha1[20];
+       int i;
+
+       setup_git_directory();
+       git_config(git_default_config);
+
+       for (i = 1; i < argc; i++) {
+               if (!strcmp("-m", argv[i])) {
+                       if (i+1 >= argc)
+                               usage(git_update_ref_usage);
+                       msg = argv[++i];
+                       if (!*msg)
+                               die("Refusing to perform update with empty message.");
+                       if (strchr(msg, '\n'))
+                               die("Refusing to perform update with \\n in message.");
+                       continue;
+               }
+               if (!refname) {
+                       refname = argv[i];
+                       continue;
+               }
+               if (!value) {
+                       value = argv[i];
+                       continue;
+               }
+               if (!oldval) {
+                       oldval = argv[i];
+                       continue;
+               }
+       }
+       if (!refname || !value)
+               usage(git_update_ref_usage);
+
+       if (get_sha1(value, sha1))
+               die("%s: not a valid SHA1", value);
+       memset(oldsha1, 0, 20);
+       if (oldval && get_sha1(oldval, oldsha1))
+               die("%s: not a valid old SHA1", oldval);
+
+       lock = lock_any_ref_for_update(refname, oldval ? oldsha1 : NULL, 0);
+       if (!lock)
+               return 1;
+       if (write_ref_sha1(lock, sha1, msg) < 0)
+               return 1;
+
+       /* write_ref_sha1 always unlocks the ref, no need to do it explicitly */
+       return 0;
+}
diff --git a/builtin-write-tree.c b/builtin-write-tree.c
new file mode 100644 (file)
index 0000000..70e9b6f
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * GIT - The information manager from hell
+ *
+ * Copyright (C) Linus Torvalds, 2005
+ */
+#include "builtin.h"
+#include "cache.h"
+#include "tree.h"
+#include "cache-tree.h"
+
+static const char write_tree_usage[] =
+"git-write-tree [--missing-ok] [--prefix=<prefix>/]";
+
+int write_tree(unsigned char *sha1, int missing_ok, const char *prefix)
+{
+       int entries, was_valid, newfd;
+
+       /* We can't free this memory, it becomes part of a linked list parsed atexit() */
+       struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
+
+       newfd = hold_lock_file_for_update(lock_file, get_index_file());
+
+       entries = read_cache();
+       if (entries < 0)
+               die("git-write-tree: error reading cache");
+
+       if (!active_cache_tree)
+               active_cache_tree = cache_tree();
+
+       was_valid = cache_tree_fully_valid(active_cache_tree);
+
+       if (!was_valid) {
+               if (cache_tree_update(active_cache_tree,
+                                     active_cache, active_nr,
+                                     missing_ok, 0) < 0)
+                       die("git-write-tree: error building trees");
+               if (0 <= newfd) {
+                       if (!write_cache(newfd, active_cache, active_nr))
+                               commit_lock_file(lock_file);
+               }
+               /* Not being able to write is fine -- we are only interested
+                * in updating the cache-tree part, and if the next caller
+                * ends up using the old index with unupdated cache-tree part
+                * it misses the work we did here, but that is just a
+                * performance penalty and not a big deal.
+                */
+       }
+
+       if (prefix) {
+               struct cache_tree *subtree =
+                       cache_tree_find(active_cache_tree, prefix);
+               memcpy(sha1, subtree->sha1, 20);
+       }
+       else
+               memcpy(sha1, active_cache_tree->sha1, 20);
+
+       rollback_lock_file(lock_file);
+
+       return 0;
+}
+
+int cmd_write_tree(int argc, const char **argv, char **envp)
+{
+       int missing_ok = 0, ret;
+       const char *prefix = NULL;
+       unsigned char sha1[20];
+
+       setup_git_directory();
+
+       while (1 < argc) {
+               const char *arg = argv[1];
+               if (!strcmp(arg, "--missing-ok"))
+                       missing_ok = 1;
+               else if (!strncmp(arg, "--prefix=", 9))
+                       prefix = arg + 9;
+               else
+                       die(write_tree_usage);
+               argc--; argv++;
+       }
+
+       if (argc > 2)
+               die("too many options");
+
+       ret = write_tree(sha1, missing_ok, prefix);
+       printf("%s\n", sha1_to_hex(sha1));
+
+       return ret;
+}
index b9f36beb66042f00653508b5edeec53b336516aa..f12d5e68f6778fbf96c85b81d250cf7f230edf1d 100644 (file)
--- a/builtin.h
+++ b/builtin.h
@@ -1,6 +1,8 @@
 #ifndef BUILTIN_H
 #define BUILTIN_H
 
+#include <stdio.h>
+
 #ifndef PATH_MAX
 # define PATH_MAX 4096
 #endif
@@ -45,5 +47,18 @@ extern int cmd_diff_stages(int argc, const char **argv, char **envp);
 extern int cmd_diff_tree(int argc, const char **argv, char **envp);
 extern int cmd_cat_file(int argc, const char **argv, char **envp);
 extern int cmd_rev_parse(int argc, const char **argv, char **envp);
+extern int cmd_update_index(int argc, const char **argv, char **envp);
+extern int cmd_update_ref(int argc, const char **argv, char **envp);
+
+extern int cmd_write_tree(int argc, const char **argv, char **envp);
+extern int write_tree(unsigned char *sha1, int missing_ok, const char *prefix);
+
+extern int cmd_mailsplit(int argc, const char **argv, char **envp);
+extern int split_mbox(const char **mbox, const char *dir, int allow_bare, int nr_prec, int skip);
+
+extern int cmd_mailinfo(int argc, const char **argv, char **envp);
+extern int mailinfo(FILE *in, FILE *out, int ks, const char *encoding, const char *msg, const char *patch);
 
+extern int cmd_stripspace(int argc, const char **argv, char **envp);
+extern void stripspace(FILE *in, FILE *out);
 #endif
index 984c75f5dd06bd1d53eda206a2b57eea093dfd58..3e077d4c6ca4f7162e3a3254e1b3e90db33270ff 100644 (file)
--- a/config.c
+++ b/config.c
@@ -317,7 +317,33 @@ int git_config_from_file(config_fn_t fn, const char *filename)
 
 int git_config(config_fn_t fn)
 {
-       return git_config_from_file(fn, git_path("config"));
+       int ret = 0;
+       char *repo_config = NULL;
+       const char *home = NULL, *filename;
+
+       /* $GIT_CONFIG makes git read _only_ the given config file,
+        * $GIT_CONFIG_LOCAL will make it process it in addition to the
+        * global config file, the same way it would the per-repository
+        * config file otherwise. */
+       filename = getenv("GIT_CONFIG");
+       if (!filename) {
+               home = getenv("HOME");
+               filename = getenv("GIT_CONFIG_LOCAL");
+               if (!filename)
+                       filename = repo_config = strdup(git_path("config"));
+       }
+
+       if (home) {
+               char *user_config = strdup(mkpath("%s/.gitconfig", home));
+               if (!access(user_config, R_OK))
+                       ret = git_config_from_file(fn, user_config);
+               free(user_config);
+       }
+
+       ret += git_config_from_file(fn, filename);
+       if (repo_config)
+               free(repo_config);
+       return ret;
 }
 
 /*
@@ -490,10 +516,19 @@ int git_config_set_multivar(const char* key, const char* value,
        int i, dot;
        int fd = -1, in_fd;
        int ret;
-       char* config_filename = strdup(git_path("config"));
-       char* lock_file = strdup(git_path("config.lock"));
+       char* config_filename;
+       char* lock_file;
        const char* last_dot = strrchr(key, '.');
 
+       config_filename = getenv("GIT_CONFIG");
+       if (!config_filename) {
+               config_filename = getenv("GIT_CONFIG_LOCAL");
+               if (!config_filename)
+                       config_filename  = git_path("config");
+       }
+       config_filename = strdup(config_filename);
+       lock_file = strdup(mkpath("%s.lock", config_filename));
+
        /*
         * Since "key" actually contains the section name and the real
         * key name separated by a dot, we have to know where the dot is.
@@ -600,7 +635,7 @@ int git_config_set_multivar(const char* key, const char* value,
                 * As a side effect, we make sure to transform only a valid
                 * existing config file.
                 */
-               if (git_config(store_aux)) {
+               if (git_config_from_file(store_aux, config_filename)) {
                        fprintf(stderr, "invalid config file\n");
                        free(store.key);
                        if (store.value_regex != NULL) {
index 52d709e58d53f053ec00bfb9f31501684cbaf0e5..db7342e4d26baa50dd944ebf06fb8b66fea59aaa 100644 (file)
--- a/connect.c
+++ b/connect.c
@@ -581,6 +581,11 @@ int git_connect(int fd[2], char *url, const char *prog)
        enum protocol protocol = PROTO_LOCAL;
        int free_path = 0;
 
+       /* Without this we cannot rely on waitpid() to tell
+        * what happened to our children.
+        */
+       signal(SIGCHLD, SIG_DFL);
+
        host = strstr(url, "://");
        if(host) {
                *host = '\0';
index a67d6b479ec57816de1b259f9efed2258e999703..0fabd8981c6047e5144cfb3776b4f4a5588f24e5 100644 (file)
@@ -103,12 +103,12 @@ static int write_subdirectory(void *buffer, unsigned long size, const char *base
                if (!slash) {
                        newlen += sprintf(new + newlen, "%o %s", mode, path);
                        new[newlen++] = '\0';
-                       memcpy(new + newlen, buffer + len - 20, 20);
+                       memcpy(new + newlen, (char *) buffer + len - 20, 20);
                        newlen += 20;
 
                        used += len;
                        size -= len;
-                       buffer += len;
+                       buffer = (char *) buffer + len;
                        continue;
                }
 
@@ -121,7 +121,7 @@ static int write_subdirectory(void *buffer, unsigned long size, const char *base
 
                used += len;
                size -= len;
-               buffer += len;
+               buffer = (char *) buffer + len;
        }
 
        write_sha1_file(new, newlen, tree_type, result_sha1);
@@ -137,13 +137,13 @@ static void convert_tree(void *buffer, unsigned long size, unsigned char *result
        while (size) {
                int len = 1+strlen(buffer);
 
-               convert_binary_sha1(buffer + len);
+               convert_binary_sha1((char *) buffer + len);
 
                len += 20;
                if (len > size)
                        die("corrupt tree object");
                size -= len;
-               buffer += len;
+               buffer = (char *) buffer + len;
        }
 
        write_subdirectory(orig_buffer, orig_size, "", 0, result_sha1);
@@ -244,14 +244,14 @@ static void convert_date(void *buffer, unsigned long size, unsigned char *result
        // "tree <sha1>\n"
        memcpy(new + newlen, buffer, 46);
        newlen += 46;
-       buffer += 46;
+       buffer = (char *) buffer + 46;
        size -= 46;
 
        // "parent <sha1>\n"
        while (!memcmp(buffer, "parent ", 7)) {
                memcpy(new + newlen, buffer, 48);
                newlen += 48;
-               buffer += 48;
+               buffer = (char *) buffer + 48;
                size -= 48;
        }
 
@@ -275,11 +275,11 @@ static void convert_commit(void *buffer, unsigned long size, unsigned char *resu
 
        if (memcmp(buffer, "tree ", 5))
                die("Bad commit '%s'", (char*) buffer);
-       convert_ascii_sha1(buffer+5);
-       buffer += 46;    /* "tree " + "hex sha1" + "\n" */
+       convert_ascii_sha1((char *) buffer + 5);
+       buffer = (char *) buffer + 46;    /* "tree " + "hex sha1" + "\n" */
        while (!memcmp(buffer, "parent ", 7)) {
-               convert_ascii_sha1(buffer+7);
-               buffer += 48;
+               convert_ascii_sha1((char *) buffer + 7);
+               buffer = (char *) buffer + 48;
        }
        convert_date(orig_buffer, orig_size, result_sha1);
 }
index 5f9249aeedfa7d4156cf412da6a7e7d77f33ff86..ebaad0397f0bce50db7542abb904ba42b6173344 100644 (file)
@@ -17,7 +17,7 @@ static int sha1flush(struct sha1file *f, unsigned int count)
        for (;;) {
                int ret = xwrite(f->fd, buf, count);
                if (ret > 0) {
-                       buf += ret;
+                       buf = (char *) buf + ret;
                        count -= ret;
                        if (count)
                                continue;
@@ -57,7 +57,7 @@ int sha1write(struct sha1file *f, void *buf, unsigned int count)
                memcpy(f->buffer + offset, buf, nr);
                count -= nr;
                offset += nr;
-               buf += nr;
+               buf = (char *) buf + nr;
                left -= nr;
                if (!left) {
                        SHA1_Update(&f->ctx, f->buffer, offset);
index 0747ce2a37ac90cd89044ab9e5aac688f0e1a269..1ba4d669da346abb5dab86ea7842a7ad4d3e1cde 100644 (file)
--- a/daemon.c
+++ b/daemon.c
@@ -673,6 +673,11 @@ int main(int argc, char **argv)
        int inetd_mode = 0;
        int i;
 
+       /* Without this we cannot rely on waitpid() to tell
+        * what happened to our children.
+        */
+       signal(SIGCHLD, SIG_DFL);
+
        for (i = 1; i < argc; i++) {
                char *arg = argv[i];
 
index 25a798d050c6319f3d524c9c605ab70ea35ae7a1..8b9172aa2ef7402b0a847815bb11a26d1c4444fe 100644 (file)
@@ -22,6 +22,7 @@
 #include <string.h>
 #include "delta.h"
 
+#include "git-compat-util.h"
 
 /* maximum hash entry list for the same hash bucket */
 #define HASH_LIMIT 64
@@ -131,7 +132,7 @@ struct delta_index {
        const void *src_buf;
        unsigned long src_size;
        unsigned int hash_mask;
-       struct index_entry *hash[0];
+       struct index_entry *hash[FLEX_ARRAY];
 };
 
 struct delta_index * create_delta_index(const void *buf, unsigned long bufsize)
@@ -283,7 +284,7 @@ create_delta(const struct delta_index *index,
        ref_data = index->src_buf;
        ref_top = ref_data + index->src_size;
        data = trg_buf;
-       top = trg_buf + trg_size;
+       top = (const unsigned char *) trg_buf + trg_size;
 
        outpos++;
        val = 0;
index 2183b41b03d08092ba6d98a642757640b86710d6..116b5a9d6834c9517ccc2202510b37a1e1d863a5 100644 (file)
@@ -34,21 +34,23 @@ int run_diff_files(struct rev_info *revs, int silent_on_removed)
                        continue;
 
                if (ce_stage(ce)) {
-                       struct {
-                               struct combine_diff_path p;
-                               struct combine_diff_parent filler[5];
-                       } combine;
+                       struct combine_diff_path *dpath;
                        int num_compare_stages = 0;
+                       size_t path_len;
 
-                       combine.p.next = NULL;
-                       combine.p.len = ce_namelen(ce);
-                       combine.p.path = xmalloc(combine.p.len + 1);
-                       memcpy(combine.p.path, ce->name, combine.p.len);
-                       combine.p.path[combine.p.len] = 0;
-                       combine.p.mode = 0;
-                       memset(combine.p.sha1, 0, 20);
-                       memset(&combine.p.parent[0], 0,
-                              sizeof(combine.filler));
+                       path_len = ce_namelen(ce);
+
+                       dpath = xmalloc (combine_diff_path_size (5, path_len));
+                       dpath->path = (char *) &(dpath->parent[5]);
+
+                       dpath->next = NULL;
+                       dpath->len = path_len;
+                       memcpy(dpath->path, ce->name, path_len);
+                       dpath->path[path_len] = '\0';
+                       dpath->mode = 0;
+                       memset(dpath->sha1, 0, 20);
+                       memset(&(dpath->parent[0]), 0,
+                                       sizeof(struct combine_diff_parent)*5);
 
                        while (i < entries) {
                                struct cache_entry *nce = active_cache[i];
@@ -64,11 +66,11 @@ int run_diff_files(struct rev_info *revs, int silent_on_removed)
                                if (2 <= stage) {
                                        int mode = ntohl(nce->ce_mode);
                                        num_compare_stages++;
-                                       memcpy(combine.p.parent[stage-2].sha1,
+                                       memcpy(dpath->parent[stage-2].sha1,
                                               nce->sha1, 20);
-                                       combine.p.parent[stage-2].mode =
+                                       dpath->parent[stage-2].mode =
                                                canon_mode(mode);
-                                       combine.p.parent[stage-2].status =
+                                       dpath->parent[stage-2].status =
                                                DIFF_STATUS_MODIFIED;
                                }
 
@@ -83,13 +85,14 @@ int run_diff_files(struct rev_info *revs, int silent_on_removed)
                        i--;
 
                        if (revs->combine_merges && num_compare_stages == 2) {
-                               show_combined_diff(&combine.p, 2,
+                               show_combined_diff(dpath, 2,
                                                   revs->dense_combined_merges,
                                                   revs);
-                               free(combine.p.path);
+                               free(dpath);
                                continue;
                        }
-                       free(combine.p.path);
+                       free(dpath);
+                       dpath = NULL;
 
                        /*
                         * Show the diff for the 'ce' if we found the one
@@ -329,8 +332,8 @@ int run_diff_index(struct rev_info *revs, int cached)
        }
        mark_merge_entries();
 
-       ent = revs->pending_objects->item;
-       tree_name = revs->pending_objects->name;
+       ent = revs->pending.objects[0].item;
+       tree_name = revs->pending.objects[0].name;
        tree = parse_tree_indirect(ent->sha1);
        if (!tree)
                return error("bad tree object %s", tree_name);
diff --git a/diff.c b/diff.c
index 9e9cfc8b75b7b761980b0d32cb0c516ea3505ba3..22b643cc2520664416ceac9998d401a6585f07bf 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -25,6 +25,20 @@ int git_diff_config(const char *var, const char *value)
        return git_default_config(var, value);
 }
 
+enum color_diff {
+       DIFF_PLAIN = 0,
+       DIFF_METAINFO = 1,
+       DIFF_FILE_OLD = 2,
+       DIFF_FILE_NEW = 3,
+};
+
+static const char *diff_colors[] = {
+       "\033[0;0m",
+       "\033[1;35m",
+       "\033[1;31m",
+       "\033[1;34m",
+};
+
 static char *quote_one(const char *str)
 {
        int needlen;
@@ -177,23 +191,54 @@ static int fill_mmfile(mmfile_t *mf, struct diff_filespec *one)
 }
 
 struct emit_callback {
+       struct xdiff_emit_state xm;
+       int nparents, color_diff;
        const char **label_path;
 };
 
-static int fn_out(void *priv, mmbuffer_t *mb, int nbuf)
+static inline void color_diff(int diff_use_color, enum color_diff ix)
+{
+       if (diff_use_color)
+               fputs(diff_colors[ix], stdout);
+}
+
+static void fn_out_consume(void *priv, char *line, unsigned long len)
 {
        int i;
        struct emit_callback *ecbdata = priv;
 
        if (ecbdata->label_path[0]) {
+               color_diff(ecbdata->color_diff, DIFF_METAINFO);
                printf("--- %s\n", ecbdata->label_path[0]);
+               color_diff(ecbdata->color_diff, DIFF_METAINFO);
                printf("+++ %s\n", ecbdata->label_path[1]);
                ecbdata->label_path[0] = ecbdata->label_path[1] = NULL;
        }
-       for (i = 0; i < nbuf; i++)
-               if (!fwrite(mb[i].ptr, mb[i].size, 1, stdout))
-                       return -1;
-       return 0;
+
+       /* This is not really necessary for now because
+        * this codepath only deals with two-way diffs.
+        */
+       for (i = 0; i < len && line[i] == '@'; i++)
+               ;
+       if (2 <= i && i < len && line[i] == ' ') {
+               ecbdata->nparents = i - 1;
+               color_diff(ecbdata->color_diff, DIFF_METAINFO);
+       }
+       else if (len < ecbdata->nparents)
+               color_diff(ecbdata->color_diff, DIFF_PLAIN);
+       else {
+               int nparents = ecbdata->nparents;
+               int color = DIFF_PLAIN;
+               for (i = 0; i < nparents && len; i++) {
+                       if (line[i] == '-')
+                               color = DIFF_FILE_OLD;
+                       else if (line[i] == '+')
+                               color = DIFF_FILE_NEW;
+               }
+               color_diff(ecbdata->color_diff, color);
+       }
+       fwrite(line, len, 1, stdout);
+       color_diff(ecbdata->color_diff, DIFF_PLAIN);
 }
 
 static char *pprint_rename(const char *a, const char *b)
@@ -515,7 +560,7 @@ static void emit_binary_diff(mmfile_t *one, mmfile_t *two)
                else
                        line[0] = bytes - 26 + 'a' - 1;
                encode_85(line + 1, cp, bytes);
-               cp += bytes;
+               cp = (char *) cp + bytes;
                puts(line);
        }
        printf("\n");
@@ -549,25 +594,35 @@ static void builtin_diff(const char *name_a,
        b_two = quote_two("b/", name_b);
        lbl[0] = DIFF_FILE_VALID(one) ? a_one : "/dev/null";
        lbl[1] = DIFF_FILE_VALID(two) ? b_two : "/dev/null";
+       color_diff(o->color_diff, DIFF_METAINFO);
        printf("diff --git %s %s\n", a_one, b_two);
        if (lbl[0][0] == '/') {
                /* /dev/null */
+               color_diff(o->color_diff, DIFF_METAINFO);
                printf("new file mode %06o\n", two->mode);
-               if (xfrm_msg && xfrm_msg[0])
+               if (xfrm_msg && xfrm_msg[0]) {
+                       color_diff(o->color_diff, DIFF_METAINFO);
                        puts(xfrm_msg);
+               }
        }
        else if (lbl[1][0] == '/') {
                printf("deleted file mode %06o\n", one->mode);
-               if (xfrm_msg && xfrm_msg[0])
+               if (xfrm_msg && xfrm_msg[0]) {
+                       color_diff(o->color_diff, DIFF_METAINFO);
                        puts(xfrm_msg);
+               }
        }
        else {
                if (one->mode != two->mode) {
+                       color_diff(o->color_diff, DIFF_METAINFO);
                        printf("old mode %06o\n", one->mode);
+                       color_diff(o->color_diff, DIFF_METAINFO);
                        printf("new mode %06o\n", two->mode);
                }
-               if (xfrm_msg && xfrm_msg[0])
+               if (xfrm_msg && xfrm_msg[0]) {
+                       color_diff(o->color_diff, DIFF_METAINFO);
                        puts(xfrm_msg);
+               }
                /*
                 * we do not run diff between different kind
                 * of objects.
@@ -575,6 +630,7 @@ static void builtin_diff(const char *name_a,
                if ((one->mode ^ two->mode) & S_IFMT)
                        goto free_ab_and_return;
                if (complete_rewrite) {
+                       color_diff(o->color_diff, DIFF_PLAIN);
                        emit_rewrite_diff(name_a, name_b, one, two);
                        goto free_ab_and_return;
                }
@@ -602,7 +658,9 @@ static void builtin_diff(const char *name_a,
                xdemitcb_t ecb;
                struct emit_callback ecbdata;
 
+               memset(&ecbdata, 0, sizeof(ecbdata));
                ecbdata.label_path = lbl;
+               ecbdata.color_diff = o->color_diff;
                xpp.flags = XDF_NEED_MINIMAL;
                xecfg.ctxlen = o->context;
                xecfg.flags = XDL_EMIT_FUNCNAMES;
@@ -612,8 +670,9 @@ static void builtin_diff(const char *name_a,
                        xecfg.ctxlen = strtoul(diffopts + 10, NULL, 10);
                else if (!strncmp(diffopts, "-u", 2))
                        xecfg.ctxlen = strtoul(diffopts + 2, NULL, 10);
-               ecb.outf = fn_out;
+               ecb.outf = xdiff_outf;
                ecb.priv = &ecbdata;
+               ecbdata.xm.consume = fn_out_consume;
                xdl_diff(&mf1, &mf2, &xpp, &xecfg, &ecb);
        }
 
@@ -1456,6 +1515,8 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
                else if (40 < options->abbrev)
                        options->abbrev = 40;
        }
+       else if (!strcmp(arg, "--color"))
+               options->color_diff = 1;
        else
                return 0;
        return 1;
diff --git a/diff.h b/diff.h
index 4fc597c59421b7558e0beb0f2994e15950592aaa..de9de574e78693d27ff911d882030a59407a99d5 100644 (file)
--- a/diff.h
+++ b/diff.h
@@ -32,7 +32,8 @@ struct diff_options {
                 full_index:1,
                 silent_on_remove:1,
                 find_copies_harder:1,
-                summary:1;
+                summary:1,
+                color_diff:1;
        int context;
        int break_opt;
        int detect_rename;
index 0bc2b22f848457a8e6ffafc6a1d9a26be677209f..aef6da60447b98a05d91513b4aa74e505b0a0d84 100644 (file)
@@ -30,7 +30,7 @@ static void prepare_order(const char *orderfile)
        close(fd);
        if (map == MAP_FAILED)
                return;
-       endp = map + st.st_size;
+       endp = (char *) map + st.st_size;
        for (pass = 0; pass < 2; pass++) {
                cnt = 0;
                cp = map;
index e6b57b8ab9595be2875f328c0a8b0f2f58897793..b9ce1125d855273e499b57173d4281507e656fdd 100755 (executable)
@@ -34,7 +34,96 @@ When you have resolved this problem run \"git rebase --continue\".
 If you would prefer to skip this patch, instead run \"git rebase --skip\".
 To restore the original branch and stop rebasing run \"git rebase --abort\".
 "
+
+MRESOLVEMSG="
+When you have resolved this problem run \"git rebase --continue\".
+To restore the original branch and stop rebasing run \"git rebase --abort\".
+"
 unset newbase
+strategy=recursive
+do_merge=
+dotest=$GIT_DIR/.dotest-merge
+prec=4
+
+continue_merge () {
+       test -n "$prev_head" || die "prev_head must be defined"
+       test -d "$dotest" || die "$dotest directory does not exist"
+
+       unmerged=$(git-ls-files -u)
+       if test -n "$unmerged"
+       then
+               echo "You still have unmerged paths in your index"
+               echo "did you forget update-index?"
+               die "$MRESOLVEMSG"
+       fi
+
+       if test -n "`git-diff-index HEAD`"
+       then
+               git-commit -C "`cat $dotest/current`"
+       else
+               echo "Previous merge succeeded automatically"
+       fi
+
+       prev_head=`git-rev-parse HEAD^0`
+
+       # save the resulting commit so we can read-tree on it later
+       echo "$prev_head" > "$dotest/`printf %0${prec}d $msgnum`.result"
+       echo "$prev_head" > "$dotest/prev_head"
+
+       # onto the next patch:
+       msgnum=$(($msgnum + 1))
+       printf "%0${prec}d" "$msgnum" > "$dotest/msgnum"
+}
+
+call_merge () {
+       cmt="$(cat $dotest/`printf %0${prec}d $1`)"
+       echo "$cmt" > "$dotest/current"
+       git-merge-$strategy "$cmt^" -- HEAD "$cmt"
+       rv=$?
+       case "$rv" in
+       0)
+               git-commit -C "$cmt" || die "commit failed: $MRESOLVEMSG"
+               ;;
+       1)
+               test -d "$GIT_DIR/rr-cache" && git-rerere
+               die "$MRESOLVEMSG"
+               ;;
+       2)
+               echo "Strategy: $rv $strategy failed, try another" 1>&2
+               die "$MRESOLVEMSG"
+               ;;
+       *)
+               die "Unknown exit code ($rv) from command:" \
+                       "git-merge-$strategy $cmt^ -- HEAD $cmt"
+               ;;
+       esac
+}
+
+finish_rb_merge () {
+       set -e
+
+       msgnum=1
+       echo "Finalizing rebased commits..."
+       git-reset --hard "`cat $dotest/onto`"
+       end="`cat $dotest/end`"
+       while test "$msgnum" -le "$end"
+       do
+               msgnum=`printf "%0${prec}d" "$msgnum"`
+               printf "%0${prec}d" "$msgnum" > "$dotest/msgnum"
+
+               git-read-tree `cat "$dotest/$msgnum.result"`
+               git-checkout-index -q -f -u -a
+               git-commit -C "`cat $dotest/$msgnum`"
+
+               echo "Committed $msgnum"
+               echo '    '`git-rev-list --pretty=oneline -1 HEAD | \
+                                       sed 's/^[a-f0-9]\+ //'`
+               msgnum=$(($msgnum + 1))
+       done
+       rm -r "$dotest"
+       echo "All done."
+}
+
 while case "$#" in 0) break ;; esac
 do
        case "$1" in
@@ -46,17 +135,43 @@ do
                        exit 1
                        ;;
                esac
+               if test -d "$dotest"
+               then
+                       prev_head="`cat $dotest/prev_head`"
+                       end="`cat $dotest/end`"
+                       msgnum="`cat $dotest/msgnum`"
+                       onto="`cat $dotest/onto`"
+                       continue_merge
+                       while test "$msgnum" -le "$end"
+                       do
+                               call_merge "$msgnum"
+                               continue_merge
+                       done
+                       finish_rb_merge
+                       exit
+               fi
                git am --resolved --3way --resolvemsg="$RESOLVEMSG"
                exit
                ;;
        --skip)
+               if test -d "$dotest"
+               then
+                       die "--skip is not supported when using --merge"
+               fi
                git am -3 --skip --resolvemsg="$RESOLVEMSG"
                exit
                ;;
        --abort)
-               [ -d .dotest ] || die "No rebase in progress?"
+               if test -d "$dotest"
+               then
+                       rm -r "$dotest"
+               elif test -d .dotest
+               then
+                       rm -r .dotest
+               else
+                       die "No rebase in progress?"
+               fi
                git reset --hard ORIG_HEAD
-               rm -r .dotest
                exit
                ;;
        --onto)
@@ -64,6 +179,23 @@ do
                newbase="$2"
                shift
                ;;
+       -M|-m|--m|--me|--mer|--merg|--merge)
+               do_merge=t
+               ;;
+       -s=*|--s=*|--st=*|--str=*|--stra=*|--strat=*|--strate=*|\
+               --strateg=*|--strategy=*|\
+       -s|--s|--st|--str|--stra|--strat|--strate|--strateg|--strategy)
+               case "$#,$1" in
+               *,*=*)
+                       strategy=`expr "$1" : '-[^=]*=\(.*\)'` ;;
+               1,*)
+                       usage ;;
+               *)
+                       strategy="$2"
+                       shift ;;
+               esac
+               do_merge=t
+               ;;
        -*)
                usage
                ;;
@@ -75,16 +207,25 @@ do
 done
 
 # Make sure we do not have .dotest
-if mkdir .dotest
+if test -z "$do_merge"
 then
-       rmdir .dotest
-else
-       echo >&2 '
+       if mkdir .dotest
+       then
+               rmdir .dotest
+       else
+               echo >&2 '
 It seems that I cannot create a .dotest directory, and I wonder if you
 are in the middle of patch application or another rebase.  If that is not
 the case, please rm -fr .dotest and run me again.  I am stopping in case
 you still have something valuable there.'
-       exit 1
+               exit 1
+       fi
+else
+       if test -d "$dotest"
+       then
+               die "previous dotest directory $dotest still exists." \
+                       'try git-rebase < --continue | --abort >'
+       fi
 fi
 
 # The tree must be really really clean.
@@ -152,6 +293,48 @@ then
        exit 0
 fi
 
-git-format-patch -k --stdout --full-index "$upstream"..ORIG_HEAD |
-git am --binary -3 -k --resolvemsg="$RESOLVEMSG"
+if test -z "$do_merge"
+then
+       git-format-patch -k --stdout --full-index "$upstream"..ORIG_HEAD |
+       git am --binary -3 -k --resolvemsg="$RESOLVEMSG"
+       exit $?
+fi
+
+if test "@@NO_PYTHON@@" && test "$strategy" = "recursive"
+then
+       die 'The recursive merge strategy currently relies on Python,
+which this installation of git was not configured with.  Please consider
+a different merge strategy (e.g. octopus, resolve, stupid, ours)
+or install Python and git with Python support.'
+
+fi
+
+# start doing a rebase with git-merge
+# this is rename-aware if the recursive (default) strategy is used
+
+mkdir -p "$dotest"
+echo "$onto" > "$dotest/onto"
+prev_head=`git-rev-parse HEAD^0`
+echo "$prev_head" > "$dotest/prev_head"
+
+msgnum=0
+for cmt in `git-rev-list --no-merges "$upstream"..ORIG_HEAD \
+                       | perl -e 'print reverse <>'`
+do
+       msgnum=$(($msgnum + 1))
+       echo "$cmt" > "$dotest/`printf "%0${prec}d" $msgnum`"
+done
+
+printf "%0${prec}d" 1 > "$dotest/msgnum"
+printf "%0${prec}d" "$msgnum" > "$dotest/end"
+
+end=$msgnum
+msgnum=1
+
+while test "$msgnum" -le "$end"
+do
+       call_merge "$msgnum"
+       continue_merge
+done
 
+finish_rb_merge
diff --git a/git.c b/git.c
index 329ebec78cd057a14a22cca2e89a9cb09a53f5dd..94e9a4a4b98acbd9e08943dbbe57616bd60b28e9 100644 (file)
--- a/git.c
+++ b/git.c
@@ -178,7 +178,13 @@ static void handle_internal_command(int argc, const char **argv, char **envp)
                { "diff-stages", cmd_diff_stages },
                { "diff-tree", cmd_diff_tree },
                { "cat-file", cmd_cat_file },
-               { "rev-parse", cmd_rev_parse }
+               { "rev-parse", cmd_rev_parse },
+               { "write-tree", cmd_write_tree },
+               { "mailsplit", cmd_mailsplit },
+               { "mailinfo", cmd_mailinfo },
+               { "stripspace", cmd_stripspace },
+               { "update-index", cmd_update_index },
+               { "update-ref", cmd_update_ref }
        };
        int i;
 
index 5eabe06dafadc150acbb3cc534ad7bf758a0080e..f2e50462d9b6160127b35f659ff8586369723b07 100755 (executable)
@@ -17,30 +17,50 @@ use Fcntl ':mode';
 binmode STDOUT, ':utf8';
 
 my $cgi = new CGI;
-my $version =          "267";
-my $my_url =           $cgi->url();
-my $my_uri =           $cgi->url(-absolute => 1);
-my $rss_link =         "";
+my $version = "267";
+my $my_url = $cgi->url();
+my $my_uri = $cgi->url(-absolute => 1);
+my $rss_link = "";
+
+# location of the git-core binaries
+my $gitbin = "/usr/bin";
 
 # absolute fs-path which will be prepended to the project path
-#my $projectroot =     "/pub/scm";
-my $projectroot =      "/home/kay/public_html/pub/scm";
+#my $projectroot = "/pub/scm";
+my $projectroot = "/home/kay/public_html/pub/scm";
 
-# location of the git-core binaries
-my $gitbin =           "/usr/bin";
+# version of the git-core binaries
+my $git_version = qx($gitbin/git --version);
+if ($git_version =~ m/git version (.*)$/) {
+       $git_version = $1;
+} else {
+       $git_version = "unknown";
+}
 
 # location for temporary files needed for diffs
-my $git_temp =         "/tmp/gitweb";
+my $git_temp = "/tmp/gitweb";
 
 # target of the home link on top of all pages
-my $home_link =                $my_uri;
+my $home_link = $my_uri;
 
 # html text to include at home page
-my $home_text =                "indextext.html";
+my $home_text = "indextext.html";
+
+# URI of default stylesheet
+my $stylesheet = "gitweb.css";
 
 # source of projects list
-#my $projects_list =   $projectroot;
-my $projects_list =    "index/index.aux";
+#my $projects_list = $projectroot;
+my $projects_list = "index/index.aux";
+
+# default blob_plain mimetype and default charset for text/plain blob
+my $default_blob_plain_mimetype = 'text/plain';
+my $default_text_plain_charset  = undef;
+
+# file to use for guessing MIME types before trying /etc/mime.types
+# (relative to the current git repository)
+my $mimetypes_file = undef;
+
 
 # input validation and dispatch
 my $action = $cgi->param('a');
@@ -66,8 +86,9 @@ if (defined $order) {
        }
 }
 
-my $project = $cgi->param('p');
+my $project = ($cgi->param('p') || $ENV{'PATH_INFO'});
 if (defined $project) {
+       $project =~ s|^/||; $project =~ s|/$||;
        $project = validate_input($project);
        if (!defined($project)) {
                die_error(undef, "Invalid project parameter.");
@@ -240,6 +261,19 @@ sub unquote {
        return $str;
 }
 
+# CSS class for given age value (in seconds)
+sub age_class {
+       my $age = shift;
+
+       if ($age < 60*60*2) {
+               return "age0";
+       } elsif ($age < 60*60*24*2) {
+               return "age1";
+       } else {
+               return "age2";
+       }
+}
+
 sub git_header_html {
        my $status = shift || "200 OK";
        my $expires = shift;
@@ -249,6 +283,12 @@ sub git_header_html {
                $title .= " - $project";
                if (defined $action) {
                        $title .= "/$action";
+                       if (defined $file_name) {
+                               $title .= " - $file_name";
+                               if ($action eq "tree" && $file_name !~ m|/$|) {
+                                       $title .= "/";
+                               }
+                       }
                }
        }
        print $cgi->header(-type=>'text/html',  -charset => 'utf-8', -status=> $status, -expires => $expires);
@@ -257,71 +297,13 @@ sub git_header_html {
 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US" lang="en-US">
 <!-- git web interface v$version, (C) 2005-2006, Kay Sievers <kay.sievers\@vrfy.org>, Christian Gierke -->
+<!-- git core binaries version $git_version -->
 <head>
 <meta http-equiv="content-type" content="text/html; charset=utf-8"/>
 <meta name="robots" content="index, nofollow"/>
 <title>$title</title>
+<link rel="stylesheet" type="text/css" href="$stylesheet"/>
 $rss_link
-<style type="text/css">
-body {
-       font-family: sans-serif; font-size: 12px; border:solid #d9d8d1; border-width:1px;
-       margin:10px; background-color:#ffffff; color:#000000;
-}
-a { color:#0000cc; }
-a:hover, a:visited, a:active { color:#880000; }
-div.page_header { height:25px; padding:8px; font-size:18px; font-weight:bold; background-color:#d9d8d1; }
-div.page_header a:visited, a.header { color:#0000cc; }
-div.page_header a:hover { color:#880000; }
-div.page_nav { padding:8px; }
-div.page_nav a:visited { color:#0000cc; }
-div.page_path { padding:8px; border:solid #d9d8d1; border-width:0px 0px 1px}
-div.page_footer { height:17px; padding:4px 8px; background-color: #d9d8d1; }
-div.page_footer_text { float:left; color:#555555; font-style:italic; }
-div.page_body { padding:8px; }
-div.title, a.title {
-       display:block; padding:6px 8px;
-       font-weight:bold; background-color:#edece6; text-decoration:none; color:#000000;
-}
-a.title:hover { background-color: #d9d8d1; }
-div.title_text { padding:6px 0px; border: solid #d9d8d1; border-width:0px 0px 1px; }
-div.log_body { padding:8px 8px 8px 150px; }
-span.age { position:relative; float:left; width:142px; font-style:italic; }
-div.log_link {
-       padding:0px 8px;
-       font-size:10px; font-family:sans-serif; font-style:normal;
-       position:relative; float:left; width:136px;
-}
-div.list_head { padding:6px 8px 4px; border:solid #d9d8d1; border-width:1px 0px 0px; font-style:italic; }
-a.list { text-decoration:none; color:#000000; }
-a.list:hover { text-decoration:underline; color:#880000; }
-a.text { text-decoration:none; color:#0000cc; }
-a.text:visited { text-decoration:none; color:#880000; }
-a.text:hover { text-decoration:underline; color:#880000; }
-table { padding:8px 4px; }
-th { padding:2px 5px; font-size:12px; text-align:left; }
-tr.light:hover { background-color:#edece6; }
-tr.dark { background-color:#f6f6f0; }
-tr.dark:hover { background-color:#edece6; }
-td { padding:2px 5px; font-size:12px; vertical-align:top; }
-td.link { padding:2px 5px; font-family:sans-serif; font-size:10px; }
-div.pre { font-family:monospace; font-size:12px; white-space:pre; }
-div.diff_info { font-family:monospace; color:#000099; background-color:#edece6; font-style:italic; }
-div.index_include { border:solid #d9d8d1; border-width:0px 0px 1px; padding:12px 8px; }
-div.search { margin:4px 8px; position:absolute; top:56px; right:12px }
-a.linenr { color:#999999; text-decoration:none }
-a.rss_logo {
-       float:right; padding:3px 0px; width:35px; line-height:10px;
-       border:1px solid; border-color:#fcc7a5 #7d3302 #3e1a01 #ff954e;
-       color:#ffffff; background-color:#ff6600;
-       font-weight:bold; font-family:sans-serif; font-size:10px;
-       text-align:center; text-decoration:none;
-}
-a.rss_logo:hover { background-color:#ee5500; }
-span.tag {
-       padding:0px 4px; font-size:10px; font-weight:normal;
-       background-color:#ffffaa; border:1px solid; border-color:#ffffcc #ffee00 #ffee00 #ffffcc;
-}
-</style>
 </head>
 <body>
 EOF
@@ -377,7 +359,7 @@ sub git_footer_html {
 
 sub die_error {
        my $status = shift || "403 Forbidden";
-       my $error = shift || "Malformed query, file missing or permission denied"; 
+       my $error = shift || "Malformed query, file missing or permission denied";
 
        git_header_html($status);
        print "<div class=\"page_body\">\n" .
@@ -641,13 +623,13 @@ sub git_diff_print {
                while (my $line = <$fd>) {
                        chomp($line);
                        my $char = substr($line, 0, 1);
-                       my $color = "";
+                       my $diff_class = "";
                        if ($char eq '+') {
-                               $color = " style=\"color:#008800;\"";
+                               $diff_class = " add";
                        } elsif ($char eq "-") {
-                               $color = " style=\"color:#cc0000;\"";
+                               $diff_class = " rem";
                        } elsif ($char eq "@") {
-                               $color = " style=\"color:#990099;\"";
+                               $diff_class = " chunk_header";
                        } elsif ($char eq "\\") {
                                # skip errors
                                next;
@@ -658,7 +640,7 @@ sub git_diff_print {
                                        $line =~ s/\t/$spaces/;
                                }
                        }
-                       print "<div class=\"pre\"$color>" . esc_html($line) . "</div>\n";
+                       print "<div class=\"diff$diff_class\">" . esc_html($line) . "</div>\n";
                }
        }
        close $fd;
@@ -844,7 +826,7 @@ sub git_get_project_config {
        $key =~ s/^gitweb\.//;
        return if ($key =~ m/\W/);
 
-       my $val = qx(git-repo-config --get gitweb.$key);
+       my $val = qx($gitbin/git-repo-config --get gitweb.$key);
        return ($val);
 }
 
@@ -890,7 +872,7 @@ sub git_project_list {
                close $fd;
                print "</div>\n";
        }
-       print "<table cellspacing=\"0\">\n" .
+       print "<table class=\"project_list\">\n" .
              "<tr>\n";
        if (!defined($order) || (defined($order) && ($order eq "project"))) {
                @projects = sort {$a->{'path'} cmp $b->{'path'}} @projects;
@@ -929,15 +911,7 @@ sub git_project_list {
                print "<td>" . $cgi->a({-href => "$my_uri?" . esc_param("p=$pr->{'path'};a=summary"), -class => "list"}, esc_html($pr->{'path'})) . "</td>\n" .
                      "<td>$pr->{'descr'}</td>\n" .
                      "<td><i>" . chop_str($pr->{'owner'}, 15) . "</i></td>\n";
-               my $colored_age;
-               if ($pr->{'commit'}{'age'} < 60*60*2) {
-                       $colored_age = "<span style =\"color: #009900;\"><b><i>$pr->{'commit'}{'age_string'}</i></b></span>";
-               } elsif ($pr->{'commit'}{'age'} < 60*60*24*2) {
-                       $colored_age = "<span style =\"color: #009900;\"><i>$pr->{'commit'}{'age_string'}</i></span>";
-               } else {
-                       $colored_age = "<i>$pr->{'commit'}{'age_string'}</i>";
-               }
-               print "<td>$colored_age</td>\n" .
+               print "<td class=\"". age_class($pr->{'commit'}{'age'}) . "\">" . $pr->{'commit'}{'age_string'} . "</td>\n" .
                      "<td class=\"link\">" .
                      $cgi->a({-href => "$my_uri?" . esc_param("p=$pr->{'path'};a=summary")}, "summary") .
                      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$pr->{'path'};a=shortlog")}, "shortlog") .
@@ -1102,7 +1076,7 @@ sub git_summary {
                              "<td>";
                        if (length($co{'title_short'}) < length($co{'title'})) {
                                print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$commit"), -class => "list", -title => "$co{'title'}"},
-                                     "<b>" . esc_html($co{'title_short'}) . "$ref</b>");
+                                     "<b>" . esc_html($co{'title_short'}) . "$ref</b>");
                        } else {
                                print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$commit"), -class => "list"},
                                      "<b>" . esc_html($co{'title'}) . "$ref</b>");
@@ -1160,7 +1134,7 @@ sub git_summary {
                                print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=$tag{'reftype'};h=$tag{'refid'}")}, $tag{'reftype'});
                                if ($tag{'reftype'} eq "commit") {
                                      print " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog;h=$tag{'name'}")}, "shortlog") .
-                                           " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log;h=$tag{'refid'}")}, "log");
+                                           " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log;h=$tag{'refid'}")}, "log");
                                }
                                print "</td>\n" .
                                      "</tr>";
@@ -1281,7 +1255,7 @@ sub git_blame {
        print "<div class=\"page_path\"><b>" . esc_html($file_name) . "</b></div>\n";
        print "<div class=\"page_body\">\n";
        print <<HTML;
-<table style="border-collapse: collapse;">
+<table class="blame">
   <tr>
     <th>Commit</th>
     <th>Age</th>
@@ -1302,7 +1276,7 @@ HTML
                my $data;
                my $age;
                my $age_str;
-               my $age_style;
+               my $age_class;
 
                chomp $line;
                $line_class_num = ($line_class_num + 1) % $line_class_len;
@@ -1314,16 +1288,14 @@ HTML
                        $lineno   = $4;
                        $data     = $5;
                } else {
-                       print qq(  <tr><td colspan="5" style="color: red; background-color: yellow;">Unable to parse: $line</td></tr>\n);
+                       print qq(  <tr><td colspan="5" class="error">Unable to parse: $line</td></tr>\n);
                        next;
                }
                $short_rev  = substr ($long_rev, 0, 8);
                $age        = time () - $time;
                $age_str    = age_string ($age);
                $age_str    =~ s/ /&nbsp;/g;
-               $age_style  = 'font-style: italic;';
-               $age_style .= ' color: #009900; background: transparent;' if ($age < 60*60*24*2);
-               $age_style .= ' font-weight: bold;' if ($age < 60*60*2);
+               $age_class  = age_class($age);
                $author     = esc_html ($author);
                $author     =~ s/ /&nbsp;/g;
                # escape tabs
@@ -1334,15 +1306,14 @@ HTML
                        }
                }
                $data = esc_html ($data);
-               $data =~ s/ /&nbsp;/g;
 
                print <<HTML;
   <tr class="$line_class[$line_class_num]">
-    <td style="font-family: monospace;"><a href="$my_uri?${\esc_param ("p=$project;a=commit;h=$long_rev")}" class="text">$short_rev..</a></td>
-    <td style="$age_style">$age_str</td>
+    <td class="sha1"><a href="$my_uri?${\esc_param ("p=$project;a=commit;h=$long_rev")}" class="text">$short_rev..</a></td>
+    <td class="$age_class">$age_str</td>
     <td>$author</td>
-    <td style="text-align: right;"><a id="$lineno" href="#$lineno" class="linenr">$lineno</a></td>
-    <td style="font-family: monospace;">$data</td>
+    <td class="linenr"><a id="$lineno" href="#$lineno" class="linenr">$lineno</a></td>
+    <td class="pre">$data</td>
   </tr>
 HTML
        } # while (my $line = <$fd>)
@@ -1401,7 +1372,7 @@ sub git_tags {
                        print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=$tag{'reftype'};h=$tag{'refid'}")}, $tag{'reftype'});
                        if ($tag{'reftype'} eq "commit") {
                              print " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog;h=$tag{'name'}")}, "shortlog") .
-                                   " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log;h=$tag{'refid'}")}, "log");
+                                   " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log;h=$tag{'refid'}")}, "log");
                        }
                        print "</td>\n" .
                              "</tr>";
@@ -1538,15 +1509,85 @@ sub git_blob {
        git_footer_html();
 }
 
+sub mimetype_guess_file {
+       my $filename = shift;
+       my $mimemap = shift;
+       -r $mimemap or return undef;
+
+       my %mimemap;
+       open(MIME, $mimemap) or return undef;
+       while (<MIME>) {
+               my ($mime, $exts) = split(/\t+/);
+               my @exts = split(/\s+/, $exts);
+               foreach my $ext (@exts) {
+                       $mimemap{$ext} = $mime;
+               }
+       }
+       close(MIME);
+
+       $filename =~ /\.(.*?)$/;
+       return $mimemap{$1};
+}
+
+sub mimetype_guess {
+       my $filename = shift;
+       my $mime;
+       $filename =~ /\./ or return undef;
+
+       if ($mimetypes_file) {
+               my $file = $mimetypes_file;
+               #$file =~ m#^/# or $file = "$projectroot/$path/$file";
+               $mime = mimetype_guess_file($filename, $file);
+       }
+       $mime ||= mimetype_guess_file($filename, '/etc/mime.types');
+       return $mime;
+}
+
+sub git_blob_plain_mimetype {
+       my $fd = shift;
+       my $filename = shift;
+
+       # just in case
+       return $default_blob_plain_mimetype unless $fd;
+
+       if ($filename) {
+               my $mime = mimetype_guess($filename);
+               $mime and return $mime;
+       }
+
+       if (-T $fd) {
+               return 'text/plain' .
+                      ($default_text_plain_charset ? '; charset='.$default_text_plain_charset : '');
+       } elsif (! $filename) {
+               return 'application/octet-stream';
+       } elsif ($filename =~ m/\.png$/i) {
+               return 'image/png';
+       } elsif ($filename =~ m/\.gif$/i) {
+               return 'image/gif';
+       } elsif ($filename =~ m/\.jpe?g$/i) {
+               return 'image/jpeg';
+       } else {
+               return 'application/octet-stream';
+       }
+}
+
 sub git_blob_plain {
-       my $save_as = "$hash.txt";
+       open my $fd, "-|", "$gitbin/git-cat-file blob $hash" or return;
+       my $type = git_blob_plain_mimetype($fd, $file_name);
+
+       # save as filename, even when no $file_name is given
+       my $save_as = "$hash";
        if (defined $file_name) {
                $save_as = $file_name;
+       } elsif ($type =~ m/^text\//) {
+               $save_as .= '.txt';
        }
-       print $cgi->header(-type => "text/plain", -charset => 'utf-8', '-content-disposition' => "inline; filename=\"$save_as\"");
-       open my $fd, "-|", "$gitbin/git-cat-file blob $hash" or return;
+
+       print $cgi->header(-type => "$type", '-content-disposition' => "inline; filename=\"$save_as\"");
        undef $/;
+       binmode STDOUT, ':raw';
        print <$fd>;
+       binmode STDOUT, ':utf8'; # as set at the beginning of gitweb.cgi
        $/ = "\n";
        close $fd;
 }
@@ -1617,7 +1658,7 @@ sub git_tree {
                        print "<tr class=\"light\">\n";
                }
                $alternate ^= 1;
-               print "<td style=\"font-family:monospace\">" . mode_str($t_mode) . "</td>\n";
+               print "<td class=\"mode\">" . mode_str($t_mode) . "</td>\n";
                if ($t_type eq "blob") {
                        print "<td class=\"list\">" .
                              $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$t_hash$base_key;f=$base$t_name"), -class => "list"}, esc_html($t_name)) .
@@ -1883,7 +1924,7 @@ sub git_commit {
              "<tr>" .
              "<td></td><td> $ad{'rfc2822'}";
        if ($ad{'hour_local'} < 6) {
-               printf(" (<span style=\"color: #cc0000;\">%02d:%02d</span> %s)", $ad{'hour_local'}, $ad{'minute_local'}, $ad{'tz_local'});
+               printf(" (<span class=\"atnight\">%02d:%02d</span> %s)", $ad{'hour_local'}, $ad{'minute_local'}, $ad{'tz_local'});
        } else {
                printf(" (%02d:%02d %s)", $ad{'hour_local'}, $ad{'minute_local'}, $ad{'tz_local'});
        }
@@ -1891,10 +1932,10 @@ sub git_commit {
              "</tr>\n";
        print "<tr><td>committer</td><td>" . esc_html($co{'committer'}) . "</td></tr>\n";
        print "<tr><td></td><td> $cd{'rfc2822'}" . sprintf(" (%02d:%02d %s)", $cd{'hour_local'}, $cd{'minute_local'}, $cd{'tz_local'}) . "</td></tr>\n";
-       print "<tr><td>commit</td><td style=\"font-family:monospace\">$co{'id'}</td></tr>\n";
+       print "<tr><td>commit</td><td class=\"sha1\">$co{'id'}</td></tr>\n";
        print "<tr>" .
              "<td>tree</td>" .
-             "<td style=\"font-family:monospace\">" .
+             "<td class=\"sha1\">" .
              $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tree;h=$co{'tree'};hb=$hash"), class => "list"}, $co{'tree'}) .
              "</td>" .
              "<td class=\"link\">" . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tree;h=$co{'tree'};hb=$hash")}, "tree") .
@@ -1904,14 +1945,14 @@ sub git_commit {
        foreach my $par (@$parents) {
                print "<tr>" .
                      "<td>parent</td>" .
-                     "<td style=\"font-family:monospace\">" . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$par"), class => "list"}, $par) . "</td>" .
+                     "<td class=\"sha1\">" . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$par"), class => "list"}, $par) . "</td>" .
                      "<td class=\"link\">" .
                      $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$par")}, "commit") .
                      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff;h=$hash;hp=$par")}, "commitdiff") .
                      "</td>" .
                      "</tr>\n";
        }
-       print "</table>". 
+       print "</table>".
              "</div>\n";
        print "<div class=\"page_body\">\n";
        my $comment = $co{'comment'};
@@ -1929,7 +1970,7 @@ sub git_commit {
                }
                if ($line =~ m/^ *(signed[ \-]off[ \-]by[ :]|acked[ \-]by[ :]|cc[ :])/i) {
                        $signed = 1;
-                       print "<span style=\"color: #888888\">" . esc_html($line) . "</span><br/>\n";
+                       print "<span class=\"signoff\">" . esc_html($line) . "</span><br/>\n";
                } else {
                        $signed = 0;
                        print format_log_line_html($line) . "<br/>\n";
@@ -1941,7 +1982,7 @@ sub git_commit {
                print(($#difftree + 1) . " files changed:\n");
        }
        print "</div>\n";
-       print "<table cellspacing=\"0\">\n";
+       print "<table class=\"diff_tree\">\n";
        my $alternate = 0;
        foreach my $line (@difftree) {
                # ':100644 100644 03b218260e99b78c6df0ed378e59ed9205ccc96d 3b93d5e7cc7f7dd4ebed13a5cc1a4ad976fc94d8 M      ls-files.c'
@@ -1969,12 +2010,12 @@ sub git_commit {
                        }
                        print "<td>" .
                              $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$to_id;hb=$hash;f=$file"), -class => "list"}, esc_html($file)) . "</td>\n" .
-                             "<td><span style=\"color: #008000;\">[new " . file_type($to_mode) . "$mode_chng]</span></td>\n" .
+                             "<td><span class=\"file_status new\">[new " . file_type($to_mode) . "$mode_chng]</span></td>\n" .
                              "<td class=\"link\">" . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$to_id;hb=$hash;f=$file")}, "blob") . "</td>\n";
                } elsif ($status eq "D") {
                        print "<td>" .
                              $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$from_id;hb=$hash;f=$file"), -class => "list"}, esc_html($file)) . "</td>\n" .
-                             "<td><span style=\"color: #c00000;\">[deleted " . file_type($from_mode). "]</span></td>\n" .
+                             "<td><span class=\"file_status deleted\">[deleted " . file_type($from_mode). "]</span></td>\n" .
                              "<td class=\"link\">" .
                              $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$from_id;hb=$hash;f=$file")}, "blob") .
                              " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=history;h=$hash;f=$file")}, "history") .
@@ -1982,7 +2023,7 @@ sub git_commit {
                } elsif ($status eq "M" || $status eq "T") {
                        my $mode_chnge = "";
                        if ($from_mode != $to_mode) {
-                               $mode_chnge = " <span style=\"color: #777777;\">[changed";
+                               $mode_chnge = " <span class=\"file_status mode_chnge\">[changed";
                                if (((oct $from_mode) & S_IFMT) != ((oct $to_mode) & S_IFMT)) {
                                        $mode_chnge .= " from " . file_type($from_mode) . " to " . file_type($to_mode);
                                }
@@ -2018,7 +2059,7 @@ sub git_commit {
                        }
                        print "<td>" .
                              $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$to_id;hb=$hash;f=$to_file"), -class => "list"}, esc_html($to_file)) . "</td>\n" .
-                             "<td><span style=\"color: #777777;\">[moved from " .
+                             "<td><span class=\"file_status moved\">[moved from " .
                              $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$from_id;hb=$hash;f=$from_file"), -class => "list"}, esc_html($from_file)) .
                              " with " . (int $similarity) . "% similarity$mode_chng]</span></td>\n" .
                              "<td class=\"link\">" .
@@ -2378,7 +2419,7 @@ sub git_search {
                                        my $match = esc_html($2) || "";
                                        my $trail = esc_html($3) || "";
                                        $trail = chop_str($trail, 30, 10);
-                                       my $text = "$lead<span style=\"color:#e00000\">$match</span>$trail";
+                                       my $text = "$lead<span class=\"match\">$match</span>$trail";
                                        print chop_str($text, 80, 5) . "<br/>\n";
                                }
                        }
@@ -2427,7 +2468,7 @@ sub git_search {
                                        while (my $setref = shift @files) {
                                                my %set = %$setref;
                                                print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$set{'id'};hb=$co{'id'};f=$set{'file'}"), class => "list"},
-                                                     "<span style=\"color:#e00000\">" . esc_html($set{'file'}) . "</span>") .
+                                                     "<span class=\"match\">" . esc_html($set{'file'}) . "</span>") .
                                                      "<br/>\n";
                                        }
                                        print "</td>\n" .
diff --git a/gitweb/gitweb.css b/gitweb/gitweb.css
new file mode 100644 (file)
index 0000000..98410f5
--- /dev/null
@@ -0,0 +1,320 @@
+body {
+       font-family: sans-serif;
+       font-size: 12px;
+       border: solid #d9d8d1;
+       border-width: 1px;
+       margin: 10px;
+       background-color: #ffffff;
+       color: #000000;
+}
+
+a {
+       color: #0000cc;
+}
+
+a:hover, a:visited, a:active {
+       color: #880000;
+}
+
+div.page_header {
+       height: 25px;
+       padding: 8px;
+       font-size: 18px;
+       font-weight: bold;
+       background-color: #d9d8d1;
+}
+
+div.page_header a:visited, a.header {
+       color: #0000cc;
+}
+
+div.page_header a:hover {
+       color: #880000;
+}
+
+div.page_nav {
+       padding: 8px;
+}
+
+div.page_nav a:visited {
+       color: #0000cc;
+}
+
+div.page_path {
+       padding: 8px;
+       border: solid #d9d8d1;
+       border-width: 0px 0px 1px;
+}
+
+div.page_footer {
+       height: 17px;
+       padding: 4px 8px;
+       background-color: #d9d8d1;
+}
+
+div.page_footer_text {
+       float: left;
+       color: #555555;
+       font-style: italic;
+}
+
+div.page_body {
+       padding: 8px;
+}
+
+div.title, a.title {
+       display: block;
+       padding: 6px 8px;
+       font-weight: bold;
+       background-color: #edece6;
+       text-decoration: none;
+       color: #000000;
+}
+
+a.title:hover {
+       background-color: #d9d8d1;
+}
+
+div.title_text {
+       padding: 6px 0px;
+       border: solid #d9d8d1;
+       border-width: 0px 0px 1px;
+}
+
+div.log_body {
+       padding: 8px 8px 8px 150px;
+}
+
+span.age {
+       position: relative;
+       float: left;
+       width: 142px;
+       font-style: italic;
+}
+
+div.page_body span.signoff {
+       color: #888888;
+}
+
+div.log_link {
+       padding: 0px 8px;
+       font-size: 10px;
+       font-family: sans-serif;
+       font-style: normal;
+       position: relative;
+       float: left;
+       width: 136px;
+}
+
+div.list_head {
+       padding: 6px 8px 4px;
+       border: solid #d9d8d1;
+       border-width: 1px 0px 0px;
+       font-style: italic;
+}
+
+a.list {
+       text-decoration: none;
+       color: #000000;
+}
+
+a.list:hover {
+       text-decoration: underline;
+       color: #880000;
+}
+
+a.text {
+       text-decoration: none;
+       color: #0000cc;
+}
+
+a.text:visited {
+       text-decoration: none;
+       color: #880000;
+}
+
+a.text:hover {
+       text-decoration: underline;
+       color: #880000;
+}
+
+table {
+       padding: 8px 4px;
+}
+
+table.project_list, table.diff_tree {
+       border-spacing: 0;
+}
+
+table.blame {
+       border-collapse: collapse;
+}
+
+th {
+       padding: 2px 5px;
+       font-size: 12px;
+       text-align: left;
+}
+
+tr.light:hover {
+       background-color: #edece6;
+}
+
+tr.dark {
+       background-color: #f6f6f0;
+}
+
+tr.dark:hover {
+       background-color: #edece6;
+}
+
+td {
+       padding: 2px 5px;
+       font-size: 12px;
+       vertical-align: top;
+}
+
+td.link {
+       padding: 2px 5px;
+       font-family: sans-serif;
+       font-size: 10px;
+}
+
+td.sha1 {
+       font-family: monospace;
+}
+
+td.error {
+       color: red;
+       background-color: yellow;
+}
+
+table.diff_tree span.file_status.new {
+       color: #008000;
+}
+
+table.diff_tree span.file_status.deleted {
+       color: #c00000;
+}
+
+table.diff_tree span.file_status.moved,
+table.diff_tree span.file_status.mode_chnge {
+       color: #777777;
+}
+
+/* age2: 60*60*24*2 <= age */
+table.project_list td.age2, table.blame td.age2 {
+       font-style: italic;
+}
+
+/* age1: 60*60*2 <= age < 60*60*24*2 */
+table.project_list td.age1 {
+       color: #009900;
+       font-style: italic;
+}
+
+table.blame td.age1 {
+       color: #009900;
+       background: transparent;
+}
+
+/* age0: age < 60*60*2 */
+table.project_list td.age0 {
+       color: #009900;
+       font-style: italic;
+       font-weight: bold;
+}
+
+table.blame td.age0 {
+       color: #009900;
+       background: transparent;
+       font-weight: bold;
+}
+
+td.pre, div.pre, div.diff {
+       font-family: monospace;
+       font-size: 12px;
+       white-space: pre;
+}
+
+td.mode {
+       font-family: monospace;
+}
+
+div.diff.add {
+       color: #008800;
+}
+
+div.diff.rem {
+       color: #cc0000;
+}
+
+div.diff.chunk_header {
+       color: #990099;
+}
+
+div.diff_info {
+       font-family: monospace;
+       color: #000099;
+       background-color: #edece6;
+       font-style: italic;
+}
+
+div.index_include {
+       border: solid #d9d8d1;
+       border-width: 0px 0px 1px;
+       padding: 12px 8px;
+}
+
+div.search {
+       margin: 4px 8px;
+       position: absolute;
+       top: 56px;
+       right: 12px
+}
+
+td.linenr {
+       text-align: right;
+}
+
+a.linenr {
+       color: #999999;
+       text-decoration: none
+}
+
+a.rss_logo {
+       float: right;
+       padding: 3px 0px;
+       width: 35px;
+       line-height: 10px;
+       border: 1px solid;
+       border-color: #fcc7a5 #7d3302 #3e1a01 #ff954e;
+       color: #ffffff;
+       background-color: #ff6600;
+       font-weight: bold;
+       font-family: sans-serif;
+       font-size: 10px;
+       text-align: center;
+       text-decoration: none;
+}
+
+a.rss_logo:hover {
+       background-color: #ee5500;
+}
+
+span.tag {
+       padding: 0px 4px;
+       font-size: 10px;
+       font-weight: normal;
+       background-color: #ffffaa;
+       border: 1px solid;
+       border-color: #ffffcc #ffee00 #ffee00 #ffffcc;
+}
+
+span.atnight {
+       color: #cc0000;
+}
+
+span.match {
+       color: #e00000;
+}
index da1a7f5416a4bf3a633d3577382984f528694a78..2b63d89501cc8838cca96506b3532d33fc452a15 100644 (file)
@@ -123,7 +123,7 @@ static size_t fwrite_sha1_file(void *ptr, size_t eltsize, size_t nmemb,
        struct object_request *obj_req = (struct object_request *)data;
        do {
                ssize_t retval = write(obj_req->local,
-                                      ptr + posn, size - posn);
+                                      (char *) ptr + posn, size - posn);
                if (retval < 0)
                        return posn;
                posn += retval;
@@ -1136,13 +1136,14 @@ int fetch(unsigned char *sha1)
 
 static inline int needs_quote(int ch)
 {
-       switch (ch) {
-       case '/': case '-': case '.':
-       case 'A'...'Z': case 'a'...'z': case '0'...'9':
+       if (((ch >= 'A') && (ch <= 'Z'))
+                       || ((ch >= 'a') && (ch <= 'z'))
+                       || ((ch >= '0') && (ch <= '9'))
+                       || (ch == '/')
+                       || (ch == '-')
+                       || (ch == '.'))
                return 0;
-       default:
-               return 1;
-       }
+       return 1;
 }
 
 static inline int hex(int v)
index ba64f8fff58936ab59074b835b9df06eecf3031e..8d472f0202827db726ede451be3fd63940c38c96 100644 (file)
@@ -196,7 +196,7 @@ static size_t fwrite_sha1_file(void *ptr, size_t eltsize, size_t nmemb,
        struct transfer_request *request = (struct transfer_request *)data;
        do {
                ssize_t retval = write(request->local_fileno,
-                                      ptr + posn, size - posn);
+                                      (char *) ptr + posn, size - posn);
                if (retval < 0)
                        return posn;
                posn += retval;
@@ -1077,13 +1077,14 @@ static int fetch_indices(void)
 
 static inline int needs_quote(int ch)
 {
-       switch (ch) {
-       case '/': case '-': case '.':
-       case 'A'...'Z': case 'a'...'z': case '0'...'9':
+       if (((ch >= 'A') && (ch <= 'Z'))
+                       || ((ch >= 'a') && (ch <= 'z'))
+                       || ((ch >= '0') && (ch <= '9'))
+                       || (ch == '/')
+                       || (ch == '-')
+                       || (ch == '.'))
                return 0;
-       default:
-               return 1;
-       }
+       return 1;
 }
 
 static inline int hex(int v)
@@ -1171,7 +1172,7 @@ static void one_remote_object(const char *hex)
 
        obj->flags |= REMOTE;
        if (!object_list_contains(objects, obj))
-               add_object(obj, &objects, NULL, "");
+               object_list_insert(obj, &objects);
 }
 
 static void handle_lockprop_ctx(struct xml_ctx *ctx, int tag_closed)
@@ -1699,6 +1700,15 @@ static int locking_available(void)
        return lock_flags;
 }
 
+struct object_list **add_one_object(struct object *obj, struct object_list **p)
+{
+       struct object_list *entry = xmalloc(sizeof(struct object_list));
+       entry->item = obj;
+       entry->next = *p;
+       *p = entry;
+       return &entry->next;
+}
+
 static struct object_list **process_blob(struct blob *blob,
                                         struct object_list **p,
                                         struct name_path *path,
@@ -1712,8 +1722,7 @@ static struct object_list **process_blob(struct blob *blob,
                return p;
 
        obj->flags |= SEEN;
-       name = strdup(name);
-       return add_object(obj, p, path, name);
+       return add_one_object(obj, p);
 }
 
 static struct object_list **process_tree(struct tree *tree,
@@ -1735,7 +1744,7 @@ static struct object_list **process_tree(struct tree *tree,
 
        obj->flags |= SEEN;
        name = strdup(name);
-       p = add_object(obj, p, NULL, name);
+       p = add_one_object(obj, p);
        me.up = path;
        me.elem = name;
        me.elem_len = strlen(name);
@@ -1756,8 +1765,9 @@ static struct object_list **process_tree(struct tree *tree,
 
 static int get_delta(struct rev_info *revs, struct remote_lock *lock)
 {
+       int i;
        struct commit *commit;
-       struct object_list **p = &objects, *pending;
+       struct object_list **p = &objects;
        int count = 0;
 
        while ((commit = get_revision(revs)) != NULL) {
@@ -1767,15 +1777,16 @@ static int get_delta(struct rev_info *revs, struct remote_lock *lock)
                        count += add_send_request(&commit->object, lock);
        }
 
-       for (pending = revs->pending_objects; pending; pending = pending->next) {
-               struct object *obj = pending->item;
-               const char *name = pending->name;
+       for (i = 0; i < revs->pending.nr; i++) {
+               struct object_array_entry *entry = revs->pending.objects + i;
+               struct object *obj = entry->item;
+               const char *name = entry->name;
 
                if (obj->flags & (UNINTERESTING | SEEN))
                        continue;
                if (obj->type == TYPE_TAG) {
                        obj->flags |= SEEN;
-                       p = add_object(obj, p, NULL, name);
+                       p = add_one_object(obj, p);
                        continue;
                }
                if (obj->type == TYPE_TREE) {
diff --git a/http.c b/http.c
index 08769cc7cd4282a485fb9cd22c4ecfa97096a419..6c1937b676ecd584cededed1c82df30448046ebe 100644 (file)
--- a/http.c
+++ b/http.c
@@ -34,7 +34,7 @@ size_t fread_buffer(void *ptr, size_t eltsize, size_t nmemb,
        size_t size = eltsize * nmemb;
        if (size > buffer->size - buffer->posn)
                size = buffer->size - buffer->posn;
-       memcpy(ptr, buffer->buffer + buffer->posn, size);
+       memcpy(ptr, (char *) buffer->buffer + buffer->posn, size);
        buffer->posn += size;
        return size;
 }
@@ -49,7 +49,7 @@ size_t fwrite_buffer(const void *ptr, size_t eltsize,
                        buffer->size = buffer->posn + size;
                buffer->buffer = xrealloc(buffer->buffer, buffer->size);
        }
-       memcpy(buffer->buffer + buffer->posn, ptr, size);
+       memcpy((char *) buffer->buffer + buffer->posn, ptr, size);
        buffer->posn += size;
        data_received++;
        return size;
index 285ad29afb4c4126010ddc2f76963b4f8f1cce70..94e39cd94cb26b2147e214ce4032121df871834e 100644 (file)
@@ -93,7 +93,7 @@ typedef struct {
        char *data;
        int len;
        unsigned char flags;
-       unsigned char crlf:1;
+       unsigned int crlf:1;
 } msg_data_t;
 
 #define DRV_OK          0
diff --git a/mailinfo.c b/mailinfo.c
deleted file mode 100644 (file)
index d9b74f3..0000000
+++ /dev/null
@@ -1,831 +0,0 @@
-/*
- * Another stupid program, this one parsing the headers of an
- * email to figure out authorship and subject
- */
-#define _GNU_SOURCE
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <ctype.h>
-#ifndef NO_ICONV
-#include <iconv.h>
-#endif
-#include "git-compat-util.h"
-#include "cache.h"
-
-static FILE *cmitmsg, *patchfile;
-
-static int keep_subject = 0;
-static char *metainfo_charset = NULL;
-static char line[1000];
-static char date[1000];
-static char name[1000];
-static char email[1000];
-static char subject[1000];
-
-static enum  {
-       TE_DONTCARE, TE_QP, TE_BASE64,
-} transfer_encoding;
-static char charset[256];
-
-static char multipart_boundary[1000];
-static int multipart_boundary_len;
-static int patch_lines = 0;
-
-static char *sanity_check(char *name, char *email)
-{
-       int len = strlen(name);
-       if (len < 3 || len > 60)
-               return email;
-       if (strchr(name, '@') || strchr(name, '<') || strchr(name, '>'))
-               return email;
-       return name;
-}
-
-static int bogus_from(char *line)
-{
-       /* John Doe <johndoe> */
-       char *bra, *ket, *dst, *cp;
-
-       /* This is fallback, so do not bother if we already have an
-        * e-mail address.
-        */ 
-       if (*email)
-               return 0;
-
-       bra = strchr(line, '<');
-       if (!bra)
-               return 0;
-       ket = strchr(bra, '>');
-       if (!ket)
-               return 0;
-
-       for (dst = email, cp = bra+1; cp < ket; )
-               *dst++ = *cp++;
-       *dst = 0;
-       for (cp = line; isspace(*cp); cp++)
-               ;
-       for (bra--; isspace(*bra); bra--)
-               *bra = 0;
-       cp = sanity_check(cp, email);
-       strcpy(name, cp);
-       return 1;
-}
-
-static int handle_from(char *in_line)
-{
-       char line[1000];
-       char *at;
-       char *dst;
-
-       strcpy(line, in_line);
-       at = strchr(line, '@');
-       if (!at)
-               return bogus_from(line);
-
-       /*
-        * If we already have one email, don't take any confusing lines
-        */
-       if (*email && strchr(at+1, '@'))
-               return 0;
-
-       /* Pick up the string around '@', possibly delimited with <>
-        * pair; that is the email part.  White them out while copying.
-        */
-       while (at > line) {
-               char c = at[-1];
-               if (isspace(c))
-                       break;
-               if (c == '<') {
-                       at[-1] = ' ';
-                       break;
-               }
-               at--;
-       }
-       dst = email;
-       for (;;) {
-               unsigned char c = *at;
-               if (!c || c == '>' || isspace(c)) {
-                       if (c == '>')
-                               *at = ' ';
-                       break;
-               }
-               *at++ = ' ';
-               *dst++ = c;
-       }
-       *dst++ = 0;
-
-       /* The remainder is name.  It could be "John Doe <john.doe@xz>"
-        * or "john.doe@xz (John Doe)", but we have whited out the
-        * email part, so trim from both ends, possibly removing
-        * the () pair at the end.
-        */
-       at = line + strlen(line);
-       while (at > line) {
-               unsigned char c = *--at;
-               if (!isspace(c)) {
-                       at[(c == ')') ? 0 : 1] = 0;
-                       break;
-               }
-       }
-
-       at = line;
-       for (;;) {
-               unsigned char c = *at;
-               if (!c || !isspace(c)) {
-                       if (c == '(')
-                               at++;
-                       break;
-               }
-               at++;
-       }
-       at = sanity_check(at, email);
-       strcpy(name, at);
-       return 1;
-}
-
-static int handle_date(char *line)
-{
-       strcpy(date, line);
-       return 0;
-}
-
-static int handle_subject(char *line)
-{
-       strcpy(subject, line);
-       return 0;
-}
-
-/* NOTE NOTE NOTE.  We do not claim we do full MIME.  We just attempt
- * to have enough heuristics to grok MIME encoded patches often found
- * on our mailing lists.  For example, we do not even treat header lines
- * case insensitively.
- */
-
-static int slurp_attr(const char *line, const char *name, char *attr)
-{
-       char *ends, *ap = strcasestr(line, name);
-       size_t sz;
-
-       if (!ap) {
-               *attr = 0;
-               return 0;
-       }
-       ap += strlen(name);
-       if (*ap == '"') {
-               ap++;
-               ends = "\"";
-       }
-       else
-               ends = "; \t";
-       sz = strcspn(ap, ends);
-       memcpy(attr, ap, sz);
-       attr[sz] = 0;
-       return 1;
-}
-
-static int handle_subcontent_type(char *line)
-{
-       /* We do not want to mess with boundary.  Note that we do not
-        * handle nested multipart.
-        */
-       if (strcasestr(line, "boundary=")) {
-               fprintf(stderr, "Not handling nested multipart message.\n");
-               exit(1);
-       }
-       slurp_attr(line, "charset=", charset);
-       if (*charset) {
-               int i, c;
-               for (i = 0; (c = charset[i]) != 0; i++)
-                       charset[i] = tolower(c);
-       }
-       return 0;
-}
-
-static int handle_content_type(char *line)
-{
-       *multipart_boundary = 0;
-       if (slurp_attr(line, "boundary=", multipart_boundary + 2)) {
-               memcpy(multipart_boundary, "--", 2);
-               multipart_boundary_len = strlen(multipart_boundary);
-       }
-       slurp_attr(line, "charset=", charset);
-       return 0;
-}
-
-static int handle_content_transfer_encoding(char *line)
-{
-       if (strcasestr(line, "base64"))
-               transfer_encoding = TE_BASE64;
-       else if (strcasestr(line, "quoted-printable"))
-               transfer_encoding = TE_QP;
-       else
-               transfer_encoding = TE_DONTCARE;
-       return 0;
-}
-
-static int is_multipart_boundary(const char *line)
-{
-       return (!memcmp(line, multipart_boundary, multipart_boundary_len));
-}
-
-static int eatspace(char *line)
-{
-       int len = strlen(line);
-       while (len > 0 && isspace(line[len-1]))
-               line[--len] = 0;
-       return len;
-}
-
-#define SEEN_FROM 01
-#define SEEN_DATE 02
-#define SEEN_SUBJECT 04
-#define SEEN_BOGUS_UNIX_FROM 010
-#define SEEN_PREFIX  020
-
-/* First lines of body can have From:, Date:, and Subject: or empty */
-static void handle_inbody_header(int *seen, char *line)
-{
-       if (*seen & SEEN_PREFIX)
-               return;
-       if (isspace(*line)) {
-               char *cp;
-               for (cp = line + 1; *cp; cp++) {
-                       if (!isspace(*cp))
-                               break;
-               }
-               if (!*cp)
-                       return;
-       }
-       if (!memcmp(">From", line, 5) && isspace(line[5])) {
-               if (!(*seen & SEEN_BOGUS_UNIX_FROM)) {
-                       *seen |= SEEN_BOGUS_UNIX_FROM;
-                       return;
-               }
-       }
-       if (!memcmp("From:", line, 5) && isspace(line[5])) {
-               if (!(*seen & SEEN_FROM) && handle_from(line+6)) {
-                       *seen |= SEEN_FROM;
-                       return;
-               }
-       }
-       if (!memcmp("Date:", line, 5) && isspace(line[5])) {
-               if (!(*seen & SEEN_DATE)) {
-                       handle_date(line+6);
-                       *seen |= SEEN_DATE;
-                       return;
-               }
-       }
-       if (!memcmp("Subject:", line, 8) && isspace(line[8])) {
-               if (!(*seen & SEEN_SUBJECT)) {
-                       handle_subject(line+9);
-                       *seen |= SEEN_SUBJECT;
-                       return;
-               }
-       }
-       if (!memcmp("[PATCH]", line, 7) && isspace(line[7])) {
-               if (!(*seen & SEEN_SUBJECT)) {
-                       handle_subject(line);
-                       *seen |= SEEN_SUBJECT;
-                       return;
-               }
-       }
-       *seen |= SEEN_PREFIX;
-}
-
-static char *cleanup_subject(char *subject)
-{
-       if (keep_subject)
-               return subject;
-       for (;;) {
-               char *p;
-               int len, remove;
-               switch (*subject) {
-               case 'r': case 'R':
-                       if (!memcmp("e:", subject+1, 2)) {
-                               subject +=3;
-                               continue;
-                       }
-                       break;
-               case ' ': case '\t': case ':':
-                       subject++;
-                       continue;
-
-               case '[':
-                       p = strchr(subject, ']');
-                       if (!p) {
-                               subject++;
-                               continue;
-                       }
-                       len = strlen(p);
-                       remove = p - subject;
-                       if (remove <= len *2) {
-                               subject = p+1;
-                               continue;
-                       }       
-                       break;
-               }
-               eatspace(subject);
-               return subject;
-       }
-}                      
-
-static void cleanup_space(char *buf)
-{
-       unsigned char c;
-       while ((c = *buf) != 0) {
-               buf++;
-               if (isspace(c)) {
-                       buf[-1] = ' ';
-                       c = *buf;
-                       while (isspace(c)) {
-                               int len = strlen(buf);
-                               memmove(buf, buf+1, len);
-                               c = *buf;
-                       }
-               }
-       }
-}
-
-static void decode_header_bq(char *it);
-typedef int (*header_fn_t)(char *);
-struct header_def {
-       const char *name;
-       header_fn_t func;
-       int namelen;
-};
-
-static void check_header(char *line, struct header_def *header)
-{
-       int i;
-
-       if (header[0].namelen <= 0) {
-               for (i = 0; header[i].name; i++)
-                       header[i].namelen = strlen(header[i].name);
-       }
-       for (i = 0; header[i].name; i++) {
-               int len = header[i].namelen;
-               if (!strncasecmp(line, header[i].name, len) &&
-                   line[len] == ':' && isspace(line[len + 1])) {
-                       /* Unwrap inline B and Q encoding, and optionally
-                        * normalize the meta information to utf8.
-                        */
-                       decode_header_bq(line + len + 2);
-                       header[i].func(line + len + 2);
-                       break;
-               }
-       }
-}
-
-static void check_subheader_line(char *line)
-{
-       static struct header_def header[] = {
-               { "Content-Type", handle_subcontent_type },
-               { "Content-Transfer-Encoding",
-                 handle_content_transfer_encoding },
-               { NULL },
-       };
-       check_header(line, header);
-}
-static void check_header_line(char *line)
-{
-       static struct header_def header[] = {
-               { "From", handle_from },
-               { "Date", handle_date },
-               { "Subject", handle_subject },
-               { "Content-Type", handle_content_type },
-               { "Content-Transfer-Encoding",
-                 handle_content_transfer_encoding },
-               { NULL },
-       };
-       check_header(line, header);
-}
-
-static int is_rfc2822_header(char *line)
-{
-       /*
-        * The section that defines the loosest possible
-        * field name is "3.6.8 Optional fields".
-        *
-        * optional-field = field-name ":" unstructured CRLF
-        * field-name = 1*ftext
-        * ftext = %d33-57 / %59-126
-        */
-       int ch;
-       char *cp = line;
-       while ((ch = *cp++)) {
-               if (ch == ':')
-                       return cp != line;
-               if ((33 <= ch && ch <= 57) ||
-                   (59 <= ch && ch <= 126))
-                       continue;
-               break;
-       }
-       return 0;
-}
-
-static int read_one_header_line(char *line, int sz, FILE *in)
-{
-       int ofs = 0;
-       while (ofs < sz) {
-               int peek, len;
-               if (fgets(line + ofs, sz - ofs, in) == NULL)
-                       break;
-               len = eatspace(line + ofs);
-               if ((len == 0) || !is_rfc2822_header(line)) {
-                       /* Re-add the newline */
-                       line[ofs + len] = '\n';
-                       line[ofs + len + 1] = '\0';
-                       break;
-               }
-               ofs += len;
-               /* Yuck, 2822 header "folding" */
-               peek = fgetc(in); ungetc(peek, in);
-               if (peek != ' ' && peek != '\t')
-                       break;
-       }
-       /* Count mbox From headers as headers */
-       if (!ofs && !memcmp(line, "From ", 5))
-               ofs = 1;
-       return ofs;
-}
-
-static unsigned hexval(int c)
-{
-       if (c >= '0' && c <= '9')
-               return c - '0';
-       if (c >= 'a' && c <= 'f')
-               return c - 'a' + 10;
-       if (c >= 'A' && c <= 'F')
-               return c - 'A' + 10;
-       return ~0;
-}
-
-static int decode_q_segment(char *in, char *ot, char *ep, int rfc2047)
-{
-       int c;
-       while ((c = *in++) != 0 && (in <= ep)) {
-               if (c == '=') {
-                       int d = *in++;
-                       if (d == '\n' || !d)
-                               break; /* drop trailing newline */
-                       *ot++ = ((hexval(d) << 4) | hexval(*in++));
-                       continue;
-               }
-               if (rfc2047 && c == '_') /* rfc2047 4.2 (2) */
-                       c = 0x20;
-               *ot++ = c;
-       }
-       *ot = 0;
-       return 0;
-}
-
-static int decode_b_segment(char *in, char *ot, char *ep)
-{
-       /* Decode in..ep, possibly in-place to ot */
-       int c, pos = 0, acc = 0;
-
-       while ((c = *in++) != 0 && (in <= ep)) {
-               if (c == '+')
-                       c = 62;
-               else if (c == '/')
-                       c = 63;
-               else if ('A' <= c && c <= 'Z')
-                       c -= 'A';
-               else if ('a' <= c && c <= 'z')
-                       c -= 'a' - 26;
-               else if ('0' <= c && c <= '9')
-                       c -= '0' - 52;
-               else if (c == '=') {
-                       /* padding is almost like (c == 0), except we do
-                        * not output NUL resulting only from it;
-                        * for now we just trust the data.
-                        */
-                       c = 0;
-               }
-               else
-                       continue; /* garbage */
-               switch (pos++) {
-               case 0:
-                       acc = (c << 2);
-                       break;
-               case 1:
-                       *ot++ = (acc | (c >> 4));
-                       acc = (c & 15) << 4;
-                       break;
-               case 2:
-                       *ot++ = (acc | (c >> 2));
-                       acc = (c & 3) << 6;
-                       break;
-               case 3:
-                       *ot++ = (acc | c);
-                       acc = pos = 0;
-                       break;
-               }
-       }
-       *ot = 0;
-       return 0;
-}
-
-static void convert_to_utf8(char *line, char *charset)
-{
-#ifndef NO_ICONV
-       char *in, *out;
-       size_t insize, outsize, nrc;
-       char outbuf[4096]; /* cheat */
-       static char latin_one[] = "latin1";
-       char *input_charset = *charset ? charset : latin_one;
-       iconv_t conv = iconv_open(metainfo_charset, input_charset);
-
-       if (conv == (iconv_t) -1) {
-               static int warned_latin1_once = 0;
-               if (input_charset != latin_one) {
-                       fprintf(stderr, "cannot convert from %s to %s\n",
-                               input_charset, metainfo_charset);
-                       *charset = 0;
-               }
-               else if (!warned_latin1_once) {
-                       warned_latin1_once = 1;
-                       fprintf(stderr, "tried to convert from %s to %s, "
-                               "but your iconv does not work with it.\n",
-                               input_charset, metainfo_charset);
-               }
-               return;
-       }
-       in = line;
-       insize = strlen(in);
-       out = outbuf;
-       outsize = sizeof(outbuf);
-       nrc = iconv(conv, &in, &insize, &out, &outsize);
-       iconv_close(conv);
-       if (nrc == (size_t) -1)
-               return;
-       *out = 0;
-       strcpy(line, outbuf);
-#endif
-}
-
-static void decode_header_bq(char *it)
-{
-       char *in, *out, *ep, *cp, *sp;
-       char outbuf[1000];
-
-       in = it;
-       out = outbuf;
-       while ((ep = strstr(in, "=?")) != NULL) {
-               int sz, encoding;
-               char charset_q[256], piecebuf[256];
-               if (in != ep) {
-                       sz = ep - in;
-                       memcpy(out, in, sz);
-                       out += sz;
-                       in += sz;
-               }
-               /* E.g.
-                * ep : "=?iso-2022-jp?B?GyR...?= foo"
-                * ep : "=?ISO-8859-1?Q?Foo=FCbar?= baz"
-                */
-               ep += 2;
-               cp = strchr(ep, '?');
-               if (!cp)
-                       return; /* no munging */
-               for (sp = ep; sp < cp; sp++)
-                       charset_q[sp - ep] = tolower(*sp);
-               charset_q[cp - ep] = 0;
-               encoding = cp[1];
-               if (!encoding || cp[2] != '?')
-                       return; /* no munging */
-               ep = strstr(cp + 3, "?=");
-               if (!ep)
-                       return; /* no munging */
-               switch (tolower(encoding)) {
-               default:
-                       return; /* no munging */
-               case 'b':
-                       sz = decode_b_segment(cp + 3, piecebuf, ep);
-                       break;
-               case 'q':
-                       sz = decode_q_segment(cp + 3, piecebuf, ep, 1);
-                       break;
-               }
-               if (sz < 0)
-                       return;
-               if (metainfo_charset)
-                       convert_to_utf8(piecebuf, charset_q);
-               strcpy(out, piecebuf);
-               out += strlen(out);
-               in = ep + 2;
-       }
-       strcpy(out, in);
-       strcpy(it, outbuf);
-}
-
-static void decode_transfer_encoding(char *line)
-{
-       char *ep;
-
-       switch (transfer_encoding) {
-       case TE_QP:
-               ep = line + strlen(line);
-               decode_q_segment(line, line, ep, 0);
-               break;
-       case TE_BASE64:
-               ep = line + strlen(line);
-               decode_b_segment(line, line, ep);
-               break;
-       case TE_DONTCARE:
-               break;
-       }
-}
-
-static void handle_info(void)
-{
-       char *sub;
-
-       sub = cleanup_subject(subject);
-       cleanup_space(name);
-       cleanup_space(date);
-       cleanup_space(email);
-       cleanup_space(sub);
-
-       printf("Author: %s\nEmail: %s\nSubject: %s\nDate: %s\n\n",
-              name, email, sub, date);
-}
-
-/* We are inside message body and have read line[] already.
- * Spit out the commit log.
- */
-static int handle_commit_msg(int *seen)
-{
-       if (!cmitmsg)
-               return 0;
-       do {
-               if (!memcmp("diff -", line, 6) ||
-                   !memcmp("---", line, 3) ||
-                   !memcmp("Index: ", line, 7))
-                       break;
-               if ((multipart_boundary[0] && is_multipart_boundary(line))) {
-                       /* We come here when the first part had only
-                        * the commit message without any patch.  We
-                        * pretend we have not seen this line yet, and
-                        * go back to the loop.
-                        */
-                       return 1;
-               }
-
-               /* Unwrap transfer encoding and optionally
-                * normalize the log message to UTF-8.
-                */
-               decode_transfer_encoding(line);
-               if (metainfo_charset)
-                       convert_to_utf8(line, charset);
-
-               handle_inbody_header(seen, line);
-               if (!(*seen & SEEN_PREFIX))
-                       continue;
-
-               fputs(line, cmitmsg);
-       } while (fgets(line, sizeof(line), stdin) != NULL);
-       fclose(cmitmsg);
-       cmitmsg = NULL;
-       return 0;
-}
-
-/* We have done the commit message and have the first
- * line of the patch in line[].
- */
-static void handle_patch(void)
-{
-       do {
-               if (multipart_boundary[0] && is_multipart_boundary(line))
-                       break;
-               /* Only unwrap transfer encoding but otherwise do not
-                * do anything.  We do *NOT* want UTF-8 conversion
-                * here; we are dealing with the user payload.
-                */
-               decode_transfer_encoding(line);
-               fputs(line, patchfile);
-               patch_lines++;
-       } while (fgets(line, sizeof(line), stdin) != NULL);
-}
-
-/* multipart boundary and transfer encoding are set up for us, and we
- * are at the end of the sub header.  do equivalent of handle_body up
- * to the next boundary without closing patchfile --- we will expect
- * that the first part to contain commit message and a patch, and
- * handle other parts as pure patches.
- */
-static int handle_multipart_one_part(int *seen)
-{
-       int n = 0;
-
-       while (fgets(line, sizeof(line), stdin) != NULL) {
-       again:
-               n++;
-               if (is_multipart_boundary(line))
-                       break;
-               if (handle_commit_msg(seen))
-                       goto again;
-               handle_patch();
-               break;
-       }
-       if (n == 0)
-               return -1;
-       return 0;
-}
-
-static void handle_multipart_body(void)
-{
-       int seen = 0;
-       int part_num = 0;
-
-       /* Skip up to the first boundary */
-       while (fgets(line, sizeof(line), stdin) != NULL)
-               if (is_multipart_boundary(line)) {
-                       part_num = 1;
-                       break;
-               }
-       if (!part_num)
-               return;
-       /* We are on boundary line.  Start slurping the subhead. */
-       while (1) {
-               int hdr = read_one_header_line(line, sizeof(line), stdin);
-               if (!hdr) {
-                       if (handle_multipart_one_part(&seen) < 0)
-                               return;
-                       /* Reset per part headers */
-                       transfer_encoding = TE_DONTCARE;
-                       charset[0] = 0;
-               }
-               else
-                       check_subheader_line(line);
-       }
-       fclose(patchfile);
-       if (!patch_lines) {
-               fprintf(stderr, "No patch found\n");
-               exit(1);
-       }
-}
-
-/* Non multipart message */
-static void handle_body(void)
-{
-       int seen = 0;
-
-       handle_commit_msg(&seen);
-       handle_patch();
-       fclose(patchfile);
-       if (!patch_lines) {
-               fprintf(stderr, "No patch found\n");
-               exit(1);
-       }
-}
-
-static const char mailinfo_usage[] =
-       "git-mailinfo [-k] [-u | --encoding=<encoding>] msg patch <mail >info";
-
-int main(int argc, char **argv)
-{
-       /* NEEDSWORK: might want to do the optional .git/ directory
-        * discovery
-        */
-       git_config(git_default_config);
-
-       while (1 < argc && argv[1][0] == '-') {
-               if (!strcmp(argv[1], "-k"))
-                       keep_subject = 1;
-               else if (!strcmp(argv[1], "-u"))
-                       metainfo_charset = git_commit_encoding;
-               else if (!strncmp(argv[1], "--encoding=", 11))
-                       metainfo_charset = argv[1] + 11;
-               else
-                       usage(mailinfo_usage);
-               argc--; argv++;
-       }
-
-       if (argc != 3)
-               usage(mailinfo_usage);
-       cmitmsg = fopen(argv[1], "w");
-       if (!cmitmsg) {
-               perror(argv[1]);
-               exit(1);
-       }
-       patchfile = fopen(argv[2], "w");
-       if (!patchfile) {
-               perror(argv[2]);
-               exit(1);
-       }
-       while (1) {
-               int hdr = read_one_header_line(line, sizeof(line), stdin);
-               if (!hdr) {
-                       if (multipart_boundary[0])
-                               handle_multipart_body();
-                       else
-                               handle_body();
-                       handle_info();
-                       break;
-               }
-               check_header_line(line);
-       }
-       return 0;
-}
diff --git a/mailsplit.c b/mailsplit.c
deleted file mode 100644 (file)
index 70a569c..0000000
+++ /dev/null
@@ -1,188 +0,0 @@
-/*
- * Totally braindamaged mbox splitter program.
- *
- * It just splits a mbox into a list of files: "0001" "0002" ..
- * so you can process them further from there.
- */
-#include <unistd.h>
-#include <stdlib.h>
-#include <fcntl.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <string.h>
-#include <stdio.h>
-#include "cache.h"
-
-static const char git_mailsplit_usage[] =
-"git-mailsplit [-d<prec>] [-f<n>] [-b] -o<directory> <mbox>...";
-
-static int is_from_line(const char *line, int len)
-{
-       const char *colon;
-
-       if (len < 20 || memcmp("From ", line, 5))
-               return 0;
-
-       colon = line + len - 2;
-       line += 5;
-       for (;;) {
-               if (colon < line)
-                       return 0;
-               if (*--colon == ':')
-                       break;
-       }
-
-       if (!isdigit(colon[-4]) ||
-           !isdigit(colon[-2]) ||
-           !isdigit(colon[-1]) ||
-           !isdigit(colon[ 1]) ||
-           !isdigit(colon[ 2]))
-               return 0;
-
-       /* year */
-       if (strtol(colon+3, NULL, 10) <= 90)
-               return 0;
-
-       /* Ok, close enough */
-       return 1;
-}
-
-/* Could be as small as 64, enough to hold a Unix "From " line. */
-static char buf[4096];
-
-/* Called with the first line (potentially partial)
- * already in buf[] -- normally that should begin with
- * the Unix "From " line.  Write it into the specified
- * file.
- */
-static int split_one(FILE *mbox, const char *name, int allow_bare)
-{
-       FILE *output = NULL;
-       int len = strlen(buf);
-       int fd;
-       int status = 0;
-       int is_bare = !is_from_line(buf, len);
-
-       if (is_bare && !allow_bare)
-               goto corrupt;
-
-       fd = open(name, O_WRONLY | O_CREAT | O_EXCL, 0666);
-       if (fd < 0)
-               die("cannot open output file %s", name);
-       output = fdopen(fd, "w");
-
-       /* Copy it out, while searching for a line that begins with
-        * "From " and having something that looks like a date format.
-        */
-       for (;;) {
-               int is_partial = (buf[len-1] != '\n');
-
-               if (fputs(buf, output) == EOF)
-                       die("cannot write output");
-
-               if (fgets(buf, sizeof(buf), mbox) == NULL) {
-                       if (feof(mbox)) {
-                               status = 1;
-                               break;
-                       }
-                       die("cannot read mbox");
-               }
-               len = strlen(buf);
-               if (!is_partial && !is_bare && is_from_line(buf, len))
-                       break; /* done with one message */
-       }
-       fclose(output);
-       return status;
-
- corrupt:
-       if (output)
-               fclose(output);
-       unlink(name);
-       fprintf(stderr, "corrupt mailbox\n");
-       exit(1);
-}
-
-int main(int argc, const char **argv)
-{
-       int nr = 0, nr_prec = 4;
-       int allow_bare = 0;
-       const char *dir = NULL;
-       const char **argp;
-       static const char *stdin_only[] = { "-", NULL };
-       char *name;
-
-       for (argp = argv+1; *argp; argp++) {
-               const char *arg = *argp;
-
-               if (arg[0] != '-')
-                       break;
-               /* do flags here */
-               if ( arg[1] == 'd' ) {
-                       nr_prec = strtol(arg+2, NULL, 10);
-                       if (nr_prec < 3 || 10 <= nr_prec)
-                               usage(git_mailsplit_usage);
-                       continue;
-               } else if ( arg[1] == 'f' ) {
-                       nr = strtol(arg+2, NULL, 10);
-               } else if ( arg[1] == 'b' && !arg[2] ) {
-                       allow_bare = 1;
-               } else if ( arg[1] == 'o' && arg[2] ) {
-                       dir = arg+2;
-               } else if ( arg[1] == '-' && !arg[2] ) {
-                       argp++; /* -- marks end of options */
-                       break;
-               } else {
-                       die("unknown option: %s", arg);
-               }
-       }
-
-       if ( !dir ) {
-               /* Backwards compatibility: if no -o specified, accept
-                  <mbox> <dir> or just <dir> */
-               switch (argc - (argp-argv)) {
-               case 1:
-                       dir = argp[0];
-                       argp = stdin_only;
-                       break;
-               case 2:
-                       stdin_only[0] = argp[0];
-                       dir = argp[1];
-                       argp = stdin_only;
-                       break;
-               default:
-                       usage(git_mailsplit_usage);
-               }
-       } else {
-               /* New usage: if no more argument, parse stdin */
-               if ( !*argp )
-                       argp = stdin_only;
-       }
-
-       name = xmalloc(strlen(dir) + 2 + 3 * sizeof(nr));
-
-       while (*argp) {
-               const char *file = *argp++;
-               FILE *f = !strcmp(file, "-") ? stdin : fopen(file, "r");
-               int file_done = 0;
-
-               if ( !f )
-                       die ("cannot open mbox %s", file);
-
-               if (fgets(buf, sizeof(buf), f) == NULL) {
-                       if (f == stdin)
-                               break; /* empty stdin is OK */
-                       die("cannot read mbox %s", file);
-               }
-
-               while (!file_done) {
-                       sprintf(name, "%s/%0*d", dir, nr_prec, ++nr);
-                       file_done = split_one(f, name, allow_bare);
-               }
-
-               if (f != stdin)
-                       fclose(f);
-       }
-
-       printf("%d\n", nr);
-       return 0;
-}
index 024196e7ac53cb9ad09b38ea646bad81aff33306..190e12fb7ceeb012a063f8c97b574e9650d95cc1 100644 (file)
@@ -99,6 +99,11 @@ int main(int argc, char **argv)
 {
        int i, force_file = 0;
 
+       /* Without this we cannot rely on waitpid() to tell
+        * what happened to our children.
+        */
+       signal(SIGCHLD, SIG_DFL);
+
        if (argc < 3)
                usage("git-merge-index [-o] [-q] <merge-program> (-a | <filename>*)");
 
index c29b93ea7199643885cd74148d03db7d1bbb533b..3a5ac35d163bae5431c9cad8c9e31918c2734241 100644 (file)
@@ -125,11 +125,10 @@ static const char* get_rev_name(struct object *o)
 
        return buffer;
 }
-       
+
 int main(int argc, char **argv)
 {
-       struct object_list *revs = NULL;
-       struct object_list **walker = &revs;
+       struct object_array revs = { 0, 0, NULL };
        int as_is = 0, all = 0, transform_stdin = 0;
 
        setup_git_directory();
@@ -184,9 +183,7 @@ int main(int argc, char **argv)
                if (cutoff > commit->date)
                        cutoff = commit->date;
 
-               object_list_append((struct object *)commit, walker);
-               (*walker)->name = *argv;
-               walker = &((*walker)->next);
+               add_object_array((struct object *)commit, *argv, &revs);
        }
 
        for_each_ref(name_ref);
@@ -243,9 +240,13 @@ int main(int argc, char **argv)
                        if (objs[i])
                                printf("%s %s\n", sha1_to_hex(objs[i]->sha1),
                                                get_rev_name(objs[i]));
-       } else
-               for ( ; revs; revs = revs->next)
-                       printf("%s %s\n", revs->name, get_rev_name(revs->item));
+       } else {
+               int i;
+               for (i = 0; i < revs.nr; i++)
+                       printf("%s %s\n",
+                               revs.objects[i].name,
+                               get_rev_name(revs.objects[i].item));
+       }
 
        return 0;
 }
index 8afa2276fbe9e79bba700b99b1ef345bb6d7f5e8..a7d49c60d7176fbc90635e07dc2ce658125edafc 100644 (file)
@@ -127,6 +127,9 @@ void mark_reachable(struct object *obj, unsigned int mask)
 
        if (!track_object_refs)
                die("cannot do reachability with object refs turned off");
+       /* nothing to lookup */
+       if (!refs_hash_size)
+               return;
        /* If we've been here already, don't bother */
        if (obj->flags & mask)
                return;
index e26e319ccd22e5c9e9125e58f9ed32620afd66b1..37784cee9a90a7e5b44dd660d6b7ea9191b59486 100644 (file)
--- a/object.c
+++ b/object.c
@@ -200,3 +200,20 @@ int object_list_contains(struct object_list *list, struct object *obj)
        }
        return 0;
 }
+
+void add_object_array(struct object *obj, const char *name, struct object_array *array)
+{
+       unsigned nr = array->nr;
+       unsigned alloc = array->alloc;
+       struct object_array_entry *objects = array->objects;
+
+       if (nr >= alloc) {
+               alloc = (alloc + 32) * 2;
+               objects = xrealloc(objects, alloc * sizeof(*objects));
+               array->alloc = alloc;
+               array->objects = objects;
+       }
+       objects[nr].item = obj;
+       objects[nr].name = name;
+       array->nr = ++nr;
+}
index c537b4b72acabc8b2425ca9bb50c890e730aa788..6f23a9a18099cbc9fdc827f53843b1157dd10935 100644 (file)
--- a/object.h
+++ b/object.h
@@ -4,7 +4,6 @@
 struct object_list {
        struct object *item;
        struct object_list *next;
-       const char *name;
 };
 
 struct object_refs {
@@ -13,6 +12,15 @@ struct object_refs {
        struct object *ref[FLEX_ARRAY]; /* more */
 };
 
+struct object_array {
+       unsigned int nr;
+       unsigned int alloc;
+       struct object_array_entry {
+               struct object *item;
+               const char *name;
+       } *objects;
+};
+
 #define TYPE_BITS   3
 #define FLAG_BITS  27
 
@@ -72,4 +80,7 @@ unsigned object_list_length(struct object_list *list);
 
 int object_list_contains(struct object_list *list, struct object *obj);
 
+/* Object array handling .. */
+void add_object_array(struct object *obj, const char *name, struct object_array *array);
+
 #endif /* OBJECT_H */
index e57587909e54d9317a18e3bfd43559562a40f0d5..3a62e1b7e40927390e10ae63943596a367ecbf47 100644 (file)
@@ -29,10 +29,10 @@ static int verify_packfile(struct packed_git *p)
        pack_base = p->pack_base;
        SHA1_Update(&ctx, pack_base, pack_size - 20);
        SHA1_Final(sha1, &ctx);
-       if (memcmp(sha1, pack_base + pack_size - 20, 20))
+       if (memcmp(sha1, (char *) pack_base + pack_size - 20, 20))
                return error("Packfile %s SHA1 mismatch with itself",
                             p->pack_name);
-       if (memcmp(sha1, index_base + index_size - 40, 20))
+       if (memcmp(sha1, (char *) index_base + index_size - 40, 20))
                return error("Packfile %s SHA1 mismatch with idx",
                             p->pack_name);
 
@@ -135,7 +135,7 @@ int verify_pack(struct packed_git *p, int verbose)
        SHA1_Init(&ctx);
        SHA1_Update(&ctx, index_base, index_size - 20);
        SHA1_Final(sha1, &ctx);
-       if (memcmp(sha1, index_base + index_size - 20, 20))
+       if (memcmp(sha1, (char *) index_base + index_size - 20, 20))
                ret = error("Packfile index for %s SHA1 mismatch",
                            p->pack_name);
 
index 7a8c16c317dc42217ab01da8b130b815ad81846d..bed2497b7974bed96a6ac9478807059cb8de17cb 100644 (file)
@@ -156,7 +156,7 @@ static void prepare_pack_revindex(struct pack_revindex *rix)
 
        rix->revindex = xmalloc(sizeof(unsigned long) * (num_ent + 1));
        for (i = 0; i < num_ent; i++) {
-               unsigned int hl = *((unsigned int *)(index + 24 * i));
+               unsigned int hl = *((unsigned int *)((char *) index + 24*i));
                rix->revindex[i] = ntohl(hl);
        }
        /* This knows the pack format -- the 20-byte trailer
@@ -300,7 +300,7 @@ static unsigned long write_object(struct sha1file *f,
                use_packed_git(p);
 
                datalen = find_packed_object_size(p, entry->in_pack_offset);
-               buf = p->pack_base + entry->in_pack_offset;
+               buf = (char *) p->pack_base + entry->in_pack_offset;
                sha1write(f, buf, datalen);
                unuse_packed_git(p);
                hdrlen = 0; /* not really */
index cd81f5a66eae2a4c1fdab4086b896d6a06dcf687..41fb960569e1c7ed479fb123902752c552ff1341 100644 (file)
@@ -246,12 +246,12 @@ static struct pack_list * pack_list_difference(const struct pack_list *A,
 static void cmp_two_packs(struct pack_list *p1, struct pack_list *p2)
 {
        int p1_off, p2_off;
-       void *p1_base, *p2_base;
+       unsigned char *p1_base, *p2_base;
        struct llist_item *p1_hint = NULL, *p2_hint = NULL;
-       
+
        p1_off = p2_off = 256 * 4 + 4;
-       p1_base = (void *)p1->pack->index_base;
-       p2_base = (void *)p2->pack->index_base;
+       p1_base = (unsigned char *) p1->pack->index_base;
+       p2_base = (unsigned char *) p2->pack->index_base;
 
        while (p1_off <= p1->pack->index_size - 3 * 20 &&
               p2_off <= p2->pack->index_size - 3 * 20)
@@ -351,11 +351,11 @@ static size_t sizeof_union(struct packed_git *p1, struct packed_git *p2)
 {
        size_t ret = 0;
        int p1_off, p2_off;
-       void *p1_base, *p2_base;
+       char *p1_base, *p2_base;
 
        p1_off = p2_off = 256 * 4 + 4;
-       p1_base = (void *)p1->index_base;
-       p2_base = (void *)p2->index_base;
+       p1_base = (char *)p1->index_base;
+       p2_base = (char *)p2->index_base;
 
        while (p1_off <= p1->index_size - 3 * 20 &&
               p2_off <= p2->index_size - 3 * 20)
@@ -534,7 +534,7 @@ static struct pack_list * add_pack(struct packed_git *p)
 {
        struct pack_list l;
        size_t off;
-       void *base;
+       unsigned char *base;
 
        if (!p->pack_local && !(alt_odb || verbose))
                return NULL;
@@ -543,7 +543,7 @@ static struct pack_list * add_pack(struct packed_git *p)
        llist_init(&l.all_objects);
 
        off = 256 * 4 + 4;
-       base = (void *)p->index_base;
+       base = (unsigned char *)p->index_base;
        while (off <= p->index_size - 3 * 20) {
                llist_insert_back(l.all_objects, base + off);
                off += 24;
diff --git a/pager.c b/pager.c
index 9a30939016620072f319b5fef81d0c913513a641..2d186e8bde1067a62c9615e8d7f0368f98442ca4 100644 (file)
--- a/pager.c
+++ b/pager.c
@@ -46,7 +46,7 @@ void setup_pager(void)
        close(fd[0]);
        close(fd[1]);
 
-       setenv("LESS", "-S", 0);
+       setenv("LESS", "-RS", 0);
        run_pager(pager);
        die("unable to execute pager '%s'", pager);
        exit(255);
index 8f318ed8aaf180419f97887501317fc1d2b54c09..e3a1d425ee2e333bce0f87b1e50fc112a8ec51a3 100644 (file)
@@ -25,7 +25,7 @@ void *patch_delta(const void *src_buf, unsigned long src_size,
                return NULL;
 
        data = delta_buf;
-       top = delta_buf + delta_size;
+       top = (const unsigned char *) delta_buf + delta_size;
 
        /* make sure the orig file size matches what we expect */
        size = get_delta_hdr_size(&data, top);
@@ -56,7 +56,7 @@ void *patch_delta(const void *src_buf, unsigned long src_size,
                            cp_off + cp_size > src_size ||
                            cp_size > size)
                                goto bad;
-                       memcpy(out, src_buf + cp_off, cp_size);
+                       memcpy(out, (char *) src_buf + cp_off, cp_size);
                        out += cp_size;
                        size -= cp_size;
                } else if (cmd) {
index 3d724acf23fab5e8a8e3dd5b1dce957ea1d4a9d3..c1e81f976f26726db2432f72f7356087a6e3a7d8 100644 (file)
@@ -22,7 +22,7 @@ ssize_t safe_write(int fd, const void *buf, ssize_t n)
        while (n) {
                int ret = xwrite(fd, buf, n);
                if (ret > 0) {
-                       buf += ret;
+                       buf = (char *) buf + ret;
                        n -= ret;
                        continue;
                }
@@ -68,7 +68,7 @@ static void safe_read(int fd, void *buffer, unsigned size)
        int n = 0;
 
        while (n < size) {
-               int ret = xread(fd, buffer + n, size - n);
+               int ret = xread(fd, (char *) buffer + n, size - n);
                if (ret < 0)
                        die("read error (%s)", strerror(errno));
                if (!ret)
diff --git a/quote.c b/quote.c
index 06792d47c3e324ee9893a4e52035f0642f38005f..dcc23266109ce30e3fd098f3c76799b67b75a332 100644 (file)
--- a/quote.c
+++ b/quote.c
@@ -206,7 +206,14 @@ char *unquote_c_style(const char *quoted, const char **endp)
                                case '\\': case '"':
                                        break; /* verbatim */
 
-                               case '0'...'7':
+                               case '0':
+                               case '1':
+                               case '2':
+                               case '3':
+                               case '4':
+                               case '5':
+                               case '6':
+                               case '7':
                                        /* octal */
                                        ac = ((ch - '0') << 6);
                                        if ((ch = *sp++) < '0' || '7' < ch)
index c499c5185678b6b4ac35d57d1ab0b36cc2ff056b..3c32aae7e8ae63bccad83a603f2b86c66965cd26 100644 (file)
@@ -706,7 +706,7 @@ static int verify_hdr(struct cache_header *hdr, unsigned long size)
        SHA1_Init(&c);
        SHA1_Update(&c, hdr, size - 20);
        SHA1_Final(sha1, &c);
-       if (memcmp(sha1, (void *)hdr + size - 20, 20))
+       if (memcmp(sha1, (char *) hdr + size - 20, 20))
                return error("bad index file sha1 signature");
        return 0;
 }
@@ -770,7 +770,7 @@ int read_cache(void)
 
        offset = sizeof(*hdr);
        for (i = 0; i < active_nr; i++) {
-               struct cache_entry *ce = map + offset;
+               struct cache_entry *ce = (struct cache_entry *) ((char *) map + offset);
                offset = offset + ce_size(ce);
                active_cache[i] = ce;
        }
@@ -783,10 +783,11 @@ int read_cache(void)
                 * in 4-byte network byte order.
                 */
                unsigned long extsize;
-               memcpy(&extsize, map + offset + 4, 4);
+               memcpy(&extsize, (char *) map + offset + 4, 4);
                extsize = ntohl(extsize);
-               if (read_index_extension(map + offset,
-                                        map + offset + 8, extsize) < 0)
+               if (read_index_extension(((const char *) map) + offset,
+                                        (char *) map + offset + 8,
+                                        extsize) < 0)
                        goto unmap;
                offset += 8;
                offset += extsize;
@@ -820,7 +821,7 @@ static int ce_write(SHA_CTX *context, int fd, void *data, unsigned int len)
                }
                write_buffer_len = buffered;
                len -= partial;
-               data += partial;
+               data = (char *) data + partial;
        }
        return 0;
 }
index 08fc4cc57d064248247c912014e4eba32490f0ff..ab8f1afeea0cbff7707e313af1c18ff56363ae09 100644 (file)
@@ -64,7 +64,20 @@ static int show_config(const char* key_, const char* value_)
 
 static int get_value(const char* key_, const char* regex_)
 {
+       int ret = -1;
        char *tl;
+       char *global = NULL, *repo_config = NULL;
+       const char *local;
+
+       local = getenv("GIT_CONFIG");
+       if (!local) {
+               const char *home = getenv("HOME");
+               local = getenv("GIT_CONFIG_LOCAL");
+               if (!local)
+                       local = repo_config = strdup(git_path("config"));
+               if (home)
+                       global = strdup(mkpath("%s/.gitconfig", home));
+       }
 
        key = strdup(key_);
        for (tl=key+strlen(key)-1; tl >= key && *tl != '.'; --tl)
@@ -76,7 +89,7 @@ static int get_value(const char* key_, const char* regex_)
                key_regexp = (regex_t*)malloc(sizeof(regex_t));
                if (regcomp(key_regexp, key, REG_EXTENDED)) {
                        fprintf(stderr, "Invalid key pattern: %s\n", key_);
-                       return -1;
+                       goto free_strings;
                }
        }
 
@@ -89,11 +102,16 @@ static int get_value(const char* key_, const char* regex_)
                regexp = (regex_t*)malloc(sizeof(regex_t));
                if (regcomp(regexp, regex_, REG_EXTENDED)) {
                        fprintf(stderr, "Invalid pattern: %s\n", regex_);
-                       return -1;
+                       goto free_strings;
                }
        }
 
-       git_config(show_config);
+       if (do_all && global)
+               git_config_from_file(show_config, global);
+       git_config_from_file(show_config, local);
+       if (!do_all && !seen && global)
+               git_config_from_file(show_config, global);
+
        free(key);
        if (regexp) {
                regfree(regexp);
@@ -101,9 +119,16 @@ static int get_value(const char* key_, const char* regex_)
        }
 
        if (do_all)
-               return !seen;
-
-       return (seen == 1) ? 0 : 1;
+               ret = !seen;
+       else
+               ret =  (seen == 1) ? 0 : 1;
+
+free_strings:
+       if (repo_config)
+               free(repo_config);
+       if (global)
+               free(global);
+       return ret;
 }
 
 int main(int argc, const char **argv)
index 7bff2a10b1044651a776b42d4ee3d91661830aa2..b963f2adfd92050310f7327e1ee10e21d5996a7c 100644 (file)
@@ -31,17 +31,12 @@ static char *path_name(struct name_path *path, const char *name)
        return n;
 }
 
-struct object_list **add_object(struct object *obj,
-                                      struct object_list **p,
-                                      struct name_path *path,
-                                      const char *name)
+void add_object(struct object *obj,
+               struct object_array *p,
+               struct name_path *path,
+               const char *name)
 {
-       struct object_list *entry = xmalloc(sizeof(*entry));
-       entry->item = obj;
-       entry->next = *p;
-       entry->name = path_name(path, name);
-       *p = entry;
-       return &entry->next;
+       add_object_array(obj, path_name(path, name), p);
 }
 
 static void mark_blob_uninteresting(struct blob *blob)
@@ -117,9 +112,9 @@ void mark_parents_uninteresting(struct commit *commit)
        }
 }
 
-static void add_pending_object(struct rev_info *revs, struct object *obj, const char *name)
+void add_pending_object(struct rev_info *revs, struct object *obj, const char *name)
 {
-       add_object(obj, &revs->pending_objects, NULL, name);
+       add_object_array(obj, name, &revs->pending);
 }
 
 static struct object *get_reference(struct rev_info *revs, const char *name, const unsigned char *sha1, unsigned int flags)
@@ -836,7 +831,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
                object = get_reference(revs, arg, sha1, flags ^ local_flags);
                add_pending_object(revs, object, arg);
        }
-       if (def && !revs->pending_objects) {
+       if (def && !revs->pending.nr) {
                unsigned char sha1[20];
                struct object *object;
                if (get_sha1(def, sha1))
@@ -868,11 +863,13 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
 
 void prepare_revision_walk(struct rev_info *revs)
 {
-       struct object_list *list;
+       int nr = revs->pending.nr;
+       struct object_array_entry *list = revs->pending.objects;
 
-       list = revs->pending_objects;
-       revs->pending_objects = NULL;
-       while (list) {
+       revs->pending.nr = 0;
+       revs->pending.alloc = 0;
+       revs->pending.objects = NULL;
+       while (--nr >= 0) {
                struct commit *commit = handle_commit(revs, list->item, list->name);
                if (commit) {
                        if (!(commit->object.flags & SEEN)) {
@@ -880,7 +877,7 @@ void prepare_revision_walk(struct rev_info *revs)
                                insert_by_date(commit, &revs->commits);
                        }
                }
-               list = list->next;
+               list++;
        }
 
        if (revs->no_walk)
index 4020e25c336356868057ee88e84b395f55b87b38..c010a0811604e55f4e46d08d986ae62edfb192ee 100644 (file)
@@ -18,7 +18,7 @@ typedef void (prune_fn_t)(struct rev_info *revs, struct commit *commit);
 struct rev_info {
        /* Starting list */
        struct commit_list *commits;
-       struct object_list *pending_objects;
+       struct object_array pending;
 
        /* Basic information */
        const char *prefix;
@@ -99,9 +99,11 @@ struct name_path {
        const char *elem;
 };
 
-extern struct object_list **add_object(struct object *obj,
-                                      struct object_list **p,
-                                      struct name_path *path,
-                                      const char *name);
+extern void add_object(struct object *obj,
+                      struct object_array *p,
+                      struct name_path *path,
+                      const char *name);
+
+extern void add_pending_object(struct rev_info *revs, struct object *obj, const char *name);
 
 #endif
index b4ff233bad3c15692610f93549b25cd0cf042392..c80528b506e98c1e1dae463fa08159677a2a9473 100644 (file)
@@ -486,8 +486,9 @@ int use_packed_git(struct packed_git *p)
                 * this is cheap.
                 */
                if (memcmp((char*)(p->index_base) + p->index_size - 40,
-                          p->pack_base + p->pack_size - 20, 20)) {
-                             
+                          (char *) p->pack_base + p->pack_size - 20,
+                          20)) {
+
                        die("packfile %s does not match index.", p->pack_name);
                }
        }
@@ -701,7 +702,7 @@ static void *unpack_sha1_rest(z_stream *stream, void *buffer, unsigned long size
        int bytes = strlen(buffer) + 1;
        unsigned char *buf = xmalloc(1+size);
 
-       memcpy(buf, buffer + bytes, stream->total_out - bytes);
+       memcpy(buf, (char *) buffer + bytes, stream->total_out - bytes);
        bytes = stream->total_out - bytes;
        if (bytes < size) {
                stream->next_out = buf + bytes;
@@ -853,7 +854,7 @@ static unsigned long unpack_object_header(struct packed_git *p, unsigned long of
        if (offset >= p->pack_size)
                die("object offset outside of pack file");
 
-       pack =  p->pack_base + offset;
+       pack =  (unsigned char *) p->pack_base + offset;
        c = *pack++;
        offset++;
        *type = (c >> 4) & 7;
@@ -883,7 +884,7 @@ int check_reuse_pack_delta(struct packed_git *p, unsigned long offset,
        ptr = unpack_object_header(p, ptr, kindp, sizep);
        if (*kindp != OBJ_DELTA)
                goto done;
-       memcpy(base, p->pack_base + ptr, 20);
+       memcpy(base, (char *) p->pack_base + ptr, 20);
        status = 0;
  done:
        unuse_packed_git(p);
@@ -903,7 +904,7 @@ void packed_object_info_detail(struct pack_entry *e,
        enum object_type kind;
 
        offset = unpack_object_header(p, e->offset, &kind, size);
-       pack = p->pack_base + offset;
+       pack = (unsigned char *) p->pack_base + offset;
        if (kind != OBJ_DELTA)
                *delta_chain_length = 0;
        else {
@@ -919,7 +920,7 @@ void packed_object_info_detail(struct pack_entry *e,
                        find_pack_entry_one(pack, &base_ent, p);
                        offset = unpack_object_header(p, base_ent.offset,
                                                      &kind, &junk);
-                       pack = p->pack_base + offset;
+                       pack = (unsigned char *) p->pack_base + offset;
                        chain_length++;
                } while (kind == OBJ_DELTA);
                *delta_chain_length = chain_length;
@@ -957,7 +958,7 @@ static int packed_object_info(struct pack_entry *entry,
                die("cannot map packed file");
 
        offset = unpack_object_header(p, entry->offset, &kind, &size);
-       pack = p->pack_base + offset;
+       pack = (unsigned char *) p->pack_base + offset;
        left = p->pack_size - offset;
 
        switch (kind) {
@@ -1096,7 +1097,7 @@ void *unpack_entry_gently(struct pack_entry *entry,
        void *retval;
 
        offset = unpack_object_header(p, entry->offset, &kind, &size);
-       pack = p->pack_base + offset;
+       pack = (unsigned char *) p->pack_base + offset;
        left = p->pack_size - offset;
        switch (kind) {
        case OBJ_DELTA:
@@ -1134,7 +1135,7 @@ int nth_packed_object_sha1(const struct packed_git *p, int n,
        void *index = p->index_base + 256;
        if (n < 0 || num_packed_objects(p) <= n)
                return -1;
-       memcpy(sha1, (index + 24 * n + 4), 20);
+       memcpy(sha1, (char *) index + (24 * n) + 4, 20);
        return 0;
 }
 
@@ -1148,9 +1149,9 @@ int find_pack_entry_one(const unsigned char *sha1,
 
        do {
                int mi = (lo + hi) / 2;
-               int cmp = memcmp(index + 24 * mi + 4, sha1, 20);
+               int cmp = memcmp((char *) index + (24 * mi) + 4, sha1, 20);
                if (!cmp) {
-                       e->offset = ntohl(*((unsigned int *)(index + 24 * mi)));
+                       e->offset = ntohl(*((unsigned int *) ((char *) index + (24 * mi))));
                        memcpy(e->sha1, sha1, 20);
                        e->p = p;
                        return 1;
@@ -1290,7 +1291,7 @@ void *read_object_with_reference(const unsigned char *sha1,
                ref_length = strlen(ref_type);
 
                if (memcmp(buffer, ref_type, ref_length) ||
-                   get_sha1_hex(buffer + ref_length, actual_sha1)) {
+                   get_sha1_hex((char *) buffer + ref_length, actual_sha1)) {
                        free(buffer);
                        return NULL;
                }
@@ -1408,7 +1409,7 @@ static int write_buffer(int fd, const void *buf, size_t len)
                        return error("file write error (%s)", strerror(errno));
                }
                len -= size;
-               buf += size;
+               buf = (char *) buf + size;
        }
        return 0;
 }
index e3067b878e8721886c9fe61b136b46415165671d..1e59cd2008d8089efef74b940d1a1094add7e687 100644 (file)
@@ -24,7 +24,7 @@ static ssize_t force_write(int fd, void *buffer, size_t length)
 {
        ssize_t ret = 0;
        while (ret < length) {
-               ssize_t size = write(fd, buffer + ret, length - ret);
+               ssize_t size = write(fd, (char *) buffer + ret, length - ret);
                if (size < 0) {
                        return size;
                }
diff --git a/stripspace.c b/stripspace.c
deleted file mode 100644 (file)
index 65a6346..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-#include <stdio.h>
-#include <string.h>
-#include <ctype.h>
-
-/*
- * Remove empty lines from the beginning and end.
- *
- * Turn multiple consecutive empty lines into just one
- * empty line.  Return true if it is an incomplete line.
- */
-static int cleanup(char *line)
-{
-       int len = strlen(line);
-
-       if (len && line[len-1] == '\n') {
-               if (len == 1)
-                       return 0;
-               do {
-                       unsigned char c = line[len-2];
-                       if (!isspace(c))
-                               break;
-                       line[len-2] = '\n';
-                       len--;
-                       line[len] = 0;
-               } while (len > 1);
-               return 0;
-       }
-       return 1;
-}
-
-int main(int argc, char **argv)
-{
-       int empties = -1;
-       int incomplete = 0;
-       char line[1024];
-
-       while (fgets(line, sizeof(line), stdin)) {
-               incomplete = cleanup(line);
-
-               /* Not just an empty line? */
-               if (line[0] != '\n') {
-                       if (empties > 0)
-                               putchar('\n');
-                       empties = 0;
-                       fputs(line, stdout);
-                       continue;
-               }
-               if (empties < 0)
-                       continue;
-               empties++;
-       }
-       if (incomplete)
-               putchar('\n');
-       return 0;
-}
index 549598575b9fdcefe857ddef1b6d980a9773b033..632c55f6d5d66c8e39e213284e163a23b32c2260 100644 (file)
@@ -19,7 +19,7 @@ endif
 all: $(T) clean
 
 $(T):
-       @echo "*** $@ ***"; '$(SHELL_PATH_SQ)' $@ $(GIT_TEST_OPTS)
+       @echo "*** $@ ***"; GIT_CONFIG=.git/config '$(SHELL_PATH_SQ)' $@ $(GIT_TEST_OPTS)
 
 clean:
        rm -fr trash
index 8260d57b63d7f988a4c236d99ce2c9931a67c5e3..0de2497746e31f3dce418dbd248ff5d3e7cb939a 100755 (executable)
@@ -309,5 +309,29 @@ EOF
 
 test_expect_success 'new variable inserts into proper section' 'cmp .git/config expect'
 
+cat > other-config << EOF
+[ein]
+       bahn = strasse
+EOF
+
+cat > expect << EOF
+ein.bahn=strasse
+EOF
+
+GIT_CONFIG=other-config git-repo-config -l > output
+
+test_expect_success 'alternative GIT_CONFIG' 'cmp output expect'
+
+GIT_CONFIG=other-config git-repo-config anwohner.park ausweis
+
+cat > expect << EOF
+[ein]
+       bahn = strasse
+[anwohner]
+       park = ausweis
+EOF
+
+test_expect_success '--set in alternative GIT_CONFIG' 'cmp other-config expect'
+
 test_done
 
diff --git a/t/t3402-rebase-merge.sh b/t/t3402-rebase-merge.sh
new file mode 100755 (executable)
index 0000000..d34c6cf
--- /dev/null
@@ -0,0 +1,112 @@
+#!/bin/sh
+#
+# Copyright (c) 2006 Junio C Hamano
+#
+
+test_description='git rebase --merge test'
+
+. ./test-lib.sh
+
+if test "$no_python"; then
+       echo "Skipping: no python => no recursive merge"
+       test_done
+       exit 0
+fi
+
+T="A quick brown fox
+jumps over the lazy dog."
+for i in 1 2 3 4 5 6 7 8 9 10
+do
+       echo "$i $T"
+done >original
+
+test_expect_success setup '
+       git add original &&
+       git commit -m"initial" &&
+       git branch side &&
+       echo "11 $T" >>original &&
+       git commit -a -m"master updates a bit." &&
+
+       echo "12 $T" >>original &&
+       git commit -a -m"master updates a bit more." &&
+
+       git checkout side &&
+       (echo "0 $T" ; cat original) >renamed &&
+       git add renamed &&
+       git update-index --force-remove original &&
+       git commit -a -m"side renames and edits." &&
+
+       tr "[a-z]" "[A-Z]" <original >newfile &&
+       git add newfile &&
+       git commit -a -m"side edits further." &&
+
+       tr "[a-m]" "[A-M]" <original >newfile &&
+       rm -f original &&
+       git commit -a -m"side edits once again." &&
+
+       git branch test-rebase side &&
+       git branch test-rebase-pick side &&
+       git branch test-reference-pick side &&
+       git checkout -b test-merge side
+'
+
+test_expect_success 'reference merge' '
+       git merge -s recursive "reference merge" HEAD master
+'
+
+test_expect_success rebase '
+       git checkout test-rebase &&
+       git rebase --merge master
+'
+
+test_expect_success 'merge and rebase should match' '
+       git diff-tree -r test-rebase test-merge >difference &&
+       if test -s difference
+       then
+               cat difference
+               (exit 1)
+       else
+               echo happy
+       fi
+'
+
+test_expect_success 'rebase the other way' '
+       git reset --hard master &&
+       git rebase --merge side
+'
+
+test_expect_success 'merge and rebase should match' '
+       git diff-tree -r test-rebase test-merge >difference &&
+       if test -s difference
+       then
+               cat difference
+               (exit 1)
+       else
+               echo happy
+       fi
+'
+
+test_expect_success 'picking rebase' '
+       git reset --hard side &&
+       git rebase --merge --onto master side^^ &&
+       mb=$(git merge-base master HEAD) &&
+       if test "$mb" = "$(git rev-parse master)"
+       then
+               echo happy
+       else
+               git show-branch
+               (exit 1)
+       fi &&
+       f=$(git diff-tree --name-only HEAD^ HEAD) &&
+       g=$(git diff-tree --name-only HEAD^^ HEAD^) &&
+       case "$f,$g" in
+       newfile,newfile)
+               echo happy ;;
+       *)
+               echo "$f"
+               echo "$g"
+               (exit 1)
+       esac
+'
+
+test_done
diff --git a/tag.c b/tag.c
index 5f70a5b8104078b139d2ec87b7aebc6791b4d253..74d0dabe5d8c1f06a3f67475368c34e3b4046456 100644 (file)
--- a/tag.c
+++ b/tag.c
@@ -47,10 +47,10 @@ int parse_tag_buffer(struct tag *item, void *data, unsigned long size)
 
        if (size < 64)
                return -1;
-       if (memcmp("object ", data, 7) || get_sha1_hex(data + 7, object))
+       if (memcmp("object ", data, 7) || get_sha1_hex((char *) data + 7, object))
                return -1;
 
-       type_line = data + 48;
+       type_line = (char *) data + 48;
        if (memcmp("\ntype ", type_line-1, 6))
                return -1;
 
index 297c6972b9256578f9cd7a92404dda55c8e34a2e..3f83e98f3a443e4537f53abf32bcca1389c5d9d8 100644 (file)
@@ -43,7 +43,7 @@ void update_tree_entry(struct tree_desc *desc)
 
        if (size < len)
                die("corrupt tree file");
-       desc->buf = buf + len;
+       desc->buf = (char *) buf + len;
        desc->size = size - len;
 }
 
@@ -66,7 +66,7 @@ const unsigned char *tree_entry_extract(struct tree_desc *desc, const char **pat
        const void *tree = desc->buf;
        unsigned long size = desc->size;
        int len = strlen(tree)+1;
-       const unsigned char *sha1 = tree + len;
+       const unsigned char *sha1 = (unsigned char *) tree + len;
        const char *path;
        unsigned int mode;
 
@@ -80,7 +80,8 @@ const unsigned char *tree_entry_extract(struct tree_desc *desc, const char **pat
 
 int tree_entry(struct tree_desc *desc, struct name_entry *entry)
 {
-       const void *tree = desc->buf, *path;
+       const void *tree = desc->buf;
+       const char *path;
        unsigned long len, size = desc->size;
 
        if (!size)
@@ -95,10 +96,10 @@ int tree_entry(struct tree_desc *desc, struct name_entry *entry)
        entry->pathlen = len;
 
        path += len + 1;
-       entry->sha1 = path;
+       entry->sha1 = (const unsigned char *) path;
 
        path += 20;
-       len = path - tree;
+       len = path - (char *) tree;
        if (len > size)
                die("corrupt tree file");
 
diff --git a/update-index.c b/update-index.c
deleted file mode 100644 (file)
index fbccc4a..0000000
+++ /dev/null
@@ -1,653 +0,0 @@
-/*
- * GIT - The information manager from hell
- *
- * Copyright (C) Linus Torvalds, 2005
- */
-#include "cache.h"
-#include "strbuf.h"
-#include "quote.h"
-#include "cache-tree.h"
-#include "tree-walk.h"
-
-/*
- * Default to not allowing changes to the list of files. The
- * tool doesn't actually care, but this makes it harder to add
- * files to the revision control by mistake by doing something
- * like "git-update-index *" and suddenly having all the object
- * files be revision controlled.
- */
-static int allow_add;
-static int allow_remove;
-static int allow_replace;
-static int info_only;
-static int force_remove;
-static int verbose;
-static int mark_valid_only = 0;
-#define MARK_VALID 1
-#define UNMARK_VALID 2
-
-static void report(const char *fmt, ...)
-{
-       va_list vp;
-
-       if (!verbose)
-               return;
-
-       va_start(vp, fmt);
-       vprintf(fmt, vp);
-       putchar('\n');
-       va_end(vp);
-}
-
-static int mark_valid(const char *path)
-{
-       int namelen = strlen(path);
-       int pos = cache_name_pos(path, namelen);
-       if (0 <= pos) {
-               switch (mark_valid_only) {
-               case MARK_VALID:
-                       active_cache[pos]->ce_flags |= htons(CE_VALID);
-                       break;
-               case UNMARK_VALID:
-                       active_cache[pos]->ce_flags &= ~htons(CE_VALID);
-                       break;
-               }
-               cache_tree_invalidate_path(active_cache_tree, path);
-               active_cache_changed = 1;
-               return 0;
-       }
-       return -1;
-}
-
-static int add_file_to_cache(const char *path)
-{
-       int size, namelen, option, status;
-       struct cache_entry *ce;
-       struct stat st;
-
-       status = lstat(path, &st);
-
-       /* We probably want to do this in remove_file_from_cache() and
-        * add_cache_entry() instead...
-        */
-       cache_tree_invalidate_path(active_cache_tree, path);
-
-       if (status < 0 || S_ISDIR(st.st_mode)) {
-               /* When we used to have "path" and now we want to add
-                * "path/file", we need a way to remove "path" before
-                * being able to add "path/file".  However,
-                * "git-update-index --remove path" would not work.
-                * --force-remove can be used but this is more user
-                * friendly, especially since we can do the opposite
-                * case just fine without --force-remove.
-                */
-               if (status == 0 || (errno == ENOENT || errno == ENOTDIR)) {
-                       if (allow_remove) {
-                               if (remove_file_from_cache(path))
-                                       return error("%s: cannot remove from the index",
-                                                    path);
-                               else
-                                       return 0;
-                       } else if (status < 0) {
-                               return error("%s: does not exist and --remove not passed",
-                                            path);
-                       }
-               }
-               if (0 == status)
-                       return error("%s: is a directory - add files inside instead",
-                                    path);
-               else
-                       return error("lstat(\"%s\"): %s", path,
-                                    strerror(errno));
-       }
-
-       namelen = strlen(path);
-       size = cache_entry_size(namelen);
-       ce = xcalloc(1, size);
-       memcpy(ce->name, path, namelen);
-       ce->ce_flags = htons(namelen);
-       fill_stat_cache_info(ce, &st);
-
-       ce->ce_mode = create_ce_mode(st.st_mode);
-       if (!trust_executable_bit) {
-               /* If there is an existing entry, pick the mode bits
-                * from it.
-                */
-               int pos = cache_name_pos(path, namelen);
-               if (0 <= pos)
-                       ce->ce_mode = active_cache[pos]->ce_mode;
-       }
-
-       if (index_path(ce->sha1, path, &st, !info_only))
-               return -1;
-       option = allow_add ? ADD_CACHE_OK_TO_ADD : 0;
-       option |= allow_replace ? ADD_CACHE_OK_TO_REPLACE : 0;
-       if (add_cache_entry(ce, option))
-               return error("%s: cannot add to the index - missing --add option?",
-                            path);
-       return 0;
-}
-
-static int add_cacheinfo(unsigned int mode, const unsigned char *sha1,
-                        const char *path, int stage)
-{
-       int size, len, option;
-       struct cache_entry *ce;
-
-       if (!verify_path(path))
-               return -1;
-
-       len = strlen(path);
-       size = cache_entry_size(len);
-       ce = xcalloc(1, size);
-
-       memcpy(ce->sha1, sha1, 20);
-       memcpy(ce->name, path, len);
-       ce->ce_flags = create_ce_flags(len, stage);
-       ce->ce_mode = create_ce_mode(mode);
-       if (assume_unchanged)
-               ce->ce_flags |= htons(CE_VALID);
-       option = allow_add ? ADD_CACHE_OK_TO_ADD : 0;
-       option |= allow_replace ? ADD_CACHE_OK_TO_REPLACE : 0;
-       if (add_cache_entry(ce, option))
-               return error("%s: cannot add to the index - missing --add option?",
-                            path);
-       report("add '%s'", path);
-       cache_tree_invalidate_path(active_cache_tree, path);
-       return 0;
-}
-
-static void chmod_path(int flip, const char *path)
-{
-       int pos;
-       struct cache_entry *ce;
-       unsigned int mode;
-
-       pos = cache_name_pos(path, strlen(path));
-       if (pos < 0)
-               goto fail;
-       ce = active_cache[pos];
-       mode = ntohl(ce->ce_mode);
-       if (!S_ISREG(mode))
-               goto fail;
-       switch (flip) {
-       case '+':
-               ce->ce_mode |= htonl(0111); break;
-       case '-':
-               ce->ce_mode &= htonl(~0111); break;
-       default:
-               goto fail;
-       }
-       cache_tree_invalidate_path(active_cache_tree, path);
-       active_cache_changed = 1;
-       report("chmod %cx '%s'", flip, path);
-       return;
- fail:
-       die("git-update-index: cannot chmod %cx '%s'", flip, path);
-}
-
-static struct lock_file lock_file;
-
-static void update_one(const char *path, const char *prefix, int prefix_length)
-{
-       const char *p = prefix_path(prefix, prefix_length, path);
-       if (!verify_path(p)) {
-               fprintf(stderr, "Ignoring path %s\n", path);
-               goto free_return;
-       }
-       if (mark_valid_only) {
-               if (mark_valid(p))
-                       die("Unable to mark file %s", path);
-               goto free_return;
-       }
-       cache_tree_invalidate_path(active_cache_tree, path);
-
-       if (force_remove) {
-               if (remove_file_from_cache(p))
-                       die("git-update-index: unable to remove %s", path);
-               report("remove '%s'", path);
-               goto free_return;
-       }
-       if (add_file_to_cache(p))
-               die("Unable to process file %s", path);
-       report("add '%s'", path);
- free_return:
-       if (p < path || p > path + strlen(path))
-               free((char*)p);
-}
-
-static void read_index_info(int line_termination)
-{
-       struct strbuf buf;
-       strbuf_init(&buf);
-       while (1) {
-               char *ptr, *tab;
-               char *path_name;
-               unsigned char sha1[20];
-               unsigned int mode;
-               int stage;
-
-               /* This reads lines formatted in one of three formats:
-                *
-                * (1) mode         SP sha1          TAB path
-                * The first format is what "git-apply --index-info"
-                * reports, and used to reconstruct a partial tree
-                * that is used for phony merge base tree when falling
-                * back on 3-way merge.
-                *
-                * (2) mode SP type SP sha1          TAB path
-                * The second format is to stuff git-ls-tree output
-                * into the index file.
-                * 
-                * (3) mode         SP sha1 SP stage TAB path
-                * This format is to put higher order stages into the
-                * index file and matches git-ls-files --stage output.
-                */
-               read_line(&buf, stdin, line_termination);
-               if (buf.eof)
-                       break;
-
-               mode = strtoul(buf.buf, &ptr, 8);
-               if (ptr == buf.buf || *ptr != ' ')
-                       goto bad_line;
-
-               tab = strchr(ptr, '\t');
-               if (!tab || tab - ptr < 41)
-                       goto bad_line;
-
-               if (tab[-2] == ' ' && '0' <= tab[-1] && tab[-1] <= '3') {
-                       stage = tab[-1] - '0';
-                       ptr = tab + 1; /* point at the head of path */
-                       tab = tab - 2; /* point at tail of sha1 */
-               }
-               else {
-                       stage = 0;
-                       ptr = tab + 1; /* point at the head of path */
-               }
-
-               if (get_sha1_hex(tab - 40, sha1) || tab[-41] != ' ')
-                       goto bad_line;
-
-               if (line_termination && ptr[0] == '"')
-                       path_name = unquote_c_style(ptr, NULL);
-               else
-                       path_name = ptr;
-
-               if (!verify_path(path_name)) {
-                       fprintf(stderr, "Ignoring path %s\n", path_name);
-                       if (path_name != ptr)
-                               free(path_name);
-                       continue;
-               }
-               cache_tree_invalidate_path(active_cache_tree, path_name);
-
-               if (!mode) {
-                       /* mode == 0 means there is no such path -- remove */
-                       if (remove_file_from_cache(path_name))
-                               die("git-update-index: unable to remove %s",
-                                   ptr);
-               }
-               else {
-                       /* mode ' ' sha1 '\t' name
-                        * ptr[-1] points at tab,
-                        * ptr[-41] is at the beginning of sha1
-                        */
-                       ptr[-42] = ptr[-1] = 0;
-                       if (add_cacheinfo(mode, sha1, path_name, stage))
-                               die("git-update-index: unable to update %s",
-                                   path_name);
-               }
-               if (path_name != ptr)
-                       free(path_name);
-               continue;
-
-       bad_line:
-               die("malformed index info %s", buf.buf);
-       }
-}
-
-static const char update_index_usage[] =
-"git-update-index [-q] [--add] [--replace] [--remove] [--unmerged] [--refresh] [--really-refresh] [--cacheinfo] [--chmod=(+|-)x] [--assume-unchanged] [--info-only] [--force-remove] [--stdin] [--index-info] [--unresolve] [--again] [--ignore-missing] [-z] [--verbose] [--] <file>...";
-
-static unsigned char head_sha1[20];
-static unsigned char merge_head_sha1[20];
-
-static struct cache_entry *read_one_ent(const char *which,
-                                       unsigned char *ent, const char *path,
-                                       int namelen, int stage)
-{
-       unsigned mode;
-       unsigned char sha1[20];
-       int size;
-       struct cache_entry *ce;
-
-       if (get_tree_entry(ent, path, sha1, &mode)) {
-               if (which)
-                       error("%s: not in %s branch.", path, which);
-               return NULL;
-       }
-       if (mode == S_IFDIR) {
-               if (which)
-                       error("%s: not a blob in %s branch.", path, which);
-               return NULL;
-       }
-       size = cache_entry_size(namelen);
-       ce = xcalloc(1, size);
-
-       memcpy(ce->sha1, sha1, 20);
-       memcpy(ce->name, path, namelen);
-       ce->ce_flags = create_ce_flags(namelen, stage);
-       ce->ce_mode = create_ce_mode(mode);
-       return ce;
-}
-
-static int unresolve_one(const char *path)
-{
-       int namelen = strlen(path);
-       int pos;
-       int ret = 0;
-       struct cache_entry *ce_2 = NULL, *ce_3 = NULL;
-
-       /* See if there is such entry in the index. */
-       pos = cache_name_pos(path, namelen);
-       if (pos < 0) {
-               /* If there isn't, either it is unmerged, or
-                * resolved as "removed" by mistake.  We do not
-                * want to do anything in the former case.
-                */
-               pos = -pos-1;
-               if (pos < active_nr) {
-                       struct cache_entry *ce = active_cache[pos];
-                       if (ce_namelen(ce) == namelen &&
-                           !memcmp(ce->name, path, namelen)) {
-                               fprintf(stderr,
-                                       "%s: skipping still unmerged path.\n",
-                                       path);
-                               goto free_return;
-                       }
-               }
-       }
-
-       /* Grab blobs from given path from HEAD and MERGE_HEAD,
-        * stuff HEAD version in stage #2,
-        * stuff MERGE_HEAD version in stage #3.
-        */
-       ce_2 = read_one_ent("our", head_sha1, path, namelen, 2);
-       ce_3 = read_one_ent("their", merge_head_sha1, path, namelen, 3);
-
-       if (!ce_2 || !ce_3) {
-               ret = -1;
-               goto free_return;
-       }
-       if (!memcmp(ce_2->sha1, ce_3->sha1, 20) &&
-           ce_2->ce_mode == ce_3->ce_mode) {
-               fprintf(stderr, "%s: identical in both, skipping.\n",
-                       path);
-               goto free_return;
-       }
-
-       cache_tree_invalidate_path(active_cache_tree, path);
-       remove_file_from_cache(path);
-       if (add_cache_entry(ce_2, ADD_CACHE_OK_TO_ADD)) {
-               error("%s: cannot add our version to the index.", path);
-               ret = -1;
-               goto free_return;
-       }
-       if (!add_cache_entry(ce_3, ADD_CACHE_OK_TO_ADD))
-               return 0;
-       error("%s: cannot add their version to the index.", path);
-       ret = -1;
- free_return:
-       free(ce_2);
-       free(ce_3);
-       return ret;
-}
-
-static void read_head_pointers(void)
-{
-       if (read_ref(git_path("HEAD"), head_sha1))
-               die("No HEAD -- no initial commit yet?\n");
-       if (read_ref(git_path("MERGE_HEAD"), merge_head_sha1)) {
-               fprintf(stderr, "Not in the middle of a merge.\n");
-               exit(0);
-       }
-}
-
-static int do_unresolve(int ac, const char **av,
-                       const char *prefix, int prefix_length)
-{
-       int i;
-       int err = 0;
-
-       /* Read HEAD and MERGE_HEAD; if MERGE_HEAD does not exist, we
-        * are not doing a merge, so exit with success status.
-        */
-       read_head_pointers();
-
-       for (i = 1; i < ac; i++) {
-               const char *arg = av[i];
-               const char *p = prefix_path(prefix, prefix_length, arg);
-               err |= unresolve_one(p);
-               if (p < arg || p > arg + strlen(arg))
-                       free((char*)p);
-       }
-       return err;
-}
-
-static int do_reupdate(int ac, const char **av,
-                      const char *prefix, int prefix_length)
-{
-       /* Read HEAD and run update-index on paths that are
-        * merged and already different between index and HEAD.
-        */
-       int pos;
-       int has_head = 1;
-       const char **pathspec = get_pathspec(prefix, av + 1);
-
-       if (read_ref(git_path("HEAD"), head_sha1))
-               /* If there is no HEAD, that means it is an initial
-                * commit.  Update everything in the index.
-                */
-               has_head = 0;
- redo:
-       for (pos = 0; pos < active_nr; pos++) {
-               struct cache_entry *ce = active_cache[pos];
-               struct cache_entry *old = NULL;
-               int save_nr;
-
-               if (ce_stage(ce) || !ce_path_match(ce, pathspec))
-                       continue;
-               if (has_head)
-                       old = read_one_ent(NULL, head_sha1,
-                                          ce->name, ce_namelen(ce), 0);
-               if (old && ce->ce_mode == old->ce_mode &&
-                   !memcmp(ce->sha1, old->sha1, 20)) {
-                       free(old);
-                       continue; /* unchanged */
-               }
-               /* Be careful.  The working tree may not have the
-                * path anymore, in which case, under 'allow_remove',
-                * or worse yet 'allow_replace', active_nr may decrease.
-                */
-               save_nr = active_nr;
-               update_one(ce->name + prefix_length, prefix, prefix_length);
-               if (save_nr != active_nr)
-                       goto redo;
-       }
-       return 0;
-}
-
-int main(int argc, const char **argv)
-{
-       int i, newfd, entries, has_errors = 0, line_termination = '\n';
-       int allow_options = 1;
-       int read_from_stdin = 0;
-       const char *prefix = setup_git_directory();
-       int prefix_length = prefix ? strlen(prefix) : 0;
-       char set_executable_bit = 0;
-       unsigned int refresh_flags = 0;
-
-       git_config(git_default_config);
-
-       newfd = hold_lock_file_for_update(&lock_file, get_index_file());
-       if (newfd < 0)
-               die("unable to create new index file");
-
-       entries = read_cache();
-       if (entries < 0)
-               die("cache corrupted");
-
-       for (i = 1 ; i < argc; i++) {
-               const char *path = argv[i];
-
-               if (allow_options && *path == '-') {
-                       if (!strcmp(path, "--")) {
-                               allow_options = 0;
-                               continue;
-                       }
-                       if (!strcmp(path, "-q")) {
-                               refresh_flags |= REFRESH_QUIET;
-                               continue;
-                       }
-                       if (!strcmp(path, "--add")) {
-                               allow_add = 1;
-                               continue;
-                       }
-                       if (!strcmp(path, "--replace")) {
-                               allow_replace = 1;
-                               continue;
-                       }
-                       if (!strcmp(path, "--remove")) {
-                               allow_remove = 1;
-                               continue;
-                       }
-                       if (!strcmp(path, "--unmerged")) {
-                               refresh_flags |= REFRESH_UNMERGED;
-                               continue;
-                       }
-                       if (!strcmp(path, "--refresh")) {
-                               has_errors |= refresh_cache(refresh_flags);
-                               continue;
-                       }
-                       if (!strcmp(path, "--really-refresh")) {
-                               has_errors |= refresh_cache(REFRESH_REALLY | refresh_flags);
-                               continue;
-                       }
-                       if (!strcmp(path, "--cacheinfo")) {
-                               unsigned char sha1[20];
-                               unsigned int mode;
-
-                               if (i+3 >= argc)
-                                       die("git-update-index: --cacheinfo <mode> <sha1> <path>");
-
-                               if ((sscanf(argv[i+1], "%o", &mode) != 1) ||
-                                   get_sha1_hex(argv[i+2], sha1) ||
-                                   add_cacheinfo(mode, sha1, argv[i+3], 0))
-                                       die("git-update-index: --cacheinfo"
-                                           " cannot add %s", argv[i+3]);
-                               i += 3;
-                               continue;
-                       }
-                       if (!strcmp(path, "--chmod=-x") ||
-                           !strcmp(path, "--chmod=+x")) {
-                               if (argc <= i+1)
-                                       die("git-update-index: %s <path>", path);
-                               set_executable_bit = path[8];
-                               continue;
-                       }
-                       if (!strcmp(path, "--assume-unchanged")) {
-                               mark_valid_only = MARK_VALID;
-                               continue;
-                       }
-                       if (!strcmp(path, "--no-assume-unchanged")) {
-                               mark_valid_only = UNMARK_VALID;
-                               continue;
-                       }
-                       if (!strcmp(path, "--info-only")) {
-                               info_only = 1;
-                               continue;
-                       }
-                       if (!strcmp(path, "--force-remove")) {
-                               force_remove = 1;
-                               continue;
-                       }
-                       if (!strcmp(path, "-z")) {
-                               line_termination = 0;
-                               continue;
-                       }
-                       if (!strcmp(path, "--stdin")) {
-                               if (i != argc - 1)
-                                       die("--stdin must be at the end");
-                               read_from_stdin = 1;
-                               break;
-                       }
-                       if (!strcmp(path, "--index-info")) {
-                               if (i != argc - 1)
-                                       die("--index-info must be at the end");
-                               allow_add = allow_replace = allow_remove = 1;
-                               read_index_info(line_termination);
-                               break;
-                       }
-                       if (!strcmp(path, "--unresolve")) {
-                               has_errors = do_unresolve(argc - i, argv + i,
-                                                         prefix, prefix_length);
-                               if (has_errors)
-                                       active_cache_changed = 0;
-                               goto finish;
-                       }
-                       if (!strcmp(path, "--again")) {
-                               has_errors = do_reupdate(argc - i, argv + i,
-                                                        prefix, prefix_length);
-                               if (has_errors)
-                                       active_cache_changed = 0;
-                               goto finish;
-                       }
-                       if (!strcmp(path, "--ignore-missing")) {
-                               refresh_flags |= REFRESH_IGNORE_MISSING;
-                               continue;
-                       }
-                       if (!strcmp(path, "--verbose")) {
-                               verbose = 1;
-                               continue;
-                       }
-                       if (!strcmp(path, "-h") || !strcmp(path, "--help"))
-                               usage(update_index_usage);
-                       die("unknown option %s", path);
-               }
-               update_one(path, prefix, prefix_length);
-               if (set_executable_bit)
-                       chmod_path(set_executable_bit, path);
-       }
-       if (read_from_stdin) {
-               struct strbuf buf;
-               strbuf_init(&buf);
-               while (1) {
-                       char *path_name;
-                       const char *p;
-                       read_line(&buf, stdin, line_termination);
-                       if (buf.eof)
-                               break;
-                       if (line_termination && buf.buf[0] == '"')
-                               path_name = unquote_c_style(buf.buf, NULL);
-                       else
-                               path_name = buf.buf;
-                       p = prefix_path(prefix, prefix_length, path_name);
-                       update_one(p, NULL, 0);
-                       if (set_executable_bit)
-                               chmod_path(set_executable_bit, p);
-                       if (p < path_name || p > path_name + strlen(path_name))
-                               free((char*) p);
-                       if (path_name != buf.buf)
-                               free(path_name);
-               }
-       }
-
- finish:
-       if (active_cache_changed) {
-               if (write_cache(newfd, active_cache, active_nr) ||
-                   commit_lock_file(&lock_file))
-                       die("Unable to write new index file");
-       }
-
-       return has_errors ? 1 : 0;
-}
diff --git a/update-ref.c b/update-ref.c
deleted file mode 100644 (file)
index a1e6bb9..0000000
+++ /dev/null
@@ -1,56 +0,0 @@
-#include "cache.h"
-#include "refs.h"
-
-static const char git_update_ref_usage[] =
-"git-update-ref <refname> <value> [<oldval>] [-m <reason>]";
-
-int main(int argc, char **argv)
-{
-       const char *refname=NULL, *value=NULL, *oldval=NULL, *msg=NULL;
-       struct ref_lock *lock;
-       unsigned char sha1[20], oldsha1[20];
-       int i;
-
-       setup_git_directory();
-       git_config(git_default_config);
-
-       for (i = 1; i < argc; i++) {
-               if (!strcmp("-m", argv[i])) {
-                       if (i+1 >= argc)
-                               usage(git_update_ref_usage);
-                       msg = argv[++i];
-                       if (!*msg)
-                               die("Refusing to perform update with empty message.");
-                       if (strchr(msg, '\n'))
-                               die("Refusing to perform update with \\n in message.");
-                       continue;
-               }
-               if (!refname) {
-                       refname = argv[i];
-                       continue;
-               }
-               if (!value) {
-                       value = argv[i];
-                       continue;
-               }
-               if (!oldval) {
-                       oldval = argv[i];
-                       continue;
-               }
-       }
-       if (!refname || !value)
-               usage(git_update_ref_usage);
-
-       if (get_sha1(value, sha1))
-               die("%s: not a valid SHA1", value);
-       memset(oldsha1, 0, 20);
-       if (oldval && get_sha1(oldval, oldsha1))
-               die("%s: not a valid old SHA1", oldval);
-
-       lock = lock_any_ref_for_update(refname, oldval ? oldsha1 : NULL, 0);
-       if (!lock)
-               return 1;
-       if (write_ref_sha1(lock, sha1, msg) < 0)
-               return 1;
-       return 0;
-}
diff --git a/write-tree.c b/write-tree.c
deleted file mode 100644 (file)
index bd07da6..0000000
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * GIT - The information manager from hell
- *
- * Copyright (C) Linus Torvalds, 2005
- */
-#include "cache.h"
-#include "tree.h"
-#include "cache-tree.h"
-
-static int missing_ok = 0;
-static char *prefix = NULL;
-
-static const char write_tree_usage[] =
-"git-write-tree [--missing-ok] [--prefix=<prefix>/]";
-
-static struct lock_file lock_file;
-
-int main(int argc, char **argv)
-{
-       int entries, was_valid, newfd;
-
-       setup_git_directory();
-
-       newfd = hold_lock_file_for_update(&lock_file, get_index_file());
-       entries = read_cache();
-
-       while (1 < argc) {
-               char *arg = argv[1];
-               if (!strcmp(arg, "--missing-ok"))
-                       missing_ok = 1;
-               else if (!strncmp(arg, "--prefix=", 9))
-                       prefix = arg + 9;
-               else
-                       die(write_tree_usage);
-               argc--; argv++;
-       }
-
-       if (argc > 2)
-               die("too many options");
-
-       if (entries < 0)
-               die("git-write-tree: error reading cache");
-
-       if (!active_cache_tree)
-               active_cache_tree = cache_tree();
-
-       was_valid = cache_tree_fully_valid(active_cache_tree);
-       if (!was_valid) {
-               if (cache_tree_update(active_cache_tree,
-                                     active_cache, active_nr,
-                                     missing_ok, 0) < 0)
-                       die("git-write-tree: error building trees");
-               if (0 <= newfd) {
-                       if (!write_cache(newfd, active_cache, active_nr))
-                               commit_lock_file(&lock_file);
-               }
-               /* Not being able to write is fine -- we are only interested
-                * in updating the cache-tree part, and if the next caller
-                * ends up using the old index with unupdated cache-tree part
-                * it misses the work we did here, but that is just a
-                * performance penalty and not a big deal.
-                */
-       }
-       if (prefix) {
-               struct cache_tree *subtree =
-                       cache_tree_find(active_cache_tree, prefix);
-               printf("%s\n", sha1_to_hex(subtree->sha1));
-       }
-       else
-               printf("%s\n", sha1_to_hex(active_cache_tree->sha1));
-       return 0;
-}