diff options
| author | David Bremner <david@tethera.net> | 2026-01-25 07:56:39 +0900 |
|---|---|---|
| committer | David Bremner <david@tethera.net> | 2026-02-16 07:24:18 +0900 |
| commit | b7df6feb9809fa9735141fad01616555f4b21726 (patch) | |
| tree | 434e4e477ca68628d20bd355d2b668c0c5fd2817 /git-remote-notmuch.c | |
| parent | c4c0843d8fed39d4d7da6be493248f75d0c0d706 (diff) | |
cli/git-remote: handle message deletions
There are two main possibilities. One is explicit delete ('D' command
in the stream from git fast-export) and one is files disappearing
between commits. It is less clear the latter can happen in well formed
sequence of commits, but it could result e.g. from manual changes to
the repo.
Diffstat (limited to 'git-remote-notmuch.c')
| -rw-r--r-- | git-remote-notmuch.c | 123 |
1 files changed, 118 insertions, 5 deletions
diff --git a/git-remote-notmuch.c b/git-remote-notmuch.c index addf23c7..a81c969e 100644 --- a/git-remote-notmuch.c +++ b/git-remote-notmuch.c @@ -49,6 +49,18 @@ typedef enum { MSG_STATE_DELETED } _message_state_t; +static _message_state_t +get_message_state (GHashTable *mid_state, const char *key) +{ + gpointer val = NULL; + + if (! g_hash_table_lookup_extended (mid_state, key, NULL, + &val)) + return MSG_STATE_UNKNOWN; + else + return GPOINTER_TO_INT (val); +} + static bool set_message_state (GHashTable *mid_state, const char *mid, _message_state_t state) { @@ -262,7 +274,7 @@ cmd_import (notmuch_database_t *notmuch, exit (EXIT_FAILURE); for (; - notmuch_messages_valid (messages); + ! notmuch_messages_status (messages); notmuch_messages_move_to_next (messages)) { const char *tag_buf = ""; const char *mid; @@ -293,6 +305,11 @@ cmd_import (notmuch_database_t *notmuch, write_data (tag_buf); notmuch_message_destroy (message); } + status = notmuch_messages_status (messages); + if (status != NOTMUCH_STATUS_ITERATOR_EXHAUSTED && + print_status_database ("git-remote-notmuch", notmuch, status)) + exit (EXIT_FAILURE); + puts (""); puts ("done"); fflush (stdout); @@ -344,16 +361,112 @@ path_to_mid (notmuch_database_t *notmuch, const char *path, char **mid_p, size_t return true; } +/* In order to force a message to be deleted from the database, we + * need to delete all of its filenames. XXX TODO Add to library + * API? */ +static notmuch_status_t +remove_message_all (notmuch_database_t *notmuch, + notmuch_message_t *message) +{ + notmuch_filenames_t *filenames = NULL; + notmuch_status_t status; + + for (filenames = notmuch_message_get_filenames (message); + notmuch_filenames_valid (filenames); + notmuch_filenames_move_to_next (filenames)) { + const char *filename = notmuch_filenames_get (filenames); + status = notmuch_database_remove_message (notmuch, filename); + if (status != NOTMUCH_STATUS_SUCCESS && + status != NOTMUCH_STATUS_DUPLICATE_MESSAGE_ID) { + fprintf (stderr, "failed to remove %s from database\n", filename); + return status; + } + } + return NOTMUCH_STATUS_SUCCESS; +} + static void -mark_unseen (unused (notmuch_database_t *notmuch), - unused (GHashTable *mid_state)) +mark_unseen (notmuch_database_t *notmuch, + GHashTable *mid_state) { + notmuch_status_t status; + notmuch_messages_t *messages; + notmuch_query_t *query; + + if (debug_flags && strchr (debug_flags, 'd')) { + flog ("total mids = %d\n", g_hash_table_size (mid_state)); + } + status = notmuch_query_create_with_syntax (notmuch, + "", + NOTMUCH_QUERY_SYNTAX_XAPIAN, + &query); + + if (print_status_database ("git-remote-nm", notmuch, status)) + exit (EXIT_FAILURE); + + 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_status (messages); + notmuch_messages_move_to_next (messages)) { + notmuch_message_t *message = notmuch_messages_get (messages); + const char *mid = notmuch_message_get_message_id (message); + + switch (get_message_state (mid_state, mid)) { + case MSG_STATE_SEEN: + case MSG_STATE_DELETED: + break; + case MSG_STATE_UNKNOWN: + set_message_state (mid_state, mid, MSG_STATE_DELETED); + break; + case MSG_STATE_MISSING: + INTERNAL_ERROR ("found missing mid %s", mid); + } + notmuch_message_destroy (message); + } + status = notmuch_messages_status (messages); + if (status != NOTMUCH_STATUS_ITERATOR_EXHAUSTED && + print_status_database ("git-remote-notmuch", notmuch, status)) + exit (EXIT_FAILURE); + } static void -purge_database (unused (notmuch_database_t *notmuch), - unused (GHashTable *mid_state)) +purge_database (notmuch_database_t *notmuch, GHashTable *msg_state) { + gpointer key, value; + + GHashTableIter iter; + int count = 0; + + if (debug_flags && strchr (debug_flags, 'd')) + flog ("removing unseen messages from database\n"); + + g_hash_table_iter_init (&iter, msg_state); + while (g_hash_table_iter_next (&iter, &key, &value)) { + notmuch_message_t *message; + const char *mid = key; + + if (GPOINTER_TO_INT (value) != MSG_STATE_DELETED) + continue; + + ASSERT (NOTMUCH_STATUS_SUCCESS == + notmuch_database_find_message (notmuch, + mid, &message)); + /* If the message is in the database, clean up */ + if (message) { + remove_message_all (notmuch, message); + if (debug_flags && strchr (debug_flags, 'd')) + flog ("removed from database %s\n", mid); + count++; + } + } + if (debug_flags && strchr (debug_flags, 'd')) + flog ("removed %d messages from database\n", count); } static void |
