This enables the push command, and the helper is now feature complete.
---
 git-remote-notmuch.c    | 157 ++++++++++++++++++++++++++++++++++++++++
 test/T860-git-remote.sh |  69 ++++++++++++++++++
 test/make-export.py     |  44 +++++++++++
 3 files changed, 270 insertions(+)
 create mode 100644 test/make-export.py

diff --git a/git-remote-notmuch.c b/git-remote-notmuch.c
index a4ed98e2..35579004 100644
--- a/git-remote-notmuch.c
+++ b/git-remote-notmuch.c
@@ -224,6 +224,161 @@ cmd_import (notmuch_database_t *notmuch) {
     store_lastmod(notmuch, nm_dir);
 }
 
+static GString *
+get_tok (const char *line, size_t index) {
+
+    const char *tok = line;
+    size_t tok_len =0;
+
+    for (size_t count = 0; count<=index; count++) {
+       ASSERT(tok = strsplit_len (tok+tok_len, ' ', &tok_len));
+    }
+
+    return g_string_new_len (tok, tok_len);
+}
+
+static GString *
+read_data (char **line_p, size_t *len_p) {
+    ssize_t nread;
+    size_t bytes;
+    size_t data_size;
+    const char *tok;
+    size_t tok_len =0;
+
+    ASSERT (line_p);
+    ASSERT (len_p);
+    ASSERT((nread = getline(line_p, len_p, stdin) != -1));
+
+    tok=*line_p;
+    for (size_t count = 0; count<=1; count++) {
+       ASSERT(tok = strsplit_len (tok+tok_len, ' ', &tok_len));
+    }
+
+    /* TODO: don't ignore tok_len */
+    ASSERT(sscanf (tok, "%zu", &data_size) == 1);
+
+    *line_p = realloc (*line_p, data_size+1);
+    bytes = fread (*line_p, 1, data_size, stdin);
+    ASSERT (bytes == data_size);
+
+    *len_p = data_size;
+
+    return g_string_new_len (*line_p, *len_p);
+}
+
+static void free_string (GString *str) {
+    g_string_free (str, true);
+}
+
+static void
+cmd_export (notmuch_database_t *notmuch)
+{
+  ssize_t nread;
+  size_t len = 0;
+  char *line = NULL;
+  GHashTable* blobs;
+
+  ASSERT(blobs = g_hash_table_new_full ((GHashFunc)g_string_hash,
+                                       (GEqualFunc)g_string_equal,
+                                       (GDestroyNotify)free_string, 
(GDestroyNotify)free_string));
+
+  while ((nread = getline(&line, &len, stdin)) != -1) {
+      flog ("\texport %s\n", line);
+      if (STRNCMP_LITERAL (line, "done") == 0) {
+         break;
+      }
+      else if (STRNCMP_LITERAL (line, "blob") == 0) {
+         GString *mark;
+         GString *data;
+
+         flog ("export blob\n");
+         read_one_line (stdin, &line, &len);
+         mark = get_tok (line, 1);
+
+         data = read_data (&line, &len);
+
+         g_hash_table_insert (blobs, mark, data);
+         read_one_line (stdin, &line, &len);
+         /* TODO free things */
+      }
+
+      else if (STRNCMP_LITERAL (line, "commit") == 0) {
+         char *mid = NULL;
+         size_t mid_len = 0;
+
+         flog ("export commit\n");
+
+         /* mark for commit (ignored) */
+         read_one_line (stdin, &line, &len);
+         /* author (ignored) */
+         read_one_line (stdin, &line, &len);
+         /* committer (ignored) */
+         read_one_line (stdin, &line, &len);
+
+         /* commit message (ignored) */
+         (void)read_data (&line, &len);
+
+         while (strlen (line) > 0) {
+             GString *mark, *path;
+             GBytes *blob;
+             char *basename;
+             notmuch_message_t *message;
+             char *tag;
+             const char *tok;
+             size_t tok_len;
+             size_t max_tok_len;
+
+             read_one_line (stdin, &line, &len);
+             if (strlen (line) ==0)
+                 break;
+
+             ASSERT(mark = get_tok (line, 2));
+             ASSERT(blob = g_hash_table_lookup (blobs, mark));
+             free_string (mark);
+
+             ASSERT(path = get_tok (line, 3));
+
+             basename = g_path_get_dirname (path->str+6);
+             ASSERT(HEX_SUCCESS ==
+                    hex_decode (notmuch, basename, &mid, &mid_len));
+             g_free (basename);
+             free_string (path);
+
+             ASSERT(NOTMUCH_STATUS_SUCCESS ==
+                    notmuch_database_find_message (notmuch, mid, &message));
+             ASSERT(message);
+
+
+             ASSERT(NOTMUCH_STATUS_SUCCESS ==
+                    notmuch_message_freeze (message));
+
+             ASSERT(NOTMUCH_STATUS_SUCCESS ==
+                    notmuch_message_remove_all_tags (message));
+
+             tok = g_bytes_get_data (blob, &max_tok_len);
+             tok_len = 0;
+             while ((tok_len < max_tok_len) &&
+                    (tok = strsplit_len (tok + tok_len, '\n', &tok_len)) != 
NULL) {
+                 tag = strndup (tok, tok_len);
+                 ASSERT(NOTMUCH_STATUS_SUCCESS ==
+                        notmuch_message_add_tag (message, tag));
+                 free (tag);
+             }
+
+             ASSERT(NOTMUCH_STATUS_SUCCESS ==
+                    notmuch_message_thaw (message));
+
+             notmuch_message_destroy (message);
+         }
+         puts("ok refs/heads/master");
+      }
+
+  }
+  store_lastmod(notmuch, nm_dir);
+  puts("");
+  g_hash_table_destroy (blobs);
+}
+
 int
 main (int argc, char *argv[])
 {
@@ -296,6 +451,8 @@ main (int argc, char *argv[])
 
       if (STRNCMP_LITERAL (s, "capabilities")== 0)
          cmd_capabilities ();
+      else if (STRNCMP_LITERAL (s, "export") == 0)
+         cmd_export (db);
       else if (STRNCMP_LITERAL (s, "import") == 0)
          cmd_import (db);
       else if (STRNCMP_LITERAL (s, "list") == 0)
diff --git a/test/T860-git-remote.sh b/test/T860-git-remote.sh
index d5809e6b..6dc55391 100755
--- a/test/T860-git-remote.sh
+++ b/test/T860-git-remote.sh
@@ -21,6 +21,7 @@ export GIT_COMMITTER_EMAIL="notm...@example.com"
 export GIT_REMOTE_NM_DEBUG="s"
 export GIT_REMOTE_NM_LOG=grn-log.txt
 EXPECTED=$NOTMUCH_SRCDIR/test/git-remote.expected-output
+MAKE_EXPORT_PY=$NOTMUCH_SRCDIR/test/make-export.py
 
 TAG_FILE="87/b1/4efc743a.3060...@april.org/tags"
 
@@ -78,4 +79,72 @@ zznew
 EOF
 test_expect_equal_file EXPECTED repo/$TAG_FILE
 
+test_begin_subtest 'export runs'
+run_helper <<EOF | notmuch_sanitize_git > OUTPUT
+export
+blob
+mark :1
+data 10
+tag1
+tag2
+
+commit refs/heads/master
+mark :2
+author Notmuch Test Suite <notm...@example.com> 1234 +0000
+committer Notmuch Test Suite <notm...@example.com> 1234 +0000
+data 8
+ignored
+M 100644 :1 $TAG_FILE
+
+done
+
+EOF
+cat <<EOF > EXPECTED
+ok refs/heads/master
+
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
+# this test depends on the previous one
+test_begin_subtest 'export modifies database'
+notmuch dump id:4efc743a.3060...@april.org | tail -n 1 > OUTPUT
+cat <<EOF > EXPECTED
++tag1 +tag2 -- id:4efc743a.3060...@april.org
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
+test_begin_subtest 'restore via export'
+notmuch dump > BEFORE
+python3 $MAKE_EXPORT_PY > export.in
+notmuch tag +transient -- id:4efc743a.3060...@april.org
+run_helper < export.in > OUTPUT
+notmuch dump id:4efc743a.3060...@april.org | tail -n 1 > OUTPUT
+cat <<EOF > EXPECTED
++tag1 +tag2 -- id:4efc743a.3060...@april.org
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
+test_begin_subtest "push updates database"
+git -C repo push origin master
+notmuch dump id:4efc743a.3060...@april.org | tail -n 1 > OUTPUT
+cat <<EOF > EXPECTED
++tag1 +tag2 -- id:4efc743a.3060...@april.org
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
+test_begin_subtest "adding tag via repo"
+cat<<EOF >repo/$TAG_FILE
+tag1
+tag2
+tag3
+EOF
+git -C repo add $TAG_FILE
+git -C repo commit -m 'testing push'
+git -C repo push origin master
+notmuch dump id:4efc743a.3060...@april.org | tail -n 1 > OUTPUT
+cat <<EOF > EXPECTED
++tag1 +tag2 +tag3 -- id:4efc743a.3060...@april.org
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
 test_done
diff --git a/test/make-export.py b/test/make-export.py
new file mode 100644
index 00000000..3837dc3a
--- /dev/null
+++ b/test/make-export.py
@@ -0,0 +1,44 @@
+# generate a test input for the 'export' subcommand of the
+# git-remote-notmuch helper
+
+from notmuch2 import Database
+from time import time
+from hashlib import sha1
+
+def hexencode(str):
+    output_charset = 
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-_@=.,"
+    out = ""
+    for char in str:
+        if not char in output_charset:
+            out+= f"%{ord(char):x}"
+        else:
+            out+= char
+    return out
+
+db = Database(config=Database.CONFIG.SEARCH)
+
+count=1
+print("export")
+mark={}
+
+for msg in db.messages(""):
+    mark[msg.messageid]=count
+    blob=""
+    for tag in msg.tags:
+        blob += f"{tag}\n"
+    print (f"blob\nmark :{count}");
+    print (f"data {len(blob)}\n{blob}")
+    count=count+1
+
+print (f"\ncommit refs/heads/master\nmark :{count+1}")
+ctime = int(time())
+print (f"author Notmuch Test Suite <notm...@example.com> {ctime} +0000")
+print (f"committer Notmuch Test Suite <notm...@example.com> {ctime} +0000")
+print (f"data 8\nignored")
+
+for msg in db.messages(""):
+    digest = sha1(msg.messageid.encode('utf8')).hexdigest()
+    filename = hexencode(msg.messageid)
+    print (f"M 100644 :{mark[msg.messageid]} 
{digest[0:2]}/{digest[2:4]}/{filename}/tags")
+
+print("\ndone\n")
-- 
2.43.0

_______________________________________________
notmuch mailing list -- notmuch@notmuchmail.org
To unsubscribe send an email to notmuch-le...@notmuchmail.org

Reply via email to