magit-sparse-checkout.el (6797B)
1 ;;; magit-sparse-checkout.el --- Sparse checkout support for Magit -*- lexical-binding:t -*- 2 3 ;; Copyright (C) 2008-2024 The Magit Project Contributors 4 5 ;; Author: Kyle Meyer <kyle@kyleam.com> 6 ;; Maintainer: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev> 7 8 ;; SPDX-License-Identifier: GPL-3.0-or-later 9 10 ;; Magit is free software: you can redistribute it and/or modify it 11 ;; under the terms of the GNU General Public License as published by 12 ;; the Free Software Foundation, either version 3 of the License, or 13 ;; (at your option) any later version. 14 ;; 15 ;; Magit is distributed in the hope that it will be useful, but WITHOUT 16 ;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 17 ;; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public 18 ;; License for more details. 19 ;; 20 ;; You should have received a copy of the GNU General Public License 21 ;; along with Magit. If not, see <https://www.gnu.org/licenses/>. 22 23 ;;; Commentary: 24 25 ;; This library provides an interface to the `git sparse-checkout' 26 ;; command. It's been possible to define sparse checkouts since Git 27 ;; v1.7.0 by adding patterns to $GIT_DIR/info/sparse-checkout and 28 ;; calling `git read-tree -mu HEAD' to update the index and working 29 ;; tree. However, Git v2.25 introduced the `git sparse-checkout' 30 ;; command along with "cone mode", which restricts the possible 31 ;; patterns to directories to provide better performance. 32 ;; 33 ;; The goal of this library is to support the `git sparse-checkout' 34 ;; command operating in cone mode. 35 36 ;;; Code: 37 38 (require 'magit) 39 40 ;;; Utilities 41 42 (defun magit-sparse-checkout-enabled-p () 43 "Return non-nil if working tree is a sparse checkout." 44 (magit-get-boolean "core.sparsecheckout")) 45 46 (defun magit-sparse-checkout--assert-version () 47 ;; Older versions of Git have the ability to define sparse checkout 48 ;; patterns in .git/info/sparse-checkout, but the sparse-checkout 49 ;; command isn't available until 2.25.0. 50 (when (magit-git-version< "2.25.0") 51 (user-error "`git sparse-checkout' not available until Git v2.25"))) 52 53 (defun magit-sparse-checkout--auto-enable () 54 (if (magit-sparse-checkout-enabled-p) 55 (unless (magit-get-boolean "core.sparsecheckoutcone") 56 (user-error 57 "Magit's sparse checkout functionality requires cone mode")) 58 ;; Note: Don't use `magit-sparse-checkout-enable' because it's 59 ;; asynchronous. 60 (magit-run-git "sparse-checkout" "init" "--cone"))) 61 62 (defun magit-sparse-checkout-directories () 63 "Return directories that are recursively included in the sparse checkout. 64 See the `git sparse-checkout' manpage for details about 65 \"recursive\" versus \"parent\" directories in cone mode." 66 (and (magit-get-boolean "core.sparsecheckoutcone") 67 (mapcar #'file-name-as-directory 68 (magit-git-lines "sparse-checkout" "list")))) 69 70 ;;; Commands 71 72 ;;;###autoload (autoload 'magit-sparse-checkout "magit-sparse-checkout" nil t) 73 (transient-define-prefix magit-sparse-checkout () 74 "Create and manage sparse checkouts." 75 :man-page "git-sparse-checkout" 76 ["Arguments for enabling" 77 :if-not magit-sparse-checkout-enabled-p 78 ("-i" "Use sparse index" "--sparse-index")] 79 ["Actions" 80 [:if-not magit-sparse-checkout-enabled-p 81 ("e" "Enable sparse checkout" magit-sparse-checkout-enable)] 82 [:if magit-sparse-checkout-enabled-p 83 ("d" "Disable sparse checkout" magit-sparse-checkout-disable) 84 ("r" "Reapply rules" magit-sparse-checkout-reapply)] 85 [("s" "Set directories" magit-sparse-checkout-set) 86 ("a" "Add directories" magit-sparse-checkout-add)]]) 87 88 ;;;###autoload 89 (defun magit-sparse-checkout-enable (&optional args) 90 "Convert the working tree to a sparse checkout." 91 (interactive (list (transient-args 'magit-sparse-checkout))) 92 (magit-sparse-checkout--assert-version) 93 (magit-run-git-async "sparse-checkout" "init" "--cone" args)) 94 95 ;;;###autoload 96 (defun magit-sparse-checkout-set (directories) 97 "Restrict working tree to DIRECTORIES. 98 To extend rather than override the currently configured 99 directories, call `magit-sparse-checkout-add' instead." 100 (interactive 101 (list (magit-completing-read-multiple 102 "Include these directories: " 103 ;; Note: Given that the appeal of sparse checkouts is 104 ;; dealing with very large trees, listing all subdirectories 105 ;; may need to be reconsidered. 106 (magit-revision-directories "HEAD")))) 107 (magit-sparse-checkout--assert-version) 108 (magit-sparse-checkout--auto-enable) 109 (magit-run-git-async "sparse-checkout" "set" directories)) 110 111 ;;;###autoload 112 (defun magit-sparse-checkout-add (directories) 113 "Add DIRECTORIES to the working tree. 114 To override rather than extend the currently configured 115 directories, call `magit-sparse-checkout-set' instead." 116 (interactive 117 (list (magit-completing-read-multiple 118 "Add these directories: " 119 ;; Same performance note as in `magit-sparse-checkout-set', 120 ;; but even more so given the additional processing. 121 (seq-remove 122 (let ((re (concat 123 "\\`" 124 (regexp-opt (magit-sparse-checkout-directories))))) 125 (lambda (d) (string-match-p re d))) 126 (magit-revision-directories "HEAD"))))) 127 (magit-sparse-checkout--assert-version) 128 (magit-sparse-checkout--auto-enable) 129 (magit-run-git-async "sparse-checkout" "add" directories)) 130 131 ;;;###autoload 132 (defun magit-sparse-checkout-reapply () 133 "Reapply the sparse checkout rules to the working tree. 134 Some operations such as merging or rebasing may need to check out 135 files that aren't included in the sparse checkout. Call this 136 command to reset to the sparse checkout state." 137 (interactive) 138 (magit-sparse-checkout--assert-version) 139 (magit-run-git-async "sparse-checkout" "reapply")) 140 141 ;;;###autoload 142 (defun magit-sparse-checkout-disable () 143 "Convert sparse checkout to full checkout. 144 Note that disabling the sparse checkout does not clear the 145 configured directories. Call `magit-sparse-checkout-enable' to 146 restore the previous sparse checkout." 147 (interactive) 148 (magit-sparse-checkout--assert-version) 149 (magit-run-git-async "sparse-checkout" "disable")) 150 151 ;;; Miscellaneous 152 153 (defun magit-sparse-checkout-insert-header () 154 "Insert header line with sparse checkout information. 155 This header is not inserted by default. To enable it, add it to 156 `magit-status-headers-hook'." 157 (when (magit-sparse-checkout-enabled-p) 158 (insert (propertize (format "%-10s" "Sparse! ") 159 'font-lock-face 'magit-section-heading)) 160 (insert 161 (let ((dirs (magit-sparse-checkout-directories))) 162 (pcase (length dirs) 163 (0 "top-level directory") 164 (1 (car dirs)) 165 (n (format "%d directories" n))))) 166 (insert ?\n))) 167 168 ;;; _ 169 (provide 'magit-sparse-checkout) 170 ;;; magit-sparse-checkout.el ends here