From: Linus Torvalds Date: Tue, 12 Apr 2005 00:23:58 +0000 (-0700) Subject: Make "rev-tree" capable of showing the difference in reachability between two X-Git-Tag: v0.99~911 X-Git-Url: http://git.tremily.us/?a=commitdiff_plain;h=28258afe912303f717fcdccaca124c1ae3e8e004;p=git.git Make "rev-tree" capable of showing the difference in reachability between two or more commit points. This is important both to know what the difference between two commit points is, but also to figure out where to try to merge from. --- diff --git a/rev-tree.c b/rev-tree.c index 73c9d1f55..c10f4ee29 100644 --- a/rev-tree.c +++ b/rev-tree.c @@ -1,6 +1,19 @@ #include "cache.h" -#define SEEN 1 +/* + * The low 16 bits of the "flags" field shows whether + * a commit is part of the path to the root for that + * parent. + * + * Bit 16 is an internal flag that we've seen the + * definition for this rev, and not just seen it as + * a parent target. + */ +#define MAX_COMMITS (16) +#define marked(rev) ((rev)->flags & 0xffff) +#define SEEN 0x10000 + +static int show_edges = 0; struct parent { struct revision *parent; @@ -112,17 +125,66 @@ static void read_cache_file(const char *path) FILE *file = fopen(path, "r"); char line[100]; + if (!file) + usage("bad revtree cache file (%s)", path); + while (fgets(line, sizeof(line), file)) { unsigned char sha1[20], parent[20]; + struct revision *rev; + if (get_sha1_hex(line, sha1) || get_sha1_hex(line + 41, parent)) usage("bad rev-tree cache file %s", path); - add_relationship(lookup_rev(sha1), parent); + rev = lookup_rev(sha1); + rev->flags |= SEEN; + add_relationship(rev, parent); } fclose(file); } +static void mark_sha1_path(struct revision *rev, unsigned int mask) +{ + struct parent *p; + + if (rev->flags & mask) + return; + + rev->flags |= mask; + p = rev->parent; + while (p) { + mark_sha1_path(p->parent, mask); + p = p->next; + } +} + /* - * Usage: rev-tree [--cache ] + * Some revisions are less interesting than others. + * + * For example, if we use a cache-file, that one may contain + * revisions that were never used. They are never interesting. + * + * And sometimes we're only interested in "edge" commits, ie + * places where the marking changes between parent and child. + */ +static int interesting(struct revision *rev) +{ + unsigned mask = marked(rev); + + if (!mask) + return 0; + if (show_edges) { + struct parent *p = rev->parent; + while (p) { + if (mask != marked(p->parent)) + return 1; + p = p->next; + } + return 0; + } + return 1; +} + +/* + * Usage: rev-tree [--edges] [--cache ] [] * * The cache-file can be quite important for big trees. This is an * expensive operation if you have to walk the whole chain of @@ -131,26 +193,50 @@ static void read_cache_file(const char *path) int main(int argc, char **argv) { int i; - unsigned char sha1[20]; + int nr = 0; + unsigned char sha1[MAX_COMMITS][20]; - while (argc > 2) { - if (!strcmp(argv[1], "--cache")) { + /* + * First - pick up all the revisions we can (both from + * caches and from commit file chains). + */ + for (i = 1; i < argc ; i++) { + char *arg = argv[i]; + + if (!strcmp(arg, "--cache")) { read_cache_file(argv[2]); - argv += 2; - argc -= 2; + i++; + continue; + } + + if (!strcmp(arg, "--edges")) { + show_edges = 1; continue; } - usage("unknown option %s", argv[1]); + + if (nr >= MAX_COMMITS || get_sha1_hex(arg, sha1[nr])) + usage("rev-tree [--edges] [--cache ] []"); + parse_commit(sha1[nr]); + nr++; } - if (argc != 2 || get_sha1_hex(argv[1], sha1)) - usage("rev-tree [--cache ] "); - parse_commit(sha1); + /* + * Now we have the maximal tree. Walk the different sha files back to the root. + */ + for (i = 0; i < nr; i++) + mark_sha1_path(lookup_rev(sha1[i]), 1 << i); + + /* + * Now print out the results.. + */ for (i = 0; i < nr_revs; i++) { struct revision *rev = revs[i]; struct parent *p; - printf("%s", sha1_to_hex(rev->sha1)); + if (!interesting(rev)) + continue; + + printf("%x %s", marked(rev), sha1_to_hex(rev->sha1)); p = rev->parent; while (p) { printf(" %s", sha1_to_hex(p->parent->sha1));