config

Personal configuration.
git clone git://code.dwrz.net/config
Log | Files | Refs

magit-files.el (22789B)


      1 ;;; magit-files.el --- Finding files  -*- lexical-binding:t -*-
      2 
      3 ;; Copyright (C) 2008-2024 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 support for finding blobs, staged files,
     26 ;; and Git configuration files.  It also implements modes useful in
     27 ;; buffers visiting files and blobs, and the commands used by those
     28 ;; modes.
     29 
     30 ;;; Code:
     31 
     32 (require 'magit)
     33 
     34 ;;; Find Blob
     35 
     36 (defvar magit-find-file-hook nil)
     37 (add-hook 'magit-find-file-hook #'magit-blob-mode)
     38 
     39 ;;;###autoload
     40 (defun magit-find-file (rev file)
     41   "View FILE from REV.
     42 Switch to a buffer visiting blob REV:FILE, creating one if none
     43 already exists.  If prior to calling this command the current
     44 buffer and/or cursor position is about the same file, then go
     45 to the line and column corresponding to that location."
     46   (interactive (magit-find-file-read-args "Find file"))
     47   (magit-find-file--internal rev file #'pop-to-buffer-same-window))
     48 
     49 ;;;###autoload
     50 (defun magit-find-file-other-window (rev file)
     51   "View FILE from REV, in another window.
     52 Switch to a buffer visiting blob REV:FILE, creating one if none
     53 already exists.  If prior to calling this command the current
     54 buffer and/or cursor position is about the same file, then go to
     55 the line and column corresponding to that location."
     56   (interactive (magit-find-file-read-args "Find file in other window"))
     57   (magit-find-file--internal rev file #'switch-to-buffer-other-window))
     58 
     59 ;;;###autoload
     60 (defun magit-find-file-other-frame (rev file)
     61   "View FILE from REV, in another frame.
     62 Switch to a buffer visiting blob REV:FILE, creating one if none
     63 already exists.  If prior to calling this command the current
     64 buffer and/or cursor position is about the same file, then go to
     65 the line and column corresponding to that location."
     66   (interactive (magit-find-file-read-args "Find file in other frame"))
     67   (magit-find-file--internal rev file #'switch-to-buffer-other-frame))
     68 
     69 (defun magit-find-file-read-args (prompt)
     70   (let ((pseudo-revs '("{worktree}" "{index}")))
     71     (if-let ((rev (magit-completing-read "Find file from revision"
     72                                          (append pseudo-revs
     73                                                  (magit-list-refnames nil t))
     74                                          nil nil nil 'magit-revision-history
     75                                          (or (magit-branch-or-commit-at-point)
     76                                              (magit-get-current-branch)))))
     77         (list rev (magit-read-file-from-rev (if (member rev pseudo-revs)
     78                                                 "HEAD"
     79                                               rev)
     80                                             prompt))
     81       (user-error "Nothing selected"))))
     82 
     83 (defun magit-find-file--internal (rev file fn)
     84   (let ((buf (magit-find-file-noselect rev file))
     85         line col)
     86     (when-let ((visited-file (magit-file-relative-name)))
     87       (setq line (line-number-at-pos))
     88       (setq col (current-column))
     89       (cond
     90        ((not (equal visited-file file)))
     91        ((equal magit-buffer-revision rev))
     92        ((equal rev "{worktree}")
     93         (setq line (magit-diff-visit--offset file magit-buffer-revision line)))
     94        ((equal rev "{index}")
     95         (setq line (magit-diff-visit--offset file nil line)))
     96        (magit-buffer-revision
     97         (setq line (magit-diff-visit--offset
     98                     file (concat magit-buffer-revision ".." rev) line)))
     99        (t
    100         (setq line (magit-diff-visit--offset file (list "-R" rev) line)))))
    101     (funcall fn buf)
    102     (when line
    103       (with-current-buffer buf
    104         (widen)
    105         (goto-char (point-min))
    106         (forward-line (1- line))
    107         (move-to-column col)))
    108     buf))
    109 
    110 (defun magit-find-file-noselect (rev file)
    111   "Read FILE from REV into a buffer and return the buffer.
    112 REV is a revision or one of \"{worktree}\" or \"{index}\".
    113 FILE must be relative to the top directory of the repository."
    114   (magit-find-file-noselect-1 rev file))
    115 
    116 (defun magit-find-file-noselect-1 (rev file &optional revert)
    117   "Read FILE from REV into a buffer and return the buffer.
    118 REV is a revision or one of \"{worktree}\" or \"{index}\".
    119 FILE must be relative to the top directory of the repository.
    120 Non-nil REVERT means to revert the buffer.  If `ask-revert',
    121 then only after asking.  A non-nil value for REVERT is ignored if REV is
    122 \"{worktree}\"."
    123   (if (equal rev "{worktree}")
    124       (find-file-noselect (expand-file-name file (magit-toplevel)))
    125     (let ((topdir (magit-toplevel)))
    126       (when (file-name-absolute-p file)
    127         (setq file (file-relative-name file topdir)))
    128       (with-current-buffer (magit-get-revision-buffer-create rev file)
    129         (when (or (not magit-buffer-file-name)
    130                   (if (eq revert 'ask-revert)
    131                       (y-or-n-p (format "%s already exists; revert it? "
    132                                         (buffer-name))))
    133                   revert)
    134           (setq magit-buffer-revision
    135                 (if (equal rev "{index}")
    136                     "{index}"
    137                   (magit-rev-format "%H" rev)))
    138           (setq magit-buffer-refname rev)
    139           (setq magit-buffer-file-name (expand-file-name file topdir))
    140           (setq default-directory
    141                 (let ((dir (file-name-directory magit-buffer-file-name)))
    142                   (if (file-exists-p dir) dir topdir)))
    143           (setq-local revert-buffer-function #'magit-revert-rev-file-buffer)
    144           (revert-buffer t t)
    145           (run-hooks (if (equal rev "{index}")
    146                          'magit-find-index-hook
    147                        'magit-find-file-hook)))
    148         (current-buffer)))))
    149 
    150 (defun magit-get-revision-buffer-create (rev file)
    151   (magit-get-revision-buffer rev file t))
    152 
    153 (defun magit-get-revision-buffer (rev file &optional create)
    154   (funcall (if create #'get-buffer-create #'get-buffer)
    155            (format "%s.~%s~" file (subst-char-in-string ?/ ?_ rev))))
    156 
    157 (defun magit-revert-rev-file-buffer (_ignore-auto noconfirm)
    158   (when (or noconfirm
    159             (and (not (buffer-modified-p))
    160                  (catch 'found
    161                    (dolist (regexp revert-without-query)
    162                      (when (string-match regexp magit-buffer-file-name)
    163                        (throw 'found t)))))
    164             (yes-or-no-p (format "Revert buffer from Git %s? "
    165                                  (if (equal magit-buffer-refname "{index}")
    166                                      "index"
    167                                    (concat "revision " magit-buffer-refname)))))
    168     (let* ((inhibit-read-only t)
    169            (default-directory (magit-toplevel))
    170            (file (file-relative-name magit-buffer-file-name))
    171            (coding-system-for-read (or coding-system-for-read 'undecided)))
    172       (erase-buffer)
    173       (magit-git-insert "cat-file" "-p"
    174                         (if (equal magit-buffer-refname "{index}")
    175                             (concat ":" file)
    176                           (concat magit-buffer-refname ":" file)))
    177       (setq buffer-file-coding-system last-coding-system-used))
    178     (let ((buffer-file-name magit-buffer-file-name)
    179           (after-change-major-mode-hook
    180            (remq 'global-diff-hl-mode-enable-in-buffers
    181                  after-change-major-mode-hook)))
    182       (normal-mode t))
    183     (setq buffer-read-only t)
    184     (set-buffer-modified-p nil)
    185     (goto-char (point-min))))
    186 
    187 (define-advice lsp (:around (fn &rest args) magit-find-file)
    188   "Do nothing when visiting blob using `magit-find-file' and similar.
    189 See also https://github.com/doomemacs/doomemacs/pull/6309."
    190   (unless magit-buffer-revision
    191     (apply fn args)))
    192 
    193 ;;; Find Index
    194 
    195 (defvar magit-find-index-hook nil)
    196 
    197 (defun magit-find-file-index-noselect (file &optional revert)
    198   "Read FILE from the index into a buffer and return the buffer.
    199 FILE must to be relative to the top directory of the repository."
    200   (magit-find-file-noselect-1 "{index}" file (or revert 'ask-revert)))
    201 
    202 (defun magit-update-index ()
    203   "Update the index with the contents of the current buffer.
    204 The current buffer has to be visiting a file in the index, which
    205 is done using `magit-find-index-noselect'."
    206   (interactive)
    207   (let ((file (magit-file-relative-name)))
    208     (unless (equal magit-buffer-refname "{index}")
    209       (user-error "%s isn't visiting the index" file))
    210     (if (y-or-n-p (format "Update index with contents of %s" (buffer-name)))
    211         (let ((index (make-temp-name
    212                       (expand-file-name "magit-update-index-" (magit-gitdir))))
    213               (buffer (current-buffer)))
    214           (when magit-wip-before-change-mode
    215             (magit-wip-commit-before-change (list file) " before un-/stage"))
    216           (unwind-protect
    217               (progn
    218                 (let ((coding-system-for-write buffer-file-coding-system))
    219                   (with-temp-file index
    220                     (insert-buffer-substring buffer)))
    221                 (magit-with-toplevel
    222                   (magit-call-git
    223                    "update-index" "--cacheinfo"
    224                    (substring (magit-git-string "ls-files" "-s" file)
    225                               0 6)
    226                    (magit-git-string "hash-object" "-t" "blob" "-w"
    227                                      (concat "--path=" file)
    228                                      "--" (magit-convert-filename-for-git index))
    229                    file)))
    230             (ignore-errors (delete-file index)))
    231           (set-buffer-modified-p nil)
    232           (when magit-wip-after-apply-mode
    233             (magit-wip-commit-after-apply (list file) " after un-/stage")))
    234       (message "Abort")))
    235   (when-let ((buffer (magit-get-mode-buffer 'magit-status-mode)))
    236     (with-current-buffer buffer
    237       (magit-refresh)))
    238   t)
    239 
    240 ;;; Find Config File
    241 
    242 (defun magit-find-git-config-file (filename &optional wildcards)
    243   "Edit a file located in the current repository's git directory.
    244 
    245 When \".git\", located at the root of the working tree, is a
    246 regular file, then that makes it cumbersome to open a file
    247 located in the actual git directory.
    248 
    249 This command is like `find-file', except that it temporarily
    250 binds `default-directory' to the actual git directory, while
    251 reading the FILENAME."
    252   (interactive
    253    (let ((default-directory (magit-gitdir)))
    254      (find-file-read-args "Find file: "
    255                           (confirm-nonexistent-file-or-buffer))))
    256   (find-file filename wildcards))
    257 
    258 (defun magit-find-git-config-file-other-window (filename &optional wildcards)
    259   "Edit a file located in the current repo's git directory, in another window.
    260 
    261 When \".git\", located at the root of the working tree, is a
    262 regular file, then that makes it cumbersome to open a file
    263 located in the actual git directory.
    264 
    265 This command is like `find-file-other-window', except that it
    266 temporarily binds `default-directory' to the actual git
    267 directory, while reading the FILENAME."
    268   (interactive
    269    (let ((default-directory (magit-gitdir)))
    270      (find-file-read-args "Find file in other window: "
    271                           (confirm-nonexistent-file-or-buffer))))
    272   (find-file-other-window filename wildcards))
    273 
    274 (defun magit-find-git-config-file-other-frame (filename &optional wildcards)
    275   "Edit a file located in the current repo's git directory, in another frame.
    276 
    277 When \".git\", located at the root of the working tree, is a
    278 regular file, then that makes it cumbersome to open a file
    279 located in the actual git directory.
    280 
    281 This command is like `find-file-other-frame', except that it
    282 temporarily binds `default-directory' to the actual git
    283 directory, while reading the FILENAME."
    284   (interactive
    285    (let ((default-directory (magit-gitdir)))
    286      (find-file-read-args "Find file in other frame: "
    287                           (confirm-nonexistent-file-or-buffer))))
    288   (find-file-other-frame filename wildcards))
    289 
    290 ;;; File Dispatch
    291 
    292 ;;;###autoload (autoload 'magit-file-dispatch "magit" nil t)
    293 (transient-define-prefix magit-file-dispatch ()
    294   "Invoke a Magit command that acts on the visited file.
    295 When invoked outside a file-visiting buffer, then fall back
    296 to `magit-dispatch'."
    297   :info-manual "(magit) Minor Mode for Buffers Visiting Files"
    298   [:if magit-file-relative-name
    299    ["File actions"
    300     ("  s" "Stage"    magit-stage-buffer-file)
    301     ("  u" "Unstage"  magit-unstage-buffer-file)
    302     (", x" "Untrack"  magit-file-untrack)
    303     (", r" "Rename"   magit-file-rename)
    304     (", k" "Delete"   magit-file-delete)
    305     (", c" "Checkout" magit-file-checkout)]
    306    ["Inspect"
    307     ("D" "Diff..."    magit-diff)
    308     ("d" "Diff"       magit-diff-buffer-file)]
    309    [""
    310     ("L" "Log..."     magit-log)
    311     ("l" "Log"        magit-log-buffer-file)
    312     ("t" "Trace"      magit-log-trace-definition)
    313     (7 "M" "Merged"   magit-log-merged)]
    314    [""
    315     ("B" "Blame..."   magit-blame)
    316     ("b" "Blame"      magit-blame-addition)
    317     ("r" "...removal" magit-blame-removal)
    318     ("f" "...reverse" magit-blame-reverse)
    319     ("m" "Blame echo" magit-blame-echo)
    320     ("q" "Quit blame" magit-blame-quit)]
    321    ["Navigate"
    322     ("p" "Prev blob"   magit-blob-previous)
    323     ("n" "Next blob"   magit-blob-next)
    324     ("v" "Goto blob"   magit-find-file)
    325     ("V" "Goto file"   magit-blob-visit-file)
    326     ("g" "Goto status" magit-status-here)
    327     ("G" "Goto magit"  magit-display-repository-buffer)]
    328    ["More actions"
    329     ("c" "Commit"     magit-commit)
    330     ("e" "Edit line"  magit-edit-line-commit)]]
    331   [:if-not magit-file-relative-name
    332    ["File actions"
    333     ("s" "Stage"    magit-stage-file)
    334     ("u" "Unstage"  magit-unstage-file)
    335     ("x" "Untrack"  magit-file-untrack)
    336     ("r" "Rename"   magit-file-rename)
    337     ("k" "Delete"   magit-file-delete)
    338     ("c" "Checkout" magit-file-checkout)]
    339    ["Navigate"
    340     ("g" "Goto status" magit-status-here :if-not-mode magit-status-mode)
    341     ("G" "Goto magit"  magit-display-repository-buffer)]])
    342 
    343 ;;; Blob Mode
    344 
    345 (defvar-keymap magit-blob-mode-map
    346   :doc "Keymap for `magit-blob-mode'."
    347   "p" #'magit-blob-previous
    348   "n" #'magit-blob-next
    349   "b" #'magit-blame-addition
    350   "r" #'magit-blame-removal
    351   "f" #'magit-blame-reverse
    352   "q" #'magit-kill-this-buffer)
    353 
    354 (define-minor-mode magit-blob-mode
    355   "Enable some Magit features in blob-visiting buffers.
    356 
    357 Currently this only adds the following key bindings.
    358 \n\\{magit-blob-mode-map}"
    359   :package-version '(magit . "2.3.0"))
    360 
    361 (defun magit-blob-next ()
    362   "Visit the next blob which modified the current file."
    363   (interactive)
    364   (if magit-buffer-file-name
    365       (magit-blob-visit (or (magit-blob-successor magit-buffer-revision
    366                                                   magit-buffer-file-name)
    367                             magit-buffer-file-name))
    368     (if (buffer-file-name (buffer-base-buffer))
    369         (user-error "You have reached the end of time")
    370       (user-error "Buffer isn't visiting a file or blob"))))
    371 
    372 (defun magit-blob-previous ()
    373   "Visit the previous blob which modified the current file."
    374   (interactive)
    375   (if-let ((file (or magit-buffer-file-name
    376                      (buffer-file-name (buffer-base-buffer)))))
    377       (if-let ((ancestor (magit-blob-ancestor magit-buffer-revision file)))
    378           (magit-blob-visit ancestor)
    379         (user-error "You have reached the beginning of time"))
    380     (user-error "Buffer isn't visiting a file or blob")))
    381 
    382 ;;;###autoload
    383 (defun magit-blob-visit-file ()
    384   "View the file from the worktree corresponding to the current blob.
    385 When visiting a blob or the version from the index, then go to
    386 the same location in the respective file in the working tree."
    387   (interactive)
    388   (if-let ((file (magit-file-relative-name)))
    389       (magit-find-file--internal "{worktree}" file #'pop-to-buffer-same-window)
    390     (user-error "Not visiting a blob")))
    391 
    392 (defun magit-blob-visit (blob-or-file)
    393   (if (stringp blob-or-file)
    394       (find-file blob-or-file)
    395     (pcase-let ((`(,rev ,file) blob-or-file))
    396       (magit-find-file rev file)
    397       (apply #'message "%s (%s %s ago)"
    398              (magit-rev-format "%s" rev)
    399              (magit--age (magit-rev-format "%ct" rev))))))
    400 
    401 (defun magit-blob-ancestor (rev file)
    402   (let ((lines (magit-with-toplevel
    403                  (magit-git-lines "log" "-2" "--format=%H" "--name-only"
    404                                   "--follow" (or rev "HEAD") "--" file))))
    405     (if rev (cddr lines) (butlast lines 2))))
    406 
    407 (defun magit-blob-successor (rev file)
    408   (let ((lines (magit-with-toplevel
    409                  (magit-git-lines "log" "--format=%H" "--name-only" "--follow"
    410                                   "HEAD" "--" file))))
    411     (catch 'found
    412       (while lines
    413         (if (equal (nth 2 lines) rev)
    414             (throw 'found (list (nth 0 lines) (nth 1 lines)))
    415           (setq lines (nthcdr 2 lines)))))))
    416 
    417 ;;; File Commands
    418 
    419 (defun magit-file-rename (file newname)
    420   "Rename or move FILE to NEWNAME.
    421 NEWNAME may be a file or directory name.  If FILE isn't tracked in
    422 Git, fallback to using `rename-file'."
    423   (interactive
    424    (let* ((file (magit-read-file "Rename file"))
    425           (path (expand-file-name file (magit-toplevel))))
    426      (list path (expand-file-name
    427                  (read-file-name (format "Move %s to destination: " file)
    428                                  (file-name-directory path))))))
    429   (let ((oldbuf (get-file-buffer file))
    430         (dstdir (file-name-directory newname))
    431         (dstfile (if (directory-name-p newname)
    432                      (concat newname (file-name-nondirectory file))
    433                    newname)))
    434     (when (and oldbuf (buffer-modified-p oldbuf))
    435       (user-error "Save %s before moving it" file))
    436     (when (file-exists-p dstfile)
    437       (user-error "%s already exists" dstfile))
    438     (unless (file-exists-p dstdir)
    439       (user-error "Destination directory %s does not exist" dstdir))
    440     (if (magit-file-tracked-p file)
    441         (magit-call-git "mv"
    442                         (magit-convert-filename-for-git file)
    443                         (magit-convert-filename-for-git newname))
    444       (rename-file file newname current-prefix-arg))
    445     (when oldbuf
    446       (with-current-buffer oldbuf
    447         (let ((buffer-read-only buffer-read-only))
    448           (set-visited-file-name dstfile nil t))
    449         (if (fboundp 'vc-refresh-state)
    450             (vc-refresh-state)
    451           (with-no-warnings
    452             (vc-find-file-hook))))))
    453   (magit-refresh))
    454 
    455 (defun magit-file-untrack (files &optional force)
    456   "Untrack the selected FILES or one file read in the minibuffer.
    457 
    458 With a prefix argument FORCE do so even when the files have
    459 staged as well as unstaged changes."
    460   (interactive (list (or (if-let ((files (magit-region-values 'file t)))
    461                              (if (magit-file-tracked-p (car files))
    462                                  (magit-confirm-files 'untrack files "Untrack")
    463                                (user-error "Already untracked"))
    464                            (list (magit-read-tracked-file "Untrack file"))))
    465                      current-prefix-arg))
    466   (magit-with-toplevel
    467     (magit-run-git "rm" "--cached" (and force "--force") "--" files)))
    468 
    469 (defun magit-file-delete (files &optional force)
    470   "Delete the selected FILES or one file read in the minibuffer.
    471 
    472 With a prefix argument FORCE do so even when the files have
    473 uncommitted changes.  When the files aren't being tracked in
    474 Git, then fallback to using `delete-file'."
    475   (interactive (list (if-let ((files (magit-region-values 'file t)))
    476                          (magit-confirm-files 'delete files "Delete")
    477                        (list (magit-read-file "Delete file")))
    478                      current-prefix-arg))
    479   (if (magit-file-tracked-p (car files))
    480       (magit-call-git "rm" (and force "--force") "--" files)
    481     (let ((topdir (magit-toplevel)))
    482       (dolist (file files)
    483         (delete-file (expand-file-name file topdir) t))))
    484   (magit-refresh))
    485 
    486 ;;;###autoload
    487 (defun magit-file-checkout (rev file)
    488   "Checkout FILE from REV."
    489   (interactive
    490    (let ((rev (magit-read-branch-or-commit
    491                "Checkout from revision" magit-buffer-revision)))
    492      (list rev (magit-read-file-from-rev rev "Checkout file"))))
    493   (magit-with-toplevel
    494     (magit-run-git "checkout" rev "--" file)))
    495 
    496 ;;; Read File
    497 
    498 (defvar magit-read-file-hist nil)
    499 
    500 (defun magit-read-file-from-rev (rev prompt &optional default)
    501   (let ((files (magit-revision-files rev)))
    502     (magit-completing-read
    503      prompt files nil t nil 'magit-read-file-hist
    504      (car (member (or default (magit-current-file)) files)))))
    505 
    506 (defun magit-read-file (prompt &optional tracked-only)
    507   (magit-with-toplevel
    508     (let ((choices (nconc (magit-list-files)
    509                           (and (not tracked-only)
    510                                (magit-untracked-files)))))
    511       (magit-completing-read
    512        prompt choices nil t nil nil
    513        (car (member (or (magit-section-value-if '(file submodule))
    514                         (magit-file-relative-name nil tracked-only))
    515                     choices))))))
    516 
    517 (defun magit-read-tracked-file (prompt)
    518   (magit-read-file prompt t))
    519 
    520 (defun magit-read-unmerged-file (&optional prompt)
    521   (let ((current  (magit-current-file))
    522         (unmerged (magit-unmerged-files)))
    523     (unless unmerged
    524       (user-error "There are no unresolved conflicts"))
    525     (magit-completing-read (or prompt "Resolve file")
    526                            unmerged nil t nil nil
    527                            (car (member current unmerged)))))
    528 
    529 (defun magit-read-file-choice (prompt files &optional error default)
    530   "Read file from FILES.
    531 
    532 If FILES has only one member, return that instead of prompting.
    533 If FILES has no members, give a user error.  ERROR can be given
    534 to provide a more informative error.
    535 
    536 If DEFAULT is non-nil, use this as the default value instead of
    537 `magit-current-file'."
    538   (pcase (length files)
    539     (0 (user-error (or error "No file choices")))
    540     (1 (car files))
    541     (_ (magit-completing-read
    542         prompt files nil t nil 'magit-read-file-hist
    543         (car (member (or default (magit-current-file)) files))))))
    544 
    545 (defun magit-read-changed-file (rev-or-range prompt &optional default)
    546   (magit-read-file-choice
    547    prompt
    548    (magit-changed-files rev-or-range)
    549    default
    550    (concat "No file changed in " rev-or-range)))
    551 
    552 ;;; _
    553 (provide 'magit-files)
    554 ;;; magit-files.el ends here