17

I love undo-tree (screenshot below). It provides an easy way for navigating through different versions of a file (top window) by letting the user move around the file's history tree in a separate window (bottom window) with simple keys such as n, p and C-b and C-f to switch across descendants of the same parent.

Most notably, undo-tree updates the buffer holding the file with the corresponding version based on the selection in the tree view, automatically.

Of course, magit is phenomenal for git, and it would be amazing if it had a similar ability, i.e. letting users navigate a file's git history interactively while simultaneously updating the buffer holding the file automatically.

Is this at all possible with magit today?

enter image description here

Amelio Vazquez-Reina
  • 5,157
  • 4
  • 32
  • 47
  • Don't know if it's possible with magit but have a lookt at https://github.com/pidu/git-timemachine – clemera Oct 10 '15 at 20:57
  • I'm not sure, but I think `undo-tree` uses a tree (it's in the name :-p), whereas Git history is a [directed acyclic graph](https://en.wikipedia.org/wiki/Directed_acyclic_graph). It might be non-trivial. – suvayu Oct 12 '15 at 06:31
  • 2
    Magit does support something like this, but I am currently changing how the feature is turned on. I'll post an answer when I am done with that. – tarsius Oct 13 '15 at 00:20

1 Answers1

8

Magit recently gained support for something like this, but the feature is still a bit rough around the edges and has to be enabled explicitly.


A similar feature has existed for a long time - when you move from one commit to another in a log buffer, and another window in the same frame is the repository's revision buffer, then that buffer is refreshed to show that commit instead of whatever commit it was previously showing.

Until very recently the revision buffer was created when you first moved from one commit to another. I have changed that behavior and now the revision buffer is only ever updated. If no window in the same frame already displays that buffer, then moving around the log will no longer cause the buffer to be displayed in some new window or an existing window which previously displayed some other buffer.

So now you have to press SPC once to display the revision buffer. Only then will it start being updated when you move around the log buffer.

If you don't want the revision buffer to be updated, then do this:

(remove-hook 'magit-section-movement-hook
             'magit-log-maybe-update-revision-buffer)

If on the other hand you also want the revision buffer to be updated when moving around in the status buffer, then use this:

(add-hook 'magit-section-movement-hook
          'magit-status-maybe-update-revision-buffer)

It is now possible for a blob buffer to be automatically updated in a similar way. First add this:

(add-hook 'magit-section-movement-hook
          'magit-log-maybe-update-blob-buffer)

And maybe also:

(add-hook 'magit-section-movement-hook
          'magit-status-maybe-update-blob-buffer)

Then, inside a log (or status) buffer press RET to display or update the revision buffer, and to also switch to that buffer. Move to the file you are interested in and press RET again. You are now in a "blob-visiting" buffer, which is similar to a file-visiting buffer, except that it does not visit the file in the working tree but the file as it was in some commit.

Switch back to the log (or status) buffer and move to another commit. The blob buffer is being updated.


Note that displaying another blob, does not actually reuse the existing blob buffer. Instead it creates a new buffer which visits that blob and then displays that buffer in the window which previously displayed the other blob buffer. That other buffer is not being deleted, which unfortunately means that quite a few buffers can accumulate in a short period of time. Which would be one of those rough edges, I mentioned earlier. Pressing q in a blob buffer does delete it, so you can clean up by going to the window which was used to display the various blob buffers and keep pressing q until all the buffers have been deleted.

Additionally it is possible to navigate to the next or previous blob from within a blob buffer using p and n. I assume that in most cases you start with a recent blob and then move backward to older blobs using p. If you do that and then want to move in the other direction, I recommend you use q to kill the current buffer and thereby move to the previous buffer i.e. the next blob.

Also note that you don't have to first visit a revision to be able to visit a blob. You could also use M-x magit-find-file RET <revision> RET <file> RET directly, but I find that less convenient.

tarsius
  • 25,298
  • 4
  • 69
  • 109
  • Thank you tarsius. I am working with magit `20151014.231` (the latest as far as I can tell). I tried following the steps you described above. I went to `magit-log-buffer-file` which allows me to navigate the commits up and down with `n` and `p` but when I press `SPC` or `RET` on one of them I get: `Symbol's value as variable is void: magit-blame-mode` – Amelio Vazquez-Reina Oct 14 '15 at 14:35
  • You probably added `magit-blame-maybe-update-revision-buffer` to `magit-section-movement-hook` by mistake. – tarsius Oct 15 '15 at 06:39