dotemacs

My Emacs configuration
git clone git://git.entf.net/dotemacs
Log | Files | Refs | LICENSE

magit-worktree.el (7752B)


      1 ;;; magit-worktree.el --- worktree support  -*- lexical-binding: t -*-
      2 
      3 ;; Copyright (C) 2010-2021  The Magit Project Contributors
      4 ;;
      5 ;; You should have received a copy of the AUTHORS.md file which
      6 ;; lists all contributors.  If not, see http://magit.vc/authors.
      7 
      8 ;; Author: Jonas Bernoulli <jonas@bernoul.li>
      9 ;; Maintainer: Jonas Bernoulli <jonas@bernoul.li>
     10 
     11 ;; SPDX-License-Identifier: GPL-3.0-or-later
     12 
     13 ;; Magit is free software; you can redistribute it and/or modify it
     14 ;; under the terms of the GNU General Public License as published by
     15 ;; the Free Software Foundation; either version 3, or (at your option)
     16 ;; any later version.
     17 ;;
     18 ;; Magit is distributed in the hope that it will be useful, but WITHOUT
     19 ;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
     20 ;; or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
     21 ;; License for more details.
     22 ;;
     23 ;; You should have received a copy of the GNU General Public License
     24 ;; along with Magit.  If not, see http://www.gnu.org/licenses.
     25 
     26 ;;; Commentary:
     27 
     28 ;; This library implements support for `git-worktree'.
     29 
     30 ;;; Code:
     31 
     32 (require 'magit)
     33 
     34 ;;; Options
     35 
     36 (defcustom magit-worktree-read-directory-name-function 'read-directory-name
     37   "Function used to read a directory for worktree commands.
     38 This is called with one argument, the prompt, and can be used
     39 to e.g. use a base directory other than `default-directory'.
     40 Used by `magit-worktree-checkout' and `magit-worktree-branch'."
     41   :package-version '(magit . "3.0.0")
     42   :group 'magit-commands
     43   :type 'function)
     44 
     45 ;;; Commands
     46 
     47 ;;;###autoload (autoload 'magit-worktree "magit-worktree" nil t)
     48 (transient-define-prefix magit-worktree ()
     49   "Act on a worktree."
     50   :man-page "git-worktree"
     51   [["Create new"
     52     ("b" "worktree"              magit-worktree-checkout)
     53     ("c" "branch and worktree"   magit-worktree-branch)]
     54    ["Commands"
     55     ("m" "Move worktree"         magit-worktree-move)
     56     ("k" "Delete worktree"       magit-worktree-delete)
     57     ("g" "Visit worktree"        magit-worktree-status)]])
     58 
     59 ;;;###autoload
     60 (defun magit-worktree-checkout (path branch)
     61   "Checkout BRANCH in a new worktree at PATH."
     62   (interactive
     63    (let ((branch (magit-read-branch-or-commit "Checkout")))
     64      (list (funcall magit-worktree-read-directory-name-function
     65                     (format "Checkout %s in new worktree: " branch))
     66            branch)))
     67   (magit-run-git "worktree" "add" (expand-file-name path) branch)
     68   (magit-diff-visit-directory path))
     69 
     70 ;;;###autoload
     71 (defun magit-worktree-branch (path branch start-point &optional force)
     72   "Create a new BRANCH and check it out in a new worktree at PATH."
     73   (interactive
     74    `(,(funcall magit-worktree-read-directory-name-function
     75                "Create worktree: ")
     76      ,@(magit-branch-read-args "Create and checkout branch")
     77      ,current-prefix-arg))
     78   (magit-run-git "worktree" "add" (if force "-B" "-b")
     79                  branch (expand-file-name path) start-point)
     80   (magit-diff-visit-directory path))
     81 
     82 ;;;###autoload
     83 (defun magit-worktree-move (worktree path)
     84   "Move WORKTREE to PATH."
     85   (interactive
     86    (list (magit-completing-read "Move worktree"
     87                                 (cdr (magit-list-worktrees))
     88                                 nil t nil nil
     89                                 (magit-section-value-if 'worktree))
     90          (funcall magit-worktree-read-directory-name-function
     91                   "Move worktree to: ")))
     92   (if (file-directory-p (expand-file-name ".git" worktree))
     93       (user-error "You may not move the main working tree")
     94     (let ((preexisting-directory (file-directory-p path)))
     95       (when (and (zerop (magit-call-git "worktree" "move" worktree
     96                                         (expand-file-name path)))
     97                  (not (file-exists-p default-directory))
     98                  (derived-mode-p 'magit-status-mode))
     99         (kill-buffer)
    100         (magit-diff-visit-directory
    101          (if preexisting-directory
    102              (concat (file-name-as-directory path)
    103                      (file-name-nondirectory worktree))
    104            path)))
    105       (magit-refresh))))
    106 
    107 (defun magit-worktree-delete (worktree)
    108   "Delete a worktree, defaulting to the worktree at point.
    109 The primary worktree cannot be deleted."
    110   (interactive
    111    (list (magit-completing-read "Delete worktree"
    112                                 (cdr (magit-list-worktrees))
    113                                 nil t nil nil
    114                                 (magit-section-value-if 'worktree))))
    115   (if (file-directory-p (expand-file-name ".git" worktree))
    116       (user-error "Deleting %s would delete the shared .git directory" worktree)
    117     (let ((primary (file-name-as-directory (caar (magit-list-worktrees)))))
    118       (magit-confirm-files (if magit-delete-by-moving-to-trash 'trash 'delete)
    119                            (list "worktree"))
    120       (when (file-exists-p worktree)
    121         (let ((delete-by-moving-to-trash magit-delete-by-moving-to-trash))
    122           (delete-directory worktree t magit-delete-by-moving-to-trash)))
    123       (if (file-exists-p default-directory)
    124           (magit-run-git "worktree" "prune")
    125         (let ((default-directory primary))
    126           (magit-run-git "worktree" "prune"))
    127         (when (derived-mode-p 'magit-status-mode)
    128           (kill-buffer)
    129           (magit-status-setup-buffer primary))))))
    130 
    131 (defun magit-worktree-status (worktree)
    132   "Show the status for the worktree at point.
    133 If there is no worktree at point, then read one in the
    134 minibuffer.  If the worktree at point is the one whose
    135 status is already being displayed in the current buffer,
    136 then show it in Dired instead."
    137   (interactive
    138    (list (or (magit-section-value-if 'worktree)
    139              (magit-completing-read
    140               "Show status for worktree"
    141               (cl-delete (directory-file-name (magit-toplevel))
    142                          (magit-list-worktrees)
    143                          :test #'equal :key #'car)))))
    144   (magit-diff-visit-directory worktree))
    145 
    146 ;;; Sections
    147 
    148 (defvar magit-worktree-section-map
    149   (let ((map (make-sparse-keymap)))
    150     (define-key map [remap magit-visit-thing]  'magit-worktree-status)
    151     (define-key map [remap magit-delete-thing] 'magit-worktree-delete)
    152     map)
    153   "Keymap for `worktree' sections.")
    154 
    155 (defun magit-insert-worktrees ()
    156   "Insert sections for all worktrees.
    157 If there is only one worktree, then insert nothing."
    158   (let ((worktrees (magit-list-worktrees)))
    159     (when (> (length worktrees) 1)
    160       (magit-insert-section (worktrees)
    161         (magit-insert-heading "Worktrees:")
    162         (let* ((cols
    163                 (mapcar
    164                  (pcase-lambda (`(,path ,barep ,commit ,branch))
    165                    (cons (cond
    166                           (branch (propertize
    167                                    branch 'font-lock-face
    168                                    (if (equal branch (magit-get-current-branch))
    169                                        'magit-branch-current
    170                                      'magit-branch-local)))
    171                           (commit (propertize (magit-rev-abbrev commit)
    172                                               'font-lock-face 'magit-hash))
    173                           (barep  "(bare)"))
    174                          path))
    175                  worktrees))
    176                (align (1+ (-max (--map (string-width (car it)) cols)))))
    177           (pcase-dolist (`(,head . ,path) cols)
    178             (magit-insert-section (worktree path)
    179               (insert head)
    180               (insert (make-string (- align (length head)) ?\s))
    181               (insert (let ((r (file-relative-name path))
    182                             (a (abbreviate-file-name path)))
    183                         (if (< (string-width r) (string-width a)) r a)))
    184               (insert ?\n))))
    185         (insert ?\n)))))
    186 
    187 ;;; _
    188 (provide 'magit-worktree)
    189 ;;; magit-worktree.el ends here