[PATCH 3/5] cli/git-remote: add support for import command

Subject: [PATCH 3/5] cli/git-remote: add support for import command

Date: Wed, 28 Aug 2024 08:45:56 -0700

To: notmuch@notmuchmail.org

Cc:

From: David Bremner


The output in default.import is based on a modified version
of Felipe's git-remote-nm with Blake2 hashing replaced by SHA1
(for portability). This enable git-pull, so test that as well.
---
 git-remote-notmuch.c    | 129 ++++++++++++++++++++++++++++++++++++++++
 test/T860-git-remote.sh |  38 +++++++++++-
 2 files changed, 166 insertions(+), 1 deletion(-)

diff --git a/git-remote-notmuch.c b/git-remote-notmuch.c
index cfc43a68..a4ed98e2 100644
--- a/git-remote-notmuch.c
+++ b/git-remote-notmuch.c
@@ -76,6 +76,54 @@ static unsigned long read_lastmod (const void *ctx, const char *dir) {
     return num;
 }
 
+static void
+store_lastmod (notmuch_database_t *notmuch, const char *dir) {
+    char *filename = NULL;
+    FILE *out;
+
+    ASSERT(filename = talloc_asprintf (notmuch, "%s/lastmod", dir));
+
+    ASSERT(out = fopen (filename, "w"));
+
+    ASSERT(fprintf (out, "%zu\n", notmuch_database_get_revision (notmuch, NULL)) > 0);
+}
+
+static void
+read_one_line (FILE *stream, char **line_p, size_t *len_p) {
+    ssize_t nread;
+
+    nread = getline(line_p, len_p, stream);
+    if (nread < 0) {
+	perror ("getline");
+	exit (EXIT_FAILURE);
+    }
+    ASSERT(line_p);
+
+    chomp_newline(*line_p);
+}
+
+static const char*
+shell2string (const char* command) {
+    FILE *out;
+    char *line = NULL;
+    size_t len;
+
+    out = popen (command,"r");
+    if (out == NULL) {
+	perror("popen");
+	exit(EXIT_FAILURE);
+    }
+
+    read_one_line (out, &line, &len);
+    return line;
+}
+
+static void
+wr_data(const char *data) {
+    printf ("data %zu\n", strlen(data));
+    fputs (data, stdout);
+}
+
 static void
 cmd_capabilities () {
     fputs("import\nexport\nrefspec refs/heads/*:refs/notmuch/*\n\n", stdout);
@@ -97,6 +145,85 @@ usage() {
     exit(EXIT_FAILURE);
 }
 
+static void
+cmd_import (notmuch_database_t *notmuch) {
+    const char* ident = NULL;
+    const char* lastmod_str = NULL;
+    notmuch_messages_t *messages;
+    notmuch_status_t status;
+    notmuch_query_t *query;
+    char *mid_buf = NULL;
+    size_t mid_buf_len = 0;
+
+    ident = shell2string("git var GIT_COMMITTER_IDENT");
+
+    printf ("feature done\ncommit refs/notmuch/master\nmark :1\ncommitter %s\n", ident);
+
+    ASSERT(lastmod_str = talloc_asprintf (notmuch, "lastmod: %zu\n", lastmod));
+    wr_data (lastmod_str);
+    if (lastmod > 0)
+	puts("from refs/notmuch/master^0");
+    puts("deleteall");
+
+    status = notmuch_query_create_with_syntax (notmuch,
+					       "",
+					       NOTMUCH_QUERY_SYNTAX_XAPIAN,
+					       &query);
+
+    if (print_status_database ("git-remote-nm", notmuch, status))
+	exit(EXIT_FAILURE);
+
+    if (debug_flags && strchr (debug_flags, 's'))
+	notmuch_query_set_sort (query, NOTMUCH_SORT_NEWEST_FIRST);
+    else
+	notmuch_query_set_sort (query, NOTMUCH_SORT_UNSORTED);
+
+    status = notmuch_query_search_messages (query, &messages);
+    if (print_status_query ("git-remote-nm", query, status))
+	exit(EXIT_FAILURE);
+
+    for (;
+	 notmuch_messages_valid (messages);
+	 notmuch_messages_move_to_next (messages)) {
+
+	const char* tag_buf = "";
+	const char* mid;
+
+	const char* hash;
+
+	notmuch_message_t *message = notmuch_messages_get (messages);
+	mid = notmuch_message_get_message_id (message);
+
+	if (hex_encode (notmuch, mid, &mid_buf, &mid_buf_len) != HEX_SUCCESS) {
+	    fprintf (stderr, "Error: failed to hex-encode message-id %s\n", mid);
+	    exit(EXIT_FAILURE);
+	}
+
+	/* we can't use _notmuch_sha1_from_string because we don't want
+	 * to include the null terminator */
+	GChecksum *sha1;
+	sha1 = g_checksum_new (G_CHECKSUM_SHA1);
+	g_checksum_update (sha1, (const guchar *) mid, strlen (mid));
+	hash = g_checksum_get_string (sha1);
+	printf ("M 644 inline %2.2s/%2.2s/%s/tags\n", hash, hash+2, mid_buf);
+
+	g_checksum_free (sha1);
+
+	for (notmuch_tags_t *tags = notmuch_message_get_tags (message);
+	     notmuch_tags_valid (tags);
+	     notmuch_tags_move_to_next (tags)) {
+	    const char *tag_str = notmuch_tags_get (tags);
+	    ASSERT(tag_buf=talloc_asprintf (message, "%s%s\n", tag_buf, tag_str));
+	}
+	wr_data (tag_buf);
+	notmuch_message_destroy (message);
+    }
+    puts("");
+    puts("done");
+    fflush (stdout);
+    store_lastmod(notmuch, nm_dir);
+}
+
 int
 main (int argc, char *argv[])
 {
@@ -169,6 +296,8 @@ main (int argc, char *argv[])
 
       if (STRNCMP_LITERAL (s, "capabilities")== 0)
 	  cmd_capabilities ();
+      else if (STRNCMP_LITERAL (s, "import") == 0)
+	  cmd_import (db);
       else if (STRNCMP_LITERAL (s, "list") == 0)
 	  cmd_list ();
 
diff --git a/test/T860-git-remote.sh b/test/T860-git-remote.sh
index 7b2b6b49..d5809e6b 100755
--- a/test/T860-git-remote.sh
+++ b/test/T860-git-remote.sh
@@ -20,7 +20,7 @@ export GIT_COMMITTER_NAME="Notmuch Test Suite"
 export GIT_COMMITTER_EMAIL="notmuch@example.com"
 export GIT_REMOTE_NM_DEBUG="s"
 export GIT_REMOTE_NM_LOG=grn-log.txt
-EXPECTED=$NOTMUCH_SRCDIR/test/git-remote-nm.expected-output
+EXPECTED=$NOTMUCH_SRCDIR/test/git-remote.expected-output
 
 TAG_FILE="87/b1/4EFC743A.3060609@april.org/tags"
 
@@ -42,4 +42,40 @@ cat <<EOF > EXPECTED
 EOF
 test_expect_equal_file EXPECTED OUTPUT
 
+test_begin_subtest 'import writes lastmod file'
+echo import | run_helper dummy-alias dummy-url > /dev/null
+lastmod=$(notmuch count --lastmod '*' | cut -f3)
+test_expect_equal "${lastmod}" "$(cat ${git_tmp}/notmuch/lastmod)"
+
+# note that this test must not be the first time import is run,
+# because it depends on the lastmod file
+test_begin_subtest 'import produces expected output'
+echo import | run_helper | notmuch_sanitize_git > OUTPUT
+test_expect_equal_file $EXPECTED/default.import OUTPUT
+
+test_begin_subtest "clone has every message"
+git clone notmuch::currently-ignored repo
+find repo -name tags -type f | sed -e s,repo/../../,id:, -e s,/tags$,, | sort > OUTPUT
+notmuch search --output=messages '*' | sort > EXPECTED
+test_expect_equal_file EXPECTED OUTPUT
+
+test_begin_subtest "pull get new tag"
+notmuch tag +zznew -- id:4EFC743A.3060609@april.org
+git -C repo pull
+cat<<EOF >EXPECTED
+inbox
+unread
+zznew
+EOF
+test_expect_equal_file EXPECTED repo/$TAG_FILE
+
+test_begin_subtest "pull sees deletion"
+notmuch tag -unread -- id:4EFC743A.3060609@april.org
+git -C repo pull
+cat<<EOF >EXPECTED
+inbox
+zznew
+EOF
+test_expect_equal_file EXPECTED repo/$TAG_FILE
+
 test_done
-- 
2.43.0

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

Thread: