1#ifndef REVISION_H
2#define REVISION_H
3
4/*
5 * The low 16 bits of the "flags" field shows whether
6 * a commit is part of the path to the root for that
7 * parent.
8 *
9 * Bit 16 is an internal flag that we've seen the
10 * definition for this rev, and not just seen it as
11 * a parent target.
12 */
13#define marked(rev) ((rev)->flags & 0xffff)
14#define SEEN 0x10000
15#define USED 0x20000
16#define REACHABLE 0x40000
17
18struct parent {
19 struct revision *parent;
20 struct parent *next;
21};
22
23struct revision {
24 unsigned int flags;
25 unsigned char sha1[20];
26 unsigned long date;
27 struct parent *parent;
28 char tag[1];
29};
30
31static struct revision **revs;
32static int nr_revs, rev_allocs;
33
34static int find_rev(unsigned char *sha1)
35{
36 int first = 0, last = nr_revs;
37
38 while (first < last) {
39 int next = (first + last) / 2;
40 struct revision *rev = revs[next];
41 int cmp;
42
43 cmp = memcmp(sha1, rev->sha1, 20);
44 if (!cmp)
45 return next;
46 if (cmp < 0) {
47 last = next;
48 continue;
49 }
50 first = next+1;
51 }
52 return -first-1;
53}
54
55static struct revision *lookup_rev(unsigned char *sha1, const char *tag)
56{
57 int pos = find_rev(sha1);
58 struct revision *n;
59
60 if (pos >= 0) {
61 n = revs[pos];
62 if (strcmp(n->tag, tag))
63 error("expected tag %s on object %s: got %s", tag, sha1_to_hex(sha1), n->tag);
64 return n;
65 }
66
67 pos = -pos-1;
68
69 if (rev_allocs == nr_revs) {
70 rev_allocs = alloc_nr(rev_allocs);
71 revs = realloc(revs, rev_allocs * sizeof(struct revision *));
72 }
73 n = malloc(sizeof(struct revision) + strlen(tag));
74
75 n->flags = 0;
76 memcpy(n->sha1, sha1, 20);
77 n->parent = NULL;
78 strcpy(n->tag, tag);
79
80 /* Insert it into the right place */
81 memmove(revs + pos + 1, revs + pos, (nr_revs - pos) * sizeof(struct revision *));
82 revs[pos] = n;
83 nr_revs++;
84
85 return n;
86}
87
88static struct revision *add_relationship(struct revision *rev, unsigned char *needs, const char *tag)
89{
90 struct revision *parent_rev = lookup_rev(needs, tag);
91 struct parent **pp = &rev->parent, *p;
92
93 while ((p = *pp) != NULL) {
94 if (p->parent == parent_rev)
95 return parent_rev;
96 pp = &p->next;
97 }
98
99 p = malloc(sizeof(*p));
100 p->parent = parent_rev;
101 p->next = NULL;
102 *pp = p;
103 return parent_rev;
104}
105
106static void mark_reachable(struct revision *rev, unsigned int mask)
107{
108 struct parent *p = rev->parent;
109
110 /* If we've been here already, don't bother */
111 if (rev->flags & mask)
112 return;
113 rev->flags |= mask | USED;
114 while (p) {
115 mark_reachable(p->parent, mask);
116 p = p->next;
117 }
118}
119
120static unsigned long parse_commit_date(const char *buf)
121{
122 unsigned long date;
123
124 if (memcmp(buf, "author", 6))
125 return 0;
126 while (*buf++ != '\n')
127 /* nada */;
128 if (memcmp(buf, "committer", 9))
129 return 0;
130 while (*buf++ != '>')
131 /* nada */;
132 date = strtoul(buf, NULL, 10);
133 if (date == ULONG_MAX)
134 date = 0;
135 return date;
136}
137
138static struct revision * parse_commit(unsigned char *sha1)
139{
140 struct revision *rev = lookup_rev(sha1, "commit");
141
142 if (!(rev->flags & SEEN)) {
143 void *buffer, *bufptr;
144 unsigned long size;
145 char type[20];
146 unsigned char parent[20];
147
148 rev->flags |= SEEN;
149 buffer = bufptr = read_sha1_file(sha1, type, &size);
150 if (!buffer || strcmp(type, "commit"))
151 die("%s is not a commit object", sha1_to_hex(sha1));
152 bufptr += 46; /* "tree " + "hex sha1" + "\n" */
153 while (!memcmp(bufptr, "parent ", 7) && !get_sha1_hex(bufptr+7, parent)) {
154 add_relationship(rev, parent, "commit");
155 parse_commit(parent);
156 bufptr += 48; /* "parent " + "hex sha1" + "\n" */
157 }
158 rev->date = parse_commit_date(bufptr);
159 free(buffer);
160 }
161 return rev;
162}
163
164#endif /* REVISION_H */