c18f904f59bf0e44241ea57e8dac46452fa3194c
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
73 printf("export GIT_AUTHOR_NAME=%s\n", author_name);
74 printf("export GIT_AUTHOR_EMAIL=%s\n", author_email);
75
76 printf("export GIT_AUTHOR_DATE='%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 printf("git-read-tree -m HEAD\n");
86 printf("git-checkout-cache -f -u -a\n");
87}
88
89static void commit(void)
90{
91 const char *cmit_parent = initial_commit ? "" : "-p HEAD";
92
93 printf("tree=$(git-write-tree)\n");
94 printf("cat > .cmitmsg <<EOFMSG\n%s\nEOFMSG\n", log);
95 printf("commit=$(cat .cmitmsg | git-commit-tree $tree %s)\n", cmit_parent);
96 printf("echo $commit > .git/HEAD\n");
97
98 *date = 0;
99 *author = 0;
100 *branch = 0;
101 *ancestor = 0;
102 *tag = 0;
103 loglen = 0;
104
105 initial_commit = 0;
106}
107
108static void get_rcs_name(char *rcspathname, char *name, char *dir)
109{
110 sprintf(rcspathname, "%s/%s,v", rcsdir, name);
111 if (!access(rcspathname, R_OK))
112 return;
113
114 sprintf(rcspathname, "%s/Attic/%s,v", rcsdir, name);
115 if (!access(rcspathname, R_OK))
116 return;
117
118 if (dir) {
119 sprintf(rcspathname, "%s/%.*s/Attic/%s,v", rcsdir, (int)(dir - name), name, dir+1);
120 if (!access(rcspathname, R_OK))
121 return;
122 }
123 fprintf(stderr, "Unable to find RCS file for %s\n", name);
124 exit(1);
125}
126
127static void update_file(char *line)
128{
129 static char rcspathname[4096];
130 char *name, *version;
131 char *dir;
132
133 while (isspace(*line))
134 line++;
135 name = line;
136 line = strchr(line, ':');
137 if (!line)
138 return;
139 *line++ = 0;
140 line = strchr(line, '>');
141 if (!line)
142 return;
143 *line++ = 0;
144 version = line;
145 line = strchr(line, '(');
146 if (line) { /* "(DEAD)" */
147 printf("git-update-cache --force-remove '%s'\n", name);
148 return;
149 }
150
151 dir = strrchr(name, '/');
152 if (dir)
153 printf("mkdir -p %.*s\n", (int)(dir - name), name);
154
155 get_rcs_name(rcspathname, name, dir);
156
157 printf("co -p -r%s '%s' > '%s'\n", version, rcspathname, name);
158 printf("git-update-cache --add -- '%s'\n", name);
159}
160
161struct hdrentry {
162 const char *name;
163 char *dest;
164} hdrs[] = {
165 { "Date:", date },
166 { "Author:", author },
167 { "Branch:", branch },
168 { "Ancestor branch:", ancestor },
169 { "Tag:", tag },
170 { "Log:", NULL },
171 { NULL, NULL }
172};
173
174int main(int argc, char **argv)
175{
176 static char line[1000];
177 enum state state = Header;
178
179 rcsdir = getenv("RCSDIR");
180 if (!rcsdir) {
181 fprintf(stderr, "I need an $RCSDIR\n");
182 exit(1);
183 }
184
185 printf("[ -d .git ] && exit 1\n");
186 printf("git-init-db\n");
187 printf("mkdir -p .git/refs/heads\n");
188 printf("mkdir -p .git/refs/tags\n");
189 printf("ln -sf refs/heads/master .git/HEAD\n");
190
191 while (fgets(line, sizeof(line), stdin) != NULL) {
192 int linelen = strlen(line);
193
194 while (linelen && isspace(line[linelen-1]))
195 line[--linelen] = 0;
196
197 switch (state) {
198 struct hdrentry *entry;
199
200 case Header:
201 if (verbose)
202 printf("# H: %s\n", line);
203 for (entry = hdrs ; entry->name ; entry++) {
204 int len = strlen(entry->name);
205 char *val;
206
207 if (memcmp(entry->name, line, len))
208 continue;
209 if (!entry->dest) {
210 state = Log;
211 break;
212 }
213 val = line + len;
214 linelen -= len;
215 while (isspace(*val)) {
216 val++;
217 linelen--;
218 }
219 memcpy(entry->dest, val, linelen+1);
220 break;
221 }
222 continue;
223
224 case Log:
225 if (verbose)
226 printf("# L: %s\n", line);
227 if (!strcmp(line, "Members:")) {
228 while (loglen && isspace(log[loglen-1]))
229 log[--loglen] = 0;
230 prepare_commit();
231 state = Members;
232 continue;
233 }
234
235 if (loglen + linelen + 5 > sizeof(log))
236 continue;
237 memcpy(log + loglen, line, linelen);
238 loglen += linelen;
239 log[loglen++] = '\n';
240 continue;
241
242 case Members:
243 if (verbose)
244 printf("# M: %s\n", line);
245 if (!linelen) {
246 commit();
247 state = Header;
248 continue;
249 }
250 update_file(line);
251 continue;
252 }
253 }
254 return 0;
255}