Compare commits
16 Commits
Author | SHA1 | Date | |
---|---|---|---|
065ef47b2b | |||
702d3a6889 | |||
512df2948e | |||
|
e81277ecb4 | ||
|
4188c51081 | ||
|
5702bf8e27 | ||
|
732fad0a98 | ||
|
c2809e6e0e | ||
731553906d | |||
c907c006db | |||
fdae5aa6de | |||
da5e24260a | |||
bb6fa28de0 | |||
e7744c8e9a | |||
da9eb32e36 | |||
7c11878345 |
80
README.md
80
README.md
@ -1,19 +1,57 @@
|
||||
# Usage
|
||||
```help
|
||||
usage: vimtabdiff.py [-h] [--vim VIM] pathA pathB
|
||||
Do you use `git difftool` to review changes before making a commit? The problem with that is that you get to see the diff of one file at a time. You can't easily stop it after few files and you can't go back to a previous file. `vimtabdiff.py` loads all the files with diffs, one in each vim tab page. You can move around any file and edit the diffs easily.
|
||||
|
||||
Show diff of files from two directories in vim tabs
|
||||
|
||||
positional arguments:
|
||||
pathA
|
||||
pathB
|
||||
# Install
|
||||
|
||||
options:
|
||||
-h, --help show this help message and exit
|
||||
--vim VIM vim command to run
|
||||
```bash
|
||||
mkdir -p ~/bin
|
||||
|
||||
# for python version >= 3.10
|
||||
curl -o ~/bin/vimtabdiff.py "https://raw.githubusercontent.com/balki/vimtabdiff/master/vimtabdiff.py"
|
||||
|
||||
# for python version < 3.10
|
||||
curl -o ~/bin/vimtabdiff.py "https://raw.githubusercontent.com/balki/vimtabdiff/br-py38/vimtabdiff.py"
|
||||
|
||||
chmod +x ~/bin/vimtabdiff.py
|
||||
```
|
||||
|
||||
# Git difftool
|
||||
You may need to add `~/bin` to your PATH variable if not already done. See [here](https://wiki.archlinux.org/title/Environment_variables#Per_user) for help.
|
||||
👍 this [issue](https://github.com/balki/vimtabdiff/issues/1) for `pip install` support
|
||||
|
||||
|
||||
# Screenshot
|
||||
![image](https://user-images.githubusercontent.com/189196/206880555-c71b472c-144c-4c82-a4ab-f8a4fd36f7a5.png)
|
||||
|
||||
# Usage
|
||||
```help
|
||||
usage: vimtabdiff.py [-h] [--vim VIM] [--onlydiffs] pathA pathB
|
||||
|
||||
Show diff of files from two directories in vim tabs
|
||||
|
||||
positional arguments:
|
||||
pathA
|
||||
pathB
|
||||
|
||||
options:
|
||||
-h, --help show this help message and exit
|
||||
--vim VIM vim command to run
|
||||
--onlydiffs only open files where there is a diff
|
||||
```
|
||||
|
||||
## Relevant vim tips
|
||||
|
||||
* `gt` → Go to next tab
|
||||
* `gT` → Go to previous tab
|
||||
* `:tabr` → Go to first tab
|
||||
* `:drop filenam<Tab>` → Go to the tab with filename
|
||||
* `g<Tab>` → Go to last used tab (Works in vim version > 8.2.1401)
|
||||
* `:set mouse=a` → Now clicking on a tab works
|
||||
* `]c` → Go to next diff hunk
|
||||
* `[c` → Go to previous diff hunk
|
||||
* `do`, `dp` → Diff obtain, Diff put
|
||||
* `zo`, `zc`, `zi` → Fold open, Fold close, toggle all folds
|
||||
|
||||
# See Git diffs
|
||||
|
||||
|
||||
## Setup
|
||||
@ -34,30 +72,32 @@
|
||||
|
||||
## Using custom vim command
|
||||
|
||||
Using clean vim without reading `vimrc`
|
||||
```bash
|
||||
git config --global difftool.vimtabdiff.cmd 'vimtabdiff.py --vim "vim --clean" $LOCAL $REMOTE'
|
||||
```
|
||||
|
||||
Git config file (`~/.gitconfig`) should look like this
|
||||
|
||||
```toml
|
||||
```TOML
|
||||
[alias]
|
||||
...
|
||||
dt = difftool --tool vimtabdiff --dir-diff
|
||||
[difftool "vimtabdiff"]
|
||||
cmd = vimtabdiff.py --vim \"vim --clean\" $LOCAL $REMOTE
|
||||
```
|
||||
Using better diff algorithm
|
||||
|
||||
```bash
|
||||
git config --global difftool.vimtabdiff.cmd 'vimtabdiff.py --vim "vim +\"set diffopt+=algorithm:patience\"" $LOCAL $REMOTE'
|
||||
|
||||
# Known issues
|
||||
```
|
||||
|
||||
1. If your path to custom vim has space, it does not work. i.e. Following does *not* work
|
||||
|
||||
```bash
|
||||
git config --global difftool.vimtabdiff.cmd 'vimtabdiff.py --vim "/home/foo/my files/bin/vim" $LOCAL $REMOTE'
|
||||
```
|
||||
2. Not tested in non-linux OS. Pull requests welcome if found any issues.
|
||||
*Note:* Not tested in non-linux OS. But I guess it should work fine. Pull requests welcome if found any issues.
|
||||
|
||||
# Similar
|
||||
|
||||
https://gist.github.com/Osse/4709787
|
||||
* https://gist.github.com/Osse/4709787 is very similar, written as a `zsh` script.
|
||||
* https://github.com/will133/vim-dirdiff is a Vim plugin that uses an interactive list of files instead of tabs.
|
||||
* https://github.com/Soares/tabdiff.vim is a Vim plugin takes a list of files as aguments.
|
||||
* [`:Git difftool -y`](https://github.com/tpope/vim-fugitive/blob/d507d00bd04794119beeb41da118774a96815b65/doc/fugitive.txt#L92) is a command from vim-fugitive which is a vim git plugin.
|
||||
|
@ -1,33 +1,45 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
import os
|
||||
import argparse
|
||||
import pathlib
|
||||
import itertools
|
||||
import tempfile
|
||||
import subprocess
|
||||
import shlex
|
||||
from pathlib import Path
|
||||
from typing import TypeVar
|
||||
from collections.abc import Iterator, Callable
|
||||
|
||||
R = TypeVar('R')
|
||||
|
||||
|
||||
def star(f):
|
||||
def star(f: Callable[..., R]) -> Callable[[tuple], R]:
|
||||
""" see https://stackoverflow.com/q/21892989 """
|
||||
return lambda args: f(*args)
|
||||
|
||||
|
||||
def parse_args():
|
||||
def parse_args() -> argparse.Namespace:
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Show diff of files from two directories in vim tabs")
|
||||
parser.add_argument("pathA")
|
||||
parser.add_argument("pathB")
|
||||
description="Show diff of files from two directories in vim tabs",
|
||||
epilog="See https://github.com/balki/vimtabdiff for more info")
|
||||
parser.add_argument("pathA", type=Path)
|
||||
parser.add_argument("pathB", type=Path)
|
||||
parser.add_argument("--vim", help="vim command to run", default="vim")
|
||||
parser.add_argument(
|
||||
"--onlydiffs", help="only open files where there is a diff", action="store_true"
|
||||
)
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def get_dir_info(dirname):
|
||||
if not dirname:
|
||||
def get_dir_info(dirpath: Path | None) -> tuple[list[Path], list[Path]]:
|
||||
if not dirpath:
|
||||
return [], []
|
||||
dirs, files = [], []
|
||||
dirp = pathlib.Path(dirname)
|
||||
for p in dirp.iterdir():
|
||||
for p in dirpath.iterdir():
|
||||
if p.is_dir():
|
||||
dirs.append(p)
|
||||
else:
|
||||
@ -35,27 +47,26 @@ def get_dir_info(dirname):
|
||||
return dirs, files
|
||||
|
||||
|
||||
def get_pairs(aItems, bItems):
|
||||
aItems = [(item, 'A') for item in aItems]
|
||||
bItems = [(item, 'B') for item in bItems]
|
||||
def get_pairs(aPaths: list[Path],
|
||||
bPaths: list[Path]) -> Iterator[tuple[Path | None, Path | None]]:
|
||||
aItems = [(item, 'A') for item in aPaths]
|
||||
bItems = [(item, 'B') for item in bPaths]
|
||||
abItems = aItems + bItems
|
||||
abItems.sort(key=star(lambda item, tag: (item.name, tag)))
|
||||
for _, items in itertools.groupby(abItems,
|
||||
key=star(lambda item, _: item.name)):
|
||||
items = list(items)
|
||||
# NOTE: python 3.10's match expression can make this better
|
||||
if len(items) == 2:
|
||||
(aItem, _), (bItem, _) = items
|
||||
yield aItem, bItem
|
||||
else:
|
||||
(item, tag), = items
|
||||
if tag == 'A':
|
||||
match list(items):
|
||||
case [(aItem, _), (bItem, _)]:
|
||||
yield aItem, bItem
|
||||
case [(item, 'A'),]:
|
||||
yield item, None
|
||||
else:
|
||||
case [(item, 'B'),]:
|
||||
yield None, item
|
||||
|
||||
|
||||
def get_file_pairs(a, b):
|
||||
def get_file_pairs(
|
||||
a: Path | None,
|
||||
b: Path | None) -> Iterator[tuple[Path | None, Path | None]]:
|
||||
aDirs, aFiles = get_dir_info(a)
|
||||
bDirs, bFiles = get_dir_info(b)
|
||||
yield from get_pairs(aFiles, bFiles)
|
||||
@ -63,22 +74,35 @@ def get_file_pairs(a, b):
|
||||
yield from get_file_pairs(aDir, bDir)
|
||||
|
||||
|
||||
def main():
|
||||
def main() -> None:
|
||||
args = parse_args()
|
||||
vimCmdFile = tempfile.NamedTemporaryFile(mode='w', delete=False)
|
||||
with vimCmdFile:
|
||||
cmds = f"""
|
||||
let s:spr = &splitright
|
||||
set splitright
|
||||
"""
|
||||
print(cmds, file=vimCmdFile)
|
||||
for a, b in get_file_pairs(args.pathA, args.pathB):
|
||||
aPath = a.resolve() if a else os.devnull
|
||||
bPath = b.resolve() if b else os.devnull
|
||||
print(
|
||||
f"tabedit {aPath} | diffthis | vsp {bPath} | diffthis | diffupdate",
|
||||
file=vimCmdFile)
|
||||
if (
|
||||
args.onlydiffs
|
||||
and a and b
|
||||
and open(aPath, mode="rb").read() == open(bPath, mode="rb").read()
|
||||
):
|
||||
continue
|
||||
print(f"tabedit {aPath} | vsp {bPath}", file=vimCmdFile)
|
||||
cmds = f"""
|
||||
let &splitright = s:spr
|
||||
tabdo windo :1
|
||||
tabdo windo diffthis
|
||||
tabdo windo diffupdate
|
||||
tabfirst | tabclose
|
||||
call delete("{vimCmdFile.name}")
|
||||
"""
|
||||
print(cmds, file=vimCmdFile)
|
||||
subprocess.run(args.vim.split() + ["-S", vimCmdFile.name])
|
||||
subprocess.run(shlex.split(args.vim) + ["-S", vimCmdFile.name])
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
Loading…
Reference in New Issue
Block a user