Hello everyone,

I spent some time on the org-mode project of FSF40 hackason. I know it is already too late to be considered officially taking part in the event, I still want to share my effort on it. And, wait, before you close, there is another helpful (maybe) advice for `org-archive-subtree'. I discovered that its doc string says:

> ... The tree will be moved to that location, the subtree heading be marked DONE, and the current time will be added.

When I was testing with `org-archive-subtree', I found that it did not mark the archived subtree as DONE, which was confusing me. Then when I read the code, I found that this behavior actually depends on the value of the variable `org-archive-mark-done'. I guess this variable was introduced after the doc string was written? Anyway, without checking the code and having knowledge of this variable, it may confuse users if they only read the doc string of the function and find that it does not behave as said.

Okay, come back to the FSF40 hackason project. I do have a minimal and naive `org-unarchive-subtree' implementation. I am really new to Emacs Lisp hacking and so I may make really naive assumptions, but at least I have tested the code and at least in my case it does work as expected. So, I am just sharing it and hope that some one may point out some of my silly mistakes and if I have violated some of the coding conventions. (I did read the documentation about Emacs Lisp coding conventions, but I can not guarantee that I have remembered them all.) Please be gentle about my mistakes though :)

I noticed when I was compiling the file that there is a warning:

In org-unarchive-subtree:
org-archive.el:681:41: Warning: Unused lexical variable ‘-compare-fn’

Though it is not accessed directly, it is used by `-contains?', then how do I deal with this warning? Also, it seems that `-contains?' is implemented by dash.el. Should I require it in this function? Or should I declare it has already been defined? Generally speaking, how should I tell if a function is already defined so that I need only declare it rather than requiring the package?

Also, I am a little bit confused about the commit messages and ChangeLog entries part in the contributing documentation. Are these messages part of the patch file or they should only present in this e-mail? Anyway, I have my git commit message following the requirement presented in the documentation. And I append it as follows:


lisp/org-archive.el: implement org-unarchive-subtree

* org-archive.el (org-unarchive-subtree): implement
org-unarchive-subtree, which tries to restore archived headings to its
original location.


Do I miss anything to contribute to org-mode? Thanks for pointing out.


Best regards


Xiaoduan Chang
From e8b36e5172776c5b168b34c902f5e2a895873469 Mon Sep 17 00:00:00 2001
From: Xiaoduan Chang <[email protected]>
Date: Mon, 24 Nov 2025 00:20:51 +0800
Subject: [PATCH] lisp/org-archive.el: implement org-unarchive-subtree

* org-archive.el (org-unarchive-subtree): implement
org-unarchive-subtree, which tries to restore archived headings to its
original location.
---
 lisp/org-archive.el | 32 ++++++++++++++++++++++++++++++++
 1 file changed, 32 insertions(+)

diff --git a/lisp/org-archive.el b/lisp/org-archive.el
index e3d5c423b..44284b948 100644
--- a/lisp/org-archive.el
+++ b/lisp/org-archive.el
@@ -659,6 +659,38 @@ This command is set with the variable `org-archive-default-command'."
       (call-interactively org-archive-default-command)
     (error "Abort")))
 
+;;;###autoload
+(defun org-unarchive-subtree ()
+  "Try moving the subtree at point back to where it was archived from.
+If the original file is missing, the process is aborted. If the original
+heading is missing, then it is inserted as a top level subtree."
+  (interactive)
+  (let ((file (abbreviate-file-name (org-entry-get (point) "ARCHIVE_FILE")))
+        (olpath (org-entry-get (point) "ARCHIVE_OLPATH")))
+    (unless file
+      (user-error "ERROR: missing property ARCHIVE_FILE!"))
+    (unless (file-exists-p file)
+      (user-error "ERROR: original file is missing: %s" file))
+    (org-copy-subtree 1 nil t)
+    (save-excursion
+      (switch-to-buffer (find-file-noselect file))
+      (let* ((headings (mapcar (lambda (l)
+                                 (list (mapconcat 'identity (car l) "/")
+                                       (car (cdr l))))
+                               (org-map-entries (lambda () (list (org-get-outline-path t) (point))))))
+             (old-parent-heading (let ((-compare-fn (lambda (s l)
+                                                      (equal s (car l)))))
+                                   (-contains? headings olpath))))
+        (if old-parent-heading
+            (let ((pos (car (cdr (car old-parent-heading)))))
+              (goto-char pos)
+              (org-paste-subtree '(16)))
+          (message "WARNING: Can not locate the original parent heading, unarchiving as a top level heading!")
+          (goto-char (point-max))
+          (org-paste-subtree 1))))
+    (switch-to-buffer (current-buffer))
+    (org-cut-subtree)))
+
 (provide 'org-archive)
 
 ;; Local variables:
-- 
2.51.0

Reply via email to