[PATCH] cvs2git and file permissions
[git/dscho.git] / cvs2git.c
blobd438475d03b7ff897a9e2b976a34a75b86c01a80
1 /*
2 * cvs2git
4 * Copyright (C) Linus Torvalds 2005
5 */
7 #include <stdio.h>
8 #include <ctype.h>
9 #include <string.h>
10 #include <stdlib.h>
11 #include <unistd.h>
13 static int verbose = 0;
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.
20 * You've been warned. I told you it was stupid.
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 ;)
28 * Usage:
30 * TZ=UTC cvsps -A |
31 * cvs2git --cvsroot=[root] --module=[module] > script
33 * Creates a shell script that will generate the .git archive of
34 * the names CVS repository.
36 * IMPORTANT NOTE ABOUT "cvsps"! This requires version 2.1 or better,
37 * and the "TZ=UTC" and the "-A" flag is required for sane results!
39 enum state {
40 Header,
41 Log,
42 Members
45 static const char *cvsroot;
46 static const char *cvsmodule;
48 static char date[100];
49 static char author[100];
50 static char branch[100];
51 static char ancestor[100];
52 static char tag[100];
53 static char log[32768];
54 static int loglen = 0;
55 static int initial_commit = 1;
57 static void lookup_author(char *n, char **name, char **email)
60 * FIXME!!! I'm lazy and stupid.
62 * This could be something like
64 * printf("lookup_author '%s'\n", n);
65 * *name = "$author_name";
66 * *email = "$author_email";
68 * and that would allow the script to do its own
69 * lookups at run-time.
71 *name = n;
72 *email = n;
75 static void prepare_commit(void)
77 char *author_name, *author_email;
78 char *src_branch;
80 lookup_author(author, &author_name, &author_email);
82 printf("export GIT_COMMITTER_NAME=%s\n", author_name);
83 printf("export GIT_COMMITTER_EMAIL=%s\n", author_email);
84 printf("export GIT_COMMITTER_DATE='+0000 %s'\n", date);
86 printf("export GIT_AUTHOR_NAME=%s\n", author_name);
87 printf("export GIT_AUTHOR_EMAIL=%s\n", author_email);
88 printf("export GIT_AUTHOR_DATE='+0000 %s'\n", date);
90 if (initial_commit)
91 return;
93 src_branch = *ancestor ? ancestor : branch;
94 if (!strcmp(src_branch, "HEAD"))
95 src_branch = "master";
96 printf("ln -sf refs/heads/'%s' .git/HEAD\n", src_branch);
99 * Even if cvsps claims an ancestor, we'll let the new
100 * branch name take precedence if it already exists
102 if (*ancestor) {
103 src_branch = branch;
104 if (!strcmp(src_branch, "HEAD"))
105 src_branch = "master";
106 printf("[ -e .git/refs/heads/'%s' ] && ln -sf refs/heads/'%s' .git/HEAD\n",
107 src_branch, src_branch);
110 printf("git-read-tree -m HEAD || exit 1\n");
111 printf("git-checkout-cache -f -u -a\n");
114 static void commit(void)
116 const char *cmit_parent = initial_commit ? "" : "-p HEAD";
117 const char *dst_branch;
118 char *space;
119 int i;
121 printf("tree=$(git-write-tree)\n");
122 printf("cat > .cmitmsg <<EOFMSG\n");
124 /* Escape $ characters, and remove control characters */
125 for (i = 0; i < loglen; i++) {
126 unsigned char c = log[i];
128 switch (c) {
129 case '$':
130 case '\\':
131 case '`':
132 putchar('\\');
133 break;
134 case 0 ... 31:
135 if (c == '\n' || c == '\t')
136 break;
137 case 128 ... 159:
138 continue;
140 putchar(c);
142 printf("\nEOFMSG\n");
143 printf("commit=$(cat .cmitmsg | git-commit-tree $tree %s)\n", cmit_parent);
145 dst_branch = branch;
146 if (!strcmp(dst_branch, "HEAD"))
147 dst_branch = "master";
149 printf("echo $commit > .git/refs/heads/'%s'\n", dst_branch);
151 space = strchr(tag, ' ');
152 if (space)
153 *space = 0;
154 if (strcmp(tag, "(none)"))
155 printf("echo $commit > .git/refs/tags/'%s'\n", tag);
157 printf("echo 'Committed (to %s):' ; cat .cmitmsg; echo\n", dst_branch);
159 *date = 0;
160 *author = 0;
161 *branch = 0;
162 *ancestor = 0;
163 *tag = 0;
164 loglen = 0;
166 initial_commit = 0;
169 static void update_file(char *line)
171 char *name, *version;
172 char *dir;
174 while (isspace(*line))
175 line++;
176 name = line;
177 line = strchr(line, ':');
178 if (!line)
179 return;
180 *line++ = 0;
181 line = strchr(line, '>');
182 if (!line)
183 return;
184 *line++ = 0;
185 version = line;
186 line = strchr(line, '(');
187 if (line) { /* "(DEAD)" */
188 printf("git-update-cache --force-remove '%s'\n", name);
189 return;
192 dir = strrchr(name, '/');
193 if (dir)
194 printf("mkdir -p %.*s\n", (int)(dir - name), name);
196 printf("cvs -q -d %s checkout -d .git-tmp -r%s '%s/%s'\n",
197 cvsroot, version, cvsmodule, name);
198 printf("mv -f .git-tmp/%s %s\n", dir ? dir+1 : name, name);
199 printf("rm -rf .git-tmp\n");
200 printf("git-update-cache --add -- '%s'\n", name);
203 struct 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 }
216 int main(int argc, char **argv)
218 static char line[1000];
219 enum state state = Header;
220 int i;
222 for (i = 1; i < argc; i++) {
223 const char *arg = argv[i];
224 if (!memcmp(arg, "--cvsroot=", 10)) {
225 cvsroot = arg + 10;
226 continue;
228 if (!memcmp(arg, "--module=", 9)) {
229 cvsmodule = arg+9;
230 continue;
232 if (!strcmp(arg, "-v")) {
233 verbose = 1;
234 continue;
239 if (!cvsroot)
240 cvsroot = getenv("CVSROOT");
242 if (!cvsmodule || !cvsroot) {
243 fprintf(stderr, "I need a CVSROOT and module name\n");
244 exit(1);
247 printf("[ -d .git ] && exit 1\n");
248 printf("git-init-db\n");
249 printf("mkdir -p .git/refs/heads\n");
250 printf("mkdir -p .git/refs/tags\n");
251 printf("ln -sf refs/heads/master .git/HEAD\n");
253 while (fgets(line, sizeof(line), stdin) != NULL) {
254 int linelen = strlen(line);
256 while (linelen && isspace(line[linelen-1]))
257 line[--linelen] = 0;
259 switch (state) {
260 struct hdrentry *entry;
262 case Header:
263 if (verbose)
264 printf("# H: %s\n", line);
265 for (entry = hdrs ; entry->name ; entry++) {
266 int len = strlen(entry->name);
267 char *val;
269 if (memcmp(entry->name, line, len))
270 continue;
271 if (!entry->dest) {
272 state = Log;
273 break;
275 val = line + len;
276 linelen -= len;
277 while (isspace(*val)) {
278 val++;
279 linelen--;
281 memcpy(entry->dest, val, linelen+1);
282 break;
284 continue;
286 case Log:
287 if (verbose)
288 printf("# L: %s\n", line);
289 if (!strcmp(line, "Members:")) {
290 while (loglen && isspace(log[loglen-1]))
291 log[--loglen] = 0;
292 prepare_commit();
293 state = Members;
294 continue;
297 if (loglen + linelen + 5 > sizeof(log))
298 continue;
299 memcpy(log + loglen, line, linelen);
300 loglen += linelen;
301 log[loglen++] = '\n';
302 continue;
304 case Members:
305 if (verbose)
306 printf("# M: %s\n", line);
307 if (!linelen) {
308 commit();
309 state = Header;
310 continue;
312 update_file(line);
313 continue;
316 return 0;