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