Re: notmuch-search-toggle-order and notmuch-tree
[notmuch-archives.git] / e5 / 3c468b8453fea902914daa416fc3a284013a36
1 Return-Path: <mforney@mforney.org>\r
2 X-Original-To: notmuch@notmuchmail.org\r
3 Delivered-To: notmuch@notmuchmail.org\r
4 Received: from localhost (localhost [127.0.0.1])\r
5         by olra.theworths.org (Postfix) with ESMTP id 8D945431E62\r
6         for <notmuch@notmuchmail.org>; Mon, 10 Dec 2012 22:55:42 -0800 (PST)\r
7 X-Virus-Scanned: Debian amavisd-new at olra.theworths.org\r
8 X-Spam-Flag: NO\r
9 X-Spam-Score: -0.7\r
10 X-Spam-Level: \r
11 X-Spam-Status: No, score=-0.7 tagged_above=-999 required=5\r
12         tests=[RCVD_IN_DNSWL_LOW=-0.7] autolearn=disabled\r
13 Received: from olra.theworths.org ([127.0.0.1])\r
14         by localhost (olra.theworths.org [127.0.0.1]) (amavisd-new, port 10024)\r
15         with ESMTP id xpN7W7eipY4g for <notmuch@notmuchmail.org>;\r
16         Mon, 10 Dec 2012 22:55:33 -0800 (PST)\r
17 Received: from mail-pb0-f53.google.com (mail-pb0-f53.google.com\r
18         [209.85.160.53]) (using TLSv1 with cipher RC4-SHA (128/128 bits))\r
19         (No client certificate requested)\r
20         by olra.theworths.org (Postfix) with ESMTPS id 5F21D431FAE\r
21         for <notmuch@notmuchmail.org>; Mon, 10 Dec 2012 22:55:33 -0800 (PST)\r
22 Received: by mail-pb0-f53.google.com with SMTP id jt11so2279356pbb.26\r
23         for <notmuch@notmuchmail.org>; Mon, 10 Dec 2012 22:55:32 -0800 (PST)\r
24 X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;\r
25         d=google.com; s=20120113;\r
26         h=from:to:subject:date:message-id:x-mailer:in-reply-to:references\r
27         :x-gm-message-state;\r
28         bh=3N0dY6fCEF5/AFsfE/ySqxnuib2mCKQgpSCauMWO8LU=;\r
29         b=G5Yqi3J1FnFlrlBecrJb32LhMBD1XSnVQQ47+P9/8XUFaB773wsP4zp0eUUYPnBxDG\r
30         rZFfsbBXbU5Yvb5tftLfUC3vEKP0PaCwVgl0avCBr9bJus9qM5qaunELuIDPN2f5mQoF\r
31         PUGZ8fJveTbOYqa5u3zjItz8fzypb97zxAFodTm9TRNPJKxEq2cv/X0oQyMOsLpIWiOL\r
32         jVVbVVM/kMITBPbiplGrWA8PUiNmV7AeY6DcpQphBd2cHCxs1mHAl3l+znvE9CbSraAz\r
33         9NVMBwyLGSKSDd6CclAr2iRvwsOjfZRvizwOvN6wYPMZUumzx7Cn/ggGI+1pDrtDHL4v\r
34         X/vA==\r
35 Received: by 10.68.232.2 with SMTP id tk2mr46481367pbc.92.1355208932676;\r
36         Mon, 10 Dec 2012 22:55:32 -0800 (PST)\r
37 Received: from localhost ([2601:9:580:4c:21f:16ff:fe34:119e])\r
38         by mx.google.com with ESMTPS id qt3sm13240882pbb.32.2012.12.10.22.55.31\r
39         (version=TLSv1/SSLv3 cipher=OTHER);\r
40         Mon, 10 Dec 2012 22:55:31 -0800 (PST)\r
41 From: Michael Forney <mforney@mforney.org>\r
42 To: notmuch@notmuchmail.org\r
43 Subject: [PATCH 1/2] lib: Replace freeze/thaw functionality with single sync\r
44         function\r
45 Date: Mon, 10 Dec 2012 22:54:52 -0800\r
46 Message-Id: <1355208893-16122-2-git-send-email-mforney@mforney.org>\r
47 X-Mailer: git-send-email 1.8.0\r
48 In-Reply-To: <1355208893-16122-1-git-send-email-mforney@mforney.org>\r
49 References: <1355208893-16122-1-git-send-email-mforney@mforney.org>\r
50 X-Gm-Message-State:\r
51  ALoCoQlWzDRk31/WdUDpXOQOP+1paqEyVfxtwJKqS1PJoOsnuYgKhXvLW+U9y3G9BlwrxXkZy2rK\r
52 X-BeenThere: notmuch@notmuchmail.org\r
53 X-Mailman-Version: 2.1.13\r
54 Precedence: list\r
55 List-Id: "Use and development of the notmuch mail system."\r
56         <notmuch.notmuchmail.org>\r
57 List-Unsubscribe: <http://notmuchmail.org/mailman/options/notmuch>,\r
58         <mailto:notmuch-request@notmuchmail.org?subject=unsubscribe>\r
59 List-Archive: <http://notmuchmail.org/pipermail/notmuch>\r
60 List-Post: <mailto:notmuch@notmuchmail.org>\r
61 List-Help: <mailto:notmuch-request@notmuchmail.org?subject=help>\r
62 List-Subscribe: <http://notmuchmail.org/mailman/listinfo/notmuch>,\r
63         <mailto:notmuch-request@notmuchmail.org?subject=subscribe>\r
64 X-List-Received-Date: Tue, 11 Dec 2012 06:55:42 -0000\r
65 \r
66 In all cases I could find, message tags were being modified with\r
67 freeze/thaw. This commit changes the default operation of\r
68 notmuch_message_{add_tag,remove_tag,remove_all_tags} to *not* commit\r
69 changes until notmuch_message_sync is called.\r
70 ---\r
71  bindings/go/src/notmuch/notmuch.go      |  69 ++++------------\r
72  bindings/python/docs/source/message.rst |   4 +-\r
73  bindings/python/notmuch/__init__.py     |   1 -\r
74  bindings/python/notmuch/errors.py       |   7 --\r
75  bindings/python/notmuch/message.py      | 139 ++++++++------------------------\r
76  bindings/ruby/defs.h                    |   6 +-\r
77  bindings/ruby/init.c                    |  12 +--\r
78  bindings/ruby/message.c                 |  27 +------\r
79  bindings/ruby/status.c                  |   2 -\r
80  contrib/notmuch-deliver/src/main.c      |  10 +++\r
81  lib/database.cc                         |  14 ++--\r
82  lib/message.cc                          |  92 +++++----------------\r
83  lib/notmuch-private.h                   |   7 +-\r
84  lib/notmuch.h                           |  87 ++++++--------------\r
85  notmuch-new.c                           |   4 +-\r
86  notmuch-tag.c                           |   4 +-\r
87  tag-util.c                              |  10 +--\r
88  17 files changed, 123 insertions(+), 372 deletions(-)\r
89 \r
90 diff --git a/bindings/go/src/notmuch/notmuch.go b/bindings/go/src/notmuch/notmuch.go\r
91 index 00bd53a..56eb710 100644\r
92 --- a/bindings/go/src/notmuch/notmuch.go\r
93 +++ b/bindings/go/src/notmuch/notmuch.go\r
94 @@ -26,7 +26,6 @@ const (\r
95         STATUS_DUPLICATE_MESSAGE_ID\r
96         STATUS_NULL_POINTER\r
97         STATUS_TAG_TOO_LONG\r
98 -       STATUS_UNBALANCED_FREEZE_THAW\r
99         STATUS_UNBALANCED_ATOMIC\r
100  \r
101         STATUS_LAST_STATUS\r
102 @@ -926,7 +925,7 @@ func (self *Message) RemoveTag(tag string) Status {\r
103  \r
104  /* Remove all tags from the given message.\r
105   *\r
106 - * See notmuch_message_freeze for an example showing how to safely\r
107 + * See notmuch_message_sync for an example showing how to safely\r
108   * replace tag values.\r
109   *\r
110   * NOTMUCH_STATUS_READ_ONLY_DATABASE: Database was opened in read-only\r
111 @@ -939,79 +938,39 @@ func (self *Message) RemoveAllTags() Status {\r
112         return Status(C.notmuch_message_remove_all_tags(self.message))\r
113  }\r
114  \r
115 -/* Freeze the current state of 'message' within the database.\r
116 +/* Synchronize the current state of 'message' into the database.\r
117   *\r
118 - * This means that changes to the message state, (via\r
119 + * This will commit any changes made to the message state, (via\r
120   * notmuch_message_add_tag, notmuch_message_remove_tag, and\r
121 - * notmuch_message_remove_all_tags), will not be committed to the\r
122 - * database until the message is thawed with notmuch_message_thaw.\r
123 + * notmuch_message_remove_all_tags), to the database.\r
124   *\r
125 - * Multiple calls to freeze/thaw are valid and these calls will\r
126 - * "stack". That is there must be as many calls to thaw as to freeze\r
127 - * before a message is actually thawed.\r
128 - *\r
129 - * The ability to do freeze/thaw allows for safe transactions to\r
130 - * change tag values. For example, explicitly setting a message to\r
131 - * have a given set of tags might look like this:\r
132 - *\r
133 - *    notmuch_message_freeze (message);\r
134 + * If this method succeeds, the message in the database is guaranteed to\r
135 + * have the full set of changes made to the message committed to the\r
136 + * database. For example, explicitly setting a message to have a given\r
137 + * set of tags might look like this:\r
138   *\r
139   *    notmuch_message_remove_all_tags (message);\r
140   *\r
141   *    for (i = 0; i < NUM_TAGS; i++)\r
142   *        notmuch_message_add_tag (message, tags[i]);\r
143   *\r
144 - *    notmuch_message_thaw (message);\r
145 + *    notmuch_message_sync (message);\r
146   *\r
147 - * With freeze/thaw used like this, the message in the database is\r
148 - * guaranteed to have either the full set of original tag values, or\r
149 - * the full set of new tag values, but nothing in between.\r
150 - *\r
151 - * Imagine the example above without freeze/thaw and the operation\r
152 - * somehow getting interrupted. This could result in the message being\r
153 - * left with no tags if the interruption happened after\r
154 - * notmuch_message_remove_all_tags but before notmuch_message_add_tag.\r
155 + * This method only works if the database associated with 'message' was\r
156 + * opened in read-write mode.\r
157   *\r
158   * Return value:\r
159   *\r
160 - * NOTMUCH_STATUS_SUCCESS: Message successfully frozen.\r
161 + * NOTMUCH_STATUS_SUCCESS: Message successfully synchronized.\r
162   *\r
163   * NOTMUCH_STATUS_READ_ONLY_DATABASE: Database was opened in read-only\r
164   *     mode so message cannot be modified.\r
165   */\r
166 -func (self *Message) Freeze() Status {\r
167 -       if self.message == nil {\r
168 -               return STATUS_NULL_POINTER\r
169 -       }\r
170 -       return Status(C.notmuch_message_freeze(self.message))\r
171 -}\r
172 -\r
173 -/* Thaw the current 'message', synchronizing any changes that may have\r
174 - * occurred while 'message' was frozen into the notmuch database.\r
175 - *\r
176 - * See notmuch_message_freeze for an example of how to use this\r
177 - * function to safely provide tag changes.\r
178 - *\r
179 - * Multiple calls to freeze/thaw are valid and these calls with\r
180 - * "stack". That is there must be as many calls to thaw as to freeze\r
181 - * before a message is actually thawed.\r
182 - *\r
183 - * Return value:\r
184 - *\r
185 - * NOTMUCH_STATUS_SUCCESS: Message successfully thawed, (or at least\r
186 - *     its frozen count has successfully been reduced by 1).\r
187 - *\r
188 - * NOTMUCH_STATUS_UNBALANCED_FREEZE_THAW: An attempt was made to thaw\r
189 - *     an unfrozen message. That is, there have been an unbalanced\r
190 - *     number of calls to notmuch_message_freeze and\r
191 - *     notmuch_message_thaw.\r
192 - */\r
193 -func (self *Message) Thaw() Status {\r
194 +func (self *Message) Sync() Status {\r
195         if self.message == nil {\r
196                 return STATUS_NULL_POINTER\r
197         }\r
198 -\r
199 -       return Status(C.notmuch_message_thaw(self.message))\r
200 +       return Status(C.notmuch_message_sync(self.message))\r
201  }\r
202  \r
203  /* Destroy a notmuch_message_t object.\r
204 diff --git a/bindings/python/docs/source/message.rst b/bindings/python/docs/source/message.rst\r
205 index 1a6cc3d..d3ada71 100644\r
206 --- a/bindings/python/docs/source/message.rst\r
207 +++ b/bindings/python/docs/source/message.rst\r
208 @@ -43,8 +43,6 @@\r
209  \r
210     .. automethod:: remove_all_tags\r
211  \r
212 -   .. automethod:: freeze\r
213 -\r
214 -   .. automethod:: thaw\r
215 +   .. automethod:: sync\r
216  \r
217     .. automethod:: __str__\r
218 diff --git a/bindings/python/notmuch/__init__.py b/bindings/python/notmuch/__init__.py\r
219 index 5561624..edd7cbd 100644\r
220 --- a/bindings/python/notmuch/__init__.py\r
221 +++ b/bindings/python/notmuch/__init__.py\r
222 @@ -72,7 +72,6 @@ from .errors import (\r
223      DuplicateMessageIdError,\r
224      NullPointerError,\r
225      TagTooLongError,\r
226 -    UnbalancedFreezeThawError,\r
227      UnbalancedAtomicError,\r
228      NotInitializedError,\r
229  )\r
230 diff --git a/bindings/python/notmuch/errors.py b/bindings/python/notmuch/errors.py\r
231 index f153a9c..cb748d6 100644\r
232 --- a/bindings/python/notmuch/errors.py\r
233 +++ b/bindings/python/notmuch/errors.py\r
234 @@ -54,7 +54,6 @@ STATUS = Status(['SUCCESS',\r
235    'DUPLICATE_MESSAGE_ID',\r
236    'NULL_POINTER',\r
237    'TAG_TOO_LONG',\r
238 -  'UNBALANCED_FREEZE_THAW',\r
239    'UNBALANCED_ATOMIC',\r
240    'NOT_INITIALIZED'])\r
241  """STATUS is a class, whose attributes provide constants that serve as return\r
242 @@ -71,7 +70,6 @@ description.\r
243    * DUPLICATE_MESSAGE_ID\r
244    * NULL_POINTER\r
245    * TAG_TOO_LONG\r
246 -  * UNBALANCED_FREEZE_THAW\r
247    * UNBALANCED_ATOMIC\r
248    * NOT_INITIALIZED\r
249  \r
250 @@ -99,7 +97,6 @@ class NotmuchError(Exception, Python3StringMixIn):\r
251              STATUS.DUPLICATE_MESSAGE_ID: DuplicateMessageIdError,\r
252              STATUS.NULL_POINTER: NullPointerError,\r
253              STATUS.TAG_TOO_LONG: TagTooLongError,\r
254 -            STATUS.UNBALANCED_FREEZE_THAW: UnbalancedFreezeThawError,\r
255              STATUS.UNBALANCED_ATOMIC: UnbalancedAtomicError,\r
256              STATUS.NOT_INITIALIZED: NotInitializedError,\r
257          }\r
258 @@ -167,10 +164,6 @@ class TagTooLongError(NotmuchError):\r
259      status = STATUS.TAG_TOO_LONG\r
260  \r
261  \r
262 -class UnbalancedFreezeThawError(NotmuchError):\r
263 -    status = STATUS.UNBALANCED_FREEZE_THAW\r
264 -\r
265 -\r
266  class UnbalancedAtomicError(NotmuchError):\r
267      status = STATUS.UNBALANCED_ATOMIC\r
268  \r
269 diff --git a/bindings/python/notmuch/message.py b/bindings/python/notmuch/message.py\r
270 index d1c1b58..600c01e 100644\r
271 --- a/bindings/python/notmuch/message.py\r
272 +++ b/bindings/python/notmuch/message.py\r
273 @@ -308,7 +308,7 @@ class Message(Python3StringMixIn):\r
274      _add_tag.argtypes = [NotmuchMessageP, c_char_p]\r
275      _add_tag.restype = c_uint\r
276  \r
277 -    def add_tag(self, tag, sync_maildir_flags=False):\r
278 +    def add_tag(self, tag):\r
279          """Adds a tag to the given message\r
280  \r
281          Adds a tag to the current message. The maximal tag length is defined in\r
282 @@ -316,14 +316,6 @@ class Message(Python3StringMixIn):\r
283  \r
284          :param tag: String with a 'tag' to be added.\r
285  \r
286 -        :param sync_maildir_flags: If notmuch configuration is set to do\r
287 -            this, add maildir flags corresponding to notmuch tags. See\r
288 -            underlying method :meth:`tags_to_maildir_flags`. Use False\r
289 -            if you want to add/remove many tags on a message without\r
290 -            having to physically rename the file every time. Do note,\r
291 -            that this will do nothing when a message is frozen, as tag\r
292 -            changes will not be committed to the database yet.\r
293 -\r
294          :returns: STATUS.SUCCESS if the tag was successfully added.\r
295                    Raises an exception otherwise.\r
296          :raises: :exc:`NullPointerError` if the `tag` argument is NULL\r
297 @@ -342,29 +334,19 @@ class Message(Python3StringMixIn):\r
298          # bail out on failure\r
299          if status != STATUS.SUCCESS:\r
300              raise NotmuchError(status)\r
301 -\r
302 -        if sync_maildir_flags:\r
303 -            self.tags_to_maildir_flags()\r
304          return STATUS.SUCCESS\r
305  \r
306      _remove_tag = nmlib.notmuch_message_remove_tag\r
307      _remove_tag.argtypes = [NotmuchMessageP, c_char_p]\r
308      _remove_tag.restype = c_uint\r
309  \r
310 -    def remove_tag(self, tag, sync_maildir_flags=False):\r
311 +    def remove_tag(self, tag):\r
312          """Removes a tag from the given message\r
313  \r
314          If the message has no such tag, this is a non-operation and\r
315          will report success anyway.\r
316  \r
317          :param tag: String with a 'tag' to be removed.\r
318 -        :param sync_maildir_flags: If notmuch configuration is set to do\r
319 -            this, add maildir flags corresponding to notmuch tags. See\r
320 -            underlying method :meth:`tags_to_maildir_flags`. Use False\r
321 -            if you want to add/remove many tags on a message without\r
322 -            having to physically rename the file every time. Do note,\r
323 -            that this will do nothing when a message is frozen, as tag\r
324 -            changes will not be committed to the database yet.\r
325  \r
326          :returns: STATUS.SUCCESS if the tag was successfully removed or if\r
327                    the message had no such tag.\r
328 @@ -385,29 +367,18 @@ class Message(Python3StringMixIn):\r
329          if status != STATUS.SUCCESS:\r
330              raise NotmuchError(status)\r
331  \r
332 -        if sync_maildir_flags:\r
333 -            self.tags_to_maildir_flags()\r
334          return STATUS.SUCCESS\r
335  \r
336      _remove_all_tags = nmlib.notmuch_message_remove_all_tags\r
337      _remove_all_tags.argtypes = [NotmuchMessageP]\r
338      _remove_all_tags.restype = c_uint\r
339  \r
340 -    def remove_all_tags(self, sync_maildir_flags=False):\r
341 +    def remove_all_tags(self):\r
342          """Removes all tags from the given message.\r
343  \r
344 -        See :meth:`freeze` for an example showing how to safely\r
345 +        See :meth:`sync` for an example showing how to safely\r
346          replace tag values.\r
347  \r
348 -\r
349 -        :param sync_maildir_flags: If notmuch configuration is set to do\r
350 -            this, add maildir flags corresponding to notmuch tags. See\r
351 -            :meth:`tags_to_maildir_flags`. Use False if you want to\r
352 -            add/remove many tags on a message without having to\r
353 -            physically rename the file every time. Do note, that this\r
354 -            will do nothing when a message is frozen, as tag changes\r
355 -            will not be committed to the database yet.\r
356 -\r
357          :returns: STATUS.SUCCESS if the tags were successfully removed.\r
358                    Raises an exception otherwise.\r
359          :raises: :exc:`ReadOnlyDatabaseError` if the database was opened\r
360 @@ -424,46 +395,40 @@ class Message(Python3StringMixIn):\r
361          if status != STATUS.SUCCESS:\r
362              raise NotmuchError(status)\r
363  \r
364 -        if sync_maildir_flags:\r
365 -            self.tags_to_maildir_flags()\r
366          return STATUS.SUCCESS\r
367  \r
368 -    _freeze = nmlib.notmuch_message_freeze\r
369 -    _freeze.argtypes = [NotmuchMessageP]\r
370 -    _freeze.restype = c_uint\r
371 -\r
372 -    def freeze(self):\r
373 -        """Freezes the current state of 'message' within the database\r
374 +    _sync = nmlib.notmuch_message_sync\r
375 +    _sync.argtypes = [NotmuchMessageP]\r
376 +    _sync.restype = c_uint\r
377  \r
378 -        This means that changes to the message state, (via :meth:`add_tag`,\r
379 -        :meth:`remove_tag`, and :meth:`remove_all_tags`), will not be\r
380 -        committed to the database until the message is :meth:`thaw` ed.\r
381 +    def sync(self, sync_maildir_flags=False):\r
382 +        """Synchronize the current state of 'message' into the database.\r
383  \r
384 -        Multiple calls to freeze/thaw are valid and these calls will\r
385 -        "stack". That is there must be as many calls to thaw as to freeze\r
386 -        before a message is actually thawed.\r
387 +        This will commit any changes made to the message state, (via\r
388 +        :meth:`add_tag`, :meth:`remove_tag`, and :meth:`remove_all_tags`), to\r
389 +        the database.\r
390  \r
391 -        The ability to do freeze/thaw allows for safe transactions to\r
392 -        change tag values. For example, explicitly setting a message to\r
393 -        have a given set of tags might look like this::\r
394 +        If this method succeeds, the message in the database is guaranteed to\r
395 +        have the full set of changes made to the message committed to the\r
396 +        database. For example, explicitly setting a message to have a given set\r
397 +        of tags might look like this:\r
398  \r
399 -          msg.freeze()\r
400            msg.remove_all_tags(False)\r
401            for tag in new_tags:\r
402                msg.add_tag(tag, False)\r
403 -          msg.thaw()\r
404 +          msg.sync()\r
405            msg.tags_to_maildir_flags()\r
406  \r
407 -        With freeze/thaw used like this, the message in the database is\r
408 -        guaranteed to have either the full set of original tag values, or\r
409 -        the full set of new tag values, but nothing in between.\r
410 +        This method only works if the database associated with 'message' was\r
411 +        opened with NOTMUCH_DATABASE_MODE_READ_WRITE.\r
412  \r
413 -        Imagine the example above without freeze/thaw and the operation\r
414 -        somehow getting interrupted. This could result in the message being\r
415 -        left with no tags if the interruption happened after\r
416 -        :meth:`remove_all_tags` but before :meth:`add_tag`.\r
417 +        :param sync_maildir_flags: If notmuch configuration is set to do\r
418 +            this, add maildir flags corresponding to notmuch tags. See\r
419 +            :meth:`tags_to_maildir_flags`. Use False if you want to\r
420 +            add/remove many tags on a message without having to\r
421 +            physically rename the file every time.\r
422  \r
423 -        :returns: STATUS.SUCCESS if the message was successfully frozen.\r
424 +        :returns: STATUS.SUCCESS if the message was successfully synchronized.\r
425                    Raises an exception otherwise.\r
426          :raises: :exc:`ReadOnlyDatabaseError` if the database was opened\r
427                   in read-only mode so message cannot be modified\r
428 @@ -473,50 +438,14 @@ class Message(Python3StringMixIn):\r
429          if not self._msg:\r
430              raise NotInitializedError()\r
431  \r
432 -        status = self._freeze(self._msg)\r
433 -\r
434 -        if STATUS.SUCCESS == status:\r
435 -            # return on success\r
436 -            return status\r
437 -\r
438 -        raise NotmuchError(status)\r
439 -\r
440 -    _thaw = nmlib.notmuch_message_thaw\r
441 -    _thaw.argtypes = [NotmuchMessageP]\r
442 -    _thaw.restype = c_uint\r
443 -\r
444 -    def thaw(self):\r
445 -        """Thaws the current 'message'\r
446 +        status = self._sync(self._msg)\r
447  \r
448 -        Thaw the current 'message', synchronizing any changes that may have\r
449 -        occurred while 'message' was frozen into the notmuch database.\r
450 -\r
451 -        See :meth:`freeze` for an example of how to use this\r
452 -        function to safely provide tag changes.\r
453 -\r
454 -        Multiple calls to freeze/thaw are valid and these calls with\r
455 -        "stack". That is there must be as many calls to thaw as to freeze\r
456 -        before a message is actually thawed.\r
457 -\r
458 -        :returns: STATUS.SUCCESS if the message was successfully frozen.\r
459 -                  Raises an exception otherwise.\r
460 -        :raises: :exc:`UnbalancedFreezeThawError` if an attempt was made\r
461 -                 to thaw an unfrozen message. That is, there have been\r
462 -                 an unbalanced number of calls to :meth:`freeze` and\r
463 -                 :meth:`thaw`.\r
464 -        :raises: :exc:`NotInitializedError` if message has not been\r
465 -                 initialized\r
466 -        """\r
467 -        if not self._msg:\r
468 -            raise NotInitializedError()\r
469 -\r
470 -        status = self._thaw(self._msg)\r
471 -\r
472 -        if STATUS.SUCCESS == status:\r
473 -            # return on success\r
474 -            return status\r
475 +        if status != STATUS.SUCCESS:\r
476 +            raise NotmuchError(status)\r
477  \r
478 -        raise NotmuchError(status)\r
479 +        if sync_maildir_flags:\r
480 +            self.tags_to_maildir_flags()\r
481 +        raise STATUS.SUCCESS\r
482  \r
483      def is_match(self):\r
484          """(Not implemented)"""\r
485 @@ -537,9 +466,9 @@ class Message(Python3StringMixIn):\r
486          Also, if this filename is in a directory named "new", rename it\r
487          to be within the neighboring directory named "cur".\r
488  \r
489 -        Do note that calling this method while a message is frozen might\r
490 -        not work yet, as the modified tags have not been committed yet\r
491 -        to the database.\r
492 +        Do note that calling this method before :meth:`sync` is called\r
493 +        might not work yet, as the modified tags have not been committed\r
494 +        yet to the database.\r
495  \r
496          :returns: a :class:`STATUS` value. In short, you want to see\r
497              notmuch.STATUS.SUCCESS here. See there for details."""\r
498 diff --git a/bindings/ruby/defs.h b/bindings/ruby/defs.h\r
499 index fe81b3f..de36936 100644\r
500 --- a/bindings/ruby/defs.h\r
501 +++ b/bindings/ruby/defs.h\r
502 @@ -43,7 +43,6 @@ extern VALUE notmuch_rb_eFileError;\r
503  extern VALUE notmuch_rb_eFileNotEmailError;\r
504  extern VALUE notmuch_rb_eNullPointerError;\r
505  extern VALUE notmuch_rb_eTagTooLongError;\r
506 -extern VALUE notmuch_rb_eUnbalancedFreezeThawError;\r
507  extern VALUE notmuch_rb_eUnbalancedAtomicError;\r
508  \r
509  extern ID ID_call;\r
510 @@ -329,10 +328,7 @@ VALUE\r
511  notmuch_rb_message_tags_to_maildir_flags (VALUE self);\r
512  \r
513  VALUE\r
514 -notmuch_rb_message_freeze (VALUE self);\r
515 -\r
516 -VALUE\r
517 -notmuch_rb_message_thaw (VALUE self);\r
518 +notmuch_rb_message_sync (VALUE self);\r
519  \r
520  /* tags.c */\r
521  VALUE\r
522 diff --git a/bindings/ruby/init.c b/bindings/ruby/init.c\r
523 index f4931d3..b7a7456 100644\r
524 --- a/bindings/ruby/init.c\r
525 +++ b/bindings/ruby/init.c\r
526 @@ -39,7 +39,6 @@ VALUE notmuch_rb_eFileError;\r
527  VALUE notmuch_rb_eFileNotEmailError;\r
528  VALUE notmuch_rb_eNullPointerError;\r
529  VALUE notmuch_rb_eTagTooLongError;\r
530 -VALUE notmuch_rb_eUnbalancedFreezeThawError;\r
531  VALUE notmuch_rb_eUnbalancedAtomicError;\r
532  \r
533  ID ID_call;\r
534 @@ -191,14 +190,6 @@ Init_notmuch (void)\r
535       */\r
536      notmuch_rb_eTagTooLongError = rb_define_class_under (mod, "TagTooLongError", notmuch_rb_eBaseError);\r
537      /*\r
538 -     * Document-class: Notmuch::UnbalancedFreezeThawError\r
539 -     *\r
540 -     * Raised when the notmuch_message_thaw function has been called more times\r
541 -     * than notmuch_message_freeze.\r
542 -     */\r
543 -    notmuch_rb_eUnbalancedFreezeThawError = rb_define_class_under (mod, "UnbalancedFreezeThawError",\r
544 -                                                                  notmuch_rb_eBaseError);\r
545 -    /*\r
546       * Document-class: Notmuch::UnbalancedAtomicError\r
547       *\r
548       * Raised when notmuch_database_end_atomic has been called more times than\r
549 @@ -338,8 +329,7 @@ Init_notmuch (void)\r
550      rb_define_method (notmuch_rb_cMessage, "remove_all_tags", notmuch_rb_message_remove_all_tags, 0); /* in message.c */\r
551      rb_define_method (notmuch_rb_cMessage, "maildir_flags_to_tags", notmuch_rb_message_maildir_flags_to_tags, 0); /* in message.c */\r
552      rb_define_method (notmuch_rb_cMessage, "tags_to_maildir_flags", notmuch_rb_message_tags_to_maildir_flags, 0); /* in message.c */\r
553 -    rb_define_method (notmuch_rb_cMessage, "freeze", notmuch_rb_message_freeze, 0); /* in message.c */\r
554 -    rb_define_method (notmuch_rb_cMessage, "thaw", notmuch_rb_message_thaw, 0); /* in message.c */\r
555 +    rb_define_method (notmuch_rb_cMessage, "sync", notmuch_rb_message_sync, 0); /* in message.c */\r
556  \r
557      /*\r
558       * Document-class: Notmuch::Tags\r
559 diff --git a/bindings/ruby/message.c b/bindings/ruby/message.c\r
560 index eed4b31..719ca16 100644\r
561 --- a/bindings/ruby/message.c\r
562 +++ b/bindings/ruby/message.c\r
563 @@ -328,38 +328,19 @@ notmuch_rb_message_tags_to_maildir_flags (VALUE self)\r
564  }\r
565  \r
566  /*\r
567 - * call-seq: MESSAGE.freeze => true\r
568 + * call-seq: MESSAGE.sync => true\r
569   *\r
570 - * Freeze the 'message'\r
571 + * Sync the 'message' to the database\r
572   */\r
573  VALUE\r
574 -notmuch_rb_message_freeze (VALUE self)\r
575 +notmuch_rb_message_sync (VALUE self)\r
576  {\r
577      notmuch_status_t ret;\r
578      notmuch_message_t *message;\r
579  \r
580      Data_Get_Notmuch_Message (self, message);\r
581  \r
582 -    ret = notmuch_message_freeze (message);\r
583 -    notmuch_rb_status_raise (ret);\r
584 -\r
585 -    return Qtrue;\r
586 -}\r
587 -\r
588 -/*\r
589 - * call-seq: MESSAGE.thaw => true\r
590 - *\r
591 - * Thaw a 'message'\r
592 - */\r
593 -VALUE\r
594 -notmuch_rb_message_thaw (VALUE self)\r
595 -{\r
596 -    notmuch_status_t ret;\r
597 -    notmuch_message_t *message;\r
598 -\r
599 -    Data_Get_Notmuch_Message (self, message);\r
600 -\r
601 -    ret = notmuch_message_thaw (message);\r
602 +    ret = notmuch_message_sync (message);\r
603      notmuch_rb_status_raise (ret);\r
604  \r
605      return Qtrue;\r
606 diff --git a/bindings/ruby/status.c b/bindings/ruby/status.c\r
607 index b11fb9f..8d1390a 100644\r
608 --- a/bindings/ruby/status.c\r
609 +++ b/bindings/ruby/status.c\r
610 @@ -41,8 +41,6 @@ notmuch_rb_status_raise (notmuch_status_t status)\r
611         rb_raise (notmuch_rb_eNullPointerError, "null pointer");\r
612      case NOTMUCH_STATUS_TAG_TOO_LONG:\r
613         rb_raise (notmuch_rb_eTagTooLongError, "tag too long");\r
614 -    case NOTMUCH_STATUS_UNBALANCED_FREEZE_THAW:\r
615 -       rb_raise (notmuch_rb_eUnbalancedFreezeThawError, "unbalanced freeze/thaw");\r
616      case NOTMUCH_STATUS_UNBALANCED_ATOMIC:\r
617         rb_raise (notmuch_rb_eUnbalancedAtomicError, "unbalanced atomic");\r
618      default:\r
619 diff --git a/contrib/notmuch-deliver/src/main.c b/contrib/notmuch-deliver/src/main.c\r
620 index 032b9d6..afe84ff 100644\r
621 --- a/contrib/notmuch-deliver/src/main.c\r
622 +++ b/contrib/notmuch-deliver/src/main.c\r
623 @@ -300,6 +300,11 @@ add_tags(notmuch_message_t *message, char **tags)\r
624                                 tags[i], notmuch_status_to_string(ret));\r
625         }\r
626  \r
627 +       ret = notmuch_message_sync(message);\r
628 +       if (ret != NOTMUCH_STATUS_SUCCESS)\r
629 +               g_warning("Failed to sync changes to database: %s",\r
630 +                       notmuch_status_to_string(ret));\r
631 +\r
632         return i;\r
633  }\r
634  \r
635 @@ -319,6 +324,11 @@ rm_tags(notmuch_message_t *message, char **tags)\r
636                                 tags[i], notmuch_status_to_string(ret));\r
637         }\r
638  \r
639 +       ret = notmuch_message_sync(message);\r
640 +       if (ret != NOTMUCH_STATUS_SUCCESS)\r
641 +               g_warning("Failed to sync changes to database: %s",\r
642 +                       notmuch_status_to_string(ret));\r
643 +\r
644         return i;\r
645  }\r
646  \r
647 diff --git a/lib/database.cc b/lib/database.cc\r
648 index 91d4329..98ea789 100644\r
649 --- a/lib/database.cc\r
650 +++ b/lib/database.cc\r
651 @@ -264,8 +264,6 @@ notmuch_status_to_string (notmuch_status_t status)\r
652         return "Erroneous NULL pointer";\r
653      case NOTMUCH_STATUS_TAG_TOO_LONG:\r
654         return "Tag value is too long (exceeds NOTMUCH_TAG_MAX)";\r
655 -    case NOTMUCH_STATUS_UNBALANCED_FREEZE_THAW:\r
656 -       return "Unbalanced number of calls to notmuch_message_freeze/thaw";\r
657      case NOTMUCH_STATUS_UNBALANCED_ATOMIC:\r
658         return "Unbalanced number of calls to notmuch_database_begin_atomic/end_atomic";\r
659      default:\r
660 @@ -917,7 +915,7 @@ notmuch_database_upgrade (notmuch_database_t *notmuch,\r
661             filename = _notmuch_message_talloc_copy_data (message);\r
662             if (filename && *filename != '\0') {\r
663                 _notmuch_message_add_filename (message, filename);\r
664 -               _notmuch_message_sync (message);\r
665 +               notmuch_message_sync (message);\r
666             }\r
667             talloc_free (filename);\r
668  \r
669 @@ -993,7 +991,7 @@ notmuch_database_upgrade (notmuch_database_t *notmuch,\r
670             filename = _notmuch_message_talloc_copy_data (message);\r
671             if (filename && *filename != '\0') {\r
672                 _notmuch_message_clear_data (message);\r
673 -               _notmuch_message_sync (message);\r
674 +               notmuch_message_sync (message);\r
675             }\r
676             talloc_free (filename);\r
677  \r
678 @@ -1483,7 +1481,7 @@ _merge_threads (notmuch_database_t *notmuch,\r
679  \r
680         _notmuch_message_remove_term (message, "thread", loser_thread_id);\r
681         _notmuch_message_add_term (message, "thread", winner_thread_id);\r
682 -       _notmuch_message_sync (message);\r
683 +       notmuch_message_sync (message);\r
684  \r
685         notmuch_message_destroy (message);\r
686         message = NULL;\r
687 @@ -1601,7 +1599,7 @@ _notmuch_database_link_message_to_children (notmuch_database_t *notmuch,\r
688         } else if (strcmp (*thread_id, child_thread_id)) {\r
689             _notmuch_message_remove_term (child_message, "reference",\r
690                                           message_id);\r
691 -           _notmuch_message_sync (child_message);\r
692 +           notmuch_message_sync (child_message);\r
693             ret = _merge_threads (notmuch, *thread_id, child_thread_id);\r
694             if (ret)\r
695                 goto DONE;\r
696 @@ -1828,7 +1826,7 @@ notmuch_database_add_message (notmuch_database_t *notmuch,\r
697             ret = NOTMUCH_STATUS_DUPLICATE_MESSAGE_ID;\r
698         }\r
699  \r
700 -       _notmuch_message_sync (message);\r
701 +       notmuch_message_sync (message);\r
702      } catch (const Xapian::Error &error) {\r
703         fprintf (stderr, "A Xapian exception occurred adding message: %s.\n",\r
704                  error.get_msg().c_str());\r
705 @@ -1873,7 +1871,7 @@ notmuch_database_remove_message (notmuch_database_t *notmuch,\r
706             if (status == NOTMUCH_STATUS_SUCCESS)\r
707                 _notmuch_message_delete (message);\r
708             else if (status == NOTMUCH_STATUS_DUPLICATE_MESSAGE_ID)\r
709 -               _notmuch_message_sync (message);\r
710 +               notmuch_message_sync (message);\r
711  \r
712             notmuch_message_destroy (message);\r
713      }\r
714 diff --git a/lib/message.cc b/lib/message.cc\r
715 index 320901f..b7e4bbe 100644\r
716 --- a/lib/message.cc\r
717 +++ b/lib/message.cc\r
718 @@ -28,7 +28,6 @@\r
719  struct visible _notmuch_message {\r
720      notmuch_database_t *notmuch;\r
721      Xapian::docid doc_id;\r
722 -    int frozen;\r
723      char *message_id;\r
724      char *thread_id;\r
725      char *in_reply_to;\r
726 @@ -97,7 +96,6 @@ _notmuch_message_create_for_document (const void *talloc_owner,\r
727      message->notmuch = notmuch;\r
728      message->doc_id = doc_id;\r
729  \r
730 -    message->frozen = 0;\r
731      message->flags = 0;\r
732  \r
733      /* Each of these will be lazily created as needed. */\r
734 @@ -199,7 +197,7 @@ _notmuch_message_create (const void *talloc_owner,\r
735   *     returned message contains a newly created document (not yet\r
736   *     added to the database) and a document ID that is known not to\r
737   *     exist in the database. The caller can modify the message, and a\r
738 - *     call to _notmuch_message_sync will add * the document to the\r
739 + *     call to notmuch_message_sync will add the document to the\r
740   *     database.\r
741   *\r
742   * If an error occurs, this function will return NULL and *status\r
743 @@ -476,7 +474,7 @@ notmuch_message_get_replies (notmuch_message_t *message)\r
744  /* Add an additional 'filename' for 'message'.\r
745   *\r
746   * This change will not be reflected in the database until the next\r
747 - * call to _notmuch_message_sync. */\r
748 + * call to notmuch_message_sync. */\r
749  notmuch_status_t\r
750  _notmuch_message_add_filename (notmuch_message_t *message,\r
751                                const char *filename)\r
752 @@ -515,7 +513,7 @@ _notmuch_message_add_filename (notmuch_message_t *message,\r
753  /* Remove a particular 'filename' from 'message'.\r
754   *\r
755   * This change will not be reflected in the database until the next\r
756 - * call to _notmuch_message_sync.\r
757 + * call to notmuch_message_sync.\r
758   *\r
759   * If this message still has other filenames, returns\r
760   * NOTMUCH_STATUS_DUPLICATE_MESSAGE_ID.\r
761 @@ -831,17 +829,22 @@ _notmuch_message_set_header_values (notmuch_message_t *message,\r
762      message->doc.add_value (NOTMUCH_VALUE_SUBJECT, subject);\r
763  }\r
764  \r
765 -/* Synchronize changes made to message->doc out into the database. */\r
766 -void\r
767 -_notmuch_message_sync (notmuch_message_t *message)\r
768 +/* Synchronize changes made to message->doc into the given database. */\r
769 +notmuch_status_t\r
770 +_notmuch_message_sync_to_database (notmuch_message_t *message,\r
771 +                                  notmuch_database_t *notmuch)\r
772  {\r
773 +    notmuch_status_t status;\r
774      Xapian::WritableDatabase *db;\r
775  \r
776 -    if (message->notmuch->mode == NOTMUCH_DATABASE_MODE_READ_ONLY)\r
777 -       return;\r
778 +    status = _notmuch_database_ensure_writable(notmuch);\r
779 +    if (status)\r
780 +       return status;\r
781  \r
782 -    db = static_cast <Xapian::WritableDatabase *> (message->notmuch->xapian_db);\r
783 +    db = static_cast <Xapian::WritableDatabase *> (notmuch->xapian_db);\r
784      db->replace_document (message->doc_id, message->doc);\r
785 +\r
786 +    return NOTMUCH_STATUS_SUCCESS;\r
787  }\r
788  \r
789  /* Delete a message document from the database. */\r
790 @@ -879,7 +882,7 @@ _notmuch_message_close (notmuch_message_t *message)\r
791   * names to prefix values.\r
792   *\r
793   * This change will not be reflected in the database until the next\r
794 - * call to _notmuch_message_sync. */\r
795 + * call to notmuch_message_sync. */\r
796  notmuch_private_status_t\r
797  _notmuch_message_add_term (notmuch_message_t *message,\r
798                            const char *prefix_name,\r
799 @@ -940,7 +943,7 @@ _notmuch_message_gen_terms (notmuch_message_t *message,\r
800   * names to prefix values.\r
801   *\r
802   * This change will not be reflected in the database until the next\r
803 - * call to _notmuch_message_sync. */\r
804 + * call to notmuch_message_sync. */\r
805  notmuch_private_status_t\r
806  _notmuch_message_remove_term (notmuch_message_t *message,\r
807                               const char *prefix_name,\r
808 @@ -977,11 +980,6 @@ notmuch_status_t\r
809  notmuch_message_add_tag (notmuch_message_t *message, const char *tag)\r
810  {\r
811      notmuch_private_status_t private_status;\r
812 -    notmuch_status_t status;\r
813 -\r
814 -    status = _notmuch_database_ensure_writable (message->notmuch);\r
815 -    if (status)\r
816 -       return status;\r
817  \r
818      if (tag == NULL)\r
819         return NOTMUCH_STATUS_NULL_POINTER;\r
820 @@ -995,9 +993,6 @@ notmuch_message_add_tag (notmuch_message_t *message, const char *tag)\r
821                         private_status);\r
822      }\r
823  \r
824 -    if (! message->frozen)\r
825 -       _notmuch_message_sync (message);\r
826 -\r
827      return NOTMUCH_STATUS_SUCCESS;\r
828  }\r
829  \r
830 @@ -1005,11 +1000,6 @@ notmuch_status_t\r
831  notmuch_message_remove_tag (notmuch_message_t *message, const char *tag)\r
832  {\r
833      notmuch_private_status_t private_status;\r
834 -    notmuch_status_t status;\r
835 -\r
836 -    status = _notmuch_database_ensure_writable (message->notmuch);\r
837 -    if (status)\r
838 -       return status;\r
839  \r
840      if (tag == NULL)\r
841         return NOTMUCH_STATUS_NULL_POINTER;\r
842 @@ -1023,9 +1013,6 @@ notmuch_message_remove_tag (notmuch_message_t *message, const char *tag)\r
843                         private_status);\r
844      }\r
845  \r
846 -    if (! message->frozen)\r
847 -       _notmuch_message_sync (message);\r
848 -\r
849      return NOTMUCH_STATUS_SUCCESS;\r
850  }\r
851  \r
852 @@ -1113,10 +1100,6 @@ notmuch_message_maildir_flags_to_tags (notmuch_message_t *message)\r
853      if (! seen_maildir_info)\r
854         return NOTMUCH_STATUS_SUCCESS;\r
855  \r
856 -    status = notmuch_message_freeze (message);\r
857 -    if (status)\r
858 -       return status;\r
859 -\r
860      for (i = 0; i < ARRAY_SIZE(flag2tag); i++) {\r
861         if ((strchr (combined_flags, flag2tag[i].flag) != NULL)\r
862             ^ \r
863 @@ -1129,7 +1112,7 @@ notmuch_message_maildir_flags_to_tags (notmuch_message_t *message)\r
864         if (status)\r
865             return status;\r
866      }\r
867 -    status = notmuch_message_thaw (message);\r
868 +    status = notmuch_message_sync (message);\r
869  \r
870      talloc_free (combined_flags);\r
871  \r
872 @@ -1348,7 +1331,7 @@ notmuch_message_tags_to_maildir_flags (notmuch_message_t *message)\r
873                 continue;\r
874             }\r
875  \r
876 -           _notmuch_message_sync (message);\r
877 +           notmuch_message_sync (message);\r
878         }\r
879  \r
880         talloc_free (filename_new);\r
881 @@ -1364,14 +1347,9 @@ notmuch_status_t\r
882  notmuch_message_remove_all_tags (notmuch_message_t *message)\r
883  {\r
884      notmuch_private_status_t private_status;\r
885 -    notmuch_status_t status;\r
886      notmuch_tags_t *tags;\r
887      const char *tag;\r
888  \r
889 -    status = _notmuch_database_ensure_writable (message->notmuch);\r
890 -    if (status)\r
891 -       return status;\r
892 -\r
893      for (tags = notmuch_message_get_tags (message);\r
894          notmuch_tags_valid (tags);\r
895          notmuch_tags_move_to_next (tags))\r
896 @@ -1385,44 +1363,14 @@ notmuch_message_remove_all_tags (notmuch_message_t *message)\r
897         }\r
898      }\r
899  \r
900 -    if (! message->frozen)\r
901 -       _notmuch_message_sync (message);\r
902 -\r
903      talloc_free (tags);\r
904      return NOTMUCH_STATUS_SUCCESS;\r
905  }\r
906  \r
907  notmuch_status_t\r
908 -notmuch_message_freeze (notmuch_message_t *message)\r
909 +notmuch_message_sync (notmuch_message_t *message)\r
910  {\r
911 -    notmuch_status_t status;\r
912 -\r
913 -    status = _notmuch_database_ensure_writable (message->notmuch);\r
914 -    if (status)\r
915 -       return status;\r
916 -\r
917 -    message->frozen++;\r
918 -\r
919 -    return NOTMUCH_STATUS_SUCCESS;\r
920 -}\r
921 -\r
922 -notmuch_status_t\r
923 -notmuch_message_thaw (notmuch_message_t *message)\r
924 -{\r
925 -    notmuch_status_t status;\r
926 -\r
927 -    status = _notmuch_database_ensure_writable (message->notmuch);\r
928 -    if (status)\r
929 -       return status;\r
930 -\r
931 -    if (message->frozen > 0) {\r
932 -       message->frozen--;\r
933 -       if (message->frozen == 0)\r
934 -           _notmuch_message_sync (message);\r
935 -       return NOTMUCH_STATUS_SUCCESS;\r
936 -    } else {\r
937 -       return NOTMUCH_STATUS_UNBALANCED_FREEZE_THAW;\r
938 -    }\r
939 +    return _notmuch_message_sync_to_database (message, message->notmuch);\r
940  }\r
941  \r
942  void\r
943 diff --git a/lib/notmuch-private.h b/lib/notmuch-private.h\r
944 index 7a409f5..9a43129 100644\r
945 --- a/lib/notmuch-private.h\r
946 +++ b/lib/notmuch-private.h\r
947 @@ -122,7 +122,6 @@ typedef enum _notmuch_private_status {\r
948      NOTMUCH_PRIVATE_STATUS_FILE_NOT_EMAIL = NOTMUCH_STATUS_FILE_NOT_EMAIL,\r
949      NOTMUCH_PRIVATE_STATUS_NULL_POINTER = NOTMUCH_STATUS_NULL_POINTER,\r
950      NOTMUCH_PRIVATE_STATUS_TAG_TOO_LONG = NOTMUCH_STATUS_TAG_TOO_LONG,\r
951 -    NOTMUCH_PRIVATE_STATUS_UNBALANCED_FREEZE_THAW = NOTMUCH_STATUS_UNBALANCED_FREEZE_THAW,\r
952  \r
953      /* Then add our own private values. */\r
954      NOTMUCH_PRIVATE_STATUS_TERM_TOO_LONG = NOTMUCH_STATUS_LAST_STATUS,\r
955 @@ -295,8 +294,10 @@ _notmuch_message_set_header_values (notmuch_message_t *message,\r
956                                     const char *date,\r
957                                     const char *from,\r
958                                     const char *subject);\r
959 -void\r
960 -_notmuch_message_sync (notmuch_message_t *message);\r
961 +\r
962 +notmuch_status_t\r
963 +_notmuch_message_sync_to_database (notmuch_message_t *message,\r
964 +                                  notmuch_database_t *notmuch);\r
965  \r
966  notmuch_status_t\r
967  _notmuch_message_delete (notmuch_message_t *message);\r
968 diff --git a/lib/notmuch.h b/lib/notmuch.h\r
969 index 3633bed..f6962ee 100644\r
970 --- a/lib/notmuch.h\r
971 +++ b/lib/notmuch.h\r
972 @@ -78,9 +78,6 @@ typedef int notmuch_bool_t;\r
973   * NOTMUCH_STATUS_TAG_TOO_LONG: A tag value is too long (exceeds\r
974   *     NOTMUCH_TAG_MAX)\r
975   *\r
976 - * NOTMUCH_STATUS_UNBALANCED_FREEZE_THAW: The notmuch_message_thaw\r
977 - *     function has been called more times than notmuch_message_freeze.\r
978 - *\r
979   * NOTMUCH_STATUS_UNBALANCED_ATOMIC: notmuch_database_end_atomic has\r
980   *     been called more times than notmuch_database_begin_atomic.\r
981   *\r
982 @@ -99,7 +96,6 @@ typedef enum _notmuch_status {\r
983      NOTMUCH_STATUS_DUPLICATE_MESSAGE_ID,\r
984      NOTMUCH_STATUS_NULL_POINTER,\r
985      NOTMUCH_STATUS_TAG_TOO_LONG,\r
986 -    NOTMUCH_STATUS_UNBALANCED_FREEZE_THAW,\r
987      NOTMUCH_STATUS_UNBALANCED_ATOMIC,\r
988  \r
989      NOTMUCH_STATUS_LAST_STATUS\r
990 @@ -1043,6 +1039,9 @@ notmuch_message_get_tags (notmuch_message_t *message);\r
991  \r
992  /* Add a tag to the given message.\r
993   *\r
994 + * The changes to the message will not be committed into the database\r
995 + * until notmuch_message_sync is called.\r
996 + *\r
997   * Return value:\r
998   *\r
999   * NOTMUCH_STATUS_SUCCESS: Tag successfully added to message\r
1000 @@ -1051,15 +1050,15 @@ notmuch_message_get_tags (notmuch_message_t *message);\r
1001   *\r
1002   * NOTMUCH_STATUS_TAG_TOO_LONG: The length of 'tag' is too long\r
1003   *     (exceeds NOTMUCH_TAG_MAX)\r
1004 - *\r
1005 - * NOTMUCH_STATUS_READ_ONLY_DATABASE: Database was opened in read-only\r
1006 - *     mode so message cannot be modified.\r
1007   */\r
1008  notmuch_status_t\r
1009  notmuch_message_add_tag (notmuch_message_t *message, const char *tag);\r
1010  \r
1011  /* Remove a tag from the given message.\r
1012   *\r
1013 + * The changes to the message will not be committed into the database\r
1014 + * until notmuch_message_sync is called.\r
1015 + *\r
1016   * Return value:\r
1017   *\r
1018   * NOTMUCH_STATUS_SUCCESS: Tag successfully removed from message\r
1019 @@ -1068,17 +1067,14 @@ notmuch_message_add_tag (notmuch_message_t *message, const char *tag);\r
1020   *\r
1021   * NOTMUCH_STATUS_TAG_TOO_LONG: The length of 'tag' is too long\r
1022   *     (exceeds NOTMUCH_TAG_MAX)\r
1023 - *\r
1024 - * NOTMUCH_STATUS_READ_ONLY_DATABASE: Database was opened in read-only\r
1025 - *     mode so message cannot be modified.\r
1026   */\r
1027  notmuch_status_t\r
1028  notmuch_message_remove_tag (notmuch_message_t *message, const char *tag);\r
1029  \r
1030  /* Remove all tags from the given message.\r
1031   *\r
1032 - * See notmuch_message_freeze for an example showing how to safely\r
1033 - * replace tag values.\r
1034 + * See notmuch_message_sync for an example showing how to safely replace\r
1035 + * tag values.\r
1036   *\r
1037   * NOTMUCH_STATUS_READ_ONLY_DATABASE: Database was opened in read-only\r
1038   *     mode so message cannot be modified.\r
1039 @@ -1148,79 +1144,44 @@ notmuch_message_maildir_flags_to_tags (notmuch_message_t *message);\r
1040   *\r
1041   * A client can ensure that maildir filename flags remain synchronized\r
1042   * with notmuch database tags by calling this function after changing\r
1043 - * tags, (after calls to notmuch_message_add_tag,\r
1044 - * notmuch_message_remove_tag, or notmuch_message_freeze/\r
1045 - * notmuch_message_thaw). See also notmuch_message_maildir_flags_to_tags\r
1046 - * for synchronizing maildir flag changes back to tags.\r
1047 + * tags, (after calls to notmuch_message_sync).\r
1048 + *\r
1049 + * See also notmuch_message_maildir_flags_to_tags for synchronizing\r
1050 + * maildir flag changes back to tags.\r
1051   */\r
1052  notmuch_status_t\r
1053  notmuch_message_tags_to_maildir_flags (notmuch_message_t *message);\r
1054  \r
1055 -/* Freeze the current state of 'message' within the database.\r
1056 +/* Synchronize the current state of 'message' into the database.\r
1057   *\r
1058 - * This means that changes to the message state, (via\r
1059 + * This will commit any changes made to the message state, (via\r
1060   * notmuch_message_add_tag, notmuch_message_remove_tag, and\r
1061 - * notmuch_message_remove_all_tags), will not be committed to the\r
1062 - * database until the message is thawed with notmuch_message_thaw.\r
1063 - *\r
1064 - * Multiple calls to freeze/thaw are valid and these calls will\r
1065 - * "stack". That is there must be as many calls to thaw as to freeze\r
1066 - * before a message is actually thawed.\r
1067 + * notmuch_message_remove_all_tags), to the database.\r
1068   *\r
1069 - * The ability to do freeze/thaw allows for safe transactions to\r
1070 - * change tag values. For example, explicitly setting a message to\r
1071 - * have a given set of tags might look like this:\r
1072 - *\r
1073 - *    notmuch_message_freeze (message);\r
1074 + * If this method succeeds, the message in the database is guaranteed to\r
1075 + * have the full set of changes made to the message committed to the\r
1076 + * database. For example, explicitly setting a message to have a given\r
1077 + * set of tags might look like this:\r
1078   *\r
1079   *    notmuch_message_remove_all_tags (message);\r
1080   *\r
1081   *    for (i = 0; i < NUM_TAGS; i++)\r
1082   *        notmuch_message_add_tag (message, tags[i]);\r
1083   *\r
1084 - *    notmuch_message_thaw (message);\r
1085 - *\r
1086 - * With freeze/thaw used like this, the message in the database is\r
1087 - * guaranteed to have either the full set of original tag values, or\r
1088 - * the full set of new tag values, but nothing in between.\r
1089 + *    notmuch_message_sync (message);\r
1090   *\r
1091 - * Imagine the example above without freeze/thaw and the operation\r
1092 - * somehow getting interrupted. This could result in the message being\r
1093 - * left with no tags if the interruption happened after\r
1094 - * notmuch_message_remove_all_tags but before notmuch_message_add_tag.\r
1095 + * This method only works if the database associated with 'message' was\r
1096 + * opened in read-write mode.\r
1097   *\r
1098   * Return value:\r
1099   *\r
1100 - * NOTMUCH_STATUS_SUCCESS: Message successfully frozen.\r
1101 + * NOTMUCH_STATUS_SUCCESS: Message successfully synchronized.\r
1102   *\r
1103   * NOTMUCH_STATUS_READ_ONLY_DATABASE: Database was opened in read-only\r
1104   *     mode so message cannot be modified.\r
1105   */\r
1106  notmuch_status_t\r
1107 -notmuch_message_freeze (notmuch_message_t *message);\r
1108 -\r
1109 -/* Thaw the current 'message', synchronizing any changes that may have\r
1110 - * occurred while 'message' was frozen into the notmuch database.\r
1111 - *\r
1112 - * See notmuch_message_freeze for an example of how to use this\r
1113 - * function to safely provide tag changes.\r
1114 - *\r
1115 - * Multiple calls to freeze/thaw are valid and these calls with\r
1116 - * "stack". That is there must be as many calls to thaw as to freeze\r
1117 - * before a message is actually thawed.\r
1118 - *\r
1119 - * Return value:\r
1120 - *\r
1121 - * NOTMUCH_STATUS_SUCCESS: Message successfully thawed, (or at least\r
1122 - *     its frozen count has successfully been reduced by 1).\r
1123 - *\r
1124 - * NOTMUCH_STATUS_UNBALANCED_FREEZE_THAW: An attempt was made to thaw\r
1125 - *     an unfrozen message. That is, there have been an unbalanced\r
1126 - *     number of calls to notmuch_message_freeze and\r
1127 - *     notmuch_message_thaw.\r
1128 - */\r
1129 -notmuch_status_t\r
1130 -notmuch_message_thaw (notmuch_message_t *message);\r
1131 +notmuch_message_sync (notmuch_message_t *message);\r
1132  \r
1133  /* Destroy a notmuch_message_t object.\r
1134   *\r
1135 diff --git a/notmuch-new.c b/notmuch-new.c\r
1136 index feb9c32..644a809 100644\r
1137 --- a/notmuch-new.c\r
1138 +++ b/notmuch-new.c\r
1139 @@ -509,12 +509,11 @@ add_files (notmuch_database_t *notmuch,\r
1140         /* success */\r
1141         case NOTMUCH_STATUS_SUCCESS:\r
1142             state->added_messages++;\r
1143 -           notmuch_message_freeze (message);\r
1144             for (tag=state->new_tags; *tag != NULL; tag++)\r
1145                 notmuch_message_add_tag (message, *tag);\r
1146             if (state->synchronize_flags == TRUE)\r
1147                 notmuch_message_maildir_flags_to_tags (message);\r
1148 -           notmuch_message_thaw (message);\r
1149 +           notmuch_message_sync (message);\r
1150             break;\r
1151         /* Non-fatal issues (go on to next file) */\r
1152         case NOTMUCH_STATUS_DUPLICATE_MESSAGE_ID:\r
1153 @@ -537,7 +536,6 @@ add_files (notmuch_database_t *notmuch,\r
1154         case NOTMUCH_STATUS_FILE_ERROR:\r
1155         case NOTMUCH_STATUS_NULL_POINTER:\r
1156         case NOTMUCH_STATUS_TAG_TOO_LONG:\r
1157 -       case NOTMUCH_STATUS_UNBALANCED_FREEZE_THAW:\r
1158         case NOTMUCH_STATUS_UNBALANCED_ATOMIC:\r
1159         case NOTMUCH_STATUS_LAST_STATUS:\r
1160             INTERNAL_ERROR ("add_message returned unexpected value: %d",  status);\r
1161 diff --git a/notmuch-tag.c b/notmuch-tag.c\r
1162 index 88d559b..23714c9 100644\r
1163 --- a/notmuch-tag.c\r
1164 +++ b/notmuch-tag.c\r
1165 @@ -145,8 +145,6 @@ tag_query (void *ctx, notmuch_database_t *notmuch, const char *query_string,\r
1166          notmuch_messages_move_to_next (messages)) {\r
1167         message = notmuch_messages_get (messages);\r
1168  \r
1169 -       notmuch_message_freeze (message);\r
1170 -\r
1171         for (i = 0; tag_ops[i].tag; i++) {\r
1172             if (tag_ops[i].remove)\r
1173                 notmuch_message_remove_tag (message, tag_ops[i].tag);\r
1174 @@ -154,7 +152,7 @@ tag_query (void *ctx, notmuch_database_t *notmuch, const char *query_string,\r
1175                 notmuch_message_add_tag (message, tag_ops[i].tag);\r
1176         }\r
1177  \r
1178 -       notmuch_message_thaw (message);\r
1179 +       notmuch_message_sync (message);\r
1180  \r
1181         if (synchronize_flags)\r
1182             notmuch_message_tags_to_maildir_flags (message);\r
1183 diff --git a/tag-util.c b/tag-util.c\r
1184 index eab482f..65db504 100644\r
1185 --- a/tag-util.c\r
1186 +++ b/tag-util.c\r
1187 @@ -228,12 +228,6 @@ tag_op_list_apply (notmuch_message_t *message,\r
1188      if (! (flags & TAG_FLAG_PRE_OPTIMIZED) && ! makes_changes (message, list, flags))\r
1189         return NOTMUCH_STATUS_SUCCESS;\r
1190  \r
1191 -    status = notmuch_message_freeze (message);\r
1192 -    if (status) {\r
1193 -       message_error (message, status, "freezing message");\r
1194 -       return status;\r
1195 -    }\r
1196 -\r
1197      if (flags & TAG_FLAG_REMOVE_ALL) {\r
1198         status = notmuch_message_remove_all_tags (message);\r
1199         if (status) {\r
1200 @@ -259,9 +253,9 @@ tag_op_list_apply (notmuch_message_t *message,\r
1201         }\r
1202      }\r
1203  \r
1204 -    status = notmuch_message_thaw (message);\r
1205 +    status = notmuch_message_sync (message);\r
1206      if (status) {\r
1207 -       message_error (message, status, "thawing message");\r
1208 +       message_error (message, status, "syncing message");\r
1209         return status;\r
1210      }\r
1211  \r
1212 -- \r
1213 1.8.0\r
1214 \r