]> git.ipfire.org Git - thirdparty/git.git/blame - commit-tree.c
Make the revision tracking track the object types too.
[thirdparty/git.git] / commit-tree.c
CommitLineData
8bc9a0c7
LT
1/*
2 * GIT - The information manager from hell
3 *
4 * Copyright (C) Linus Torvalds, 2005
5 */
e83c5163
LT
6#include "cache.h"
7
8#include <pwd.h>
9#include <time.h>
27de946d
DW
10#include <string.h>
11#include <ctype.h>
12#include <time.h>
e83c5163
LT
13
14#define BLOCKING (1ul << 14)
15#define ORIG_OFFSET (40)
16
17/*
18 * Leave space at the beginning to insert the tag
19 * once we know how big things are.
20 *
21 * FIXME! Share the code with "write-tree.c"
22 */
23static void init_buffer(char **bufp, unsigned int *sizep)
24{
25 char *buf = malloc(BLOCKING);
26 memset(buf, 0, ORIG_OFFSET);
27 *sizep = ORIG_OFFSET;
28 *bufp = buf;
29}
30
31static void add_buffer(char **bufp, unsigned int *sizep, const char *fmt, ...)
32{
33 char one_line[2048];
34 va_list args;
35 int len;
36 unsigned long alloc, size, newsize;
37 char *buf;
38
39 va_start(args, fmt);
40 len = vsnprintf(one_line, sizeof(one_line), fmt, args);
41 va_end(args);
42 size = *sizep;
43 newsize = size + len;
44 alloc = (size + 32767) & ~32767;
45 buf = *bufp;
46 if (newsize > alloc) {
aebb2679 47 alloc = (newsize + 32767) & ~32767;
e83c5163
LT
48 buf = realloc(buf, alloc);
49 *bufp = buf;
50 }
51 *sizep = newsize;
52 memcpy(buf + size, one_line, len);
53}
54
55static int prepend_integer(char *buffer, unsigned val, int i)
56{
57 buffer[--i] = '\0';
58 do {
59 buffer[--i] = '0' + (val % 10);
60 val /= 10;
61 } while (val);
62 return i;
63}
64
65static void finish_buffer(char *tag, char **bufp, unsigned int *sizep)
66{
67 int taglen;
68 int offset;
69 char *buf = *bufp;
70 unsigned int size = *sizep;
71
72 offset = prepend_integer(buf, size - ORIG_OFFSET, ORIG_OFFSET);
73 taglen = strlen(tag);
74 offset -= taglen;
75 buf += offset;
76 size -= offset;
77 memcpy(buf, tag, taglen);
78
79 *bufp = buf;
80 *sizep = size;
81}
82
83static void remove_special(char *p)
84{
85 char c;
86 char *dst = p;
87
88 for (;;) {
89 c = *p;
90 p++;
91 switch(c) {
92 case '\n': case '<': case '>':
93 continue;
94 }
95 *dst++ = c;
96 if (!c)
97 break;
98 }
99}
100
27de946d
DW
101static const char *month_names[] = {
102 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
103 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
104};
105
106static const char *weekday_names[] = {
107 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
108};
109
110
111static char *skipfws(char *str)
112{
113 while (isspace(*str))
114 str++;
115 return str;
116}
117
118
119/* Gr. strptime is crap for this; it doesn't have a way to require RFC2822
120 (i.e. English) day/month names, and it doesn't work correctly with %z. */
121static void parse_rfc2822_date(char *date, char *result, int maxlen)
122{
123 struct tm tm;
124 char *p;
125 int i, offset;
126 time_t then;
127
128 memset(&tm, 0, sizeof(tm));
129
130 /* Skip day-name */
131 p = skipfws(date);
132 if (!isdigit(*p)) {
133 for (i=0; i<7; i++) {
134 if (!strncmp(p,weekday_names[i],3) && p[3] == ',') {
135 p = skipfws(p+4);
136 goto day;
137 }
138 }
139 return;
140 }
141
142 /* day */
143 day:
144 tm.tm_mday = strtoul(p, &p, 10);
145
146 if (tm.tm_mday < 1 || tm.tm_mday > 31)
147 return;
148
149 if (!isspace(*p))
150 return;
151
152 p = skipfws(p);
153
154 /* month */
155
156 for (i=0; i<12; i++) {
157 if (!strncmp(p, month_names[i], 3) && isspace(p[3])) {
158 tm.tm_mon = i;
159 p = skipfws(p+strlen(month_names[i]));
160 goto year;
161 }
162 }
163 return; /* Error -- bad month */
164
165 /* year */
166 year:
167 tm.tm_year = strtoul(p, &p, 10);
168
169 if (!tm.tm_year && !isspace(*p))
170 return;
171
172 if (tm.tm_year > 1900)
173 tm.tm_year -= 1900;
174
175 p=skipfws(p);
176
177 /* hour */
178 if (!isdigit(*p))
179 return;
180 tm.tm_hour = strtoul(p, &p, 10);
181
182 if (!tm.tm_hour > 23)
183 return;
184
185 if (*p != ':')
186 return; /* Error -- bad time */
187 p++;
188
189 /* minute */
190 if (!isdigit(*p))
191 return;
192 tm.tm_min = strtoul(p, &p, 10);
193
194 if (!tm.tm_min > 59)
195 return;
196
197 if (isspace(*p))
198 goto zone;
199
200 if (*p != ':')
201 return; /* Error -- bad time */
202 p++;
203
204 /* second */
205 if (!isdigit(*p))
206 return;
207 tm.tm_sec = strtoul(p, &p, 10);
208
209 if (!tm.tm_sec > 59)
210 return;
211
212 if (!isspace(*p))
213 return;
214
215 zone:
216 p = skipfws(p);
217
218 if (*p == '-')
219 offset = -60;
220 else if (*p == '+')
221 offset = 60;
222 else
223 return;
224
225 if (!isdigit(p[1]) || !isdigit(p[2]) || !isdigit(p[3]) || !isdigit(p[4]))
226 return;
227
228 i = strtoul(p+1, NULL, 10);
229 offset *= ((i % 100) + ((i / 100) * 60));
230
231 if (*(skipfws(p + 5)))
232 return;
233
234 then = mktime(&tm); /* mktime appears to ignore the GMT offset, stupidly */
235 if (then == -1)
236 return;
237
238 then -= offset;
239
240 snprintf(result, maxlen, "%lu %5.5s", then, p);
241}
242
d0d7cbe7
LT
243static void check_valid(unsigned char *sha1, const char *expect)
244{
245 void *buf;
246 char type[20];
247 unsigned long size;
248
249 buf = read_sha1_file(sha1, type, &size);
250 if (!buf || strcmp(type, expect))
251 die("%s is not a valid '%s' object", sha1_to_hex(sha1), expect);
252 free(buf);
253}
254
e83c5163
LT
255/*
256 * Having more than two parents may be strange, but hey, there's
257 * no conceptual reason why the file format couldn't accept multi-way
258 * merges. It might be the "union" of several packages, for example.
259 *
260 * I don't really expect that to happen, but this is here to make
261 * it clear that _conceptually_ it's ok..
262 */
263#define MAXPARENT (16)
264
265int main(int argc, char **argv)
266{
267 int i, len;
268 int parents = 0;
269 unsigned char tree_sha1[20];
270 unsigned char parent_sha1[MAXPARENT][20];
d6d3f9d0 271 unsigned char commit_sha1[20];
e83c5163
LT
272 char *gecos, *realgecos;
273 char *email, realemail[1000];
27de946d
DW
274 char date[20], realdate[20];
275 char *audate;
e83c5163
LT
276 char comment[1000];
277 struct passwd *pw;
278 time_t now;
27de946d 279 struct tm *tm;
e83c5163
LT
280 char *buffer;
281 unsigned int size;
282
283 if (argc < 2 || get_sha1_hex(argv[1], tree_sha1) < 0)
284 usage("commit-tree <sha1> [-p <sha1>]* < changelog");
285
d0d7cbe7 286 check_valid(tree_sha1, "tree");
e83c5163
LT
287 for (i = 2; i < argc; i += 2) {
288 char *a, *b;
289 a = argv[i]; b = argv[i+1];
290 if (!b || strcmp(a, "-p") || get_sha1_hex(b, parent_sha1[parents]))
291 usage("commit-tree <sha1> [-p <sha1>]* < changelog");
d0d7cbe7 292 check_valid(parent_sha1[parents], "commit");
e83c5163
LT
293 parents++;
294 }
295 if (!parents)
296 fprintf(stderr, "Committing initial tree %s\n", argv[1]);
297 pw = getpwuid(getuid());
298 if (!pw)
2de381f9 299 die("You don't exist. Go away!");
e83c5163
LT
300 realgecos = pw->pw_gecos;
301 len = strlen(pw->pw_name);
302 memcpy(realemail, pw->pw_name, len);
303 realemail[len] = '@';
304 gethostname(realemail+len+1, sizeof(realemail)-len-1);
305 time(&now);
27de946d
DW
306 tm = localtime(&now);
307
308 strftime(realdate, sizeof(realdate), "%s %z", tm);
309 strcpy(date, realdate);
e83c5163 310
bf16c71e
LT
311 gecos = getenv("AUTHOR_NAME") ? : realgecos;
312 email = getenv("AUTHOR_EMAIL") ? : realemail;
27de946d
DW
313 audate = getenv("AUTHOR_DATE");
314 if (audate)
315 parse_rfc2822_date(audate, date, sizeof(date));
e83c5163
LT
316
317 remove_special(gecos); remove_special(realgecos);
318 remove_special(email); remove_special(realemail);
e83c5163
LT
319
320 init_buffer(&buffer, &size);
321 add_buffer(&buffer, &size, "tree %s\n", sha1_to_hex(tree_sha1));
322
323 /*
324 * NOTE! This ordering means that the same exact tree merged with a
325 * different order of parents will be a _different_ changeset even
326 * if everything else stays the same.
327 */
328 for (i = 0; i < parents; i++)
329 add_buffer(&buffer, &size, "parent %s\n", sha1_to_hex(parent_sha1[i]));
330
331 /* Person/date information */
332 add_buffer(&buffer, &size, "author %s <%s> %s\n", gecos, email, date);
333 add_buffer(&buffer, &size, "committer %s <%s> %s\n\n", realgecos, realemail, realdate);
334
335 /* And add the comment */
336 while (fgets(comment, sizeof(comment), stdin) != NULL)
337 add_buffer(&buffer, &size, "%s", comment);
338
339 finish_buffer("commit ", &buffer, &size);
340
d6d3f9d0
LT
341 write_sha1_file(buffer, size, commit_sha1);
342 printf("%s\n", sha1_to_hex(commit_sha1));
e83c5163
LT
343 return 0;
344}