diff options
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | Makefile | 6 | ||||
-rw-r--r-- | combine-diff.c | 12 | ||||
-rw-r--r-- | commit.c | 6 | ||||
-rw-r--r-- | config.c | 39 | ||||
-rw-r--r-- | connect.c | 18 | ||||
-rw-r--r-- | exec_cmd.c | 4 | ||||
-rwxr-xr-x | gitk | 5 | ||||
-rw-r--r-- | gsimm.c | 94 | ||||
-rw-r--r-- | gsimm.h | 28 | ||||
-rw-r--r-- | http-push.c | 10 | ||||
-rw-r--r-- | pager.c | 2 | ||||
-rw-r--r-- | quote.c | 2 | ||||
-rw-r--r-- | rabinpoly.c | 154 | ||||
-rw-r--r-- | rabinpoly.h | 31 | ||||
-rw-r--r-- | rev-list.c | 6 | ||||
-rw-r--r-- | revision.c | 29 | ||||
-rw-r--r-- | revision.h | 3 | ||||
-rw-r--r-- | sha1_file.c | 6 | ||||
-rw-r--r-- | t/Makefile | 2 | ||||
-rw-r--r-- | test-gsimm.c | 209 |
21 files changed, 618 insertions, 49 deletions
diff --git a/.gitignore b/.gitignore index b5959d6311..145f8555ad 100644 --- a/.gitignore +++ b/.gitignore @@ -123,6 +123,7 @@ git-write-tree git-core-*/?* test-date test-delta +test-gsimm common-cmds.h *.tar.gz *.dsc @@ -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 diff --git a/combine-diff.c b/combine-diff.c index 27f6f57f3a..ca36f5d5e7 100644 --- a/combine-diff.c +++ b/combine-diff.c @@ -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) @@ -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); } @@ -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; } @@ -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; } diff --git a/exec_cmd.c b/exec_cmd.c index 590e738969..44bb2f23de 100644 --- a/exec_cmd.c +++ b/exec_cmd.c @@ -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; @@ -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 index 0000000000..7024bf8f58 --- /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 index 0000000000..4b023b91a9 --- /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 diff --git a/http-push.c b/http-push.c index 4a9dcf2bf6..b4327d9243 100644 --- a/http-push.c +++ b/http-push.c @@ -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 @@ -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) @@ -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 index 0000000000..c26f3829c3 --- /dev/null +++ b/rabinpoly.c @@ -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 index 0000000000..a19a097459 --- /dev/null +++ b/rabinpoly.h @@ -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/rev-list.c b/rev-list.c index a4d72af6e0..8b0ec388fa 100644 --- a/rev-list.c +++ b/rev-list.c @@ -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) diff --git a/revision.c b/revision.c index 4d2a64ef6b..dbd54da5ba 100644 --- a/revision.c +++ b/revision.c @@ -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); diff --git a/revision.h b/revision.h index 05f658a214..48d7b4ca94 100644 --- a/revision.h +++ b/revision.h @@ -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; diff --git a/sha1_file.c b/sha1_file.c index e3d011309a..f2d33afb27 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -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; diff --git a/t/Makefile b/t/Makefile index fe65f53c5f..549598575b 100644 --- a/t/Makefile +++ b/t/Makefile @@ -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 index 0000000000..bd28b7da28 --- /dev/null +++ b/test-gsimm.c @@ -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; +} |