]> code.delx.au - gnu-emacs/blobdiff - lisp/org/org-attach.el
Update copyright year to 2015
[gnu-emacs] / lisp / org / org-attach.el
index ae97db20f70bc1e7e191baee44f68c4abf93e33b..71e2dbabdb20bc2e235e312d751f9fc2bb51e7fb 100644 (file)
@@ -1,10 +1,9 @@
 ;;; org-attach.el --- Manage file attachments to org-mode tasks
 
-;; Copyright (C) 2008-2011 Free Software Foundation, Inc.
+;; Copyright (C) 2008-2015 Free Software Foundation, Inc.
 
 ;; Author: John Wiegley <johnw@newartisans.com>
 ;; Keywords: org data task
-;; Version: 7.4
 
 ;; This file is part of GNU Emacs.
 ;;
@@ -42,6 +41,7 @@
   (require 'cl))
 (require 'org-id)
 (require 'org)
+(require 'vc-git)
 
 (defgroup org-attach nil
   "Options concerning entry attachments in Org-mode."
@@ -55,6 +55,15 @@ where the Org file lives."
   :group 'org-attach
   :type 'directory)
 
+(defcustom org-attach-git-annex-cutoff (* 32 1024)
+  "If non-nil, files larger than this will be annexed instead of stored."
+  :group 'org-attach
+  :version "24.4"
+  :package-version '(Org . "8.0")
+  :type '(choice
+         (const :tag "None" nil)
+         (integer :tag "Bytes")))
+
 (defcustom org-attach-auto-tag "ATTACH"
   "Tag that will be triggered automatically when an entry has an attachment."
   :group 'org-attach
@@ -79,12 +88,15 @@ Allowed values are:
 mv    rename the file to move it into the attachment directory
 cp    copy the file
 ln    create a hard link.  Note that this is not supported
+      on all systems, and then the result is not defined.
+lns   create a symbol link.  Note that this is not supported
       on all systems, and then the result is not defined."
   :group 'org-attach
   :type '(choice
          (const :tag "Copy" cp)
          (const :tag "Move/Rename" mv)
-         (const :tag "Link" ln)))
+         (const :tag "Hard Link" ln)
+         (const :tag "Symbol Link" lns)))
 
 (defcustom org-attach-expert nil
   "Non-nil means do not show the splash buffer with the attach dispatcher."
@@ -96,10 +108,18 @@ ln    create a hard link.  Note that this is not supported
   :group 'org-attach
   :type 'boolean)
 
-
 (defvar org-attach-inherited nil
   "Indicates if the last access to the attachment directory was inherited.")
 
+(defcustom org-attach-store-link-p nil
+  "Non-nil means store a link to a file when attaching it."
+  :group 'org-attach
+  :version "24.1"
+  :type '(choice
+         (const :tag "Don't store link" nil)
+         (const :tag "Link to origin location" t)
+         (const :tag "Link to the attach-dir location" attached)))
+
 ;;;###autoload
 (defun org-attach ()
   "The dispatcher for attachment commands.
@@ -123,7 +143,7 @@ Shows a list of commands and prompts for another key to execute a command."
              (princ "Select an Attachment Command:
 
 a       Select a file and attach it to the task, using `org-attach-method'.
-c/m/l   Attach a file using copy/move/link method.
+c/m/l/y Attach a file using copy/move/link/symbolic-link method.
 n       Create a new attachment, as an Emacs buffer.
 z       Synchronize the current task with its attachment
         directory, in case you added attachments yourself.
@@ -151,6 +171,8 @@ i       Make children of the current entry inherit its attachment directory.")))
        (let ((org-attach-method 'mv)) (call-interactively 'org-attach-attach)))
        ((memq c '(?l ?\C-l))
        (let ((org-attach-method 'ln)) (call-interactively 'org-attach-attach)))
+       ((memq c '(?y ?\C-y))
+       (let ((org-attach-method 'lns)) (call-interactively 'org-attach-attach)))
        ((memq c '(?n ?\C-n)) (call-interactively 'org-attach-new))
        ((memq c '(?z ?\C-z)) (call-interactively 'org-attach-sync))
        ((memq c '(?o ?\C-o)) (call-interactively 'org-attach-open))
@@ -186,7 +208,9 @@ the directory and (if necessary) the corresponding ID will be created."
            (save-excursion
              (save-restriction
                (widen)
-               (goto-char org-entry-property-inherited-from)
+               (if (marker-position org-entry-property-inherited-from)
+                   (goto-char org-entry-property-inherited-from)
+                 (org-back-to-heading t))
                (let (org-attach-allow-inheritance)
                  (org-attach-dir create-if-not-exists-p)))))
       (org-attach-check-absolute-path attach-dir)
@@ -240,18 +264,32 @@ the ATTACH_DIR property) their own attachment directory."
 (defun org-attach-commit ()
   "Commit changes to git if `org-attach-directory' is properly initialized.
 This checks for the existence of a \".git\" directory in that directory."
-  (let ((dir (expand-file-name org-attach-directory)))
-    (when (file-exists-p (expand-file-name ".git" dir))
+  (let* ((dir (expand-file-name org-attach-directory))
+        (git-dir (vc-git-root dir))
+        (changes 0))
+    (when (and git-dir (executable-find "git"))
       (with-temp-buffer
        (cd dir)
-       (shell-command "git add .")
-       (shell-command "git ls-files --deleted" t)
-       (mapc (lambda (file)
-                (unless (string= file "")
-                  (shell-command
-                   (concat "git rm \"" file "\""))))
-             (split-string (buffer-string) "\n"))
-       (shell-command "git commit -m 'Synchronized attachments'")))))
+       (let ((have-annex
+              (and org-attach-git-annex-cutoff
+                   (file-exists-p (expand-file-name "annex" git-dir)))))
+         (dolist (new-or-modified
+                  (split-string
+                   (shell-command-to-string
+                    "git ls-files -zmo --exclude-standard") "\0" t))
+           (if (and have-annex
+                    (>= (nth 7 (file-attributes new-or-modified))
+                        org-attach-git-annex-cutoff))
+               (call-process "git" nil nil nil "annex" "add" new-or-modified)
+             (call-process "git" nil nil nil "add" new-or-modified))
+           (incf changes)))
+       (dolist (deleted
+                (split-string
+                 (shell-command-to-string "git ls-files -z --deleted") "\0" t))
+         (call-process "git" nil nil nil "rm" deleted)
+         (incf changes))
+       (when (> changes 0)
+         (shell-command "git commit -m 'Synchronized attachments'"))))))
 
 (defun org-attach-tag (&optional off)
   "Turn the autotag on or (if OFF is set) off."
@@ -264,10 +302,19 @@ This checks for the existence of a \".git\" directory in that directory."
   "Turn the autotag off."
   (org-attach-tag 'off))
 
+(defun org-attach-store-link (file)
+  "Add a link to `org-stored-link' when attaching a file.
+Only do this when `org-attach-store-link-p' is non-nil."
+  (setq org-stored-links
+       (cons (list (org-attach-expand-link file)
+                   (file-name-nondirectory file))
+             org-stored-links)))
+
 (defun org-attach-attach (file &optional visit-dir method)
   "Move/copy/link FILE into the attachment directory of the current task.
 If VISIT-DIR is non-nil, visit the directory with dired.
-METHOD may be `cp', `mv', or `ln', default taken from `org-attach-method'."
+METHOD may be `cp', `mv', `ln', or `lns' default taken from
+`org-attach-method'."
   (interactive "fFile to keep as an attachment: \nP")
   (setq method (or method org-attach-method))
   (let ((basename (file-name-nondirectory file)))
@@ -279,9 +326,14 @@ METHOD may be `cp', `mv', or `ln', default taken from `org-attach-method'."
       (cond
        ((eq method 'mv)        (rename-file file fname))
        ((eq method 'cp)        (copy-file file fname))
-       ((eq method 'ln) (add-name-to-file file fname)))
+       ((eq method 'ln) (add-name-to-file file fname))
+       ((eq method 'lns) (make-symbolic-link file fname)))
       (org-attach-commit)
       (org-attach-tag)
+      (cond ((eq org-attach-store-link-p 'attached)
+            (org-attach-store-link fname))
+           ((eq org-attach-store-link-p t)
+            (org-attach-store-link file)))
       (if visit-dir
          (dired attach-dir)
        (message "File \"%s\" is now a task attachment." basename)))))
@@ -300,6 +352,13 @@ Beware that this does not work on systems that do not support hard links.
 On some systems, this apparently does copy the file instead."
   (interactive)
   (let ((org-attach-method 'ln)) (call-interactively 'org-attach-attach)))
+(defun org-attach-attach-lns ()
+  "Attach a file by creating a symbolic link to it.
+
+Beware that this does not work on systems that do not support symbolic links.
+On some systems, this apparently does copy the file instead."
+  (interactive)
+  (let ((org-attach-method 'lns)) (call-interactively 'org-attach-attach)))
 
 (defun org-attach-new (file)
   "Create a new attachment FILE for the current task.
@@ -372,14 +431,14 @@ This ignores files starting with a \".\", and files ending in \"~\"."
                (directory-files dir nil "[^~]\\'"))))
 
 (defun org-attach-reveal (&optional if-exists)
-  "Show the attachment directory of the current task in dired."
+  "Show the attachment directory of the current task.
+This will attempt to use an external program to show the directory."
   (interactive "P")
   (let ((attach-dir (org-attach-dir (not if-exists))))
     (and attach-dir (org-open-file attach-dir))))
 
 (defun org-attach-reveal-in-emacs ()
-  "Show the attachment directory of the current task.
-This will attempt to use an external program to show the directory."
+  "Show the attachment directory of the current task in dired."
   (interactive)
   (let ((attach-dir (org-attach-dir t)))
     (dired attach-dir)))
@@ -396,7 +455,7 @@ If IN-EMACS is non-nil, force opening in Emacs."
         (file (if (= (length files) 1)
                   (car files)
                 (org-icompleting-read "Open attachment: "
-                                 (mapcar 'list files) nil t))))
+                                      (mapcar 'list files) nil t))))
     (org-open-file (expand-file-name file attach-dir) in-emacs)))
 
 (defun org-attach-open-in-emacs ()
@@ -418,4 +477,8 @@ prefix."
 
 (provide 'org-attach)
 
+;; Local variables:
+;; generated-autoload-file: "org-loaddefs.el"
+;; End:
+
 ;;; org-attach.el ends here