Move setup_diff_pager to libgit.a
[gitweb.git] / remote-testsvn.c
index 45eba9f2d9d2ff51c08b7700da8edd02c1ca5f56..51fba059a242103fd13bb4b01c471662e205ec43 100644 (file)
@@ -12,7 +12,8 @@ static const char *url;
 static int dump_from_file;
 static const char *private_ref;
 static const char *remote_ref = "refs/heads/master";
-static const char *marksfilename;
+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);
@@ -48,14 +49,150 @@ static void terminate_batch(void)
        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, &note))
+               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, &note))
+                               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)
@@ -79,7 +216,7 @@ static int cmd_import(const char *line)
                        "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();
 
@@ -150,7 +287,8 @@ static int do_command(struct strbuf *line)
 int main(int argc, const char **argv)
 {
        struct strbuf buf = STRBUF_INIT, url_sb = STRBUF_INIT,
-                       private_ref_sb = STRBUF_INIT, marksfilename_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;
 
@@ -176,6 +314,9 @@ int main(int argc, const char **argv)
        strbuf_addf(&private_ref_sb, "refs/svn/%s/master", remote->name);
        private_ref = private_ref_sb.buf;
 
+       strbuf_addf(&notes_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;
@@ -195,6 +336,7 @@ int main(int argc, const char **argv)
        strbuf_release(&buf);
        strbuf_release(&url_sb);
        strbuf_release(&private_ref_sb);
+       strbuf_release(&notes_ref_sb);
        strbuf_release(&marksfilename_sb);
        return 0;
 }