static int dump_from_file;
static const char *private_ref;
static const char *remote_ref = "refs/heads/master";
+static const char *marksfilename, *notes_ref;
+struct rev_note { unsigned int rev_nr; };
static int cmd_capabilities(const char *line);
static int cmd_import(const char *line);
fflush(stdout);
}
+/* NOTE: 'ref' refers to a git reference, while 'rev' refers to a svn revision. */
+static char *read_ref_note(const unsigned char sha1[20])
+{
+ const unsigned char *note_sha1;
+ char *msg = NULL;
+ unsigned long msglen;
+ enum object_type type;
+
+ init_notes(NULL, notes_ref, NULL, 0);
+ if (!(note_sha1 = get_note(NULL, sha1)))
+ return NULL; /* note tree not found */
+ if (!(msg = read_sha1_file(note_sha1, &type, &msglen)))
+ error("Empty notes tree. %s", notes_ref);
+ else if (!msglen || type != OBJ_BLOB) {
+ error("Note contains unusable content. "
+ "Is something else using this notes tree? %s", notes_ref);
+ free(msg);
+ msg = NULL;
+ }
+ free_notes(NULL);
+ return msg;
+}
+
+static int parse_rev_note(const char *msg, struct rev_note *res)
+{
+ const char *key, *value, *end;
+ size_t len;
+
+ while (*msg) {
+ end = strchr(msg, '\n');
+ len = end ? end - msg : strlen(msg);
+
+ key = "Revision-number: ";
+ if (!prefixcmp(msg, key)) {
+ long i;
+ char *end;
+ value = msg + strlen(key);
+ i = strtol(value, &end, 0);
+ if (end == value || i < 0 || i > UINT32_MAX)
+ return -1;
+ res->rev_nr = i;
+ }
+ msg += len + 1;
+ }
+ return 0;
+}
+
+static int note2mark_cb(const unsigned char *object_sha1,
+ const unsigned char *note_sha1, char *note_path,
+ void *cb_data)
+{
+ FILE *file = (FILE *)cb_data;
+ char *msg;
+ unsigned long msglen;
+ enum object_type type;
+ struct rev_note note;
+
+ if (!(msg = read_sha1_file(note_sha1, &type, &msglen)) ||
+ !msglen || type != OBJ_BLOB) {
+ free(msg);
+ return 1;
+ }
+ if (parse_rev_note(msg, ¬e))
+ return 2;
+ if (fprintf(file, ":%d %s\n", note.rev_nr, sha1_to_hex(object_sha1)) < 1)
+ return 3;
+ return 0;
+}
+
+static void regenerate_marks(void)
+{
+ int ret;
+ FILE *marksfile = fopen(marksfilename, "w+");
+
+ if (!marksfile)
+ die_errno("Couldn't create mark file %s.", marksfilename);
+ ret = for_each_note(NULL, 0, note2mark_cb, marksfile);
+ if (ret)
+ die("Regeneration of marks failed, returned %d.", ret);
+ fclose(marksfile);
+}
+
+static void check_or_regenerate_marks(int latestrev)
+{
+ FILE *marksfile;
+ struct strbuf sb = STRBUF_INIT;
+ struct strbuf line = STRBUF_INIT;
+ int found = 0;
+
+ if (latestrev < 1)
+ return;
+
+ init_notes(NULL, notes_ref, NULL, 0);
+ marksfile = fopen(marksfilename, "r");
+ if (!marksfile) {
+ regenerate_marks();
+ marksfile = fopen(marksfilename, "r");
+ if (!marksfile)
+ die_errno("cannot read marks file %s!", marksfilename);
+ fclose(marksfile);
+ } else {
+ strbuf_addf(&sb, ":%d ", latestrev);
+ while (strbuf_getline(&line, marksfile, '\n') != EOF) {
+ if (!prefixcmp(line.buf, sb.buf)) {
+ found++;
+ break;
+ }
+ }
+ fclose(marksfile);
+ if (!found)
+ regenerate_marks();
+ }
+ free_notes(NULL);
+ strbuf_release(&sb);
+ strbuf_release(&line);
+}
+
static int cmd_import(const char *line)
{
int code;
int dumpin_fd;
- unsigned int startrev = 0;
+ char *note_msg;
+ unsigned char head_sha1[20];
+ unsigned int startrev;
struct argv_array svndump_argv = ARGV_ARRAY_INIT;
struct child_process svndump_proc;
+ if (read_ref(private_ref, head_sha1))
+ startrev = 0;
+ else {
+ note_msg = read_ref_note(head_sha1);
+ if(note_msg == NULL) {
+ warning("No note found for %s.", private_ref);
+ startrev = 0;
+ } else {
+ struct rev_note note = { 0 };
+ if (parse_rev_note(note_msg, ¬e))
+ die("Revision number couldn't be parsed from note.");
+ startrev = note.rev_nr + 1;
+ free(note_msg);
+ }
+ }
+ check_or_regenerate_marks(startrev - 1);
+
if (dump_from_file) {
dumpin_fd = open(url, O_RDONLY);
if(dumpin_fd < 0)
die("Unable to start %s, code %d", svndump_proc.argv[0], code);
dumpin_fd = svndump_proc.out;
}
+ /* setup marks file import/export */
+ printf("feature import-marks-if-exists=%s\n"
+ "feature export-marks=%s\n", marksfilename, marksfilename);
+
svndump_init_fd(dumpin_fd, STDIN_FILENO);
- svndump_read(url, private_ref);
+ svndump_read(url, private_ref, notes_ref);
svndump_deinit();
svndump_reset();
int main(int argc, const char **argv)
{
struct strbuf buf = STRBUF_INIT, url_sb = STRBUF_INIT,
- private_ref_sb = STRBUF_INIT;
+ private_ref_sb = STRBUF_INIT, marksfilename_sb = STRBUF_INIT,
+ notes_ref_sb = STRBUF_INIT;
static struct remote *remote;
const char *url_in;
strbuf_addf(&private_ref_sb, "refs/svn/%s/master", remote->name);
private_ref = private_ref_sb.buf;
+ strbuf_addf(¬es_ref_sb, "refs/notes/%s/revs", remote->name);
+ notes_ref = notes_ref_sb.buf;
+
+ strbuf_addf(&marksfilename_sb, "%s/info/fast-import/remote-svn/%s.marks",
+ get_git_dir(), remote->name);
+ marksfilename = marksfilename_sb.buf;
+
while (1) {
if (strbuf_getline(&buf, stdin, '\n') == EOF) {
if (ferror(stdin))
strbuf_release(&buf);
strbuf_release(&url_sb);
strbuf_release(&private_ref_sb);
+ strbuf_release(¬es_ref_sb);
+ strbuf_release(&marksfilename_sb);
return 0;
}