remote-svn: add incremental import

Search for a note attached to the ref to update and read it's 'Revision-number:'-line. Start import from the next svn revision. If there is no next revision in the svn repo, svnrdump terminates with a message on stderr an non-zero return value. This looks a little weird, but there is no other way to know whether there is a new revision in the svn repo. On the start of an incremental import, the parent of the first commit in the fast-import stream is set to the branch name to update. All following commits specify their parent by a mark number. Previous mark files are currently not reused. Signed-off-by: Florian Achleitner <florian.achleitner.2.6.31@gmail.com> Acked-by: David Michael Barr <b@rr-dav.id.au> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Florian Achleitner committed Sep 19, 2012 at 17:21 UTC 8e43a1d010e22a0e432de440b08cc4389d8370f9
7 files changed +95 -17
contrib/svn-fe/svn-fe.c
+2 -1
@@ -10,7 +10,8 @@ int main(int argc, char **argv)
10 {
11 if (svndump_init(NULL))
12 return 1;
13 - svndump_read((argc > 1) ? argv[1] : NULL, "refs/heads/master");
13 + svndump_read((argc > 1) ? argv[1] : NULL, "refs/heads/master",
14 + "refs/notes/svn/revs");
15 svndump_deinit();
16 svndump_reset();
17 return 0;
remote-testsvn.c
+75 -4
@@ -12,7 +12,8 @@ static const char *url;
12 static int dump_from_file;
13 static const char *private_ref;
14 static const char *remote_ref = "refs/heads/master";
15 -static const char *marksfilename;
15 +static const char *marksfilename, *notes_ref;
16 +struct rev_note { unsigned int rev_nr; };
17
18 static int cmd_capabilities(const char *line);
19 static int cmd_import(const char *line);
@@ -48,14 +49,79 @@ static void terminate_batch(void)
49 fflush(stdout);
50 }
51
52 +/* NOTE: 'ref' refers to a git reference, while 'rev' refers to a svn revision. */
53 +static char *read_ref_note(const unsigned char sha1[20])
54 +{
55 + const unsigned char *note_sha1;
56 + char *msg = NULL;
57 + unsigned long msglen;
58 + enum object_type type;
59 +
60 + init_notes(NULL, notes_ref, NULL, 0);
61 + if (!(note_sha1 = get_note(NULL, sha1)))
62 + return NULL; /* note tree not found */
63 + if (!(msg = read_sha1_file(note_sha1, &type, &msglen)))
64 + error("Empty notes tree. %s", notes_ref);
65 + else if (!msglen || type != OBJ_BLOB) {
66 + error("Note contains unusable content. "
67 + "Is something else using this notes tree? %s", notes_ref);
68 + free(msg);
69 + msg = NULL;
70 + }
71 + free_notes(NULL);
72 + return msg;
73 +}
74 +
75 +static int parse_rev_note(const char *msg, struct rev_note *res)
76 +{
77 + const char *key, *value, *end;
78 + size_t len;
79 +
80 + while (*msg) {
81 + end = strchr(msg, '\n');
82 + len = end ? end - msg : strlen(msg);
83 +
84 + key = "Revision-number: ";
85 + if (!prefixcmp(msg, key)) {
86 + long i;
87 + char *end;
88 + value = msg + strlen(key);
89 + i = strtol(value, &end, 0);
90 + if (end == value || i < 0 || i > UINT32_MAX)
91 + return -1;
92 + res->rev_nr = i;
93 + }
94 + msg += len + 1;
95 + }
96 + return 0;
97 +}
98 +
99 static int cmd_import(const char *line)
100 {
101 int code;
102 int dumpin_fd;
55 - unsigned int startrev = 0;
103 + char *note_msg;
104 + unsigned char head_sha1[20];
105 + unsigned int startrev;
106 struct argv_array svndump_argv = ARGV_ARRAY_INIT;
107 struct child_process svndump_proc;
108
109 + if (read_ref(private_ref, head_sha1))
110 + startrev = 0;
111 + else {
112 + note_msg = read_ref_note(head_sha1);
113 + if(note_msg == NULL) {
114 + warning("No note found for %s.", private_ref);
115 + startrev = 0;
116 + } else {
117 + struct rev_note note = { 0 };
118 + if (parse_rev_note(note_msg, &note))
119 + die("Revision number couldn't be parsed from note.");
120 + startrev = note.rev_nr + 1;
121 + free(note_msg);
122 + }
123 + }
124 +
125 if (dump_from_file) {
126 dumpin_fd = open(url, O_RDONLY);
127 if(dumpin_fd < 0)
@@ -79,7 +145,7 @@ static int cmd_import(const char *line)
145 "feature export-marks=%s\n", marksfilename, marksfilename);
146
147 svndump_init_fd(dumpin_fd, STDIN_FILENO);
82 - svndump_read(url, private_ref);
148 + svndump_read(url, private_ref, notes_ref);
149 svndump_deinit();
150 svndump_reset();
151
@@ -150,7 +216,8 @@ static int do_command(struct strbuf *line)
216 int main(int argc, const char **argv)
217 {
218 struct strbuf buf = STRBUF_INIT, url_sb = STRBUF_INIT,
153 - private_ref_sb = STRBUF_INIT, marksfilename_sb = STRBUF_INIT;
219 + private_ref_sb = STRBUF_INIT, marksfilename_sb = STRBUF_INIT,
220 + notes_ref_sb = STRBUF_INIT;
221 static struct remote *remote;
222 const char *url_in;
223
@@ -176,6 +243,9 @@ int main(int argc, const char **argv)
243 strbuf_addf(&private_ref_sb, "refs/svn/%s/master", remote->name);
244 private_ref = private_ref_sb.buf;
245
246 + strbuf_addf(&notes_ref_sb, "refs/notes/%s/revs", remote->name);
247 + notes_ref = notes_ref_sb.buf;
248 +
249 strbuf_addf(&marksfilename_sb, "%s/info/fast-import/remote-svn/%s.marks",
250 get_git_dir(), remote->name);
251 marksfilename = marksfilename_sb.buf;
@@ -195,6 +265,7 @@ int main(int argc, const char **argv)
265 strbuf_release(&buf);
266 strbuf_release(&url_sb);
267 strbuf_release(&private_ref_sb);
268 + strbuf_release(&notes_ref_sb);
269 strbuf_release(&marksfilename_sb);
270 return 0;
271 }
test-svn-fe.c
+1 -1
@@ -40,7 +40,7 @@ int main(int argc, char *argv[])
40 if (argc == 2) {
41 if (svndump_init(argv[1]))
42 return 1;
43 - svndump_read(NULL, "refs/heads/master");
43 + svndump_read(NULL, "refs/heads/master", "refs/notes/svn/revs");
44 svndump_deinit();
45 svndump_reset();
46 return 0;
vcs-svn/fast_export.c
+8 -2
@@ -68,13 +68,19 @@ void fast_export_modify(const char *path, uint32_t mode, const char *dataref)
68 }
69
70 void fast_export_begin_note(uint32_t revision, const char *author,
71 - const char *log, unsigned long timestamp)
71 + const char *log, unsigned long timestamp, const char *note_ref)
72 {
73 + static int firstnote = 1;
74 size_t loglen = strlen(log);
74 - printf("commit refs/notes/svn/revs\n");
75 + printf("commit %s\n", note_ref);
76 printf("committer %s <%s@%s> %ld +0000\n", author, author, "local", timestamp);
77 printf("data %"PRIuMAX"\n", (uintmax_t)loglen);
78 fwrite(log, loglen, 1, stdout);
79 + if (firstnote) {
80 + if (revision > 1)
81 + printf("from %s^0", note_ref);
82 + firstnote = 0;
83 + }
84 fputc('\n', stdout);
85 }
86
vcs-svn/fast_export.h
+3 -3
@@ -11,10 +11,10 @@ void fast_export_delete(const char *path);
11 void fast_export_modify(const char *path, uint32_t mode, const char *dataref);
12 void fast_export_note(const char *committish, const char *dataref);
13 void fast_export_begin_note(uint32_t revision, const char *author,
14 - const char *log, unsigned long timestamp);
14 + const char *log, unsigned long timestamp, const char *note_ref);
15 void fast_export_begin_commit(uint32_t revision, const char *author,
16 - const struct strbuf *log, const char *uuid,
17 - const char *url, unsigned long timestamp, const char *local_ref);
16 + const struct strbuf *log, const char *uuid,const char *url,
17 + unsigned long timestamp, const char *local_ref);
18 void fast_export_end_commit(uint32_t revision);
19 void fast_export_data(uint32_t mode, off_t len, struct line_buffer *input);
20 void fast_export_buf_to_data(const struct strbuf *data);
vcs-svn/svndump.c
+5 -5
@@ -309,20 +309,20 @@ static void begin_revision(const char *remote_ref)
309 rev_ctx.timestamp, remote_ref);
310 }
311
312 -static void end_revision(void)
312 +static void end_revision(const char *note_ref)
313 {
314 struct strbuf mark = STRBUF_INIT;
315 if (rev_ctx.revision) {
316 fast_export_end_commit(rev_ctx.revision);
317 fast_export_begin_note(rev_ctx.revision, "remote-svn",
318 - "Note created by remote-svn.", rev_ctx.timestamp);
318 + "Note created by remote-svn.", rev_ctx.timestamp, note_ref);
319 strbuf_addf(&mark, ":%"PRIu32, rev_ctx.revision);
320 fast_export_note(mark.buf, "inline");
321 fast_export_buf_to_data(&rev_ctx.note);
322 }
323 }
324
325 -void svndump_read(const char *url, const char *local_ref)
325 +void svndump_read(const char *url, const char *local_ref, const char *notes_ref)
326 {
327 char *val;
328 char *t;
@@ -363,7 +363,7 @@ void svndump_read(const char *url, const char *local_ref)
363 if (active_ctx == REV_CTX)
364 begin_revision(local_ref);
365 if (active_ctx != DUMP_CTX)
366 - end_revision();
366 + end_revision(notes_ref);
367 active_ctx = REV_CTX;
368 reset_rev_ctx(atoi(val));
369 strbuf_addf(&rev_ctx.note, "%s\n", t);
@@ -479,7 +479,7 @@ void svndump_read(const char *url, const char *local_ref)
479 if (active_ctx == REV_CTX)
480 begin_revision(local_ref);
481 if (active_ctx != DUMP_CTX)
482 - end_revision();
482 + end_revision(notes_ref);
483 }
484
485 static void init(int report_fd)
vcs-svn/svndump.h
+1 -1
@@ -3,7 +3,7 @@
3
4 int svndump_init(const char *filename);
5 int svndump_init_fd(int in_fd, int back_fd);
6 -void svndump_read(const char *url, const char *local_ref);
6 +void svndump_read(const char *url, const char *local_ref, const char *notes_ref);
7 void svndump_deinit(void);
8 void svndump_reset(void);
9