Skip to content

Commit 358947a

Browse files
authored
Use git diff for diffs (#150)
* NOTES * Working on diff conversion * Parsing diffs! * Support num_lines for end skips * Rename + Change test * Test with patch-u5 * LocalFileDiff --> dataclass * Codes --> dataclass * Parsing raw diff output * Use git diff --raw for directory diffs * add diff endpoint * Use git diff ops in webdiff! * Context * Update highlight.js * post to /diff * Add an option (-w) * Factor out DiffOptionsControl * wire up context + diff algorithm * Add git diff -b as well; unclear if this is different? * styled diff options panel * thread through some options * wire up styling * Wire up configurable highlight.js themes * fix "show -3 lines" bug * maxLinesForSyntax * Support binary diffs again * update codediff.js, bugfix * Fix bug with pure moves * add unidiff dependency * delete irrelevant test * blacken, version bump * Fix issue with git webdiff by adding --no-symlinks * blacken * check for symlinks and inline * we may be in business! * swap in symlink-y directory * test case for "Show -1 more lines" * no chardiff on last line * no-trailing-newline test * fix bug w/ trailing newlines * pin Werkzeug * delete TODO * add long line no space test * pare back test case * overflow-wrap: anywhere * webdiff.colors * add docs on configuration * fix typo * add some implementation notes
1 parent 05d2a65 commit 358947a

File tree

294 files changed

+6468
-885
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

294 files changed

+6468
-885
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,3 +64,4 @@ webdiff/static/js/file_diff.js
6464

6565
wheelhouse
6666
.vscode
67+
NOTES.md

README

Lines changed: 0 additions & 23 deletions
This file was deleted.

README.md

Lines changed: 60 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
Two-column web-based git difftool.
55

66
Features include:
7+
78
* Side-by-side (two column) diff view
89
* Runs in the browser of your choice on any platform.
910
* Syntax highlighting via highlight.js
@@ -60,6 +61,33 @@ Make sure you chmod this file to only be readable by yourself. You can generate
6061
a personal access token for webdiff via github.com → profile → Settings →
6162
Personal access tokens. Make sure to grant all the "repo" privileges.
6263

64+
## Configuration
65+
66+
webdiff can be configured via [`git config`][git config]. To change the syntax highlighting theme, for example:
67+
68+
git config webdiff.theme rainbow
69+
70+
(You can find a list of supported themes in the [themes] directory.)
71+
72+
As with any git configuration setting, these can be set globally or per-repo.
73+
74+
Options are:
75+
76+
| Setting | Default | Notes |
77+
| -------------- | ------------- | ------ |
78+
| webdiff.theme | googlecode | Syntax highlighting theme (see [themes] directory). |
79+
| webdiff.port | -1 | Port on which to serve webdiff. Default is random open port. This can be overridden with `--port`. |
80+
| webdiff.maxDiffWidth | 100 | Maximum length of lines in the diff display. After this width, lines will wrap. |
81+
| webdiff.unified | 8 | Lines of context to display by default (`git diff -U`) |
82+
| webdiff.extraDirDiffArgs | "" | Any extra arguments to pass to `git diff` when diffing directories. |
83+
| webdiff.extraFileDiffArgs | "" | Any extra arguments to pass to `git diff` when diffing files. |
84+
| webdiff.openBrowser | true | Whether to automatically open the browser UI when you run webdiff. |
85+
| webdiff.maxLinesForSyntax | 10000 | Maximum lines in file to do syntax highlighting. |
86+
| webdiff.colors.delete | #fee | CSS background color for delete (left) lines |
87+
| webdiff.colors.insert | #efe | CSS background color for insert (right) lines |
88+
| webdiff.colors.charDelete | #fcc | CSS background color for deleted characters in a delete (left) line |
89+
| webdiff.colors.charInsert | #cfc | CSS background color for inserted characters in an insert (right) line |
90+
6391
## Development
6492

6593
python3 -m venv venv
@@ -76,38 +104,37 @@ Then from the root directory:
76104

77105
or to launch in debug mode:
78106

79-
./test.sh $(pwd)/../testdata/webdiffdiff/{left,right}
107+
./test.sh $(pwd)/testdata/manyfiles/{left,right}
80108

81109
(or any other directory in testdata)
82110

83111
To run the Python tests:
84112

85113
pytest
86114

87-
To run the JavaScript tests:
88-
89-
python -m SimpleHTTPServer
90-
open tests/runner.html
91-
92115
To format the code, run:
93116

94117
./scripts/black.sh
95118
cd ts
96119
yarn prettier
97120

121+
To debug `git webdiff`, run:
122+
123+
WEBDIFF_CONFIG=$(pwd)/testing.cfg ./webdiff/gitwebdiff.py
124+
98125
To iterate on the PyPI package, run:
99126

100127
# from outside the webdiff virtualenv:
101-
pip uninstall webdiff
128+
pip3 uninstall webdiff
102129

103130
# from inside the webdiff virtualenv, adjust for current version
104131
python setup.py sdist
105132
mkdir /tmp/webdiff-test
106-
cp dist/webdiff-X.Y.Z.tar.gz /tmp/webdiff-test
133+
cp dist/webdiff-?.?.?.tar.gz /tmp/webdiff-test
107134

108135
deactivate
109136
cd /tmp/webdiff-test
110-
pip3 install webdiff-X.Y.Z.tar.gz
137+
pip3 install webdiff-?.?.?.tar.gz
111138

112139
To publish to pypitest:
113140

@@ -121,6 +148,30 @@ And to the real pypi:
121148

122149
See [pypirc][] docs for details on setting up `~/.pypirc`.
123150

151+
## Implementation notes
152+
153+
webdiff doesn't calculate any diffs itself. Instead, it relies on `git diff`. This is possible because `git diff` has a `--no-index` mode that allows it to operate outside of a git repository. Of course, this means that you need to have `git` installed to use webdiff!
154+
155+
When you run `webdiff dir1 dir2`, webdiff runs:
156+
157+
git diff --raw --no-index dir1 dir2
158+
159+
To ask `git` which files are adds, removes, renames and changes. Then, when it's serving the web UI for a particular diff, it runs:
160+
161+
git diff --no-index (diff args) file1 file2
162+
163+
This produces a patch, which is what the web UI renders. (It also needs both full files for syntax highlighting.)
164+
165+
When you run `git webdiff (args)`, it runs:
166+
167+
git difftool -d -x webdiff (args)
168+
169+
This tells `git` to set up two directories and invoke `webdiff leftdir rightdir`.
170+
171+
There's one complication involving symlinks. `git difftool -d` may fill one of the sides (typically the right) with symlinks. This is faster than copying files, but unfortunately `git diff --no-index` does not resolve these symlinks. To make this work, if a directory contains symlinks, webdiff makes a copy of it before diffing. For file diffs, it resolves the symlink before passing it to `git diff --no-index`. The upshot is that you can run `git webdiff`, edit a file, reload the browser window and see the changes.
172+
124173
[pypirc]: https://packaging.python.org/specifications/pypirc/
125174
[Homebrew]: https://brew.sh/
126175
[ImageMagick]: https://imagemagick.org/index.php
176+
[git config]: https://git-scm.com/docs/git-config
177+
[themes]: http://example.com

TODO

Lines changed: 0 additions & 14 deletions
This file was deleted.

requirements.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,5 @@ pillow
55
requests
66
binaryornot
77
black
8+
unidiff==0.7.4
9+
Werkzeug==2.2.2

setup.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77

88
setup(name='webdiff',
9-
version='0.16.0',
9+
version='1.0.0',
1010
description='Two-column web-based git difftool',
1111
long_description=long_description,
1212
long_description_content_type='text/markdown',
@@ -25,7 +25,9 @@
2525
'flask==2.2.2',
2626
'pillow',
2727
'requests',
28-
'PyGithub==1.55'
28+
'PyGithub==1.55',
29+
'unidiff==0.7.4',
30+
'Werkzeug==2.2.2',
2931
],
3032
include_package_data=True,
3133
package_data = {

test-gitwebdiff.sh

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#!/bin/bash
2+
export TESTING=true
3+
export DEBUG=true
4+
export WEBDIFF_CONFIG=$(pwd)/testing.cfg
5+
export WEBDIFF_PORT=$(($RANDOM + 10000))
6+
export PYTHONPATH=.
7+
./webdiff/gitwebdiff.py $*
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Now it's completely transparent that `requestStatus` will end in "success." It's easy to accidentally produce half-synchronous code with callbacks or raw Promises, but difficult with `async`.footnote:[There's still a more subtle bug in this version: if you call fetchWithCache twice in a row with the same URL, it will issue two requests. How would you fix this?]
2+
3+
// This is the fix, it's three lines longer but I think it adds complexity that's irrelevant to the point that I'm trying to make here. https://www.typescriptlang.org/play?#code/MYewdgzgLgBA+sAhsAFgUwFwwN4G0CuATgDZbSECWYA5gLpYAKhIAthRGgDwBKaEADuA4A+AL4wAvDlEBuAFCIIATzDAYAM3yqoFcBrRRUAdQpQUAYWToAFEVIxyVagEpGzNhx59BkNMJxyMDAU6jC2JMFg8EioaM4BQUGEBkRRCFZoBCS08kGigTCgkLDqBqgMkvqGKOHEzrnRGVnEtJWl1QwNyVCpVeXy+YoqapraulHtqAAqaAAeUCZmlrG1ZFCUNK4wTKzsXI40-tgFRdAwyQJCaJWIAO6Ipn0oixYZtfUF3b0XPhwAdFA5lBrB98nJiAZzmgAI74PhQADKUEQPQgWAA5MQQIgACZOdEwAA+MHREHwwGAfAgBOJ6LQhGYhHR8iGqg0WmAOj01AMAFUOIRbAKAJI4tYbFwJKGw+FIlH4CCVTHYvE0ZknISwfjMdQUCE3e6PSYoGbzF7LGwAAwA9Ar6daACTYO2EUWiS0fJIwuHQOWopVkilU9X5IA
4+
5+
Note that if you return a Promise from an `async` function, it will not get wrapped in another Promise: the return type will be `Promise<T>` rather than `Promise<Promise<T>>`. Again, TypeScript will help you build an intuition for this:
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Now it's completely transparent that `requestStatus` will end in "success." It's easy to accidentally produce half-synchronous code with callbacks or raw Promises, but difficult with `async`.footnote:[There's still a more subtle bug in this version: if you call ++fetchWithCache++ twice in a row with the same URL, it will issue two requests. How would you fix this?]
2+
3+
// This is the fix, it's three lines longer but I think it adds complexity that's irrelevant to the point that I'm trying to make here. https://www.typescriptlang.org/play?#code/MYewdgzgLgBA+sAhsAFgUwFwwN4G0CuATgDZbSECWYA5gLpYAKhIAthRGgDwBKaEADuA4A+AL4wAvDlEBuAFCIIATzDAYAM3yqoFcBrRRUAdQpQUAYWToAFEVIxyVagEpGzNhx59BkNMJxyMDAU6jC2JMFg8EioaM4BQUGEBkRRCFZoBCS08kGigTCgkLDqBqgMkvqGKOHEzrnRGVnEtJWl1QwNyVCpVeXy+YoqapraulHtqAAqaAAeUCZmlrG1ZFCUNK4wTKzsXI40-tgFRdAwyQJCaJWIAO6Ipn0oixYZtfUF3b0XPhwAdFA5lBrB98nJiAZzmgAI74PhQADKUEQPQgWAA5MQQIgACZOdEwAA+MHREHwwGAfAgBOJ6LQhGYhHR8iGqg0WmAOj01AMAFUOIRbAKAJI4tYbFwJKGw+FIlH4CCVTHYvE0ZknISwfjMdQUCE3e6PSYoGbzF7LGwAAwA9Ar6daACTYO2EUWiS0fJIwuHQOWopVkilU9X5IA
4+
5+
Note that if you return a Promise from an `async` function, it will not get wrapped in another Promise: the return type will be `Promise<T>` rather than ++Promise&#x200b;&lt;Promise&lt;T&gt;&gt;++. Again, TypeScript will help you build an intuition for this:
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
This is a file
2+
It has four lines
3+
and a
4+
trailing newline

0 commit comments

Comments
 (0)