Merge branch 'lt/logopt' into next
authorJunio C Hamano <junkio@cox.net>
Tue, 18 Apr 2006 20:18:21 +0000 (13:18 -0700)
committerJunio C Hamano <junkio@cox.net>
Tue, 18 Apr 2006 20:18:21 +0000 (13:18 -0700)
* lt/logopt:
Fix "git log --stat": make sure to set recursive with --stat.

21 files changed:
.gitignore
Makefile
combine-diff.c
commit.c
config.c
connect.c
exec_cmd.c
gitk
gsimm.c [new file with mode: 0644]
gsimm.h [new file with mode: 0644]
http-push.c
pager.c
quote.c
rabinpoly.c [new file with mode: 0644]
rabinpoly.h [new file with mode: 0644]
rev-list.c
revision.c
revision.h
sha1_file.c
t/Makefile
test-gsimm.c [new file with mode: 0644]
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 1130af4f3809610436f4c5d17c3970a947ddae5c..84a4b84226cef5ac058760935a2770a3c164f436 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
 
@@ -653,7 +656,8 @@ 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)
        rm -f $(GIT_TARNAME).tar.gz git-core_$(GIT_VERSION)-*.tar.gz
index 27f6f57f3ab8e94e853d67ba031437fe6491d52d..ca36f5d5e7342e9198675ce0faf11ba49ca67876 100644 (file)
@@ -600,7 +600,7 @@ static int show_patch_diff(struct combine_diff_path *elem, int num_parent,
 {
        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;
@@ -652,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;
@@ -759,7 +758,7 @@ static int show_patch_diff(struct combine_diff_path *elem, int num_parent,
 static void show_raw_diff(struct combine_diff_path *p, int num_parent, struct rev_info *rev)
 {
        struct diff_options *opt = &rev->diffopt;
-       int i, offset, mod_type = 'A';
+       int i, offset;
        const char *prefix;
        int line_termination, inter_name_termination;
 
@@ -771,13 +770,6 @@ static void show_raw_diff(struct combine_diff_path *p, int num_parent, struct re
        if (rev->loginfo)
                show_log(rev, rev->loginfo, "\n");
 
-       for (i = 0; i < num_parent; i++) {
-               if (p->parent[i].mode)
-                       mod_type = 'M';
-       }
-       if (!p->mode)
-               mod_type = 'D';
-
        if (opt->output_format == DIFF_FORMAT_RAW) {
                offset = strlen(COLONS) - num_parent;
                if (offset < 0)
index ca25574500fe3c88d2cc2953b7dd019b3acab9e7..2717dd81c346d89bf5d6727e3aa1f5b65ff39aca 100644 (file)
--- a/commit.c
+++ b/commit.c
@@ -160,8 +160,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 +192,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);
        }
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 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;
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
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 4a9dcf2bf64887735573d7c02b6b9f21ad4644f6..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
diff --git a/pager.c b/pager.c
index e5ba2738b681387495b840d39dfeca920c865616..b063353d96eb6d526438c1fa22cacd28bc12b0c1 100644 (file)
--- a/pager.c
+++ b/pager.c
@@ -20,7 +20,7 @@ void setup_pager(void)
                return;
        if (!pager)
                pager = "less";
-       else if (!*pager)
+       else if (!*pager || !strcmp(pager, "cat"))
                return;
 
        if (pipe(fd) < 0)
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();
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);
index a4d72af6e01d88aa41962736d9a52f58bf65aad7..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"
@@ -341,6 +341,8 @@ int main(int argc, const char **argv)
 
        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 4d2a64ef6b3e0a8f9af30264c5e7f2dc8b56d148..dbd54da5baec5b5a2320a40a66da24541705e971 100644 (file)
@@ -851,6 +851,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;
@@ -888,8 +899,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;
@@ -902,6 +925,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 05f658a214a7e1ca7e009c0de9667bf66552dc47..48d7b4ca94f3fd00f7a1f6a3fb57ebed934ffc0d 100644 (file)
@@ -7,7 +7,8 @@
 #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;
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 fe65f53c5fbcf07bb69214b0f0cff8aef551e906..549598575b9fdcefe857ddef1b6d980a9773b033 100644 (file)
@@ -25,5 +25,5 @@ clean:
        rm -fr trash
 
 .PHONY: $(T) clean
-.NOPARALLEL:
+.NOTPARALLEL:
 
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;
+}