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