notmuch: move new maildir files to cur upon opening a folder

By default "notmuch new" will index files in place and won't move new
files from the new/ directory to cur/ because it assumes that is the
job of the email client.
During normal operation, once moved by the client, the "notmuch new"
command will "fix" the database by detecting file renames.
This workflow is a problem because we need to move the new/ files to
cur/, otherwise the maildir lib will not work properly, but at the same
time we cannot afford the notmuch database to be out of sync with the
location of message files on disk, because we rely on it for listing
folders, displaying emails, ect...

This change uses a trick that request notmuch to synchronize message
tags to maildir flags, that will effectively rename new files and cause
them to be moved into the cur/ directory.

Signed-off-by: Julian Pidancet <>
Acked-by: Robin Jarry <>
Acked-by: Tim Culverhouse <>
Julian Pidancet 1 month ago committed by Robin Jarry
parent 81440e79ad
commit f20933f512
  1. 40

@ -281,8 +281,12 @@ func (w *worker) handleOpenDirectory(msg *types.OpenDirectory) error {
q := ""
if != nil {
folders, _ :=
if _, ok := folders[msg.Directory]; ok {
dir, ok := folders[msg.Directory]
if ok {
q = fmt.Sprintf("folder:%s", strconv.Quote(msg.Directory))
if err := w.processNewMaildirFiles(string(dir)); err != nil {
return err
if q == "" {
@ -901,3 +905,37 @@ func (w *worker) handleRemoveDirectory(msg *types.RemoveDirectory) error {
return nil
// This is a hack that calls MsgModifyTags with an empty list of tags to
// apply on new messages causing notmuch to rename files and effectively
// move them into the cur/ dir.
func (w *worker) processNewMaildirFiles(dir string) error {
f, err := os.Open(filepath.Join(dir, "new"))
if err != nil {
return err
defer f.Close()
names, err := f.Readdirnames(0)
if err != nil {
return err
for _, n := range names {
if n[0] == '.' {
key, err := w.db.MsgIDFromFilename(filepath.Join(dir, "new", n))
if err != nil {
// Message is not yet indexed, leave it alone
// Force message to move from new/ to cur/
err = w.db.MsgModifyTags(key, nil, nil)
if err != nil {
logging.Errorf("MsgModifyTags failed: %v", err)
return nil