<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Magit on The Search</title>
    <link>https://waldencui.com/tags/magit/</link>
    <description>Recent content in Magit on The Search</description>
    <generator>Hugo -- 0.147.7</generator>
    <language>en-us</language>
    <lastBuildDate>Mon, 13 Jul 2026 12:32:15 -0700</lastBuildDate>
    <atom:link href="https://waldencui.com/tags/magit/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>A Custom Function To Rebase All Worktrees in Magit</title>
      <link>https://waldencui.com/post/magit_rebase_all/</link>
      <pubDate>Mon, 13 Jul 2026 12:32:15 -0700</pubDate>
      <guid>https://waldencui.com/post/magit_rebase_all/</guid>
      <description>This post shows how to use a custom function to rebase all changed worktrees into the main branch in Emacs Magit.</description>
      <content:encoded><![CDATA[<p>
I&#39;ve been using Git worktrees for quite a while during agentic coding, and it&#39;s been largely successful with my current workflow. One hassle to perform inside of Emacs after some changes in worktrees is that I have to go to the worktrees and rebase them to the main branch one by one. In Magit, this is relatively easy when performed individually. But when I have more than one worktrees need rebasing, it quickly become a repetitive chore.</p>
<p>
Since Emacs is highly extensible, I wonder if I can inject a transient command such that each time I want to rebase the worktrees, it&#39;s just one command away for all worktrees.</p>
<p>
Here it is.</p>
<div class="src src-elisp">
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-elisp" data-lang="elisp"><span style="display:flex;"><span>  <span style="color:#75715e">;;;; Inside of user-lisp/ folder</span>
</span></span><span style="display:flex;"><span>  (defcustom my-magit-worktree-base-branch <span style="color:#e6db74">&#34;main&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;Branch onto which </span><span style="color:#e6db74">`my-magit-rebase-all-worktrees&#39;</span><span style="color:#e6db74"> rebases worktrees.&#34;</span>
</span></span><span style="display:flex;"><span>    :type <span style="color:#e6db74">&#39;string</span>
</span></span><span style="display:flex;"><span>    :group <span style="color:#e6db74">&#39;magit-commands</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#75715e">;;;###autoload</span>
</span></span><span style="display:flex;"><span>  (defun my-magit-rebase-all-worktrees ()
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;Rebase every clean worktree onto </span><span style="color:#e6db74">`my-magit-worktree-base-branch&#39;</span><span style="color:#e6db74">.
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">  The base branch itself and detached, bare, missing, or dirty worktrees are
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">  skipped.  Stop at the first failed rebase so that at most one worktree is
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">  left needing conflict resolution.&#34;</span>
</span></span><span style="display:flex;"><span>    (interactive)
</span></span><span style="display:flex;"><span>    (let* ((default-directory (<span style="color:#a6e22e">file-name-as-directory</span> (magit-toplevel)))
</span></span><span style="display:flex;"><span>           (base my-magit-worktree-base-branch)
</span></span><span style="display:flex;"><span>           (worktrees (magit-list-worktrees))
</span></span><span style="display:flex;"><span>           (log-buffer (<span style="color:#a6e22e">get-buffer-create</span> <span style="color:#e6db74">&#34;*Magit update worktrees*&#34;</span>))
</span></span><span style="display:flex;"><span>           updated skipped failed)
</span></span><span style="display:flex;"><span>      (unless (magit-local-branch-p base)
</span></span><span style="display:flex;"><span>        (<span style="color:#a6e22e">user-error</span> <span style="color:#e6db74">&#34;No local branch named %s&#34;</span> base))
</span></span><span style="display:flex;"><span>      (with-current-buffer log-buffer
</span></span><span style="display:flex;"><span>        (let ((inhibit-read-only <span style="color:#66d9ef">t</span>))
</span></span><span style="display:flex;"><span>          (fundamental-mode)
</span></span><span style="display:flex;"><span>          (setq buffer-read-only <span style="color:#66d9ef">nil</span>)
</span></span><span style="display:flex;"><span>          (<span style="color:#a6e22e">erase-buffer</span>)
</span></span><span style="display:flex;"><span>          (<span style="color:#a6e22e">insert</span> (<span style="color:#a6e22e">format</span> <span style="color:#e6db74">&#34;Rebasing worktrees onto %s\n\n&#34;</span> base))))
</span></span><span style="display:flex;"><span>      (catch <span style="color:#e6db74">&#39;rebase-failed</span>
</span></span><span style="display:flex;"><span>        (pcase-dolist (<span style="color:#f92672">`</span>(<span style="color:#f92672">,</span>directory <span style="color:#f92672">,</span>_commit <span style="color:#f92672">,</span>branch <span style="color:#f92672">,</span>bare <span style="color:#f92672">,</span>detached
</span></span><span style="display:flex;"><span>                                    <span style="color:#f92672">,</span>_locked <span style="color:#f92672">,</span>prunable)
</span></span><span style="display:flex;"><span>                       worktrees)
</span></span><span style="display:flex;"><span>          (let ((reason
</span></span><span style="display:flex;"><span>                 (cond
</span></span><span style="display:flex;"><span>                  ((<span style="color:#a6e22e">equal</span> branch base) <span style="color:#e6db74">&#34;base branch&#34;</span>)
</span></span><span style="display:flex;"><span>                  (bare <span style="color:#e6db74">&#34;bare&#34;</span>)
</span></span><span style="display:flex;"><span>                  (detached <span style="color:#e6db74">&#34;detached HEAD&#34;</span>)
</span></span><span style="display:flex;"><span>                  ((or prunable (not (<span style="color:#a6e22e">file-directory-p</span> directory))) <span style="color:#e6db74">&#34;missing&#34;</span>)
</span></span><span style="display:flex;"><span>                  ((let ((default-directory
</span></span><span style="display:flex;"><span>                          (<span style="color:#a6e22e">file-name-as-directory</span> directory)))
</span></span><span style="display:flex;"><span>                     (magit-git-lines <span style="color:#e6db74">&#34;status&#34;</span> <span style="color:#e6db74">&#34;--porcelain&#34;</span>))
</span></span><span style="display:flex;"><span>                   <span style="color:#e6db74">&#34;dirty&#34;</span>))))
</span></span><span style="display:flex;"><span>            (if reason
</span></span><span style="display:flex;"><span>                (push (<span style="color:#a6e22e">format</span> <span style="color:#e6db74">&#34;%s (%s)&#34;</span> (or branch directory) reason) skipped)
</span></span><span style="display:flex;"><span>              (with-current-buffer log-buffer
</span></span><span style="display:flex;"><span>                (let ((inhibit-read-only <span style="color:#66d9ef">t</span>))
</span></span><span style="display:flex;"><span>                  (<span style="color:#a6e22e">goto-char</span> (<span style="color:#a6e22e">point-max</span>))
</span></span><span style="display:flex;"><span>                  (<span style="color:#a6e22e">insert</span> (<span style="color:#a6e22e">format</span> <span style="color:#e6db74">&#34;$ git -C %s rebase %s\n&#34;</span>
</span></span><span style="display:flex;"><span>                                  (shell-quote-argument directory)
</span></span><span style="display:flex;"><span>                                  (shell-quote-argument base)))))
</span></span><span style="display:flex;"><span>              (let* ((default-directory (<span style="color:#a6e22e">file-name-as-directory</span> directory))
</span></span><span style="display:flex;"><span>                     (process-environment
</span></span><span style="display:flex;"><span>                      (<span style="color:#a6e22e">cons</span> <span style="color:#e6db74">&#34;GIT_EDITOR=:&#34;</span> process-environment))
</span></span><span style="display:flex;"><span>                     (exit-code
</span></span><span style="display:flex;"><span>                      (process-file <span style="color:#e6db74">&#34;git&#34;</span> <span style="color:#66d9ef">nil</span> (<span style="color:#a6e22e">list</span> log-buffer <span style="color:#66d9ef">t</span>) <span style="color:#66d9ef">nil</span>
</span></span><span style="display:flex;"><span>                                    <span style="color:#e6db74">&#34;rebase&#34;</span> base)))
</span></span><span style="display:flex;"><span>                (with-current-buffer log-buffer
</span></span><span style="display:flex;"><span>                  (let ((inhibit-read-only <span style="color:#66d9ef">t</span>))
</span></span><span style="display:flex;"><span>                    (<span style="color:#a6e22e">goto-char</span> (<span style="color:#a6e22e">point-max</span>))
</span></span><span style="display:flex;"><span>                    (<span style="color:#a6e22e">insert</span> <span style="color:#e6db74">&#34;\n&#34;</span>)))
</span></span><span style="display:flex;"><span>                (if (and (<span style="color:#a6e22e">integerp</span> exit-code) (zerop exit-code))
</span></span><span style="display:flex;"><span>                    (push branch updated)
</span></span><span style="display:flex;"><span>                  (setq failed (<span style="color:#a6e22e">cons</span> branch directory))
</span></span><span style="display:flex;"><span>                  (throw <span style="color:#e6db74">&#39;rebase-failed</span> <span style="color:#66d9ef">nil</span>)))))))
</span></span><span style="display:flex;"><span>      (with-current-buffer log-buffer
</span></span><span style="display:flex;"><span>        (special-mode))
</span></span><span style="display:flex;"><span>      (magit-refresh-all)
</span></span><span style="display:flex;"><span>      (if failed
</span></span><span style="display:flex;"><span>          (progn
</span></span><span style="display:flex;"><span>            (display-buffer log-buffer)
</span></span><span style="display:flex;"><span>            (<span style="color:#a6e22e">user-error</span> <span style="color:#e6db74">&#34;Rebase failed in %s (%s); resolve or abort it there&#34;</span>
</span></span><span style="display:flex;"><span>                        (<span style="color:#a6e22e">car</span> failed) (<span style="color:#a6e22e">cdr</span> failed)))
</span></span><span style="display:flex;"><span>        (<span style="color:#a6e22e">message</span> <span style="color:#e6db74">&#34;Rebased %d worktree%s onto %s%s&#34;</span>
</span></span><span style="display:flex;"><span>                 (<span style="color:#a6e22e">length</span> updated)
</span></span><span style="display:flex;"><span>                 (if (<span style="color:#a6e22e">=</span> (<span style="color:#a6e22e">length</span> updated) <span style="color:#ae81ff">1</span>) <span style="color:#e6db74">&#34;&#34;</span> <span style="color:#e6db74">&#34;s&#34;</span>)
</span></span><span style="display:flex;"><span>                 base
</span></span><span style="display:flex;"><span>                 (if skipped
</span></span><span style="display:flex;"><span>                     (<span style="color:#a6e22e">format</span> <span style="color:#e6db74">&#34;; skipped %s&#34;</span> (string-join (<span style="color:#a6e22e">nreverse</span> skipped) <span style="color:#e6db74">&#34;, &#34;</span>))
</span></span><span style="display:flex;"><span>                   <span style="color:#e6db74">&#34;&#34;</span>)))))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#75715e">;;;; In init.el</span>
</span></span><span style="display:flex;"><span>  (use-package magit
</span></span><span style="display:flex;"><span>    :functions magit-insert-worktrees
</span></span><span style="display:flex;"><span>    :bind (<span style="color:#e6db74">&#34;C-x g&#34;</span> <span style="color:#f92672">.</span> magit-status)
</span></span><span style="display:flex;"><span>    :config
</span></span><span style="display:flex;"><span>    (add-hook <span style="color:#e6db74">&#39;magit-status-sections-hook</span> <span style="color:#a6e22e">#&#39;</span>magit-insert-worktrees <span style="color:#66d9ef">t</span>)
</span></span><span style="display:flex;"><span>    (transient-append-suffix <span style="color:#e6db74">&#39;magit-worktree</span> <span style="color:#e6db74">&#34;g&#34;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">&#39;</span>(<span style="color:#e6db74">&#34;u&#34;</span> <span style="color:#e6db74">&#34;Update all from main&#34;</span> my-magit-rebase-all-worktrees)))</span></span></code></pre></div>
</div>
<p>
It&#39;s inserting into <code class="verbatim">magit-worktree</code>, so you can invoke it with <code class="verbatim">Z u</code> inside of Magit.</p>
]]></content:encoded>
    </item>
  </channel>
</rss>
