]> asedeno.scripts.mit.edu Git - git.git/commitdiff
Merge branch 'master' into next
authorJunio C Hamano <junkio@cox.net>
Fri, 21 Apr 2006 07:45:55 +0000 (00:45 -0700)
committerJunio C Hamano <junkio@cox.net>
Fri, 21 Apr 2006 07:45:55 +0000 (00:45 -0700)
* master:
  fix pack-object buffer size
  mailinfo: decode underscore used in "Q" encoding properly.
  Reintroduce svn pools to solve the memory leak.
  pack-objects: do not stop at object that is "too small"

14 files changed:
.gitignore
Makefile
commit.c
commit.h
daemon.c
diff-lib.c
git.c
gsimm.c [new file with mode: 0644]
gsimm.h [new file with mode: 0644]
log-tree.c
rabinpoly.c [new file with mode: 0644]
rabinpoly.h [new file with mode: 0644]
test-gsimm.c [new file with mode: 0644]
update-index.c

index b5959d63116aa1e3e900f43a137295b47376e1e0..145f8555ad4b5c13cb7a6076cf7d9e809beff952 100644 (file)
@@ -123,6 +123,7 @@ git-write-tree
 git-core-*/?*
 test-date
 test-delta
+test-gsimm
 common-cmds.h
 *.tar.gz
 *.dsc
index 3ecd674c14d279e81a37c666cba419656fe044c0..9cbb90b95d88e5928cfc5207c581a933b140ecad 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -606,6 +606,9 @@ test-date$X: test-date.c date.o ctype.o
 test-delta$X: test-delta.c diff-delta.o patch-delta.o
        $(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $^ -lz
 
+test-gsimm$X: test-gsimm.c gsimm.o rabinpoly.o
+       $(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $^
+
 check:
        for i in *.c; do sparse $(ALL_CFLAGS) $(SPARSE_FLAGS) $$i || exit; done
 
@@ -654,6 +657,7 @@ clean:
        rm -f *.o mozilla-sha1/*.o arm/*.o ppc/*.o compat/*.o xdiff/*.o \
                $(LIB_FILE) $(XDIFF_LIB)
        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)
        rm -f $(GIT_TARNAME).tar.gz git-core_$(GIT_VERSION)-*.tar.gz
index 2717dd81c346d89bf5d6727e3aa1f5b65ff39aca..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");
@@ -428,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);
@@ -435,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;
@@ -445,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;
 }
 
@@ -457,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:");
@@ -480,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;
@@ -506,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;
                        }
@@ -544,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 de142afe733a6ecd3bcec2abd9f71a358b4ae09d..01eec60a1ecc2ff3c73850cb510ba71767e47e03 100644 (file)
--- a/commit.h
+++ b/commit.h
@@ -45,6 +45,7 @@ enum cmit_fmt {
        CMIT_FMT_FULL,
        CMIT_FMT_FULLER,
        CMIT_FMT_ONELINE,
+       CMIT_FMT_EMAIL,
 
        CMIT_FMT_UNSPECIFIED,
 };
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 0a832c35855a4bf831e40ff9183cde0569566c15..13b216f2733763b4e6cc6a3c37ff2624d7f55266 100644 (file)
@@ -195,6 +195,56 @@ static int fn_out(void *priv, mmbuffer_t *mb, int nbuf)
        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;
 
@@ -204,12 +254,14 @@ struct diffstat_t {
                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)
+                                         const char *name_a,
+                                         const char *name_b)
 {
        struct diffstat_file *x;
        x = xcalloc(sizeof (*x), 1);
@@ -219,7 +271,12 @@ static struct diffstat_file *diffstat_add(struct diffstat_t *diffstat,
                                diffstat->alloc * sizeof(x));
        }
        diffstat->files[diffstat->nr++] = x;
-       x->name = strdup(name);
+       if (name_b) {
+               x->name = pprint_rename(name_a, name_b);
+               x->is_renamed = 1;
+       }
+       else
+               x->name = strdup(name_a);
        return x;
 }
 
@@ -305,7 +362,8 @@ static void show_stats(struct diffstat_t* data)
                        printf(" %s%-*s |  Unmerged\n", prefix, len, name);
                        goto free_diffstat_file;
                }
-               else if (added + deleted == 0) {
+               else if (!data->files[i]->is_renamed &&
+                        (added + deleted == 0)) {
                        total_files--;
                        goto free_diffstat_file;
                }
@@ -425,13 +483,14 @@ static void builtin_diff(const char *name_a,
 }
 
 static void builtin_diffstat(const char *name_a, const char *name_b,
-               struct diff_filespec *one, struct diff_filespec *two,
-               struct diffstat_t *diffstat)
+                            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);
+       data = diffstat_add(diffstat, name_a, name_b);
 
        if (!one || !two) {
                data->is_unmerged = 1;
@@ -992,7 +1051,7 @@ static void run_diff(struct diff_filepair *p, struct diff_options *o)
 }
 
 static void run_diffstat(struct diff_filepair *p, struct diff_options *o,
-               struct diffstat_t *diffstat)
+                        struct diffstat_t *diffstat)
 {
        const char *name;
        const char *other;
@@ -1374,7 +1433,7 @@ static void diff_flush_patch(struct diff_filepair *p, struct diff_options *o)
 }
 
 static void diff_flush_stat(struct diff_filepair *p, struct diff_options *o,
-               struct diffstat_t *diffstat)
+                           struct diffstat_t *diffstat)
 {
        if (diff_unmodified_pair(p))
                return;
@@ -1559,7 +1618,7 @@ void diff_flush(struct diff_options *options)
                for (i = 0; i < q->nr; i++) {
                        struct diff_filepair *p = q->queue[i];
                        flush_one_pair(p, DIFF_FORMAT_DIFFSTAT, options,
-                                       diffstat);
+                                      diffstat);
                }
                show_stats(diffstat);
                free(diffstat);
diff --git a/git.c b/git.c
index 40b7e42ae9f87b5cdc7e2f968bbb12caa8f2d01c..c20ae45bb77c840eec3cba4b90269930b0c96ccb 100644 (file)
--- a/git.c
+++ b/git.c
@@ -276,6 +276,43 @@ static int cmd_help(int argc, const char **argv, char **envp)
        return 0;
 }
 
+static int cmd_format_patch(int argc, const char **argv, char **envp)
+{
+       struct commit *commit;
+       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");
+
+       prepare_revision_walk(&rev);
+       while ((commit = get_revision(&rev)) != NULL) {
+               nr++;
+               list = realloc(list, nr * sizeof(list[0]));
+               list[nr - 1] = commit;
+       }
+       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;
+}
+
 static int cmd_log_wc(int argc, const char **argv, char **envp,
                      struct rev_info *rev)
 {
@@ -346,6 +383,7 @@ static void handle_internal_command(int argc, const char **argv, char **envp)
                { "log", cmd_log },
                { "whatchanged", cmd_wc },
                { "show", cmd_show },
+               { "fmt-patch", cmd_format_patch },
        };
        int i;
 
diff --git a/gsimm.c b/gsimm.c
new file mode 100644 (file)
index 0000000..7024bf8
--- /dev/null
+++ b/gsimm.c
@@ -0,0 +1,94 @@
+#include "rabinpoly.h"
+#include "gsimm.h"
+
+/* Has to be power of two. Since the Rabin hash only has 63
+   usable bits, the number of hashes is limited to 32.
+   Lower powers of two could be used for speeding up processing
+   of very large files.  */
+#define NUM_HASHES_PER_CHAR 32
+
+/* Size of cache used to eliminate duplicate substrings.
+   Make small enough to comfortably fit in L1 cache.  */
+#define DUP_CACHE_SIZE 256
+
+/* For the final counting, do not count each bit individually, but
+   group them. Must be power of two, at most NUM_HASHES_PER_CHAR.
+   However, larger sizes result in higher cache usage. Use 8 bits
+   per group for efficient processing of large files on fast machines
+   with decent caches, or 4 bits for faster processing of small files
+   and for machines with small caches.  */
+#define GROUP_BITS 4
+#define GROUP_COUNTERS (1<<GROUP_BITS)
+
+static void freq_to_md(u_char *md, int *freq)
+{ int j, k;
+
+  for (j = 0; j < MD_LENGTH; j++)
+  { u_char ch = 0;
+
+    for (k = 0; k < 8; k++) ch = 2*ch + (freq[8*j+k] > 0);
+    md[j] = ch;
+  }
+  bzero (freq, sizeof(freq[0]) * MD_BITS);
+}
+
+void gb_simm_process(u_char *data, unsigned len, u_char *md)
+{ size_t j = 0;
+  u_int32_t ofs;
+  u_int32_t dup_cache[DUP_CACHE_SIZE];
+  u_int32_t count [MD_BITS * (GROUP_COUNTERS/GROUP_BITS)];
+  int freq[MD_BITS];
+
+  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)));
+
+  /* Ignore incomplete substrings */
+  while (j < len && j < RABIN_WINDOW_SIZE) rabin_slide8 (data[j++]);
+
+  while (j < len)
+  { u_int64_t hash;
+    u_int32_t ofs, sum;
+    u_char idx;
+    int k;
+
+    hash = rabin_slide8 (data[j++]);
+
+    /* In order to update a much larger frequency table
+       with only 32 bits of checksum, randomly select a
+       part of the table to update. The selection should
+       only depend on the content of the represented data,
+       and be independent of the bits used for the update.
+
+       Instead of updating 32 individual counters, process
+       the checksum in MD_BITS / GROUP_BITS groups of
+       GROUP_BITS bits, and count the frequency of each bit pattern.
+    */
+
+    idx = (hash >> 32);
+    sum = (u_int32_t) hash;
+    ofs = idx % (MD_BITS / NUM_HASHES_PER_CHAR) * NUM_HASHES_PER_CHAR;
+    idx %= DUP_CACHE_SIZE;
+    if (dup_cache[idx] != sum)
+    { dup_cache[idx] = sum;
+      for (k = 0; k < NUM_HASHES_PER_CHAR / GROUP_BITS; k++)
+      { count[ofs * GROUP_COUNTERS / GROUP_BITS + (sum % GROUP_COUNTERS)]++;
+        ofs += GROUP_BITS;
+        sum >>= GROUP_BITS;
+  } } }
+
+  /* Distribute the occurrences of each bit group over the frequency table. */
+  for (ofs = 0; ofs < MD_BITS; ofs += GROUP_BITS)
+  { int j;
+    for (j = 0; j < GROUP_COUNTERS; j++)
+    { int k;
+      for (k = 0; k < GROUP_BITS; k++)
+      { freq[ofs + k] += ((1<<k) & j)
+          ? count[ofs * GROUP_COUNTERS / GROUP_BITS + j]
+          : -count[ofs * GROUP_COUNTERS / GROUP_BITS + j];
+  } } }
+
+  if (md)
+  { rabin_reset();
+    freq_to_md (md, freq);
+} }
diff --git a/gsimm.h b/gsimm.h
new file mode 100644 (file)
index 0000000..4b023b9
--- /dev/null
+++ b/gsimm.h
@@ -0,0 +1,28 @@
+#ifndef GSIMM_H
+#define GSIMM_H
+
+/* Length of file message digest (MD) in bytes. Longer MD's are
+   better, but increase processing time for diminishing returns.
+   Must be multiple of NUM_HASHES_PER_CHAR / 8, and at least 24
+   for good results
+*/
+#define MD_LENGTH 32
+#define MD_BITS (MD_LENGTH * 8)
+
+/* The MIN_FILE_SIZE indicates the absolute minimal file size that
+   can be processed. As indicated above, the first and last
+   RABIN_WINDOW_SIZE - 1 bytes are skipped.
+   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 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 MAX_FILE_SIZE (256*1024*1024 - 1)
+
+void gb_simm_process(u_char *data, unsigned len, u_char *md);
+
+#endif
index 9634c4677f84e7d88965b47b5af32f23668b2142..aaf2b9423f01243584e15e6c93007a3f42c9d917 100644 (file)
@@ -37,12 +37,20 @@ void show_log(struct rev_info *opt, struct log_info *log, const char *sep)
        /*
         * Print header line of header..
         */
-       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');
+
+       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');
+       }
 
        /*
         * And then the pretty-printed message itself
@@ -152,15 +160,18 @@ static int log_tree_diff(struct rev_info *opt, struct commit *commit, struct 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;
 
-       if (!log_tree_diff(opt, commit, &log) && opt->loginfo && opt->always_show_header) {
+       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;
        }
        opt->loginfo = NULL;
-       return 0;
+       return shown;
 }
diff --git a/rabinpoly.c b/rabinpoly.c
new file mode 100644 (file)
index 0000000..c26f382
--- /dev/null
@@ -0,0 +1,154 @@
+/*
+ *
+ * Copyright (C) 1999 David Mazieres (dm@uun.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ *
+ */
+
+  /* Faster generic_fls */
+  /* (c) 2002, D.Phillips and Sistina Software */
+
+#include "rabinpoly.h"
+#define MSB64 0x8000000000000000ULL
+
+static inline unsigned fls8(unsigned n)
+{
+       return n & 0xf0?
+           n & 0xc0? (n >> 7) + 7: (n >> 5) + 5:
+           n & 0x0c? (n >> 3) + 3: n - ((n + 1) >> 2);
+}
+
+static inline unsigned fls16(unsigned n)
+{
+       return n & 0xff00? fls8(n >> 8) + 8: fls8(n);
+}
+
+static inline unsigned fls32(unsigned n)
+{
+       return n & 0xffff0000? fls16(n >> 16) + 16: fls16(n);
+}
+
+static inline unsigned fls64(unsigned long long n) /* should be u64 */
+{
+       return n & 0xffffffff00000000ULL? fls32(n >> 32) + 32: fls32(n);
+}
+
+
+static u_int64_t polymod (u_int64_t nh, u_int64_t nl, u_int64_t d);
+static void      polymult (u_int64_t *php, u_int64_t *plp,
+                           u_int64_t x, u_int64_t y);
+static u_int64_t polymmult (u_int64_t x, u_int64_t y, u_int64_t d);
+
+static u_int64_t poly = 0xb15e234bd3792f63ull; // Actual polynomial
+static u_int64_t T[256];                       // Lookup table for mod
+static int shift;
+
+u_int64_t append8 (u_int64_t p, u_char m)
+{ return ((p << 8) | m) ^ T[p >> shift];
+}
+
+static u_int64_t
+polymod (u_int64_t nh, u_int64_t nl, u_int64_t d)
+{ int i, k;
+  assert (d);
+  k = fls64 (d) - 1;
+  d <<= 63 - k;
+
+  if (nh) {
+    if (nh & MSB64)
+      nh ^= d;
+    for (i = 62; i >= 0; i--)
+      if (nh & 1ULL << i) {
+       nh ^= d >> (63 - i);
+       nl ^= d << (i + 1);
+      }
+  }
+  for (i = 63; i >= k; i--)
+    if (nl & 1ULL << i)
+      nl ^= d >> (63 - i);
+  return nl;
+}
+
+static void
+polymult (u_int64_t *php, u_int64_t *plp, u_int64_t x, u_int64_t y)
+{ int i;
+  u_int64_t ph = 0, pl = 0;
+  if (x & 1)
+    pl = y;
+  for (i = 1; i < 64; i++)
+    if (x & (1ULL << i)) {
+      ph ^= y >> (64 - i);
+      pl ^= y << i;
+    }
+  if (php)
+    *php = ph;
+  if (plp)
+    *plp = pl;
+}
+
+static u_int64_t
+polymmult (u_int64_t x, u_int64_t y, u_int64_t d)
+{
+  u_int64_t h, l;
+  polymult (&h, &l, x, y);
+  return polymod (h, l, d);
+}
+
+static int size = RABIN_WINDOW_SIZE;
+static u_int64_t fingerprint = 0;
+static int bufpos = -1;
+static u_int64_t U[256];
+static u_char buf[RABIN_WINDOW_SIZE];
+
+void rabin_init ()
+{ u_int64_t sizeshift = 1;
+ u_int64_t T1;
+  int xshift;
+  int i, j;
+  assert (poly >= 0x100);
+  xshift = fls64 (poly) - 1;
+  shift = xshift - 8;
+  T1 = polymod (0, 1ULL << xshift, poly);
+  for (j = 0; j < 256; j++)
+    T[j] = polymmult (j, T1, poly) | ((u_int64_t) j << xshift);
+
+  for (i = 1; i < size; i++)
+    sizeshift = append8 (sizeshift, 0);
+  for (i = 0; i < 256; i++)
+    U[i] = polymmult (i, sizeshift, poly);
+  bzero (buf, sizeof (buf));
+}
+
+void
+rabin_reset ()
+{ rabin_init();
+  fingerprint = 0;
+  bzero (buf, sizeof (buf));
+}
+
+u_int64_t
+rabin_slide8 (u_char m)
+{ u_char om;
+  if (++bufpos >= size) bufpos = 0;
+
+  om = buf[bufpos];
+  buf[bufpos] = m;
+  fingerprint = append8 (fingerprint ^ U[om], m);
+
+  return fingerprint;
+}
+
diff --git a/rabinpoly.h b/rabinpoly.h
new file mode 100644 (file)
index 0000000..a19a097
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ *
+ * Copyright (C) 2000 David Mazieres (dm@uun.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ *
+ * Translated to C and simplified by Geert Bosch (bosch@gnat.com)
+ */
+
+#include <assert.h>
+#include <strings.h>
+#include <sys/types.h>
+
+#ifndef RABIN_WINDOW_SIZE
+#define RABIN_WINDOW_SIZE 48
+#endif
+void rabin_reset();
+u_int64_t rabin_slide8(u_char c);
diff --git a/test-gsimm.c b/test-gsimm.c
new file mode 100644 (file)
index 0000000..bd28b7d
--- /dev/null
@@ -0,0 +1,209 @@
+#include <unistd.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <libgen.h>
+#include <stdio.h>
+#include <assert.h>
+#include <math.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+
+#include "rabinpoly.h"
+#include "gsimm.h"
+
+#define MIN(x,y) ((y)<(x) ? (y) : (x))
+#define MAX(x,y) ((y)>(x) ? (y) : (x))
+
+/* The RABIN_WINDOW_SIZE is the size of fingerprint window used by
+   Rabin algorithm. This is not a modifiable parameter.
+
+   The first RABIN_WINDOW_SIZE - 1 bytes are skipped, in order to ensure
+   fingerprints are good hashes. This does somewhat reduce the
+   influence of the first few bytes in the file (they're part of
+   fewer windows, like the last few bytes), but that actually isn't
+   so bad as files often start with fixed content that may bias comparisons.
+*/
+
+typedef struct fileinfo
+{ char         *name;
+  size_t       length;
+  u_char       md[MD_LENGTH];
+  int          match;
+} File;
+
+int flag_verbose = 0;
+int flag_debug = 0;
+char *flag_relative = 0;
+
+char cmd[12] = "        ...";
+char md_strbuf[MD_LENGTH * 2 + 1];
+u_char relative_md [MD_LENGTH];
+
+File *file;
+int    file_count;
+size_t file_bytes;
+
+char hex[17] = "0123456789abcdef";
+
+void usage()
+{  fprintf (stderr, "usage: %s [-dhvw] [-r fingerprint] file ...\n", cmd);
+   fprintf (stderr, " -d\tdebug output, repeate for more verbosity\n");
+   fprintf (stderr, " -h\tshow this usage information\n");
+   fprintf (stderr, " -r\tshow distance relative to fingerprint "
+                    "(%u hex digits)\n", MD_LENGTH * 2);
+   fprintf (stderr, " -v\tverbose output, repeat for even more verbosity\n");
+   fprintf (stderr, " -w\tenable warnings for suspect statistics\n");
+   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;
+
+  for (j = 0; j < MD_LENGTH; j++)
+  { u_char ch = md[j];
+
+    md_strbuf[j*2] = hex[ch >> 4];
+    md_strbuf[j*2+1] = hex[ch & 0xF];
+  }
+
+  md_strbuf[j*2] = 0;
+  return md_strbuf;
+}
+
+void process_file (char *name)
+{ int fd;
+  struct stat fs;
+  u_char *data;
+  File *fi = file+file_count;;
+
+  fd = open (name, O_RDONLY, 0);
+  if (fd < 0)
+  { perror (name);
+    exit (2);
+  }
+
+  if (fstat (fd, &fs))
+  { perror (name);
+    exit (2);
+  }
+
+  if (fs.st_size >= MIN_FILE_SIZE
+      && fs.st_size <= MAX_FILE_SIZE)
+  { fi->length = fs.st_size;
+    fi->name = name;
+
+    data = (u_char *) mmap (0, fs.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+
+    if (data == (u_char *) -1)
+    { perror (name);
+      exit (2);
+    }
+
+    gb_simm_process (data, fs.st_size, fi->md);
+    if (flag_relative)
+    { 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",
+               md_to_str (fi->md), (long long unsigned) 0,
+              (unsigned) fs.st_size, name);
+    }
+    munmap (data, fs.st_size);
+    file_bytes += fs.st_size;
+    file_count++;
+  } else if (flag_verbose)
+  { fprintf (stdout, "skipping %s (size %llu)\n", name, (long long unsigned) fs.st_size); }
+
+  close (fd);
+}
+
+u_char *str_to_md(char *str, u_char *md)
+{ int j;
+
+  if (!md || !str) return 0;
+
+  bzero (md, MD_LENGTH);
+
+  for (j = 0; j < MD_LENGTH * 2; j++)
+  { char ch = str[j];
+
+    if (ch >= '0' && ch <= '9')
+    { md [j/2] = (md [j/2] << 4) + (ch - '0');
+    }
+    else
+    { ch |= 32;
+
+      if (ch < 'a' || ch > 'f') break;
+      md [j/2] = (md[j/2] << 4) + (ch - 'a' + 10);
+  } }
+
+  return (j != MD_LENGTH * 2 || str[j] != 0) ? 0 : md;
+}
+
+int main (int argc, char *argv[])
+{ int ch, j;
+
+  strncpy (cmd, basename (argv[0]), 8);
+
+  while ((ch = getopt(argc, argv, "dhr:vw")) != -1)
+  { switch (ch)
+    { case 'd': flag_debug++;
+               break;
+      case 'r': if (!optarg)
+                { fprintf (stderr, "%s: missing argument for -r\n", cmd);
+                  return 1;
+                }
+                if (str_to_md (optarg, relative_md)) flag_relative = optarg;
+                else
+                { fprintf (stderr, "%s: not a valid fingerprint\n", optarg);
+                  return 1;
+                }
+                break;
+      case 'v': flag_verbose++;
+                break;
+      case 'w': break;
+      default : usage();
+                return (ch != 'h');
+  } }
+
+  argc -= optind;
+  argv += optind;
+
+  if (argc == 0) usage();
+
+  rabin_reset ();
+  if (flag_verbose && flag_relative)
+  { fprintf (stdout, "distances are relative to %s\n", flag_relative);
+  }
+
+  file = (File *) calloc (argc, sizeof (File));
+
+  for (j = 0; j < argc; j++) process_file (argv[j]);
+
+  if (flag_verbose)
+  { fprintf (stdout, "%li bytes in %i files\n", (long) file_bytes, file_count);
+  }
+
+  return 0;
+}
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))