Using emacs (and magit?) to visit a file in given commit/branch/etc

╄→гoц情女王★ 提交于 2019-12-02 16:41:10

The canonical way to do that in Emacs is to use VC: C-x v ~ from the file's buffer will ask you for a revision and then show that file as it was at that revision. It should work for any control system supported by VC, such as Git, Bzr, ...

  • C-xvl to view the file's history.
  • n and p to move between commits.
  • f to visit the file as of the commit at point.

That's bound to log-view-find-revision, and if we look at the code we see the critical bit is:

(switch-to-buffer (vc-find-revision file revision)))

So we could wrap that in a custom function like so:

(defun my-vc-visit-file-revision (file revision)
  "Visit FILE as it was at REVISION."
  (interactive
   (list (expand-file-name
          (read-file-name (if (buffer-file-name)
                              (format "File (%s): " (file-name-nondirectory
                                                     (buffer-file-name)))
                            "File: ")))
         (read-string "Revision: ")))
  (require 'vc)
  (switch-to-buffer
   (vc-find-revision file revision)))

Edit: Stefan has provided a better answer, but if you liked being able to select the file as well as the revision, here's a version of my function which maintains the interactive file selection, but uses the code from vc-revision-other-window for the revision handling.

I concluded that using other-window by default does indeed make more sense, so I've done the same here -- unless you provide a prefix argument in which case it uses the current window.

(defun my-vc-visit-file-revision (file rev)
  "Visit revision REV of FILE in another window.
With prefix argument, uses the current window instead.
If the current file is named `F', the revision is named `F.~REV~'.
If `F.~REV~' already exists, use it instead of checking it out again."
  ;; based on `vc-revision-other-window'.
  (interactive
   (let ((file (expand-file-name
                (read-file-name
                 (if (buffer-file-name)
                     (format "File (%s): " (file-name-nondirectory
                                            (buffer-file-name)))
                   "File: ")))))
     (require 'vc)
     (unless (vc-backend file)
       (error "File %s is not under version control" file))
     (list file (vc-read-revision
                 "Revision to visit (default is working revision): "
                 (list file)))))
  (require 'vc)
  (unless (vc-backend file)
    (error "File %s is not under version control" file))
  (let ((revision (if (string-equal rev "")
                      (vc-working-revision file)
                    rev))
        (visit (if current-prefix-arg
                   'switch-to-buffer
                 'switch-to-buffer-other-window)))
    (funcall visit (vc-find-revision file revision))))

There's a package called git-timemachine that makes the process of viewing previous versions of a file almost completely seamless; see the link for installation instructions and a demo. (If you are already using MELPA, just do M-x package-install RET git-timemachine RET).

The way it works is, you call M-x git-timemachine RET from a buffer visiting a tracked file. Then you can:

  • p Visit previous historic version
  • n Visit next historic version
  • w Copy the abbreviated hash of the current historic version
  • W Copy the full hash of the current historic version
  • q Exit the time machine.

Note that if you know the hash of the commit you want to visit, the custom command from @phils' solution will serve you better for that specific use case. But for navigating between different versions of a file I find that using git-timemachine is even easier than using the functionality that VC provides.

You can of course bind git-timemachine to a key binding of your choice.

If you are viewing the commit in magit you can just press Enter on the file or part of the file you are interested in.

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!