Make the revision tracking track the object types too.
[git.git] / revision.h
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
18 struct parent {
19         struct revision *parent;
20         struct parent *next;
21 };
22
23 struct revision {
24         unsigned int flags;
25         unsigned char sha1[20];
26         unsigned long date;
27         struct parent *parent;
28         char tag[1];
29 };
30
31 static struct revision **revs;
32 static int nr_revs, rev_allocs;
33
34 static 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
55 static 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
88 static 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
106 static 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
120 static 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
138 static 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 */