Stop carrying Geert's similarity estimator.
authorJunio C Hamano <junkio@cox.net>
Thu, 20 Apr 2006 07:33:37 +0000 (00:33 -0700)
committerJunio C Hamano <junkio@cox.net>
Thu, 20 Apr 2006 07:37:08 +0000 (00:37 -0700)
I tried the code with pack-objects.c::try_delta(), and was
somewhat dissapointed. The current type-path based heuristics
already limits the delta attempts to similar objects anyway, so
it is not a good place to apply it.

The Net never forgets, so we can resurrect it if we wanted to
later.

44 files changed:
Documentation/diff-options.txt
Documentation/git-annotate.txt [new file with mode: 0644]
Documentation/git-blame.txt [new file with mode: 0644]
Documentation/git-clone.txt
Documentation/git.txt
GIT-VERSION-GEN
Makefile
combine-diff.c
commit.c
commit.h
config.c
connect.c
daemon.c
diff-files.c
diff-lib.c [new file with mode: 0644]
diff-tree.c
diff.c [deleted file]
diff.h
exec_cmd.c
git-merge.sh
git-repack.sh
git-svnimport.perl
git.c
gitk
gsimm.c
gsimm.h
http-push.c
log-tree.c
log-tree.h
pack-objects.c
pager.c
quote.c
rev-list.c
revision.c
revision.h
sha1_file.c
sha1_name.c
t/Makefile
t/t1200-tutorial.sh
templates/hooks--pre-commit
test-gsimm.c
tree-walk.c
tree-walk.h
update-index.c
index 447e522a7bf20f7c0c524aee169c776e6d51471c..c183dc9da0392bcfbae3e1edcd4d13f89a9923c3 100644 (file)
@@ -10,6 +10,9 @@
 --stat::
        Generate a diffstat instead of a patch.
 
+--patch-with-stat::
+       Generate patch and prepend its diffstat.
+
 -z::
        \0 line termination on output
 
diff --git a/Documentation/git-annotate.txt b/Documentation/git-annotate.txt
new file mode 100644 (file)
index 0000000..7baf731
--- /dev/null
@@ -0,0 +1,44 @@
+git-annotate(1)
+===============
+
+NAME
+----
+git-annotate - Annotate file lines with commit info
+
+SYNOPSIS
+--------
+git-annotate [options] file [revision]
+
+DESCRIPTION
+-----------
+Annotates each line in the given file with information from the commit
+which introduced the line. Optionally annotate from a given revision.
+
+OPTIONS
+-------
+-l, --long::
+       Show long rev (Defaults off).
+
+-t, --time::
+       Show raw timestamp (Defaults off).
+
+-r, --rename::
+       Follow renames (Defaults on).
+
+-S, --rev-file <revs-file>::
+       Use revs from revs-file instead of calling git-rev-list.
+
+-h, --help::
+       Show help message.
+
+SEE ALSO
+--------
+gitlink:git-blame[1]
+
+AUTHOR
+------
+Written by Ryan Anderson <ryan@michonline.com>.
+
+GIT
+---
+Part of the gitlink:git[7] suite
diff --git a/Documentation/git-blame.txt b/Documentation/git-blame.txt
new file mode 100644 (file)
index 0000000..5189878
--- /dev/null
@@ -0,0 +1,42 @@
+git-blame(1)
+============
+
+NAME
+----
+git-blame - Blame file lines on commits
+
+SYNOPSIS
+--------
+git-blame file [options] file [revision]
+
+DESCRIPTION
+-----------
+Annotates each line in the given file with information from the commit
+which introduced the line. Start annotation from the given revision.
+
+OPTIONS
+-------
+-c, --compability::
+       Use the same output mode as git-annotate (Default: off).
+
+-l, --long::
+       Show long rev (Defaults off).
+
+-S, --rev-file <revs-file>::
+       Use revs from revs-file instead of calling git-rev-list.
+
+-h, --help::
+       Show help message.
+
+
+SEE ALSO
+--------
+gitlink:git-annotate[1]
+
+AUTHOR
+------
+Written by Fredrik Kuivinen <freku045@student.liu.se>.
+
+GIT
+---
+Part of the gitlink:git[7] suite
index 9ac54c282c3fe5ef9182f2829ef51cf8de8a1475..131e445747db6f9d573207ba5395b0264f983efd 100644 (file)
@@ -10,6 +10,7 @@ SYNOPSIS
 --------
 [verse]
 'git-clone' [-l [-s]] [-q] [-n] [--bare] [-o <name>] [-u <upload-pack>]
+         [--reference <repository>]
          <repository> [<directory>]
 
 DESCRIPTION
@@ -46,10 +47,18 @@ OPTIONS
 -s::
        When the repository to clone is on the local machine,
        instead of using hard links, automatically setup
-       .git/objects/info/alternatives to share the objects
+       .git/objects/info/alternates to share the objects
        with the source repository.  The resulting repository
        starts out without any object of its own.
 
+--reference <repository>::
+       If the reference repository is on the local machine
+       automatically setup .git/objects/info/alternates to
+       obtain objects from the reference repository.  Using
+       an already existing repository as an alternate will
+       require less objects to be copied from the repository
+       being cloned, reducing network and local storage costs.
+
 --quiet::
 -q::
        Operate quietly.  This flag is passed to "rsync" and
@@ -112,6 +121,16 @@ $ git show-branch
 ------------
 
 
+Clone from upstream while borrowing from an existing local directory::
++
+------------
+$ git clone --reference my2.6 \
+       git://git.kernel.org/pub/scm/.../linux-2.7 \
+       my2.7
+$ cd my2.7
+------------
+
+
 Create a bare repository to publish your changes to the public::
 +
 ------------
index 03d860b191001c2fa3a5c70f68ad0a8c4a558203..24ca55da684265b5e14f0e60c00917c3d623fcbc 100644 (file)
@@ -399,6 +399,12 @@ gitlink:git-update-ref[1]::
 
 Interrogators:
 
+gitlink:git-annotate[1]::
+       Annotate file lines with commit info.
+
+gitlink:git-blame[1]::
+       Blame file lines on commits.
+
 gitlink:git-check-ref-format[1]::
        Make sure ref name is well formed.
 
index e88fe5ae7cd6c4b9e423a95ec125e4697467f0f1..7fcefcd7c45084d005e8fd677cdf72921864f4a2 100755 (executable)
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 GVF=GIT-VERSION-FILE
-DEF_VER=v1.3-rc4.GIT
+DEF_VER=v1.3.GIT
 
 # First try git-describe, then see if there is a version file
 # (included in release tarballs), then default
index aa499ed52fbfce3641cbd9ec2f737489f8202d67..9cbb90b95d88e5928cfc5207c581a933b140ecad 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -199,12 +199,12 @@ LIB_H = \
        tree-walk.h log-tree.h
 
 DIFF_OBJS = \
-       diff.o diffcore-break.o diffcore-order.o \
+       diff-lib.o diffcore-break.o diffcore-order.o \
        diffcore-pickaxe.o diffcore-rename.o tree-diff.o combine-diff.o \
        diffcore-delta.o log-tree.o
 
 LIB_OBJS = \
-       blob.o commit.o connect.o csum-file.o gsimm.o rabinpoly.o \
+       blob.o commit.o connect.o csum-file.o \
        date.o diff-delta.o entry.o exec_cmd.o ident.o index.o \
        object.o pack-check.o patch-delta.o path.o pkt-line.o \
        quote.o read-cache.o refs.o run-command.o \
@@ -656,7 +656,7 @@ rpm: dist
 clean:
        rm -f *.o mozilla-sha1/*.o arm/*.o ppc/*.o compat/*.o xdiff/*.o \
                $(LIB_FILE) $(XDIFF_LIB)
-       rm -f $(ALL_PROGRAMS) git$X
+       rm -f $(ALL_PROGRAMS) $(BUILT_INS) git$X
        rm -f test-date$X test-delta$X test-gsimm$X
        rm -f *.spec *.pyc *.pyo */*.pyc */*.pyo common-cmds.h TAGS tags
        rm -rf $(GIT_TARNAME)
@@ -675,7 +675,6 @@ check-docs::
        @for v in $(ALL_PROGRAMS) $(BUILT_INS) git$X gitk; \
        do \
                case "$$v" in \
-               git-annotate | git-blame | \
                git-merge-octopus | git-merge-ours | git-merge-recursive | \
                git-merge-resolve | git-merge-stupid | \
                git-ssh-pull | git-ssh-push ) continue ;; \
index 9bd27f82ec1d7c313987325a4948a5eed8c9b799..ca36f5d5e7342e9198675ce0faf11ba49ca67876 100644 (file)
@@ -5,6 +5,7 @@
 #include "diffcore.h"
 #include "quote.h"
 #include "xdiff-interface.h"
+#include "log-tree.h"
 
 static int uninteresting(struct diff_filepair *p)
 {
@@ -584,12 +585,22 @@ static void reuse_combine_diff(struct sline *sline, unsigned long cnt,
        sline->p_lno[i] = sline->p_lno[j];
 }
 
+static void dump_quoted_path(const char *prefix, const char *path)
+{
+       fputs(prefix, stdout);
+       if (quote_c_style(path, NULL, NULL, 0))
+               quote_c_style(path, NULL, stdout, 0);
+       else
+               printf("%s", path);
+       putchar('\n');
+}
+
 static int show_patch_diff(struct combine_diff_path *elem, int num_parent,
-                          int dense, const char *header,
-                          struct diff_options *opt)
+                          int dense, struct rev_info *rev)
 {
+       struct diff_options *opt = &rev->diffopt;
        unsigned long result_size, cnt, lno;
-       char *result, *cp, *ep;
+       char *result, *cp;
        struct sline *sline; /* survived lines */
        int mode_differs = 0;
        int i, show_hunks, shown_header = 0;
@@ -641,7 +652,6 @@ static int show_patch_diff(struct combine_diff_path *elem, int num_parent,
                cnt++; /* incomplete line */
 
        sline = xcalloc(cnt+2, sizeof(*sline));
-       ep = result;
        sline[0].bol = result;
        for (lno = 0; lno <= cnt + 1; lno++) {
                sline[lno].lost_tail = &sline[lno].lost_head;
@@ -689,16 +699,9 @@ static int show_patch_diff(struct combine_diff_path *elem, int num_parent,
        if (show_hunks || mode_differs || working_tree_file) {
                const char *abb;
 
-               if (header) {
-                       shown_header++;
-                       printf("%s%c", header, opt->line_termination);
-               }
-               printf("diff --%s ", dense ? "cc" : "combined");
-               if (quote_c_style(elem->path, NULL, NULL, 0))
-                       quote_c_style(elem->path, NULL, stdout, 0);
-               else
-                       printf("%s", elem->path);
-               putchar('\n');
+               if (rev->loginfo)
+                       show_log(rev, rev->loginfo, "\n");
+               dump_quoted_path(dense ? "diff --cc " : "diff --combined ", elem->path);
                printf("index ");
                for (i = 0; i < num_parent; i++) {
                        abb = find_unique_abbrev(elem->parent[i].sha1,
@@ -729,6 +732,8 @@ static int show_patch_diff(struct combine_diff_path *elem, int num_parent,
                        }
                        putchar('\n');
                }
+               dump_quoted_path("--- a/", elem->path);
+               dump_quoted_path("+++ b/", elem->path);
                dump_sline(sline, cnt, num_parent);
        }
        free(result);
@@ -750,9 +755,10 @@ static int show_patch_diff(struct combine_diff_path *elem, int num_parent,
 
 #define COLONS "::::::::::::::::::::::::::::::::"
 
-static void show_raw_diff(struct combine_diff_path *p, int num_parent, const char *header, struct diff_options *opt)
+static void show_raw_diff(struct combine_diff_path *p, int num_parent, struct rev_info *rev)
 {
-       int i, offset, mod_type = 'A';
+       struct diff_options *opt = &rev->diffopt;
+       int i, offset;
        const char *prefix;
        int line_termination, inter_name_termination;
 
@@ -761,15 +767,8 @@ static void show_raw_diff(struct combine_diff_path *p, int num_parent, const cha
        if (!line_termination)
                inter_name_termination = 0;
 
-       if (header)
-               printf("%s%c", header, line_termination);
-
-       for (i = 0; i < num_parent; i++) {
-               if (p->parent[i].mode)
-                       mod_type = 'M';
-       }
-       if (!p->mode)
-               mod_type = 'D';
+       if (rev->loginfo)
+               show_log(rev, rev->loginfo, "\n");
 
        if (opt->output_format == DIFF_FORMAT_RAW) {
                offset = strlen(COLONS) - num_parent;
@@ -810,40 +809,44 @@ static void show_raw_diff(struct combine_diff_path *p, int num_parent, const cha
        }
 }
 
-int show_combined_diff(struct combine_diff_path *p,
+void show_combined_diff(struct combine_diff_path *p,
                       int num_parent,
                       int dense,
-                      const char *header,
-                      struct diff_options *opt)
+                      struct rev_info *rev)
 {
+       struct diff_options *opt = &rev->diffopt;
        if (!p->len)
-               return 0;
+               return;
        switch (opt->output_format) {
        case DIFF_FORMAT_RAW:
        case DIFF_FORMAT_NAME_STATUS:
        case DIFF_FORMAT_NAME:
-               show_raw_diff(p, num_parent, header, opt);
-               return 1;
-
-       default:
+               show_raw_diff(p, num_parent, rev);
+               return;
        case DIFF_FORMAT_PATCH:
-               return show_patch_diff(p, num_parent, dense, header, opt);
+               show_patch_diff(p, num_parent, dense, rev);
+               return;
+       default:
+               return;
        }
 }
 
-const char *diff_tree_combined_merge(const unsigned char *sha1,
-                            const char *header, int dense,
-                            struct diff_options *opt)
+void diff_tree_combined_merge(const unsigned char *sha1,
+                            int dense, struct rev_info *rev)
 {
+       struct diff_options *opt = &rev->diffopt;
        struct commit *commit = lookup_commit(sha1);
        struct diff_options diffopts;
        struct commit_list *parents;
        struct combine_diff_path *p, *paths = NULL;
        int num_parent, i, num_paths;
+       int do_diffstat;
 
+       do_diffstat = (opt->output_format == DIFF_FORMAT_DIFFSTAT ||
+                      opt->with_stat);
        diffopts = *opt;
-       diffopts.output_format = DIFF_FORMAT_NO_OUTPUT;
        diffopts.with_raw = 0;
+       diffopts.with_stat = 0;
        diffopts.recursive = 1;
 
        /* count parents */
@@ -857,11 +860,24 @@ const char *diff_tree_combined_merge(const unsigned char *sha1,
             parents;
             parents = parents->next, i++) {
                struct commit *parent = parents->item;
+               /* show stat against the first parent even
+                * when doing combined diff.
+                */
+               if (i == 0 && do_diffstat)
+                       diffopts.output_format = DIFF_FORMAT_DIFFSTAT;
+               else
+                       diffopts.output_format = DIFF_FORMAT_NO_OUTPUT;
                diff_tree_sha1(parent->object.sha1, commit->object.sha1, "",
                               &diffopts);
                diffcore_std(&diffopts);
                paths = intersect_paths(paths, i, num_parent);
+
+               if (do_diffstat && rev->loginfo)
+                       show_log(rev, rev->loginfo,
+                                opt->with_stat ? "---\n" : "\n");
                diff_flush(&diffopts);
+               if (opt->with_stat)
+                       putchar('\n');
        }
 
        /* find out surviving paths */
@@ -874,17 +890,13 @@ const char *diff_tree_combined_merge(const unsigned char *sha1,
                        int saved_format = opt->output_format;
                        opt->output_format = DIFF_FORMAT_RAW;
                        for (p = paths; p; p = p->next) {
-                               if (show_combined_diff(p, num_parent, dense,
-                                                      header, opt))
-                                       header = NULL;
+                               show_combined_diff(p, num_parent, dense, rev);
                        }
                        opt->output_format = saved_format;
                        putchar(opt->line_termination);
                }
                for (p = paths; p; p = p->next) {
-                       if (show_combined_diff(p, num_parent, dense,
-                                              header, opt))
-                               header = NULL;
+                       show_combined_diff(p, num_parent, dense, rev);
                }
        }
 
@@ -894,5 +906,4 @@ const char *diff_tree_combined_merge(const unsigned char *sha1,
                paths = paths->next;
                free(tmp);
        }
-       return header;
 }
index ca25574500fe3c88d2cc2953b7dd019b3acab9e7..06e00987cc471cb80b9ab891cb2c99bc42f26b96 100644 (file)
--- a/commit.c
+++ b/commit.c
@@ -36,6 +36,8 @@ enum cmit_fmt get_commit_format(const char *arg)
                return CMIT_FMT_FULL;
        if (!strcmp(arg, "=fuller"))
                return CMIT_FMT_FULLER;
+       if (!strcmp(arg, "=email"))
+               return CMIT_FMT_EMAIL;
        if (!strcmp(arg, "=oneline"))
                return CMIT_FMT_ONELINE;
        die("invalid --pretty format");
@@ -160,8 +162,8 @@ struct commit_graft *read_graft_line(char *buf, int len)
 
        if (buf[len-1] == '\n')
                buf[--len] = 0;
-       if (buf[0] == '#')
-               return 0;
+       if (buf[0] == '#' || buf[0] == '\0')
+               return NULL;
        if ((len + 1) % 41) {
        bad_graft_data:
                error("bad graft data: %s", buf);
@@ -192,6 +194,8 @@ int read_graft_file(const char *graft_file)
                /* The format is just "Commit Parent1 Parent2 ...\n" */
                int len = strlen(buf);
                struct commit_graft *graft = read_graft_line(buf, len);
+               if (!graft)
+                       continue;
                if (register_commit_graft(graft, 1))
                        error("duplicate graft data: %s", buf);
        }
@@ -426,6 +430,10 @@ static int add_user_info(const char *what, enum cmit_fmt fmt, char *buf, const c
        time = strtoul(date, &date, 10);
        tz = strtol(date, NULL, 10);
 
+       if (fmt == CMIT_FMT_EMAIL) {
+               what = "From";
+               filler = "";
+       }
        ret = sprintf(buf, "%s: %.*s%.*s\n", what,
                      (fmt == CMIT_FMT_FULLER) ? 4 : 0,
                      filler, namelen, line);
@@ -433,6 +441,9 @@ static int add_user_info(const char *what, enum cmit_fmt fmt, char *buf, const c
        case CMIT_FMT_MEDIUM:
                ret += sprintf(buf + ret, "Date:   %s\n", show_date(time, tz));
                break;
+       case CMIT_FMT_EMAIL:
+               ret += sprintf(buf + ret, "Date: %s\n", show_date(time, tz));
+               break;
        case CMIT_FMT_FULLER:
                ret += sprintf(buf + ret, "%sDate: %s\n", what, show_date(time, tz));
                break;
@@ -443,10 +454,12 @@ static int add_user_info(const char *what, enum cmit_fmt fmt, char *buf, const c
        return ret;
 }
 
-static int is_empty_line(const char *line, int len)
+static int is_empty_line(const char *line, int *len_p)
 {
+       int len = *len_p;
        while (len && isspace(line[len-1]))
                len--;
+       *len_p = len;
        return !len;
 }
 
@@ -455,7 +468,8 @@ static int add_merge_info(enum cmit_fmt fmt, char *buf, const struct commit *com
        struct commit_list *parent = commit->parents;
        int offset;
 
-       if ((fmt == CMIT_FMT_ONELINE) || !parent || !parent->next)
+       if ((fmt == CMIT_FMT_ONELINE) || (fmt == CMIT_FMT_EMAIL) ||
+           !parent || !parent->next)
                return 0;
 
        offset = sprintf(buf, "Merge:");
@@ -478,9 +492,15 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit
 {
        int hdr = 1, body = 0;
        unsigned long offset = 0;
-       int indent = (fmt == CMIT_FMT_ONELINE) ? 0 : 4;
+       int indent = 4;
        int parents_shown = 0;
        const char *msg = commit->buffer;
+       const char *subject = NULL;
+
+       if (fmt == CMIT_FMT_EMAIL)
+               subject = "Subject: [PATCH] ";
+       if (fmt == CMIT_FMT_ONELINE || fmt == CMIT_FMT_EMAIL)
+               indent = 0;
 
        for (;;) {
                const char *line = msg;
@@ -504,7 +524,7 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit
                if (hdr) {
                        if (linelen == 1) {
                                hdr = 0;
-                               if (fmt != CMIT_FMT_ONELINE)
+                               if ((fmt != CMIT_FMT_ONELINE) && !subject)
                                        buf[offset++] = '\n';
                                continue;
                        }
@@ -542,20 +562,28 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit
                        continue;
                }
 
-               if (is_empty_line(line, linelen)) {
+               if (is_empty_line(line, &linelen)) {
                        if (!body)
                                continue;
+                       if (subject)
+                               continue;
                        if (fmt == CMIT_FMT_SHORT)
                                break;
                } else {
                        body = 1;
                }
 
+               if (subject) {
+                       memcpy(buf + offset, subject, 9);
+                       offset += 9;
+               }
                memset(buf + offset, ' ', indent);
                memcpy(buf + offset + indent, line, linelen);
                offset += linelen + indent;
+               buf[offset++] = '\n';
                if (fmt == CMIT_FMT_ONELINE)
                        break;
+               subject = NULL;
        }
        while (offset && isspace(buf[offset-1]))
                offset--;
index 918c9ab5e4eb3173c0d0a0748df40ef857fc7b8b..01eec60a1ecc2ff3c73850cb510ba71767e47e03 100644 (file)
--- a/commit.h
+++ b/commit.h
@@ -45,6 +45,9 @@ enum cmit_fmt {
        CMIT_FMT_FULL,
        CMIT_FMT_FULLER,
        CMIT_FMT_ONELINE,
+       CMIT_FMT_EMAIL,
+
+       CMIT_FMT_UNSPECIFIED,
 };
 
 extern enum cmit_fmt get_commit_format(const char *arg);
index 95ec34923d3fcf4d2a8fa16cbab9a5cbfe8e4f6f..7ea8a7369a316c5a74266d891e9173a5926a8c53 100644 (file)
--- a/config.c
+++ b/config.c
@@ -420,6 +420,7 @@ int git_config_set_multivar(const char* key, const char* value,
 {
        int i;
        int fd, in_fd;
+       int ret;
        char* config_filename = strdup(git_path("config"));
        char* lock_file = strdup(git_path("config.lock"));
        const char* last_dot = strrchr(key, '.');
@@ -429,9 +430,10 @@ int git_config_set_multivar(const char* key, const char* value,
         * key name separated by a dot, we have to know where the dot is.
         */
 
-       if (last_dot == NULL) { 
+       if (last_dot == NULL) {
                fprintf(stderr, "key does not contain a section: %s\n", key);
-               return 2;
+               ret = 2;
+               goto out_free;
        }
        store.baselen = last_dot - key;
 
@@ -447,7 +449,8 @@ int git_config_set_multivar(const char* key, const char* value,
                                 (i == store.baselen+1 && !isalpha(key[i])))) {
                        fprintf(stderr, "invalid key: %s\n", key);
                        free(store.key);
-                       return 1;
+                       ret = 1;
+                       goto out_free;
                } else
                        store.key[i] = tolower(key[i]);
        store.key[i] = 0;
@@ -460,7 +463,8 @@ int git_config_set_multivar(const char* key, const char* value,
        if (fd < 0) {
                fprintf(stderr, "could not lock config file\n");
                free(store.key);
-               return -1;
+               ret = -1;
+               goto out_free;
        }
 
        /*
@@ -475,13 +479,15 @@ int git_config_set_multivar(const char* key, const char* value,
                              strerror(errno));
                        close(fd);
                        unlink(lock_file);
-                       return 3; /* same as "invalid config file" */
+                       ret = 3; /* same as "invalid config file" */
+                       goto out_free;
                }
                /* if nothing to unset, error out */
                if (value == NULL) {
                        close(fd);
                        unlink(lock_file);
-                       return 5;
+                       ret = 5;
+                       goto out_free;
                }
 
                store.key = (char*)key;
@@ -507,7 +513,8 @@ int git_config_set_multivar(const char* key, const char* value,
                                fprintf(stderr, "Invalid pattern: %s\n",
                                        value_regex);
                                free(store.value_regex);
-                               return 6;
+                               ret = 6;
+                               goto out_free;
                        }
                }
 
@@ -528,7 +535,8 @@ int git_config_set_multivar(const char* key, const char* value,
                                regfree(store.value_regex);
                                free(store.value_regex);
                        }
-                       return 3;
+                       ret = 3;
+                       goto out_free;
                }
 
                free(store.key);
@@ -542,7 +550,8 @@ int git_config_set_multivar(const char* key, const char* value,
                                (store.seen > 1 && multi_replace == 0)) {
                        close(fd);
                        unlink(lock_file);
-                       return 5;
+                       ret = 5;
+                       goto out_free;
                }
 
                fstat(in_fd, &st);
@@ -593,10 +602,18 @@ int git_config_set_multivar(const char* key, const char* value,
 
        if (rename(lock_file, config_filename) < 0) {
                fprintf(stderr, "Could not rename the lock file?\n");
-               return 4;
+               ret = 4;
+               goto out_free;
        }
 
-       return 0;
+       ret = 0;
+
+out_free:
+       if (config_filename)
+               free(config_filename);
+       if (lock_file)
+               free(lock_file);
+       return ret;
 }
 
 
index 3f2d65c313418c9e6b433a18f4b7d03c6ff276c2..6a8f8a6a24dd8894cfcb7e89d8952a9a595d5200 100644 (file)
--- a/connect.c
+++ b/connect.c
@@ -74,7 +74,7 @@ int get_ack(int fd, unsigned char *result_sha1)
                line[--len] = 0;
        if (!strcmp(line, "NAK"))
                return 0;
-       if (!strncmp(line, "ACK ", 3)) {
+       if (!strncmp(line, "ACK ", 4)) {
                if (!get_sha1_hex(line+4, result_sha1)) {
                        if (strstr(line+45, "continue"))
                                return 2;
@@ -567,6 +567,7 @@ int git_connect(int fd[2], char *url, const char *prog)
        int pipefd[2][2];
        pid_t pid;
        enum protocol protocol = PROTO_LOCAL;
+       int free_path = 0;
 
        host = strstr(url, "://");
        if(host) {
@@ -610,16 +611,23 @@ int git_connect(int fd[2], char *url, const char *prog)
                char *ptr = path;
                if (path[1] == '~')
                        path++;
-               else
+               else {
                        path = strdup(ptr);
+                       free_path = 1;
+               }
 
                *ptr = '\0';
        }
 
        if (protocol == PROTO_GIT) {
+               int ret;
                if (git_use_proxy(host))
-                       return git_proxy_connect(fd, prog, host, path);
-               return git_tcp_connect(fd, prog, host, path);
+                       ret = git_proxy_connect(fd, prog, host, path);
+               else
+                       ret = git_tcp_connect(fd, prog, host, path);
+               if (free_path)
+                       free(path);
+               return ret;
        }
 
        if (pipe(pipefd[0]) < 0 || pipe(pipefd[1]) < 0)
@@ -659,6 +667,8 @@ int git_connect(int fd[2], char *url, const char *prog)
        fd[1] = pipefd[1][1];
        close(pipefd[0][1]);
        close(pipefd[1][0]);
+       if (free_path)
+               free(path);
        return pid;
 }
 
index a1ccda30e2211368c66c1186437891f548f0e338..776749e3432fca916981d73d04a1fa19ed3d88b8 100644 (file)
--- a/daemon.c
+++ b/daemon.c
@@ -535,7 +535,7 @@ static int socksetup(int port, int **socklist_p)
 
                if (set_reuse_addr(sockfd)) {
                        close(sockfd);
-                       return 0;       /* not fatal */
+                       continue;
                }
 
                if (bind(sockfd, ai->ai_addr, ai->ai_addrlen) < 0) {
index 3e7f5f105b19fd47fd8b3716617592ec2a60da47..ffbef48b2e05869a0a74ea7df53ee80106aa81b3 100644 (file)
@@ -5,12 +5,14 @@
  */
 #include "cache.h"
 #include "diff.h"
+#include "commit.h"
+#include "revision.h"
 
 static const char diff_files_usage[] =
 "git-diff-files [-q] [-0/-1/2/3 |-c|--cc] [<common diff options>] [<path>...]"
 COMMON_DIFF_OPTIONS_HELP;
 
-static struct diff_options diff_options;
+static struct rev_info rev;
 static int silent = 0;
 static int diff_unmerged_stage = 2;
 static int combine_merges = 0;
@@ -18,12 +20,12 @@ static int dense_combined_merges = 0;
 
 static void show_unmerge(const char *path)
 {
-       diff_unmerge(&diff_options, path);
+       diff_unmerge(&rev.diffopt, path);
 }
 
 static void show_file(int pfx, struct cache_entry *ce)
 {
-       diff_addremove(&diff_options, pfx, ntohl(ce->ce_mode),
+       diff_addremove(&rev.diffopt, pfx, ntohl(ce->ce_mode),
                       ce->sha1, ce->name, NULL);
 }
 
@@ -31,7 +33,7 @@ static void show_modified(int oldmode, int mode,
                          const unsigned char *old_sha1, const unsigned char *sha1,
                          char *path)
 {
-       diff_change(&diff_options, oldmode, mode, old_sha1, sha1, path, NULL);
+       diff_change(&rev.diffopt, oldmode, mode, old_sha1, sha1, path, NULL);
 }
 
 int main(int argc, const char **argv)
@@ -41,7 +43,7 @@ int main(int argc, const char **argv)
        int entries, i;
 
        git_config(git_diff_config);
-       diff_setup(&diff_options);
+       diff_setup(&rev.diffopt);
        while (1 < argc && argv[1][0] == '-') {
                if (!strcmp(argv[1], "--")) {
                        argv++;
@@ -74,7 +76,7 @@ int main(int argc, const char **argv)
                        dense_combined_merges = combine_merges = 1;
                else {
                        int diff_opt_cnt;
-                       diff_opt_cnt = diff_opt_parse(&diff_options,
+                       diff_opt_cnt = diff_opt_parse(&rev.diffopt,
                                                      argv+1, argc-1);
                        if (diff_opt_cnt < 0)
                                usage(diff_files_usage);
@@ -89,13 +91,13 @@ int main(int argc, const char **argv)
                argv++; argc--;
        }
        if (dense_combined_merges)
-               diff_options.output_format = DIFF_FORMAT_PATCH;
+               rev.diffopt.output_format = DIFF_FORMAT_PATCH;
 
        /* Find the directory, and set up the pathspec */
        pathspec = get_pathspec(prefix, argv + 1);
        entries = read_cache();
 
-       if (diff_setup_done(&diff_options) < 0)
+       if (diff_setup_done(&rev.diffopt) < 0)
                usage(diff_files_usage);
 
        /* At this point, if argc == 1, then we are doing everything.
@@ -167,8 +169,7 @@ int main(int argc, const char **argv)
                        if (combine_merges && num_compare_stages == 2) {
                                show_combined_diff(&combine.p, 2,
                                                   dense_combined_merges,
-                                                  NULL,
-                                                  &diff_options);
+                                                  &rev);
                                free(combine.p.path);
                                continue;
                        }
@@ -194,7 +195,7 @@ int main(int argc, const char **argv)
                        continue;
                }
                changed = ce_match_stat(ce, &st, 0);
-               if (!changed && !diff_options.find_copies_harder)
+               if (!changed && !rev.diffopt.find_copies_harder)
                        continue;
                oldmode = ntohl(ce->ce_mode);
 
@@ -207,7 +208,7 @@ int main(int argc, const char **argv)
                              ce->sha1, (changed ? null_sha1 : ce->sha1),
                              ce->name);
        }
-       diffcore_std(&diff_options);
-       diff_flush(&diff_options);
+       diffcore_std(&rev.diffopt);
+       diff_flush(&rev.diffopt);
        return 0;
 }
diff --git a/diff-lib.c b/diff-lib.c
new file mode 100644 (file)
index 0000000..13b216f
--- /dev/null
@@ -0,0 +1,1795 @@
+/*
+ * Copyright (C) 2005 Junio C Hamano
+ */
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <signal.h>
+#include "cache.h"
+#include "quote.h"
+#include "diff.h"
+#include "diffcore.h"
+#include "xdiff-interface.h"
+
+static int use_size_cache;
+
+int diff_rename_limit_default = -1;
+
+int git_diff_config(const char *var, const char *value)
+{
+       if (!strcmp(var, "diff.renamelimit")) {
+               diff_rename_limit_default = git_config_int(var, value);
+               return 0;
+       }
+
+       return git_default_config(var, value);
+}
+
+static char *quote_one(const char *str)
+{
+       int needlen;
+       char *xp;
+
+       if (!str)
+               return NULL;
+       needlen = quote_c_style(str, NULL, NULL, 0);
+       if (!needlen)
+               return strdup(str);
+       xp = xmalloc(needlen + 1);
+       quote_c_style(str, xp, NULL, 0);
+       return xp;
+}
+
+static char *quote_two(const char *one, const char *two)
+{
+       int need_one = quote_c_style(one, NULL, NULL, 1);
+       int need_two = quote_c_style(two, NULL, NULL, 1);
+       char *xp;
+
+       if (need_one + need_two) {
+               if (!need_one) need_one = strlen(one);
+               if (!need_two) need_one = strlen(two);
+
+               xp = xmalloc(need_one + need_two + 3);
+               xp[0] = '"';
+               quote_c_style(one, xp + 1, NULL, 1);
+               quote_c_style(two, xp + need_one + 1, NULL, 1);
+               strcpy(xp + need_one + need_two + 1, "\"");
+               return xp;
+       }
+       need_one = strlen(one);
+       need_two = strlen(two);
+       xp = xmalloc(need_one + need_two + 1);
+       strcpy(xp, one);
+       strcpy(xp + need_one, two);
+       return xp;
+}
+
+static const char *external_diff(void)
+{
+       static const char *external_diff_cmd = NULL;
+       static int done_preparing = 0;
+
+       if (done_preparing)
+               return external_diff_cmd;
+       external_diff_cmd = getenv("GIT_EXTERNAL_DIFF");
+       done_preparing = 1;
+       return external_diff_cmd;
+}
+
+#define TEMPFILE_PATH_LEN              50
+
+static struct diff_tempfile {
+       const char *name; /* filename external diff should read from */
+       char hex[41];
+       char mode[10];
+       char tmp_path[TEMPFILE_PATH_LEN];
+} diff_temp[2];
+
+static int count_lines(const char *data, int size)
+{
+       int count, ch, completely_empty = 1, nl_just_seen = 0;
+       count = 0;
+       while (0 < size--) {
+               ch = *data++;
+               if (ch == '\n') {
+                       count++;
+                       nl_just_seen = 1;
+                       completely_empty = 0;
+               }
+               else {
+                       nl_just_seen = 0;
+                       completely_empty = 0;
+               }
+       }
+       if (completely_empty)
+               return 0;
+       if (!nl_just_seen)
+               count++; /* no trailing newline */
+       return count;
+}
+
+static void print_line_count(int count)
+{
+       switch (count) {
+       case 0:
+               printf("0,0");
+               break;
+       case 1:
+               printf("1");
+               break;
+       default:
+               printf("1,%d", count);
+               break;
+       }
+}
+
+static void copy_file(int prefix, const char *data, int size)
+{
+       int ch, nl_just_seen = 1;
+       while (0 < size--) {
+               ch = *data++;
+               if (nl_just_seen)
+                       putchar(prefix);
+               putchar(ch);
+               if (ch == '\n')
+                       nl_just_seen = 1;
+               else
+                       nl_just_seen = 0;
+       }
+       if (!nl_just_seen)
+               printf("\n\\ No newline at end of file\n");
+}
+
+static void emit_rewrite_diff(const char *name_a,
+                             const char *name_b,
+                             struct diff_filespec *one,
+                             struct diff_filespec *two)
+{
+       int lc_a, lc_b;
+       diff_populate_filespec(one, 0);
+       diff_populate_filespec(two, 0);
+       lc_a = count_lines(one->data, one->size);
+       lc_b = count_lines(two->data, two->size);
+       printf("--- %s\n+++ %s\n@@ -", name_a, name_b);
+       print_line_count(lc_a);
+       printf(" +");
+       print_line_count(lc_b);
+       printf(" @@\n");
+       if (lc_a)
+               copy_file('-', one->data, one->size);
+       if (lc_b)
+               copy_file('+', two->data, two->size);
+}
+
+static int fill_mmfile(mmfile_t *mf, struct diff_filespec *one)
+{
+       if (!DIFF_FILE_VALID(one)) {
+               mf->ptr = ""; /* does not matter */
+               mf->size = 0;
+               return 0;
+       }
+       else if (diff_populate_filespec(one, 0))
+               return -1;
+       mf->ptr = one->data;
+       mf->size = one->size;
+       return 0;
+}
+
+struct emit_callback {
+       const char **label_path;
+};
+
+static int fn_out(void *priv, mmbuffer_t *mb, int nbuf)
+{
+       int i;
+       struct emit_callback *ecbdata = priv;
+
+       if (ecbdata->label_path[0]) {
+               printf("--- %s\n", ecbdata->label_path[0]);
+               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;
+}
+
+static char *pprint_rename(const char *a, const char *b)
+{
+       const char *old = a;
+       const char *new = b;
+       char *name = NULL;
+       int pfx_length, sfx_length;
+       int len_a = strlen(a);
+       int len_b = strlen(b);
+
+       /* Find common prefix */
+       pfx_length = 0;
+       while (*old && *new && *old == *new) {
+               if (*old == '/')
+                       pfx_length = old - a + 1;
+               old++;
+               new++;
+       }
+
+       /* Find common suffix */
+       old = a + len_a;
+       new = b + len_b;
+       sfx_length = 0;
+       while (a <= old && b <= new && *old == *new) {
+               if (*old == '/')
+                       sfx_length = len_a - (old - a);
+               old--;
+               new--;
+       }
+
+       /*
+        * pfx{mid-a => mid-b}sfx
+        * {pfx-a => pfx-b}sfx
+        * pfx{sfx-a => sfx-b}
+        * name-a => name-b
+        */
+       if (pfx_length + sfx_length) {
+               name = xmalloc(len_a + len_b - pfx_length - sfx_length + 7);
+               sprintf(name, "%.*s{%.*s => %.*s}%s",
+                       pfx_length, a,
+                       len_a - pfx_length - sfx_length, a + pfx_length,
+                       len_b - pfx_length - sfx_length, b + pfx_length,
+                       a + len_a - sfx_length);
+       }
+       else {
+               name = xmalloc(len_a + len_b + 5);
+               sprintf(name, "%s => %s", a, b);
+       }
+       return name;
+}
+
+struct diffstat_t {
+       struct xdiff_emit_state xm;
+
+       int nr;
+       int alloc;
+       struct diffstat_file {
+               char *name;
+               unsigned is_unmerged:1;
+               unsigned is_binary:1;
+               unsigned is_renamed:1;
+               unsigned int added, deleted;
+       } **files;
+};
+
+static struct diffstat_file *diffstat_add(struct diffstat_t *diffstat,
+                                         const char *name_a,
+                                         const char *name_b)
+{
+       struct diffstat_file *x;
+       x = xcalloc(sizeof (*x), 1);
+       if (diffstat->nr == diffstat->alloc) {
+               diffstat->alloc = alloc_nr(diffstat->alloc);
+               diffstat->files = xrealloc(diffstat->files,
+                               diffstat->alloc * sizeof(x));
+       }
+       diffstat->files[diffstat->nr++] = x;
+       if (name_b) {
+               x->name = pprint_rename(name_a, name_b);
+               x->is_renamed = 1;
+       }
+       else
+               x->name = strdup(name_a);
+       return x;
+}
+
+static void diffstat_consume(void *priv, char *line, unsigned long len)
+{
+       struct diffstat_t *diffstat = priv;
+       struct diffstat_file *x = diffstat->files[diffstat->nr - 1];
+
+       if (line[0] == '+')
+               x->added++;
+       else if (line[0] == '-')
+               x->deleted++;
+}
+
+static const char pluses[] = "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++";
+static const char minuses[]= "----------------------------------------------------------------------";
+
+static void show_stats(struct diffstat_t* data)
+{
+       char *prefix = "";
+       int i, len, add, del, total, adds = 0, dels = 0;
+       int max, max_change = 0, max_len = 0;
+       int total_files = data->nr;
+
+       if (data->nr == 0)
+               return;
+
+       for (i = 0; i < data->nr; i++) {
+               struct diffstat_file *file = data->files[i];
+
+               len = strlen(file->name);
+               if (max_len < len)
+                       max_len = len;
+
+               if (file->is_binary || file->is_unmerged)
+                       continue;
+               if (max_change < file->added + file->deleted)
+                       max_change = file->added + file->deleted;
+       }
+
+       for (i = 0; i < data->nr; i++) {
+               char *name = data->files[i]->name;
+               int added = data->files[i]->added;
+               int deleted = data->files[i]->deleted;
+
+               if (0 < (len = quote_c_style(name, NULL, NULL, 0))) {
+                       char *qname = xmalloc(len + 1);
+                       quote_c_style(name, qname, NULL, 0);
+                       free(name);
+                       data->files[i]->name = name = qname;
+               }
+
+               /*
+                * "scale" the filename
+                */
+               len = strlen(name);
+               max = max_len;
+               if (max > 50)
+                       max = 50;
+               if (len > max) {
+                       char *slash;
+                       prefix = "...";
+                       max -= 3;
+                       name += len - max;
+                       slash = strchr(name, '/');
+                       if (slash)
+                               name = slash;
+               }
+               len = max;
+
+               /*
+                * scale the add/delete
+                */
+               max = max_change;
+               if (max + len > 70)
+                       max = 70 - len;
+
+               if (data->files[i]->is_binary) {
+                       printf(" %s%-*s |  Bin\n", prefix, len, name);
+                       goto free_diffstat_file;
+               }
+               else if (data->files[i]->is_unmerged) {
+                       printf(" %s%-*s |  Unmerged\n", prefix, len, name);
+                       goto free_diffstat_file;
+               }
+               else if (!data->files[i]->is_renamed &&
+                        (added + deleted == 0)) {
+                       total_files--;
+                       goto free_diffstat_file;
+               }
+
+               add = added;
+               del = deleted;
+               total = add + del;
+               adds += add;
+               dels += del;
+
+               if (max_change > 0) {
+                       total = (total * max + max_change / 2) / max_change;
+                       add = (add * max + max_change / 2) / max_change;
+                       del = total - add;
+               }
+               printf(" %s%-*s |%5d %.*s%.*s\n", prefix,
+                               len, name, added + deleted,
+                               add, pluses, del, minuses);
+       free_diffstat_file:
+               free(data->files[i]->name);
+               free(data->files[i]);
+       }
+       free(data->files);
+       printf(" %d files changed, %d insertions(+), %d deletions(-)\n",
+                       total_files, adds, dels);
+}
+
+#define FIRST_FEW_BYTES 8000
+static int mmfile_is_binary(mmfile_t *mf)
+{
+       long sz = mf->size;
+       if (FIRST_FEW_BYTES < sz)
+               sz = FIRST_FEW_BYTES;
+       if (memchr(mf->ptr, 0, sz))
+               return 1;
+       return 0;
+}
+
+static void builtin_diff(const char *name_a,
+                        const char *name_b,
+                        struct diff_filespec *one,
+                        struct diff_filespec *two,
+                        const char *xfrm_msg,
+                        int complete_rewrite)
+{
+       mmfile_t mf1, mf2;
+       const char *lbl[2];
+       char *a_one, *b_two;
+
+       a_one = quote_two("a/", 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";
+       printf("diff --git %s %s\n", a_one, b_two);
+       if (lbl[0][0] == '/') {
+               /* /dev/null */
+               printf("new file mode %06o\n", two->mode);
+               if (xfrm_msg && xfrm_msg[0])
+                       puts(xfrm_msg);
+       }
+       else if (lbl[1][0] == '/') {
+               printf("deleted file mode %06o\n", one->mode);
+               if (xfrm_msg && xfrm_msg[0])
+                       puts(xfrm_msg);
+       }
+       else {
+               if (one->mode != two->mode) {
+                       printf("old mode %06o\n", one->mode);
+                       printf("new mode %06o\n", two->mode);
+               }
+               if (xfrm_msg && xfrm_msg[0])
+                       puts(xfrm_msg);
+               /*
+                * we do not run diff between different kind
+                * of objects.
+                */
+               if ((one->mode ^ two->mode) & S_IFMT)
+                       goto free_ab_and_return;
+               if (complete_rewrite) {
+                       emit_rewrite_diff(name_a, name_b, one, two);
+                       goto free_ab_and_return;
+               }
+       }
+
+       if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0)
+               die("unable to read files to diff");
+
+       if (mmfile_is_binary(&mf1) || mmfile_is_binary(&mf2))
+               printf("Binary files %s and %s differ\n", lbl[0], lbl[1]);
+       else {
+               /* Crazy xdl interfaces.. */
+               const char *diffopts = getenv("GIT_DIFF_OPTS");
+               xpparam_t xpp;
+               xdemitconf_t xecfg;
+               xdemitcb_t ecb;
+               struct emit_callback ecbdata;
+
+               ecbdata.label_path = lbl;
+               xpp.flags = XDF_NEED_MINIMAL;
+               xecfg.ctxlen = 3;
+               xecfg.flags = XDL_EMIT_FUNCNAMES;
+               if (!diffopts)
+                       ;
+               else if (!strncmp(diffopts, "--unified=", 10))
+                       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.priv = &ecbdata;
+               xdl_diff(&mf1, &mf2, &xpp, &xecfg, &ecb);
+       }
+
+ free_ab_and_return:
+       free(a_one);
+       free(b_two);
+       return;
+}
+
+static void builtin_diffstat(const char *name_a, const char *name_b,
+                            struct diff_filespec *one,
+                            struct diff_filespec *two,
+                            struct diffstat_t *diffstat)
+{
+       mmfile_t mf1, mf2;
+       struct diffstat_file *data;
+
+       data = diffstat_add(diffstat, name_a, name_b);
+
+       if (!one || !two) {
+               data->is_unmerged = 1;
+               return;
+       }
+
+       if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0)
+               die("unable to read files to diff");
+
+       if (mmfile_is_binary(&mf1) || mmfile_is_binary(&mf2))
+               data->is_binary = 1;
+       else {
+               /* Crazy xdl interfaces.. */
+               xpparam_t xpp;
+               xdemitconf_t xecfg;
+               xdemitcb_t ecb;
+
+               xpp.flags = XDF_NEED_MINIMAL;
+               xecfg.ctxlen = 0;
+               xecfg.flags = 0;
+               ecb.outf = xdiff_outf;
+               ecb.priv = diffstat;
+               xdl_diff(&mf1, &mf2, &xpp, &xecfg, &ecb);
+       }
+}
+
+struct diff_filespec *alloc_filespec(const char *path)
+{
+       int namelen = strlen(path);
+       struct diff_filespec *spec = xmalloc(sizeof(*spec) + namelen + 1);
+
+       memset(spec, 0, sizeof(*spec));
+       spec->path = (char *)(spec + 1);
+       memcpy(spec->path, path, namelen+1);
+       return spec;
+}
+
+void fill_filespec(struct diff_filespec *spec, const unsigned char *sha1,
+                  unsigned short mode)
+{
+       if (mode) {
+               spec->mode = canon_mode(mode);
+               memcpy(spec->sha1, sha1, 20);
+               spec->sha1_valid = !!memcmp(sha1, null_sha1, 20);
+       }
+}
+
+/*
+ * Given a name and sha1 pair, if the dircache tells us the file in
+ * the work tree has that object contents, return true, so that
+ * prepare_temp_file() does not have to inflate and extract.
+ */
+static int work_tree_matches(const char *name, const unsigned char *sha1)
+{
+       struct cache_entry *ce;
+       struct stat st;
+       int pos, len;
+
+       /* We do not read the cache ourselves here, because the
+        * benchmark with my previous version that always reads cache
+        * shows that it makes things worse for diff-tree comparing
+        * two linux-2.6 kernel trees in an already checked out work
+        * tree.  This is because most diff-tree comparisons deal with
+        * only a small number of files, while reading the cache is
+        * expensive for a large project, and its cost outweighs the
+        * savings we get by not inflating the object to a temporary
+        * file.  Practically, this code only helps when we are used
+        * by diff-cache --cached, which does read the cache before
+        * calling us.
+        */
+       if (!active_cache)
+               return 0;
+
+       len = strlen(name);
+       pos = cache_name_pos(name, len);
+       if (pos < 0)
+               return 0;
+       ce = active_cache[pos];
+       if ((lstat(name, &st) < 0) ||
+           !S_ISREG(st.st_mode) || /* careful! */
+           ce_match_stat(ce, &st, 0) ||
+           memcmp(sha1, ce->sha1, 20))
+               return 0;
+       /* we return 1 only when we can stat, it is a regular file,
+        * stat information matches, and sha1 recorded in the cache
+        * matches.  I.e. we know the file in the work tree really is
+        * the same as the <name, sha1> pair.
+        */
+       return 1;
+}
+
+static struct sha1_size_cache {
+       unsigned char sha1[20];
+       unsigned long size;
+} **sha1_size_cache;
+static int sha1_size_cache_nr, sha1_size_cache_alloc;
+
+static struct sha1_size_cache *locate_size_cache(unsigned char *sha1,
+                                                int find_only,
+                                                unsigned long size)
+{
+       int first, last;
+       struct sha1_size_cache *e;
+
+       first = 0;
+       last = sha1_size_cache_nr;
+       while (last > first) {
+               int cmp, next = (last + first) >> 1;
+               e = sha1_size_cache[next];
+               cmp = memcmp(e->sha1, sha1, 20);
+               if (!cmp)
+                       return e;
+               if (cmp < 0) {
+                       last = next;
+                       continue;
+               }
+               first = next+1;
+       }
+       /* not found */
+       if (find_only)
+               return NULL;
+       /* insert to make it at "first" */
+       if (sha1_size_cache_alloc <= sha1_size_cache_nr) {
+               sha1_size_cache_alloc = alloc_nr(sha1_size_cache_alloc);
+               sha1_size_cache = xrealloc(sha1_size_cache,
+                                          sha1_size_cache_alloc *
+                                          sizeof(*sha1_size_cache));
+       }
+       sha1_size_cache_nr++;
+       if (first < sha1_size_cache_nr)
+               memmove(sha1_size_cache + first + 1, sha1_size_cache + first,
+                       (sha1_size_cache_nr - first - 1) *
+                       sizeof(*sha1_size_cache));
+       e = xmalloc(sizeof(struct sha1_size_cache));
+       sha1_size_cache[first] = e;
+       memcpy(e->sha1, sha1, 20);
+       e->size = size;
+       return e;
+}
+
+/*
+ * While doing rename detection and pickaxe operation, we may need to
+ * grab the data for the blob (or file) for our own in-core comparison.
+ * diff_filespec has data and size fields for this purpose.
+ */
+int diff_populate_filespec(struct diff_filespec *s, int size_only)
+{
+       int err = 0;
+       if (!DIFF_FILE_VALID(s))
+               die("internal error: asking to populate invalid file.");
+       if (S_ISDIR(s->mode))
+               return -1;
+
+       if (!use_size_cache)
+               size_only = 0;
+
+       if (s->data)
+               return err;
+       if (!s->sha1_valid ||
+           work_tree_matches(s->path, s->sha1)) {
+               struct stat st;
+               int fd;
+               if (lstat(s->path, &st) < 0) {
+                       if (errno == ENOENT) {
+                       err_empty:
+                               err = -1;
+                       empty:
+                               s->data = "";
+                               s->size = 0;
+                               return err;
+                       }
+               }
+               s->size = st.st_size;
+               if (!s->size)
+                       goto empty;
+               if (size_only)
+                       return 0;
+               if (S_ISLNK(st.st_mode)) {
+                       int ret;
+                       s->data = xmalloc(s->size);
+                       s->should_free = 1;
+                       ret = readlink(s->path, s->data, s->size);
+                       if (ret < 0) {
+                               free(s->data);
+                               goto err_empty;
+                       }
+                       return 0;
+               }
+               fd = open(s->path, O_RDONLY);
+               if (fd < 0)
+                       goto err_empty;
+               s->data = mmap(NULL, s->size, PROT_READ, MAP_PRIVATE, fd, 0);
+               close(fd);
+               if (s->data == MAP_FAILED)
+                       goto err_empty;
+               s->should_munmap = 1;
+       }
+       else {
+               char type[20];
+               struct sha1_size_cache *e;
+
+               if (size_only) {
+                       e = locate_size_cache(s->sha1, 1, 0);
+                       if (e) {
+                               s->size = e->size;
+                               return 0;
+                       }
+                       if (!sha1_object_info(s->sha1, type, &s->size))
+                               locate_size_cache(s->sha1, 0, s->size);
+               }
+               else {
+                       s->data = read_sha1_file(s->sha1, type, &s->size);
+                       s->should_free = 1;
+               }
+       }
+       return 0;
+}
+
+void diff_free_filespec_data(struct diff_filespec *s)
+{
+       if (s->should_free)
+               free(s->data);
+       else if (s->should_munmap)
+               munmap(s->data, s->size);
+       s->should_free = s->should_munmap = 0;
+       s->data = NULL;
+       free(s->cnt_data);
+       s->cnt_data = NULL;
+}
+
+static void prep_temp_blob(struct diff_tempfile *temp,
+                          void *blob,
+                          unsigned long size,
+                          const unsigned char *sha1,
+                          int mode)
+{
+       int fd;
+
+       fd = git_mkstemp(temp->tmp_path, TEMPFILE_PATH_LEN, ".diff_XXXXXX");
+       if (fd < 0)
+               die("unable to create temp-file");
+       if (write(fd, blob, size) != size)
+               die("unable to write temp-file");
+       close(fd);
+       temp->name = temp->tmp_path;
+       strcpy(temp->hex, sha1_to_hex(sha1));
+       temp->hex[40] = 0;
+       sprintf(temp->mode, "%06o", mode);
+}
+
+static void prepare_temp_file(const char *name,
+                             struct diff_tempfile *temp,
+                             struct diff_filespec *one)
+{
+       if (!DIFF_FILE_VALID(one)) {
+       not_a_valid_file:
+               /* A '-' entry produces this for file-2, and
+                * a '+' entry produces this for file-1.
+                */
+               temp->name = "/dev/null";
+               strcpy(temp->hex, ".");
+               strcpy(temp->mode, ".");
+               return;
+       }
+
+       if (!one->sha1_valid ||
+           work_tree_matches(name, one->sha1)) {
+               struct stat st;
+               if (lstat(name, &st) < 0) {
+                       if (errno == ENOENT)
+                               goto not_a_valid_file;
+                       die("stat(%s): %s", name, strerror(errno));
+               }
+               if (S_ISLNK(st.st_mode)) {
+                       int ret;
+                       char buf[PATH_MAX + 1]; /* ought to be SYMLINK_MAX */
+                       if (sizeof(buf) <= st.st_size)
+                               die("symlink too long: %s", name);
+                       ret = readlink(name, buf, st.st_size);
+                       if (ret < 0)
+                               die("readlink(%s)", name);
+                       prep_temp_blob(temp, buf, st.st_size,
+                                      (one->sha1_valid ?
+                                       one->sha1 : null_sha1),
+                                      (one->sha1_valid ?
+                                       one->mode : S_IFLNK));
+               }
+               else {
+                       /* we can borrow from the file in the work tree */
+                       temp->name = name;
+                       if (!one->sha1_valid)
+                               strcpy(temp->hex, sha1_to_hex(null_sha1));
+                       else
+                               strcpy(temp->hex, sha1_to_hex(one->sha1));
+                       /* Even though we may sometimes borrow the
+                        * contents from the work tree, we always want
+                        * one->mode.  mode is trustworthy even when
+                        * !(one->sha1_valid), as long as
+                        * DIFF_FILE_VALID(one).
+                        */
+                       sprintf(temp->mode, "%06o", one->mode);
+               }
+               return;
+       }
+       else {
+               if (diff_populate_filespec(one, 0))
+                       die("cannot read data blob for %s", one->path);
+               prep_temp_blob(temp, one->data, one->size,
+                              one->sha1, one->mode);
+       }
+}
+
+static void remove_tempfile(void)
+{
+       int i;
+
+       for (i = 0; i < 2; i++)
+               if (diff_temp[i].name == diff_temp[i].tmp_path) {
+                       unlink(diff_temp[i].name);
+                       diff_temp[i].name = NULL;
+               }
+}
+
+static void remove_tempfile_on_signal(int signo)
+{
+       remove_tempfile();
+       signal(SIGINT, SIG_DFL);
+       raise(signo);
+}
+
+static int spawn_prog(const char *pgm, const char **arg)
+{
+       pid_t pid;
+       int status;
+
+       fflush(NULL);
+       pid = fork();
+       if (pid < 0)
+               die("unable to fork");
+       if (!pid) {
+               execvp(pgm, (char *const*) arg);
+               exit(255);
+       }
+
+       while (waitpid(pid, &status, 0) < 0) {
+               if (errno == EINTR)
+                       continue;
+               return -1;
+       }
+
+       /* Earlier we did not check the exit status because
+        * diff exits non-zero if files are different, and
+        * we are not interested in knowing that.  It was a
+        * mistake which made it harder to quit a diff-*
+        * session that uses the git-apply-patch-script as
+        * the GIT_EXTERNAL_DIFF.  A custom GIT_EXTERNAL_DIFF
+        * should also exit non-zero only when it wants to
+        * abort the entire diff-* session.
+        */
+       if (WIFEXITED(status) && !WEXITSTATUS(status))
+               return 0;
+       return -1;
+}
+
+/* An external diff command takes:
+ *
+ * diff-cmd name infile1 infile1-sha1 infile1-mode \
+ *               infile2 infile2-sha1 infile2-mode [ rename-to ]
+ *
+ */
+static void run_external_diff(const char *pgm,
+                             const char *name,
+                             const char *other,
+                             struct diff_filespec *one,
+                             struct diff_filespec *two,
+                             const char *xfrm_msg,
+                             int complete_rewrite)
+{
+       const char *spawn_arg[10];
+       struct diff_tempfile *temp = diff_temp;
+       int retval;
+       static int atexit_asked = 0;
+       const char *othername;
+       const char **arg = &spawn_arg[0];
+
+       othername = (other? other : name);
+       if (one && two) {
+               prepare_temp_file(name, &temp[0], one);
+               prepare_temp_file(othername, &temp[1], two);
+               if (! atexit_asked &&
+                   (temp[0].name == temp[0].tmp_path ||
+                    temp[1].name == temp[1].tmp_path)) {
+                       atexit_asked = 1;
+                       atexit(remove_tempfile);
+               }
+               signal(SIGINT, remove_tempfile_on_signal);
+       }
+
+       if (one && two) {
+               *arg++ = pgm;
+               *arg++ = name;
+               *arg++ = temp[0].name;
+               *arg++ = temp[0].hex;
+               *arg++ = temp[0].mode;
+               *arg++ = temp[1].name;
+               *arg++ = temp[1].hex;
+               *arg++ = temp[1].mode;
+               if (other) {
+                       *arg++ = other;
+                       *arg++ = xfrm_msg;
+               }
+       } else {
+               *arg++ = pgm;
+               *arg++ = name;
+       }
+       *arg = NULL;
+       retval = spawn_prog(pgm, spawn_arg);
+       remove_tempfile();
+       if (retval) {
+               fprintf(stderr, "external diff died, stopping at %s.\n", name);
+               exit(1);
+       }
+}
+
+static void run_diff_cmd(const char *pgm,
+                        const char *name,
+                        const char *other,
+                        struct diff_filespec *one,
+                        struct diff_filespec *two,
+                        const char *xfrm_msg,
+                        int complete_rewrite)
+{
+       if (pgm) {
+               run_external_diff(pgm, name, other, one, two, xfrm_msg,
+                                 complete_rewrite);
+               return;
+       }
+       if (one && two)
+               builtin_diff(name, other ? other : name,
+                            one, two, xfrm_msg, complete_rewrite);
+       else
+               printf("* Unmerged path %s\n", name);
+}
+
+static void diff_fill_sha1_info(struct diff_filespec *one)
+{
+       if (DIFF_FILE_VALID(one)) {
+               if (!one->sha1_valid) {
+                       struct stat st;
+                       if (lstat(one->path, &st) < 0)
+                               die("stat %s", one->path);
+                       if (index_path(one->sha1, one->path, &st, 0))
+                               die("cannot hash %s\n", one->path);
+               }
+       }
+       else
+               memset(one->sha1, 0, 20);
+}
+
+static void run_diff(struct diff_filepair *p, struct diff_options *o)
+{
+       const char *pgm = external_diff();
+       char msg[PATH_MAX*2+300], *xfrm_msg;
+       struct diff_filespec *one;
+       struct diff_filespec *two;
+       const char *name;
+       const char *other;
+       char *name_munged, *other_munged;
+       int complete_rewrite = 0;
+       int len;
+
+       if (DIFF_PAIR_UNMERGED(p)) {
+               /* unmerged */
+               run_diff_cmd(pgm, p->one->path, NULL, NULL, NULL, NULL, 0);
+               return;
+       }
+
+       name = p->one->path;
+       other = (strcmp(name, p->two->path) ? p->two->path : NULL);
+       name_munged = quote_one(name);
+       other_munged = quote_one(other);
+       one = p->one; two = p->two;
+
+       diff_fill_sha1_info(one);
+       diff_fill_sha1_info(two);
+
+       len = 0;
+       switch (p->status) {
+       case DIFF_STATUS_COPIED:
+               len += snprintf(msg + len, sizeof(msg) - len,
+                               "similarity index %d%%\n"
+                               "copy from %s\n"
+                               "copy to %s\n",
+                               (int)(0.5 + p->score * 100.0/MAX_SCORE),
+                               name_munged, other_munged);
+               break;
+       case DIFF_STATUS_RENAMED:
+               len += snprintf(msg + len, sizeof(msg) - len,
+                               "similarity index %d%%\n"
+                               "rename from %s\n"
+                               "rename to %s\n",
+                               (int)(0.5 + p->score * 100.0/MAX_SCORE),
+                               name_munged, other_munged);
+               break;
+       case DIFF_STATUS_MODIFIED:
+               if (p->score) {
+                       len += snprintf(msg + len, sizeof(msg) - len,
+                                       "dissimilarity index %d%%\n",
+                                       (int)(0.5 + p->score *
+                                             100.0/MAX_SCORE));
+                       complete_rewrite = 1;
+                       break;
+               }
+               /* fallthru */
+       default:
+               /* nothing */
+               ;
+       }
+
+       if (memcmp(one->sha1, two->sha1, 20)) {
+               char one_sha1[41];
+               int abbrev = o->full_index ? 40 : DEFAULT_ABBREV;
+               memcpy(one_sha1, sha1_to_hex(one->sha1), 41);
+
+               len += snprintf(msg + len, sizeof(msg) - len,
+                               "index %.*s..%.*s",
+                               abbrev, one_sha1, abbrev,
+                               sha1_to_hex(two->sha1));
+               if (one->mode == two->mode)
+                       len += snprintf(msg + len, sizeof(msg) - len,
+                                       " %06o", one->mode);
+               len += snprintf(msg + len, sizeof(msg) - len, "\n");
+       }
+
+       if (len)
+               msg[--len] = 0;
+       xfrm_msg = len ? msg : NULL;
+
+       if (!pgm &&
+           DIFF_FILE_VALID(one) && DIFF_FILE_VALID(two) &&
+           (S_IFMT & one->mode) != (S_IFMT & two->mode)) {
+               /* a filepair that changes between file and symlink
+                * needs to be split into deletion and creation.
+                */
+               struct diff_filespec *null = alloc_filespec(two->path);
+               run_diff_cmd(NULL, name, other, one, null, xfrm_msg, 0);
+               free(null);
+               null = alloc_filespec(one->path);
+               run_diff_cmd(NULL, name, other, null, two, xfrm_msg, 0);
+               free(null);
+       }
+       else
+               run_diff_cmd(pgm, name, other, one, two, xfrm_msg,
+                            complete_rewrite);
+
+       free(name_munged);
+       free(other_munged);
+}
+
+static void run_diffstat(struct diff_filepair *p, struct diff_options *o,
+                        struct diffstat_t *diffstat)
+{
+       const char *name;
+       const char *other;
+
+       if (DIFF_PAIR_UNMERGED(p)) {
+               /* unmerged */
+               builtin_diffstat(p->one->path, NULL, NULL, NULL, diffstat);
+               return;
+       }
+
+       name = p->one->path;
+       other = (strcmp(name, p->two->path) ? p->two->path : NULL);
+
+       diff_fill_sha1_info(p->one);
+       diff_fill_sha1_info(p->two);
+
+       builtin_diffstat(name, other, p->one, p->two, diffstat);
+}
+
+void diff_setup(struct diff_options *options)
+{
+       memset(options, 0, sizeof(*options));
+       options->output_format = DIFF_FORMAT_RAW;
+       options->line_termination = '\n';
+       options->break_opt = -1;
+       options->rename_limit = -1;
+
+       options->change = diff_change;
+       options->add_remove = diff_addremove;
+}
+
+int diff_setup_done(struct diff_options *options)
+{
+       if ((options->find_copies_harder &&
+            options->detect_rename != DIFF_DETECT_COPY) ||
+           (0 <= options->rename_limit && !options->detect_rename))
+               return -1;
+
+       /*
+        * These cases always need recursive; we do not drop caller-supplied
+        * recursive bits for other formats here.
+        */
+       if ((options->output_format == DIFF_FORMAT_PATCH) ||
+           (options->output_format == DIFF_FORMAT_DIFFSTAT))
+               options->recursive = 1;
+
+       if (options->detect_rename && options->rename_limit < 0)
+               options->rename_limit = diff_rename_limit_default;
+       if (options->setup & DIFF_SETUP_USE_CACHE) {
+               if (!active_cache)
+                       /* read-cache does not die even when it fails
+                        * so it is safe for us to do this here.  Also
+                        * it does not smudge active_cache or active_nr
+                        * when it fails, so we do not have to worry about
+                        * cleaning it up ourselves either.
+                        */
+                       read_cache();
+       }
+       if (options->setup & DIFF_SETUP_USE_SIZE_CACHE)
+               use_size_cache = 1;
+       if (options->abbrev <= 0 || 40 < options->abbrev)
+               options->abbrev = 40; /* full */
+
+       return 0;
+}
+
+int diff_opt_parse(struct diff_options *options, const char **av, int ac)
+{
+       const char *arg = av[0];
+       if (!strcmp(arg, "-p") || !strcmp(arg, "-u"))
+               options->output_format = DIFF_FORMAT_PATCH;
+       else if (!strcmp(arg, "--patch-with-raw")) {
+               options->output_format = DIFF_FORMAT_PATCH;
+               options->with_raw = 1;
+       }
+       else if (!strcmp(arg, "--stat"))
+               options->output_format = DIFF_FORMAT_DIFFSTAT;
+       else if (!strcmp(arg, "--patch-with-stat")) {
+               options->output_format = DIFF_FORMAT_PATCH;
+               options->with_stat = 1;
+       }
+       else if (!strcmp(arg, "-z"))
+               options->line_termination = 0;
+       else if (!strncmp(arg, "-l", 2))
+               options->rename_limit = strtoul(arg+2, NULL, 10);
+       else if (!strcmp(arg, "--full-index"))
+               options->full_index = 1;
+       else if (!strcmp(arg, "--name-only"))
+               options->output_format = DIFF_FORMAT_NAME;
+       else if (!strcmp(arg, "--name-status"))
+               options->output_format = DIFF_FORMAT_NAME_STATUS;
+       else if (!strcmp(arg, "-R"))
+               options->reverse_diff = 1;
+       else if (!strncmp(arg, "-S", 2))
+               options->pickaxe = arg + 2;
+       else if (!strcmp(arg, "-s"))
+               options->output_format = DIFF_FORMAT_NO_OUTPUT;
+       else if (!strncmp(arg, "-O", 2))
+               options->orderfile = arg + 2;
+       else if (!strncmp(arg, "--diff-filter=", 14))
+               options->filter = arg + 14;
+       else if (!strcmp(arg, "--pickaxe-all"))
+               options->pickaxe_opts = DIFF_PICKAXE_ALL;
+       else if (!strcmp(arg, "--pickaxe-regex"))
+               options->pickaxe_opts = DIFF_PICKAXE_REGEX;
+       else if (!strncmp(arg, "-B", 2)) {
+               if ((options->break_opt =
+                    diff_scoreopt_parse(arg)) == -1)
+                       return -1;
+       }
+       else if (!strncmp(arg, "-M", 2)) {
+               if ((options->rename_score =
+                    diff_scoreopt_parse(arg)) == -1)
+                       return -1;
+               options->detect_rename = DIFF_DETECT_RENAME;
+       }
+       else if (!strncmp(arg, "-C", 2)) {
+               if ((options->rename_score =
+                    diff_scoreopt_parse(arg)) == -1)
+                       return -1;
+               options->detect_rename = DIFF_DETECT_COPY;
+       }
+       else if (!strcmp(arg, "--find-copies-harder"))
+               options->find_copies_harder = 1;
+       else if (!strcmp(arg, "--abbrev"))
+               options->abbrev = DEFAULT_ABBREV;
+       else if (!strncmp(arg, "--abbrev=", 9)) {
+               options->abbrev = strtoul(arg + 9, NULL, 10);
+               if (options->abbrev < MINIMUM_ABBREV)
+                       options->abbrev = MINIMUM_ABBREV;
+               else if (40 < options->abbrev)
+                       options->abbrev = 40;
+       }
+       else
+               return 0;
+       return 1;
+}
+
+static int parse_num(const char **cp_p)
+{
+       unsigned long num, scale;
+       int ch, dot;
+       const char *cp = *cp_p;
+
+       num = 0;
+       scale = 1;
+       dot = 0;
+       for(;;) {
+               ch = *cp;
+               if ( !dot && ch == '.' ) {
+                       scale = 1;
+                       dot = 1;
+               } else if ( ch == '%' ) {
+                       scale = dot ? scale*100 : 100;
+                       cp++;   /* % is always at the end */
+                       break;
+               } else if ( ch >= '0' && ch <= '9' ) {
+                       if ( scale < 100000 ) {
+                               scale *= 10;
+                               num = (num*10) + (ch-'0');
+                       }
+               } else {
+                       break;
+               }
+               cp++;
+       }
+       *cp_p = cp;
+
+       /* user says num divided by scale and we say internally that
+        * is MAX_SCORE * num / scale.
+        */
+       return (num >= scale) ? MAX_SCORE : (MAX_SCORE * num / scale);
+}
+
+int diff_scoreopt_parse(const char *opt)
+{
+       int opt1, opt2, cmd;
+
+       if (*opt++ != '-')
+               return -1;
+       cmd = *opt++;
+       if (cmd != 'M' && cmd != 'C' && cmd != 'B')
+               return -1; /* that is not a -M, -C nor -B option */
+
+       opt1 = parse_num(&opt);
+       if (cmd != 'B')
+               opt2 = 0;
+       else {
+               if (*opt == 0)
+                       opt2 = 0;
+               else if (*opt != '/')
+                       return -1; /* we expect -B80/99 or -B80 */
+               else {
+                       opt++;
+                       opt2 = parse_num(&opt);
+               }
+       }
+       if (*opt != 0)
+               return -1;
+       return opt1 | (opt2 << 16);
+}
+
+struct diff_queue_struct diff_queued_diff;
+
+void diff_q(struct diff_queue_struct *queue, struct diff_filepair *dp)
+{
+       if (queue->alloc <= queue->nr) {
+               queue->alloc = alloc_nr(queue->alloc);
+               queue->queue = xrealloc(queue->queue,
+                                       sizeof(dp) * queue->alloc);
+       }
+       queue->queue[queue->nr++] = dp;
+}
+
+struct diff_filepair *diff_queue(struct diff_queue_struct *queue,
+                                struct diff_filespec *one,
+                                struct diff_filespec *two)
+{
+       struct diff_filepair *dp = xmalloc(sizeof(*dp));
+       dp->one = one;
+       dp->two = two;
+       dp->score = 0;
+       dp->status = 0;
+       dp->source_stays = 0;
+       dp->broken_pair = 0;
+       if (queue)
+               diff_q(queue, dp);
+       return dp;
+}
+
+void diff_free_filepair(struct diff_filepair *p)
+{
+       diff_free_filespec_data(p->one);
+       diff_free_filespec_data(p->two);
+       free(p->one);
+       free(p->two);
+       free(p);
+}
+
+/* This is different from find_unique_abbrev() in that
+ * it stuffs the result with dots for alignment.
+ */
+const char *diff_unique_abbrev(const unsigned char *sha1, int len)
+{
+       int abblen;
+       const char *abbrev;
+       if (len == 40)
+               return sha1_to_hex(sha1);
+
+       abbrev = find_unique_abbrev(sha1, len);
+       if (!abbrev)
+               return sha1_to_hex(sha1);
+       abblen = strlen(abbrev);
+       if (abblen < 37) {
+               static char hex[41];
+               if (len < abblen && abblen <= len + 2)
+                       sprintf(hex, "%s%.*s", abbrev, len+3-abblen, "..");
+               else
+                       sprintf(hex, "%s...", abbrev);
+               return hex;
+       }
+       return sha1_to_hex(sha1);
+}
+
+static void diff_flush_raw(struct diff_filepair *p,
+                          int line_termination,
+                          int inter_name_termination,
+                          struct diff_options *options,
+                          int output_format)
+{
+       int two_paths;
+       char status[10];
+       int abbrev = options->abbrev;
+       const char *path_one, *path_two;
+
+       path_one = p->one->path;
+       path_two = p->two->path;
+       if (line_termination) {
+               path_one = quote_one(path_one);
+               path_two = quote_one(path_two);
+       }
+
+       if (p->score)
+               sprintf(status, "%c%03d", p->status,
+                       (int)(0.5 + p->score * 100.0/MAX_SCORE));
+       else {
+               status[0] = p->status;
+               status[1] = 0;
+       }
+       switch (p->status) {
+       case DIFF_STATUS_COPIED:
+       case DIFF_STATUS_RENAMED:
+               two_paths = 1;
+               break;
+       case DIFF_STATUS_ADDED:
+       case DIFF_STATUS_DELETED:
+               two_paths = 0;
+               break;
+       default:
+               two_paths = 0;
+               break;
+       }
+       if (output_format != DIFF_FORMAT_NAME_STATUS) {
+               printf(":%06o %06o %s ",
+                      p->one->mode, p->two->mode,
+                      diff_unique_abbrev(p->one->sha1, abbrev));
+               printf("%s ",
+                      diff_unique_abbrev(p->two->sha1, abbrev));
+       }
+       printf("%s%c%s", status, inter_name_termination, path_one);
+       if (two_paths)
+               printf("%c%s", inter_name_termination, path_two);
+       putchar(line_termination);
+       if (path_one != p->one->path)
+               free((void*)path_one);
+       if (path_two != p->two->path)
+               free((void*)path_two);
+}
+
+static void diff_flush_name(struct diff_filepair *p,
+                           int inter_name_termination,
+                           int line_termination)
+{
+       char *path = p->two->path;
+
+       if (line_termination)
+               path = quote_one(p->two->path);
+       else
+               path = p->two->path;
+       printf("%s%c", path, line_termination);
+       if (p->two->path != path)
+               free(path);
+}
+
+int diff_unmodified_pair(struct diff_filepair *p)
+{
+       /* This function is written stricter than necessary to support
+        * the currently implemented transformers, but the idea is to
+        * let transformers to produce diff_filepairs any way they want,
+        * and filter and clean them up here before producing the output.
+        */
+       struct diff_filespec *one, *two;
+
+       if (DIFF_PAIR_UNMERGED(p))
+               return 0; /* unmerged is interesting */
+
+       one = p->one;
+       two = p->two;
+
+       /* deletion, addition, mode or type change
+        * and rename are all interesting.
+        */
+       if (DIFF_FILE_VALID(one) != DIFF_FILE_VALID(two) ||
+           DIFF_PAIR_MODE_CHANGED(p) ||
+           strcmp(one->path, two->path))
+               return 0;
+
+       /* both are valid and point at the same path.  that is, we are
+        * dealing with a change.
+        */
+       if (one->sha1_valid && two->sha1_valid &&
+           !memcmp(one->sha1, two->sha1, sizeof(one->sha1)))
+               return 1; /* no change */
+       if (!one->sha1_valid && !two->sha1_valid)
+               return 1; /* both look at the same file on the filesystem. */
+       return 0;
+}
+
+static void diff_flush_patch(struct diff_filepair *p, struct diff_options *o)
+{
+       if (diff_unmodified_pair(p))
+               return;
+
+       if ((DIFF_FILE_VALID(p->one) && S_ISDIR(p->one->mode)) ||
+           (DIFF_FILE_VALID(p->two) && S_ISDIR(p->two->mode)))
+               return; /* no tree diffs in patch format */
+
+       run_diff(p, o);
+}
+
+static void diff_flush_stat(struct diff_filepair *p, struct diff_options *o,
+                           struct diffstat_t *diffstat)
+{
+       if (diff_unmodified_pair(p))
+               return;
+
+       if ((DIFF_FILE_VALID(p->one) && S_ISDIR(p->one->mode)) ||
+           (DIFF_FILE_VALID(p->two) && S_ISDIR(p->two->mode)))
+               return; /* no tree diffs in patch format */
+
+       run_diffstat(p, o, diffstat);
+}
+
+int diff_queue_is_empty(void)
+{
+       struct diff_queue_struct *q = &diff_queued_diff;
+       int i;
+       for (i = 0; i < q->nr; i++)
+               if (!diff_unmodified_pair(q->queue[i]))
+                       return 0;
+       return 1;
+}
+
+#if DIFF_DEBUG
+void diff_debug_filespec(struct diff_filespec *s, int x, const char *one)
+{
+       fprintf(stderr, "queue[%d] %s (%s) %s %06o %s\n",
+               x, one ? one : "",
+               s->path,
+               DIFF_FILE_VALID(s) ? "valid" : "invalid",
+               s->mode,
+               s->sha1_valid ? sha1_to_hex(s->sha1) : "");
+       fprintf(stderr, "queue[%d] %s size %lu flags %d\n",
+               x, one ? one : "",
+               s->size, s->xfrm_flags);
+}
+
+void diff_debug_filepair(const struct diff_filepair *p, int i)
+{
+       diff_debug_filespec(p->one, i, "one");
+       diff_debug_filespec(p->two, i, "two");
+       fprintf(stderr, "score %d, status %c stays %d broken %d\n",
+               p->score, p->status ? p->status : '?',
+               p->source_stays, p->broken_pair);
+}
+
+void diff_debug_queue(const char *msg, struct diff_queue_struct *q)
+{
+       int i;
+       if (msg)
+               fprintf(stderr, "%s\n", msg);
+       fprintf(stderr, "q->nr = %d\n", q->nr);
+       for (i = 0; i < q->nr; i++) {
+               struct diff_filepair *p = q->queue[i];
+               diff_debug_filepair(p, i);
+       }
+}
+#endif
+
+static void diff_resolve_rename_copy(void)
+{
+       int i, j;
+       struct diff_filepair *p, *pp;
+       struct diff_queue_struct *q = &diff_queued_diff;
+
+       diff_debug_queue("resolve-rename-copy", q);
+
+       for (i = 0; i < q->nr; i++) {
+               p = q->queue[i];
+               p->status = 0; /* undecided */
+               if (DIFF_PAIR_UNMERGED(p))
+                       p->status = DIFF_STATUS_UNMERGED;
+               else if (!DIFF_FILE_VALID(p->one))
+                       p->status = DIFF_STATUS_ADDED;
+               else if (!DIFF_FILE_VALID(p->two))
+                       p->status = DIFF_STATUS_DELETED;
+               else if (DIFF_PAIR_TYPE_CHANGED(p))
+                       p->status = DIFF_STATUS_TYPE_CHANGED;
+
+               /* from this point on, we are dealing with a pair
+                * whose both sides are valid and of the same type, i.e.
+                * either in-place edit or rename/copy edit.
+                */
+               else if (DIFF_PAIR_RENAME(p)) {
+                       if (p->source_stays) {
+                               p->status = DIFF_STATUS_COPIED;
+                               continue;
+                       }
+                       /* See if there is some other filepair that
+                        * copies from the same source as us.  If so
+                        * we are a copy.  Otherwise we are either a
+                        * copy if the path stays, or a rename if it
+                        * does not, but we already handled "stays" case.
+                        */
+                       for (j = i + 1; j < q->nr; j++) {
+                               pp = q->queue[j];
+                               if (strcmp(pp->one->path, p->one->path))
+                                       continue; /* not us */
+                               if (!DIFF_PAIR_RENAME(pp))
+                                       continue; /* not a rename/copy */
+                               /* pp is a rename/copy from the same source */
+                               p->status = DIFF_STATUS_COPIED;
+                               break;
+                       }
+                       if (!p->status)
+                               p->status = DIFF_STATUS_RENAMED;
+               }
+               else if (memcmp(p->one->sha1, p->two->sha1, 20) ||
+                        p->one->mode != p->two->mode)
+                       p->status = DIFF_STATUS_MODIFIED;
+               else {
+                       /* This is a "no-change" entry and should not
+                        * happen anymore, but prepare for broken callers.
+                        */
+                       error("feeding unmodified %s to diffcore",
+                             p->one->path);
+                       p->status = DIFF_STATUS_UNKNOWN;
+               }
+       }
+       diff_debug_queue("resolve-rename-copy done", q);
+}
+
+static void flush_one_pair(struct diff_filepair *p,
+                          int diff_output_format,
+                          struct diff_options *options,
+                          struct diffstat_t *diffstat)
+{
+       int inter_name_termination = '\t';
+       int line_termination = options->line_termination;
+       if (!line_termination)
+               inter_name_termination = 0;
+
+       switch (p->status) {
+       case DIFF_STATUS_UNKNOWN:
+               break;
+       case 0:
+               die("internal error in diff-resolve-rename-copy");
+               break;
+       default:
+               switch (diff_output_format) {
+               case DIFF_FORMAT_DIFFSTAT:
+                       diff_flush_stat(p, options, diffstat);
+                       break;
+               case DIFF_FORMAT_PATCH:
+                       diff_flush_patch(p, options);
+                       break;
+               case DIFF_FORMAT_RAW:
+               case DIFF_FORMAT_NAME_STATUS:
+                       diff_flush_raw(p, line_termination,
+                                      inter_name_termination,
+                                      options, diff_output_format);
+                       break;
+               case DIFF_FORMAT_NAME:
+                       diff_flush_name(p,
+                                       inter_name_termination,
+                                       line_termination);
+                       break;
+               case DIFF_FORMAT_NO_OUTPUT:
+                       break;
+               }
+       }
+}
+
+void diff_flush(struct diff_options *options)
+{
+       struct diff_queue_struct *q = &diff_queued_diff;
+       int i;
+       int diff_output_format = options->output_format;
+       struct diffstat_t *diffstat = NULL;
+
+       if (diff_output_format == DIFF_FORMAT_DIFFSTAT || options->with_stat) {
+               diffstat = xcalloc(sizeof (struct diffstat_t), 1);
+               diffstat->xm.consume = diffstat_consume;
+       }
+
+       if (options->with_raw) {
+               for (i = 0; i < q->nr; i++) {
+                       struct diff_filepair *p = q->queue[i];
+                       flush_one_pair(p, DIFF_FORMAT_RAW, options, NULL);
+               }
+               putchar(options->line_termination);
+       }
+       if (options->with_stat) {
+               for (i = 0; i < q->nr; i++) {
+                       struct diff_filepair *p = q->queue[i];
+                       flush_one_pair(p, DIFF_FORMAT_DIFFSTAT, options,
+                                      diffstat);
+               }
+               show_stats(diffstat);
+               free(diffstat);
+               diffstat = NULL;
+               putchar(options->line_termination);
+       }
+       for (i = 0; i < q->nr; i++) {
+               struct diff_filepair *p = q->queue[i];
+               flush_one_pair(p, diff_output_format, options, diffstat);
+               diff_free_filepair(p);
+       }
+
+       if (diffstat) {
+               show_stats(diffstat);
+               free(diffstat);
+       }
+
+       free(q->queue);
+       q->queue = NULL;
+       q->nr = q->alloc = 0;
+}
+
+static void diffcore_apply_filter(const char *filter)
+{
+       int i;
+       struct diff_queue_struct *q = &diff_queued_diff;
+       struct diff_queue_struct outq;
+       outq.queue = NULL;
+       outq.nr = outq.alloc = 0;
+
+       if (!filter)
+               return;
+
+       if (strchr(filter, DIFF_STATUS_FILTER_AON)) {
+               int found;
+               for (i = found = 0; !found && i < q->nr; i++) {
+                       struct diff_filepair *p = q->queue[i];
+                       if (((p->status == DIFF_STATUS_MODIFIED) &&
+                            ((p->score &&
+                              strchr(filter, DIFF_STATUS_FILTER_BROKEN)) ||
+                             (!p->score &&
+                              strchr(filter, DIFF_STATUS_MODIFIED)))) ||
+                           ((p->status != DIFF_STATUS_MODIFIED) &&
+                            strchr(filter, p->status)))
+                               found++;
+               }
+               if (found)
+                       return;
+
+               /* otherwise we will clear the whole queue
+                * by copying the empty outq at the end of this
+                * function, but first clear the current entries
+                * in the queue.
+                */
+               for (i = 0; i < q->nr; i++)
+                       diff_free_filepair(q->queue[i]);
+       }
+       else {
+               /* Only the matching ones */
+               for (i = 0; i < q->nr; i++) {
+                       struct diff_filepair *p = q->queue[i];
+
+                       if (((p->status == DIFF_STATUS_MODIFIED) &&
+                            ((p->score &&
+                              strchr(filter, DIFF_STATUS_FILTER_BROKEN)) ||
+                             (!p->score &&
+                              strchr(filter, DIFF_STATUS_MODIFIED)))) ||
+                           ((p->status != DIFF_STATUS_MODIFIED) &&
+                            strchr(filter, p->status)))
+                               diff_q(&outq, p);
+                       else
+                               diff_free_filepair(p);
+               }
+       }
+       free(q->queue);
+       *q = outq;
+}
+
+void diffcore_std(struct diff_options *options)
+{
+       if (options->break_opt != -1)
+               diffcore_break(options->break_opt);
+       if (options->detect_rename)
+               diffcore_rename(options);
+       if (options->break_opt != -1)
+               diffcore_merge_broken();
+       if (options->pickaxe)
+               diffcore_pickaxe(options->pickaxe, options->pickaxe_opts);
+       if (options->orderfile)
+               diffcore_order(options->orderfile);
+       diff_resolve_rename_copy();
+       diffcore_apply_filter(options->filter);
+}
+
+
+void diffcore_std_no_resolve(struct diff_options *options)
+{
+       if (options->pickaxe)
+               diffcore_pickaxe(options->pickaxe, options->pickaxe_opts);
+       if (options->orderfile)
+               diffcore_order(options->orderfile);
+       diffcore_apply_filter(options->filter);
+}
+
+void diff_addremove(struct diff_options *options,
+                   int addremove, unsigned mode,
+                   const unsigned char *sha1,
+                   const char *base, const char *path)
+{
+       char concatpath[PATH_MAX];
+       struct diff_filespec *one, *two;
+
+       /* This may look odd, but it is a preparation for
+        * feeding "there are unchanged files which should
+        * not produce diffs, but when you are doing copy
+        * detection you would need them, so here they are"
+        * entries to the diff-core.  They will be prefixed
+        * with something like '=' or '*' (I haven't decided
+        * which but should not make any difference).
+        * Feeding the same new and old to diff_change() 
+        * also has the same effect.
+        * Before the final output happens, they are pruned after
+        * merged into rename/copy pairs as appropriate.
+        */
+       if (options->reverse_diff)
+               addremove = (addremove == '+' ? '-' :
+                            addremove == '-' ? '+' : addremove);
+
+       if (!path) path = "";
+       sprintf(concatpath, "%s%s", base, path);
+       one = alloc_filespec(concatpath);
+       two = alloc_filespec(concatpath);
+
+       if (addremove != '+')
+               fill_filespec(one, sha1, mode);
+       if (addremove != '-')
+               fill_filespec(two, sha1, mode);
+
+       diff_queue(&diff_queued_diff, one, two);
+}
+
+void diff_change(struct diff_options *options,
+                unsigned old_mode, unsigned new_mode,
+                const unsigned char *old_sha1,
+                const unsigned char *new_sha1,
+                const char *base, const char *path) 
+{
+       char concatpath[PATH_MAX];
+       struct diff_filespec *one, *two;
+
+       if (options->reverse_diff) {
+               unsigned tmp;
+               const unsigned char *tmp_c;
+               tmp = old_mode; old_mode = new_mode; new_mode = tmp;
+               tmp_c = old_sha1; old_sha1 = new_sha1; new_sha1 = tmp_c;
+       }
+       if (!path) path = "";
+       sprintf(concatpath, "%s%s", base, path);
+       one = alloc_filespec(concatpath);
+       two = alloc_filespec(concatpath);
+       fill_filespec(one, old_sha1, old_mode);
+       fill_filespec(two, new_sha1, new_mode);
+
+       diff_queue(&diff_queued_diff, one, two);
+}
+
+void diff_unmerge(struct diff_options *options,
+                 const char *path)
+{
+       struct diff_filespec *one, *two;
+       one = alloc_filespec(path);
+       two = alloc_filespec(path);
+       diff_queue(&diff_queued_diff, one, two);
+}
index 2b79dd0a68c30343d74e3e82c056fef6a355e82e..7207867a74d0869c6a6b839c040321c843ee9c2f 100644 (file)
@@ -3,7 +3,7 @@
 #include "commit.h"
 #include "log-tree.h"
 
-static struct log_tree_opt log_tree_opt;
+static struct rev_info log_tree_opt;
 
 static int diff_tree_commit_sha1(const unsigned char *sha1)
 {
@@ -62,47 +62,21 @@ int main(int argc, const char **argv)
 {
        int nr_sha1;
        char line[1000];
-       unsigned char sha1[2][20];
-       const char *prefix = setup_git_directory();
-       static struct log_tree_opt *opt = &log_tree_opt;
+       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);
        nr_sha1 = 0;
-       init_log_tree_opt(opt);
+       init_revisions(opt);
+       opt->abbrev = 0;
+       opt->diff = 1;
+       argc = setup_revisions(argc, argv, opt, NULL);
 
-       for (;;) {
-               int opt_cnt;
-               const char *arg;
+       while (--argc > 0) {
+               const char *arg = *++argv;
 
-               argv++;
-               argc--;
-               arg = *argv;
-               if (!arg)
-                       break;
-
-               if (*arg != '-') {
-                       if (nr_sha1 < 2 && !get_sha1(arg, sha1[nr_sha1])) {
-                               nr_sha1++;
-                               continue;
-                       }
-                       break;
-               }
-
-               opt_cnt = log_tree_opt_parse(opt, argv, argc);
-               if (opt_cnt < 0)
-                       usage(diff_tree_usage);
-               else if (opt_cnt) {
-                       argv += opt_cnt - 1;
-                       argc -= opt_cnt - 1;
-                       continue;
-               }
-
-               if (!strcmp(arg, "--")) {
-                       argv++;
-                       argc--;
-                       break;
-               }
                if (!strcmp(arg, "--stdin")) {
                        read_stdin = 1;
                        continue;
@@ -110,18 +84,36 @@ int main(int argc, const char **argv)
                usage(diff_tree_usage);
        }
 
-       if (opt->combine_merges)
-               opt->ignore_merges = 0;
-
-       /* We can only do dense combined merges with diff output */
-       if (opt->dense_combined_merges)
-               opt->diffopt.output_format = DIFF_FORMAT_PATCH;
-
-       if (opt->diffopt.output_format == DIFF_FORMAT_PATCH)
-               opt->diffopt.recursive = 1;
-
-       diff_tree_setup_paths(get_pathspec(prefix, argv), opt);
-       diff_setup_done(&opt->diffopt);
+       /*
+        * 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.
+        */
+       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;
+                       }
+               }
+       }
 
        switch (nr_sha1) {
        case 0:
@@ -129,10 +121,12 @@ int main(int argc, const char **argv)
                        usage(diff_tree_usage);
                break;
        case 1:
-               diff_tree_commit_sha1(sha1[0]);
+               diff_tree_commit_sha1(tree1->sha1);
                break;
        case 2:
-               diff_tree_sha1(sha1[0], sha1[1], "", &opt->diffopt);
+               diff_tree_sha1(tree1->sha1,
+                              tree2->sha1,
+                              "", &opt->diffopt);
                log_tree_diff_flush(opt);
                break;
        }
diff --git a/diff.c b/diff.c
deleted file mode 100644 (file)
index f1b672d..0000000
--- a/diff.c
+++ /dev/null
@@ -1,1700 +0,0 @@
-/*
- * Copyright (C) 2005 Junio C Hamano
- */
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <signal.h>
-#include "cache.h"
-#include "quote.h"
-#include "diff.h"
-#include "diffcore.h"
-#include "xdiff-interface.h"
-
-static int use_size_cache;
-
-int diff_rename_limit_default = -1;
-
-int git_diff_config(const char *var, const char *value)
-{
-       if (!strcmp(var, "diff.renamelimit")) {
-               diff_rename_limit_default = git_config_int(var, value);
-               return 0;
-       }
-
-       return git_default_config(var, value);
-}
-
-static char *quote_one(const char *str)
-{
-       int needlen;
-       char *xp;
-
-       if (!str)
-               return NULL;
-       needlen = quote_c_style(str, NULL, NULL, 0);
-       if (!needlen)
-               return strdup(str);
-       xp = xmalloc(needlen + 1);
-       quote_c_style(str, xp, NULL, 0);
-       return xp;
-}
-
-static char *quote_two(const char *one, const char *two)
-{
-       int need_one = quote_c_style(one, NULL, NULL, 1);
-       int need_two = quote_c_style(two, NULL, NULL, 1);
-       char *xp;
-
-       if (need_one + need_two) {
-               if (!need_one) need_one = strlen(one);
-               if (!need_two) need_one = strlen(two);
-
-               xp = xmalloc(need_one + need_two + 3);
-               xp[0] = '"';
-               quote_c_style(one, xp + 1, NULL, 1);
-               quote_c_style(two, xp + need_one + 1, NULL, 1);
-               strcpy(xp + need_one + need_two + 1, "\"");
-               return xp;
-       }
-       need_one = strlen(one);
-       need_two = strlen(two);
-       xp = xmalloc(need_one + need_two + 1);
-       strcpy(xp, one);
-       strcpy(xp + need_one, two);
-       return xp;
-}
-
-static const char *external_diff(void)
-{
-       static const char *external_diff_cmd = NULL;
-       static int done_preparing = 0;
-
-       if (done_preparing)
-               return external_diff_cmd;
-       external_diff_cmd = getenv("GIT_EXTERNAL_DIFF");
-       done_preparing = 1;
-       return external_diff_cmd;
-}
-
-#define TEMPFILE_PATH_LEN              50
-
-static struct diff_tempfile {
-       const char *name; /* filename external diff should read from */
-       char hex[41];
-       char mode[10];
-       char tmp_path[TEMPFILE_PATH_LEN];
-} diff_temp[2];
-
-static int count_lines(const char *data, int size)
-{
-       int count, ch, completely_empty = 1, nl_just_seen = 0;
-       count = 0;
-       while (0 < size--) {
-               ch = *data++;
-               if (ch == '\n') {
-                       count++;
-                       nl_just_seen = 1;
-                       completely_empty = 0;
-               }
-               else {
-                       nl_just_seen = 0;
-                       completely_empty = 0;
-               }
-       }
-       if (completely_empty)
-               return 0;
-       if (!nl_just_seen)
-               count++; /* no trailing newline */
-       return count;
-}
-
-static void print_line_count(int count)
-{
-       switch (count) {
-       case 0:
-               printf("0,0");
-               break;
-       case 1:
-               printf("1");
-               break;
-       default:
-               printf("1,%d", count);
-               break;
-       }
-}
-
-static void copy_file(int prefix, const char *data, int size)
-{
-       int ch, nl_just_seen = 1;
-       while (0 < size--) {
-               ch = *data++;
-               if (nl_just_seen)
-                       putchar(prefix);
-               putchar(ch);
-               if (ch == '\n')
-                       nl_just_seen = 1;
-               else
-                       nl_just_seen = 0;
-       }
-       if (!nl_just_seen)
-               printf("\n\\ No newline at end of file\n");
-}
-
-static void emit_rewrite_diff(const char *name_a,
-                             const char *name_b,
-                             struct diff_filespec *one,
-                             struct diff_filespec *two)
-{
-       int lc_a, lc_b;
-       diff_populate_filespec(one, 0);
-       diff_populate_filespec(two, 0);
-       lc_a = count_lines(one->data, one->size);
-       lc_b = count_lines(two->data, two->size);
-       printf("--- %s\n+++ %s\n@@ -", name_a, name_b);
-       print_line_count(lc_a);
-       printf(" +");
-       print_line_count(lc_b);
-       printf(" @@\n");
-       if (lc_a)
-               copy_file('-', one->data, one->size);
-       if (lc_b)
-               copy_file('+', two->data, two->size);
-}
-
-static int fill_mmfile(mmfile_t *mf, struct diff_filespec *one)
-{
-       if (!DIFF_FILE_VALID(one)) {
-               mf->ptr = ""; /* does not matter */
-               mf->size = 0;
-               return 0;
-       }
-       else if (diff_populate_filespec(one, 0))
-               return -1;
-       mf->ptr = one->data;
-       mf->size = one->size;
-       return 0;
-}
-
-struct emit_callback {
-       const char **label_path;
-};
-
-static int fn_out(void *priv, mmbuffer_t *mb, int nbuf)
-{
-       int i;
-       struct emit_callback *ecbdata = priv;
-
-       if (ecbdata->label_path[0]) {
-               printf("--- %s\n", ecbdata->label_path[0]);
-               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;
-}
-
-struct diffstat_t {
-       struct xdiff_emit_state xm;
-
-       int nr;
-       int alloc;
-       struct diffstat_file {
-               char *name;
-               unsigned int added, deleted;
-       } **files;
-};
-
-static struct diffstat_file *diffstat_add(struct diffstat_t *diffstat,
-               const char *name)
-{
-       struct diffstat_file *x;
-       x = xcalloc(sizeof (*x), 1);
-       if (diffstat->nr == diffstat->alloc) {
-               diffstat->alloc = alloc_nr(diffstat->alloc);
-               diffstat->files = xrealloc(diffstat->files,
-                               diffstat->alloc * sizeof(x));
-       }
-       diffstat->files[diffstat->nr++] = x;
-       x->name = strdup(name);
-       return x;
-}
-
-static void diffstat_consume(void *priv, char *line, unsigned long len)
-{
-       struct diffstat_t *diffstat = priv;
-       struct diffstat_file *x = diffstat->files[diffstat->nr - 1];
-
-       if (line[0] == '+')
-               x->added++;
-       else if (line[0] == '-')
-               x->deleted++;
-}
-
-static const char pluses[] = "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++";
-static const char minuses[]= "----------------------------------------------------------------------";
-
-static void show_stats(struct diffstat_t* data)
-{
-       char *prefix = "";
-       int i, len, add, del, total, adds = 0, dels = 0;
-       int max, max_change = 0, max_len = 0;
-       int total_files = data->nr;
-
-       if (data->nr == 0)
-               return;
-
-       printf("---\n");
-
-       for (i = 0; i < data->nr; i++) {
-               struct diffstat_file *file = data->files[i];
-
-               if (max_change < file->added + file->deleted)
-                       max_change = file->added + file->deleted;
-               len = strlen(file->name);
-               if (max_len < len)
-                       max_len = len;
-       }
-
-       for (i = 0; i < data->nr; i++) {
-               char *name = data->files[i]->name;
-               int added = data->files[i]->added;
-               int deleted = data->files[i]->deleted;
-
-               if (0 < (len = quote_c_style(name, NULL, NULL, 0))) {
-                       char *qname = xmalloc(len + 1);
-                       quote_c_style(name, qname, NULL, 0);
-                       free(name);
-                       data->files[i]->name = name = qname;
-               }
-
-               /*
-                * "scale" the filename
-                */
-               len = strlen(name);
-               max = max_len;
-               if (max > 50)
-                       max = 50;
-               if (len > max) {
-                       char *slash;
-                       prefix = "...";
-                       max -= 3;
-                       name += len - max;
-                       slash = strchr(name, '/');
-                       if (slash)
-                               name = slash;
-               }
-               len = max;
-
-               /*
-                * scale the add/delete
-                */
-               max = max_change;
-               if (max + len > 70)
-                       max = 70 - len;
-
-               if (added < 0) {
-                       /* binary file */
-                       printf(" %s%-*s |  Bin\n", prefix, len, name);
-                       goto free_diffstat_file;
-               } else if (added + deleted == 0) {
-                       total_files--;
-                       goto free_diffstat_file;
-               }
-
-               add = added;
-               del = deleted;
-               total = add + del;
-               adds += add;
-               dels += del;
-
-               if (max_change > 0) {
-                       total = (total * max + max_change / 2) / max_change;
-                       add = (add * max + max_change / 2) / max_change;
-                       del = total - add;
-               }
-               printf(" %s%-*s |%5d %.*s%.*s\n", prefix,
-                               len, name, added + deleted,
-                               add, pluses, del, minuses);
-       free_diffstat_file:
-               free(data->files[i]->name);
-               free(data->files[i]);
-       }
-       free(data->files);
-       printf(" %d files changed, %d insertions(+), %d deletions(-)\n",
-                       total_files, adds, dels);
-}
-
-#define FIRST_FEW_BYTES 8000
-static int mmfile_is_binary(mmfile_t *mf)
-{
-       long sz = mf->size;
-       if (FIRST_FEW_BYTES < sz)
-               sz = FIRST_FEW_BYTES;
-       if (memchr(mf->ptr, 0, sz))
-               return 1;
-       return 0;
-}
-
-static void builtin_diff(const char *name_a,
-                        const char *name_b,
-                        struct diff_filespec *one,
-                        struct diff_filespec *two,
-                        const char *xfrm_msg,
-                        int complete_rewrite)
-{
-       mmfile_t mf1, mf2;
-       const char *lbl[2];
-       char *a_one, *b_two;
-
-       a_one = quote_two("a/", 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";
-       printf("diff --git %s %s\n", a_one, b_two);
-       if (lbl[0][0] == '/') {
-               /* /dev/null */
-               printf("new file mode %06o\n", two->mode);
-               if (xfrm_msg && xfrm_msg[0])
-                       puts(xfrm_msg);
-       }
-       else if (lbl[1][0] == '/') {
-               printf("deleted file mode %06o\n", one->mode);
-               if (xfrm_msg && xfrm_msg[0])
-                       puts(xfrm_msg);
-       }
-       else {
-               if (one->mode != two->mode) {
-                       printf("old mode %06o\n", one->mode);
-                       printf("new mode %06o\n", two->mode);
-               }
-               if (xfrm_msg && xfrm_msg[0])
-                       puts(xfrm_msg);
-               /*
-                * we do not run diff between different kind
-                * of objects.
-                */
-               if ((one->mode ^ two->mode) & S_IFMT)
-                       goto free_ab_and_return;
-               if (complete_rewrite) {
-                       emit_rewrite_diff(name_a, name_b, one, two);
-                       goto free_ab_and_return;
-               }
-       }
-
-       if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0)
-               die("unable to read files to diff");
-
-       if (mmfile_is_binary(&mf1) || mmfile_is_binary(&mf2))
-               printf("Binary files %s and %s differ\n", lbl[0], lbl[1]);
-       else {
-               /* Crazy xdl interfaces.. */
-               const char *diffopts = getenv("GIT_DIFF_OPTS");
-               xpparam_t xpp;
-               xdemitconf_t xecfg;
-               xdemitcb_t ecb;
-               struct emit_callback ecbdata;
-
-               ecbdata.label_path = lbl;
-               xpp.flags = XDF_NEED_MINIMAL;
-               xecfg.ctxlen = 3;
-               xecfg.flags = XDL_EMIT_FUNCNAMES;
-               if (!diffopts)
-                       ;
-               else if (!strncmp(diffopts, "--unified=", 10))
-                       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.priv = &ecbdata;
-               xdl_diff(&mf1, &mf2, &xpp, &xecfg, &ecb);
-       }
-
- free_ab_and_return:
-       free(a_one);
-       free(b_two);
-       return;
-}
-
-static void builtin_diffstat(const char *name_a, const char *name_b,
-               struct diff_filespec *one, struct diff_filespec *two,
-               struct diffstat_t *diffstat)
-{
-       mmfile_t mf1, mf2;
-       struct diffstat_file *data;
-
-       data = diffstat_add(diffstat, name_a ? name_a : name_b);
-
-       if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0)
-               die("unable to read files to diff");
-
-       if (mmfile_is_binary(&mf1) || mmfile_is_binary(&mf2))
-               data->added = -1;
-       else {
-               /* Crazy xdl interfaces.. */
-               xpparam_t xpp;
-               xdemitconf_t xecfg;
-               xdemitcb_t ecb;
-
-               xpp.flags = XDF_NEED_MINIMAL;
-               xecfg.ctxlen = 0;
-               xecfg.flags = 0;
-               ecb.outf = xdiff_outf;
-               ecb.priv = diffstat;
-               xdl_diff(&mf1, &mf2, &xpp, &xecfg, &ecb);
-       }
-}
-
-struct diff_filespec *alloc_filespec(const char *path)
-{
-       int namelen = strlen(path);
-       struct diff_filespec *spec = xmalloc(sizeof(*spec) + namelen + 1);
-
-       memset(spec, 0, sizeof(*spec));
-       spec->path = (char *)(spec + 1);
-       memcpy(spec->path, path, namelen+1);
-       return spec;
-}
-
-void fill_filespec(struct diff_filespec *spec, const unsigned char *sha1,
-                  unsigned short mode)
-{
-       if (mode) {
-               spec->mode = canon_mode(mode);
-               memcpy(spec->sha1, sha1, 20);
-               spec->sha1_valid = !!memcmp(sha1, null_sha1, 20);
-       }
-}
-
-/*
- * Given a name and sha1 pair, if the dircache tells us the file in
- * the work tree has that object contents, return true, so that
- * prepare_temp_file() does not have to inflate and extract.
- */
-static int work_tree_matches(const char *name, const unsigned char *sha1)
-{
-       struct cache_entry *ce;
-       struct stat st;
-       int pos, len;
-
-       /* We do not read the cache ourselves here, because the
-        * benchmark with my previous version that always reads cache
-        * shows that it makes things worse for diff-tree comparing
-        * two linux-2.6 kernel trees in an already checked out work
-        * tree.  This is because most diff-tree comparisons deal with
-        * only a small number of files, while reading the cache is
-        * expensive for a large project, and its cost outweighs the
-        * savings we get by not inflating the object to a temporary
-        * file.  Practically, this code only helps when we are used
-        * by diff-cache --cached, which does read the cache before
-        * calling us.
-        */
-       if (!active_cache)
-               return 0;
-
-       len = strlen(name);
-       pos = cache_name_pos(name, len);
-       if (pos < 0)
-               return 0;
-       ce = active_cache[pos];
-       if ((lstat(name, &st) < 0) ||
-           !S_ISREG(st.st_mode) || /* careful! */
-           ce_match_stat(ce, &st, 0) ||
-           memcmp(sha1, ce->sha1, 20))
-               return 0;
-       /* we return 1 only when we can stat, it is a regular file,
-        * stat information matches, and sha1 recorded in the cache
-        * matches.  I.e. we know the file in the work tree really is
-        * the same as the <name, sha1> pair.
-        */
-       return 1;
-}
-
-static struct sha1_size_cache {
-       unsigned char sha1[20];
-       unsigned long size;
-} **sha1_size_cache;
-static int sha1_size_cache_nr, sha1_size_cache_alloc;
-
-static struct sha1_size_cache *locate_size_cache(unsigned char *sha1,
-                                                int find_only,
-                                                unsigned long size)
-{
-       int first, last;
-       struct sha1_size_cache *e;
-
-       first = 0;
-       last = sha1_size_cache_nr;
-       while (last > first) {
-               int cmp, next = (last + first) >> 1;
-               e = sha1_size_cache[next];
-               cmp = memcmp(e->sha1, sha1, 20);
-               if (!cmp)
-                       return e;
-               if (cmp < 0) {
-                       last = next;
-                       continue;
-               }
-               first = next+1;
-       }
-       /* not found */
-       if (find_only)
-               return NULL;
-       /* insert to make it at "first" */
-       if (sha1_size_cache_alloc <= sha1_size_cache_nr) {
-               sha1_size_cache_alloc = alloc_nr(sha1_size_cache_alloc);
-               sha1_size_cache = xrealloc(sha1_size_cache,
-                                          sha1_size_cache_alloc *
-                                          sizeof(*sha1_size_cache));
-       }
-       sha1_size_cache_nr++;
-       if (first < sha1_size_cache_nr)
-               memmove(sha1_size_cache + first + 1, sha1_size_cache + first,
-                       (sha1_size_cache_nr - first - 1) *
-                       sizeof(*sha1_size_cache));
-       e = xmalloc(sizeof(struct sha1_size_cache));
-       sha1_size_cache[first] = e;
-       memcpy(e->sha1, sha1, 20);
-       e->size = size;
-       return e;
-}
-
-/*
- * While doing rename detection and pickaxe operation, we may need to
- * grab the data for the blob (or file) for our own in-core comparison.
- * diff_filespec has data and size fields for this purpose.
- */
-int diff_populate_filespec(struct diff_filespec *s, int size_only)
-{
-       int err = 0;
-       if (!DIFF_FILE_VALID(s))
-               die("internal error: asking to populate invalid file.");
-       if (S_ISDIR(s->mode))
-               return -1;
-
-       if (!use_size_cache)
-               size_only = 0;
-
-       if (s->data)
-               return err;
-       if (!s->sha1_valid ||
-           work_tree_matches(s->path, s->sha1)) {
-               struct stat st;
-               int fd;
-               if (lstat(s->path, &st) < 0) {
-                       if (errno == ENOENT) {
-                       err_empty:
-                               err = -1;
-                       empty:
-                               s->data = "";
-                               s->size = 0;
-                               return err;
-                       }
-               }
-               s->size = st.st_size;
-               if (!s->size)
-                       goto empty;
-               if (size_only)
-                       return 0;
-               if (S_ISLNK(st.st_mode)) {
-                       int ret;
-                       s->data = xmalloc(s->size);
-                       s->should_free = 1;
-                       ret = readlink(s->path, s->data, s->size);
-                       if (ret < 0) {
-                               free(s->data);
-                               goto err_empty;
-                       }
-                       return 0;
-               }
-               fd = open(s->path, O_RDONLY);
-               if (fd < 0)
-                       goto err_empty;
-               s->data = mmap(NULL, s->size, PROT_READ, MAP_PRIVATE, fd, 0);
-               close(fd);
-               if (s->data == MAP_FAILED)
-                       goto err_empty;
-               s->should_munmap = 1;
-       }
-       else {
-               char type[20];
-               struct sha1_size_cache *e;
-
-               if (size_only) {
-                       e = locate_size_cache(s->sha1, 1, 0);
-                       if (e) {
-                               s->size = e->size;
-                               return 0;
-                       }
-                       if (!sha1_object_info(s->sha1, type, &s->size))
-                               locate_size_cache(s->sha1, 0, s->size);
-               }
-               else {
-                       s->data = read_sha1_file(s->sha1, type, &s->size);
-                       s->should_free = 1;
-               }
-       }
-       return 0;
-}
-
-void diff_free_filespec_data(struct diff_filespec *s)
-{
-       if (s->should_free)
-               free(s->data);
-       else if (s->should_munmap)
-               munmap(s->data, s->size);
-       s->should_free = s->should_munmap = 0;
-       s->data = NULL;
-       free(s->cnt_data);
-       s->cnt_data = NULL;
-}
-
-static void prep_temp_blob(struct diff_tempfile *temp,
-                          void *blob,
-                          unsigned long size,
-                          const unsigned char *sha1,
-                          int mode)
-{
-       int fd;
-
-       fd = git_mkstemp(temp->tmp_path, TEMPFILE_PATH_LEN, ".diff_XXXXXX");
-       if (fd < 0)
-               die("unable to create temp-file");
-       if (write(fd, blob, size) != size)
-               die("unable to write temp-file");
-       close(fd);
-       temp->name = temp->tmp_path;
-       strcpy(temp->hex, sha1_to_hex(sha1));
-       temp->hex[40] = 0;
-       sprintf(temp->mode, "%06o", mode);
-}
-
-static void prepare_temp_file(const char *name,
-                             struct diff_tempfile *temp,
-                             struct diff_filespec *one)
-{
-       if (!DIFF_FILE_VALID(one)) {
-       not_a_valid_file:
-               /* A '-' entry produces this for file-2, and
-                * a '+' entry produces this for file-1.
-                */
-               temp->name = "/dev/null";
-               strcpy(temp->hex, ".");
-               strcpy(temp->mode, ".");
-               return;
-       }
-
-       if (!one->sha1_valid ||
-           work_tree_matches(name, one->sha1)) {
-               struct stat st;
-               if (lstat(name, &st) < 0) {
-                       if (errno == ENOENT)
-                               goto not_a_valid_file;
-                       die("stat(%s): %s", name, strerror(errno));
-               }
-               if (S_ISLNK(st.st_mode)) {
-                       int ret;
-                       char buf[PATH_MAX + 1]; /* ought to be SYMLINK_MAX */
-                       if (sizeof(buf) <= st.st_size)
-                               die("symlink too long: %s", name);
-                       ret = readlink(name, buf, st.st_size);
-                       if (ret < 0)
-                               die("readlink(%s)", name);
-                       prep_temp_blob(temp, buf, st.st_size,
-                                      (one->sha1_valid ?
-                                       one->sha1 : null_sha1),
-                                      (one->sha1_valid ?
-                                       one->mode : S_IFLNK));
-               }
-               else {
-                       /* we can borrow from the file in the work tree */
-                       temp->name = name;
-                       if (!one->sha1_valid)
-                               strcpy(temp->hex, sha1_to_hex(null_sha1));
-                       else
-                               strcpy(temp->hex, sha1_to_hex(one->sha1));
-                       /* Even though we may sometimes borrow the
-                        * contents from the work tree, we always want
-                        * one->mode.  mode is trustworthy even when
-                        * !(one->sha1_valid), as long as
-                        * DIFF_FILE_VALID(one).
-                        */
-                       sprintf(temp->mode, "%06o", one->mode);
-               }
-               return;
-       }
-       else {
-               if (diff_populate_filespec(one, 0))
-                       die("cannot read data blob for %s", one->path);
-               prep_temp_blob(temp, one->data, one->size,
-                              one->sha1, one->mode);
-       }
-}
-
-static void remove_tempfile(void)
-{
-       int i;
-
-       for (i = 0; i < 2; i++)
-               if (diff_temp[i].name == diff_temp[i].tmp_path) {
-                       unlink(diff_temp[i].name);
-                       diff_temp[i].name = NULL;
-               }
-}
-
-static void remove_tempfile_on_signal(int signo)
-{
-       remove_tempfile();
-       signal(SIGINT, SIG_DFL);
-       raise(signo);
-}
-
-static int spawn_prog(const char *pgm, const char **arg)
-{
-       pid_t pid;
-       int status;
-
-       fflush(NULL);
-       pid = fork();
-       if (pid < 0)
-               die("unable to fork");
-       if (!pid) {
-               execvp(pgm, (char *const*) arg);
-               exit(255);
-       }
-
-       while (waitpid(pid, &status, 0) < 0) {
-               if (errno == EINTR)
-                       continue;
-               return -1;
-       }
-
-       /* Earlier we did not check the exit status because
-        * diff exits non-zero if files are different, and
-        * we are not interested in knowing that.  It was a
-        * mistake which made it harder to quit a diff-*
-        * session that uses the git-apply-patch-script as
-        * the GIT_EXTERNAL_DIFF.  A custom GIT_EXTERNAL_DIFF
-        * should also exit non-zero only when it wants to
-        * abort the entire diff-* session.
-        */
-       if (WIFEXITED(status) && !WEXITSTATUS(status))
-               return 0;
-       return -1;
-}
-
-/* An external diff command takes:
- *
- * diff-cmd name infile1 infile1-sha1 infile1-mode \
- *               infile2 infile2-sha1 infile2-mode [ rename-to ]
- *
- */
-static void run_external_diff(const char *pgm,
-                             const char *name,
-                             const char *other,
-                             struct diff_filespec *one,
-                             struct diff_filespec *two,
-                             const char *xfrm_msg,
-                             int complete_rewrite)
-{
-       const char *spawn_arg[10];
-       struct diff_tempfile *temp = diff_temp;
-       int retval;
-       static int atexit_asked = 0;
-       const char *othername;
-       const char **arg = &spawn_arg[0];
-
-       othername = (other? other : name);
-       if (one && two) {
-               prepare_temp_file(name, &temp[0], one);
-               prepare_temp_file(othername, &temp[1], two);
-               if (! atexit_asked &&
-                   (temp[0].name == temp[0].tmp_path ||
-                    temp[1].name == temp[1].tmp_path)) {
-                       atexit_asked = 1;
-                       atexit(remove_tempfile);
-               }
-               signal(SIGINT, remove_tempfile_on_signal);
-       }
-
-       if (one && two) {
-               *arg++ = pgm;
-               *arg++ = name;
-               *arg++ = temp[0].name;
-               *arg++ = temp[0].hex;
-               *arg++ = temp[0].mode;
-               *arg++ = temp[1].name;
-               *arg++ = temp[1].hex;
-               *arg++ = temp[1].mode;
-               if (other) {
-                       *arg++ = other;
-                       *arg++ = xfrm_msg;
-               }
-       } else {
-               *arg++ = pgm;
-               *arg++ = name;
-       }
-       *arg = NULL;
-       retval = spawn_prog(pgm, spawn_arg);
-       remove_tempfile();
-       if (retval) {
-               fprintf(stderr, "external diff died, stopping at %s.\n", name);
-               exit(1);
-       }
-}
-
-static void run_diff_cmd(const char *pgm,
-                        const char *name,
-                        const char *other,
-                        struct diff_filespec *one,
-                        struct diff_filespec *two,
-                        const char *xfrm_msg,
-                        int complete_rewrite)
-{
-       if (pgm) {
-               run_external_diff(pgm, name, other, one, two, xfrm_msg,
-                                 complete_rewrite);
-               return;
-       }
-       if (one && two)
-               builtin_diff(name, other ? other : name,
-                            one, two, xfrm_msg, complete_rewrite);
-       else
-               printf("* Unmerged path %s\n", name);
-}
-
-static void diff_fill_sha1_info(struct diff_filespec *one)
-{
-       if (DIFF_FILE_VALID(one)) {
-               if (!one->sha1_valid) {
-                       struct stat st;
-                       if (lstat(one->path, &st) < 0)
-                               die("stat %s", one->path);
-                       if (index_path(one->sha1, one->path, &st, 0))
-                               die("cannot hash %s\n", one->path);
-               }
-       }
-       else
-               memset(one->sha1, 0, 20);
-}
-
-static void run_diff(struct diff_filepair *p, struct diff_options *o)
-{
-       const char *pgm = external_diff();
-       char msg[PATH_MAX*2+300], *xfrm_msg;
-       struct diff_filespec *one;
-       struct diff_filespec *two;
-       const char *name;
-       const char *other;
-       char *name_munged, *other_munged;
-       int complete_rewrite = 0;
-       int len;
-
-       if (DIFF_PAIR_UNMERGED(p)) {
-               /* unmerged */
-               run_diff_cmd(pgm, p->one->path, NULL, NULL, NULL, NULL, 0);
-               return;
-       }
-
-       name = p->one->path;
-       other = (strcmp(name, p->two->path) ? p->two->path : NULL);
-       name_munged = quote_one(name);
-       other_munged = quote_one(other);
-       one = p->one; two = p->two;
-
-       diff_fill_sha1_info(one);
-       diff_fill_sha1_info(two);
-
-       len = 0;
-       switch (p->status) {
-       case DIFF_STATUS_COPIED:
-               len += snprintf(msg + len, sizeof(msg) - len,
-                               "similarity index %d%%\n"
-                               "copy from %s\n"
-                               "copy to %s\n",
-                               (int)(0.5 + p->score * 100.0/MAX_SCORE),
-                               name_munged, other_munged);
-               break;
-       case DIFF_STATUS_RENAMED:
-               len += snprintf(msg + len, sizeof(msg) - len,
-                               "similarity index %d%%\n"
-                               "rename from %s\n"
-                               "rename to %s\n",
-                               (int)(0.5 + p->score * 100.0/MAX_SCORE),
-                               name_munged, other_munged);
-               break;
-       case DIFF_STATUS_MODIFIED:
-               if (p->score) {
-                       len += snprintf(msg + len, sizeof(msg) - len,
-                                       "dissimilarity index %d%%\n",
-                                       (int)(0.5 + p->score *
-                                             100.0/MAX_SCORE));
-                       complete_rewrite = 1;
-                       break;
-               }
-               /* fallthru */
-       default:
-               /* nothing */
-               ;
-       }
-
-       if (memcmp(one->sha1, two->sha1, 20)) {
-               char one_sha1[41];
-               int abbrev = o->full_index ? 40 : DEFAULT_ABBREV;
-               memcpy(one_sha1, sha1_to_hex(one->sha1), 41);
-
-               len += snprintf(msg + len, sizeof(msg) - len,
-                               "index %.*s..%.*s",
-                               abbrev, one_sha1, abbrev,
-                               sha1_to_hex(two->sha1));
-               if (one->mode == two->mode)
-                       len += snprintf(msg + len, sizeof(msg) - len,
-                                       " %06o", one->mode);
-               len += snprintf(msg + len, sizeof(msg) - len, "\n");
-       }
-
-       if (len)
-               msg[--len] = 0;
-       xfrm_msg = len ? msg : NULL;
-
-       if (!pgm &&
-           DIFF_FILE_VALID(one) && DIFF_FILE_VALID(two) &&
-           (S_IFMT & one->mode) != (S_IFMT & two->mode)) {
-               /* a filepair that changes between file and symlink
-                * needs to be split into deletion and creation.
-                */
-               struct diff_filespec *null = alloc_filespec(two->path);
-               run_diff_cmd(NULL, name, other, one, null, xfrm_msg, 0);
-               free(null);
-               null = alloc_filespec(one->path);
-               run_diff_cmd(NULL, name, other, null, two, xfrm_msg, 0);
-               free(null);
-       }
-       else
-               run_diff_cmd(pgm, name, other, one, two, xfrm_msg,
-                            complete_rewrite);
-
-       free(name_munged);
-       free(other_munged);
-}
-
-static void run_diffstat(struct diff_filepair *p, struct diff_options *o,
-               struct diffstat_t *diffstat)
-{
-       const char *name;
-       const char *other;
-
-       if (DIFF_PAIR_UNMERGED(p)) {
-               /* unmerged */
-               builtin_diffstat(p->one->path, NULL, NULL, NULL, diffstat);
-               return;
-       }
-
-       name = p->one->path;
-       other = (strcmp(name, p->two->path) ? p->two->path : NULL);
-
-       diff_fill_sha1_info(p->one);
-       diff_fill_sha1_info(p->two);
-
-       builtin_diffstat(name, other, p->one, p->two, diffstat);
-}
-
-void diff_setup(struct diff_options *options)
-{
-       memset(options, 0, sizeof(*options));
-       options->output_format = DIFF_FORMAT_RAW;
-       options->line_termination = '\n';
-       options->break_opt = -1;
-       options->rename_limit = -1;
-
-       options->change = diff_change;
-       options->add_remove = diff_addremove;
-}
-
-int diff_setup_done(struct diff_options *options)
-{
-       if ((options->find_copies_harder &&
-            options->detect_rename != DIFF_DETECT_COPY) ||
-           (0 <= options->rename_limit && !options->detect_rename))
-               return -1;
-       if (options->detect_rename && options->rename_limit < 0)
-               options->rename_limit = diff_rename_limit_default;
-       if (options->setup & DIFF_SETUP_USE_CACHE) {
-               if (!active_cache)
-                       /* read-cache does not die even when it fails
-                        * so it is safe for us to do this here.  Also
-                        * it does not smudge active_cache or active_nr
-                        * when it fails, so we do not have to worry about
-                        * cleaning it up ourselves either.
-                        */
-                       read_cache();
-       }
-       if (options->setup & DIFF_SETUP_USE_SIZE_CACHE)
-               use_size_cache = 1;
-       if (options->abbrev <= 0 || 40 < options->abbrev)
-               options->abbrev = 40; /* full */
-
-       return 0;
-}
-
-int diff_opt_parse(struct diff_options *options, const char **av, int ac)
-{
-       const char *arg = av[0];
-       if (!strcmp(arg, "-p") || !strcmp(arg, "-u"))
-               options->output_format = DIFF_FORMAT_PATCH;
-       else if (!strcmp(arg, "--patch-with-raw")) {
-               options->output_format = DIFF_FORMAT_PATCH;
-               options->with_raw = 1;
-       }
-       else if (!strcmp(arg, "--stat"))
-               options->output_format = DIFF_FORMAT_DIFFSTAT;
-       else if (!strcmp(arg, "-z"))
-               options->line_termination = 0;
-       else if (!strncmp(arg, "-l", 2))
-               options->rename_limit = strtoul(arg+2, NULL, 10);
-       else if (!strcmp(arg, "--full-index"))
-               options->full_index = 1;
-       else if (!strcmp(arg, "--name-only"))
-               options->output_format = DIFF_FORMAT_NAME;
-       else if (!strcmp(arg, "--name-status"))
-               options->output_format = DIFF_FORMAT_NAME_STATUS;
-       else if (!strcmp(arg, "-R"))
-               options->reverse_diff = 1;
-       else if (!strncmp(arg, "-S", 2))
-               options->pickaxe = arg + 2;
-       else if (!strcmp(arg, "-s"))
-               options->output_format = DIFF_FORMAT_NO_OUTPUT;
-       else if (!strncmp(arg, "-O", 2))
-               options->orderfile = arg + 2;
-       else if (!strncmp(arg, "--diff-filter=", 14))
-               options->filter = arg + 14;
-       else if (!strcmp(arg, "--pickaxe-all"))
-               options->pickaxe_opts = DIFF_PICKAXE_ALL;
-       else if (!strcmp(arg, "--pickaxe-regex"))
-               options->pickaxe_opts = DIFF_PICKAXE_REGEX;
-       else if (!strncmp(arg, "-B", 2)) {
-               if ((options->break_opt =
-                    diff_scoreopt_parse(arg)) == -1)
-                       return -1;
-       }
-       else if (!strncmp(arg, "-M", 2)) {
-               if ((options->rename_score =
-                    diff_scoreopt_parse(arg)) == -1)
-                       return -1;
-               options->detect_rename = DIFF_DETECT_RENAME;
-       }
-       else if (!strncmp(arg, "-C", 2)) {
-               if ((options->rename_score =
-                    diff_scoreopt_parse(arg)) == -1)
-                       return -1;
-               options->detect_rename = DIFF_DETECT_COPY;
-       }
-       else if (!strcmp(arg, "--find-copies-harder"))
-               options->find_copies_harder = 1;
-       else if (!strcmp(arg, "--abbrev"))
-               options->abbrev = DEFAULT_ABBREV;
-       else if (!strncmp(arg, "--abbrev=", 9)) {
-               options->abbrev = strtoul(arg + 9, NULL, 10);
-               if (options->abbrev < MINIMUM_ABBREV)
-                       options->abbrev = MINIMUM_ABBREV;
-               else if (40 < options->abbrev)
-                       options->abbrev = 40;
-       }
-       else
-               return 0;
-       return 1;
-}
-
-static int parse_num(const char **cp_p)
-{
-       unsigned long num, scale;
-       int ch, dot;
-       const char *cp = *cp_p;
-
-       num = 0;
-       scale = 1;
-       dot = 0;
-       for(;;) {
-               ch = *cp;
-               if ( !dot && ch == '.' ) {
-                       scale = 1;
-                       dot = 1;
-               } else if ( ch == '%' ) {
-                       scale = dot ? scale*100 : 100;
-                       cp++;   /* % is always at the end */
-                       break;
-               } else if ( ch >= '0' && ch <= '9' ) {
-                       if ( scale < 100000 ) {
-                               scale *= 10;
-                               num = (num*10) + (ch-'0');
-                       }
-               } else {
-                       break;
-               }
-               cp++;
-       }
-       *cp_p = cp;
-
-       /* user says num divided by scale and we say internally that
-        * is MAX_SCORE * num / scale.
-        */
-       return (num >= scale) ? MAX_SCORE : (MAX_SCORE * num / scale);
-}
-
-int diff_scoreopt_parse(const char *opt)
-{
-       int opt1, opt2, cmd;
-
-       if (*opt++ != '-')
-               return -1;
-       cmd = *opt++;
-       if (cmd != 'M' && cmd != 'C' && cmd != 'B')
-               return -1; /* that is not a -M, -C nor -B option */
-
-       opt1 = parse_num(&opt);
-       if (cmd != 'B')
-               opt2 = 0;
-       else {
-               if (*opt == 0)
-                       opt2 = 0;
-               else if (*opt != '/')
-                       return -1; /* we expect -B80/99 or -B80 */
-               else {
-                       opt++;
-                       opt2 = parse_num(&opt);
-               }
-       }
-       if (*opt != 0)
-               return -1;
-       return opt1 | (opt2 << 16);
-}
-
-struct diff_queue_struct diff_queued_diff;
-
-void diff_q(struct diff_queue_struct *queue, struct diff_filepair *dp)
-{
-       if (queue->alloc <= queue->nr) {
-               queue->alloc = alloc_nr(queue->alloc);
-               queue->queue = xrealloc(queue->queue,
-                                       sizeof(dp) * queue->alloc);
-       }
-       queue->queue[queue->nr++] = dp;
-}
-
-struct diff_filepair *diff_queue(struct diff_queue_struct *queue,
-                                struct diff_filespec *one,
-                                struct diff_filespec *two)
-{
-       struct diff_filepair *dp = xmalloc(sizeof(*dp));
-       dp->one = one;
-       dp->two = two;
-       dp->score = 0;
-       dp->status = 0;
-       dp->source_stays = 0;
-       dp->broken_pair = 0;
-       if (queue)
-               diff_q(queue, dp);
-       return dp;
-}
-
-void diff_free_filepair(struct diff_filepair *p)
-{
-       diff_free_filespec_data(p->one);
-       diff_free_filespec_data(p->two);
-       free(p->one);
-       free(p->two);
-       free(p);
-}
-
-/* This is different from find_unique_abbrev() in that
- * it stuffs the result with dots for alignment.
- */
-const char *diff_unique_abbrev(const unsigned char *sha1, int len)
-{
-       int abblen;
-       const char *abbrev;
-       if (len == 40)
-               return sha1_to_hex(sha1);
-
-       abbrev = find_unique_abbrev(sha1, len);
-       if (!abbrev)
-               return sha1_to_hex(sha1);
-       abblen = strlen(abbrev);
-       if (abblen < 37) {
-               static char hex[41];
-               if (len < abblen && abblen <= len + 2)
-                       sprintf(hex, "%s%.*s", abbrev, len+3-abblen, "..");
-               else
-                       sprintf(hex, "%s...", abbrev);
-               return hex;
-       }
-       return sha1_to_hex(sha1);
-}
-
-static void diff_flush_raw(struct diff_filepair *p,
-                          int line_termination,
-                          int inter_name_termination,
-                          struct diff_options *options,
-                          int output_format)
-{
-       int two_paths;
-       char status[10];
-       int abbrev = options->abbrev;
-       const char *path_one, *path_two;
-
-       path_one = p->one->path;
-       path_two = p->two->path;
-       if (line_termination) {
-               path_one = quote_one(path_one);
-               path_two = quote_one(path_two);
-       }
-
-       if (p->score)
-               sprintf(status, "%c%03d", p->status,
-                       (int)(0.5 + p->score * 100.0/MAX_SCORE));
-       else {
-               status[0] = p->status;
-               status[1] = 0;
-       }
-       switch (p->status) {
-       case DIFF_STATUS_COPIED:
-       case DIFF_STATUS_RENAMED:
-               two_paths = 1;
-               break;
-       case DIFF_STATUS_ADDED:
-       case DIFF_STATUS_DELETED:
-               two_paths = 0;
-               break;
-       default:
-               two_paths = 0;
-               break;
-       }
-       if (output_format != DIFF_FORMAT_NAME_STATUS) {
-               printf(":%06o %06o %s ",
-                      p->one->mode, p->two->mode,
-                      diff_unique_abbrev(p->one->sha1, abbrev));
-               printf("%s ",
-                      diff_unique_abbrev(p->two->sha1, abbrev));
-       }
-       printf("%s%c%s", status, inter_name_termination, path_one);
-       if (two_paths)
-               printf("%c%s", inter_name_termination, path_two);
-       putchar(line_termination);
-       if (path_one != p->one->path)
-               free((void*)path_one);
-       if (path_two != p->two->path)
-               free((void*)path_two);
-}
-
-static void diff_flush_name(struct diff_filepair *p,
-                           int inter_name_termination,
-                           int line_termination)
-{
-       char *path = p->two->path;
-
-       if (line_termination)
-               path = quote_one(p->two->path);
-       else
-               path = p->two->path;
-       printf("%s%c", path, line_termination);
-       if (p->two->path != path)
-               free(path);
-}
-
-int diff_unmodified_pair(struct diff_filepair *p)
-{
-       /* This function is written stricter than necessary to support
-        * the currently implemented transformers, but the idea is to
-        * let transformers to produce diff_filepairs any way they want,
-        * and filter and clean them up here before producing the output.
-        */
-       struct diff_filespec *one, *two;
-
-       if (DIFF_PAIR_UNMERGED(p))
-               return 0; /* unmerged is interesting */
-
-       one = p->one;
-       two = p->two;
-
-       /* deletion, addition, mode or type change
-        * and rename are all interesting.
-        */
-       if (DIFF_FILE_VALID(one) != DIFF_FILE_VALID(two) ||
-           DIFF_PAIR_MODE_CHANGED(p) ||
-           strcmp(one->path, two->path))
-               return 0;
-
-       /* both are valid and point at the same path.  that is, we are
-        * dealing with a change.
-        */
-       if (one->sha1_valid && two->sha1_valid &&
-           !memcmp(one->sha1, two->sha1, sizeof(one->sha1)))
-               return 1; /* no change */
-       if (!one->sha1_valid && !two->sha1_valid)
-               return 1; /* both look at the same file on the filesystem. */
-       return 0;
-}
-
-static void diff_flush_patch(struct diff_filepair *p, struct diff_options *o)
-{
-       if (diff_unmodified_pair(p))
-               return;
-
-       if ((DIFF_FILE_VALID(p->one) && S_ISDIR(p->one->mode)) ||
-           (DIFF_FILE_VALID(p->two) && S_ISDIR(p->two->mode)))
-               return; /* no tree diffs in patch format */
-
-       run_diff(p, o);
-}
-
-static void diff_flush_stat(struct diff_filepair *p, struct diff_options *o,
-               struct diffstat_t *diffstat)
-{
-       if (diff_unmodified_pair(p))
-               return;
-
-       if ((DIFF_FILE_VALID(p->one) && S_ISDIR(p->one->mode)) ||
-           (DIFF_FILE_VALID(p->two) && S_ISDIR(p->two->mode)))
-               return; /* no tree diffs in patch format */
-
-       run_diffstat(p, o, diffstat);
-}
-
-int diff_queue_is_empty(void)
-{
-       struct diff_queue_struct *q = &diff_queued_diff;
-       int i;
-       for (i = 0; i < q->nr; i++)
-               if (!diff_unmodified_pair(q->queue[i]))
-                       return 0;
-       return 1;
-}
-
-#if DIFF_DEBUG
-void diff_debug_filespec(struct diff_filespec *s, int x, const char *one)
-{
-       fprintf(stderr, "queue[%d] %s (%s) %s %06o %s\n",
-               x, one ? one : "",
-               s->path,
-               DIFF_FILE_VALID(s) ? "valid" : "invalid",
-               s->mode,
-               s->sha1_valid ? sha1_to_hex(s->sha1) : "");
-       fprintf(stderr, "queue[%d] %s size %lu flags %d\n",
-               x, one ? one : "",
-               s->size, s->xfrm_flags);
-}
-
-void diff_debug_filepair(const struct diff_filepair *p, int i)
-{
-       diff_debug_filespec(p->one, i, "one");
-       diff_debug_filespec(p->two, i, "two");
-       fprintf(stderr, "score %d, status %c stays %d broken %d\n",
-               p->score, p->status ? p->status : '?',
-               p->source_stays, p->broken_pair);
-}
-
-void diff_debug_queue(const char *msg, struct diff_queue_struct *q)
-{
-       int i;
-       if (msg)
-               fprintf(stderr, "%s\n", msg);
-       fprintf(stderr, "q->nr = %d\n", q->nr);
-       for (i = 0; i < q->nr; i++) {
-               struct diff_filepair *p = q->queue[i];
-               diff_debug_filepair(p, i);
-       }
-}
-#endif
-
-static void diff_resolve_rename_copy(void)
-{
-       int i, j;
-       struct diff_filepair *p, *pp;
-       struct diff_queue_struct *q = &diff_queued_diff;
-
-       diff_debug_queue("resolve-rename-copy", q);
-
-       for (i = 0; i < q->nr; i++) {
-               p = q->queue[i];
-               p->status = 0; /* undecided */
-               if (DIFF_PAIR_UNMERGED(p))
-                       p->status = DIFF_STATUS_UNMERGED;
-               else if (!DIFF_FILE_VALID(p->one))
-                       p->status = DIFF_STATUS_ADDED;
-               else if (!DIFF_FILE_VALID(p->two))
-                       p->status = DIFF_STATUS_DELETED;
-               else if (DIFF_PAIR_TYPE_CHANGED(p))
-                       p->status = DIFF_STATUS_TYPE_CHANGED;
-
-               /* from this point on, we are dealing with a pair
-                * whose both sides are valid and of the same type, i.e.
-                * either in-place edit or rename/copy edit.
-                */
-               else if (DIFF_PAIR_RENAME(p)) {
-                       if (p->source_stays) {
-                               p->status = DIFF_STATUS_COPIED;
-                               continue;
-                       }
-                       /* See if there is some other filepair that
-                        * copies from the same source as us.  If so
-                        * we are a copy.  Otherwise we are either a
-                        * copy if the path stays, or a rename if it
-                        * does not, but we already handled "stays" case.
-                        */
-                       for (j = i + 1; j < q->nr; j++) {
-                               pp = q->queue[j];
-                               if (strcmp(pp->one->path, p->one->path))
-                                       continue; /* not us */
-                               if (!DIFF_PAIR_RENAME(pp))
-                                       continue; /* not a rename/copy */
-                               /* pp is a rename/copy from the same source */
-                               p->status = DIFF_STATUS_COPIED;
-                               break;
-                       }
-                       if (!p->status)
-                               p->status = DIFF_STATUS_RENAMED;
-               }
-               else if (memcmp(p->one->sha1, p->two->sha1, 20) ||
-                        p->one->mode != p->two->mode)
-                       p->status = DIFF_STATUS_MODIFIED;
-               else {
-                       /* This is a "no-change" entry and should not
-                        * happen anymore, but prepare for broken callers.
-                        */
-                       error("feeding unmodified %s to diffcore",
-                             p->one->path);
-                       p->status = DIFF_STATUS_UNKNOWN;
-               }
-       }
-       diff_debug_queue("resolve-rename-copy done", q);
-}
-
-static void flush_one_pair(struct diff_filepair *p,
-                          int diff_output_format,
-                          struct diff_options *options,
-                          struct diffstat_t *diffstat)
-{
-       int inter_name_termination = '\t';
-       int line_termination = options->line_termination;
-       if (!line_termination)
-               inter_name_termination = 0;
-
-       switch (p->status) {
-       case DIFF_STATUS_UNKNOWN:
-               break;
-       case 0:
-               die("internal error in diff-resolve-rename-copy");
-               break;
-       default:
-               switch (diff_output_format) {
-               case DIFF_FORMAT_DIFFSTAT:
-                       diff_flush_stat(p, options, diffstat);
-                       break;
-               case DIFF_FORMAT_PATCH:
-                       diff_flush_patch(p, options);
-                       break;
-               case DIFF_FORMAT_RAW:
-               case DIFF_FORMAT_NAME_STATUS:
-                       diff_flush_raw(p, line_termination,
-                                      inter_name_termination,
-                                      options, diff_output_format);
-                       break;
-               case DIFF_FORMAT_NAME:
-                       diff_flush_name(p,
-                                       inter_name_termination,
-                                       line_termination);
-                       break;
-               case DIFF_FORMAT_NO_OUTPUT:
-                       break;
-               }
-       }
-}
-
-void diff_flush(struct diff_options *options)
-{
-       struct diff_queue_struct *q = &diff_queued_diff;
-       int i;
-       int diff_output_format = options->output_format;
-       struct diffstat_t *diffstat = NULL;
-
-       if (diff_output_format == DIFF_FORMAT_DIFFSTAT) {
-               diffstat = xcalloc(sizeof (struct diffstat_t), 1);
-               diffstat->xm.consume = diffstat_consume;
-       }
-
-       if (options->with_raw) {
-               for (i = 0; i < q->nr; i++) {
-                       struct diff_filepair *p = q->queue[i];
-                       flush_one_pair(p, DIFF_FORMAT_RAW, options, NULL);
-               }
-               putchar(options->line_termination);
-       }
-       for (i = 0; i < q->nr; i++) {
-               struct diff_filepair *p = q->queue[i];
-               flush_one_pair(p, diff_output_format, options, diffstat);
-               diff_free_filepair(p);
-       }
-
-       if (diffstat) {
-               show_stats(diffstat);
-               free(diffstat);
-       }
-
-       free(q->queue);
-       q->queue = NULL;
-       q->nr = q->alloc = 0;
-}
-
-static void diffcore_apply_filter(const char *filter)
-{
-       int i;
-       struct diff_queue_struct *q = &diff_queued_diff;
-       struct diff_queue_struct outq;
-       outq.queue = NULL;
-       outq.nr = outq.alloc = 0;
-
-       if (!filter)
-               return;
-
-       if (strchr(filter, DIFF_STATUS_FILTER_AON)) {
-               int found;
-               for (i = found = 0; !found && i < q->nr; i++) {
-                       struct diff_filepair *p = q->queue[i];
-                       if (((p->status == DIFF_STATUS_MODIFIED) &&
-                            ((p->score &&
-                              strchr(filter, DIFF_STATUS_FILTER_BROKEN)) ||
-                             (!p->score &&
-                              strchr(filter, DIFF_STATUS_MODIFIED)))) ||
-                           ((p->status != DIFF_STATUS_MODIFIED) &&
-                            strchr(filter, p->status)))
-                               found++;
-               }
-               if (found)
-                       return;
-
-               /* otherwise we will clear the whole queue
-                * by copying the empty outq at the end of this
-                * function, but first clear the current entries
-                * in the queue.
-                */
-               for (i = 0; i < q->nr; i++)
-                       diff_free_filepair(q->queue[i]);
-       }
-       else {
-               /* Only the matching ones */
-               for (i = 0; i < q->nr; i++) {
-                       struct diff_filepair *p = q->queue[i];
-
-                       if (((p->status == DIFF_STATUS_MODIFIED) &&
-                            ((p->score &&
-                              strchr(filter, DIFF_STATUS_FILTER_BROKEN)) ||
-                             (!p->score &&
-                              strchr(filter, DIFF_STATUS_MODIFIED)))) ||
-                           ((p->status != DIFF_STATUS_MODIFIED) &&
-                            strchr(filter, p->status)))
-                               diff_q(&outq, p);
-                       else
-                               diff_free_filepair(p);
-               }
-       }
-       free(q->queue);
-       *q = outq;
-}
-
-void diffcore_std(struct diff_options *options)
-{
-       if (options->break_opt != -1)
-               diffcore_break(options->break_opt);
-       if (options->detect_rename)
-               diffcore_rename(options);
-       if (options->break_opt != -1)
-               diffcore_merge_broken();
-       if (options->pickaxe)
-               diffcore_pickaxe(options->pickaxe, options->pickaxe_opts);
-       if (options->orderfile)
-               diffcore_order(options->orderfile);
-       diff_resolve_rename_copy();
-       diffcore_apply_filter(options->filter);
-}
-
-
-void diffcore_std_no_resolve(struct diff_options *options)
-{
-       if (options->pickaxe)
-               diffcore_pickaxe(options->pickaxe, options->pickaxe_opts);
-       if (options->orderfile)
-               diffcore_order(options->orderfile);
-       diffcore_apply_filter(options->filter);
-}
-
-void diff_addremove(struct diff_options *options,
-                   int addremove, unsigned mode,
-                   const unsigned char *sha1,
-                   const char *base, const char *path)
-{
-       char concatpath[PATH_MAX];
-       struct diff_filespec *one, *two;
-
-       /* This may look odd, but it is a preparation for
-        * feeding "there are unchanged files which should
-        * not produce diffs, but when you are doing copy
-        * detection you would need them, so here they are"
-        * entries to the diff-core.  They will be prefixed
-        * with something like '=' or '*' (I haven't decided
-        * which but should not make any difference).
-        * Feeding the same new and old to diff_change() 
-        * also has the same effect.
-        * Before the final output happens, they are pruned after
-        * merged into rename/copy pairs as appropriate.
-        */
-       if (options->reverse_diff)
-               addremove = (addremove == '+' ? '-' :
-                            addremove == '-' ? '+' : addremove);
-
-       if (!path) path = "";
-       sprintf(concatpath, "%s%s", base, path);
-       one = alloc_filespec(concatpath);
-       two = alloc_filespec(concatpath);
-
-       if (addremove != '+')
-               fill_filespec(one, sha1, mode);
-       if (addremove != '-')
-               fill_filespec(two, sha1, mode);
-
-       diff_queue(&diff_queued_diff, one, two);
-}
-
-void diff_change(struct diff_options *options,
-                unsigned old_mode, unsigned new_mode,
-                const unsigned char *old_sha1,
-                const unsigned char *new_sha1,
-                const char *base, const char *path) 
-{
-       char concatpath[PATH_MAX];
-       struct diff_filespec *one, *two;
-
-       if (options->reverse_diff) {
-               unsigned tmp;
-               const unsigned char *tmp_c;
-               tmp = old_mode; old_mode = new_mode; new_mode = tmp;
-               tmp_c = old_sha1; old_sha1 = new_sha1; new_sha1 = tmp_c;
-       }
-       if (!path) path = "";
-       sprintf(concatpath, "%s%s", base, path);
-       one = alloc_filespec(concatpath);
-       two = alloc_filespec(concatpath);
-       fill_filespec(one, old_sha1, old_mode);
-       fill_filespec(two, new_sha1, new_mode);
-
-       diff_queue(&diff_queued_diff, one, two);
-}
-
-void diff_unmerge(struct diff_options *options,
-                 const char *path)
-{
-       struct diff_filespec *one, *two;
-       one = alloc_filespec(path);
-       two = alloc_filespec(path);
-       diff_queue(&diff_queued_diff, one, two);
-}
diff --git a/diff.h b/diff.h
index 2f8aff2cd4bf1eb73218e1e1e3df404b99308c1d..52fff6643ffe375e18cab6c8a3af6d5c11c8cd6d 100644 (file)
--- a/diff.h
+++ b/diff.h
@@ -6,6 +6,7 @@
 
 #include "tree-walk.h"
 
+struct rev_info;
 struct diff_options;
 
 typedef void (*change_fn_t)(struct diff_options *options,
@@ -25,6 +26,7 @@ struct diff_options {
        const char *pickaxe;
        unsigned recursive:1,
                 with_raw:1,
+                with_stat:1,
                 tree_in_recursive:1,
                 full_index:1;
        int break_opt;
@@ -69,11 +71,10 @@ struct combine_diff_path {
        (sizeof(struct combine_diff_path) + \
         sizeof(struct combine_diff_parent) * (n) + (l) + 1)
 
-extern int show_combined_diff(struct combine_diff_path *elem, int num_parent,
-                             int dense, const char *header,
-                             struct diff_options *);
+extern void show_combined_diff(struct combine_diff_path *elem, int num_parent,
+                             int dense, struct rev_info *);
 
-extern const char *diff_tree_combined_merge(const unsigned char *sha1, const char *, int, struct diff_options *opt);
+extern void diff_tree_combined_merge(const unsigned char *sha1, int, struct rev_info *);
 
 extern void diff_addremove(struct diff_options *,
                           int addremove,
@@ -120,6 +121,8 @@ extern void diffcore_std_no_resolve(struct diff_options *);
 "  --patch-with-raw\n" \
 "                output both a patch and the diff-raw format.\n" \
 "  --stat        show diffstat instead of patch.\n" \
+"  --patch-with-stat\n" \
+"                output a patch and prepend its diffstat.\n" \
 "  --name-only   show only names of changed files.\n" \
 "  --name-status show names and status of changed files.\n" \
 "  --full-index  show full object name on index lines.\n" \
index 590e738969ecfdd7ff2f23da36cdac917215313d..44bb2f23de926db59832a69a3205a320dfe61c4d 100644 (file)
@@ -32,7 +32,7 @@ const char *git_exec_path(void)
 int execv_git_cmd(const char **argv)
 {
        char git_command[PATH_MAX + 1];
-       int len, err, i;
+       int len,  i;
        const char *paths[] = { current_exec_path,
                                getenv("GIT_EXEC_PATH"),
                                builtin_exec_path };
@@ -85,8 +85,6 @@ int execv_git_cmd(const char **argv)
                /* execve() can only ever return if it fails */
                execve(git_command, (char **)argv, environ);
 
-               err = errno;
-
                argv[0] = tmp;
        }
        return -1;
index 78ab422e4ef294d8bcffba91bb7e2116e381d98e..b834e79c98af51a9f3ad4927a2d11c999349b0e4 100755 (executable)
@@ -335,5 +335,5 @@ Conflicts:
        then
                git-rerere
        fi
-       die "Automatic merge failed; fix up by hand"
+       die "Automatic merge failed; fix conflicts and then commit the result."
 fi
index a5d349fd099a8d2a411d1b71ef8a6ff5d368ba32..e0c9f323c3ba8a4f42ec5a4a4fa53a8bddb36b34 100755 (executable)
@@ -5,9 +5,9 @@
 
 USAGE='[-a] [-d] [-f] [-l] [-n] [-q]'
 . git-sh-setup
-       
+
 no_update_info= all_into_one= remove_redundant=
-local= quiet= no_reuse_delta=
+local= quiet= no_reuse_delta= extra=
 while case "$#" in 0) break ;; esac
 do
        case "$1" in
@@ -17,6 +17,8 @@ do
        -q)     quiet=-q ;;
        -f)     no_reuse_delta=--no-reuse-delta ;;
        -l)     local=--local ;;
+       --window=*) extra="$extra $1" ;;
+       --depth=*) extra="$extra $1" ;;
        *)      usage ;;
        esac
        shift
@@ -40,7 +42,7 @@ case ",$all_into_one," in
            find . -type f \( -name '*.pack' -o -name '*.idx' \) -print`
        ;;
 esac
-pack_objects="$pack_objects $local $quiet $no_reuse_delta"
+pack_objects="$pack_objects $local $quiet $no_reuse_delta$extra"
 name=$(git-rev-list --objects --all $rev_list 2>&1 |
        git-pack-objects --non-empty $pack_objects .tmp-pack) ||
        exit 1
index 4d5371ca901cb7579e454443958e8ca360763dbe..60ed7ae3ee450878d8ef6dc3bf1f60670d1078d3 100755 (executable)
@@ -98,6 +98,7 @@ package SVNconn;
 use File::Spec;
 use File::Temp qw(tempfile);
 use POSIX qw(strftime dup2);
+use Fcntl qw(SEEK_SET);
 
 sub new {
        my($what,$repo) = @_;
@@ -143,9 +144,22 @@ sub file {
        }
        my $mode;
        if (exists $properties->{'svn:executable'}) {
-               $mode = '0755';
+               $mode = '100755';
+       } elsif (exists $properties->{'svn:special'}) {
+               my ($special_content, $filesize);
+               $filesize = tell $fh;
+               seek $fh, 0, SEEK_SET;
+               read $fh, $special_content, $filesize;
+               if ($special_content =~ s/^link //) {
+                       $mode = '120000';
+                       seek $fh, 0, SEEK_SET;
+                       truncate $fh, 0;
+                       print $fh $special_content;
+               } else {
+                       die "unexpected svn:special file encountered";
+               }
        } else {
-               $mode = '0644';
+               $mode = '100644';
        }
        close ($fh);
 
diff --git a/git.c b/git.c
index 78ed403ed121a84c3efe3a57fa7c24486d724953..447d86cd571bcc06b06c64a8713c7dd3c3e1a4f5 100644 (file)
--- a/git.c
+++ b/git.c
@@ -276,128 +276,101 @@ static int cmd_help(int argc, const char **argv, char **envp)
        return 0;
 }
 
-#define LOGSIZE (65536)
-
-static int cmd_log(int argc, const char **argv, char **envp)
+static int cmd_format_patch(int argc, const char **argv, char **envp)
 {
-       struct rev_info rev;
        struct commit *commit;
-       char *buf = xmalloc(LOGSIZE);
-       static enum cmit_fmt commit_format = CMIT_FMT_DEFAULT;
-       int abbrev = DEFAULT_ABBREV;
-       int abbrev_commit = 0;
-       const char *commit_prefix = "commit ";
-       struct log_tree_opt opt;
-       int shown = 0;
-       int do_diff = 0;
-       int full_diff = 0;
-
-       init_log_tree_opt(&opt);
+       struct commit **list = NULL;
+       struct rev_info rev;
+       int nr = 0;
+
+       init_revisions(&rev);
+       rev.commit_format = CMIT_FMT_EMAIL;
+       rev.verbose_header = 1;
+       rev.diff = 1;
+       rev.diffopt.with_raw = 0;
+       rev.diffopt.with_stat = 1;
+       rev.combine_merges = 0;
+       rev.ignore_merges = 1;
+       rev.diffopt.output_format = DIFF_FORMAT_PATCH;
        argc = setup_revisions(argc, argv, &rev, "HEAD");
-       while (1 < argc) {
-               const char *arg = argv[1];
-               if (!strncmp(arg, "--pretty", 8)) {
-                       commit_format = get_commit_format(arg + 8);
-                       if (commit_format == CMIT_FMT_ONELINE)
-                               commit_prefix = "";
-               }
-               else if (!strcmp(arg, "--no-abbrev")) {
-                       abbrev = 0;
-               }
-               else if (!strcmp(arg, "--abbrev")) {
-                       abbrev = DEFAULT_ABBREV;
-               }
-               else if (!strcmp(arg, "--abbrev-commit")) {
-                       abbrev_commit = 1;
-               }
-               else if (!strncmp(arg, "--abbrev=", 9)) {
-                       abbrev = strtoul(arg + 9, NULL, 10);
-                       if (abbrev && abbrev < MINIMUM_ABBREV)
-                               abbrev = MINIMUM_ABBREV;
-                       else if (40 < abbrev)
-                               abbrev = 40;
-               }
-               else if (!strcmp(arg, "--full-diff")) {
-                       do_diff = 1;
-                       full_diff = 1;
-               }
-               else {
-                       int cnt = log_tree_opt_parse(&opt, argv+1, argc-1);
-                       if (0 < cnt) {
-                               do_diff = 1;
-                               argv += cnt;
-                               argc -= cnt;
-                               continue;
-                       }
-                       die("unrecognized argument: %s", arg);
-               }
 
-               argc--; argv++;
+       prepare_revision_walk(&rev);
+       while ((commit = get_revision(&rev)) != NULL) {
+               nr++;
+               list = realloc(list, nr * sizeof(list[0]));
+               list[nr - 1] = commit;
        }
-
-       if (do_diff) {
-               opt.diffopt.abbrev = abbrev;
-               opt.verbose_header = 0;
-               opt.always_show_header = 0;
-               opt.no_commit_id = 1;
-               if (opt.combine_merges)
-                       opt.ignore_merges = 0;
-               if (opt.dense_combined_merges)
-                       opt.diffopt.output_format = DIFF_FORMAT_PATCH;
-               if (opt.diffopt.output_format == DIFF_FORMAT_PATCH)
-                       opt.diffopt.recursive = 1;
-               if (!full_diff && rev.prune_data)
-                       diff_tree_setup_paths(rev.prune_data, &opt.diffopt);
-               diff_setup_done(&opt.diffopt);
+       while (0 <= --nr) {
+               int shown;
+               commit = list[nr];
+               shown = log_tree_commit(&rev, commit);
+               free(commit->buffer);
+               commit->buffer = NULL;
+               if (shown)
+                       printf("-- \n%s\n\n", GIT_VERSION);
        }
+       free(list);
+       return 0;
+}
 
-       prepare_revision_walk(&rev);
+static int cmd_log_wc(int argc, const char **argv, char **envp,
+                     struct rev_info *rev)
+{
+       struct commit *commit;
+
+       rev->abbrev = DEFAULT_ABBREV;
+       rev->commit_format = CMIT_FMT_DEFAULT;
+       rev->verbose_header = 1;
+       argc = setup_revisions(argc, argv, rev, "HEAD");
+
+       if (argc > 1)
+               die("unrecognized argument: %s", argv[1]);
+
+       prepare_revision_walk(rev);
        setup_pager();
-       while ((commit = get_revision(&rev)) != NULL) {
-               if (shown && do_diff && commit_format != CMIT_FMT_ONELINE)
-                       putchar('\n');
-               fputs(commit_prefix, stdout);
-               if (abbrev_commit && abbrev)
-                       fputs(find_unique_abbrev(commit->object.sha1, abbrev),
-                             stdout);
-               else
-                       fputs(sha1_to_hex(commit->object.sha1), stdout);
-               if (rev.parents) {
-                       struct commit_list *parents = commit->parents;
-                       while (parents) {
-                               struct object *o = &(parents->item->object);
-                               parents = parents->next;
-                               if (o->flags & TMP_MARK)
-                                       continue;
-                               printf(" %s", sha1_to_hex(o->sha1));
-                               o->flags |= TMP_MARK;
-                       }
-                       /* TMP_MARK is a general purpose flag that can
-                        * be used locally, but the user should clean
-                        * things up after it is done with them.
-                        */
-                       for (parents = commit->parents;
-                            parents;
-                            parents = parents->next)
-                               parents->item->object.flags &= ~TMP_MARK;
-               }
-               if (commit_format == CMIT_FMT_ONELINE)
-                       putchar(' ');
-               else
-                       putchar('\n');
-               pretty_print_commit(commit_format, commit, ~0, buf,
-                                   LOGSIZE, abbrev);
-               printf("%s\n", buf);
-               if (do_diff)
-                       log_tree_commit(&opt, commit);
-               shown = 1;
+       while ((commit = get_revision(rev)) != NULL) {
+               log_tree_commit(rev, commit);
                free(commit->buffer);
                commit->buffer = NULL;
        }
-       free(buf);
        return 0;
 }
 
+static int cmd_wc(int argc, const char **argv, char **envp)
+{
+       struct rev_info rev;
+
+       init_revisions(&rev);
+       rev.diff = 1;
+       rev.diffopt.recursive = 1;
+       return cmd_log_wc(argc, argv, envp, &rev);
+}
+
+static int cmd_show(int argc, const char **argv, char **envp)
+{
+       struct rev_info rev;
+
+       init_revisions(&rev);
+       rev.diff = 1;
+       rev.diffopt.recursive = 1;
+       rev.combine_merges = 1;
+       rev.dense_combined_merges = 1;
+       rev.always_show_header = 1;
+       rev.ignore_merges = 0;
+       rev.no_walk = 1;
+       return cmd_log_wc(argc, argv, envp, &rev);
+}
+
+static int cmd_log(int argc, const char **argv, char **envp)
+{
+       struct rev_info rev;
+
+       init_revisions(&rev);
+       rev.always_show_header = 1;
+       rev.diffopt.recursive = 1;
+       return cmd_log_wc(argc, argv, envp, &rev);
+}
+
 static void handle_internal_command(int argc, const char **argv, char **envp)
 {
        const char *cmd = argv[0];
@@ -408,9 +381,18 @@ static void handle_internal_command(int argc, const char **argv, char **envp)
                { "version", cmd_version },
                { "help", cmd_help },
                { "log", cmd_log },
+               { "whatchanged", cmd_wc },
+               { "show", cmd_show },
+               { "format-patch", cmd_format_patch },
        };
        int i;
 
+       /* Turn "git cmd --help" into "git help cmd" */
+       if (argc > 1 && !strcmp(argv[1], "--help")) {
+               argv[1] = argv[0];
+               argv[0] = cmd = "help";
+       }
+
        for (i = 0; i < ARRAY_SIZE(commands); i++) {
                struct cmd_struct *p = commands+i;
                if (strcmp(p->cmd, cmd))
diff --git a/gitk b/gitk
index f88c06e565ce34cd3985ea22d35e33735ed4f0fa..87e71629afd4b2eca8d1c768bea0a20815405b2b 100755 (executable)
--- a/gitk
+++ b/gitk
@@ -1116,11 +1116,12 @@ proc layoutrows {row endrow last} {
 
 proc addextraid {id row} {
     global displayorder commitrow commitinfo
-    global commitidx
+    global commitidx commitlisted
     global parentlist childlist children
 
     incr commitidx
     lappend displayorder $id
+    lappend commitlisted 0
     lappend parentlist {}
     set commitrow($id) $row
     readcommit $id
@@ -1500,7 +1501,7 @@ proc drawcmittext {id row col rmx} {
 proc drawcmitrow {row} {
     global displayorder rowidlist
     global idrowranges idrangedrawn iddrawn
-    global commitinfo commitlisted parentlist numcommits
+    global commitinfo parentlist numcommits
 
     if {$row >= $numcommits} return
     foreach id [lindex $rowidlist $row] {
diff --git a/gsimm.c b/gsimm.c
index bd646eb3d245460cd126327b39ca464b041f7a41..7024bf8f58e219a9b42a6b5e5217a843b6d75130 100644 (file)
--- a/gsimm.c
+++ b/gsimm.c
@@ -1,4 +1,3 @@
-#include <string.h>
 #include "rabinpoly.h"
 #include "gsimm.h"
 
@@ -33,29 +32,6 @@ static void freq_to_md(u_char *md, int *freq)
   bzero (freq, sizeof(freq[0]) * MD_BITS);
 }
 
-static int dist (u_char *l, u_char *r)
-{ int j, k;
-  int d = 0;
-
-  for (j = 0; j < MD_LENGTH; j++)
-  { u_char ch = l[j] ^ r[j];
-
-    for (k = 0; k < 8; k++) d += ((ch & (1<<k)) > 0);
-  }
-
-  return d;
-}
-
-double gb_simm_score(u_char *l, u_char *r)
-{
-       int d = dist(l, r);
-       double sim = (double) (d) / (MD_LENGTH * 4 - 1);
-       if (1.0 < sim)
-               return 0;
-       else
-               return 1.0 - sim;
-}
-
 void gb_simm_process(u_char *data, unsigned len, u_char *md)
 { size_t j = 0;
   u_int32_t ofs;
@@ -63,11 +39,6 @@ void gb_simm_process(u_char *data, unsigned len, u_char *md)
   u_int32_t count [MD_BITS * (GROUP_COUNTERS/GROUP_BITS)];
   int freq[MD_BITS];
 
-  if (len < GB_SIMM_MIN_FILE_SIZE || GB_SIMM_MAX_FILE_SIZE < len) {
-         memset(md, 0, MD_LENGTH);
-         return;
-  }
-
   bzero (freq, sizeof(freq[0]) * MD_BITS);
   bzero (dup_cache, DUP_CACHE_SIZE * sizeof (u_int32_t));
   bzero (count, (MD_BITS * (GROUP_COUNTERS/GROUP_BITS) * sizeof (u_int32_t)));
diff --git a/gsimm.h b/gsimm.h
index 17fab32d8707d00137054a9c2993443f7f65bf7b..4b023b91a93f8e1c6bd7c232f89553a208cc4a68 100644 (file)
--- a/gsimm.h
+++ b/gsimm.h
    In order to get at least an average of 12 samples
    per bit in the final message digest, require at least 3 * MD_LENGTH
    complete windows in the file.  */
-#define GB_SIMM_MIN_FILE_SIZE (3 * MD_LENGTH + 2 * (RABIN_WINDOW_SIZE - 1))
+#define MIN_FILE_SIZE (3 * MD_LENGTH + 2 * (RABIN_WINDOW_SIZE - 1))
 
 /* Limit matching algorithm to files less than 256 MB, so we can use
    32 bit integers everywhere without fear of overflow. For larger
    files we should add logic to mmap the file by piece and accumulate
    the frequency counts. */
-#define GB_SIMM_MAX_FILE_SIZE (256*1024*1024 - 1)
+#define MAX_FILE_SIZE (256*1024*1024 - 1)
 
 void gb_simm_process(u_char *data, unsigned len, u_char *md);
-double gb_simm_score(u_char *l, u_char *r);
 
 #endif
index 19a0f772e7bf729755c66ab38f5dfb64ae784f00..b4327d92438d9596b7286ead06152ad3156b6e2c 100644 (file)
@@ -60,12 +60,12 @@ enum XML_Status {
 #define LOCK_TIME 600
 #define LOCK_REFRESH 30
 
-/* bits #0-6 in revision.h */
+/* bits #0-15 in revision.h */
 
-#define LOCAL    (1u << 7)
-#define REMOTE   (1u << 8)
-#define FETCHING (1u << 9)
-#define PUSHING  (1u << 10)
+#define LOCAL    (1u<<16)
+#define REMOTE   (1u<<17)
+#define FETCHING (1u<<18)
+#define PUSHING  (1u<<19)
 
 /* We allow "recursive" symbolic refs. Only within reason, though */
 #define MAXDEPTH 5
@@ -2498,6 +2498,7 @@ int main(int argc, char **argv)
                        commit_argv[3] = old_sha1_hex;
                        commit_argc++;
                }
+               init_revisions(&revs);
                setup_revisions(commit_argc, commit_argv, &revs, NULL);
                free(new_sha1_hex);
                if (old_sha1_hex) {
index 3d404824a172c34c7ce0224de3b355f6c97eb52f..aaf2b9423f01243584e15e6c93007a3f42c9d917 100644 (file)
@@ -3,59 +3,66 @@
 #include "commit.h"
 #include "log-tree.h"
 
-void init_log_tree_opt(struct log_tree_opt *opt)
+void show_log(struct rev_info *opt, struct log_info *log, const char *sep)
 {
-       memset(opt, 0, sizeof *opt);
-       opt->ignore_merges = 1;
-       opt->header_prefix = "";
-       opt->commit_format = CMIT_FMT_RAW;
-       diff_setup(&opt->diffopt);
-}
-
-int log_tree_opt_parse(struct log_tree_opt *opt, const char **av, int ac)
-{
-       const char *arg;
-       int cnt = diff_opt_parse(&opt->diffopt, av, ac);
-       if (0 < cnt)
-               return cnt;
-       arg = *av;
-       if (!strcmp(arg, "-r"))
-               opt->diffopt.recursive = 1;
-       else if (!strcmp(arg, "-t")) {
-               opt->diffopt.recursive = 1;
-               opt->diffopt.tree_in_recursive = 1;
-       }
-       else if (!strcmp(arg, "-m"))
-               opt->ignore_merges = 0;
-       else if (!strcmp(arg, "-c"))
-               opt->combine_merges = 1;
-       else if (!strcmp(arg, "--cc")) {
-               opt->dense_combined_merges = 1;
-               opt->combine_merges = 1;
-       }
-       else if (!strcmp(arg, "-v")) {
-               opt->verbose_header = 1;
-               opt->header_prefix = "diff-tree ";
+       static char this_header[16384];
+       struct commit *commit = log->commit, *parent = log->parent;
+       int abbrev = opt->diffopt.abbrev;
+       int abbrev_commit = opt->abbrev_commit ? opt->abbrev : 40;
+       const char *extra;
+       int len;
+
+       opt->loginfo = NULL;
+       if (!opt->verbose_header) {
+               puts(sha1_to_hex(commit->object.sha1));
+               return;
        }
-       else if (!strncmp(arg, "--pretty", 8)) {
-               opt->verbose_header = 1;
-               opt->header_prefix = "diff-tree ";
-               opt->commit_format = get_commit_format(arg+8);
+
+       /*
+        * The "oneline" format has several special cases:
+        *  - The pretty-printed commit lacks a newline at the end
+        *    of the buffer, but we do want to make sure that we
+        *    have a newline there. If the separator isn't already
+        *    a newline, add an extra one.
+        *  - unlike other log messages, the one-line format does
+        *    not have an empty line between entries.
+        */
+       extra = "";
+       if (*sep != '\n' && opt->commit_format == CMIT_FMT_ONELINE)
+               extra = "\n";
+       if (opt->shown_one && opt->commit_format != CMIT_FMT_ONELINE)
+               putchar('\n');
+       opt->shown_one = 1;
+
+       /*
+        * Print header line of header..
+        */
+
+       if (opt->commit_format == CMIT_FMT_EMAIL)
+               printf("From %s  Thu Apr 7 15:13:13 2005\n",
+                      sha1_to_hex(commit->object.sha1));
+       else {
+               printf("%s%s",
+                      opt->commit_format == CMIT_FMT_ONELINE ? "" : "commit ",
+                      diff_unique_abbrev(commit->object.sha1, abbrev_commit));
+               if (parent) 
+                       printf(" (from %s)",
+                              diff_unique_abbrev(parent->object.sha1,
+                                                 abbrev_commit));
+               putchar(opt->commit_format == CMIT_FMT_ONELINE ? ' ' : '\n');
        }
-       else if (!strcmp(arg, "--root"))
-               opt->show_root_diff = 1;
-       else if (!strcmp(arg, "--no-commit-id"))
-               opt->no_commit_id = 1;
-       else if (!strcmp(arg, "--always"))
-               opt->always_show_header = 1;
-       else
-               return 0;
-       return 1;
+
+       /*
+        * And then the pretty-printed message itself
+        */
+       len = pretty_print_commit(opt->commit_format, commit, ~0u, this_header, sizeof(this_header), abbrev);
+       printf("%s%s%s", this_header, extra, sep);
 }
 
-int log_tree_diff_flush(struct log_tree_opt *opt)
+int log_tree_diff_flush(struct rev_info *opt)
 {
        diffcore_std(&opt->diffopt);
+
        if (diff_queue_is_empty()) {
                int saved_fmt = opt->diffopt.output_format;
                opt->diffopt.output_format = DIFF_FORMAT_NO_OUTPUT;
@@ -63,17 +70,14 @@ int log_tree_diff_flush(struct log_tree_opt *opt)
                opt->diffopt.output_format = saved_fmt;
                return 0;
        }
-       if (opt->header) {
-               if (!opt->no_commit_id)
-                       printf("%s%c", opt->header,
-                              opt->diffopt.line_termination);
-               opt->header = NULL;
-       }
+
+       if (opt->loginfo && !opt->no_commit_id)
+               show_log(opt, opt->loginfo, opt->diffopt.with_stat ? "---\n" : "\n");
        diff_flush(&opt->diffopt);
        return 1;
 }
 
-static int diff_root_tree(struct log_tree_opt *opt,
+static int diff_root_tree(struct rev_info *opt,
                          const unsigned char *new, const char *base)
 {
        int retval;
@@ -93,83 +97,81 @@ static int diff_root_tree(struct log_tree_opt *opt,
        return retval;
 }
 
-static const char *generate_header(struct log_tree_opt *opt,
-                                  const unsigned char *commit_sha1,
-                                  const unsigned char *parent_sha1,
-                                  const struct commit *commit)
-{
-       static char this_header[16384];
-       int offset;
-       unsigned long len;
-       int abbrev = opt->diffopt.abbrev;
-       const char *msg = commit->buffer;
-
-       if (!opt->verbose_header)
-               return sha1_to_hex(commit_sha1);
-
-       len = strlen(msg);
-
-       offset = sprintf(this_header, "%s%s ",
-                        opt->header_prefix,
-                        diff_unique_abbrev(commit_sha1, abbrev));
-       if (commit_sha1 != parent_sha1)
-               offset += sprintf(this_header + offset, "(from %s)\n",
-                                 parent_sha1
-                                 ? diff_unique_abbrev(parent_sha1, abbrev)
-                                 : "root");
-       else
-               offset += sprintf(this_header + offset, "(from parents)\n");
-       offset += pretty_print_commit(opt->commit_format, commit, len,
-                                     this_header + offset,
-                                     sizeof(this_header) - offset, abbrev);
-       if (opt->always_show_header) {
-               puts(this_header);
-               return NULL;
-       }
-       return this_header;
-}
-
-static int do_diff_combined(struct log_tree_opt *opt, struct commit *commit)
+static int do_diff_combined(struct rev_info *opt, struct commit *commit)
 {
        unsigned const char *sha1 = commit->object.sha1;
 
-       opt->header = generate_header(opt, sha1, sha1, commit);
-       opt->header = diff_tree_combined_merge(sha1, opt->header,
-                                               opt->dense_combined_merges,
-                                               &opt->diffopt);
-       if (!opt->header && opt->verbose_header)
-               opt->header_prefix = "\ndiff-tree ";
-       return 0;
+       diff_tree_combined_merge(sha1, opt->dense_combined_merges, opt);
+       return !opt->loginfo;
 }
 
-int log_tree_commit(struct log_tree_opt *opt, struct commit *commit)
+/*
+ * Show the diff of a commit.
+ *
+ * Return true if we printed any log info messages
+ */
+static int log_tree_diff(struct rev_info *opt, struct commit *commit, struct log_info *log)
 {
+       int showed_log;
        struct commit_list *parents;
        unsigned const char *sha1 = commit->object.sha1;
 
+       if (!opt->diff)
+               return 0;
+
        /* Root commit? */
-       if (opt->show_root_diff && !commit->parents) {
-               opt->header = generate_header(opt, sha1, NULL, commit);
-               diff_root_tree(opt, sha1, "");
+       parents = commit->parents;
+       if (!parents) {
+               if (opt->show_root_diff)
+                       diff_root_tree(opt, sha1, "");
+               return !opt->loginfo;
        }
 
        /* More than one parent? */
-       if (commit->parents && commit->parents->next) {
+       if (parents && parents->next) {
                if (opt->ignore_merges)
                        return 0;
                else if (opt->combine_merges)
                        return do_diff_combined(opt, commit);
+
+               /* If we show individual diffs, show the parent info */
+               log->parent = parents->item;
        }
 
-       for (parents = commit->parents; parents; parents = parents->next) {
+       showed_log = 0;
+       for (;;) {
                struct commit *parent = parents->item;
-               unsigned const char *psha1 = parent->object.sha1;
-               opt->header = generate_header(opt, sha1, psha1, commit);
-               diff_tree_sha1(psha1, sha1, "", &opt->diffopt);
-               log_tree_diff_flush(opt);               
 
-               if (!opt->header && opt->verbose_header)
-                       opt->header_prefix = "\ndiff-tree ";
+               diff_tree_sha1(parent->object.sha1, sha1, "", &opt->diffopt);
+               log_tree_diff_flush(opt);
+
+               showed_log |= !opt->loginfo;
+
+               /* Set up the log info for the next parent, if any.. */
+               parents = parents->next;
+               if (!parents)
+                       break;
+               log->parent = parents->item;
+               opt->loginfo = log;
+       }
+       return showed_log;
+}
+
+int log_tree_commit(struct rev_info *opt, struct commit *commit)
+{
+       struct log_info log;
+       int shown;
+
+       log.commit = commit;
+       log.parent = NULL;
+       opt->loginfo = &log;
+
+       shown = log_tree_diff(opt, commit, &log);
+       if (!shown && opt->loginfo && opt->always_show_header) {
+               log.parent = NULL;
+               show_log(opt, opt->loginfo, "");
+               shown = 1;
        }
-       return 0;
+       opt->loginfo = NULL;
+       return shown;
 }
index da166c6f2c7b3a0fae80fbe63c8cde38eb609054..a26e4841ff9f568f8536f286e3fc40f09b9dff3b 100644 (file)
@@ -1,23 +1,16 @@
 #ifndef LOG_TREE_H
 #define LOG_TREE_H
 
-struct log_tree_opt {
-       struct diff_options diffopt;
-       int show_root_diff;
-       int no_commit_id;
-       int verbose_header;
-       int ignore_merges;
-       int combine_merges;
-       int dense_combined_merges;
-       int always_show_header;
-       const char *header_prefix;
-       const char *header;
-       enum cmit_fmt commit_format;
+#include "revision.h"
+
+struct log_info {
+       struct commit *commit, *parent;
 };
 
-void init_log_tree_opt(struct log_tree_opt *);
-int log_tree_diff_flush(struct log_tree_opt *);
-int log_tree_commit(struct log_tree_opt *, struct commit *);
-int log_tree_opt_parse(struct log_tree_opt *, const char **, int);
+void init_log_tree_opt(struct rev_info *);
+int log_tree_diff_flush(struct rev_info *);
+int log_tree_commit(struct rev_info *, struct commit *);
+int log_tree_opt_parse(struct rev_info *, const char **, int);
+void show_log(struct rev_info *opt, struct log_info *log, const char *sep);
 
 #endif
index 18f8f82571f82e9618b87797065be36b7087ce74..09f4f2c944d95bb145823c0b07f4a7f302bcab1f 100644 (file)
@@ -8,8 +8,6 @@
 #include "pack.h"
 #include "csum-file.h"
 #include "tree-walk.h"
-#include "rabinpoly.h"
-#include "gsimm.h"
 #include <sys/time.h>
 #include <signal.h>
 
@@ -995,7 +993,6 @@ static int type_size_sort(const struct object_entry *a, const struct object_entr
 
 struct unpacked {
        struct object_entry *entry;
-       unsigned char fingerprint[MD_LENGTH];
        void *data;
 };
 
@@ -1044,9 +1041,6 @@ static int try_delta(struct unpacked *cur, struct unpacked *old, unsigned max_de
        if (old_entry->depth >= max_depth)
                return 0;
 
-       if (gb_simm_score(cur->fingerprint, old->fingerprint) < 0.4)
-               return 0;
-
        /*
         * NOTE!
         *
@@ -1083,7 +1077,6 @@ static void find_deltas(struct object_entry **list, int window, int depth)
        unsigned processed = 0;
        unsigned last_percent = 999;
 
-       rabin_reset ();
        memset(array, 0, array_size);
        i = nr_objects;
        idx = 0;
@@ -1122,8 +1115,6 @@ static void find_deltas(struct object_entry **list, int window, int depth)
                if (size != entry->size)
                        die("object %s inconsistent object length (%lu vs %lu)", sha1_to_hex(entry->sha1), size, entry->size);
 
-               gb_simm_process(n->data, size, n->fingerprint);
-
                j = window;
                while (--j > 0) {
                        unsigned int other_idx = idx + j;
@@ -1133,7 +1124,6 @@ static void find_deltas(struct object_entry **list, int window, int depth)
                        m = array + other_idx;
                        if (!m->entry)
                                break;
-
                        if (try_delta(n, m, depth) < 0)
                                break;
                }
diff --git a/pager.c b/pager.c
index 1364e15d23527c0c4a11bcbe18c80c32af6d94b4..b063353d96eb6d526438c1fa22cacd28bc12b0c1 100644 (file)
--- a/pager.c
+++ b/pager.c
@@ -5,22 +5,24 @@
  * something different on Windows, for example.
  */
 
-static void run_pager(void)
+static void run_pager(const char *pager)
 {
-       const char *prog = getenv("PAGER");
-       if (!prog)
-               prog = "less";
-       setenv("LESS", "-S", 0);
-       execlp(prog, prog, NULL);
+       execlp(pager, pager, NULL);
 }
 
 void setup_pager(void)
 {
        pid_t pid;
        int fd[2];
+       const char *pager = getenv("PAGER");
 
        if (!isatty(1))
                return;
+       if (!pager)
+               pager = "less";
+       else if (!*pager || !strcmp(pager, "cat"))
+               return;
+
        if (pipe(fd) < 0)
                return;
        pid = fork();
@@ -43,6 +45,7 @@ void setup_pager(void)
        close(fd[0]);
        close(fd[1]);
 
-       run_pager();
+       setenv("LESS", "-S", 0);
+       run_pager(pager);
        exit(255);
 }
diff --git a/quote.c b/quote.c
index 7218a7080d9a4282530b58c013606c86e4a5fdf5..06792d47c3e324ee9893a4e52035f0642f38005f 100644 (file)
--- a/quote.c
+++ b/quote.c
@@ -144,8 +144,6 @@ static int quote_c_style_counted(const char *name, int namelen,
 
                        case '\\': /* fallthru */
                        case '"': EMITQ(); break;
-                       case ' ':
-                               break;
                        default:
                                /* octal */
                                EMITQ();
index 963707a495092405c3eb5b155c195b2968afdac7..8b0ec388fa0afe5ffb28c94f2e75544612ab7265 100644 (file)
@@ -8,9 +8,9 @@
 #include "diff.h"
 #include "revision.h"
 
-/* bits #0-6 in revision.h */
+/* bits #0-15 in revision.h */
 
-#define COUNTED                (1u<<7)
+#define COUNTED                (1u<<16)
 
 static const char rev_list_usage[] =
 "git-rev-list [OPTION] <commit-id>... [ -- paths... ]\n"
@@ -39,24 +39,21 @@ static const char rev_list_usage[] =
 struct rev_info revs;
 
 static int bisect_list = 0;
-static int verbose_header = 0;
-static int abbrev = DEFAULT_ABBREV;
-static int abbrev_commit = 0;
 static int show_timestamp = 0;
 static int hdr_termination = 0;
-static const char *commit_prefix = "";
-static enum cmit_fmt commit_format = CMIT_FMT_RAW;
+static const char *header_prefix;
 
 static void show_commit(struct commit *commit)
 {
        if (show_timestamp)
                printf("%lu ", commit->date);
-       if (commit_prefix[0])
-               fputs(commit_prefix, stdout);
+       if (header_prefix)
+               fputs(header_prefix, stdout);
        if (commit->object.flags & BOUNDARY)
                putchar('-');
-       if (abbrev_commit && abbrev)
-               fputs(find_unique_abbrev(commit->object.sha1, abbrev), stdout);
+       if (revs.abbrev_commit && revs.abbrev)
+               fputs(find_unique_abbrev(commit->object.sha1, revs.abbrev),
+                     stdout);
        else
                fputs(sha1_to_hex(commit->object.sha1), stdout);
        if (revs.parents) {
@@ -78,14 +75,16 @@ static void show_commit(struct commit *commit)
                     parents = parents->next)
                        parents->item->object.flags &= ~TMP_MARK;
        }
-       if (commit_format == CMIT_FMT_ONELINE)
+       if (revs.commit_format == CMIT_FMT_ONELINE)
                putchar(' ');
        else
                putchar('\n');
 
-       if (verbose_header) {
+       if (revs.verbose_header) {
                static char pretty_header[16384];
-               pretty_print_commit(commit_format, commit, ~0, pretty_header, sizeof(pretty_header), abbrev);
+               pretty_print_commit(revs.commit_format, commit, ~0,
+                                   pretty_header, sizeof(pretty_header),
+                                   revs.abbrev);
                printf("%s%c", pretty_header, hdr_termination);
        }
        fflush(stdout);
@@ -297,58 +296,16 @@ int main(int argc, const char **argv)
        struct commit_list *list;
        int i;
 
+       init_revisions(&revs);
+       revs.abbrev = 0;
+       revs.commit_format = CMIT_FMT_UNSPECIFIED;
        argc = setup_revisions(argc, argv, &revs, NULL);
 
        for (i = 1 ; i < argc; i++) {
                const char *arg = argv[i];
 
-               /* accept -<digit>, like traditilnal "head" */
-               if ((*arg == '-') && isdigit(arg[1])) {
-                       revs.max_count = atoi(arg + 1);
-                       continue;
-               }
-               if (!strcmp(arg, "-n")) {
-                       if (++i >= argc)
-                               die("-n requires an argument");
-                       revs.max_count = atoi(argv[i]);
-                       continue;
-               }
-               if (!strncmp(arg,"-n",2)) {
-                       revs.max_count = atoi(arg + 2);
-                       continue;
-               }
                if (!strcmp(arg, "--header")) {
-                       verbose_header = 1;
-                       continue;
-               }
-               if (!strcmp(arg, "--no-abbrev")) {
-                       abbrev = 0;
-                       continue;
-               }
-               if (!strcmp(arg, "--abbrev")) {
-                       abbrev = DEFAULT_ABBREV;
-                       continue;
-               }
-               if (!strcmp(arg, "--abbrev-commit")) {
-                       abbrev_commit = 1;
-                       continue;
-               }
-               if (!strncmp(arg, "--abbrev=", 9)) {
-                       abbrev = strtoul(arg + 9, NULL, 10);
-                       if (abbrev && abbrev < MINIMUM_ABBREV)
-                               abbrev = MINIMUM_ABBREV;
-                       else if (40 < abbrev)
-                               abbrev = 40;
-                       continue;
-               }
-               if (!strncmp(arg, "--pretty", 8)) {
-                       commit_format = get_commit_format(arg+8);
-                       verbose_header = 1;
-                       hdr_termination = '\n';
-                       if (commit_format == CMIT_FMT_ONELINE)
-                               commit_prefix = "";
-                       else
-                               commit_prefix = "commit ";
+                       revs.verbose_header = 1;
                        continue;
                }
                if (!strcmp(arg, "--timestamp")) {
@@ -362,15 +319,30 @@ int main(int argc, const char **argv)
                usage(rev_list_usage);
 
        }
+       if (revs.commit_format != CMIT_FMT_UNSPECIFIED) {
+               /* The command line has a --pretty  */
+               hdr_termination = '\n';
+               if (revs.commit_format == CMIT_FMT_ONELINE)
+                       header_prefix = "";
+               else
+                       header_prefix = "commit ";
+       }
+       else if (revs.verbose_header)
+               /* Only --header was specified */
+               revs.commit_format = CMIT_FMT_RAW;
 
        list = revs.commits;
 
-       if (!list &&
-           (!(revs.tag_objects||revs.tree_objects||revs.blob_objects) && !revs.pending_objects))
+       if ((!list &&
+            (!(revs.tag_objects||revs.tree_objects||revs.blob_objects) &&
+             !revs.pending_objects)) ||
+           revs.diff)
                usage(rev_list_usage);
 
-       save_commit_buffer = verbose_header;
+       save_commit_buffer = revs.verbose_header;
        track_object_refs = 0;
+       if (bisect_list)
+               revs.limited = 1;
 
        prepare_revision_walk(&revs);
        if (revs.tree_objects)
index 0505f3f455151ac2fafc7fe2df2e6fd147d20f08..113dd5a89fc03ff17b04443f2ac7148b4c36af33 100644 (file)
@@ -116,21 +116,27 @@ static void add_pending_object(struct rev_info *revs, struct object *obj, const
        add_object(obj, &revs->pending_objects, NULL, name);
 }
 
-static struct commit *get_commit_reference(struct rev_info *revs, const char *name, const unsigned char *sha1, unsigned int flags)
+static struct object *get_reference(struct rev_info *revs, const char *name, const unsigned char *sha1, unsigned int flags)
 {
        struct object *object;
 
        object = parse_object(sha1);
        if (!object)
                die("bad object %s", name);
+       object->flags |= flags;
+       return object;
+}
+
+static struct commit *handle_commit(struct rev_info *revs, struct object *object, const char *name)
+{
+       unsigned long flags = object->flags;
 
        /*
         * Tag object? Look what it points to..
         */
        while (object->type == tag_type) {
                struct tag *tag = (struct tag *) object;
-               object->flags |= flags;
-               if (revs->tag_objects && !(object->flags & UNINTERESTING))
+               if (revs->tag_objects && !(flags & UNINTERESTING))
                        add_pending_object(revs, object, tag->tag);
                object = parse_object(tag->tagged->sha1);
                if (!object)
@@ -143,10 +149,10 @@ static struct commit *get_commit_reference(struct rev_info *revs, const char *na
         */
        if (object->type == commit_type) {
                struct commit *commit = (struct commit *)object;
-               object->flags |= flags;
                if (parse_commit(commit) < 0)
                        die("unable to parse commit %s", name);
                if (flags & UNINTERESTING) {
+                       commit->object.flags |= UNINTERESTING;
                        mark_parents_uninteresting(commit);
                        revs->limited = 1;
                }
@@ -241,7 +247,7 @@ int rev_compare_tree(struct rev_info *revs, struct tree *t1, struct tree *t2)
                return REV_TREE_DIFFERENT;
        tree_difference = REV_TREE_SAME;
        if (diff_tree_sha1(t1->object.sha1, t2->object.sha1, "",
-                          &revs->diffopt) < 0)
+                          &revs->pruning) < 0)
                return REV_TREE_DIFFERENT;
        return tree_difference;
 }
@@ -264,7 +270,7 @@ int rev_same_tree_as_empty(struct rev_info *revs, struct tree *t1)
        empty.size = 0;
 
        tree_difference = 0;
-       retval = diff_tree(&empty, &real, "", &revs->diffopt);
+       retval = diff_tree(&empty, &real, "", &revs->pruning);
        free(tree);
 
        return retval >= 0 && !tree_difference;
@@ -375,6 +381,9 @@ static void add_parents_to_list(struct rev_info *revs, struct commit *commit, st
        if (revs->prune_fn)
                revs->prune_fn(revs, commit);
 
+       if (revs->no_walk)
+               return;
+
        parent = commit->parents;
        while (parent) {
                struct commit *p = parent->item;
@@ -451,21 +460,13 @@ static void limit_list(struct rev_info *revs)
        revs->commits = newlist;
 }
 
-static void add_one_commit(struct commit *commit, struct rev_info *revs)
-{
-       if (!commit || (commit->object.flags & SEEN))
-               return;
-       commit->object.flags |= SEEN;
-       commit_list_insert(commit, &revs->commits);
-}
-
 static int all_flags;
 static struct rev_info *all_revs;
 
 static int handle_one_ref(const char *path, const unsigned char *sha1)
 {
-       struct commit *commit = get_commit_reference(all_revs, path, sha1, all_flags);
-       add_one_commit(commit, all_revs);
+       struct object *object = get_reference(all_revs, path, sha1, all_flags);
+       add_pending_object(all_revs, object, "");
        return 0;
 }
 
@@ -479,9 +480,12 @@ static void handle_all(struct rev_info *revs, unsigned flags)
 void init_revisions(struct rev_info *revs)
 {
        memset(revs, 0, sizeof(*revs));
-       revs->diffopt.recursive = 1;
-       revs->diffopt.add_remove = file_add_remove;
-       revs->diffopt.change = file_change;
+
+       revs->abbrev = DEFAULT_ABBREV;
+       revs->ignore_merges = 1;
+       revs->pruning.recursive = 1;
+       revs->pruning.add_remove = file_add_remove;
+       revs->pruning.change = file_change;
        revs->lifo = 1;
        revs->dense = 1;
        revs->prefix = setup_git_directory();
@@ -494,6 +498,10 @@ void init_revisions(struct rev_info *revs)
 
        revs->topo_setter = topo_sort_default_setter;
        revs->topo_getter = topo_sort_default_getter;
+
+       revs->commit_format = CMIT_FMT_DEFAULT;
+
+       diff_setup(&revs->diffopt);
 }
 
 /*
@@ -509,8 +517,6 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
        const char **unrecognized = argv + 1;
        int left = 1;
 
-       init_revisions(revs);
-
        /* First, search for "--" */
        seen_dashdash = 0;
        for (i = 1; i < argc; i++) {
@@ -526,13 +532,14 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
 
        flags = 0;
        for (i = 1; i < argc; i++) {
-               struct commit *commit;
+               struct object *object;
                const char *arg = argv[i];
                unsigned char sha1[20];
                char *dotdot;
                int local_flags;
 
                if (*arg == '-') {
+                       int opts;
                        if (!strncmp(arg, "--max-count=", 12)) {
                                revs->max_count = atoi(arg + 12);
                                continue;
@@ -640,6 +647,76 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
                                revs->unpacked = 1;
                                continue;
                        }
+                       if (!strcmp(arg, "-r")) {
+                               revs->diff = 1;
+                               revs->diffopt.recursive = 1;
+                               continue;
+                       }
+                       if (!strcmp(arg, "-t")) {
+                               revs->diff = 1;
+                               revs->diffopt.recursive = 1;
+                               revs->diffopt.tree_in_recursive = 1;
+                               continue;
+                       }
+                       if (!strcmp(arg, "-m")) {
+                               revs->ignore_merges = 0;
+                               continue;
+                       }
+                       if (!strcmp(arg, "-c")) {
+                               revs->diff = 1;
+                               revs->combine_merges = 1;
+                               continue;
+                       }
+                       if (!strcmp(arg, "--cc")) {
+                               revs->diff = 1;
+                               revs->dense_combined_merges = 1;
+                               revs->combine_merges = 1;
+                               continue;
+                       }
+                       if (!strcmp(arg, "-v")) {
+                               revs->verbose_header = 1;
+                               continue;
+                       }
+                       if (!strncmp(arg, "--pretty", 8)) {
+                               revs->verbose_header = 1;
+                               revs->commit_format = get_commit_format(arg+8);
+                               continue;
+                       }
+                       if (!strcmp(arg, "--root")) {
+                               revs->show_root_diff = 1;
+                               continue;
+                       }
+                       if (!strcmp(arg, "--no-commit-id")) {
+                               revs->no_commit_id = 1;
+                               continue;
+                       }
+                       if (!strcmp(arg, "--always")) {
+                               revs->always_show_header = 1;
+                               continue;
+                       }
+                       if (!strcmp(arg, "--no-abbrev")) {
+                               revs->abbrev = 0;
+                               continue;
+                       }
+                       if (!strcmp(arg, "--abbrev")) {
+                               revs->abbrev = DEFAULT_ABBREV;
+                               continue;
+                       }
+                       if (!strcmp(arg, "--abbrev-commit")) {
+                               revs->abbrev_commit = 1;
+                               continue;
+                       }
+                       if (!strcmp(arg, "--full-diff")) {
+                               revs->diff = 1;
+                               revs->full_diff = 1;
+                               continue;
+                       }
+                       opts = diff_opt_parse(&revs->diffopt, argv+i, argc-i);
+                       if (opts > 0) {
+                               revs->diff = 1;
+                               i += opts - 1;
+                               continue;
+                       }
                        *unrecognized++ = arg;
                        left++;
                        continue;
@@ -656,15 +733,15 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
                                this = "HEAD";
                        if (!get_sha1(this, from_sha1) &&
                            !get_sha1(next, sha1)) {
-                               struct commit *exclude;
-                               struct commit *include;
+                               struct object *exclude;
+                               struct object *include;
 
-                               exclude = get_commit_reference(revs, this, from_sha1, flags ^ UNINTERESTING);
-                               include = get_commit_reference(revs, next, sha1, flags);
+                               exclude = get_reference(revs, this, from_sha1, flags ^ UNINTERESTING);
+                               include = get_reference(revs, next, sha1, flags);
                                if (!exclude || !include)
                                        die("Invalid revision range %s..%s", arg, next);
-                               add_one_commit(exclude, revs);
-                               add_one_commit(include, revs);
+                               add_pending_object(revs, exclude, this);
+                               add_pending_object(revs, include, next);
                                continue;
                        }
                        *dotdot = '.';
@@ -689,32 +766,57 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
                        revs->prune_data = get_pathspec(revs->prefix, argv + i);
                        break;
                }
-               commit = get_commit_reference(revs, arg, sha1, flags ^ local_flags);
-               add_one_commit(commit, revs);
+               object = get_reference(revs, arg, sha1, flags ^ local_flags);
+               add_pending_object(revs, object, arg);
        }
-       if (def && !revs->commits) {
+       if (def && !revs->pending_objects) {
                unsigned char sha1[20];
-               struct commit *commit;
+               struct object *object;
                if (get_sha1(def, sha1) < 0)
                        die("bad default revision '%s'", def);
-               commit = get_commit_reference(revs, def, sha1, 0);
-               add_one_commit(commit, revs);
+               object = get_reference(revs, def, sha1, 0);
+               add_pending_object(revs, object, def);
        }
 
        if (revs->topo_order || revs->unpacked)
                revs->limited = 1;
 
        if (revs->prune_data) {
-               diff_tree_setup_paths(revs->prune_data, &revs->diffopt);
+               diff_tree_setup_paths(revs->prune_data, &revs->pruning);
                revs->prune_fn = try_to_simplify_commit;
+               if (!revs->full_diff)
+                       diff_tree_setup_paths(revs->prune_data, &revs->diffopt);
+       }
+       if (revs->combine_merges) {
+               revs->ignore_merges = 0;
+               if (revs->dense_combined_merges)
+                       revs->diffopt.output_format = DIFF_FORMAT_PATCH;
        }
+       revs->diffopt.abbrev = revs->abbrev;
+       diff_setup_done(&revs->diffopt);
 
        return left;
 }
 
 void prepare_revision_walk(struct rev_info *revs)
 {
-       sort_by_date(&revs->commits);
+       struct object_list *list;
+
+       list = revs->pending_objects;
+       revs->pending_objects = NULL;
+       while (list) {
+               struct commit *commit = handle_commit(revs, list->item, list->name);
+               if (commit) {
+                       if (!(commit->object.flags & SEEN)) {
+                               commit->object.flags |= SEEN;
+                               insert_by_date(commit, &revs->commits);
+                       }
+               }
+               list = list->next;
+       }
+
+       if (revs->no_walk)
+               return;
        if (revs->limited)
                limit_list(revs);
        if (revs->topo_order)
@@ -750,6 +852,17 @@ static void rewrite_parents(struct rev_info *revs, struct commit *commit)
        }
 }
 
+static void mark_boundary_to_show(struct commit *commit)
+{
+       struct commit_list *p = commit->parents;
+       while (p) {
+               commit = p->item;
+               p = p->next;
+               if (commit->object.flags & BOUNDARY)
+                       commit->object.flags |= BOUNDARY_SHOW;
+       }
+}
+
 struct commit *get_revision(struct rev_info *revs)
 {
        struct commit_list *list = revs->commits;
@@ -787,8 +900,20 @@ struct commit *get_revision(struct rev_info *revs)
                }
                if (commit->object.flags & SHOWN)
                        continue;
-               if (!(commit->object.flags & BOUNDARY) &&
-                   (commit->object.flags & UNINTERESTING))
+
+               /* We want to show boundary commits only when their
+                * children are shown.  When path-limiter is in effect,
+                * rewrite_parents() drops some commits from getting shown,
+                * and there is no point showing boundary parents that
+                * are not shown.  After rewrite_parents() rewrites the
+                * parents of a commit that is shown, we mark the boundary
+                * parents with BOUNDARY_SHOW.
+                */
+               if (commit->object.flags & BOUNDARY_SHOW) {
+                       commit->object.flags |= SHOWN;
+                       return commit;
+               }
+               if (commit->object.flags & UNINTERESTING)
                        continue;
                if (revs->min_age != -1 && (commit->date > revs->min_age))
                        continue;
@@ -801,6 +926,8 @@ struct commit *get_revision(struct rev_info *revs)
                        if (revs->parents)
                                rewrite_parents(revs, commit);
                }
+               if (revs->boundary)
+                       mark_boundary_to_show(commit);
                commit->object.flags |= SHOWN;
                return commit;
        } while (revs->commits);
index 8970b57e3c7e2ca8d213258f1befb84ac06e3a13..48d7b4ca94f3fd00f7a1f6a3fb57ebed934ffc0d 100644 (file)
@@ -7,9 +7,11 @@
 #define SHOWN          (1u<<3)
 #define TMP_MARK       (1u<<4) /* for isolated cases; clean after use */
 #define BOUNDARY       (1u<<5)
-#define ADDED          (1u<<6) /* Parents already parsed and added? */
+#define BOUNDARY_SHOW  (1u<<6)
+#define ADDED          (1u<<7) /* Parents already parsed and added? */
 
 struct rev_info;
+struct log_info;
 
 typedef void (prune_fn_t)(struct rev_info *revs, struct commit *commit);
 
@@ -26,6 +28,7 @@ struct rev_info {
        /* Traversal flags */
        unsigned int    dense:1,
                        no_merges:1,
+                       no_walk:1,
                        remove_empty_trees:1,
                        lifo:1,
                        topo_order:1,
@@ -38,13 +41,32 @@ struct rev_info {
                        boundary:1,
                        parents:1;
 
+       /* Diff flags */
+       unsigned int    diff:1,
+                       full_diff:1,
+                       show_root_diff:1,
+                       no_commit_id:1,
+                       verbose_header:1,
+                       ignore_merges:1,
+                       combine_merges:1,
+                       dense_combined_merges:1,
+                       always_show_header:1;
+
+       /* Format info */
+       unsigned int    shown_one:1,
+                       abbrev_commit:1;
+       unsigned int    abbrev;
+       enum cmit_fmt   commit_format;
+       struct log_info *loginfo;
+
        /* special limits */
        int max_count;
        unsigned long max_age;
        unsigned long min_age;
 
-       /* paths limiting */
+       /* diff info for patches and for paths limiting */
        struct diff_options diffopt;
+       struct diff_options pruning;
 
        topo_sort_set_fn_t topo_setter;
        topo_sort_get_fn_t topo_getter;
index e3d011309a610ad2b81eeabe810e3844e946734b..f2d33afb27f6c73ad6c177098fe2f2674add7fbe 100644 (file)
@@ -874,17 +874,19 @@ void packed_object_info_detail(struct pack_entry *e,
                               unsigned char *base_sha1)
 {
        struct packed_git *p = e->p;
-       unsigned long offset, left;
+       unsigned long offset;
        unsigned char *pack;
        enum object_type kind;
 
        offset = unpack_object_header(p, e->offset, &kind, size);
        pack = p->pack_base + offset;
-       left = p->pack_size - offset;
        if (kind != OBJ_DELTA)
                *delta_chain_length = 0;
        else {
                unsigned int chain_length = 0;
+               if (p->pack_size <= offset + 20)
+                       die("pack file %s records an incomplete delta base",
+                           p->pack_name);
                memcpy(base_sha1, pack, 20);
                do {
                        struct pack_entry base_ent;
index 4f92e12a8dfce29b5bb50108468259d9f04653f3..345935bb2beddac7b86d5b989ea1492f58a777d8 100644 (file)
@@ -3,6 +3,7 @@
 #include "commit.h"
 #include "tree.h"
 #include "blob.h"
+#include "tree-walk.h"
 
 static int find_short_object_filename(int len, const char *name, unsigned char *sha1)
 {
@@ -455,6 +456,19 @@ static int get_sha1_1(const char *name, int len, unsigned char *sha1)
  */
 int get_sha1(const char *name, unsigned char *sha1)
 {
+       int ret;
+       unsigned unused;
+
        prepare_alt_odb();
-       return get_sha1_1(name, strlen(name), sha1);
+       ret = get_sha1_1(name, strlen(name), sha1);
+       if (ret < 0) {
+               const char *cp = strchr(name, ':');
+               if (cp) {
+                       unsigned char tree_sha1[20];
+                       if (!get_sha1_1(name, cp-name, tree_sha1))
+                               return get_tree_entry(tree_sha1, cp+1, sha1,
+                                                     &unused);
+               }
+       }
+       return ret;
 }
index fe65f53c5fbcf07bb69214b0f0cff8aef551e906..549598575b9fdcefe857ddef1b6d980a9773b033 100644 (file)
@@ -25,5 +25,5 @@ clean:
        rm -fr trash
 
 .PHONY: $(T) clean
-.NOPARALLEL:
+.NOTPARALLEL:
 
index 10024133e3e69af0d14200d23c00d1ba7d1dbf74..16b3ea915776e976890dfc5a9a9748c92de2d3fb 100755 (executable)
@@ -49,7 +49,7 @@ test_expect_success 'git diff HEAD' 'cmp diff.expect diff.output'
 #test_expect_success 'git-read-tree --reset HEAD' "git-read-tree --reset HEAD ; test \"hello: needs update\" = \"$(git-update-index --refresh)\""
 
 cat > whatchanged.expect << EOF
-diff-tree VARIABLE (from root)
+commit VARIABLE
 Author: VARIABLE
 Date:   VARIABLE
 
@@ -72,7 +72,7 @@ index 0000000..557db03
 EOF
 
 git-whatchanged -p --root | \
-       sed -e "1s/^\(.\{10\}\).\{40\}/\1VARIABLE/" \
+       sed -e "1s/^\(.\{7\}\).\{40\}/\1VARIABLE/" \
                -e "2,3s/^\(.\{8\}\).*$/\1VARIABLE/" \
 > whatchanged.output
 test_expect_success 'git-whatchanged -p --root' 'cmp whatchanged.expect whatchanged.output'
index 43d3b6ef4a0e515cdc8d9874a81029d4d9425f64..723a9ef210bb84c217026c2cdf9d30661f64447b 100644 (file)
@@ -61,6 +61,9 @@ perl -e '
            if (/^\s*   /) {
                bad_line("indent SP followed by a TAB", $_);
            }
+           if (/^(?:[<>=]){7}/) {
+               bad_line("unresolved merge conflict", $_);
+           }
        }
     }
     exit($found_bad);
index b1e7939b6573027f39c5a86295c03d863ca01676..bd28b7da28e4456ca7fea883144e2ebbfb7b6625 100644 (file)
@@ -58,6 +58,19 @@ void usage()
    exit (1);
 }
 
+int dist (u_char *l, u_char *r)
+{ int j, k;
+  int d = 0;
+
+  for (j = 0; j < MD_LENGTH; j++)
+  { u_char ch = l[j] ^ r[j];
+
+    for (k = 0; k < 8; k++) d += ((ch & (1<<k)) > 0);
+  }
+
+  return d;
+}
+
 char *md_to_str(u_char *md)
 { int j;
 
@@ -89,8 +102,8 @@ void process_file (char *name)
     exit (2);
   }
 
-  if (fs.st_size >= GB_SIMM_MIN_FILE_SIZE
-      && fs.st_size <= GB_SIMM_MAX_FILE_SIZE)
+  if (fs.st_size >= MIN_FILE_SIZE
+      && fs.st_size <= MAX_FILE_SIZE)
   { fi->length = fs.st_size;
     fi->name = name;
 
@@ -103,11 +116,13 @@ void process_file (char *name)
 
     gb_simm_process (data, fs.st_size, fi->md);
     if (flag_relative)
-           fprintf (stdout, "%s %llu %u %s %u %3.1f\n",
-                    md_to_str (fi->md), (long long unsigned) 0,
-                    (unsigned) fs.st_size, name,
-                    (unsigned) 0,
-                    100.0 * gb_simm_score(fi->md, relative_md));
+    { int d = dist (fi->md, relative_md);
+      double sim = 1.0 - MIN (1.0, (double) (d) / (MD_LENGTH * 4 - 1));
+      fprintf (stdout, "%s %llu %u %s %u %3.1f\n",
+               md_to_str (fi->md), (long long unsigned) 0,
+              (unsigned) fs.st_size, name,
+               d, 100.0 * sim);
+    }
     else
     {
       fprintf (stdout, "%s %llu %u %s\n",
index bf8bfdfdf8564d10b0ca5450dc62612e0ae4525d..9f7abb7cb352241191e395e3967730f3bdcf6d55 100644 (file)
@@ -115,3 +115,53 @@ void traverse_trees(int n, struct tree_desc *t, const char *base, traverse_callb
        free(entry);
 }
 
+static int find_tree_entry(struct tree_desc *t, const char *name, unsigned char *result, unsigned *mode)
+{
+       int namelen = strlen(name);
+       while (t->size) {
+               const char *entry;
+               const unsigned char *sha1;
+               int entrylen, cmp;
+
+               sha1 = tree_entry_extract(t, &entry, mode);
+               update_tree_entry(t);
+               entrylen = strlen(entry);
+               if (entrylen > namelen)
+                       continue;
+               cmp = memcmp(name, entry, entrylen);
+               if (cmp > 0)
+                       continue;
+               if (cmp < 0)
+                       break;
+               if (entrylen == namelen) {
+                       memcpy(result, sha1, 20);
+                       return 0;
+               }
+               if (name[entrylen] != '/')
+                       continue;
+               if (!S_ISDIR(*mode))
+                       break;
+               if (++entrylen == namelen) {
+                       memcpy(result, sha1, 20);
+                       return 0;
+               }
+               return get_tree_entry(sha1, name + entrylen, result, mode);
+       }
+       return -1;
+}
+
+int get_tree_entry(const unsigned char *tree_sha1, const char *name, unsigned char *sha1, unsigned *mode)
+{
+       int retval;
+       void *tree;
+       struct tree_desc t;
+
+       tree = read_object_with_reference(tree_sha1, tree_type, &t.size, NULL);
+       if (!tree)
+               return -1;
+       t.buf = tree;
+       retval = find_tree_entry(&t, name, sha1, mode);
+       free(tree);
+       return retval;
+}
+
index 76893e36c32e11bb62ed50a29198b028f3e03088..47438fe1c08a8e119fd08aea0eac612c76c21a66 100644 (file)
@@ -22,4 +22,6 @@ typedef void (*traverse_callback_t)(int n, unsigned long mask, struct name_entry
 
 void traverse_trees(int n, struct tree_desc *t, const char *base, traverse_callback_t callback);
 
+int get_tree_entry(const unsigned char *, const char *, unsigned char *, unsigned *);
+
 #endif
index 1efac27c6baf26ed881099e91a3cbd5dbb3c2289..64f4c4912ea41944f892d0e1be2bc386dc582c0d 100644 (file)
@@ -6,6 +6,7 @@
 #include "cache.h"
 #include "strbuf.h"
 #include "quote.h"
+#include "tree-walk.h"
 
 /*
  * Default to not allowing changes to the list of files. The
@@ -471,6 +472,124 @@ static void read_index_info(int line_termination)
 static const char update_index_usage[] =
 "git-update-index [-q] [--add] [--replace] [--remove] [--unmerged] [--refresh] [--cacheinfo] [--chmod=(+|-)x] [--info-only] [--force-remove] [--stdin] [--index-info] [--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)) {
+               error("%s: not in %s branch.", path, which);
+               return NULL;
+       }
+       if (mode == S_IFDIR) {
+               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;
+       }
+
+       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)
+{
+       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];
+               err |= unresolve_one(arg);
+       }
+       return err;
+}
+
 int main(int argc, const char **argv)
 {
        int i, newfd, entries, has_errors = 0, line_termination = '\n';
@@ -581,6 +700,12 @@ int main(int argc, const char **argv)
                                read_index_info(line_termination);
                                break;
                        }
+                       if (!strcmp(path, "--unresolve")) {
+                               has_errors = do_unresolve(argc - i, argv + i);
+                               if (has_errors)
+                                       active_cache_changed = 0;
+                               goto finish;
+                       }
                        if (!strcmp(path, "--ignore-missing")) {
                                not_new = 1;
                                continue;
@@ -612,6 +737,8 @@ int main(int argc, const char **argv)
                                free(path_name);
                }
        }
+
+ finish:
        if (active_cache_changed) {
                if (write_cache(newfd, active_cache, active_nr) ||
                    commit_index_file(&cache_file))