config

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

magit-files.el (22858B)


      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 (defun magit--lsp--disable-when-visiting-blob (fn &rest args)
    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 (advice-add 'lsp :around #'magit--lsp--disable-when-visiting-blob)
    194 
    195 ;;; Find Index
    196 
    197 (defvar magit-find-index-hook nil)
    198 
    199 (defun magit-find-file-index-noselect (file &optional revert)
    200   "Read FILE from the index into a buffer and return the buffer.
    201 FILE must to be relative to the top directory of the repository."
    202   (magit-find-file-noselect-1 "{index}" file (or revert 'ask-revert)))
    203 
    204 (defun magit-update-index ()
    205   "Update the index with the contents of the current buffer.
    206 The current buffer has to be visiting a file in the index, which
    207 is done using `magit-find-index-noselect'."
    208   (interactive)
    209   (let ((file (magit-file-relative-name)))
    210     (unless (equal magit-buffer-refname "{index}")
    211       (user-error "%s isn't visiting the index" file))
    212     (if (y-or-n-p (format "Update index with contents of %s" (buffer-name)))
    213         (let ((index (make-temp-name
    214                       (expand-file-name "magit-update-index-" (magit-gitdir))))
    215               (buffer (current-buffer)))
    216           (when magit-wip-before-change-mode
    217             (magit-wip-commit-before-change (list file) " before un-/stage"))
    218           (unwind-protect
    219               (progn
    220                 (let ((coding-system-for-write buffer-file-coding-system))
    221                   (with-temp-file index
    222                     (insert-buffer-substring buffer)))
    223                 (magit-with-toplevel
    224                   (magit-call-git
    225                    "update-index" "--cacheinfo"
    226                    (substring (magit-git-string "ls-files" "-s" file)
    227                               0 6)
    228                    (magit-git-string "hash-object" "-t" "blob" "-w"
    229                                      (concat "--path=" file)
    230                                      "--" (magit-convert-filename-for-git index))
    231                    file)))
    232             (ignore-errors (delete-file index)))
    233           (set-buffer-modified-p nil)
    234           (when magit-wip-after-apply-mode
    235             (magit-wip-commit-after-apply (list file) " after un-/stage")))
    236       (message "Abort")))
    237   (when-let ((buffer (magit-get-mode-buffer 'magit-status-mode)))
    238     (with-current-buffer buffer
    239       (magit-refresh)))
    240   t)
    241 
    242 ;;; Find Config File
    243 
    244 (defun magit-find-git-config-file (filename &optional wildcards)
    245   "Edit a file located in the current repository's git directory.
    246 
    247 When \".git\", located at the root of the working tree, is a
    248 regular file, then that makes it cumbersome to open a file
    249 located in the actual git directory.
    250 
    251 This command is like `find-file', except that it temporarily
    252 binds `default-directory' to the actual git directory, while
    253 reading the FILENAME."
    254   (interactive
    255    (let ((default-directory (magit-gitdir)))
    256      (find-file-read-args "Find file: "
    257                           (confirm-nonexistent-file-or-buffer))))
    258   (find-file filename wildcards))
    259 
    260 (defun magit-find-git-config-file-other-window (filename &optional wildcards)
    261   "Edit a file located in the current repo's git directory, in another window.
    262 
    263 When \".git\", located at the root of the working tree, is a
    264 regular file, then that makes it cumbersome to open a file
    265 located in the actual git directory.
    266 
    267 This command is like `find-file-other-window', except that it
    268 temporarily binds `default-directory' to the actual git
    269 directory, while reading the FILENAME."
    270   (interactive
    271    (let ((default-directory (magit-gitdir)))
    272      (find-file-read-args "Find file in other window: "
    273                           (confirm-nonexistent-file-or-buffer))))
    274   (find-file-other-window filename wildcards))
    275 
    276 (defun magit-find-git-config-file-other-frame (filename &optional wildcards)
    277   "Edit a file located in the current repo's git directory, in another frame.
    278 
    279 When \".git\", located at the root of the working tree, is a
    280 regular file, then that makes it cumbersome to open a file
    281 located in the actual git directory.
    282 
    283 This command is like `find-file-other-frame', except that it
    284 temporarily binds `default-directory' to the actual git
    285 directory, while reading the FILENAME."
    286   (interactive
    287    (let ((default-directory (magit-gitdir)))
    288      (find-file-read-args "Find file in other frame: "
    289                           (confirm-nonexistent-file-or-buffer))))
    290   (find-file-other-frame filename wildcards))
    291 
    292 ;;; File Dispatch
    293 
    294 ;;;###autoload (autoload 'magit-file-dispatch "magit" nil t)
    295 (transient-define-prefix magit-file-dispatch ()
    296   "Invoke a Magit command that acts on the visited file.
    297 When invoked outside a file-visiting buffer, then fall back
    298 to `magit-dispatch'."
    299   :info-manual "(magit) Minor Mode for Buffers Visiting Files"
    300   [:if magit-file-relative-name
    301    ["File actions"
    302     ("  s" "Stage"    magit-stage-buffer-file)
    303     ("  u" "Unstage"  magit-unstage-buffer-file)
    304     (", x" "Untrack"  magit-file-untrack)
    305     (", r" "Rename"   magit-file-rename)
    306     (", k" "Delete"   magit-file-delete)
    307     (", c" "Checkout" magit-file-checkout)]
    308    ["Inspect"
    309     ("D" "Diff..."    magit-diff)
    310     ("d" "Diff"       magit-diff-buffer-file)]
    311    [""
    312     ("L" "Log..."     magit-log)
    313     ("l" "Log"        magit-log-buffer-file)
    314     ("t" "Trace"      magit-log-trace-definition)
    315     (7 "M" "Merged"   magit-log-merged)]
    316    [""
    317     ("B" "Blame..."   magit-blame)
    318     ("b" "Blame"      magit-blame-addition)
    319     ("r" "...removal" magit-blame-removal)
    320     ("f" "...reverse" magit-blame-reverse)
    321     ("m" "Blame echo" magit-blame-echo)
    322     ("q" "Quit blame" magit-blame-quit)]
    323    ["Navigate"
    324     ("p" "Prev blob"   magit-blob-previous)
    325     ("n" "Next blob"   magit-blob-next)
    326     ("v" "Goto blob"   magit-find-file)
    327     ("V" "Goto file"   magit-blob-visit-file)
    328     ("g" "Goto status" magit-status-here)
    329     ("G" "Goto magit"  magit-display-repository-buffer)]
    330    ["More actions"
    331     ("c" "Commit"     magit-commit)
    332     ("e" "Edit line"  magit-edit-line-commit)]]
    333   [:if-not magit-file-relative-name
    334    ["File actions"
    335     ("s" "Stage"    magit-stage-file)
    336     ("u" "Unstage"  magit-unstage-file)
    337     ("x" "Untrack"  magit-file-untrack)
    338     ("r" "Rename"   magit-file-rename)
    339     ("k" "Delete"   magit-file-delete)
    340     ("c" "Checkout" magit-file-checkout)]
    341    ["Navigate"
    342     ("g" "Goto status" magit-status-here :if-not-mode magit-status-mode)
    343     ("G" "Goto magit"  magit-display-repository-buffer)]])
    344 
    345 ;;; Blob Mode
    346 
    347 (defvar-keymap magit-blob-mode-map
    348   :doc "Keymap for `magit-blob-mode'."
    349   "p" #'magit-blob-previous
    350   "n" #'magit-blob-next
    351   "b" #'magit-blame-addition
    352   "r" #'magit-blame-removal
    353   "f" #'magit-blame-reverse
    354   "q" #'magit-kill-this-buffer)
    355 
    356 (define-minor-mode magit-blob-mode
    357   "Enable some Magit features in blob-visiting buffers.
    358 
    359 Currently this only adds the following key bindings.
    360 \n\\{magit-blob-mode-map}"
    361   :package-version '(magit . "2.3.0"))
    362 
    363 (defun magit-blob-next ()
    364   "Visit the next blob which modified the current file."
    365   (interactive)
    366   (if magit-buffer-file-name
    367       (magit-blob-visit (or (magit-blob-successor magit-buffer-revision
    368                                                   magit-buffer-file-name)
    369                             magit-buffer-file-name))
    370     (if (buffer-file-name (buffer-base-buffer))
    371         (user-error "You have reached the end of time")
    372       (user-error "Buffer isn't visiting a file or blob"))))
    373 
    374 (defun magit-blob-previous ()
    375   "Visit the previous blob which modified the current file."
    376   (interactive)
    377   (if-let ((file (or magit-buffer-file-name
    378                      (buffer-file-name (buffer-base-buffer)))))
    379       (if-let ((ancestor (magit-blob-ancestor magit-buffer-revision file)))
    380           (magit-blob-visit ancestor)
    381         (user-error "You have reached the beginning of time"))
    382     (user-error "Buffer isn't visiting a file or blob")))
    383 
    384 ;;;###autoload
    385 (defun magit-blob-visit-file ()
    386   "View the file from the worktree corresponding to the current blob.
    387 When visiting a blob or the version from the index, then go to
    388 the same location in the respective file in the working tree."
    389   (interactive)
    390   (if-let ((file (magit-file-relative-name)))
    391       (magit-find-file--internal "{worktree}" file #'pop-to-buffer-same-window)
    392     (user-error "Not visiting a blob")))
    393 
    394 (defun magit-blob-visit (blob-or-file)
    395   (if (stringp blob-or-file)
    396       (find-file blob-or-file)
    397     (pcase-let ((`(,rev ,file) blob-or-file))
    398       (magit-find-file rev file)
    399       (apply #'message "%s (%s %s ago)"
    400              (magit-rev-format "%s" rev)
    401              (magit--age (magit-rev-format "%ct" rev))))))
    402 
    403 (defun magit-blob-ancestor (rev file)
    404   (let ((lines (magit-with-toplevel
    405                  (magit-git-lines "log" "-2" "--format=%H" "--name-only"
    406                                   "--follow" (or rev "HEAD") "--" file))))
    407     (if rev (cddr lines) (butlast lines 2))))
    408 
    409 (defun magit-blob-successor (rev file)
    410   (let ((lines (magit-with-toplevel
    411                  (magit-git-lines "log" "--format=%H" "--name-only" "--follow"
    412                                   "HEAD" "--" file))))
    413     (catch 'found
    414       (while lines
    415         (if (equal (nth 2 lines) rev)
    416             (throw 'found (list (nth 0 lines) (nth 1 lines)))
    417           (setq lines (nthcdr 2 lines)))))))
    418 
    419 ;;; File Commands
    420 
    421 (defun magit-file-rename (file newname)
    422   "Rename or move FILE to NEWNAME.
    423 NEWNAME may be a file or directory name.  If FILE isn't tracked in
    424 Git, fallback to using `rename-file'."
    425   (interactive
    426    (let* ((file (magit-read-file "Rename file"))
    427           (path (expand-file-name file (magit-toplevel))))
    428      (list path (expand-file-name
    429                  (read-file-name (format "Move %s to destination: " file)
    430                                  (file-name-directory path))))))
    431   (let ((oldbuf (get-file-buffer file))
    432         (dstdir (file-name-directory newname))
    433         (dstfile (if (directory-name-p newname)
    434                      (concat newname (file-name-nondirectory file))
    435                    newname)))
    436     (when (and oldbuf (buffer-modified-p oldbuf))
    437       (user-error "Save %s before moving it" file))
    438     (when (file-exists-p dstfile)
    439       (user-error "%s already exists" dstfile))
    440     (unless (file-exists-p dstdir)
    441       (user-error "Destination directory %s does not exist" dstdir))
    442     (if (magit-file-tracked-p file)
    443         (magit-call-git "mv"
    444                         (magit-convert-filename-for-git file)
    445                         (magit-convert-filename-for-git newname))
    446       (rename-file file newname current-prefix-arg))
    447     (when oldbuf
    448       (with-current-buffer oldbuf
    449         (let ((buffer-read-only buffer-read-only))
    450           (set-visited-file-name dstfile nil t))
    451         (if (fboundp 'vc-refresh-state)
    452             (vc-refresh-state)
    453           (with-no-warnings
    454             (vc-find-file-hook))))))
    455   (magit-refresh))
    456 
    457 (defun magit-file-untrack (files &optional force)
    458   "Untrack the selected FILES or one file read in the minibuffer.
    459 
    460 With a prefix argument FORCE do so even when the files have
    461 staged as well as unstaged changes."
    462   (interactive (list (or (if-let ((files (magit-region-values 'file t)))
    463                              (if (magit-file-tracked-p (car files))
    464                                  (magit-confirm-files 'untrack files "Untrack")
    465                                (user-error "Already untracked"))
    466                            (list (magit-read-tracked-file "Untrack file"))))
    467                      current-prefix-arg))
    468   (magit-with-toplevel
    469     (magit-run-git "rm" "--cached" (and force "--force") "--" files)))
    470 
    471 (defun magit-file-delete (files &optional force)
    472   "Delete the selected FILES or one file read in the minibuffer.
    473 
    474 With a prefix argument FORCE do so even when the files have
    475 uncommitted changes.  When the files aren't being tracked in
    476 Git, then fallback to using `delete-file'."
    477   (interactive (list (if-let ((files (magit-region-values 'file t)))
    478                          (magit-confirm-files 'delete files "Delete")
    479                        (list (magit-read-file "Delete file")))
    480                      current-prefix-arg))
    481   (if (magit-file-tracked-p (car files))
    482       (magit-call-git "rm" (and force "--force") "--" files)
    483     (let ((topdir (magit-toplevel)))
    484       (dolist (file files)
    485         (delete-file (expand-file-name file topdir) t))))
    486   (magit-refresh))
    487 
    488 ;;;###autoload
    489 (defun magit-file-checkout (rev file)
    490   "Checkout FILE from REV."
    491   (interactive
    492    (let ((rev (magit-read-branch-or-commit
    493                "Checkout from revision" magit-buffer-revision)))
    494      (list rev (magit-read-file-from-rev rev "Checkout file"))))
    495   (magit-with-toplevel
    496     (magit-run-git "checkout" rev "--" file)))
    497 
    498 ;;; Read File
    499 
    500 (defvar magit-read-file-hist nil)
    501 
    502 (defun magit-read-file-from-rev (rev prompt &optional default)
    503   (let ((files (magit-revision-files rev)))
    504     (magit-completing-read
    505      prompt files nil t nil 'magit-read-file-hist
    506      (car (member (or default (magit-current-file)) files)))))
    507 
    508 (defun magit-read-file (prompt &optional tracked-only)
    509   (magit-with-toplevel
    510     (let ((choices (nconc (magit-list-files)
    511                           (and (not tracked-only)
    512                                (magit-untracked-files)))))
    513       (magit-completing-read
    514        prompt choices nil t nil nil
    515        (car (member (or (magit-section-value-if '(file submodule))
    516                         (magit-file-relative-name nil tracked-only))
    517                     choices))))))
    518 
    519 (defun magit-read-tracked-file (prompt)
    520   (magit-read-file prompt t))
    521 
    522 (defun magit-read-unmerged-file (&optional prompt)
    523   (let ((current  (magit-current-file))
    524         (unmerged (magit-unmerged-files)))
    525     (unless unmerged
    526       (user-error "There are no unresolved conflicts"))
    527     (magit-completing-read (or prompt "Resolve file")
    528                            unmerged nil t nil nil
    529                            (car (member current unmerged)))))
    530 
    531 (defun magit-read-file-choice (prompt files &optional error default)
    532   "Read file from FILES.
    533 
    534 If FILES has only one member, return that instead of prompting.
    535 If FILES has no members, give a user error.  ERROR can be given
    536 to provide a more informative error.
    537 
    538 If DEFAULT is non-nil, use this as the default value instead of
    539 `magit-current-file'."
    540   (pcase (length files)
    541     (0 (user-error (or error "No file choices")))
    542     (1 (car files))
    543     (_ (magit-completing-read
    544         prompt files nil t nil 'magit-read-file-hist
    545         (car (member (or default (magit-current-file)) files))))))
    546 
    547 (defun magit-read-changed-file (rev-or-range prompt &optional default)
    548   (magit-read-file-choice
    549    prompt
    550    (magit-changed-files rev-or-range)
    551    default
    552    (concat "No file changed in " rev-or-range)))
    553 
    554 ;;; _
    555 (provide 'magit-files)
    556 ;;; magit-files.el ends here