[PATCH 2/5] cli: start remote helper for git.

Subject: [PATCH 2/5] cli: start remote helper for git.

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

To: notmuch@notmuchmail.org

Cc:

From: David Bremner


This is closely based on git-remote-nm (in ruby) by Felipe Contreras.
Initially just implement the commands 'capabilites' and 'list'.  This
isn't enough to do anything useful so start some unit tests.
---
 Makefile.local          |   7 +-
 git-remote-notmuch.c    | 181 ++++++++++++++++++++++++++++++++++++++++
 test/T860-git-remote.sh |  45 ++++++++++
 3 files changed, 232 insertions(+), 1 deletion(-)
 create mode 100644 git-remote-notmuch.c
 create mode 100755 test/T860-git-remote.sh

diff --git a/Makefile.local b/Makefile.local
index 7699c208..2ac494b8 100644
--- a/Makefile.local
+++ b/Makefile.local
@@ -1,7 +1,8 @@
 # -*- makefile-gmake -*-
 
 .PHONY: all
-all: notmuch notmuch-shared build-man build-info ruby-bindings python-cffi-bindings notmuch-git nmbug
+all: notmuch notmuch-shared git-remote-notmuch \
+	build-man build-info ruby-bindings python-cffi-bindings notmuch-git nmbug
 ifeq ($(MAKECMDGOALS),)
 ifeq ($(shell cat .first-build-message 2>/dev/null),)
 	@NOTMUCH_FIRST_BUILD=1 $(MAKE) --no-print-directory all
@@ -274,6 +275,9 @@ notmuch: $(notmuch_client_modules) lib/libnotmuch.a util/libnotmuch_util.a parse
 notmuch-shared: $(notmuch_client_modules) lib/$(LINKER_NAME)
 	$(call quiet,$(FINAL_NOTMUCH_LINKER) $(CFLAGS)) $(notmuch_client_modules) $(FINAL_NOTMUCH_LDFLAGS) -o $@
 
+git-remote-notmuch: git-remote-notmuch.o status.o
+	$(call quiet,$(FINAL_NOTMUCH_LINKER) $(CFLAGS)) $^ $(FINAL_NOTMUCH_LDFLAGS) -o $@
+
 .PHONY: install
 install: all install-man install-info
 	mkdir -p "$(DESTDIR)$(prefix)/bin/"
@@ -302,6 +306,7 @@ endif
 
 SRCS  := $(SRCS) $(notmuch_client_srcs)
 CLEAN := $(CLEAN) notmuch notmuch-shared $(notmuch_client_modules)
+CLEAN := $(CLEAN) git-remote-notmuch git-remote-notmuch.o
 CLEAN := $(CLEAN) version.stamp notmuch-*.tar.gz.tmp
 CLEAN := $(CLEAN) .deps
 
diff --git a/git-remote-notmuch.c b/git-remote-notmuch.c
new file mode 100644
index 00000000..cfc43a68
--- /dev/null
+++ b/git-remote-notmuch.c
@@ -0,0 +1,181 @@
+/* notmuch - Not much of an email program, (just index and search)
+ *
+ * Copyright © 2023 Felipe Contreras
+ * Copyright © 2024 David Bremner
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see https://www.gnu.org/licenses/ .
+ *
+ * Authors: Felipe Contreras
+ *	    David Bremner <david@tethera.net>
+ */
+
+#include <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <notmuch.h>
+#include "notmuch-client.h"
+#include "path-util.h"
+#include "hex-escape.h"
+#include "string-util.h"
+
+#define ASSERT(x) assert((x))
+
+/* File scope globals */
+const char *alias = NULL;
+const char *nm_dir = NULL;
+const char *url = NULL;
+const char* debug_flags = NULL;
+unsigned long lastmod;
+notmuch_database_t *db;
+FILE *log_file = NULL;
+
+static void
+flog (const char *format, ...) {
+    va_list va_args;
+
+    if (log_file) {
+	va_start (va_args, format);
+	vfprintf (log_file, format, va_args);
+	fflush (log_file);
+	va_end (va_args);
+    }
+}
+
+static unsigned long read_lastmod (const void *ctx, const char *dir) {
+    char *filename = NULL;
+    unsigned long num = 0;
+
+    FILE *in;
+
+    ASSERT(filename = talloc_asprintf (ctx, "%s/lastmod", dir));
+
+    in = fopen (filename, "r");
+    if (in) {
+	ASSERT(fscanf (in, "%zu", &num) == 1);
+    } else {
+	if (errno != ENOENT) {
+	    fprintf (stderr, "error opening lastmod file");
+	    exit(EXIT_FAILURE);
+	}
+    }
+
+    flog ("loaded lastmod = %zu\n", num);
+
+    return num;
+}
+
+static void
+cmd_capabilities () {
+    fputs("import\nexport\nrefspec refs/heads/*:refs/notmuch/*\n\n", stdout);
+    fflush (stdout);
+}
+
+static void
+cmd_list () {
+    unsigned long current_lastmod;
+    current_lastmod = notmuch_database_get_revision (db, NULL);
+    printf("? refs/heads/master%s\n\n",
+	   lastmod == current_lastmod ? " unchanged" : "");
+    fflush (stdout);
+}
+
+static void
+usage() {
+    fprintf (stderr, "usage: git-remote-nm ALIAS URL\n");
+    exit(EXIT_FAILURE);
+}
+
+int
+main (int argc, char *argv[])
+{
+  notmuch_status_t status;
+  char *status_string = NULL;
+  const char* git_dir;
+  ssize_t nread;
+  size_t len = 0;
+  const char *log_file_name;
+
+  char *line = NULL;
+
+  debug_flags = getenv ("GIT_REMOTE_NM_DEBUG");
+  log_file_name = getenv ("GIT_REMOTE_NM_LOG");
+
+  if (log_file_name)
+      log_file = fopen (log_file_name, "w");
+
+  if (argc != 3)
+    usage();
+  /* setup globals */
+  alias = argv[1];
+  url = argv[2];
+
+  status = notmuch_database_open_with_config (NULL,
+					      NOTMUCH_DATABASE_MODE_READ_WRITE,
+					      NULL,
+					      NULL,
+					      &db,
+					      &status_string);
+  if (status) {
+      if (status_string) {
+	  fputs (status_string, stderr);
+	  free (status_string);
+	  status_string = NULL;
+      }
+      return EXIT_FAILURE;
+  }
+
+  git_dir = getenv ("GIT_DIR");
+  if (! git_dir) {
+      fprintf (stderr, "GIT_DIR not set\n");
+      exit(EXIT_FAILURE);
+  }
+  flog ("GIT_DIR=%s\n", git_dir);
+
+  ASSERT(nm_dir = talloc_asprintf(db, "%s/%s", git_dir, "notmuch"));
+
+  status = mkdir_recursive (db, nm_dir, 0700, &status_string);
+  if (status) {
+      if (status_string)
+	  fputs(status_string, stderr);
+
+      exit (EXIT_FAILURE);
+  }
+
+  lastmod = read_lastmod (db, nm_dir);
+
+  while ((nread = getline(&line, &len, stdin)) != -1) {
+      size_t count=0;
+      char *s = line;
+      flog ("command = %s\n", line);
+
+      /* skip leading space */
+      while (*s && isspace(*s))  s++;
+      while (s[count]  && ! isspace(s[count])) count++;
+
+      if (count == 0)
+	  break;
+
+      if (STRNCMP_LITERAL (s, "capabilities")== 0)
+	  cmd_capabilities ();
+      else if (STRNCMP_LITERAL (s, "list") == 0)
+	  cmd_list ();
+
+      fflush(stdout);
+      flog ("finished command = %s\n", s);
+  }
+  flog ("finished loop\n");
+
+  notmuch_database_destroy (db);
+}
diff --git a/test/T860-git-remote.sh b/test/T860-git-remote.sh
new file mode 100755
index 00000000..7b2b6b49
--- /dev/null
+++ b/test/T860-git-remote.sh
@@ -0,0 +1,45 @@
+#!/usr/bin/env bash
+test_description='git-remote-notmuch'
+. $(dirname "$0")/test-lib.sh || exit 1
+
+notmuch_sanitize_git() {
+    sed 's/^committer \(.*\) \(<[^>]*>\) [1-9][0-9]* [-+][0-9]*/committer \1 \2 TIMESTAMP TIMEZONE/'
+}
+
+add_email_corpus
+
+mkdir repo
+
+git_tmp=$(mktemp -d gitXXXXXXXX)
+
+run_helper () {
+    GIT_DIR=${git_tmp} git-remote-notmuch dummy-alias dummy-url
+}
+
+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
+
+TAG_FILE="87/b1/4EFC743A.3060609@april.org/tags"
+
+test_begin_subtest 'capabilities'
+echo capabilities | run_helper > OUTPUT
+cat <<EOF > EXPECTED
+import
+export
+refspec refs/heads/*:refs/notmuch/*
+
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
+test_begin_subtest 'list'
+echo list | run_helper > OUTPUT
+cat <<EOF > EXPECTED
+? refs/heads/master
+
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
+test_done
-- 
2.43.0

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

Thread: