THIS IS A TEST INSTANCE ONLY! REPOSITORIES CAN BE DELETED AT ANY TIME!

Git Source Code Mirror - This is a publish-only repository and all pull requests are ignored. Please follow Documentation/SubmittingPatches procedure for any of your improvements.
git
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

338 lines
8.2KB

  1. #include "cache.h"
  2. #include "refs.h"
  3. #include "remote.h"
  4. #include "object-store.h"
  5. #include "strbuf.h"
  6. #include "url.h"
  7. #include "exec-cmd.h"
  8. #include "run-command.h"
  9. #include "vcs-svn/svndump.h"
  10. #include "notes.h"
  11. #include "argv-array.h"
  12. static const char *url;
  13. static int dump_from_file;
  14. static const char *private_ref;
  15. static const char *remote_ref = "refs/heads/master";
  16. static const char *marksfilename, *notes_ref;
  17. struct rev_note { unsigned int rev_nr; };
  18. static int cmd_capabilities(const char *line);
  19. static int cmd_import(const char *line);
  20. static int cmd_list(const char *line);
  21. typedef int (*input_command_handler)(const char *);
  22. struct input_command_entry {
  23. const char *name;
  24. input_command_handler fn;
  25. unsigned char batchable; /* whether the command starts or is part of a batch */
  26. };
  27. static const struct input_command_entry input_command_list[] = {
  28. { "capabilities", cmd_capabilities, 0 },
  29. { "import", cmd_import, 1 },
  30. { "list", cmd_list, 0 },
  31. { NULL, NULL }
  32. };
  33. static int cmd_capabilities(const char *line)
  34. {
  35. printf("import\n");
  36. printf("bidi-import\n");
  37. printf("refspec %s:%s\n\n", remote_ref, private_ref);
  38. fflush(stdout);
  39. return 0;
  40. }
  41. static void terminate_batch(void)
  42. {
  43. /* terminate a current batch's fast-import stream */
  44. printf("done\n");
  45. fflush(stdout);
  46. }
  47. /* NOTE: 'ref' refers to a git reference, while 'rev' refers to a svn revision. */
  48. static char *read_ref_note(const struct object_id *oid)
  49. {
  50. const struct object_id *note_oid;
  51. char *msg = NULL;
  52. unsigned long msglen;
  53. enum object_type type;
  54. init_notes(NULL, notes_ref, NULL, 0);
  55. if (!(note_oid = get_note(NULL, oid)))
  56. return NULL; /* note tree not found */
  57. if (!(msg = read_object_file(note_oid, &type, &msglen)))
  58. error("Empty notes tree. %s", notes_ref);
  59. else if (!msglen || type != OBJ_BLOB) {
  60. error("Note contains unusable content. "
  61. "Is something else using this notes tree? %s", notes_ref);
  62. FREE_AND_NULL(msg);
  63. }
  64. free_notes(NULL);
  65. return msg;
  66. }
  67. static int parse_rev_note(const char *msg, struct rev_note *res)
  68. {
  69. const char *key, *value, *end;
  70. size_t len;
  71. while (*msg) {
  72. end = strchrnul(msg, '\n');
  73. len = end - msg;
  74. key = "Revision-number: ";
  75. if (starts_with(msg, key)) {
  76. long i;
  77. char *end;
  78. value = msg + strlen(key);
  79. i = strtol(value, &end, 0);
  80. if (end == value || i < 0 || i > UINT32_MAX)
  81. return -1;
  82. res->rev_nr = i;
  83. return 0;
  84. }
  85. msg += len + 1;
  86. }
  87. /* didn't find it */
  88. return -1;
  89. }
  90. static int note2mark_cb(const struct object_id *object_oid,
  91. const struct object_id *note_oid, char *note_path,
  92. void *cb_data)
  93. {
  94. FILE *file = (FILE *)cb_data;
  95. char *msg;
  96. unsigned long msglen;
  97. enum object_type type;
  98. struct rev_note note;
  99. if (!(msg = read_object_file(note_oid, &type, &msglen)) ||
  100. !msglen || type != OBJ_BLOB) {
  101. free(msg);
  102. return 1;
  103. }
  104. if (parse_rev_note(msg, &note))
  105. return 2;
  106. if (fprintf(file, ":%d %s\n", note.rev_nr, oid_to_hex(object_oid)) < 1)
  107. return 3;
  108. return 0;
  109. }
  110. static void regenerate_marks(void)
  111. {
  112. int ret;
  113. FILE *marksfile = xfopen(marksfilename, "w+");
  114. ret = for_each_note(NULL, 0, note2mark_cb, marksfile);
  115. if (ret)
  116. die("Regeneration of marks failed, returned %d.", ret);
  117. fclose(marksfile);
  118. }
  119. static void check_or_regenerate_marks(int latestrev)
  120. {
  121. FILE *marksfile;
  122. struct strbuf sb = STRBUF_INIT;
  123. struct strbuf line = STRBUF_INIT;
  124. int found = 0;
  125. if (latestrev < 1)
  126. return;
  127. init_notes(NULL, notes_ref, NULL, 0);
  128. marksfile = fopen(marksfilename, "r");
  129. if (!marksfile) {
  130. regenerate_marks();
  131. marksfile = xfopen(marksfilename, "r");
  132. fclose(marksfile);
  133. } else {
  134. strbuf_addf(&sb, ":%d ", latestrev);
  135. while (strbuf_getline_lf(&line, marksfile) != EOF) {
  136. if (starts_with(line.buf, sb.buf)) {
  137. found++;
  138. break;
  139. }
  140. }
  141. fclose(marksfile);
  142. if (!found)
  143. regenerate_marks();
  144. }
  145. free_notes(NULL);
  146. strbuf_release(&sb);
  147. strbuf_release(&line);
  148. }
  149. static int cmd_import(const char *line)
  150. {
  151. int code;
  152. int dumpin_fd;
  153. char *note_msg;
  154. struct object_id head_oid;
  155. unsigned int startrev;
  156. struct child_process svndump_proc = CHILD_PROCESS_INIT;
  157. const char *command = "svnrdump";
  158. if (read_ref(private_ref, &head_oid))
  159. startrev = 0;
  160. else {
  161. note_msg = read_ref_note(&head_oid);
  162. if(note_msg == NULL) {
  163. warning("No note found for %s.", private_ref);
  164. startrev = 0;
  165. } else {
  166. struct rev_note note = { 0 };
  167. if (parse_rev_note(note_msg, &note))
  168. die("Revision number couldn't be parsed from note.");
  169. startrev = note.rev_nr + 1;
  170. free(note_msg);
  171. }
  172. }
  173. check_or_regenerate_marks(startrev - 1);
  174. if (dump_from_file) {
  175. dumpin_fd = open(url, O_RDONLY);
  176. if(dumpin_fd < 0)
  177. die_errno("Couldn't open svn dump file %s.", url);
  178. } else {
  179. svndump_proc.out = -1;
  180. argv_array_push(&svndump_proc.args, command);
  181. argv_array_push(&svndump_proc.args, "dump");
  182. argv_array_push(&svndump_proc.args, url);
  183. argv_array_pushf(&svndump_proc.args, "-r%u:HEAD", startrev);
  184. code = start_command(&svndump_proc);
  185. if (code)
  186. die("Unable to start %s, code %d", command, code);
  187. dumpin_fd = svndump_proc.out;
  188. }
  189. /* setup marks file import/export */
  190. printf("feature import-marks-if-exists=%s\n"
  191. "feature export-marks=%s\n", marksfilename, marksfilename);
  192. svndump_init_fd(dumpin_fd, STDIN_FILENO);
  193. svndump_read(url, private_ref, notes_ref);
  194. svndump_deinit();
  195. svndump_reset();
  196. close(dumpin_fd);
  197. if (!dump_from_file) {
  198. code = finish_command(&svndump_proc);
  199. if (code)
  200. warning("%s, returned %d", command, code);
  201. }
  202. return 0;
  203. }
  204. static int cmd_list(const char *line)
  205. {
  206. printf("? %s\n\n", remote_ref);
  207. fflush(stdout);
  208. return 0;
  209. }
  210. static int do_command(struct strbuf *line)
  211. {
  212. const struct input_command_entry *p = input_command_list;
  213. static struct string_list batchlines = STRING_LIST_INIT_DUP;
  214. static const struct input_command_entry *batch_cmd;
  215. /*
  216. * commands can be grouped together in a batch.
  217. * Batches are ended by \n. If no batch is active the program ends.
  218. * During a batch all lines are buffered and passed to the handler function
  219. * when the batch is terminated.
  220. */
  221. if (line->len == 0) {
  222. if (batch_cmd) {
  223. struct string_list_item *item;
  224. for_each_string_list_item(item, &batchlines)
  225. batch_cmd->fn(item->string);
  226. terminate_batch();
  227. batch_cmd = NULL;
  228. string_list_clear(&batchlines, 0);
  229. return 0; /* end of the batch, continue reading other commands. */
  230. }
  231. return 1; /* end of command stream, quit */
  232. }
  233. if (batch_cmd) {
  234. if (!starts_with(batch_cmd->name, line->buf))
  235. die("Active %s batch interrupted by %s", batch_cmd->name, line->buf);
  236. /* buffer batch lines */
  237. string_list_append(&batchlines, line->buf);
  238. return 0;
  239. }
  240. for (p = input_command_list; p->name; p++) {
  241. if (starts_with(line->buf, p->name) && (strlen(p->name) == line->len ||
  242. line->buf[strlen(p->name)] == ' ')) {
  243. if (p->batchable) {
  244. batch_cmd = p;
  245. string_list_append(&batchlines, line->buf);
  246. return 0;
  247. }
  248. return p->fn(line->buf);
  249. }
  250. }
  251. die("Unknown command '%s'\n", line->buf);
  252. return 0;
  253. }
  254. int cmd_main(int argc, const char **argv)
  255. {
  256. struct strbuf buf = STRBUF_INIT, url_sb = STRBUF_INIT,
  257. private_ref_sb = STRBUF_INIT, marksfilename_sb = STRBUF_INIT,
  258. notes_ref_sb = STRBUF_INIT;
  259. static struct remote *remote;
  260. const char *url_in;
  261. setup_git_directory();
  262. if (argc < 2 || argc > 3) {
  263. usage("git-remote-svn <remote-name> [<url>]");
  264. return 1;
  265. }
  266. remote = remote_get(argv[1]);
  267. url_in = (argc == 3) ? argv[2] : remote->url[0];
  268. if (starts_with(url_in, "file://")) {
  269. dump_from_file = 1;
  270. url = url_decode(url_in + sizeof("file://")-1);
  271. } else {
  272. dump_from_file = 0;
  273. end_url_with_slash(&url_sb, url_in);
  274. url = url_sb.buf;
  275. }
  276. strbuf_addf(&private_ref_sb, "refs/svn/%s/master", remote->name);
  277. private_ref = private_ref_sb.buf;
  278. strbuf_addf(&notes_ref_sb, "refs/notes/%s/revs", remote->name);
  279. notes_ref = notes_ref_sb.buf;
  280. strbuf_addf(&marksfilename_sb, "%s/info/fast-import/remote-svn/%s.marks",
  281. get_git_dir(), remote->name);
  282. marksfilename = marksfilename_sb.buf;
  283. while (1) {
  284. if (strbuf_getline_lf(&buf, stdin) == EOF) {
  285. if (ferror(stdin))
  286. die("Error reading command stream");
  287. else
  288. die("Unexpected end of command stream");
  289. }
  290. if (do_command(&buf))
  291. break;
  292. strbuf_reset(&buf);
  293. }
  294. strbuf_release(&buf);
  295. strbuf_release(&url_sb);
  296. strbuf_release(&private_ref_sb);
  297. strbuf_release(&notes_ref_sb);
  298. strbuf_release(&marksfilename_sb);
  299. return 0;
  300. }