Merge branch 'master' into lt/logopt
authorJunio C Hamano <junkio@cox.net>
Sun, 16 Apr 2006 09:31:11 +0000 (02:31 -0700)
committerJunio C Hamano <junkio@cox.net>
Sun, 16 Apr 2006 09:31:11 +0000 (02:31 -0700)
* master:
  pager: do not fork a pager if PAGER is set to empty.
  diff-options: add --patch-with-stat
  diff-files --stat: do not dump core with unmerged index.
  Support "git cmd --help" syntax
  diff --stat: do not do its own three-dashes.
  diff-tree: typefix.
  GIT v1.3.0-rc4
  xdiff: post-process hunks to make them consistent.

Documentation/diff-options.txt
GIT-VERSION-GEN
diff.c
diff.h
git.c
pager.c
xdiff/xdiffi.c
xdiff/xmacros.h

index 447e522..c183dc9 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
 
index 656f555..e88fe5a 100755 (executable)
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 GVF=GIT-VERSION-FILE
-DEF_VER=v1.3-rc3.GIT
+DEF_VER=v1.3-rc4.GIT
 
 # First try git-describe, then see if there is a version file
 # (included in release tarballs), then default
diff --git a/diff.c b/diff.c
index f1b672d..b54bbfa 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -202,6 +202,8 @@ struct diffstat_t {
        int alloc;
        struct diffstat_file {
                char *name;
+               unsigned is_unmerged:1;
+               unsigned is_binary:1;
                unsigned int added, deleted;
        } **files;
 };
@@ -245,11 +247,11 @@ static void show_stats(struct diffstat_t* data)
        if (data->nr == 0)
                return;
 
-       printf("---\n");
-
        for (i = 0; i < data->nr; i++) {
                struct diffstat_file *file = data->files[i];
 
+               if (file->is_binary || file->is_unmerged)
+                       continue;
                if (max_change < file->added + file->deleted)
                        max_change = file->added + file->deleted;
                len = strlen(file->name);
@@ -294,11 +296,15 @@ static void show_stats(struct diffstat_t* data)
                if (max + len > 70)
                        max = 70 - len;
 
-               if (added < 0) {
-                       /* binary file */
+               if (data->files[i]->is_binary) {
                        printf(" %s%-*s |  Bin\n", prefix, len, name);
                        goto free_diffstat_file;
-               } else if (added + deleted == 0) {
+               }
+               else if (data->files[i]->is_unmerged) {
+                       printf(" %s%-*s |  Unmerged\n", prefix, len, name);
+                       goto free_diffstat_file;
+               }
+               else if (added + deleted == 0) {
                        total_files--;
                        goto free_diffstat_file;
                }
@@ -426,11 +432,16 @@ static void builtin_diffstat(const char *name_a, const char *name_b,
 
        data = diffstat_add(diffstat, name_a ? 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->added = -1;
+               data->is_binary = 1;
        else {
                /* Crazy xdl interfaces.. */
                xpparam_t xpp;
@@ -1049,6 +1060,10 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
        }
        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))
@@ -1518,7 +1533,7 @@ void diff_flush(struct diff_options *options)
        int diff_output_format = options->output_format;
        struct diffstat_t *diffstat = NULL;
 
-       if (diff_output_format == DIFF_FORMAT_DIFFSTAT) {
+       if (diff_output_format == DIFF_FORMAT_DIFFSTAT || options->with_stat) {
                diffstat = xcalloc(sizeof (struct diffstat_t), 1);
                diffstat->xm.consume = diffstat_consume;
        }
@@ -1530,6 +1545,17 @@ void diff_flush(struct diff_options *options)
                }
                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 --git a/diff.h b/diff.h
index 2f8aff2..f783bae 100644 (file)
--- a/diff.h
+++ b/diff.h
@@ -25,6 +25,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;
@@ -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" \
diff --git a/git.c b/git.c
index 9e29ade..a6cfd1d 100644 (file)
--- a/git.c
+++ b/git.c
@@ -330,8 +330,10 @@ static int cmd_log_wc(int argc, const char **argv, char **envp,
                pretty_print_commit(rev->commit_format, commit, ~0, buf,
                                    LOGSIZE, rev->abbrev);
                printf("%s\n", buf);
-               if (rev->diff)
+               if (rev->diff) {
+                       printf("---\n");
                        log_tree_commit(rev, commit);
+               }
                shown = 1;
                free(commit->buffer);
                commit->buffer = NULL;
@@ -398,6 +400,12 @@ static void handle_internal_command(int argc, const char **argv, char **envp)
        };
        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/pager.c b/pager.c
index 1364e15..e5ba273 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)
+               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);
 }
index 641362d..b95ade2 100644 (file)
@@ -45,6 +45,8 @@ static long xdl_split(unsigned long const *ha1, long off1, long lim1,
                      long *kvdf, long *kvdb, int need_min, xdpsplit_t *spl,
                      xdalgoenv_t *xenv);
 static xdchange_t *xdl_add_change(xdchange_t *xscr, long i1, long i2, long chg1, long chg2);
+static int xdl_change_compact(xdfile_t *xdf, xdfile_t *xdfo);
+
 
 
 
@@ -395,6 +397,110 @@ static xdchange_t *xdl_add_change(xdchange_t *xscr, long i1, long i2, long chg1,
 }
 
 
+static int xdl_change_compact(xdfile_t *xdf, xdfile_t *xdfo) {
+       long ix, ixo, ixs, ixref, grpsiz, nrec = xdf->nrec;
+       char *rchg = xdf->rchg, *rchgo = xdfo->rchg;
+       xrecord_t **recs = xdf->recs;
+
+       /*
+        * This is the same of what GNU diff does. Move back and forward
+        * change groups for a consistent and pretty diff output. This also
+        * helps in finding joineable change groups and reduce the diff size.
+        */
+       for (ix = ixo = 0;;) {
+               /*
+                * Find the first changed line in the to-be-compacted file.
+                * We need to keep track of both indexes, so if we find a
+                * changed lines group on the other file, while scanning the
+                * to-be-compacted file, we need to skip it properly. Note
+                * that loops that are testing for changed lines on rchg* do
+                * not need index bounding since the array is prepared with
+                * a zero at position -1 and N.
+                */
+               for (; ix < nrec && !rchg[ix]; ix++)
+                       while (rchgo[ixo++]);
+               if (ix == nrec)
+                       break;
+
+               /*
+                * Record the start of a changed-group in the to-be-compacted file
+                * and find the end of it, on both to-be-compacted and other file
+                * indexes (ix and ixo).
+                */
+               ixs = ix;
+               for (ix++; rchg[ix]; ix++);
+               for (; rchgo[ixo]; ixo++);
+
+               do {
+                       grpsiz = ix - ixs;
+
+                       /*
+                        * If the line before the current change group, is equal to
+                        * the last line of the current change group, shift backward
+                        * the group.
+                        */
+                       while (ixs > 0 && recs[ixs - 1]->ha == recs[ix - 1]->ha &&
+                              XDL_RECMATCH(recs[ixs - 1], recs[ix - 1])) {
+                               rchg[--ixs] = 1;
+                               rchg[--ix] = 0;
+
+                               /*
+                                * This change might have joined two change groups,
+                                * so we try to take this scenario in account by moving
+                                * the start index accordingly (and so the other-file
+                                * end-of-group index).
+                                */
+                               for (; rchg[ixs - 1]; ixs--);
+                               while (rchgo[--ixo]);
+                       }
+
+                       /*
+                        * Record the end-of-group position in case we are matched
+                        * with a group of changes in the other file (that is, the
+                        * change record before the enf-of-group index in the other
+                        * file is set).
+                        */
+                       ixref = rchgo[ixo - 1] ? ix: nrec;
+
+                       /*
+                        * If the first line of the current change group, is equal to
+                        * the line next of the current change group, shift forward
+                        * the group.
+                        */
+                       while (ix < nrec && recs[ixs]->ha == recs[ix]->ha &&
+                              XDL_RECMATCH(recs[ixs], recs[ix])) {
+                               rchg[ixs++] = 0;
+                               rchg[ix++] = 1;
+
+                               /*
+                                * This change might have joined two change groups,
+                                * so we try to take this scenario in account by moving
+                                * the start index accordingly (and so the other-file
+                                * end-of-group index). Keep tracking the reference
+                                * index in case we are shifting together with a
+                                * corresponding group of changes in the other file.
+                                */
+                               for (; rchg[ix]; ix++);
+                               while (rchgo[++ixo])
+                                       ixref = ix;
+                       }
+               } while (grpsiz != ix - ixs);
+
+               /*
+                * Try to move back the possibly merged group of changes, to match
+                * the recorded postion in the other file.
+                */
+               while (ixref < ix) {
+                       rchg[--ixs] = 1;
+                       rchg[--ix] = 0;
+                       while (rchgo[--ixo]);
+               }
+       }
+
+       return 0;
+}
+
+
 int xdl_build_script(xdfenv_t *xe, xdchange_t **xscr) {
        xdchange_t *cscr = NULL, *xch;
        char *rchg1 = xe->xdf1.rchg, *rchg2 = xe->xdf2.rchg;
@@ -440,13 +546,13 @@ int xdl_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
 
                return -1;
        }
-
-       if (xdl_build_script(&xe, &xscr) < 0) {
+       if (xdl_change_compact(&xe.xdf1, &xe.xdf2) < 0 ||
+           xdl_change_compact(&xe.xdf2, &xe.xdf1) < 0 ||
+           xdl_build_script(&xe, &xscr) < 0) {
 
                xdl_free_env(&xe);
                return -1;
        }
-
        if (xscr) {
                if (xdl_emit_diff(&xe, xscr, ecb, xecfg) < 0) {
 
@@ -454,10 +560,8 @@ int xdl_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
                        xdl_free_env(&xe);
                        return -1;
                }
-
                xdl_free_script(xscr);
        }
-
        xdl_free_env(&xe);
 
        return 0;
index 4c2fde8..78f0260 100644 (file)
@@ -33,6 +33,7 @@
 #define XDL_ISDIGIT(c) ((c) >= '0' && (c) <= '9')
 #define XDL_HASHLONG(v, b) (((unsigned long)(v) * GR_PRIME) >> ((CHAR_BIT * sizeof(unsigned long)) - (b)))
 #define XDL_PTRFREE(p) do { if (p) { xdl_free(p); (p) = NULL; } } while (0)
+#define XDL_RECMATCH(r1, r2) ((r1)->size == (r2)->size && memcmp((r1)->ptr, (r2)->ptr, (r1)->size) == 0)
 #define XDL_LE32_PUT(p, v) \
 do { \
        unsigned char *__p = (unsigned char *) (p); \