config

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

magit-files.el (23150B)


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