Logs
erock
created pr with ps-35
on Patchsets
Diff ↕
feat: range diff
Eric Bower <me@erock.io>
fixtures/a_b.patch | 31 +++ fixtures/a_b_reorder.patch | 55 ++++++ fixtures/a_c.patch | 33 ++++ fixtures/a_c_added_commit.patch | 80 ++++++++ fixtures/a_c_changed_commit.patch | 60 ++++++ fixtures/a_c_reorder.patch | 55 ++++++ fixtures/a_c_rm_commit.patch | 23 +++ fixtures/expected_commit_changed.txt | 11 ++ go.mod | 1 + go.sum | 15 +- pr.go | 4 +- range_diff.go | 188 ++++++++++++++++++ range_diff_test.go | 274 +++++++++++++++++++++++++++ util.go | 5 +- util_test.go | 2 +- 15 files changed, 829 insertions(+), 8 deletions(-) create mode 100644 fixtures/a_b.patch create mode 100644 fixtures/a_b_reorder.patch create mode 100644 fixtures/a_c.patch create mode 100644 fixtures/a_c_added_commit.patch create mode 100644 fixtures/a_c_changed_commit.patch create mode 100644 fixtures/a_c_reorder.patch create mode 100644 fixtures/a_c_rm_commit.patch create mode 100644 fixtures/expected_commit_changed.txt create mode 100644 range_diff.go create mode 100644 range_diff_test.go
1From 778bcd8223bbe29732a5d8c0379bc9717d93f087 Mon Sep 17 00:00:00 2001
2From: Eric Bower <me@erock.io>
3Date: Tue, 23 Jul 2024 11:59:25 -0400
4Subject: [PATCH] feat: range diff
5
6---
7 fixtures/a_b.patch | 31 +++
8 fixtures/a_b_reorder.patch | 55 ++++++
9 fixtures/a_c.patch | 33 ++++
10 fixtures/a_c_added_commit.patch | 80 ++++++++
11 fixtures/a_c_changed_commit.patch | 60 ++++++
12 fixtures/a_c_reorder.patch | 55 ++++++
13 fixtures/a_c_rm_commit.patch | 23 +++
14 fixtures/expected_commit_changed.txt | 11 ++
15 go.mod | 1 +
16 go.sum | 15 +-
17 pr.go | 4 +-
18 range_diff.go | 188 ++++++++++++++++++
19 range_diff_test.go | 274 +++++++++++++++++++++++++++
20 util.go | 5 +-
21 util_test.go | 2 +-
22 15 files changed, 829 insertions(+), 8 deletions(-)
23 create mode 100644 fixtures/a_b.patch
24 create mode 100644 fixtures/a_b_reorder.patch
25 create mode 100644 fixtures/a_c.patch
26 create mode 100644 fixtures/a_c_added_commit.patch
27 create mode 100644 fixtures/a_c_changed_commit.patch
28 create mode 100644 fixtures/a_c_reorder.patch
29 create mode 100644 fixtures/a_c_rm_commit.patch
30 create mode 100644 fixtures/expected_commit_changed.txt
31 create mode 100644 range_diff.go
32 create mode 100644 range_diff_test.go
33
34diff --git a/fixtures/a_b.patch b/fixtures/a_b.patch
35new file mode 100644
36index 0000000..c2c5167
37--- /dev/null
38+++ b/fixtures/a_b.patch
39@@ -0,0 +1,31 @@
40+From 33c682ac27479f501924cf159d0a75ad91deb589 Mon Sep 17 00:00:00 2001
41+From: Eric Bower <me@erock.io>
42+Date: Tue, 23 Jul 2024 10:07:57 -0400
43+Subject: [PATCH] chore: add torch and create random tensor
44+
45+---
46+ requirements.txt | 1 +
47+ train.py | 3 +++
48+ 2 files changed, 4 insertions(+)
49+ create mode 100644 requirements.txt
50+
51+diff --git a/requirements.txt b/requirements.txt
52+new file mode 100644
53+index 0000000..4968a39
54+--- /dev/null
55++++ b/requirements.txt
56+@@ -0,0 +1 @@
57++torch==2.3.1
58+diff --git a/train.py b/train.py
59+index 5c027f4..d21dac3 100644
60+--- a/train.py
61++++ b/train.py
62+@@ -1,2 +1,5 @@
63++import torch
64++
65+ if __name__ == "__main__":
66+ print("train!")
67++ torch.rand(3,6)
68+--
69+2.45.2
70+
71diff --git a/fixtures/a_b_reorder.patch b/fixtures/a_b_reorder.patch
72new file mode 100644
73index 0000000..2524df9
74--- /dev/null
75+++ b/fixtures/a_b_reorder.patch
76@@ -0,0 +1,55 @@
77+From 33c682ac27479f501924cf159d0a75ad91deb589 Mon Sep 17 00:00:00 2001
78+From: Eric Bower <me@erock.io>
79+Date: Tue, 23 Jul 2024 10:07:57 -0400
80+Subject: [PATCH 1/2] chore: add torch and create random tensor
81+
82+---
83+ requirements.txt | 1 +
84+ train.py | 3 +++
85+ 2 files changed, 4 insertions(+)
86+ create mode 100644 requirements.txt
87+
88+diff --git a/requirements.txt b/requirements.txt
89+new file mode 100644
90+index 0000000..4968a39
91+--- /dev/null
92++++ b/requirements.txt
93+@@ -0,0 +1 @@
94++torch==2.3.1
95+diff --git a/train.py b/train.py
96+index 5c027f4..d21dac3 100644
97+--- a/train.py
98++++ b/train.py
99+@@ -1,2 +1,5 @@
100++import torch
101++
102+ if __name__ == "__main__":
103+ print("train!")
104++ torch.rand(3,6)
105+--
106+2.45.2
107+
108+
109+From 22dde1259c34a166d5a9335ebe5236e79541cc63 Mon Sep 17 00:00:00 2001
110+From: Eric Bower <me@erock.io>
111+Date: Tue, 23 Jul 2024 10:14:37 -0400
112+Subject: [PATCH 2/2] docs: readme
113+
114+---
115+ README.md | 4 +++-
116+ 1 file changed, 3 insertions(+), 1 deletion(-)
117+
118+diff --git a/README.md b/README.md
119+index 8f3a780..3043953 100644
120+--- a/README.md
121++++ b/README.md
122+@@ -1,3 +1,5 @@
123+ # Let's build an RNN
124+
125+-This repo demonstrates building an RNN using `pytorch`
126++This repo demonstrates building an RNN using `pytorch`.
127++
128++Here is some more readme information.
129+--
130+2.45.2
131+
132diff --git a/fixtures/a_c.patch b/fixtures/a_c.patch
133new file mode 100644
134index 0000000..4960260
135--- /dev/null
136+++ b/fixtures/a_c.patch
137@@ -0,0 +1,33 @@
138+From 166848469e0b954c2e14233233f3824a46dcddb8 Mon Sep 17 00:00:00 2001
139+From: Eric Bower <me@erock.io>
140+Date: Tue, 23 Jul 2024 10:06:00 -0400
141+Subject: [PATCH] chore: add torch and create random tensor
142+
143+---
144+ requirements.txt | 1 +
145+ train.py | 3 +++
146+ 2 files changed, 4 insertions(+)
147+ create mode 100644 requirements.txt
148+
149+diff --git a/requirements.txt b/requirements.txt
150+new file mode 100644
151+index 0000000..4968a39
152+--- /dev/null
153++++ b/requirements.txt
154+@@ -0,0 +1 @@
155++torch==2.3.1
156+diff --git a/train.py b/train.py
157+index 5c027f4..d21dac3 100644
158+--- a/train.py
159++++ b/train.py
160+@@ -1,2 +1,5 @@
161++import torch
162++
163+ if __name__ == "__main__":
164+ print("train!")
165++ torch.rand(3,6)
166+
167+base-commit: 59456574a0bfee9f71c91c13046173c820152346
168+--
169+2.45.2
170+
171diff --git a/fixtures/a_c_added_commit.patch b/fixtures/a_c_added_commit.patch
172new file mode 100644
173index 0000000..f5ce9f3
174--- /dev/null
175+++ b/fixtures/a_c_added_commit.patch
176@@ -0,0 +1,80 @@
177+From 33c682ac27479f501924cf159d0a75ad91deb589 Mon Sep 17 00:00:00 2001
178+From: Eric Bower <me@erock.io>
179+Date: Tue, 23 Jul 2024 10:07:57 -0400
180+Subject: [PATCH 1/3] chore: add torch and create random tensor
181+
182+---
183+ requirements.txt | 1 +
184+ train.py | 3 +++
185+ 2 files changed, 4 insertions(+)
186+ create mode 100644 requirements.txt
187+
188+diff --git a/requirements.txt b/requirements.txt
189+new file mode 100644
190+index 0000000..4968a39
191+--- /dev/null
192++++ b/requirements.txt
193+@@ -0,0 +1 @@
194++torch==2.3.1
195+diff --git a/train.py b/train.py
196+index 5c027f4..d21dac3 100644
197+--- a/train.py
198++++ b/train.py
199+@@ -1,2 +1,5 @@
200++import torch
201++
202+ if __name__ == "__main__":
203+ print("train!")
204++ torch.rand(3,6)
205+--
206+2.45.2
207+
208+
209+From 22dde1259c34a166d5a9335ebe5236e79541cc63 Mon Sep 17 00:00:00 2001
210+From: Eric Bower <me@erock.io>
211+Date: Tue, 23 Jul 2024 10:14:37 -0400
212+Subject: [PATCH 2/3] docs: readme
213+
214+---
215+ README.md | 4 +++-
216+ 1 file changed, 3 insertions(+), 1 deletion(-)
217+
218+diff --git a/README.md b/README.md
219+index 8f3a780..3043953 100644
220+--- a/README.md
221++++ b/README.md
222+@@ -1,3 +1,5 @@
223+ # Let's build an RNN
224+
225+-This repo demonstrates building an RNN using `pytorch`
226++This repo demonstrates building an RNN using `pytorch`.
227++
228++Here is some more readme information.
229+--
230+2.45.2
231+
232+
233+From b248060488df529b850060b3c86417bb87d490cc Mon Sep 17 00:00:00 2001
234+From: Eric Bower <me@erock.io>
235+Date: Tue, 23 Jul 2024 10:20:44 -0400
236+Subject: [PATCH 3/3] chore: make tensor 6x6
237+
238+---
239+ train.py | 4 +++-
240+ 1 file changed, 3 insertions(+), 1 deletion(-)
241+
242+diff --git a/train.py b/train.py
243+index d21dac3..8cd47e0 100644
244+--- a/train.py
245++++ b/train.py
246+@@ -2,4 +2,6 @@ import torch
247+
248+ if __name__ == "__main__":
249+ print("train!")
250+- torch.rand(3,6)
251++ # let's create a 6x6 tensor!
252++ tensor = torch.rand(6,6)
253++ print(tensor)
254+--
255+2.45.2
256+
257diff --git a/fixtures/a_c_changed_commit.patch b/fixtures/a_c_changed_commit.patch
258new file mode 100644
259index 0000000..259a434
260--- /dev/null
261+++ b/fixtures/a_c_changed_commit.patch
262@@ -0,0 +1,60 @@
263+From 33c682ac27479f501924cf159d0a75ad91deb589 Mon Sep 17 00:00:00 2001
264+From: Eric Bower <me@erock.io>
265+Date: Tue, 23 Jul 2024 10:07:57 -0400
266+Subject: [PATCH 1/2] chore: add torch and create random tensor
267+
268+---
269+ requirements.txt | 1 +
270+ train.py | 3 +++
271+ 2 files changed, 4 insertions(+)
272+ create mode 100644 requirements.txt
273+
274+diff --git a/requirements.txt b/requirements.txt
275+new file mode 100644
276+index 0000000..4968a39
277+--- /dev/null
278++++ b/requirements.txt
279+@@ -0,0 +1 @@
280++torch==2.3.1
281+diff --git a/train.py b/train.py
282+index 5c027f4..d21dac3 100644
283+--- a/train.py
284++++ b/train.py
285+@@ -1,2 +1,5 @@
286++import torch
287++
288+ if __name__ == "__main__":
289+ print("train!")
290++ torch.rand(3,6)
291+--
292+2.45.2
293+
294+
295+From dce20e70280d92aeb88c3d603ad67043ead772fb Mon Sep 17 00:00:00 2001
296+From: Eric Bower <me@erock.io>
297+Date: Tue, 23 Jul 2024 10:14:37 -0400
298+Subject: [PATCH 2/2] docs: readme
299+
300+---
301+ README.md | 9 ++++++++-
302+ 1 file changed, 8 insertions(+), 1 deletion(-)
303+
304+diff --git a/README.md b/README.md
305+index 8f3a780..ba0293b 100644
306+--- a/README.md
307++++ b/README.md
308+@@ -1,3 +1,10 @@
309+ # Let's build an RNN
310+
311+-This repo demonstrates building an RNN using `pytorch`
312++This repo demonstrates building an RNN using `pytorch`.
313++
314++Here is some more readme information.
315++
316++Here is how to run this project locally:
317++
318++- install python and pip
319++- `pip install -r requirements.txt`
320+--
321+2.45.2
322+
323diff --git a/fixtures/a_c_reorder.patch b/fixtures/a_c_reorder.patch
324new file mode 100644
325index 0000000..bc90f03
326--- /dev/null
327+++ b/fixtures/a_c_reorder.patch
328@@ -0,0 +1,55 @@
329+From 7dbb94ca1bc8cadf1ce17dacb89172217d88de07 Mon Sep 17 00:00:00 2001
330+From: Eric Bower <me@erock.io>
331+Date: Tue, 23 Jul 2024 10:15:23 -0400
332+Subject: [PATCH 1/2] docs: readme
333+
334+---
335+ README.md | 4 +++-
336+ 1 file changed, 3 insertions(+), 1 deletion(-)
337+
338+diff --git a/README.md b/README.md
339+index 8f3a780..3043953 100644
340+--- a/README.md
341++++ b/README.md
342+@@ -1,3 +1,5 @@
343+ # Let's build an RNN
344+
345+-This repo demonstrates building an RNN using `pytorch`
346++This repo demonstrates building an RNN using `pytorch`.
347++
348++Here is some more readme information.
349+--
350+2.45.2
351+
352+
353+From ad175875e2bf320859554bae73743675cc5ce444 Mon Sep 17 00:00:00 2001
354+From: Eric Bower <me@erock.io>
355+Date: Tue, 23 Jul 2024 10:06:00 -0400
356+Subject: [PATCH 2/2] chore: add torch and create random tensor
357+
358+---
359+ requirements.txt | 1 +
360+ train.py | 3 +++
361+ 2 files changed, 4 insertions(+)
362+ create mode 100644 requirements.txt
363+
364+diff --git a/requirements.txt b/requirements.txt
365+new file mode 100644
366+index 0000000..4968a39
367+--- /dev/null
368++++ b/requirements.txt
369+@@ -0,0 +1 @@
370++torch==2.3.1
371+diff --git a/train.py b/train.py
372+index 5c027f4..d21dac3 100644
373+--- a/train.py
374++++ b/train.py
375+@@ -1,2 +1,5 @@
376++import torch
377++
378+ if __name__ == "__main__":
379+ print("train!")
380++ torch.rand(3,6)
381+--
382+2.45.2
383+
384diff --git a/fixtures/a_c_rm_commit.patch b/fixtures/a_c_rm_commit.patch
385new file mode 100644
386index 0000000..c6a4c5e
387--- /dev/null
388+++ b/fixtures/a_c_rm_commit.patch
389@@ -0,0 +1,23 @@
390+From 7dbb94ca1bc8cadf1ce17dacb89172217d88de07 Mon Sep 17 00:00:00 2001
391+From: Eric Bower <me@erock.io>
392+Date: Tue, 23 Jul 2024 10:15:23 -0400
393+Subject: [PATCH] docs: readme
394+
395+---
396+ README.md | 4 +++-
397+ 1 file changed, 3 insertions(+), 1 deletion(-)
398+
399+diff --git a/README.md b/README.md
400+index 8f3a780..3043953 100644
401+--- a/README.md
402++++ b/README.md
403+@@ -1,3 +1,5 @@
404+ # Let's build an RNN
405+
406+-This repo demonstrates building an RNN using `pytorch`
407++This repo demonstrates building an RNN using `pytorch`.
408++
409++Here is some more readme information.
410+--
411+2.45.2
412+
413diff --git a/fixtures/expected_commit_changed.txt b/fixtures/expected_commit_changed.txt
414new file mode 100644
415index 0000000..ac4499c
416--- /dev/null
417+++ b/fixtures/expected_commit_changed.txt
418@@ -0,0 +1,11 @@
419+1: 33c682a = 1: 33c682a chore: add torch and create random tensor
420+2: 22dde12 ! 2: 0185f34 docs: readme
421+ @@ README.md
422+ +This repo demonstrates building an RNN using `pytorch`.
423+ +
424+ +Here is some more readme information.
425+ ++
426+ ++Here is how to run this project locally:
427+ ++
428+ ++- install python and pip
429+ ++- `pip install -r requirements.txt`
430diff --git a/go.mod b/go.mod
431index 4dc95e6..53504d3 100644
432--- a/go.mod
433+++ b/go.mod
434@@ -14,6 +14,7 @@ require (
435 github.com/knadh/koanf/providers/env v0.1.0
436 github.com/knadh/koanf/providers/file v1.0.0
437 github.com/knadh/koanf/v2 v2.1.1
438+ github.com/sergi/go-diff v1.1.0
439 github.com/urfave/cli/v2 v2.27.2
440 golang.org/x/crypto v0.21.0
441 modernc.org/sqlite v1.27.0
442diff --git a/go.sum b/go.sum
443index 1ec46c6..5bc1160 100644
444--- a/go.sum
445+++ b/go.sum
446@@ -8,8 +8,6 @@ github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFI
447 github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
448 github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
449 github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
450-github.com/bluekeyes/go-gitdiff v0.7.2 h1:42jrcVZdjjxXtVsFNYTo/I6T1ZvIiQL+iDDLiH904hw=
451-github.com/bluekeyes/go-gitdiff v0.7.2/go.mod h1:QpfYYO1E0fTVHVZAZKiRjtSGY9823iCdvGXBcEzHGbM=
452 github.com/bluekeyes/go-gitdiff v0.7.4-0.20240715034416-0a4e55f9a190 h1:k6Ep4yQtmsoP/St4bf7ofXyWc6ITB/FyGy9ewaAn5os=
453 github.com/bluekeyes/go-gitdiff v0.7.4-0.20240715034416-0a4e55f9a190/go.mod h1:QpfYYO1E0fTVHVZAZKiRjtSGY9823iCdvGXBcEzHGbM=
454 github.com/charmbracelet/bubbletea v0.25.0 h1:bAfwk7jRz7FKFl9RzlIULPkStffg5k6pNt5dywy4TcM=
455@@ -36,6 +34,7 @@ github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lV
456 github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
457 github.com/creack/pty v1.1.21 h1:1/QdRyBaHHJP61QkWMXlOIBfsgdDeeKfK8SYVUWJKf0=
458 github.com/creack/pty v1.1.21/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
459+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
460 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
461 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
462 github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI=
463@@ -74,8 +73,11 @@ github.com/knadh/koanf/providers/file v1.0.0 h1:DtPvSQBeF+N0QLPMz0yf2bx0nFSxUcnc
464 github.com/knadh/koanf/providers/file v1.0.0/go.mod h1:/faSBcv2mxPVjFrXck95qeoyoZ5myJ6uxN8OOVNJJCI=
465 github.com/knadh/koanf/v2 v2.1.1 h1:/R8eXqasSTsmDCsAyYj+81Wteg8AqrV9CP6gvsTsOmM=
466 github.com/knadh/koanf/v2 v2.1.1/go.mod h1:4mnTRbZCK+ALuBXHZMjDfG9y714L7TykVnZkXbMU3Es=
467+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
468 github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
469 github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
470+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
471+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
472 github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
473 github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
474 github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
475@@ -107,6 +109,7 @@ github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo
476 github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8=
477 github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
478 github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
479+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
480 github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
481 github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
482 github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
483@@ -119,6 +122,10 @@ github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDN
484 github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
485 github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
486 github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
487+github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
488+github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
489+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
490+github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
491 github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
492 github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
493 github.com/urfave/cli/v2 v2.27.2 h1:6e0H+AkS+zDckwPCUrZkKX38mRaau4nL2uipkJpbkcI=
494@@ -143,6 +150,10 @@ golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
495 golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
496 golang.org/x/tools v0.15.0 h1:zdAyfUGbYmuVokhzVmghFl2ZJh5QhcfebBgmVPFYA+8=
497 golang.org/x/tools v0.15.0/go.mod h1:hpksKq4dtpQWS1uQ61JkdqWM3LscIS6Slf+VVkm+wQk=
498+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
499+gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
500+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
501+gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
502 gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
503 gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
504 lukechampine.com/uint128 v1.2.0 h1:mBi/5l91vocEN8otkC5bDLhi2KdCticRiwbdB0O+rjI=
505diff --git a/pr.go b/pr.go
506index 9e535a0..984fe92 100644
507--- a/pr.go
508+++ b/pr.go
509@@ -447,7 +447,7 @@ func (cmd PrCmd) SubmitPatchRequest(repoID string, userID int64, patchset io.Rea
510 _ = tx.Rollback()
511 }()
512
513- patches, err := parsePatchset(patchset)
514+ patches, err := ParsePatchset(patchset)
515 if err != nil {
516 return nil, err
517 }
518@@ -541,7 +541,7 @@ func (cmd PrCmd) SubmitPatchset(prID int64, userID int64, op PatchsetOp, patchse
519 _ = tx.Rollback()
520 }()
521
522- patches, err := parsePatchset(patchset)
523+ patches, err := ParsePatchset(patchset)
524 if err != nil {
525 return fin, err
526 }
527diff --git a/range_diff.go b/range_diff.go
528new file mode 100644
529index 0000000..28cfaef
530--- /dev/null
531+++ b/range_diff.go
532@@ -0,0 +1,188 @@
533+package git
534+
535+import (
536+ "fmt"
537+ "math"
538+
539+ "github.com/sergi/go-diff/diffmatchpatch"
540+)
541+
542+var COST_MAX = 65536
543+var RANGE_DIFF_CREATION_FACTOR_DEFAULT = 60
544+
545+type PatchRange struct {
546+ *Patch
547+ Matching int
548+}
549+
550+func NewPatchRange(patch *Patch) *PatchRange {
551+ return &PatchRange{
552+ Patch: patch,
553+ }
554+}
555+
556+func output(a []*PatchRange, b []*PatchRange) string {
557+ out := ""
558+ for _, patchB := range b {
559+ patchA := a[patchB.Matching]
560+ if patchB.ContentSha == patchA.ContentSha {
561+ out += outputPairHeader(patchA, patchB, patchB.Matching+1, patchA.Matching+1)
562+ }
563+ }
564+ return out
565+}
566+
567+func outputPairHeader(a *PatchRange, b *PatchRange, aIndex, bIndex int) string {
568+ return fmt.Sprintf("%d: %s = %d: %s %s\n", aIndex, truncateSha(a.CommitSha), bIndex, truncateSha(b.CommitSha), a.Title)
569+}
570+
571+func RangeDiff(a []*Patch, b []*Patch) string {
572+ aPatches := []*PatchRange{}
573+ for _, patch := range a {
574+ aPatches = append(aPatches, NewPatchRange(patch))
575+ }
576+ bPatches := []*PatchRange{}
577+ for _, patch := range b {
578+ bPatches = append(bPatches, NewPatchRange(patch))
579+ }
580+ findExactMatches(aPatches, bPatches)
581+ getCorrespondences(aPatches, bPatches, RANGE_DIFF_CREATION_FACTOR_DEFAULT)
582+ return output(aPatches, bPatches)
583+}
584+
585+func findExactMatches(a []*PatchRange, b []*PatchRange) {
586+ for i, patchA := range a {
587+ for j, patchB := range b {
588+ if patchA.ContentSha == patchB.ContentSha {
589+ patchA.Matching = j
590+ patchB.Matching = i
591+ }
592+ }
593+ }
594+}
595+
596+func createMatrix(rows, cols int) [][]int {
597+ mat := make([][]int, rows)
598+ for i := range mat {
599+ mat[i] = make([]int, cols)
600+ }
601+ return mat
602+}
603+
604+func diffsize(a *PatchRange, b *PatchRange) int {
605+ dmp := diffmatchpatch.New()
606+ diffs := dmp.DiffMain(a.RawText, b.RawText, false)
607+ return len(dmp.DiffPrettyText(diffs))
608+}
609+
610+func getCorrespondences(a []*PatchRange, b []*PatchRange, creationFactor int) {
611+ // n := len(a) + len(b)
612+ fmt.Println(len(a), len(b))
613+ cost := createMatrix(len(a), len(b))
614+
615+ for i, patchA := range a {
616+ var c int
617+ for j, patchB := range b {
618+ if patchA.Matching == j {
619+ c = 0
620+ } else if patchA.Matching == 0 && patchB.Matching == 0 {
621+ c = diffsize(patchA, patchB)
622+ } else {
623+ c = COST_MAX
624+ }
625+ cost[i][j] = c
626+ }
627+ }
628+
629+ assignment := computeAssignment(cost, len(a), len(b))
630+ for i, j := range assignment {
631+ if j < len(b) {
632+ a[i].Matching = j
633+ b[j].Matching = i
634+ }
635+ }
636+
637+ fmt.Println(cost, assignment)
638+ fmt.Println("A==")
639+ for _, patch := range a {
640+ fmt.Println("matches", b[patch.Matching].Title)
641+ }
642+
643+ fmt.Println("B==")
644+ for _, patch := range b {
645+ fmt.Println("matches", a[patch.Matching].Title)
646+ }
647+}
648+
649+// computeAssignment assigns patches using the Hungarian algorithm.
650+func computeAssignment(costMatrix [][]int, m, n int) []int {
651+ u := make([]int, m+1) // potential for workers
652+ v := make([]int, n+1) // potential for jobs
653+ p := make([]int, n+1) // job assignment
654+ way := make([]int, n+1)
655+
656+ for i := 1; i <= m; i++ {
657+ links := make([]int, n+1)
658+ minV := make([]int, n+1)
659+ used := make([]bool, n+1)
660+ for j := 0; j <= n; j++ {
661+ minV[j] = math.MaxInt32
662+ used[j] = false
663+ }
664+
665+ j0 := 0
666+ p[0] = i
667+
668+ for {
669+ used[j0] = true
670+ i0 := p[j0]
671+ delta := math.MaxInt32
672+ j1 := 0
673+
674+ for j := 1; j <= n; j++ {
675+ if !used[j] {
676+ cur := costMatrix[i0-1][j-1] - u[i0] - v[j]
677+ if cur < minV[j] {
678+ minV[j] = cur
679+ links[j] = j0
680+ }
681+ if minV[j] < delta {
682+ delta = minV[j]
683+ j1 = j
684+ }
685+ }
686+ }
687+
688+ for j := 0; j <= n; j++ {
689+ if used[j] {
690+ u[p[j]] += delta
691+ v[j] -= delta
692+ } else {
693+ minV[j] -= delta
694+ }
695+ }
696+
697+ j0 = j1
698+ if p[j0] == 0 {
699+ break
700+ }
701+ }
702+
703+ for {
704+ j1 := way[j0]
705+ p[j0] = p[j1]
706+ j0 = j1
707+ if j0 == 0 {
708+ break
709+ }
710+ }
711+ }
712+
713+ assignment := make([]int, m)
714+ for j := 1; j <= n; j++ {
715+ if p[j] > 0 {
716+ assignment[p[j]-1] = j - 1
717+ }
718+ }
719+ return assignment
720+}
721diff --git a/range_diff_test.go b/range_diff_test.go
722new file mode 100644
723index 0000000..8705bff
724--- /dev/null
725+++ b/range_diff_test.go
726@@ -0,0 +1,274 @@
727+package git
728+
729+import (
730+ "fmt"
731+ "testing"
732+
733+ "github.com/picosh/git-pr/fixtures"
734+)
735+
736+func bail(err error) {
737+ if err != nil {
738+ panic(bail)
739+ }
740+}
741+
742+func cmp(afile, bfile string) string {
743+ a, err := fixtures.Fixtures.Open(afile)
744+ bail(err)
745+ b, err := fixtures.Fixtures.Open(bfile)
746+ bail(err)
747+ aPatches, err := ParsePatchset(a)
748+ bail(err)
749+ bPatches, err := ParsePatchset(b)
750+ bail(err)
751+ actual := RangeDiff(aPatches, bPatches)
752+ return actual
753+}
754+
755+func fail(expected, actual string) string {
756+ return fmt.Sprintf("expected:[%s] actual:[%s]", expected, actual)
757+}
758+
759+// https://git.kernel.org/tree/t/t3206-range-diff.sh?id=d19b6cd2dd72dc811f19df4b32c7ed223256c3ee
760+
761+// simple A..B A..C (unmodified)
762+/*
763+ 1: $(test_oid t1) = 1: $(test_oid u1) s/5/A/
764+ 2: $(test_oid t2) = 2: $(test_oid u2) s/4/A/
765+ 3: $(test_oid t3) = 3: $(test_oid u3) s/11/B/
766+ 4: $(test_oid t4) = 4: $(test_oid u4) s/12/B/
767+*/
768+func TestRangeDiffUnmodified(t *testing.T) {
769+ actual := cmp("a_b.patch", "a_c.patch")
770+ expected := "1: 33c682a = 1: 1668484 chore: add torch and create random tensor\n"
771+ if expected != actual {
772+ t.Fatalf(fail(expected, actual))
773+ }
774+}
775+
776+// trivial reordering
777+/*
778+ 1: $(test_oid t1) = 1: $(test_oid r1) s/5/A/
779+ 3: $(test_oid t3) = 2: $(test_oid r2) s/11/B/
780+ 4: $(test_oid t4) = 3: $(test_oid r3) s/12/B/
781+ 2: $(test_oid t2) = 4: $(test_oid r4) s/4/A/
782+*/
783+func TestRangeDiffTrivialReordering(t *testing.T) {
784+ actual := cmp("a_b_reorder.patch", "a_c_reorder.patch")
785+ expected := `2: 22dde12 = 1: 7dbb94c docs: readme
786+1: 33c682a = 2: ad17587 chore: add torch and create random tensor
787+`
788+ if expected != actual {
789+ t.Fatalf(fail(expected, actual))
790+ }
791+}
792+
793+// removed commit
794+/*
795+ 1: $(test_oid t1) = 1: $(test_oid d1) s/5/A/
796+ 2: $(test_oid t2) < -: $(test_oid __) s/4/A/
797+ 3: $(test_oid t3) = 2: $(test_oid d2) s/11/B/
798+ 4: $(test_oid t4) = 3: $(test_oid d3) s/12/B/
799+*/
800+func TestRangeDiffRemovedCommit(t *testing.T) {
801+ actual := cmp("a_b_reorder.patch", "a_c_reorder.patch")
802+ expected := `1: 33c682a < -: ------- chore: add torch and create random tensor
803+2: 22dde12 = 1: 7dbb94c docs: readme`
804+ if expected != actual {
805+ t.Fatalf(fail(expected, actual))
806+ }
807+}
808+
809+// added commit
810+/*
811+ 1: $(test_oid t1) = 1: $(test_oid a1) s/5/A/
812+ 2: $(test_oid t2) = 2: $(test_oid a2) s/4/A/
813+ -: $(test_oid __) > 3: $(test_oid a3) s/6/A/
814+ 3: $(test_oid t3) = 4: $(test_oid a4) s/11/B/
815+ 4: $(test_oid t4) = 5: $(test_oid a5) s/12/B/
816+*/
817+/* func TestRangeDiffAddedCommit(t *testing.T) {
818+ actual := ""
819+ expected := `1: 33c682a = 1: 33c682a chore: add torch and create random tensor
820+2: 22dde12 = 2: 22dde12 docs: readme
821+-: ------- > 3: b248060 chore: make tensor 6x6`
822+ if expected != actual {
823+ t.Fatalf("expected:%s actual:%s", expected, actual)
824+ }
825+} */
826+
827+// changed commit
828+/*
829+ 1: $(test_oid t1) = 1: $(test_oid c1) s/5/A/
830+ 2: $(test_oid t2) = 2: $(test_oid c2) s/4/A/
831+ 3: $(test_oid t3) ! 3: $(test_oid c3) s/11/B/
832+ @@ file: A
833+ 9
834+ 10
835+ -11
836+ -+B
837+ ++BB
838+ 12
839+ 13
840+ 14
841+ 4: $(test_oid t4) ! 4: $(test_oid c4) s/12/B/
842+ @@ file
843+ @@ file: A
844+ 9
845+ 10
846+ - B
847+ + BB
848+ -12
849+ +B
850+ 13
851+*/
852+/* func TestRangeDiffChangedCommit(t *testing.T) {
853+ actual := ""
854+ fp, err := fixtures.Fixtures.ReadFile("extected_commit_changed.txt")
855+ if err != nil {
856+ t.Fatalf("file not found")
857+ }
858+ expected := string(fp)
859+ if expected != actual {
860+ t.Fatalf("expected:%s actual:%s", expected, actual)
861+ }
862+} */
863+
864+// renamed file
865+/*
866+ 1: $(test_oid t1) = 1: $(test_oid n1) s/5/A/
867+ 2: $(test_oid t2) ! 2: $(test_oid n2) s/4/A/
868+ @@ Metadata
869+ ZAuthor: Thomas Rast <trast@inf.ethz.ch>
870+ Z
871+ Z ## Commit message ##
872+ - s/4/A/
873+ + s/4/A/ + rename file
874+ Z
875+ - ## file ##
876+ + ## file => renamed-file ##
877+ Z@@
878+ Z 1
879+ Z 2
880+ 3: $(test_oid t3) ! 3: $(test_oid n3) s/11/B/
881+ @@ Metadata
882+ Z ## Commit message ##
883+ Z s/11/B/
884+ Z
885+ - ## file ##
886+ -@@ file: A
887+ + ## renamed-file ##
888+ +@@ renamed-file: A
889+ Z 8
890+ Z 9
891+ Z 10
892+ 4: $(test_oid t4) ! 4: $(test_oid n4) s/12/B/
893+ @@ Metadata
894+ Z ## Commit message ##
895+ Z s/12/B/
896+ Z
897+ - ## file ##
898+ -@@ file: A
899+ + ## renamed-file ##
900+ +@@ renamed-file: A
901+ Z 9
902+ Z 10
903+ Z B
904+*/
905+// func TestRangeDiffRenamedFile(t *testing.T) {}
906+
907+// file with mode only change
908+/*
909+ 1: $(test_oid t2) ! 1: $(test_oid o1) s/4/A/
910+ @@ Metadata
911+ ZAuthor: Thomas Rast <trast@inf.ethz.ch>
912+ Z
913+ Z ## Commit message ##
914+ - s/4/A/
915+ + s/4/A/ + add other-file
916+ Z
917+ Z ## file ##
918+ Z@@
919+ @@ file
920+ Z A
921+ Z 6
922+ Z 7
923+ +
924+ + ## other-file (new) ##
925+ 2: $(test_oid t3) ! 2: $(test_oid o2) s/11/B/
926+ @@ Metadata
927+ ZAuthor: Thomas Rast <trast@inf.ethz.ch>
928+ Z
929+ Z ## Commit message ##
930+ - s/11/B/
931+ + s/11/B/ + mode change other-file
932+ Z
933+ Z ## file ##
934+ Z@@ file: A
935+ @@ file: A
936+ Z 12
937+ Z 13
938+ Z 14
939+ +
940+ + ## other-file (mode change 100644 => 100755) ##
941+ 3: $(test_oid t4) = 3: $(test_oid o3) s/12/B/
942+*/
943+// func TestRangeDiffFileWithModeOnlyChange(t *testing.T) {}
944+
945+// file added and later removed
946+/*
947+ 1: $(test_oid t1) = 1: $(test_oid s1) s/5/A/
948+ 2: $(test_oid t2) ! 2: $(test_oid s2) s/4/A/
949+ @@ Metadata
950+ ZAuthor: Thomas Rast <trast@inf.ethz.ch>
951+ Z
952+ Z ## Commit message ##
953+ - s/4/A/
954+ + s/4/A/ + new-file
955+ Z
956+ Z ## file ##
957+ Z@@
958+ @@ file
959+ Z A
960+ Z 6
961+ Z 7
962+ +
963+ + ## new-file (new) ##
964+ 3: $(test_oid t3) ! 3: $(test_oid s3) s/11/B/
965+ @@ Metadata
966+ ZAuthor: Thomas Rast <trast@inf.ethz.ch>
967+ Z
968+ Z ## Commit message ##
969+ - s/11/B/
970+ + s/11/B/ + remove file
971+ Z
972+ Z ## file ##
973+ Z@@ file: A
974+ @@ file: A
975+ Z 12
976+ Z 13
977+ Z 14
978+ +
979+ + ## new-file (deleted) ##
980+ 4: $(test_oid t4) = 4: $(test_oid s4) s/12/B/
981+*/
982+// func TestRangeDiffFileAddedThenRemoved(t *testing.T) {}
983+
984+// changed message
985+/*
986+ 1: $(test_oid t1) = 1: $(test_oid m1) s/5/A/
987+ 2: $(test_oid t2) ! 2: $(test_oid m2) s/4/A/
988+ @@ Metadata
989+ Z ## Commit message ##
990+ Z s/4/A/
991+ Z
992+ + Also a silly comment here!
993+ +
994+ Z ## file ##
995+ Z@@
996+ Z 1
997+ 3: $(test_oid t3) = 3: $(test_oid m3) s/11/B/
998+ 4: $(test_oid t4) = 4: $(test_oid m4) s/12/B/
999+*/
1000+// func TestRangeDiffChangedMessage(t *testing.T) {}
1001diff --git a/util.go b/util.go
1002index 061bb6d..da69a60 100644
1003--- a/util.go
1004+++ b/util.go
1005@@ -109,7 +109,7 @@ func patchToDiff(patch io.Reader) (string, error) {
1006 return str[idx:], nil
1007 }
1008
1009-func parsePatchset(patchset io.Reader) ([]*Patch, error) {
1010+func ParsePatchset(patchset io.Reader) ([]*Patch, error) {
1011 patches := []*Patch{}
1012 buf := new(strings.Builder)
1013 _, err := io.Copy(buf, patchset)
1014@@ -172,12 +172,11 @@ func calcContentSha(diffFiles []*gitdiff.File, header *gitdiff.PatchHeader) stri
1015 authorEmail = header.Author.Email
1016 }
1017 content := fmt.Sprintf(
1018- "%s\n%s\n%s\n%s\n%s\n",
1019+ "%s\n%s\n%s\n%s\n",
1020 header.Title,
1021 header.Body,
1022 authorName,
1023 authorEmail,
1024- header.AuthorDate,
1025 )
1026 for _, diff := range diffFiles {
1027 // we need to ignore diffs with base commit because that depends
1028diff --git a/util_test.go b/util_test.go
1029index eaba33d..e579254 100644
1030--- a/util_test.go
1031+++ b/util_test.go
1032@@ -15,7 +15,7 @@ func TestParsePatchsetWithCover(t *testing.T) {
1033 if err != nil {
1034 t.Fatalf(err.Error())
1035 }
1036- actual, err := parsePatchset(file)
1037+ actual, err := ParsePatchset(file)
1038 if err != nil {
1039 t.Fatalf(err.Error())
1040 }
1041--
10422.45.2
1043
ps-35
by
erock
on feat: range diff
Eric Bower <me@erock.io>
fixtures/a_b.patch | 31 +++ fixtures/a_b_reorder.patch | 55 ++++++ fixtures/a_c.patch | 33 ++++ fixtures/a_c_added_commit.patch | 80 ++++++++ fixtures/a_c_changed_commit.patch | 60 ++++++ fixtures/a_c_reorder.patch | 55 ++++++ fixtures/a_c_rm_commit.patch | 23 +++ fixtures/expected_commit_changed.txt | 11 ++ go.mod | 1 + go.sum | 15 +- pr.go | 4 +- range_diff.go | 188 ++++++++++++++++++ range_diff_test.go | 274 +++++++++++++++++++++++++++ util.go | 5 +- util_test.go | 2 +- 15 files changed, 829 insertions(+), 8 deletions(-) create mode 100644 fixtures/a_b.patch create mode 100644 fixtures/a_b_reorder.patch create mode 100644 fixtures/a_c.patch create mode 100644 fixtures/a_c_added_commit.patch create mode 100644 fixtures/a_c_changed_commit.patch create mode 100644 fixtures/a_c_reorder.patch create mode 100644 fixtures/a_c_rm_commit.patch create mode 100644 fixtures/expected_commit_changed.txt create mode 100644 range_diff.go create mode 100644 range_diff_test.go
1From 778bcd8223bbe29732a5d8c0379bc9717d93f087 Mon Sep 17 00:00:00 2001
2From: Eric Bower <me@erock.io>
3Date: Tue, 23 Jul 2024 11:59:25 -0400
4Subject: [PATCH] feat: range diff
5
6---
7 fixtures/a_b.patch | 31 +++
8 fixtures/a_b_reorder.patch | 55 ++++++
9 fixtures/a_c.patch | 33 ++++
10 fixtures/a_c_added_commit.patch | 80 ++++++++
11 fixtures/a_c_changed_commit.patch | 60 ++++++
12 fixtures/a_c_reorder.patch | 55 ++++++
13 fixtures/a_c_rm_commit.patch | 23 +++
14 fixtures/expected_commit_changed.txt | 11 ++
15 go.mod | 1 +
16 go.sum | 15 +-
17 pr.go | 4 +-
18 range_diff.go | 188 ++++++++++++++++++
19 range_diff_test.go | 274 +++++++++++++++++++++++++++
20 util.go | 5 +-
21 util_test.go | 2 +-
22 15 files changed, 829 insertions(+), 8 deletions(-)
23 create mode 100644 fixtures/a_b.patch
24 create mode 100644 fixtures/a_b_reorder.patch
25 create mode 100644 fixtures/a_c.patch
26 create mode 100644 fixtures/a_c_added_commit.patch
27 create mode 100644 fixtures/a_c_changed_commit.patch
28 create mode 100644 fixtures/a_c_reorder.patch
29 create mode 100644 fixtures/a_c_rm_commit.patch
30 create mode 100644 fixtures/expected_commit_changed.txt
31 create mode 100644 range_diff.go
32 create mode 100644 range_diff_test.go
33
34diff --git a/fixtures/a_b.patch b/fixtures/a_b.patch
35new file mode 100644
36index 0000000..c2c5167
37--- /dev/null
38+++ b/fixtures/a_b.patch
39@@ -0,0 +1,31 @@
40+From 33c682ac27479f501924cf159d0a75ad91deb589 Mon Sep 17 00:00:00 2001
41+From: Eric Bower <me@erock.io>
42+Date: Tue, 23 Jul 2024 10:07:57 -0400
43+Subject: [PATCH] chore: add torch and create random tensor
44+
45+---
46+ requirements.txt | 1 +
47+ train.py | 3 +++
48+ 2 files changed, 4 insertions(+)
49+ create mode 100644 requirements.txt
50+
51+diff --git a/requirements.txt b/requirements.txt
52+new file mode 100644
53+index 0000000..4968a39
54+--- /dev/null
55++++ b/requirements.txt
56+@@ -0,0 +1 @@
57++torch==2.3.1
58+diff --git a/train.py b/train.py
59+index 5c027f4..d21dac3 100644
60+--- a/train.py
61++++ b/train.py
62+@@ -1,2 +1,5 @@
63++import torch
64++
65+ if __name__ == "__main__":
66+ print("train!")
67++ torch.rand(3,6)
68+--
69+2.45.2
70+
71diff --git a/fixtures/a_b_reorder.patch b/fixtures/a_b_reorder.patch
72new file mode 100644
73index 0000000..2524df9
74--- /dev/null
75+++ b/fixtures/a_b_reorder.patch
76@@ -0,0 +1,55 @@
77+From 33c682ac27479f501924cf159d0a75ad91deb589 Mon Sep 17 00:00:00 2001
78+From: Eric Bower <me@erock.io>
79+Date: Tue, 23 Jul 2024 10:07:57 -0400
80+Subject: [PATCH 1/2] chore: add torch and create random tensor
81+
82+---
83+ requirements.txt | 1 +
84+ train.py | 3 +++
85+ 2 files changed, 4 insertions(+)
86+ create mode 100644 requirements.txt
87+
88+diff --git a/requirements.txt b/requirements.txt
89+new file mode 100644
90+index 0000000..4968a39
91+--- /dev/null
92++++ b/requirements.txt
93+@@ -0,0 +1 @@
94++torch==2.3.1
95+diff --git a/train.py b/train.py
96+index 5c027f4..d21dac3 100644
97+--- a/train.py
98++++ b/train.py
99+@@ -1,2 +1,5 @@
100++import torch
101++
102+ if __name__ == "__main__":
103+ print("train!")
104++ torch.rand(3,6)
105+--
106+2.45.2
107+
108+
109+From 22dde1259c34a166d5a9335ebe5236e79541cc63 Mon Sep 17 00:00:00 2001
110+From: Eric Bower <me@erock.io>
111+Date: Tue, 23 Jul 2024 10:14:37 -0400
112+Subject: [PATCH 2/2] docs: readme
113+
114+---
115+ README.md | 4 +++-
116+ 1 file changed, 3 insertions(+), 1 deletion(-)
117+
118+diff --git a/README.md b/README.md
119+index 8f3a780..3043953 100644
120+--- a/README.md
121++++ b/README.md
122+@@ -1,3 +1,5 @@
123+ # Let's build an RNN
124+
125+-This repo demonstrates building an RNN using `pytorch`
126++This repo demonstrates building an RNN using `pytorch`.
127++
128++Here is some more readme information.
129+--
130+2.45.2
131+
132diff --git a/fixtures/a_c.patch b/fixtures/a_c.patch
133new file mode 100644
134index 0000000..4960260
135--- /dev/null
136+++ b/fixtures/a_c.patch
137@@ -0,0 +1,33 @@
138+From 166848469e0b954c2e14233233f3824a46dcddb8 Mon Sep 17 00:00:00 2001
139+From: Eric Bower <me@erock.io>
140+Date: Tue, 23 Jul 2024 10:06:00 -0400
141+Subject: [PATCH] chore: add torch and create random tensor
142+
143+---
144+ requirements.txt | 1 +
145+ train.py | 3 +++
146+ 2 files changed, 4 insertions(+)
147+ create mode 100644 requirements.txt
148+
149+diff --git a/requirements.txt b/requirements.txt
150+new file mode 100644
151+index 0000000..4968a39
152+--- /dev/null
153++++ b/requirements.txt
154+@@ -0,0 +1 @@
155++torch==2.3.1
156+diff --git a/train.py b/train.py
157+index 5c027f4..d21dac3 100644
158+--- a/train.py
159++++ b/train.py
160+@@ -1,2 +1,5 @@
161++import torch
162++
163+ if __name__ == "__main__":
164+ print("train!")
165++ torch.rand(3,6)
166+
167+base-commit: 59456574a0bfee9f71c91c13046173c820152346
168+--
169+2.45.2
170+
171diff --git a/fixtures/a_c_added_commit.patch b/fixtures/a_c_added_commit.patch
172new file mode 100644
173index 0000000..f5ce9f3
174--- /dev/null
175+++ b/fixtures/a_c_added_commit.patch
176@@ -0,0 +1,80 @@
177+From 33c682ac27479f501924cf159d0a75ad91deb589 Mon Sep 17 00:00:00 2001
178+From: Eric Bower <me@erock.io>
179+Date: Tue, 23 Jul 2024 10:07:57 -0400
180+Subject: [PATCH 1/3] chore: add torch and create random tensor
181+
182+---
183+ requirements.txt | 1 +
184+ train.py | 3 +++
185+ 2 files changed, 4 insertions(+)
186+ create mode 100644 requirements.txt
187+
188+diff --git a/requirements.txt b/requirements.txt
189+new file mode 100644
190+index 0000000..4968a39
191+--- /dev/null
192++++ b/requirements.txt
193+@@ -0,0 +1 @@
194++torch==2.3.1
195+diff --git a/train.py b/train.py
196+index 5c027f4..d21dac3 100644
197+--- a/train.py
198++++ b/train.py
199+@@ -1,2 +1,5 @@
200++import torch
201++
202+ if __name__ == "__main__":
203+ print("train!")
204++ torch.rand(3,6)
205+--
206+2.45.2
207+
208+
209+From 22dde1259c34a166d5a9335ebe5236e79541cc63 Mon Sep 17 00:00:00 2001
210+From: Eric Bower <me@erock.io>
211+Date: Tue, 23 Jul 2024 10:14:37 -0400
212+Subject: [PATCH 2/3] docs: readme
213+
214+---
215+ README.md | 4 +++-
216+ 1 file changed, 3 insertions(+), 1 deletion(-)
217+
218+diff --git a/README.md b/README.md
219+index 8f3a780..3043953 100644
220+--- a/README.md
221++++ b/README.md
222+@@ -1,3 +1,5 @@
223+ # Let's build an RNN
224+
225+-This repo demonstrates building an RNN using `pytorch`
226++This repo demonstrates building an RNN using `pytorch`.
227++
228++Here is some more readme information.
229+--
230+2.45.2
231+
232+
233+From b248060488df529b850060b3c86417bb87d490cc Mon Sep 17 00:00:00 2001
234+From: Eric Bower <me@erock.io>
235+Date: Tue, 23 Jul 2024 10:20:44 -0400
236+Subject: [PATCH 3/3] chore: make tensor 6x6
237+
238+---
239+ train.py | 4 +++-
240+ 1 file changed, 3 insertions(+), 1 deletion(-)
241+
242+diff --git a/train.py b/train.py
243+index d21dac3..8cd47e0 100644
244+--- a/train.py
245++++ b/train.py
246+@@ -2,4 +2,6 @@ import torch
247+
248+ if __name__ == "__main__":
249+ print("train!")
250+- torch.rand(3,6)
251++ # let's create a 6x6 tensor!
252++ tensor = torch.rand(6,6)
253++ print(tensor)
254+--
255+2.45.2
256+
257diff --git a/fixtures/a_c_changed_commit.patch b/fixtures/a_c_changed_commit.patch
258new file mode 100644
259index 0000000..259a434
260--- /dev/null
261+++ b/fixtures/a_c_changed_commit.patch
262@@ -0,0 +1,60 @@
263+From 33c682ac27479f501924cf159d0a75ad91deb589 Mon Sep 17 00:00:00 2001
264+From: Eric Bower <me@erock.io>
265+Date: Tue, 23 Jul 2024 10:07:57 -0400
266+Subject: [PATCH 1/2] chore: add torch and create random tensor
267+
268+---
269+ requirements.txt | 1 +
270+ train.py | 3 +++
271+ 2 files changed, 4 insertions(+)
272+ create mode 100644 requirements.txt
273+
274+diff --git a/requirements.txt b/requirements.txt
275+new file mode 100644
276+index 0000000..4968a39
277+--- /dev/null
278++++ b/requirements.txt
279+@@ -0,0 +1 @@
280++torch==2.3.1
281+diff --git a/train.py b/train.py
282+index 5c027f4..d21dac3 100644
283+--- a/train.py
284++++ b/train.py
285+@@ -1,2 +1,5 @@
286++import torch
287++
288+ if __name__ == "__main__":
289+ print("train!")
290++ torch.rand(3,6)
291+--
292+2.45.2
293+
294+
295+From dce20e70280d92aeb88c3d603ad67043ead772fb Mon Sep 17 00:00:00 2001
296+From: Eric Bower <me@erock.io>
297+Date: Tue, 23 Jul 2024 10:14:37 -0400
298+Subject: [PATCH 2/2] docs: readme
299+
300+---
301+ README.md | 9 ++++++++-
302+ 1 file changed, 8 insertions(+), 1 deletion(-)
303+
304+diff --git a/README.md b/README.md
305+index 8f3a780..ba0293b 100644
306+--- a/README.md
307++++ b/README.md
308+@@ -1,3 +1,10 @@
309+ # Let's build an RNN
310+
311+-This repo demonstrates building an RNN using `pytorch`
312++This repo demonstrates building an RNN using `pytorch`.
313++
314++Here is some more readme information.
315++
316++Here is how to run this project locally:
317++
318++- install python and pip
319++- `pip install -r requirements.txt`
320+--
321+2.45.2
322+
323diff --git a/fixtures/a_c_reorder.patch b/fixtures/a_c_reorder.patch
324new file mode 100644
325index 0000000..bc90f03
326--- /dev/null
327+++ b/fixtures/a_c_reorder.patch
328@@ -0,0 +1,55 @@
329+From 7dbb94ca1bc8cadf1ce17dacb89172217d88de07 Mon Sep 17 00:00:00 2001
330+From: Eric Bower <me@erock.io>
331+Date: Tue, 23 Jul 2024 10:15:23 -0400
332+Subject: [PATCH 1/2] docs: readme
333+
334+---
335+ README.md | 4 +++-
336+ 1 file changed, 3 insertions(+), 1 deletion(-)
337+
338+diff --git a/README.md b/README.md
339+index 8f3a780..3043953 100644
340+--- a/README.md
341++++ b/README.md
342+@@ -1,3 +1,5 @@
343+ # Let's build an RNN
344+
345+-This repo demonstrates building an RNN using `pytorch`
346++This repo demonstrates building an RNN using `pytorch`.
347++
348++Here is some more readme information.
349+--
350+2.45.2
351+
352+
353+From ad175875e2bf320859554bae73743675cc5ce444 Mon Sep 17 00:00:00 2001
354+From: Eric Bower <me@erock.io>
355+Date: Tue, 23 Jul 2024 10:06:00 -0400
356+Subject: [PATCH 2/2] chore: add torch and create random tensor
357+
358+---
359+ requirements.txt | 1 +
360+ train.py | 3 +++
361+ 2 files changed, 4 insertions(+)
362+ create mode 100644 requirements.txt
363+
364+diff --git a/requirements.txt b/requirements.txt
365+new file mode 100644
366+index 0000000..4968a39
367+--- /dev/null
368++++ b/requirements.txt
369+@@ -0,0 +1 @@
370++torch==2.3.1
371+diff --git a/train.py b/train.py
372+index 5c027f4..d21dac3 100644
373+--- a/train.py
374++++ b/train.py
375+@@ -1,2 +1,5 @@
376++import torch
377++
378+ if __name__ == "__main__":
379+ print("train!")
380++ torch.rand(3,6)
381+--
382+2.45.2
383+
384diff --git a/fixtures/a_c_rm_commit.patch b/fixtures/a_c_rm_commit.patch
385new file mode 100644
386index 0000000..c6a4c5e
387--- /dev/null
388+++ b/fixtures/a_c_rm_commit.patch
389@@ -0,0 +1,23 @@
390+From 7dbb94ca1bc8cadf1ce17dacb89172217d88de07 Mon Sep 17 00:00:00 2001
391+From: Eric Bower <me@erock.io>
392+Date: Tue, 23 Jul 2024 10:15:23 -0400
393+Subject: [PATCH] docs: readme
394+
395+---
396+ README.md | 4 +++-
397+ 1 file changed, 3 insertions(+), 1 deletion(-)
398+
399+diff --git a/README.md b/README.md
400+index 8f3a780..3043953 100644
401+--- a/README.md
402++++ b/README.md
403+@@ -1,3 +1,5 @@
404+ # Let's build an RNN
405+
406+-This repo demonstrates building an RNN using `pytorch`
407++This repo demonstrates building an RNN using `pytorch`.
408++
409++Here is some more readme information.
410+--
411+2.45.2
412+
413diff --git a/fixtures/expected_commit_changed.txt b/fixtures/expected_commit_changed.txt
414new file mode 100644
415index 0000000..ac4499c
416--- /dev/null
417+++ b/fixtures/expected_commit_changed.txt
418@@ -0,0 +1,11 @@
419+1: 33c682a = 1: 33c682a chore: add torch and create random tensor
420+2: 22dde12 ! 2: 0185f34 docs: readme
421+ @@ README.md
422+ +This repo demonstrates building an RNN using `pytorch`.
423+ +
424+ +Here is some more readme information.
425+ ++
426+ ++Here is how to run this project locally:
427+ ++
428+ ++- install python and pip
429+ ++- `pip install -r requirements.txt`
430diff --git a/go.mod b/go.mod
431index 4dc95e6..53504d3 100644
432--- a/go.mod
433+++ b/go.mod
434@@ -14,6 +14,7 @@ require (
435 github.com/knadh/koanf/providers/env v0.1.0
436 github.com/knadh/koanf/providers/file v1.0.0
437 github.com/knadh/koanf/v2 v2.1.1
438+ github.com/sergi/go-diff v1.1.0
439 github.com/urfave/cli/v2 v2.27.2
440 golang.org/x/crypto v0.21.0
441 modernc.org/sqlite v1.27.0
442diff --git a/go.sum b/go.sum
443index 1ec46c6..5bc1160 100644
444--- a/go.sum
445+++ b/go.sum
446@@ -8,8 +8,6 @@ github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFI
447 github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
448 github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
449 github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
450-github.com/bluekeyes/go-gitdiff v0.7.2 h1:42jrcVZdjjxXtVsFNYTo/I6T1ZvIiQL+iDDLiH904hw=
451-github.com/bluekeyes/go-gitdiff v0.7.2/go.mod h1:QpfYYO1E0fTVHVZAZKiRjtSGY9823iCdvGXBcEzHGbM=
452 github.com/bluekeyes/go-gitdiff v0.7.4-0.20240715034416-0a4e55f9a190 h1:k6Ep4yQtmsoP/St4bf7ofXyWc6ITB/FyGy9ewaAn5os=
453 github.com/bluekeyes/go-gitdiff v0.7.4-0.20240715034416-0a4e55f9a190/go.mod h1:QpfYYO1E0fTVHVZAZKiRjtSGY9823iCdvGXBcEzHGbM=
454 github.com/charmbracelet/bubbletea v0.25.0 h1:bAfwk7jRz7FKFl9RzlIULPkStffg5k6pNt5dywy4TcM=
455@@ -36,6 +34,7 @@ github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lV
456 github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
457 github.com/creack/pty v1.1.21 h1:1/QdRyBaHHJP61QkWMXlOIBfsgdDeeKfK8SYVUWJKf0=
458 github.com/creack/pty v1.1.21/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
459+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
460 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
461 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
462 github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI=
463@@ -74,8 +73,11 @@ github.com/knadh/koanf/providers/file v1.0.0 h1:DtPvSQBeF+N0QLPMz0yf2bx0nFSxUcnc
464 github.com/knadh/koanf/providers/file v1.0.0/go.mod h1:/faSBcv2mxPVjFrXck95qeoyoZ5myJ6uxN8OOVNJJCI=
465 github.com/knadh/koanf/v2 v2.1.1 h1:/R8eXqasSTsmDCsAyYj+81Wteg8AqrV9CP6gvsTsOmM=
466 github.com/knadh/koanf/v2 v2.1.1/go.mod h1:4mnTRbZCK+ALuBXHZMjDfG9y714L7TykVnZkXbMU3Es=
467+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
468 github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
469 github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
470+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
471+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
472 github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
473 github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
474 github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
475@@ -107,6 +109,7 @@ github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo
476 github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8=
477 github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
478 github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
479+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
480 github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
481 github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
482 github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
483@@ -119,6 +122,10 @@ github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDN
484 github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
485 github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
486 github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
487+github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
488+github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
489+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
490+github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
491 github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
492 github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
493 github.com/urfave/cli/v2 v2.27.2 h1:6e0H+AkS+zDckwPCUrZkKX38mRaau4nL2uipkJpbkcI=
494@@ -143,6 +150,10 @@ golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
495 golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
496 golang.org/x/tools v0.15.0 h1:zdAyfUGbYmuVokhzVmghFl2ZJh5QhcfebBgmVPFYA+8=
497 golang.org/x/tools v0.15.0/go.mod h1:hpksKq4dtpQWS1uQ61JkdqWM3LscIS6Slf+VVkm+wQk=
498+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
499+gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
500+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
501+gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
502 gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
503 gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
504 lukechampine.com/uint128 v1.2.0 h1:mBi/5l91vocEN8otkC5bDLhi2KdCticRiwbdB0O+rjI=
505diff --git a/pr.go b/pr.go
506index 9e535a0..984fe92 100644
507--- a/pr.go
508+++ b/pr.go
509@@ -447,7 +447,7 @@ func (cmd PrCmd) SubmitPatchRequest(repoID string, userID int64, patchset io.Rea
510 _ = tx.Rollback()
511 }()
512
513- patches, err := parsePatchset(patchset)
514+ patches, err := ParsePatchset(patchset)
515 if err != nil {
516 return nil, err
517 }
518@@ -541,7 +541,7 @@ func (cmd PrCmd) SubmitPatchset(prID int64, userID int64, op PatchsetOp, patchse
519 _ = tx.Rollback()
520 }()
521
522- patches, err := parsePatchset(patchset)
523+ patches, err := ParsePatchset(patchset)
524 if err != nil {
525 return fin, err
526 }
527diff --git a/range_diff.go b/range_diff.go
528new file mode 100644
529index 0000000..28cfaef
530--- /dev/null
531+++ b/range_diff.go
532@@ -0,0 +1,188 @@
533+package git
534+
535+import (
536+ "fmt"
537+ "math"
538+
539+ "github.com/sergi/go-diff/diffmatchpatch"
540+)
541+
542+var COST_MAX = 65536
543+var RANGE_DIFF_CREATION_FACTOR_DEFAULT = 60
544+
545+type PatchRange struct {
546+ *Patch
547+ Matching int
548+}
549+
550+func NewPatchRange(patch *Patch) *PatchRange {
551+ return &PatchRange{
552+ Patch: patch,
553+ }
554+}
555+
556+func output(a []*PatchRange, b []*PatchRange) string {
557+ out := ""
558+ for _, patchB := range b {
559+ patchA := a[patchB.Matching]
560+ if patchB.ContentSha == patchA.ContentSha {
561+ out += outputPairHeader(patchA, patchB, patchB.Matching+1, patchA.Matching+1)
562+ }
563+ }
564+ return out
565+}
566+
567+func outputPairHeader(a *PatchRange, b *PatchRange, aIndex, bIndex int) string {
568+ return fmt.Sprintf("%d: %s = %d: %s %s\n", aIndex, truncateSha(a.CommitSha), bIndex, truncateSha(b.CommitSha), a.Title)
569+}
570+
571+func RangeDiff(a []*Patch, b []*Patch) string {
572+ aPatches := []*PatchRange{}
573+ for _, patch := range a {
574+ aPatches = append(aPatches, NewPatchRange(patch))
575+ }
576+ bPatches := []*PatchRange{}
577+ for _, patch := range b {
578+ bPatches = append(bPatches, NewPatchRange(patch))
579+ }
580+ findExactMatches(aPatches, bPatches)
581+ getCorrespondences(aPatches, bPatches, RANGE_DIFF_CREATION_FACTOR_DEFAULT)
582+ return output(aPatches, bPatches)
583+}
584+
585+func findExactMatches(a []*PatchRange, b []*PatchRange) {
586+ for i, patchA := range a {
587+ for j, patchB := range b {
588+ if patchA.ContentSha == patchB.ContentSha {
589+ patchA.Matching = j
590+ patchB.Matching = i
591+ }
592+ }
593+ }
594+}
595+
596+func createMatrix(rows, cols int) [][]int {
597+ mat := make([][]int, rows)
598+ for i := range mat {
599+ mat[i] = make([]int, cols)
600+ }
601+ return mat
602+}
603+
604+func diffsize(a *PatchRange, b *PatchRange) int {
605+ dmp := diffmatchpatch.New()
606+ diffs := dmp.DiffMain(a.RawText, b.RawText, false)
607+ return len(dmp.DiffPrettyText(diffs))
608+}
609+
610+func getCorrespondences(a []*PatchRange, b []*PatchRange, creationFactor int) {
611+ // n := len(a) + len(b)
612+ fmt.Println(len(a), len(b))
613+ cost := createMatrix(len(a), len(b))
614+
615+ for i, patchA := range a {
616+ var c int
617+ for j, patchB := range b {
618+ if patchA.Matching == j {
619+ c = 0
620+ } else if patchA.Matching == 0 && patchB.Matching == 0 {
621+ c = diffsize(patchA, patchB)
622+ } else {
623+ c = COST_MAX
624+ }
625+ cost[i][j] = c
626+ }
627+ }
628+
629+ assignment := computeAssignment(cost, len(a), len(b))
630+ for i, j := range assignment {
631+ if j < len(b) {
632+ a[i].Matching = j
633+ b[j].Matching = i
634+ }
635+ }
636+
637+ fmt.Println(cost, assignment)
638+ fmt.Println("A==")
639+ for _, patch := range a {
640+ fmt.Println("matches", b[patch.Matching].Title)
641+ }
642+
643+ fmt.Println("B==")
644+ for _, patch := range b {
645+ fmt.Println("matches", a[patch.Matching].Title)
646+ }
647+}
648+
649+// computeAssignment assigns patches using the Hungarian algorithm.
650+func computeAssignment(costMatrix [][]int, m, n int) []int {
651+ u := make([]int, m+1) // potential for workers
652+ v := make([]int, n+1) // potential for jobs
653+ p := make([]int, n+1) // job assignment
654+ way := make([]int, n+1)
655+
656+ for i := 1; i <= m; i++ {
657+ links := make([]int, n+1)
658+ minV := make([]int, n+1)
659+ used := make([]bool, n+1)
660+ for j := 0; j <= n; j++ {
661+ minV[j] = math.MaxInt32
662+ used[j] = false
663+ }
664+
665+ j0 := 0
666+ p[0] = i
667+
668+ for {
669+ used[j0] = true
670+ i0 := p[j0]
671+ delta := math.MaxInt32
672+ j1 := 0
673+
674+ for j := 1; j <= n; j++ {
675+ if !used[j] {
676+ cur := costMatrix[i0-1][j-1] - u[i0] - v[j]
677+ if cur < minV[j] {
678+ minV[j] = cur
679+ links[j] = j0
680+ }
681+ if minV[j] < delta {
682+ delta = minV[j]
683+ j1 = j
684+ }
685+ }
686+ }
687+
688+ for j := 0; j <= n; j++ {
689+ if used[j] {
690+ u[p[j]] += delta
691+ v[j] -= delta
692+ } else {
693+ minV[j] -= delta
694+ }
695+ }
696+
697+ j0 = j1
698+ if p[j0] == 0 {
699+ break
700+ }
701+ }
702+
703+ for {
704+ j1 := way[j0]
705+ p[j0] = p[j1]
706+ j0 = j1
707+ if j0 == 0 {
708+ break
709+ }
710+ }
711+ }
712+
713+ assignment := make([]int, m)
714+ for j := 1; j <= n; j++ {
715+ if p[j] > 0 {
716+ assignment[p[j]-1] = j - 1
717+ }
718+ }
719+ return assignment
720+}
721diff --git a/range_diff_test.go b/range_diff_test.go
722new file mode 100644
723index 0000000..8705bff
724--- /dev/null
725+++ b/range_diff_test.go
726@@ -0,0 +1,274 @@
727+package git
728+
729+import (
730+ "fmt"
731+ "testing"
732+
733+ "github.com/picosh/git-pr/fixtures"
734+)
735+
736+func bail(err error) {
737+ if err != nil {
738+ panic(bail)
739+ }
740+}
741+
742+func cmp(afile, bfile string) string {
743+ a, err := fixtures.Fixtures.Open(afile)
744+ bail(err)
745+ b, err := fixtures.Fixtures.Open(bfile)
746+ bail(err)
747+ aPatches, err := ParsePatchset(a)
748+ bail(err)
749+ bPatches, err := ParsePatchset(b)
750+ bail(err)
751+ actual := RangeDiff(aPatches, bPatches)
752+ return actual
753+}
754+
755+func fail(expected, actual string) string {
756+ return fmt.Sprintf("expected:[%s] actual:[%s]", expected, actual)
757+}
758+
759+// https://git.kernel.org/tree/t/t3206-range-diff.sh?id=d19b6cd2dd72dc811f19df4b32c7ed223256c3ee
760+
761+// simple A..B A..C (unmodified)
762+/*
763+ 1: $(test_oid t1) = 1: $(test_oid u1) s/5/A/
764+ 2: $(test_oid t2) = 2: $(test_oid u2) s/4/A/
765+ 3: $(test_oid t3) = 3: $(test_oid u3) s/11/B/
766+ 4: $(test_oid t4) = 4: $(test_oid u4) s/12/B/
767+*/
768+func TestRangeDiffUnmodified(t *testing.T) {
769+ actual := cmp("a_b.patch", "a_c.patch")
770+ expected := "1: 33c682a = 1: 1668484 chore: add torch and create random tensor\n"
771+ if expected != actual {
772+ t.Fatalf(fail(expected, actual))
773+ }
774+}
775+
776+// trivial reordering
777+/*
778+ 1: $(test_oid t1) = 1: $(test_oid r1) s/5/A/
779+ 3: $(test_oid t3) = 2: $(test_oid r2) s/11/B/
780+ 4: $(test_oid t4) = 3: $(test_oid r3) s/12/B/
781+ 2: $(test_oid t2) = 4: $(test_oid r4) s/4/A/
782+*/
783+func TestRangeDiffTrivialReordering(t *testing.T) {
784+ actual := cmp("a_b_reorder.patch", "a_c_reorder.patch")
785+ expected := `2: 22dde12 = 1: 7dbb94c docs: readme
786+1: 33c682a = 2: ad17587 chore: add torch and create random tensor
787+`
788+ if expected != actual {
789+ t.Fatalf(fail(expected, actual))
790+ }
791+}
792+
793+// removed commit
794+/*
795+ 1: $(test_oid t1) = 1: $(test_oid d1) s/5/A/
796+ 2: $(test_oid t2) < -: $(test_oid __) s/4/A/
797+ 3: $(test_oid t3) = 2: $(test_oid d2) s/11/B/
798+ 4: $(test_oid t4) = 3: $(test_oid d3) s/12/B/
799+*/
800+func TestRangeDiffRemovedCommit(t *testing.T) {
801+ actual := cmp("a_b_reorder.patch", "a_c_reorder.patch")
802+ expected := `1: 33c682a < -: ------- chore: add torch and create random tensor
803+2: 22dde12 = 1: 7dbb94c docs: readme`
804+ if expected != actual {
805+ t.Fatalf(fail(expected, actual))
806+ }
807+}
808+
809+// added commit
810+/*
811+ 1: $(test_oid t1) = 1: $(test_oid a1) s/5/A/
812+ 2: $(test_oid t2) = 2: $(test_oid a2) s/4/A/
813+ -: $(test_oid __) > 3: $(test_oid a3) s/6/A/
814+ 3: $(test_oid t3) = 4: $(test_oid a4) s/11/B/
815+ 4: $(test_oid t4) = 5: $(test_oid a5) s/12/B/
816+*/
817+/* func TestRangeDiffAddedCommit(t *testing.T) {
818+ actual := ""
819+ expected := `1: 33c682a = 1: 33c682a chore: add torch and create random tensor
820+2: 22dde12 = 2: 22dde12 docs: readme
821+-: ------- > 3: b248060 chore: make tensor 6x6`
822+ if expected != actual {
823+ t.Fatalf("expected:%s actual:%s", expected, actual)
824+ }
825+} */
826+
827+// changed commit
828+/*
829+ 1: $(test_oid t1) = 1: $(test_oid c1) s/5/A/
830+ 2: $(test_oid t2) = 2: $(test_oid c2) s/4/A/
831+ 3: $(test_oid t3) ! 3: $(test_oid c3) s/11/B/
832+ @@ file: A
833+ 9
834+ 10
835+ -11
836+ -+B
837+ ++BB
838+ 12
839+ 13
840+ 14
841+ 4: $(test_oid t4) ! 4: $(test_oid c4) s/12/B/
842+ @@ file
843+ @@ file: A
844+ 9
845+ 10
846+ - B
847+ + BB
848+ -12
849+ +B
850+ 13
851+*/
852+/* func TestRangeDiffChangedCommit(t *testing.T) {
853+ actual := ""
854+ fp, err := fixtures.Fixtures.ReadFile("extected_commit_changed.txt")
855+ if err != nil {
856+ t.Fatalf("file not found")
857+ }
858+ expected := string(fp)
859+ if expected != actual {
860+ t.Fatalf("expected:%s actual:%s", expected, actual)
861+ }
862+} */
863+
864+// renamed file
865+/*
866+ 1: $(test_oid t1) = 1: $(test_oid n1) s/5/A/
867+ 2: $(test_oid t2) ! 2: $(test_oid n2) s/4/A/
868+ @@ Metadata
869+ ZAuthor: Thomas Rast <trast@inf.ethz.ch>
870+ Z
871+ Z ## Commit message ##
872+ - s/4/A/
873+ + s/4/A/ + rename file
874+ Z
875+ - ## file ##
876+ + ## file => renamed-file ##
877+ Z@@
878+ Z 1
879+ Z 2
880+ 3: $(test_oid t3) ! 3: $(test_oid n3) s/11/B/
881+ @@ Metadata
882+ Z ## Commit message ##
883+ Z s/11/B/
884+ Z
885+ - ## file ##
886+ -@@ file: A
887+ + ## renamed-file ##
888+ +@@ renamed-file: A
889+ Z 8
890+ Z 9
891+ Z 10
892+ 4: $(test_oid t4) ! 4: $(test_oid n4) s/12/B/
893+ @@ Metadata
894+ Z ## Commit message ##
895+ Z s/12/B/
896+ Z
897+ - ## file ##
898+ -@@ file: A
899+ + ## renamed-file ##
900+ +@@ renamed-file: A
901+ Z 9
902+ Z 10
903+ Z B
904+*/
905+// func TestRangeDiffRenamedFile(t *testing.T) {}
906+
907+// file with mode only change
908+/*
909+ 1: $(test_oid t2) ! 1: $(test_oid o1) s/4/A/
910+ @@ Metadata
911+ ZAuthor: Thomas Rast <trast@inf.ethz.ch>
912+ Z
913+ Z ## Commit message ##
914+ - s/4/A/
915+ + s/4/A/ + add other-file
916+ Z
917+ Z ## file ##
918+ Z@@
919+ @@ file
920+ Z A
921+ Z 6
922+ Z 7
923+ +
924+ + ## other-file (new) ##
925+ 2: $(test_oid t3) ! 2: $(test_oid o2) s/11/B/
926+ @@ Metadata
927+ ZAuthor: Thomas Rast <trast@inf.ethz.ch>
928+ Z
929+ Z ## Commit message ##
930+ - s/11/B/
931+ + s/11/B/ + mode change other-file
932+ Z
933+ Z ## file ##
934+ Z@@ file: A
935+ @@ file: A
936+ Z 12
937+ Z 13
938+ Z 14
939+ +
940+ + ## other-file (mode change 100644 => 100755) ##
941+ 3: $(test_oid t4) = 3: $(test_oid o3) s/12/B/
942+*/
943+// func TestRangeDiffFileWithModeOnlyChange(t *testing.T) {}
944+
945+// file added and later removed
946+/*
947+ 1: $(test_oid t1) = 1: $(test_oid s1) s/5/A/
948+ 2: $(test_oid t2) ! 2: $(test_oid s2) s/4/A/
949+ @@ Metadata
950+ ZAuthor: Thomas Rast <trast@inf.ethz.ch>
951+ Z
952+ Z ## Commit message ##
953+ - s/4/A/
954+ + s/4/A/ + new-file
955+ Z
956+ Z ## file ##
957+ Z@@
958+ @@ file
959+ Z A
960+ Z 6
961+ Z 7
962+ +
963+ + ## new-file (new) ##
964+ 3: $(test_oid t3) ! 3: $(test_oid s3) s/11/B/
965+ @@ Metadata
966+ ZAuthor: Thomas Rast <trast@inf.ethz.ch>
967+ Z
968+ Z ## Commit message ##
969+ - s/11/B/
970+ + s/11/B/ + remove file
971+ Z
972+ Z ## file ##
973+ Z@@ file: A
974+ @@ file: A
975+ Z 12
976+ Z 13
977+ Z 14
978+ +
979+ + ## new-file (deleted) ##
980+ 4: $(test_oid t4) = 4: $(test_oid s4) s/12/B/
981+*/
982+// func TestRangeDiffFileAddedThenRemoved(t *testing.T) {}
983+
984+// changed message
985+/*
986+ 1: $(test_oid t1) = 1: $(test_oid m1) s/5/A/
987+ 2: $(test_oid t2) ! 2: $(test_oid m2) s/4/A/
988+ @@ Metadata
989+ Z ## Commit message ##
990+ Z s/4/A/
991+ Z
992+ + Also a silly comment here!
993+ +
994+ Z ## file ##
995+ Z@@
996+ Z 1
997+ 3: $(test_oid t3) = 3: $(test_oid m3) s/11/B/
998+ 4: $(test_oid t4) = 4: $(test_oid m4) s/12/B/
999+*/
1000+// func TestRangeDiffChangedMessage(t *testing.T) {}
1001diff --git a/util.go b/util.go
1002index 061bb6d..da69a60 100644
1003--- a/util.go
1004+++ b/util.go
1005@@ -109,7 +109,7 @@ func patchToDiff(patch io.Reader) (string, error) {
1006 return str[idx:], nil
1007 }
1008
1009-func parsePatchset(patchset io.Reader) ([]*Patch, error) {
1010+func ParsePatchset(patchset io.Reader) ([]*Patch, error) {
1011 patches := []*Patch{}
1012 buf := new(strings.Builder)
1013 _, err := io.Copy(buf, patchset)
1014@@ -172,12 +172,11 @@ func calcContentSha(diffFiles []*gitdiff.File, header *gitdiff.PatchHeader) stri
1015 authorEmail = header.Author.Email
1016 }
1017 content := fmt.Sprintf(
1018- "%s\n%s\n%s\n%s\n%s\n",
1019+ "%s\n%s\n%s\n%s\n",
1020 header.Title,
1021 header.Body,
1022 authorName,
1023 authorEmail,
1024- header.AuthorDate,
1025 )
1026 for _, diff := range diffFiles {
1027 // we need to ignore diffs with base commit because that depends
1028diff --git a/util_test.go b/util_test.go
1029index eaba33d..e579254 100644
1030--- a/util_test.go
1031+++ b/util_test.go
1032@@ -15,7 +15,7 @@ func TestParsePatchsetWithCover(t *testing.T) {
1033 if err != nil {
1034 t.Fatalf(err.Error())
1035 }
1036- actual, err := parsePatchset(file)
1037+ actual, err := ParsePatchset(file)
1038 if err != nil {
1039 t.Fatalf(err.Error())
1040 }
1041--
10422.45.2
1043