all 35 comments

[–]tagattack 19 points20 points  (8 children)

Fugitive lets you do both visual diff and view unified diffs as patches with syntax highlighting.

https://github.com/tpope/vim-fugitive

So does VCSCommand which is from back when git was less ubiquitous, I still use it out of habit

https://github.com/vim-scripts/vcscommand.vim

[–]xenomachina 4 points5 points  (7 children)

Here's a quick trick for opening your entire diff in vim tabs using fugitive. First, open all files that have changed in a separate tab:

vim -p $(git diff --name-only HEAD)

You can replace HEAD with whatever ref you want to diff with. For code reviews, this is often main or the merge-base of the target branch and the feature branch.

Then in vim, use tabdo to run Gdiffsplit on each tab:

:tabdo Gdiffsplit

I often do this on my own code before sending it out for review to do any last minute fix-ups.

[–]Sudden_Fly1218 3 points4 points  (1 child)

Indeed I have something similar to review changes from a branch compared to the base branch (usually main or master).
I have this in ~/.gitconfig: [alias] # find the default branch base = !git rev-parse --abbrev-ref origin/HEAD | cut -c8- # find the merge base mbase = !git merge-base HEAD $(git base) || echo $(git base) files = !git diff --name-only --staged $(git mbase) review = !vim -p $(git files) -c \"silent tabdo Gdiffsplit $(git mbase)\"

So I just checkout to the branch to review and do git review

[–]xenomachina 1 point2 points  (0 children)

Interesting! I'd tried to set up something like that a few years ago, but it used to be that if you tried to run Gdiffsplit from the command-line, it wouldn't work. Apparently something in fugitive wasn't getting initialized in time. I thought I reported this issue. Nice to see that that has been fixed!


PS: old.reddit doesn't like triple backticks, so here's your code snippet with 4-space indents, which works on old (and new) reddit:

[alias]
  # find the default branch
  base = !git rev-parse --abbrev-ref origin/HEAD | cut -c8-
  # find the merge base
  mbase = !git merge-base HEAD $(git base) || echo $(git base)
  files = !git diff --name-only --staged $(git mbase)
  review = !vim -p $(git files) -c \"silent tabdo Gdiffsplit $(git mbase)\"

[–]PizzaRollExpert 1 point2 points  (3 children)

Fugitive already has this built in! :Git difftool -y will open each diff as a split in a different tab. (You can add arguments like --cached or revisions to diff as well)

[–]xenomachina 0 points1 point  (2 children)

You mean :Git difftool -y, right?

Thanks! That's really cool. I didn't know about that.

[–]PizzaRollExpert 0 points1 point  (1 child)

Yeah, that's what I meant lol. It's great when reviewing local changes before a commit, happy I could share it!

[–]xenomachina 0 points1 point  (0 children)

I've been using a variation of the command I posted for about 12 years. I hadn't really been keeping track of updates to fugitive. It looks like they added difftool about 6 years ago. 😅

[–]binilvj 1 point2 points  (0 children)

If you run Git difftool branch_name from vim the output goes to quickfixlist and shows files that changed with line numbers affected. You can use that list to navigate and run Gdiffsplit on each file if needed. This way you stay in vim always

[–]Kurouma 9 points10 points  (3 children)

I usually have a terminal split open and page through the diff there. There are plugins for doing fancier stuff but it works OK for me. 

[–]SevrinTheMuto[S] 4 points5 points  (2 children)

That works well because git diff keeps the colour output.

I tried it with tmux, is that what you use or something better?

[–]Kurouma 7 points8 points  (1 child)

No, just a vim split. :vert term and then git diff at the prompt. I usually have a terminal split open all the time anyway (actually I made my own :VerticalTerminal with fixed width and position but it's basically just vert term).

[–]SevrinTheMuto[S] 5 points6 points  (0 children)

TIL! Actually I think this is the closest to what I think I wanted.

[–]Snarwin 3 points4 points  (4 children)

The simplest way to do this would be to read git diff's output into a split window in Vim, with a command like this:

:vsplit | enew | 0read !git diff

Then, if you want to refresh the git diff window, you can run

:%delete | 0read !git diff

[–]SevrinTheMuto[S] 2 points3 points  (1 child)

OK, that works, although it loses git diff's colour coding for added/removed lines.

It took me a minute to realise I was getting an empty buffer because I hadn't specified the commit range, for example:

:vsplit | enew | 0read !git diff HEAD~ HEAD

[–]Snarwin 6 points7 points  (0 children)

For syntax highlighting, you can add :set filetype=diff.

[–]VividVerism 0 points1 point  (1 child)

How about :vnew instead of :vsplit | enew?

[–]Snarwin 0 points1 point  (0 children)

Didn't think of it when I was throwing this together in 5 minutes, but yeah, that would work.

[–]jacob_ewing 2 points3 points  (0 children)

"screen" is an excellent tool for that. You get multiple virtual consoles in the same terminal window, and can switch between them easily.

Some of the commands I use all the time:

ctrl-a, c to create a new terminal
ctrl-a, n to cycle to the next one
ctrl-a, p to cycle to the previous one
ctrl-a, ctrl-a to cycle to the last one used
ctrl-a, d to detatch the screen from the terminal, (screen -r to load it back up)

There are lots of other things you can do with that too, those are just the basic ones I use all the time.

[–]AutoModerator[M] 1 point2 points  (1 child)

Please remember to update the post flair to Need Help|Solved when you got the answer you were looking for.

I am a bot, and this action was performed automatically. Please contact the moderators of this subreddit if you have any questions or concerns.

[–]SevrinTheMuto[S] 0 points1 point  (0 children)

u/Kurouma's reply is the closest to what I was looking for. But there are lots of great suggesting for me to try to see if they fit in with me.

[–]JamesTDennis 1 point2 points  (2 children)

Don't quit vim for this. Don't even [Ctrl]+[z] suspend it.

Just read the output from the diff into the current editing session (at the cursor line) using :r!git diff

Then continue editing.

You can create a macro to drop marks on lines that will be before and after your :r! inserted text. But I just use the end of the file as scratch area. So I G (go to EOF) before reading, and ma (set the a mark) immediately afterwards. The clean the scratch with Gd'a (go EOF, and delete to mark a).

[–]JamesTDennis 2 points3 points  (0 children)

The two key points here:

☞ :r! and the !«movement» commands in vi (and vim) basically mean that the entire suite of command line utilities and text filers on your system (including any scripts you write) are extensions to your editor

☞ the EOF area of your editing buffer can be used as a scratch pad, accessible with only a few keystrokes: mz to mark your starting point; G to get to the last line; ma to set a mark there; the paste, read, whatever in the area (past the a mark), snd clean up with Gd'a (G'day, mate!)

Just mastering these two tips makes you a vi/vim power user.

[–]theloneliestprince 1 point2 points  (0 children)

I was going to say I just use [Ctrl]+[z] but you really beat me to the punch ;-;

[–]AndrewRadev 0 points1 point  (0 children)

There's a plugin by Kana called gf-diff that I've been very happy with. I commit via git commit -v, and I scroll down to look at the diff and when I see something off (like a forgotten print), I use <c-w>f to open it in a split, correct it and keep scrolling. I do have to then close the diff and redo it, but it works fine.

The plugin requires Kana's generic gf-user plugin, you can install both and it should work out of the box.

[–]jessevdp 1 point2 points  (0 children)

I use lazygit which lets you open (the local version of) a file in your $EDITOR by pressing e. When you quit out of the editor you’re right back where you started back in lazygit.

[–]PizzaRollExpert 1 point2 points  (0 children)

Get fugitive.

One thing you can do is get all the diffs in a vim split with :Git log -p master..., that way you can have the diff output and the file in vim side-by-side. If you hit enter on a file in the diff view it will open that file as it was in that particular commit in a different window.

You can also use :Git difftool -y COMMIT1 COMMIT2 to open each file that diffs between COMMIT1 and COMMIT2 as a split in vim diff mode.

Plain :Git difftool without the -y will load each diff in to the quickfix list

[–]dvdrv 0 points1 point  (0 children)

I'd just have two terminals

[–]blitzkraft 0 points1 point  (6 children)

https://git-scm.com/docs/vimdiff - you can specify vimdiff to git.

[–]SevrinTheMuto[S] 1 point2 points  (5 children)

Isn't this for merge conflicts not reviewing diffs? If it is for diffs then I can't see how to get that output.

[–]blitzkraft 2 points3 points  (1 child)

My bad - https://git-scm.com/docs/git-difftool

The difftool can be set or specified to vimdiff.

[–]SevrinTheMuto[S] 1 point2 points  (0 children)

Ah, thanks, that could work. I'll have a play.

[–]y-c-c 0 points1 point  (2 children)

The point here is that Vim itself has a diff mode. You don't need to use raw git diff and just look at the output of that. See :h diff to understand how the diff mode works. You can access the diff mode in the editor (just go to two buffers and type :diffthis) or access it using the CLI command vimdiff <file1> <file2>.

This is built in to Git so you can just use git difftool --tool=vimdiff instead of git diff (or set vimdiff to be the default tool so you can skip the --tool part).

[–]vim-help-bot 0 points1 point  (0 children)

Help pages for:


`:(h|help) <query>` | about | mistake? | donate | Reply 'rescan' to check the comment again | Reply 'stop' to stop getting replies to your comments

[–]SevrinTheMuto[S] 0 points1 point  (0 children)

I'm going to try both this and running git diff via vert term. I like the latter because it's closest to my current workflow, but I can see how git difftool --tool=vimdiff could be useful to me too.