I would like to "augment" the org-refile function in such a way that, once I moved the heading from the source to the destination, a link to this last file is created and inserted in the source, in place of the pre-existing heading. The source will generally be a TODO item, but in case of a tree, the title of the link should be the top-level heading of course.

I'm new to elisp and don't know much about emacs customization either, so any hint or help towards a solution would be greatly appreciated. (I'll also try to find my way as soon as I get a minute to work on it, of course).

2 Answers 2


Here's a lightly tested walkthrough of how you could get started with this.

The first thing we need is a way to capture where the heading-to-be-refiled is. Org might store this information itself, but we don't need to worry about that. Let's create a variable that stores a marker to the location (see (info "(elisp)Markers")) and then use advice ((info "(elisp)Advising Functions")) to instruct org-refile to set the variable to that marker before refiling.

(defvar my/org-last-refile-marker nil)

(advice-add 'org-refile
            (lambda (&rest _)
                (setq my/org-last-refile-marker (point-marker))))
            '((name . "my/org-set-refile-marker")))

Next we need to store a link after the heading has been refiled. Org provides a hook, org-after-refile-insert-hook, that we can use.

(defvar my/org-last-refile-link nil)

(defun my/org-set-last-refile-link ()
  (setq my/org-last-refile-link (org-store-link nil)))

(add-hook 'org-after-refile-insert-hook #'my/org-set-last-refile-link)

Now the link text for that heading will be stored in the variable my/org-last-refile-link. The remaining piece is to go back to the original location and insert the link. Again, we can advise org-refile, this time to do something after instead of before.

(advice-add 'org-refile
            (lambda (&rest _)
              (when (and my/org-last-refile-marker
                (let ((buf (marker-buffer my/org-last-refile-marker)))
                  (when (buffer-live-p buf)
                    (with-current-buffer buf
                        (goto-char my/org-last-refile-marker)
                        (insert (concat my/org-last-refile-link "\n"))))))
                (setq my/org-last-refile-marker nil)
                (setq my/org-last-refile-link nil)))
            '((name . "my/org-insert-refile-marker")))

That's got at least a few rough edges. For example, org-refile accepts a prefix argument that makes it do something other than refile. Also it might not work when called via org-agenda-refile. Another thing to consider is whether you want this behavior for every org-refile call. If not, it might make sense to make a custom command that wraps org-refile instead of using the :before advice above.

But hopefully above gives you something to build off of.


If you're willing to use org ids to create links that don't depend on the location of the heading, then the problem becomes a little bit easier to solve because the whole link creation process can be done before anything is refiled. You can use ids for org links by setting the variable org-id-link-to-org-use-id to true.

Important note: this will not work as intended when refiling a region containing more than one subtree or if you use any prefixes on the refile command.

So the function and advice setup is done like so:

(defun org-refile--insert-link ( &rest _ )
  (let* ((refile-region-marker (point-marker))
         (source-link (org-store-link nil)))
    (insert source-link)
    (goto-char refile-region-marker)))

(advice-add 'org-refile

The first function moves the pointer to the beginning of the heading. We save the marker at that point, and also store the link at that point. Insert a new heading (depending on your settings this might need to explicitly be org-insert-heading-after-current) and insert the link we saved. Finally we move the pointer back to our saved marker so that org-refile starts with the pointer where it is expected.

  • This is nice! Works like charm. Thanks.
    – deshmukh
    Apr 7, 2021 at 3:00

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.