1#define _XOPEN_SOURCE /* glibc2 needs this */ 2#define _BSD_SOURCE /* for tm.tm_gmtoff */ 3#include <time.h> 4#include <ctype.h> 5 6#include "cache.h" 7#include "revision.h" 8 9/* 10 * revision.h leaves the low 16 bits of the "flags" field of the 11 * revision data structure unused. We use it for a "reachable from 12 * this commit <N>" bitmask. 13 */ 14#define MAX_COMMITS 16 15 16static int show_edges = 0; 17static int basemask = 0; 18 19static void read_cache_file(const char *path) 20{ 21 FILE *file = fopen(path, "r"); 22 char line[500]; 23 24 if (!file) 25 die("bad revtree cache file (%s)", path); 26 27 while (fgets(line, sizeof(line), file)) { 28 unsigned long date; 29 unsigned char sha1[20]; 30 struct revision *rev; 31 const char *buf; 32 33 if (sscanf(line, "%lu", &date) != 1) 34 break; 35 buf = strchr(line, ' '); 36 if (!buf) 37 break; 38 if (get_sha1_hex(buf+1, sha1)) 39 break; 40 rev = lookup_rev(sha1); 41 rev->flags |= SEEN; 42 rev->date = date; 43 44 /* parents? */ 45 while ((buf = strchr(buf+1, ' ')) != NULL) { 46 unsigned char parent[20]; 47 if (get_sha1_hex(buf + 1, parent)) 48 break; 49 add_relationship(rev, parent); 50 } 51 } 52 fclose(file); 53} 54 55/* 56 * Some revisions are less interesting than others. 57 * 58 * For example, if we use a cache-file, that one may contain 59 * revisions that were never used. They are never interesting. 60 * 61 * And sometimes we're only interested in "edge" commits, ie 62 * places where the marking changes between parent and child. 63 */ 64static int interesting(struct revision *rev) 65{ 66 unsigned mask = marked(rev); 67 68 if (!mask) 69 return 0; 70 if (show_edges) { 71 struct parent *p = rev->parent; 72 while (p) { 73 if (mask != marked(p->parent)) 74 return 1; 75 p = p->next; 76 } 77 return 0; 78 } 79 if (mask & basemask) 80 return 0; 81 82 return 1; 83} 84 85/* 86 * Usage: rev-tree [--edges] [--cache <cache-file>] <commit-id> [<commit-id2>] 87 * 88 * The cache-file can be quite important for big trees. This is an 89 * expensive operation if you have to walk the whole chain of 90 * parents in a tree with a long revision history. 91 */ 92int main(int argc, char **argv) 93{ 94 int i; 95 int nr = 0; 96 unsigned char sha1[MAX_COMMITS][20]; 97 98 /* 99 * First - pick up all the revisions we can (both from 100 * caches and from commit file chains). 101 */ 102 for (i = 1; i < argc ; i++) { 103 char *arg = argv[i]; 104 105 if (!strcmp(arg, "--cache")) { 106 read_cache_file(argv[2]); 107 i++; 108 continue; 109 } 110 111 if (!strcmp(arg, "--edges")) { 112 show_edges = 1; 113 continue; 114 } 115 116 if (arg[0] == '^') { 117 arg++; 118 basemask |= 1<<nr; 119 } 120 if (nr >= MAX_COMMITS || get_sha1_hex(arg, sha1[nr])) 121 usage("rev-tree [--edges] [--cache <cache-file>] <commit-id> [<commit-id>]"); 122 parse_commit(sha1[nr]); 123 nr++; 124 } 125 126 /* 127 * Now we have the maximal tree. Walk the different sha files back to the root. 128 */ 129 for (i = 0; i < nr; i++) 130 mark_reachable(lookup_rev(sha1[i]), 1 << i); 131 132 /* 133 * Now print out the results.. 134 */ 135 for (i = 0; i < nr_revs; i++) { 136 struct revision *rev = revs[i]; 137 struct parent *p; 138 139 if (!interesting(rev)) 140 continue; 141 142 printf("%lu %s:%d", rev->date, sha1_to_hex(rev->sha1), marked(rev)); 143 p = rev->parent; 144 while (p) { 145 printf(" %s:%d", sha1_to_hex(p->parent->sha1), marked(p->parent)); 146 p = p->next; 147 } 148 printf("\n"); 149 } 150 return 0; 151}