175

I use Vim. I open a file. I edit it and I want to see what I've edited before I save it.

How can I do this in Vim?

chaos
  • 122,029
  • 33
  • 303
  • 309
teerapap
  • 5,303
  • 7
  • 33
  • 40

15 Answers15

197
:w !diff % -
chaos
  • 122,029
  • 33
  • 303
  • 309
  • 3
    Only works if diff is installed on your system (read: doesn't work cross-platform) – semperos Apr 18 '11 at 13:41
  • 4
    Is there a way to do this with vimdiff? I tried :w !vimdiff % - but without success. – Joe J Aug 16 '11 at 14:32
  • 23
    Can somebody explain that? I don't understand what's happening. I understand you're shelling out to `diff`. `%` refers to the currently open filepath. Why is all this an argument to the `:w` command? Also, how does `-` get assigned to the contents of the working buffer? Is that automatic in vim, that the contents of the buffer (or maybe a specific range in the buffer) gets assigned to stdin for shell commands? – Nathan Wallace Oct 07 '13 at 15:39
  • 15
    @NathanWallace: It's an argument to `:w` because we're writing the file to the command (on `stdin`). In the command, `-` tells it to read from `stdin`. – chaos Oct 07 '13 at 16:15
  • 1
    @JoeJ: Take a look at my answer - especially the comment. – Tobias Heinicke Mar 12 '14 at 18:35
  • @JoeJ I have added another answer on how to view changes like in vimdiff. – Tobias Heinicke Mar 12 '14 at 19:04
  • I ended up using this `:w !meld % /dev/stdin`. – deft_code Mar 30 '15 at 07:01
  • 20
    Or use `:w !git diff % -` for a colorized version, if you have git installed! – Dergachev Nov 26 '15 at 17:29
  • @Dergachev one more reason I like your command because it uses a pager for longer diffs. – Tim Stewart Jan 27 '17 at 05:04
  • 5
    @Dergachev I get the error `fatal: bad flag '-' used after filename` when I run `:w !git diff % -`. – Grayscale Jul 26 '18 at 03:08
  • `icdiff` is a diff tool on linux with all the nice settings set from start. Also colorizes individual characters when applicable : ) – dekuShrub Apr 15 '19 at 10:10
  • 1
    @Grayscale `:w !git diff --no-index % -` worked for me. But afterwards arrow keys stopped working in vim, I had to do `:!clear` to fix it. – André Chalella Jun 26 '20 at 09:01
152

Because some people asked about an explanation for the command

:w !diff % -

Here is my attempt on writing a more detailed answer:

I am assuming that you are working on a system with cat and echo installed (e.g. almost any GNU/Linux, Mac OS, BSD and other UNIX-like systems).

The above command works as follows:

  1. The syntax for saving a file in vim is:

    :w <filename>
    
  2. The syntax for executing a shell command in vim is:

    :!<command>
    
  3. Inside the shell environment issued by vim % happens to point to the current filename. You can verify this by executing the following:

    :!echo %
    

    This should output the filename (or an error, if vim was run without a filename).

    Using cat we can also output the content of the file:

    :!cat %
    

    This should return the files content in its last saved state or an error if it has never been saved.

  4. The program diff is able to read from standard input (stdin). Its man page states the following:

    [...] If a FILE is '-', read standard input. [...]

  5. Executing the save command without a filename but rather a shell command behind it causes vim to write the files content to stdin of the shell instead of saving it in a physical file. You can verify this by executing

    :w !cat
    

    This should always print the files current content (which would have been written to a file instead).

Putting it together (or tl;dr): The file is "saved" to stdin, diff is run with the filename and stdin as input.

Knowing this one could also compare files with vimdiff doing something like this - this is just an idea you do not want to do this:

:w !cat > /tmp/tempFile && vimdiff /tmp/tempFile % && rm /tmp/tempFile

(Then open readonly and close vimdiff using :qall)

Tobias Heinicke
  • 1,778
  • 1
  • 11
  • 17
  • A more sane approach to use vimdiff would be creating a shell script containing the following `vim - -c ":vnew $1 |windo diffthis"`, making it executable, saving it in the PATH as for example `vimdiffWithStdin` and then comparing with the following command in vim: `:w !vimdiffWithStdin %` – Tobias Heinicke Mar 12 '14 at 18:33
  • Even simpler: `:w !vimdiff % /dev/stdin`. I don't know if a similar trick exists for windows. – deft_code Mar 30 '15 at 07:00
70

http://vim.wikia.com/wiki/Diff_current_buffer_and_the_original_file

Here is a function and command to see a diff between the currently edited file and its unmodified version in the filesystem. Just put this in your vimrc or in the plugin directory, open a file, make some modifications without saving them, and do :DiffSaved.

function! s:DiffWithSaved()
  let filetype=&ft
  diffthis
  vnew | r # | normal! 1Gdd
  diffthis
  exe "setlocal bt=nofile bh=wipe nobl noswf ro ft=" . filetype
endfunction
com! DiffSaved call s:DiffWithSaved()

To get out of diff view you can use the :diffoff command.

Below is a similar function, adapted to mimic the 'cvs diff' command...

gnat
  • 6,213
  • 108
  • 53
  • 73
Bill Lynch
  • 80,138
  • 16
  • 128
  • 173
  • Might want to give RCSVers.vim a try: http://www.vim.org/scripts/script.php?script_id=563 – JD Frias Jun 05 '09 at 05:25
  • 8
    @luc-hermitte Isn't the alternative `:w !diff % -` superior when you're using vim on an everchanging and large number of boxes that you can't easily change the .vimrc for? (Provided they have diff installed.) – thomanski Jan 10 '12 at 13:59
  • If you are unlucky enough to not be in charge of your vim environment certainly. So far, wherever I work on, even for 2 weeks, I try to install a fully featured vim, and I have a tarball of 100+ vim config files ready to be deployed. – Luc Hermitte Jan 11 '12 at 17:38
  • 1
    Vim is not the tool of the last hope. The one that will work when nothing else is available. It is my main working tool. – Luc Hermitte Jan 11 '12 at 17:39
  • 12
    Just supplying a link is not really an answer – Skurpi Feb 20 '14 at 11:03
  • 3
    Chaos's answer is superior and in Tobias's answer, the explanation is complete. – Avi Cohen Apr 16 '14 at 15:00
  • 1
    Can you please add some content instead of just a link? SO guidelines... – Błażej Michalik Sep 07 '16 at 08:08
  • Awesome. I added a `nnoremap d :DiffSave` to it. However, what is the recommanded way to go back to edition of the file? – psychoslave Nov 30 '18 at 10:48
  • Also adding a safeguad to avoid calling it while already in a diff mode would be nice – psychoslave Nov 30 '18 at 10:51
  • This is great, and even better with https://github.com/lambdalisue/vim-unified-diff – Alcamtar Oct 29 '19 at 00:38
14

I've always likes diffchanges - nice, simple, works.

Rook
  • 60,248
  • 49
  • 165
  • 242
  • This works a LOT better than the more highly upvoted options. This gives the ability to toggle it. – Steven Lu May 14 '13 at 17:45
  • 1
    @StevenLu - Meh ... what can you do? In any case, glad you like it. I find it more practical than the other approach. – Rook May 15 '13 at 08:18
  • Me, I do second @Steven, your suggested diffchanges is excellent. Thanks! – A S Jul 14 '16 at 06:22
  • This can also be installed as a plugin from the same developer: https://github.com/jmcantrell/vim-diffchanges – Sumanth Lingappa Jun 25 '20 at 09:25
  • nice plugin, but it doesn't delete the patch it creates afterwards? Edit: My bad! It does. I was using it the wrong way. I had to use `:DiffChangesDiffToggle`. – aderchox Jul 29 '20 at 22:21
12

from vimrc_example.vim:

" Convenient command to see the difference between the current buffer and the
" file it was loaded from, thus the changes you made.
if !exists(":DiffOrig")
  command DiffOrig vert new | set bt=nofile | r # | 0d_ | diffthis
          \ | wincmd p | diffthis
endif
0x89
  • 2,940
  • 2
  • 31
  • 30
  • ...as documented at http://vimdoc.sourceforge.net/htmldoc/diff.html#:DiffOrig. Advantage of this over `w !diff % -` is that it works over remote sources too (for example: `vim sftp://example.com/foo.txt`) – Lekensteyn Aug 27 '15 at 16:56
  • 1
    I like this better because we see the difference right inside the vim buffer itself instead of in a terminal – Niko Bellic Feb 06 '17 at 23:41
  • How do you go back to normal once you are done examining the differences? – Niko Bellic Feb 06 '17 at 23:42
  • You can get rid of the if clause by substituting command with command! (see :h E174) – elig Mar 09 '19 at 22:21
  • 1
    @NikoBellic First close the "old" window (marked as scratch buffer) with ':q'. You can change between windows by tapping ctrl-W twice while in normal mode. Then you can toggle off diff by writing :diffoff – brunch875 Feb 24 '20 at 11:23
  • It's now on [`defaults.vim`](https://github.com/vim/vim/blob/fb55207ed17918c8a2a6cadf5ad9d5fcf686a7ab/runtime/defaults.vim#L139-L146) – hashlash Jan 28 '22 at 17:54
5

git supports the following command

:w !git diff --no-index -- % -

map it to a command by adding the following to your ~/.vimrc

command GitDiff execute "w !git diff --no-index -- % -"

Now executing :GitDiff becomes a handy little command to quickly show the diff before each save.

CyclicUniverse
  • 133
  • 1
  • 5
2

Not exactly what you're looking for but SCMDiff.vim is really cool. One keypress, and it diff-highlights your current file with the head revision in a source control repo. It's meant to work with many SCMS. I use it with perforce.

prestomation
  • 7,225
  • 3
  • 39
  • 37
2

There is a plugin, based on different answers here: https://github.com/gangleri/vim-diffsaved

It provides the :w !diff % - method and the more involved diffthis one.

Apart from that undotree allows this as well, but also much more (diffs between different undo checkpoints). Similar to Gundo.

blueyed
  • 27,102
  • 4
  • 75
  • 71
2

Source the following and use :DIFF command

function! s:diff()
    let tmpa = tempname()
    let tmpb = tempname()
    earlier 100h
    exec 'w '.tmpa
    later 100h
    exec 'w '.tmpb
    update
    exec 'tabnew '.tmpa
    diffthis
    vert split
    exec 'edit '.tmpb
    diffthis
endfunction
command! -nargs=0 DIFF call <SID>diff()
Mykola Golubyev
  • 57,943
  • 15
  • 89
  • 102
1

If you want to use vim for comparison like in vimdiff, you could do something like this:

Edit your .vimrc and add:

nmap <F8> :w !vim -M -R - -c ":vnew % \| windo diffthis"<CR><CR>

From there on you will see your changes and can quit the diff view using qall like in vimdiff by pressing F8 in command mode. Replace F8 with any key you like.

Edit: Added -M to disallow any modification, because it is not save.

Tobias Heinicke
  • 1,778
  • 1
  • 11
  • 17
  • This command starts to work for me, it shows me the diff side by side. However, as soon as I try and edit anything the vim window goes crazy. I start typing and I get a bash prompt behind the words in vim on either side of the screen. So it seems to display the diff, but then vim crashes. Additionally, I get this error `Vim: Error reading input, exiting...` any ideas what is going wrong here? – Trevor Jun 10 '14 at 00:06
  • @Trevor: I could only guess what the problems are. It is indeed not save to make any modifications while diffing like this. Hence I have added "-M" parameter to disallow it entirely. Sorry. – Tobias Heinicke Jun 12 '14 at 18:13
1

I can recommend the histwin plugin.

While it doesn't diff to the current saved version of the file (like the other answers), it can vimdiff changes since you started edting, and even replay your changes in order. The difference shows if you save intermediately.

Additionally, it displays a list of all undo history branches and allows you to switch or diff between them.

PS: While the plugin doesn't automatically track moments in the edit history since every file change, you can explicitly "tag" the moment when you save the file such that you can later vimdiff with it, if you want that. Maybe this could be automated?

catchmeifyoutry
  • 7,179
  • 1
  • 29
  • 26
1

Changes you just edited [buffer], i.e. those that differ from last saved version (in working directory), these may differ with last index version (Git). I mapped both:

" Find diff inbetween currrent buffer and ... A{last index version} vs B{last saved version in working directory}
"  - A{last index version}: the file as you last commited it
    " git diff to vimdiff against the index version of the file:
    nnoremap <leader>gd <Esc>:Gvdiff<CR><Esc>:echo "currentBuffer vs lastIndexVersion (last commited)"<CR>
"  - B{last saved version in working directory}: the file you last :w,
"   not neccesary commited it (not commited for sure if it is in NO git project)
    " https://vim.fandom.com/wiki/Diff_current_buffer_and_the_original_file
    nnoremap <leader>gd2 <Esc>:DiffSaved<CR><Esc>:echo "currentBuffer vs lastSaved (not neccesary equal to last commited)"<CR>
    function! s:DiffWithSaved()
        let filetype=&ft
        diffthis
        vnew | r # | normal! 1Gdd
        diffthis
        exe "setlocal bt=nofile bh=wipe nobl noswf ro ft=" . filetype
    endfunction
    com! DiffSaved call s:DiffWithSaved()

Example of vimdiff vs Gdiff.

  • vimdiff: :vimdiff
  • Gdiff: Gdiff Commiting changes, you see only Gdiff, what might, or might have same changes as vimdiff: commiting changes, you see only Gdiff, what might, or might NOT have same changes as vimdiff

Furthermore, to easy vimdiff homonym file in other path:

    " vimdiff homonym file 
   nnoremap <leader>dh  <Esc>:vsplit %:p:h/../__/%:t <bar> :windo diffthis<Left><Left><Left><Left><Left><Left><Left><Left><Left><Left><Left><Left><Left><Left><Left><Left><Left><Left><Left><Left><Left><Left><Left><Left>
        "E.g."$ vim /path01/proj02_pg064/processorder.php
        ":vsplit %:p:h/../proj01_pg05/%:t | :windo diffthis
Xopi García
  • 359
  • 1
  • 2
  • 9
  • 1
    One could just make 1 map that executes: a `Gdiff` if possible and otherwise (e.g. not a Git project) then perform a `:vimdiff`. With `try-catch-endtry`. But this way `:DiffWithSaved` in a Git project is lacked. – Xopi García May 14 '20 at 20:47
0

You can make vim create a last backup and original backup with:

:set backup
:set patchmode=.orig 

Thereafter, you can open them in a split:

:vsp %:p~ or :vsp %:.orig

And from there do:

:vimdiff in each buffer

If you're dead set on no leftovers but want vimdiff, you could also do:

ggVGy    # copy the whole buffer
:vnew    # open a split
CTRL-W w # switch to it
shift-P  # paste at start

and then do :diffthis on each split

chipfall
  • 300
  • 2
  • 6
0

AFAIK, that's what the 'changes' command is for:

:changes

:help changes

lyderic
  • 382
  • 3
  • 7
-2

Follow the above suggests I use git diff that I like much:

:w !git diff  % -
Zioalex
  • 3,441
  • 2
  • 33
  • 30