4 #include "object-store.h"
8 #include "run-command.h"
9 #include "vcs-svn/svndump.h"
13 static const char *url
;
14 static int dump_from_file
;
15 static const char *private_ref
;
16 static char *remote_ref
;
17 static const char *marksfilename
, *notes_ref
;
18 struct rev_note
{ unsigned int rev_nr
; };
20 static int cmd_capabilities(const char *line
);
21 static int cmd_import(const char *line
);
22 static int cmd_list(const char *line
);
24 typedef int (*input_command_handler
)(const char *);
25 struct input_command_entry
{
27 input_command_handler fn
;
28 unsigned char batchable
; /* whether the command starts or is part of a batch */
31 static const struct input_command_entry input_command_list
[] = {
32 { "capabilities", cmd_capabilities
, 0 },
33 { "import", cmd_import
, 1 },
34 { "list", cmd_list
, 0 },
38 static int cmd_capabilities(const char *line
)
41 printf("bidi-import\n");
42 printf("refspec %s:%s\n\n", remote_ref
, private_ref
);
47 static void terminate_batch(void)
49 /* terminate a current batch's fast-import stream */
54 /* NOTE: 'ref' refers to a git reference, while 'rev' refers to a svn revision. */
55 static char *read_ref_note(const struct object_id
*oid
)
57 const struct object_id
*note_oid
;
60 enum object_type type
;
62 init_notes(NULL
, notes_ref
, NULL
, 0);
63 if (!(note_oid
= get_note(NULL
, oid
)))
64 return NULL
; /* note tree not found */
65 if (!(msg
= read_object_file(note_oid
, &type
, &msglen
)))
66 error("Empty notes tree. %s", notes_ref
);
67 else if (!msglen
|| type
!= OBJ_BLOB
) {
68 error("Note contains unusable content. "
69 "Is something else using this notes tree? %s", notes_ref
);
76 static int parse_rev_note(const char *msg
, struct rev_note
*res
)
78 const char *key
, *value
, *end
;
82 end
= strchrnul(msg
, '\n');
85 key
= "Revision-number: ";
86 if (starts_with(msg
, key
)) {
89 value
= msg
+ strlen(key
);
90 i
= strtol(value
, &end
, 0);
91 if (end
== value
|| i
< 0 || i
> UINT32_MAX
)
102 static int note2mark_cb(const struct object_id
*object_oid
,
103 const struct object_id
*note_oid
, char *note_path
,
106 FILE *file
= (FILE *)cb_data
;
108 unsigned long msglen
;
109 enum object_type type
;
110 struct rev_note note
;
112 if (!(msg
= read_object_file(note_oid
, &type
, &msglen
)) ||
113 !msglen
|| type
!= OBJ_BLOB
) {
117 if (parse_rev_note(msg
, ¬e
))
119 if (fprintf(file
, ":%d %s\n", note
.rev_nr
, oid_to_hex(object_oid
)) < 1)
124 static void regenerate_marks(void)
127 FILE *marksfile
= xfopen(marksfilename
, "w+");
129 ret
= for_each_note(NULL
, 0, note2mark_cb
, marksfile
);
131 die("Regeneration of marks failed, returned %d.", ret
);
135 static void check_or_regenerate_marks(int latestrev
)
138 struct strbuf sb
= STRBUF_INIT
;
139 struct strbuf line
= STRBUF_INIT
;
145 init_notes(NULL
, notes_ref
, NULL
, 0);
146 marksfile
= fopen(marksfilename
, "r");
149 marksfile
= xfopen(marksfilename
, "r");
152 strbuf_addf(&sb
, ":%d ", latestrev
);
153 while (strbuf_getline_lf(&line
, marksfile
) != EOF
) {
154 if (starts_with(line
.buf
, sb
.buf
)) {
165 strbuf_release(&line
);
168 static int cmd_import(const char *line
)
173 struct object_id head_oid
;
174 unsigned int startrev
;
175 struct child_process svndump_proc
= CHILD_PROCESS_INIT
;
176 const char *command
= "svnrdump";
178 if (read_ref(private_ref
, &head_oid
))
181 note_msg
= read_ref_note(&head_oid
);
182 if(note_msg
== NULL
) {
183 warning("No note found for %s.", private_ref
);
186 struct rev_note note
= { 0 };
187 if (parse_rev_note(note_msg
, ¬e
))
188 die("Revision number couldn't be parsed from note.");
189 startrev
= note
.rev_nr
+ 1;
193 check_or_regenerate_marks(startrev
- 1);
195 if (dump_from_file
) {
196 dumpin_fd
= open(url
, O_RDONLY
);
198 die_errno("Couldn't open svn dump file %s.", url
);
200 svndump_proc
.out
= -1;
201 strvec_push(&svndump_proc
.args
, command
);
202 strvec_push(&svndump_proc
.args
, "dump");
203 strvec_push(&svndump_proc
.args
, url
);
204 strvec_pushf(&svndump_proc
.args
, "-r%u:HEAD", startrev
);
206 code
= start_command(&svndump_proc
);
208 die("Unable to start %s, code %d", command
, code
);
209 dumpin_fd
= svndump_proc
.out
;
211 /* setup marks file import/export */
212 printf("feature import-marks-if-exists=%s\n"
213 "feature export-marks=%s\n", marksfilename
, marksfilename
);
215 svndump_init_fd(dumpin_fd
, STDIN_FILENO
);
216 svndump_read(url
, private_ref
, notes_ref
);
221 if (!dump_from_file
) {
222 code
= finish_command(&svndump_proc
);
224 warning("%s, returned %d", command
, code
);
230 static int cmd_list(const char *line
)
232 printf("? %s\n\n", remote_ref
);
237 static int do_command(struct strbuf
*line
)
239 const struct input_command_entry
*p
= input_command_list
;
240 static struct string_list batchlines
= STRING_LIST_INIT_DUP
;
241 static const struct input_command_entry
*batch_cmd
;
243 * commands can be grouped together in a batch.
244 * Batches are ended by \n. If no batch is active the program ends.
245 * During a batch all lines are buffered and passed to the handler function
246 * when the batch is terminated.
248 if (line
->len
== 0) {
250 struct string_list_item
*item
;
251 for_each_string_list_item(item
, &batchlines
)
252 batch_cmd
->fn(item
->string
);
255 string_list_clear(&batchlines
, 0);
256 return 0; /* end of the batch, continue reading other commands. */
258 return 1; /* end of command stream, quit */
261 if (!starts_with(batch_cmd
->name
, line
->buf
))
262 die("Active %s batch interrupted by %s", batch_cmd
->name
, line
->buf
);
263 /* buffer batch lines */
264 string_list_append(&batchlines
, line
->buf
);
268 for (p
= input_command_list
; p
->name
; p
++) {
269 if (starts_with(line
->buf
, p
->name
) && (strlen(p
->name
) == line
->len
||
270 line
->buf
[strlen(p
->name
)] == ' ')) {
273 string_list_append(&batchlines
, line
->buf
);
276 return p
->fn(line
->buf
);
279 die("Unknown command '%s'\n", line
->buf
);
283 int cmd_main(int argc
, const char **argv
)
285 struct strbuf buf
= STRBUF_INIT
, url_sb
= STRBUF_INIT
,
286 private_ref_sb
= STRBUF_INIT
, marksfilename_sb
= STRBUF_INIT
,
287 notes_ref_sb
= STRBUF_INIT
;
288 static struct remote
*remote
;
289 const char *url_in
, *remote_ref_short
;
291 setup_git_directory();
292 if (argc
< 2 || argc
> 3) {
293 usage("git-remote-svn <remote-name> [<url>]");
297 remote_ref_short
= git_default_branch_name();
298 remote_ref
= xstrfmt("refs/heads/%s", remote_ref_short
);
300 remote
= remote_get(argv
[1]);
301 url_in
= (argc
== 3) ? argv
[2] : remote
->url
[0];
303 if (starts_with(url_in
, "file://")) {
305 url
= url_decode(url_in
+ sizeof("file://")-1);
308 end_url_with_slash(&url_sb
, url_in
);
312 strbuf_addf(&private_ref_sb
, "refs/svn/%s/%s",
313 remote
->name
, remote_ref_short
);
314 private_ref
= private_ref_sb
.buf
;
316 strbuf_addf(¬es_ref_sb
, "refs/notes/%s/revs", remote
->name
);
317 notes_ref
= notes_ref_sb
.buf
;
319 strbuf_addf(&marksfilename_sb
, "%s/info/fast-import/remote-svn/%s.marks",
320 get_git_dir(), remote
->name
);
321 marksfilename
= marksfilename_sb
.buf
;
324 if (strbuf_getline_lf(&buf
, stdin
) == EOF
) {
326 die("Error reading command stream");
328 die("Unexpected end of command stream");
330 if (do_command(&buf
))
335 strbuf_release(&buf
);
336 strbuf_release(&url_sb
);
337 strbuf_release(&private_ref_sb
);
338 strbuf_release(¬es_ref_sb
);
339 strbuf_release(&marksfilename_sb
);