[Top][All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[O] org-attach and UUID case

From: Zachary Newman
Subject: [O] org-attach and UUID case
Date: Tue, 09 Jul 2019 13:46:43 -0400
User-agent: mu4e 1.2.0; emacs 26.2

*tl;dr*: ~org-id-new~ sometimes generates upcase UUIDs, sometimes downcase.
Combined with case-insensitive filesystems, this can be bad. I have a
workaround, wondering if a fix is appropriate.

It appears that on my MacBook, ~org-id-new~ uses upper-case while on my NixOS
box it uses lowercase (I believe because ~uuidgen~ is not available). This
*should* be fine, but I sync my org files via Dropbox, which will do things
*like: if there's a file ~FOO~ and you add a file ~foo~, it'll (very
*helpfully) rename ~foo~ to ~FOO (Case Conflict)~.
See https://help.dropbox.com/installs-integrations/sync-uploads/case-conflict

This means if we:

1. create an attachment with UUID beginning with e.g. ~ff~
2. create another attachment with UUID beginning with ~FF~ (pretty likely due
   to birthday paradox)
3. wait for Dropbox to rename the latter
4. try to do anything with the latter attachment

org cannot find the attachment directory!

I'd consider this mostly the fault of Dropbox, but in general I'd hope that
org would play nice when syncing across filesystems.

We can fix it pretty simply (thanks, Gustav Wikström, for commit ae9cd437!):

#+BEGIN_SRC emacs-lisp
(setq org-attach-id-to-path-function
  (lambda (id) (upcase (org-attach-id-folder-format id))))

However, if we have some pre-existing directories, we run into trouble (they can
no longer be found!). I patched mine up by hand (because I was doing this from a
case-insensitive filesystem, which made the whole thing a nightmare). See
post-script for an automated way to do it.

Is it worth trying to prevent this? We could:

- make generated UUIDs consistent case (upcase/downcase the output of
- make ~org-attach-id-to-path-function~ upcase/downcase by default

I'd lean towards the latter option.

If we do that, we'd have to handle existing "wrong"-cased
attachment directories for users, but we have a few options for doing so:

- check all cases on disk when computing ~(org-attach-dir)~ and silently use the
  right one
- check all cases and set ~:DIR:~ (with prompt?) for the entry
- check all cases and move the dir to the right case (with prompt)
- provide a one-time migration function

I'd lean towards the second option, with a prompt.

I'm happy to work on it (and sign over the copyright etc.), but would like to
make sure that it's something we actually want to do and the approach is
acceptable before beginning. Or it might be too much of an edge-case to change
things for.


P.S. Something like the following should work if your filesystem is 
(not totally tested):

#+BEGIN_SRC emacs-lisp
(defun get-path-variants (path)
  (let* ((path-parts (split-string path "/"))
         (basedir (mapconcat 'identity (butlast path-parts 2) "/"))
         (part1 (car (last path-parts 2)))
         (part2 (cadr (last path-parts 2))))
    (list (concat basedir "/" (downcase part1) "/" (downcase part2))
          (concat basedir "/" (downcase part1) "/" (upcase   part2))
          (concat basedir "/" (upcase   part1) "/" (downcase part2))
          (concat basedir "/" (upcase   part1) "/" (upcase   part2)))))

(defun normalize-attach-dir ()
  (let* ((attach-dir (org-attach-dir)))
    (mapc (lambda (attach-dir-variant)
            (if (and (file-exists-p attach-dir-variant)
                     (not (string= attach-dir-variant attach-dir)))
                (rename-file attach-dir-variant attach-dir)))
          (get-path-variants -attach-dir))
    (org-set-property "ID" (upcase (org-entry-get (point) "ID" nil)))))

(defun attach-dir-matcher (todo tags-list level)
  (not (eq (org-attach-dir) nil)))

(defun normalize-attach-dirs ()
  (mapc (lambda (file)
          (with-current-buffer (org-get-agenda-file-buffer file)
            (org-scan-tags #'normalize-attach-dir #'attach-dir-matcher nil)))
        (org-agenda-files t)))

reply via email to

[Prev in Thread] Current Thread [Next in Thread]