1adc918f51d668fe818468213f64b5d476148bb3
1/*
2 * cvs2git
3 *
4 * Copyright (C) Linus Torvalds 2005
5 */
6
7#include <stdio.h>
8#include <ctype.h>
9#include <string.h>
10#include <stdlib.h>
11#include <unistd.h>
12
13static int verbose = 0;
14
15/*
16 * This is a really stupid program that takes cvsps output, and
17 * generates a a long _shell_script_ that will create the GIT archive
18 * from it.
19 *
20 * You've been warned. I told you it was stupid.
21 *
22 * NOTE NOTE NOTE! In order to do branches correctly, this needs
23 * the fixed cvsps that has the "Ancestor branch" tag output.
24 * Hopefully David Mansfield will update his distribution soon
25 * enough (he's the one who wrote the patch, so at least we don't
26 * have to figt maintainer issues ;)
27 */
28enum state {
29 Header,
30 Log,
31 Members
32};
33
34static char *rcsdir;
35
36static char date[100];
37static char author[100];
38static char branch[100];
39static char ancestor[100];
40static char tag[100];
41static char log[32768];
42static int loglen = 0;
43static int initial_commit = 1;
44
45static void lookup_author(char *n, char **name, char **email)
46{
47 /*
48 * FIXME!!! I'm lazy and stupid.
49 *
50 * This could be something like
51 *
52 * printf("lookup_author '%s'\n", n);
53 * *name = "$author_name";
54 * *email = "$author_email";
55 *
56 * and that would allow the script to do its own
57 * lookups at run-time.
58 */
59 *name = n;
60 *email = n;
61}
62
63static void prepare_commit(void)
64{
65 char *author_name, *author_email;
66 char *src_branch;
67
68 lookup_author(author, &author_name, &author_email);
69
70 printf("export GIT_COMMITTER_NAME=%s\n", author_name);
71 printf("export GIT_COMMITTER_EMAIL=%s\n", author_email);
72 printf("export GIT_COMMITTER_DATE='+0000 %s'\n", date);
73
74 printf("export GIT_AUTHOR_NAME=%s\n", author_name);
75 printf("export GIT_AUTHOR_EMAIL=%s\n", author_email);
76 printf("export GIT_AUTHOR_DATE='+0000 %s'\n", date);
77
78 if (initial_commit)
79 return;
80
81 src_branch = *ancestor ? ancestor : branch;
82 if (!strcmp(src_branch, "HEAD"))
83 src_branch = "master";
84 printf("ln -sf refs/heads/'%s' .git/HEAD\n", src_branch);
85
86 /*
87 * Even if cvsps claims an ancestor, we'll let the new
88 * branch name take precedence if it already exists
89 */
90 if (*ancestor) {
91 src_branch = branch;
92 if (!strcmp(src_branch, "HEAD"))
93 src_branch = "master";
94 printf("[ -e .git/refs/heads/'%s' ] && ln -sf refs/heads/'%s' .git/HEAD\n",
95 src_branch, src_branch);
96 }
97
98 printf("git-read-tree -m HEAD || exit 1\n");
99 printf("git-checkout-cache -f -u -a\n");
100}
101
102static void commit(void)
103{
104 const char *cmit_parent = initial_commit ? "" : "-p HEAD";
105 const char *dst_branch;
106 int i;
107
108 printf("tree=$(git-write-tree)\n");
109 printf("cat > .cmitmsg <<EOFMSG\n");
110
111 /* Escape $ characters, and remove control characters */
112 for (i = 0; i < loglen; i++) {
113 unsigned char c = log[i];
114
115 switch (c) {
116 case '$':
117 case '\\':
118 case '`':
119 putchar('\\');
120 break;
121 case 0 ... 31:
122 if (c == '\n' || c == '\t')
123 break;
124 case 128 ... 159:
125 continue;
126 }
127 putchar(c);
128 }
129 printf("\nEOFMSG\n");
130 printf("commit=$(cat .cmitmsg | git-commit-tree $tree %s)\n", cmit_parent);
131
132 dst_branch = branch;
133 if (!strcmp(dst_branch, "HEAD"))
134 dst_branch = "master";
135
136 printf("echo $commit > .git/refs/heads/'%s'\n", dst_branch);
137
138 printf("echo 'Committed (to %s):' ; cat .cmitmsg; echo\n", dst_branch);
139
140 *date = 0;
141 *author = 0;
142 *branch = 0;
143 *ancestor = 0;
144 *tag = 0;
145 loglen = 0;
146
147 initial_commit = 0;
148}
149
150static void get_rcs_name(char *rcspathname, char *name, char *dir)
151{
152 sprintf(rcspathname, "%s/%s,v", rcsdir, name);
153 if (!access(rcspathname, R_OK))
154 return;
155
156 sprintf(rcspathname, "%s/Attic/%s,v", rcsdir, name);
157 if (!access(rcspathname, R_OK))
158 return;
159
160 if (dir) {
161 sprintf(rcspathname, "%s/%.*s/Attic/%s,v", rcsdir, (int)(dir - name), name, dir+1);
162 if (!access(rcspathname, R_OK))
163 return;
164 }
165 fprintf(stderr, "Unable to find RCS file for %s\n", name);
166 exit(1);
167}
168
169static void update_file(char *line)
170{
171 static char rcspathname[4096];
172 char *name, *version;
173 char *dir;
174
175 while (isspace(*line))
176 line++;
177 name = line;
178 line = strchr(line, ':');
179 if (!line)
180 return;
181 *line++ = 0;
182 line = strchr(line, '>');
183 if (!line)
184 return;
185 *line++ = 0;
186 version = line;
187 line = strchr(line, '(');
188 if (line) { /* "(DEAD)" */
189 printf("git-update-cache --force-remove '%s'\n", name);
190 return;
191 }
192
193 dir = strrchr(name, '/');
194 if (dir)
195 printf("mkdir -p %.*s\n", (int)(dir - name), name);
196
197 get_rcs_name(rcspathname, name, dir);
198
199 printf("co -q -p -r%s '%s' > '%s'\n", version, rcspathname, name);
200 printf("git-update-cache --add -- '%s'\n", name);
201}
202
203struct hdrentry {
204 const char *name;
205 char *dest;
206} hdrs[] = {
207 { "Date:", date },
208 { "Author:", author },
209 { "Branch:", branch },
210 { "Ancestor branch:", ancestor },
211 { "Tag:", tag },
212 { "Log:", NULL },
213 { NULL, NULL }
214};
215
216int main(int argc, char **argv)
217{
218 static char line[1000];
219 enum state state = Header;
220
221 rcsdir = getenv("RCSDIR");
222 if (!rcsdir) {
223 fprintf(stderr, "I need an $RCSDIR\n");
224 exit(1);
225 }
226
227 printf("[ -d .git ] && exit 1\n");
228 printf("git-init-db\n");
229 printf("mkdir -p .git/refs/heads\n");
230 printf("mkdir -p .git/refs/tags\n");
231 printf("ln -sf refs/heads/master .git/HEAD\n");
232
233 while (fgets(line, sizeof(line), stdin) != NULL) {
234 int linelen = strlen(line);
235
236 while (linelen && isspace(line[linelen-1]))
237 line[--linelen] = 0;
238
239 switch (state) {
240 struct hdrentry *entry;
241
242 case Header:
243 if (verbose)
244 printf("# H: %s\n", line);
245 for (entry = hdrs ; entry->name ; entry++) {
246 int len = strlen(entry->name);
247 char *val;
248
249 if (memcmp(entry->name, line, len))
250 continue;
251 if (!entry->dest) {
252 state = Log;
253 break;
254 }
255 val = line + len;
256 linelen -= len;
257 while (isspace(*val)) {
258 val++;
259 linelen--;
260 }
261 memcpy(entry->dest, val, linelen+1);
262 break;
263 }
264 continue;
265
266 case Log:
267 if (verbose)
268 printf("# L: %s\n", line);
269 if (!strcmp(line, "Members:")) {
270 while (loglen && isspace(log[loglen-1]))
271 log[--loglen] = 0;
272 prepare_commit();
273 state = Members;
274 continue;
275 }
276
277 if (loglen + linelen + 5 > sizeof(log))
278 continue;
279 memcpy(log + loglen, line, linelen);
280 loglen += linelen;
281 log[loglen++] = '\n';
282 continue;
283
284 case Members:
285 if (verbose)
286 printf("# M: %s\n", line);
287 if (!linelen) {
288 commit();
289 state = Header;
290 continue;
291 }
292 update_file(line);
293 continue;
294 }
295 }
296 return 0;
297}