Merge branch 'nd/log-n-doc' into maint
[git] / vcs-svn / fast_export.c
1 /*
2  * Licensed under a two-clause BSD-style license.
3  * See LICENSE for details.
4  */
5
6 #include "git-compat-util.h"
7 #include "strbuf.h"
8 #include "quote.h"
9 #include "fast_export.h"
10 #include "repo_tree.h"
11 #include "strbuf.h"
12 #include "svndiff.h"
13 #include "sliding_window.h"
14 #include "line_buffer.h"
15
16 #define MAX_GITSVN_LINE_LEN 4096
17
18 static uint32_t first_commit_done;
19 static struct line_buffer postimage = LINE_BUFFER_INIT;
20 static struct line_buffer report_buffer = LINE_BUFFER_INIT;
21
22 /* NEEDSWORK: move to fast_export_init() */
23 static int init_postimage(void)
24 {
25         static int postimage_initialized;
26         if (postimage_initialized)
27                 return 0;
28         postimage_initialized = 1;
29         return buffer_tmpfile_init(&postimage);
30 }
31
32 void fast_export_init(int fd)
33 {
34         first_commit_done = 0;
35         if (buffer_fdinit(&report_buffer, fd))
36                 die_errno("cannot read from file descriptor %d", fd);
37 }
38
39 void fast_export_deinit(void)
40 {
41         if (buffer_deinit(&report_buffer))
42                 die_errno("error closing fast-import feedback stream");
43 }
44
45 void fast_export_delete(const char *path)
46 {
47         putchar('D');
48         putchar(' ');
49         quote_c_style(path, NULL, stdout, 0);
50         putchar('\n');
51 }
52
53 static void fast_export_truncate(const char *path, uint32_t mode)
54 {
55         fast_export_modify(path, mode, "inline");
56         printf("data 0\n\n");
57 }
58
59 void fast_export_modify(const char *path, uint32_t mode, const char *dataref)
60 {
61         /* Mode must be 100644, 100755, 120000, or 160000. */
62         if (!dataref) {
63                 fast_export_truncate(path, mode);
64                 return;
65         }
66         printf("M %06"PRIo32" %s ", mode, dataref);
67         quote_c_style(path, NULL, stdout, 0);
68         putchar('\n');
69 }
70
71 static char gitsvnline[MAX_GITSVN_LINE_LEN];
72 void fast_export_begin_commit(uint32_t revision, const char *author,
73                         const struct strbuf *log,
74                         const char *uuid, const char *url,
75                         unsigned long timestamp)
76 {
77         static const struct strbuf empty = STRBUF_INIT;
78         if (!log)
79                 log = ∅
80         if (*uuid && *url) {
81                 snprintf(gitsvnline, MAX_GITSVN_LINE_LEN,
82                                 "\n\ngit-svn-id: %s@%"PRIu32" %s\n",
83                                  url, revision, uuid);
84         } else {
85                 *gitsvnline = '\0';
86         }
87         printf("commit refs/heads/master\n");
88         printf("mark :%"PRIu32"\n", revision);
89         printf("committer %s <%s@%s> %ld +0000\n",
90                    *author ? author : "nobody",
91                    *author ? author : "nobody",
92                    *uuid ? uuid : "local", timestamp);
93         printf("data %"PRIuMAX"\n",
94                 (uintmax_t) (log->len + strlen(gitsvnline)));
95         fwrite(log->buf, log->len, 1, stdout);
96         printf("%s\n", gitsvnline);
97         if (!first_commit_done) {
98                 if (revision > 1)
99                         printf("from :%"PRIu32"\n", revision - 1);
100                 first_commit_done = 1;
101         }
102 }
103
104 void fast_export_end_commit(uint32_t revision)
105 {
106         printf("progress Imported commit %"PRIu32".\n\n", revision);
107 }
108
109 static void ls_from_rev(uint32_t rev, const char *path)
110 {
111         /* ls :5 path/to/old/file */
112         printf("ls :%"PRIu32" ", rev);
113         quote_c_style(path, NULL, stdout, 0);
114         putchar('\n');
115         fflush(stdout);
116 }
117
118 static void ls_from_active_commit(const char *path)
119 {
120         /* ls "path/to/file" */
121         printf("ls \"");
122         quote_c_style(path, NULL, stdout, 1);
123         printf("\"\n");
124         fflush(stdout);
125 }
126
127 static const char *get_response_line(void)
128 {
129         const char *line = buffer_read_line(&report_buffer);
130         if (line)
131                 return line;
132         if (buffer_ferror(&report_buffer))
133                 die_errno("error reading from fast-import");
134         die("unexpected end of fast-import feedback");
135 }
136
137 static void die_short_read(struct line_buffer *input)
138 {
139         if (buffer_ferror(input))
140                 die_errno("error reading dump file");
141         die("invalid dump: unexpected end of file");
142 }
143
144 static int ends_with(const char *s, size_t len, const char *suffix)
145 {
146         const size_t suffixlen = strlen(suffix);
147         if (len < suffixlen)
148                 return 0;
149         return !memcmp(s + len - suffixlen, suffix, suffixlen);
150 }
151
152 static int parse_cat_response_line(const char *header, off_t *len)
153 {
154         size_t headerlen = strlen(header);
155         uintmax_t n;
156         const char *type;
157         const char *end;
158
159         if (ends_with(header, headerlen, " missing"))
160                 return error("cat-blob reports missing blob: %s", header);
161         type = strstr(header, " blob ");
162         if (!type)
163                 return error("cat-blob header has wrong object type: %s", header);
164         n = strtoumax(type + strlen(" blob "), (char **) &end, 10);
165         if (end == type + strlen(" blob "))
166                 return error("cat-blob header does not contain length: %s", header);
167         if (memchr(type + strlen(" blob "), '-', end - type - strlen(" blob ")))
168                 return error("cat-blob header contains negative length: %s", header);
169         if (n == UINTMAX_MAX || n > maximum_signed_value_of_type(off_t))
170                 return error("blob too large for current definition of off_t");
171         *len = n;
172         if (*end)
173                 return error("cat-blob header contains garbage after length: %s", header);
174         return 0;
175 }
176
177 static void check_preimage_overflow(off_t a, off_t b)
178 {
179         if (signed_add_overflows(a, b))
180                 die("blob too large for current definition of off_t");
181 }
182
183 static long apply_delta(off_t len, struct line_buffer *input,
184                         const char *old_data, uint32_t old_mode)
185 {
186         long ret;
187         struct sliding_view preimage = SLIDING_VIEW_INIT(&report_buffer, 0);
188         FILE *out;
189
190         if (init_postimage() || !(out = buffer_tmpfile_rewind(&postimage)))
191                 die("cannot open temporary file for blob retrieval");
192         if (old_data) {
193                 const char *response;
194                 printf("cat-blob %s\n", old_data);
195                 fflush(stdout);
196                 response = get_response_line();
197                 if (parse_cat_response_line(response, &preimage.max_off))
198                         die("invalid cat-blob response: %s", response);
199                 check_preimage_overflow(preimage.max_off, 1);
200         }
201         if (old_mode == REPO_MODE_LNK) {
202                 strbuf_addstr(&preimage.buf, "link ");
203                 check_preimage_overflow(preimage.max_off, strlen("link "));
204                 preimage.max_off += strlen("link ");
205                 check_preimage_overflow(preimage.max_off, 1);
206         }
207         if (svndiff0_apply(input, len, &preimage, out))
208                 die("cannot apply delta");
209         if (old_data) {
210                 /* Read the remainder of preimage and trailing newline. */
211                 assert(!signed_add_overflows(preimage.max_off, 1));
212                 preimage.max_off++;     /* room for newline */
213                 if (move_window(&preimage, preimage.max_off - 1, 1))
214                         die("cannot seek to end of input");
215                 if (preimage.buf.buf[0] != '\n')
216                         die("missing newline after cat-blob response");
217         }
218         ret = buffer_tmpfile_prepare_to_read(&postimage);
219         if (ret < 0)
220                 die("cannot read temporary file for blob retrieval");
221         strbuf_release(&preimage.buf);
222         return ret;
223 }
224
225 void fast_export_data(uint32_t mode, off_t len, struct line_buffer *input)
226 {
227         assert(len >= 0);
228         if (mode == REPO_MODE_LNK) {
229                 /* svn symlink blobs start with "link " */
230                 if (len < 5)
231                         die("invalid dump: symlink too short for \"link\" prefix");
232                 len -= 5;
233                 if (buffer_skip_bytes(input, 5) != 5)
234                         die_short_read(input);
235         }
236         printf("data %"PRIuMAX"\n", (uintmax_t) len);
237         if (buffer_copy_bytes(input, len) != len)
238                 die_short_read(input);
239         fputc('\n', stdout);
240 }
241
242 static int parse_ls_response(const char *response, uint32_t *mode,
243                                         struct strbuf *dataref)
244 {
245         const char *tab;
246         const char *response_end;
247
248         assert(response);
249         response_end = response + strlen(response);
250
251         if (*response == 'm') { /* Missing. */
252                 errno = ENOENT;
253                 return -1;
254         }
255
256         /* Mode. */
257         if (response_end - response < (signed) strlen("100644") ||
258             response[strlen("100644")] != ' ')
259                 die("invalid ls response: missing mode: %s", response);
260         *mode = 0;
261         for (; *response != ' '; response++) {
262                 char ch = *response;
263                 if (ch < '0' || ch > '7')
264                         die("invalid ls response: mode is not octal: %s", response);
265                 *mode *= 8;
266                 *mode += ch - '0';
267         }
268
269         /* ' blob ' or ' tree ' */
270         if (response_end - response < (signed) strlen(" blob ") ||
271             (response[1] != 'b' && response[1] != 't'))
272                 die("unexpected ls response: not a tree or blob: %s", response);
273         response += strlen(" blob ");
274
275         /* Dataref. */
276         tab = memchr(response, '\t', response_end - response);
277         if (!tab)
278                 die("invalid ls response: missing tab: %s", response);
279         strbuf_add(dataref, response, tab - response);
280         return 0;
281 }
282
283 int fast_export_ls_rev(uint32_t rev, const char *path,
284                                 uint32_t *mode, struct strbuf *dataref)
285 {
286         ls_from_rev(rev, path);
287         return parse_ls_response(get_response_line(), mode, dataref);
288 }
289
290 int fast_export_ls(const char *path, uint32_t *mode, struct strbuf *dataref)
291 {
292         ls_from_active_commit(path);
293         return parse_ls_response(get_response_line(), mode, dataref);
294 }
295
296 void fast_export_blob_delta(uint32_t mode,
297                                 uint32_t old_mode, const char *old_data,
298                                 off_t len, struct line_buffer *input)
299 {
300         long postimage_len;
301
302         assert(len >= 0);
303         postimage_len = apply_delta(len, input, old_data, old_mode);
304         if (mode == REPO_MODE_LNK) {
305                 buffer_skip_bytes(&postimage, strlen("link "));
306                 postimage_len -= strlen("link ");
307         }
308         printf("data %ld\n", postimage_len);
309         buffer_copy_bytes(&postimage, postimage_len);
310         fputc('\n', stdout);
311 }