fordon_greeman / voidfiles

Branch: 2079ec5
Switch branches/tags
main
theidioticdev add emacs 2079ec5 23h ago
220 lines · 8.8 KB Raw Blame History
1 ;;; magit-gitignore.el --- Intentionally untracked files -*- lexical-binding:t -*-
2
3 ;; Copyright (C) 2008-2026 The Magit Project Contributors
4
5 ;; Author: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev>
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 implements gitignore commands.
26
27 ;;; Code:
28
29 (require 'magit)
30
31 ;;; Transient
32
33 ;;;###autoload(autoload 'magit-gitignore "magit-gitignore" nil t)
34 (transient-define-prefix magit-gitignore ()
35 "Instruct Git to ignore a file or pattern."
36 :man-page "gitignore"
37 ["Gitignore"
38 ("t" magit-gitignore-in-topdir)
39 ("s" magit-gitignore-in-subdir)
40 ("p" magit-gitignore-in-gitdir)
41 ("g" magit-gitignore-on-system)]
42 ["Skip worktree"
43 (7 "w" "do skip worktree" magit-skip-worktree)
44 (7 "W" "do not skip worktree" magit-no-skip-worktree)]
45 ["Assume unchanged"
46 (7 "u" "do assume unchanged" magit-assume-unchanged)
47 (7 "U" "do not assume unchanged" magit-no-assume-unchanged)])
48
49 ;;; Gitignore Commands
50
51 ;;;###autoload(autoload 'magit-gitignore-in-topdir "magit-gitignore" nil t)
52 (transient-define-suffix magit-gitignore-in-topdir (rule)
53 "Add the Git ignore RULE to the top-level \".gitignore\" file.
54 Since this file is tracked, it is shared with other clones of the
55 repository. Also stage the file."
56 :description "shared at toplevel (.gitignore)"
57 (interactive (list (magit-gitignore-read-pattern)))
58 (magit--gitignore rule (expand-file-name ".gitignore" (magit-toplevel)) t))
59
60 ;;;###autoload(autoload 'magit-gitignore-in-subdir "magit-gitignore" nil t)
61 (transient-define-suffix magit-gitignore-in-subdir (rule directory)
62 "Add the Git ignore RULE to a \".gitignore\" file in DIRECTORY.
63 Prompt the user for a directory and add the rule to the
64 \".gitignore\" file in that directory. Since such files are
65 tracked, they are shared with other clones of the repository.
66 Also stage the file."
67 :description "shared in subdirectory (path/to/.gitignore)"
68 (interactive (let ((dir (expand-file-name
69 (read-directory-name
70 "Limit rule to files in: "
71 (and$ (magit-current-file)
72 (file-name-directory
73 (expand-file-name $ (magit-toplevel))))))))
74 (list (magit-gitignore-read-pattern dir) dir)))
75 (magit--gitignore rule (expand-file-name ".gitignore" directory) t))
76
77 ;;;###autoload(autoload 'magit-gitignore-in-gitdir "magit-gitignore" nil t)
78 (transient-define-suffix magit-gitignore-in-gitdir (rule)
79 "Add the Git ignore RULE to \"$GIT_DIR/info/exclude\".
80 Rules in that file only affects this clone of the repository. If the
81 repository has multiple worktrees, the rules apply to all worktrees."
82 :description "privately (.git/info/exclude)"
83 (interactive (list (magit-gitignore-read-pattern)))
84 (magit--gitignore
85 rule
86 (expand-file-name "info/exclude" (magit-gitdir nil t))))
87
88 ;;;###autoload(autoload 'magit-gitignore-on-system "magit-gitignore" nil t)
89 (transient-define-suffix magit-gitignore-on-system (rule)
90 "Add the Git ignore RULE to the file specified by `core.excludesFile'.
91 Rules that are defined in that file affect all local repositories."
92 :inapt-if-not (##magit-get "core.excludesfile")
93 :description (##format "privately for all repositories (%s)"
94 (or (magit-get "core.excludesfile")
95 "core.excludesfile is not set"))
96 (interactive (list (magit-gitignore-read-pattern)))
97 (if-let ((file (magit-get "core.excludesFile")))
98 (magit--gitignore rule file)
99 (error "Variable `core.excludesFile' isn't set")))
100
101 (defun magit--gitignore (rule file &optional stage)
102 (when$ (file-name-directory file)
103 (make-directory $ t))
104 (with-temp-buffer
105 (when (file-exists-p file)
106 (insert-file-contents file))
107 (goto-char (point-max))
108 (unless (bolp)
109 (insert "\n"))
110 (insert (replace-regexp-in-string "\\(\\\\*\\)" "\\1\\1" rule))
111 (insert "\n")
112 (write-region nil nil file))
113 (if stage
114 (magit-with-toplevel
115 (magit-run-git "add" (magit-convert-filename-for-git file)))
116 (magit-refresh)))
117
118 (defun magit-gitignore-read-pattern (&optional directory)
119 (let ((choices (magit--gitignore-patterns directory))
120 (default (magit-current-file)))
121 (when default
122 (when directory
123 (setq default
124 (substring default
125 (length
126 (file-relative-name directory (magit-toplevel))))))
127 (setq default (concat "/" default))
128 (unless (member default choices)
129 (setq default (concat "*." (file-name-extension default)))
130 (unless (member default choices)
131 (setq default nil))))
132 (magit-completing-read "File or pattern to ignore"
133 choices nil 'any nil nil default)))
134
135 (defun magit--gitignore-patterns (&optional directory)
136 (let* ((topdir (magit-toplevel))
137 (default-directory (or directory topdir))
138 (files (magit-untracked-files nil directory))
139 ;; Include directories that contain only untracked files.
140 (dirs (magit--untracked-directories nil directory))
141 (globs nil)
142 (dirglobs nil))
143 (when directory
144 (let ((beg (length (file-relative-name directory topdir))))
145 (setq files (mapcar (##substring % beg) files))
146 (setq dirs (mapcar (##substring % beg) dirs))))
147 (dolist (file files)
148 (when-let ((ext (file-name-extension file)))
149 (cl-pushnew (concat "*." ext) globs :test #'equal)
150 (when-let ((dir (file-name-directory file)))
151 (cl-pushnew (concat dir "*." ext) dirglobs :test #'equal))))
152 (sort (nconc globs
153 (mapcar (##concat "/" %) (nconc files dirs dirglobs)))
154 #'string<)))
155
156 ;;; Skip Worktree Commands
157
158 ;;;###autoload
159 (defun magit-skip-worktree (file)
160 "Call \"git update-index --skip-worktree -- FILE\"."
161 (interactive
162 (list (magit-read-file-choice "Skip worktree for"
163 (cl-set-difference
164 (magit-list-files)
165 (magit-skip-worktree-files)
166 :test #'equal))))
167 (magit-with-toplevel
168 (magit-run-git "update-index" "--skip-worktree" "--" file)))
169
170 ;;;###autoload
171 (defun magit-no-skip-worktree (file)
172 "Call \"git update-index --no-skip-worktree -- FILE\"."
173 (interactive
174 (list (magit-read-file-choice "Do not skip worktree for"
175 (magit-skip-worktree-files))))
176 (magit-with-toplevel
177 (magit-run-git "update-index" "--no-skip-worktree" "--" file)))
178
179 ;;; Assume Unchanged Commands
180
181 ;;;###autoload
182 (defun magit-assume-unchanged (file)
183 "Call \"git update-index --assume-unchanged -- FILE\"."
184 (interactive
185 (list (magit-read-file-choice "Assume file to be unchanged"
186 (cl-set-difference
187 (magit-list-files)
188 (magit-assume-unchanged-files)
189 :test #'equal))))
190 (magit-with-toplevel
191 (magit-run-git "update-index" "--assume-unchanged" "--" file)))
192
193 ;;;###autoload
194 (defun magit-no-assume-unchanged (file)
195 "Call \"git update-index --no-assume-unchanged -- FILE\"."
196 (interactive
197 (list (magit-read-file-choice "Do not assume file to be unchanged"
198 (magit-assume-unchanged-files))))
199 (magit-with-toplevel
200 (magit-run-git "update-index" "--no-assume-unchanged" "--" file)))
201
202 ;;; _
203 (provide 'magit-gitignore)
204 ;; Local Variables:
205 ;; read-symbol-shorthands: (
206 ;; ("and$" . "cond-let--and$")
207 ;; ("thread$" . "cond-let--thread$")
208 ;; ("when$" . "cond-let--when$")
209 ;; ("and-let*" . "cond-let--and-let*")
210 ;; ("and-let" . "cond-let--and-let")
211 ;; ("if-let*" . "cond-let--if-let*")
212 ;; ("if-let" . "cond-let--if-let")
213 ;; ("when-let*" . "cond-let--when-let*")
214 ;; ("when-let" . "cond-let--when-let")
215 ;; ("while-let*" . "cond-let--while-let*")
216 ;; ("while-let" . "cond-let--while-let")
217 ;; ("match-string" . "match-string")
218 ;; ("match-str" . "match-string-no-properties"))
219 ;; End:
220 ;;; magit-gitignore.el ends here