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