[PATCH] notmuch restore --accumulate
authorThomas Schwinge <thomas@schwinge.name>
Mon, 5 Sep 2011 19:07:17 +0000 (21:07 +0200)
committerW. Trevor King <wking@tremily.us>
Fri, 7 Nov 2014 17:39:22 +0000 (09:39 -0800)
cd/99799c68034f23eb879174b61e46fffbe2b040 [new file with mode: 0644]

diff --git a/cd/99799c68034f23eb879174b61e46fffbe2b040 b/cd/99799c68034f23eb879174b61e46fffbe2b040
new file mode 100644 (file)
index 0000000..54a2902
--- /dev/null
@@ -0,0 +1,351 @@
+Return-Path: <thomas@schwinge.name>\r
+X-Original-To: notmuch@notmuchmail.org\r
+Delivered-To: notmuch@notmuchmail.org\r
+Received: from localhost (localhost [127.0.0.1])\r
+       by olra.theworths.org (Postfix) with ESMTP id A9C2E431FD0\r
+       for <notmuch@notmuchmail.org>; Mon,  5 Sep 2011 12:14:30 -0700 (PDT)\r
+X-Virus-Scanned: Debian amavisd-new at olra.theworths.org\r
+X-Spam-Flag: NO\r
+X-Spam-Score: 0\r
+X-Spam-Level: \r
+X-Spam-Status: No, score=0 tagged_above=-999 required=5\r
+       tests=[RCVD_IN_DNSWL_NONE=-0.0001] autolearn=disabled\r
+Received: from olra.theworths.org ([127.0.0.1])\r
+       by localhost (olra.theworths.org [127.0.0.1]) (amavisd-new, port 10024)\r
+       with ESMTP id Ko57vJQykeGw for <notmuch@notmuchmail.org>;\r
+       Mon,  5 Sep 2011 12:14:28 -0700 (PDT)\r
+X-Greylist: delayed 379 seconds by postgrey-1.32 at olra;\r
+       Mon, 05 Sep 2011 12:14:28 PDT\r
+Received: from smtprelay03.ispgateway.de (smtprelay03.ispgateway.de\r
+       [80.67.31.37])\r
+       by olra.theworths.org (Postfix) with ESMTP id 7E473431FB6\r
+       for <notmuch@notmuchmail.org>; Mon,  5 Sep 2011 12:14:28 -0700 (PDT)\r
+Received: from [87.180.32.105] (helo=stokes.schwinge.homeip.net)\r
+       by smtprelay03.ispgateway.de with esmtpa (Exim 4.68)\r
+       (envelope-from <thomas@schwinge.name>) id 1R0eWb-0005OW-TD\r
+       for notmuch@notmuchmail.org; Mon, 05 Sep 2011 21:08:06 +0200\r
+Received: (qmail 14781 invoked from network); 5 Sep 2011 19:07:36 -0000\r
+Received: from kepler.schwinge.homeip.net (192.168.111.7)\r
+       by stokes.schwinge.homeip.net with QMQP; 5 Sep 2011 19:07:36 -0000\r
+Received: (nullmailer pid 20213 invoked by uid 1000);\r
+       Mon, 05 Sep 2011 19:07:35 -0000\r
+From: Thomas Schwinge <thomas@schwinge.name>\r
+To: notmuch@notmuchmail.org\r
+Subject: =?UTF-8?q?=5BPATCH=5D=20notmuch=20restore=20--accumulate?=\r
+Date: Mon,  5 Sep 2011 21:07:17 +0200\r
+Message-Id: <1315249637-20179-1-git-send-email-thomas@schwinge.name>\r
+X-Mailer: git-send-email 1.7.5.4\r
+MIME-Version: 1.0\r
+Content-Type: text/plain; charset=UTF-8\r
+Content-Transfer-Encoding: 8bit\r
+X-Df-Sender: dGhvbWFzQHNjaHdpbmdlLm5hbWU=\r
+Cc: Thomas Schwinge <thomas@schwinge.name>\r
+X-BeenThere: notmuch@notmuchmail.org\r
+X-Mailman-Version: 2.1.13\r
+Precedence: list\r
+List-Id: "Use and development of the notmuch mail system."\r
+       <notmuch.notmuchmail.org>\r
+List-Unsubscribe: <http://notmuchmail.org/mailman/options/notmuch>,\r
+       <mailto:notmuch-request@notmuchmail.org?subject=unsubscribe>\r
+List-Archive: <http://notmuchmail.org/pipermail/notmuch>\r
+List-Post: <mailto:notmuch@notmuchmail.org>\r
+List-Help: <mailto:notmuch-request@notmuchmail.org?subject=help>\r
+List-Subscribe: <http://notmuchmail.org/mailman/listinfo/notmuch>,\r
+       <mailto:notmuch-request@notmuchmail.org?subject=subscribe>\r
+X-List-Received-Date: Mon, 05 Sep 2011 19:14:30 -0000\r
+\r
+From: Thomas Schwinge <thomas@schwinge.name>\r
+\r
+Also enhance the dump-restore testsuite, and make it generally more\r
+failure-proof.\r
+\r
+Signed-off-by: Thomas Schwinge <thomas@schwinge.name>\r
+\r
+---\r
+\r
+Hi!\r
+\r
+Beware that I have not yet used this new functionality in the wild.  ;-)\r
+(But I do plan to do so, soon.)  And, I think that the testsuite\r
+enhancements cover quite a number of real-world scenarios.\r
+\r
+\r
+Grüße,\r
+ Thomas\r
+\r
+---\r
+\r
+ NEWS              |   13 ++++++++++\r
+ notmuch-restore.c |   42 ++++++++++++++++++++++++++-------\r
+ notmuch.1         |   14 ++++++++---\r
+ notmuch.c         |   10 +++++--\r
+ test/dump-restore |   67 ++++++++++++++++++++++++++++++++++++++++------------\r
+ test/test-lib.sh  |    1 +\r
+ 6 files changed, 115 insertions(+), 32 deletions(-)\r
+\r
+diff --git a/NEWS b/NEWS\r
+index f715142..d2a788f 100644\r
+--- a/NEWS\r
++++ b/NEWS\r
+@@ -1,3 +1,16 @@\r
++Notmuch TODO (TODO)\r
++===================\r
++\r
++New command-line features\r
++-------------------------\r
++\r
++Add "notmuch restore --accumulate" option\r
++\r
++  The --accumulate switch causes the union of the existing and new tags to be\r
++  applied, instead of replacing each message's tags as they are read in from\r
++  the dump file.\r
++\r
++\r
+ Notmuch 0.7 (2011-08-01)\r
+ ========================\r
\r
+diff --git a/notmuch-restore.c b/notmuch-restore.c\r
+index f095f64..5aad60c 100644\r
+--- a/notmuch-restore.c\r
++++ b/notmuch-restore.c\r
+@@ -31,7 +31,8 @@ notmuch_restore_command (unused (void *ctx), int argc, char *argv[])\r
+     size_t line_size;\r
+     ssize_t line_len;\r
+     regex_t regex;\r
+-    int rerr;\r
++    notmuch_bool_t accumulate;\r
++    int i, rerr;\r
\r
+     config = notmuch_config_open (ctx, NULL, NULL);\r
+     if (config == NULL)\r
+@@ -44,14 +45,28 @@ notmuch_restore_command (unused (void *ctx), int argc, char *argv[])\r
\r
+     synchronize_flags = notmuch_config_get_maildir_synchronize_flags (config);\r
\r
+-    if (argc) {\r
+-      input = fopen (argv[0], "r");\r
+-      if (input == NULL) {\r
+-          fprintf (stderr, "Error opening %s for reading: %s\n",\r
+-                   argv[0], strerror (errno));\r
+-          return 1;\r
++    accumulate = FALSE;\r
++    input = NULL;\r
++    for (i = 0; i < argc; i++) {\r
++      if (STRNCMP_LITERAL (argv[i], "--accumulate") == 0) {\r
++          accumulate = TRUE;\r
++      } else {\r
++          if (input == NULL) {\r
++              input = fopen (argv[i], "r");\r
++              if (input == NULL) {\r
++                  fprintf (stderr, "Error opening %s for reading: %s\n",\r
++                           argv[i], strerror (errno));\r
++                  return 1;\r
++              }\r
++          } else {\r
++              fprintf (stderr,\r
++                       "Cannot read dump from more than one file: %s\n",\r
++                       argv[i]);\r
++              return 1;\r
++          }\r
+       }\r
+-    } else {\r
++    }\r
++    if (input == NULL) {\r
+       printf ("No filename given. Reading dump from stdin.\n");\r
+       input = stdin;\r
+     }\r
+@@ -94,6 +109,13 @@ notmuch_restore_command (unused (void *ctx), int argc, char *argv[])\r
+           goto NEXT_LINE;\r
+       }\r
\r
++      /* In order to detect missing messages, this check/optimization is\r
++       * intentionally done *after* first finding the message.  */\r
++      if (accumulate && (file_tags == NULL || *file_tags == '\0'))\r
++      {\r
++          goto NEXT_LINE;\r
++      }\r
++\r
+       db_tags_str = NULL;\r
+       for (db_tags = notmuch_message_get_tags (message);\r
+            notmuch_tags_valid (db_tags);\r
+@@ -115,7 +137,9 @@ notmuch_restore_command (unused (void *ctx), int argc, char *argv[])\r
+       }\r
\r
+       notmuch_message_freeze (message);\r
+-      notmuch_message_remove_all_tags (message);\r
++\r
++      if (!accumulate)\r
++          notmuch_message_remove_all_tags (message);\r
\r
+       next = file_tags;\r
+       while (next) {\r
+diff --git a/notmuch.1 b/notmuch.1\r
+index 5a8c83d..883371d 100644\r
+--- a/notmuch.1\r
++++ b/notmuch.1\r
+@@ -454,7 +454,7 @@ section below for details of the supported syntax for <search-terms>.\r
+ The\r
+ .BR dump " and " restore\r
+ commands can be used to create a textual dump of email tags for backup\r
+-purposes, and to restore from that dump\r
++purposes, and to restore from that dump.\r
\r
+ .RS 4\r
+ .TP 4\r
+@@ -462,17 +462,19 @@ purposes, and to restore from that dump\r
\r
+ Creates a plain-text dump of the tags of each message.\r
\r
+-The output is to the given filename, if any, or to stdout.\r
++The output is written to the given filename, if any, or to stdout.\r
\r
+ These tags are the only data in the notmuch database that can't be\r
+ recreated from the messages themselves.  The output of notmuch dump is\r
+ therefore the only critical thing to backup (and much more friendly to\r
+ incremental backup than the native database files.)\r
+ .TP\r
+-.BR restore " <filename>"\r
++.BR restore " [--accumulate] [<filename>]"\r
\r
+ Restores the tags from the given file (see\r
+-.BR "notmuch dump" "."\r
++.BR "notmuch dump" ")."\r
++\r
++The input is read from the given filename, if any, or from stdin.\r
\r
+ Note: The dump file format is specifically chosen to be\r
+ compatible with the format of files produced by sup-dump.\r
+@@ -480,6 +482,10 @@ So if you've previously been using sup for mail, then the\r
+ .B "notmuch restore"\r
+ command provides you a way to import all of your tags (or labels as\r
+ sup calls them).\r
++\r
++The --accumulate switch causes the union of the existing and new tags to be\r
++applied, instead of replacing each message's tags as they are read in from the\r
++dump file.\r
+ .RE\r
\r
+ The\r
+diff --git a/notmuch.c b/notmuch.c\r
+index 3973e35..def52b0 100644\r
+--- a/notmuch.c\r
++++ b/notmuch.c\r
+@@ -377,20 +377,24 @@ static command_t commands[] = {\r
+     { "dump", notmuch_dump_command,\r
+       "[<filename>]",\r
+       "Create a plain-text dump of the tags for each message.",\r
+-      "\tOutput is to the given filename, if any, or to stdout.\n"\r
++      "\tOutput is written to the given filename, if any, or to stdout.\n"\r
+       "\tThese tags are the only data in the notmuch database\n"\r
+       "\tthat can't be recreated from the messages themselves.\n"\r
+       "\tThe output of notmuch dump is therefore the only\n"\r
+       "\tcritical thing to backup (and much more friendly to\n"\r
+       "\tincremental backup than the native database files.)" },\r
+     { "restore", notmuch_restore_command,\r
+-      "<filename>",\r
++      "[--accumulate] [<filename>]",\r
+       "Restore the tags from the given dump file (see 'dump').",\r
++      "\tInput is read from the given filename, if any, or from stdin.\n"\r
+       "\tNote: The dump file format is specifically chosen to be\n"\r
+       "\tcompatible with the format of files produced by sup-dump.\n"\r
+       "\tSo if you've previously been using sup for mail, then the\n"\r
+       "\t\"notmuch restore\" command provides you a way to import\n"\r
+-      "\tall of your tags (or labels as sup calls them)." },\r
++      "\tall of your tags (or labels as sup calls them).\n"\r
++      "\tThe --accumulate switch causes the union of the existing and new\n"\r
++      "\ttags to be applied, instead of replacing each message's tags as\n"\r
++      "\tthey are read in from the dump file."},\r
+     { "config", notmuch_config_command,\r
+       "[get|set] <section>.<item> [value ...]",\r
+       "Get or set settings in the notmuch configuration file.",\r
+diff --git a/test/dump-restore b/test/dump-restore\r
+index a4de370..c85f10e 100755\r
+--- a/test/dump-restore\r
++++ b/test/dump-restore\r
+@@ -4,21 +4,56 @@ test_description="\"notmuch dump\" and \"notmuch restore\""\r
\r
+ add_email_corpus\r
\r
+-test_expect_success "Dumping all tags" "generate_message &&\r
+-notmuch new &&\r
+-notmuch dump dump.expected"\r
+-\r
+-test_begin_subtest "Clearing all tags"\r
+-sed -e "s/(\([^(]*\))$/()/" < dump.expected > clear.expected\r
+-notmuch restore clear.expected\r
+-notmuch dump clear.actual\r
+-test_expect_equal "$(< clear.actual)" "$(< clear.expected)"\r
+-\r
+-test_begin_subtest "Restoring original tags"\r
+-notmuch restore dump.expected\r
+-notmuch dump dump.actual\r
+-test_expect_equal "$(< dump.actual)" "$(< dump.expected)"\r
+-\r
+-test_expect_success "Restore with nothing to do" "notmuch restore dump.expected"\r
++test_expect_success 'Dumping all tags' \\r
++  'generate_message &&\r
++  notmuch new &&\r
++  notmuch dump dump.expected'\r
++\r
++# This is rather arbitrary: it matches some of the email corpus' messages, but\r
++# not all of them.\r
++search_term=from:worth\r
++\r
++test_expect_success 'Dumping all tags to stdout' \\r
++  'notmuch tag +ABC +DEF -- $search_term &&\r
++  notmuch dump > dump-ABC_DEF.expected &&\r
++  ! cmp dump.expected dump-ABC_DEF.expected'\r
++\r
++test_expect_success 'Clearing all tags' \\r
++  'sed -e "s/(\([^(]*\))$/()/" < dump.expected > clear.expected &&\r
++  notmuch restore clear.expected &&\r
++  notmuch dump clear.actual &&\r
++  test_cmp clear.expected clear.actual'\r
++\r
++test_expect_success 'Accumulate original tags' \\r
++  'notmuch tag +ABC +DEF -- $search_term &&\r
++  notmuch restore --accumulate < dump.expected &&\r
++  notmuch dump dump.actual &&\r
++  test_cmp dump-ABC_DEF.expected dump.actual'\r
++\r
++test_expect_success 'Restoring original tags' \\r
++  'notmuch restore dump.expected &&\r
++  notmuch dump dump.actual &&\r
++  test_cmp dump.expected dump.actual'\r
++\r
++test_expect_success 'Restore with nothing to do' \\r
++  'notmuch restore < dump.expected &&\r
++  notmuch dump > dump.actual &&\r
++  test_cmp dump.expected dump.actual'\r
++\r
++test_expect_success 'Restore with nothing to do, II' \\r
++  'notmuch restore --accumulate dump.expected &&\r
++  notmuch dump dump.actual &&\r
++  test_cmp dump.expected dump.actual'\r
++\r
++test_expect_success 'Restore with nothing to do, III' \\r
++  'notmuch restore --accumulate < clear.expected &&\r
++  notmuch dump dump.actual &&\r
++  test_cmp dump.expected dump.actual'\r
++\r
++test_expect_success 'Invalid restore invocation' \\r
++  '! notmuch restore one two'\r
++\r
++test_expect_success 'Invalid restore invocation, II' \\r
++  '! notmuch restore --accumulate one two'\r
\r
+ test_done\r
+diff --git a/test/test-lib.sh b/test/test-lib.sh\r
+index 22e387e..56bbce4 100755\r
+--- a/test/test-lib.sh\r
++++ b/test/test-lib.sh\r
+@@ -461,6 +461,7 @@ test_expect_equal ()\r
+     fi\r
+ }\r
\r
++# Like test_expect_equal, but takes two filenames.\r
+ test_expect_equal_file ()\r
+ {\r
+       exec 1>&6 2>&7          # Restore stdout and stderr\r
+-- \r
+tg: (8e2a14b..) t/restore-accumulate (depends on: master)\r