pico
cmds
checkout latest patchset:
ssh pr.pico.sh print pr-19 | git am -3checkout any patchset in a patch request:
ssh pr.pico.sh print ps-X | git am -3add changes to patch request:
git format-patch main --stdout | ssh pr.pico.sh pr add 19set PR to open (enables RSS notifications):
ssh pr.pico.sh pr open 19set PR to draft (stops RSS notifications):
ssh pr.pico.sh pr draft 19
Patchset
ps-51
feat: range diff
Eric Bower
2024-07-23T15:59:25ZSemantic diff summary
19 added,
6 modified,
0 signature changed,
1 removed
across 6 analyzed files
(10 files skipped: unsupported file type)
range_diff.go
-
function_declarationoutputadded -
function_declarationoutputPairHeaderadded -
function_declarationfindExactMatchesadded -
function_declarationcreateMatrixadded -
function_declarationdiffsizeadded -
type_declarationPatchRangeadded -
function_declarationRangeDiffadded -
function_declarationgetCorrespondencesadded -
function_declarationcomputeAssignmentadded -
function_declarationNewPatchRangeadded
range_diff_test.go
-
function_declarationbailadded -
function_declarationcmpadded -
function_declarationfailadded -
function_declarationTestRangeDiffUnmodifiedadded -
function_declarationTestRangeDiffTrivialReorderingadded -
function_declarationTestRangeDiffRemovedCommitadded -
function_declarationTestRangeDiffAddedCommitadded -
function_declarationTestRangeDiffChangedCommitadded
+6
-0
db.go
#
@@ -6,6 +6,7 @@ import (
"log/slog"
"time"
+ "github.com/bluekeyes/go-gitdiff/gitdiff"
"github.com/jmoiron/sqlx"
_ "modernc.org/sqlite"
)
@@ -67,6 +68,11 @@ type Patch struct {
BaseCommitSha sql.NullString `db:"base_commit_sha"`
RawText string `db:"raw_text"`
CreatedAt time.Time `db:"created_at"`
+ Files []*gitdiff.File
+}
+
+func (p *Patch) CalcDiff() string {
+ return p.RawText
}
// EventLog is a event log for RSS or other notification systems.
+31
-0
fixtures/a_b.patch
#
@@ -0,0 +1,31 @@
+From 33c682ac27479f501924cf159d0a75ad91deb589 Mon Sep 17 00:00:00 2001
+From: Eric Bower <me@erock.io>
+Date: Tue, 23 Jul 2024 10:07:57 -0400
+Subject: [PATCH] chore: add torch and create random tensor
+
+---
+ requirements.txt | 1 +
+ train.py | 3 +++
+ 2 files changed, 4 insertions(+)
+ create mode 100644 requirements.txt
+
+diff --git a/requirements.txt b/requirements.txt
+new file mode 100644
+index 0000000..4968a39
+--- /dev/null
++++ b/requirements.txt
+@@ -0,0 +1 @@
++torch==2.3.1
+diff --git a/train.py b/train.py
+index 5c027f4..d21dac3 100644
+--- a/train.py
++++ b/train.py
+@@ -1,2 +1,5 @@
++import torch
++
+ if __name__ == "__main__":
+ print("train!")
++ torch.rand(3,6)
+--
+2.45.2
+
+55
-0
fixtures/a_b_reorder.patch
#
@@ -0,0 +1,55 @@
+From 33c682ac27479f501924cf159d0a75ad91deb589 Mon Sep 17 00:00:00 2001
+From: Eric Bower <me@erock.io>
+Date: Tue, 23 Jul 2024 10:07:57 -0400
+Subject: [PATCH 1/2] chore: add torch and create random tensor
+
+---
+ requirements.txt | 1 +
+ train.py | 3 +++
+ 2 files changed, 4 insertions(+)
+ create mode 100644 requirements.txt
+
+diff --git a/requirements.txt b/requirements.txt
+new file mode 100644
+index 0000000..4968a39
+--- /dev/null
++++ b/requirements.txt
+@@ -0,0 +1 @@
++torch==2.3.1
+diff --git a/train.py b/train.py
+index 5c027f4..d21dac3 100644
+--- a/train.py
++++ b/train.py
+@@ -1,2 +1,5 @@
++import torch
++
+ if __name__ == "__main__":
+ print("train!")
++ torch.rand(3,6)
+--
+2.45.2
+
+
+From 22dde1259c34a166d5a9335ebe5236e79541cc63 Mon Sep 17 00:00:00 2001
+From: Eric Bower <me@erock.io>
+Date: Tue, 23 Jul 2024 10:14:37 -0400
+Subject: [PATCH 2/2] docs: readme
+
+---
+ README.md | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+diff --git a/README.md b/README.md
+index 8f3a780..3043953 100644
+--- a/README.md
++++ b/README.md
+@@ -1,3 +1,5 @@
+ # Let's build an RNN
+
+-This repo demonstrates building an RNN using `pytorch`
++This repo demonstrates building an RNN using `pytorch`.
++
++Here is some more readme information.
+--
+2.45.2
+
+33
-0
fixtures/a_c.patch
#
@@ -0,0 +1,33 @@
+From 166848469e0b954c2e14233233f3824a46dcddb8 Mon Sep 17 00:00:00 2001
+From: Eric Bower <me@erock.io>
+Date: Tue, 23 Jul 2024 10:06:00 -0400
+Subject: [PATCH] chore: add torch and create random tensor
+
+---
+ requirements.txt | 1 +
+ train.py | 3 +++
+ 2 files changed, 4 insertions(+)
+ create mode 100644 requirements.txt
+
+diff --git a/requirements.txt b/requirements.txt
+new file mode 100644
+index 0000000..4968a39
+--- /dev/null
++++ b/requirements.txt
+@@ -0,0 +1 @@
++torch==2.3.1
+diff --git a/train.py b/train.py
+index 5c027f4..d21dac3 100644
+--- a/train.py
++++ b/train.py
+@@ -1,2 +1,5 @@
++import torch
++
+ if __name__ == "__main__":
+ print("train!")
++ torch.rand(3,6)
+
+base-commit: 59456574a0bfee9f71c91c13046173c820152346
+--
+2.45.2
+
+80
-0
fixtures/a_c_added_commit.patch
#
@@ -0,0 +1,80 @@
+From 33c682ac27479f501924cf159d0a75ad91deb589 Mon Sep 17 00:00:00 2001
+From: Eric Bower <me@erock.io>
+Date: Tue, 23 Jul 2024 10:07:57 -0400
+Subject: [PATCH 1/3] chore: add torch and create random tensor
+
+---
+ requirements.txt | 1 +
+ train.py | 3 +++
+ 2 files changed, 4 insertions(+)
+ create mode 100644 requirements.txt
+
+diff --git a/requirements.txt b/requirements.txt
+new file mode 100644
+index 0000000..4968a39
+--- /dev/null
++++ b/requirements.txt
+@@ -0,0 +1 @@
++torch==2.3.1
+diff --git a/train.py b/train.py
+index 5c027f4..d21dac3 100644
+--- a/train.py
++++ b/train.py
+@@ -1,2 +1,5 @@
++import torch
++
+ if __name__ == "__main__":
+ print("train!")
++ torch.rand(3,6)
+--
+2.45.2
+
+
+From 22dde1259c34a166d5a9335ebe5236e79541cc63 Mon Sep 17 00:00:00 2001
+From: Eric Bower <me@erock.io>
+Date: Tue, 23 Jul 2024 10:14:37 -0400
+Subject: [PATCH 2/3] docs: readme
+
+---
+ README.md | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+diff --git a/README.md b/README.md
+index 8f3a780..3043953 100644
+--- a/README.md
++++ b/README.md
+@@ -1,3 +1,5 @@
+ # Let's build an RNN
+
+-This repo demonstrates building an RNN using `pytorch`
++This repo demonstrates building an RNN using `pytorch`.
++
++Here is some more readme information.
+--
+2.45.2
+
+
+From b248060488df529b850060b3c86417bb87d490cc Mon Sep 17 00:00:00 2001
+From: Eric Bower <me@erock.io>
+Date: Tue, 23 Jul 2024 10:20:44 -0400
+Subject: [PATCH 3/3] chore: make tensor 6x6
+
+---
+ train.py | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+diff --git a/train.py b/train.py
+index d21dac3..8cd47e0 100644
+--- a/train.py
++++ b/train.py
+@@ -2,4 +2,6 @@ import torch
+
+ if __name__ == "__main__":
+ print("train!")
+- torch.rand(3,6)
++ # let's create a 6x6 tensor!
++ tensor = torch.rand(6,6)
++ print(tensor)
+--
+2.45.2
+
+60
-0
fixtures/a_c_changed_commit.patch
#
@@ -0,0 +1,60 @@
+From 33c682ac27479f501924cf159d0a75ad91deb589 Mon Sep 17 00:00:00 2001
+From: Eric Bower <me@erock.io>
+Date: Tue, 23 Jul 2024 10:07:57 -0400
+Subject: [PATCH 1/2] chore: add torch and create random tensor
+
+---
+ requirements.txt | 1 +
+ train.py | 3 +++
+ 2 files changed, 4 insertions(+)
+ create mode 100644 requirements.txt
+
+diff --git a/requirements.txt b/requirements.txt
+new file mode 100644
+index 0000000..4968a39
+--- /dev/null
++++ b/requirements.txt
+@@ -0,0 +1 @@
++torch==2.3.1
+diff --git a/train.py b/train.py
+index 5c027f4..d21dac3 100644
+--- a/train.py
++++ b/train.py
+@@ -1,2 +1,5 @@
++import torch
++
+ if __name__ == "__main__":
+ print("train!")
++ torch.rand(3,6)
+--
+2.45.2
+
+
+From dce20e70280d92aeb88c3d603ad67043ead772fb Mon Sep 17 00:00:00 2001
+From: Eric Bower <me@erock.io>
+Date: Tue, 23 Jul 2024 10:14:37 -0400
+Subject: [PATCH 2/2] docs: readme
+
+---
+ README.md | 9 ++++++++-
+ 1 file changed, 8 insertions(+), 1 deletion(-)
+
+diff --git a/README.md b/README.md
+index 8f3a780..ba0293b 100644
+--- a/README.md
++++ b/README.md
+@@ -1,3 +1,10 @@
+ # Let's build an RNN
+
+-This repo demonstrates building an RNN using `pytorch`
++This repo demonstrates building an RNN using `pytorch`.
++
++Here is some more readme information.
++
++Here is how to run this project locally:
++
++- install python and pip
++- `pip install -r requirements.txt`
+--
+2.45.2
+
+55
-0
fixtures/a_c_reorder.patch
#
@@ -0,0 +1,55 @@
+From 7dbb94ca1bc8cadf1ce17dacb89172217d88de07 Mon Sep 17 00:00:00 2001
+From: Eric Bower <me@erock.io>
+Date: Tue, 23 Jul 2024 10:15:23 -0400
+Subject: [PATCH 1/2] docs: readme
+
+---
+ README.md | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+diff --git a/README.md b/README.md
+index 8f3a780..3043953 100644
+--- a/README.md
++++ b/README.md
+@@ -1,3 +1,5 @@
+ # Let's build an RNN
+
+-This repo demonstrates building an RNN using `pytorch`
++This repo demonstrates building an RNN using `pytorch`.
++
++Here is some more readme information.
+--
+2.45.2
+
+
+From ad175875e2bf320859554bae73743675cc5ce444 Mon Sep 17 00:00:00 2001
+From: Eric Bower <me@erock.io>
+Date: Tue, 23 Jul 2024 10:06:00 -0400
+Subject: [PATCH 2/2] chore: add torch and create random tensor
+
+---
+ requirements.txt | 1 +
+ train.py | 3 +++
+ 2 files changed, 4 insertions(+)
+ create mode 100644 requirements.txt
+
+diff --git a/requirements.txt b/requirements.txt
+new file mode 100644
+index 0000000..4968a39
+--- /dev/null
++++ b/requirements.txt
+@@ -0,0 +1 @@
++torch==2.3.1
+diff --git a/train.py b/train.py
+index 5c027f4..d21dac3 100644
+--- a/train.py
++++ b/train.py
+@@ -1,2 +1,5 @@
++import torch
++
+ if __name__ == "__main__":
+ print("train!")
++ torch.rand(3,6)
+--
+2.45.2
+
+23
-0
fixtures/a_c_rm_commit.patch
#
@@ -0,0 +1,23 @@
+From 7dbb94ca1bc8cadf1ce17dacb89172217d88de07 Mon Sep 17 00:00:00 2001
+From: Eric Bower <me@erock.io>
+Date: Tue, 23 Jul 2024 10:15:23 -0400
+Subject: [PATCH] docs: readme
+
+---
+ README.md | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+diff --git a/README.md b/README.md
+index 8f3a780..3043953 100644
+--- a/README.md
++++ b/README.md
+@@ -1,3 +1,5 @@
+ # Let's build an RNN
+
+-This repo demonstrates building an RNN using `pytorch`
++This repo demonstrates building an RNN using `pytorch`.
++
++Here is some more readme information.
+--
+2.45.2
+
+11
-0
fixtures/expected_commit_changed.txt
#
@@ -0,0 +1,11 @@
+1: 33c682a = 1: 33c682a chore: add torch and create random tensor
+2: 22dde12 ! 2: 0185f34 docs: readme
+ @@ README.md
+ +This repo demonstrates building an RNN using `pytorch`.
+ +
+ +Here is some more readme information.
+ ++
+ ++Here is how to run this project locally:
+ ++
+ ++- install python and pip
+ ++- `pip install -r requirements.txt`
+1
-0
go.mod
#
@@ -14,6 +14,7 @@ require (
github.com/knadh/koanf/providers/env v0.1.0
github.com/knadh/koanf/providers/file v1.0.0
github.com/knadh/koanf/v2 v2.1.1
+ github.com/sergi/go-diff v1.1.0
github.com/urfave/cli/v2 v2.27.2
golang.org/x/crypto v0.21.0
modernc.org/sqlite v1.27.0
+13
-2
go.sum
#
@@ -8,8 +8,6 @@ github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFI
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
-github.com/bluekeyes/go-gitdiff v0.7.2 h1:42jrcVZdjjxXtVsFNYTo/I6T1ZvIiQL+iDDLiH904hw=
-github.com/bluekeyes/go-gitdiff v0.7.2/go.mod h1:QpfYYO1E0fTVHVZAZKiRjtSGY9823iCdvGXBcEzHGbM=
github.com/bluekeyes/go-gitdiff v0.7.4-0.20240715034416-0a4e55f9a190 h1:k6Ep4yQtmsoP/St4bf7ofXyWc6ITB/FyGy9ewaAn5os=
github.com/bluekeyes/go-gitdiff v0.7.4-0.20240715034416-0a4e55f9a190/go.mod h1:QpfYYO1E0fTVHVZAZKiRjtSGY9823iCdvGXBcEzHGbM=
github.com/charmbracelet/bubbletea v0.25.0 h1:bAfwk7jRz7FKFl9RzlIULPkStffg5k6pNt5dywy4TcM=
@@ -36,6 +34,7 @@ github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lV
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.21 h1:1/QdRyBaHHJP61QkWMXlOIBfsgdDeeKfK8SYVUWJKf0=
github.com/creack/pty v1.1.21/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI=
@@ -74,8 +73,11 @@ github.com/knadh/koanf/providers/file v1.0.0 h1:DtPvSQBeF+N0QLPMz0yf2bx0nFSxUcnc
github.com/knadh/koanf/providers/file v1.0.0/go.mod h1:/faSBcv2mxPVjFrXck95qeoyoZ5myJ6uxN8OOVNJJCI=
github.com/knadh/koanf/v2 v2.1.1 h1:/R8eXqasSTsmDCsAyYj+81Wteg8AqrV9CP6gvsTsOmM=
github.com/knadh/koanf/v2 v2.1.1/go.mod h1:4mnTRbZCK+ALuBXHZMjDfG9y714L7TykVnZkXbMU3Es=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
@@ -107,6 +109,7 @@ github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo
github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8=
github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
@@ -119,6 +122,10 @@ github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDN
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
+github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
+github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/urfave/cli/v2 v2.27.2 h1:6e0H+AkS+zDckwPCUrZkKX38mRaau4nL2uipkJpbkcI=
@@ -143,6 +150,10 @@ golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/tools v0.15.0 h1:zdAyfUGbYmuVokhzVmghFl2ZJh5QhcfebBgmVPFYA+8=
golang.org/x/tools v0.15.0/go.mod h1:hpksKq4dtpQWS1uQ61JkdqWM3LscIS6Slf+VVkm+wQk=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
lukechampine.com/uint128 v1.2.0 h1:mBi/5l91vocEN8otkC5bDLhi2KdCticRiwbdB0O+rjI=
+2
-2
pr.go
#
@@ -447,7 +447,7 @@ func (cmd PrCmd) SubmitPatchRequest(repoID string, userID int64, patchset io.Rea
_ = tx.Rollback()
}()
- patches, err := parsePatchset(patchset)
+ patches, err := ParsePatchset(patchset)
if err != nil {
return nil, err
}
@@ -541,7 +541,7 @@ func (cmd PrCmd) SubmitPatchset(prID int64, userID int64, op PatchsetOp, patchse
_ = tx.Rollback()
}()
- patches, err := parsePatchset(patchset)
+ patches, err := ParsePatchset(patchset)
if err != nil {
return fin, err
}
+219
-0
range_diff.go
#
@@ -0,0 +1,219 @@
+package git
+
+import (
+ "fmt"
+ "math"
+
+ "github.com/sergi/go-diff/diffmatchpatch"
+)
+
+var COST_MAX = 65536
+var RANGE_DIFF_CREATION_FACTOR_DEFAULT = 60
+
+type PatchRange struct {
+ *Patch
+ Matching int
+ Diff string
+ DiffSize int
+ Shown bool
+}
+
+func NewPatchRange(patch *Patch) *PatchRange {
+ diff := patch.CalcDiff()
+ return &PatchRange{
+ Patch: patch,
+ Matching: -1,
+ Diff: diff,
+ DiffSize: len(diff),
+ Shown: false,
+ }
+}
+
+func output(a []*PatchRange, b []*PatchRange) string {
+ out := ""
+ for i, patchA := range a {
+ if patchA.Matching == -1 {
+ out += outputPairHeader(patchA, nil, i+1, -1)
+ }
+ }
+
+ for j, patchB := range b {
+ if patchB.Matching == -1 {
+ out += outputPairHeader(nil, patchB, -1, j+1)
+ continue
+ }
+ patchA := a[patchB.Matching]
+ if patchB.ContentSha == patchA.ContentSha {
+ out += outputPairHeader(patchA, patchB, patchB.Matching+1, patchA.Matching+1)
+ }
+ }
+ return out
+}
+
+func outputPairHeader(a *PatchRange, b *PatchRange, aIndex, bIndex int) string {
+ if a == nil {
+ return fmt.Sprintf("-: ------- > %d: %s %s\n", bIndex, truncateSha(b.CommitSha), b.Title)
+ }
+ if b == nil {
+ return fmt.Sprintf("%d: %s < -: ------- %s\n", aIndex, truncateSha(a.CommitSha), a.Title)
+ }
+ return fmt.Sprintf("%d: %s = %d: %s %s\n", aIndex, truncateSha(a.CommitSha), bIndex, truncateSha(b.CommitSha), a.Title)
+}
+
+func RangeDiff(a []*Patch, b []*Patch) string {
+ aPatches := []*PatchRange{}
+ for _, patch := range a {
+ aPatches = append(aPatches, NewPatchRange(patch))
+ }
+ bPatches := []*PatchRange{}
+ for _, patch := range b {
+ bPatches = append(bPatches, NewPatchRange(patch))
+ }
+ findExactMatches(aPatches, bPatches)
+ getCorrespondences(aPatches, bPatches, RANGE_DIFF_CREATION_FACTOR_DEFAULT)
+ return output(aPatches, bPatches)
+}
+
+func findExactMatches(a []*PatchRange, b []*PatchRange) {
+ for i, patchA := range a {
+ for j, patchB := range b {
+ if patchA.ContentSha == patchB.ContentSha {
+ patchA.Matching = j
+ patchB.Matching = i
+ }
+ }
+ }
+}
+
+func createMatrix(rows, cols int) [][]int {
+ mat := make([][]int, rows)
+ for i := range mat {
+ mat[i] = make([]int, cols)
+ }
+ return mat
+}
+
+func diffsize(a *PatchRange, b *PatchRange) int {
+ dmp := diffmatchpatch.New()
+ diffs := dmp.DiffMain(a.Diff, b.Diff, false)
+ return len(dmp.DiffPrettyText(diffs))
+}
+
+func getCorrespondences(a []*PatchRange, b []*PatchRange, creationFactor int) {
+ n := len(a) + len(b)
+ fmt.Println("rows", len(a), "cols", len(b))
+ cost := createMatrix(n, n)
+
+ for i, patchA := range a {
+ var c int
+ for j, patchB := range b {
+ if patchA.Matching == j {
+ c = 0
+ } else if patchA.Matching == -1 && patchB.Matching == -1 {
+ c = diffsize(patchA, patchB)
+ } else {
+ c = COST_MAX
+ }
+ cost[i][j] = c
+ }
+ }
+
+ for j, patchB := range b {
+ creationCost := (patchB.DiffSize * creationFactor) / 100
+ if patchB.Matching >= 0 {
+ creationCost = math.MaxInt32
+ }
+ for i := len(a); i < n; i++ {
+ cost[i][j] = creationCost
+ }
+ }
+
+ for i := len(a); i < n; i++ {
+ for j := len(b); j < n; j++ {
+ cost[i][j] = 0
+ }
+ }
+
+ assignment := computeAssignment(cost, n, n)
+ for i, j := range assignment {
+ if i < len(a) && j < len(b) {
+ a[i].Matching = j
+ b[j].Matching = i
+ }
+ }
+
+ fmt.Println("cost", cost, "assignment", assignment)
+}
+
+// computeAssignment assigns patches using the Hungarian algorithm.
+func computeAssignment(costMatrix [][]int, m, n int) []int {
+ u := make([]int, m+1) // potential for workers
+ v := make([]int, n+1) // potential for jobs
+ p := make([]int, n+1) // job assignment
+ way := make([]int, n+1)
+
+ for i := 1; i <= m; i++ {
+ links := make([]int, n+1)
+ minV := make([]int, n+1)
+ used := make([]bool, n+1)
+ for j := 0; j <= n; j++ {
+ minV[j] = math.MaxInt32
+ used[j] = false
+ }
+
+ j0 := 0
+ p[0] = i
+
+ for {
+ used[j0] = true
+ i0 := p[j0]
+ delta := math.MaxInt32
+ j1 := 0
+
+ for j := 1; j <= n; j++ {
+ if !used[j] {
+ cur := costMatrix[i0-1][j-1] - u[i0] - v[j]
+ if cur < minV[j] {
+ minV[j] = cur
+ links[j] = j0
+ }
+ if minV[j] < delta {
+ delta = minV[j]
+ j1 = j
+ }
+ }
+ }
+
+ for j := 0; j <= n; j++ {
+ if used[j] {
+ u[p[j]] += delta
+ v[j] -= delta
+ } else {
+ minV[j] -= delta
+ }
+ }
+
+ j0 = j1
+ if p[j0] == 0 {
+ break
+ }
+ }
+
+ for {
+ j1 := way[j0]
+ p[j0] = p[j1]
+ j0 = j1
+ if j0 == 0 {
+ break
+ }
+ }
+ }
+
+ assignment := make([]int, m)
+ for j := 1; j <= n; j++ {
+ if p[j] > 0 {
+ assignment[p[j]-1] = j - 1
+ }
+ }
+ return assignment
+}
+276
-0
range_diff_test.go
#
@@ -0,0 +1,276 @@
+package git
+
+import (
+ "fmt"
+ "testing"
+
+ "github.com/picosh/git-pr/fixtures"
+)
+
+func bail(err error) {
+ if err != nil {
+ panic(bail)
+ }
+}
+
+func cmp(afile, bfile string) string {
+ a, err := fixtures.Fixtures.Open(afile)
+ bail(err)
+ b, err := fixtures.Fixtures.Open(bfile)
+ bail(err)
+ aPatches, err := ParsePatchset(a)
+ bail(err)
+ bPatches, err := ParsePatchset(b)
+ bail(err)
+ actual := RangeDiff(aPatches, bPatches)
+ return actual
+}
+
+func fail(expected, actual string) string {
+ return fmt.Sprintf("expected:[%s] actual:[%s]", expected, actual)
+}
+
+// https://git.kernel.org/tree/t/t3206-range-diff.sh?id=d19b6cd2dd72dc811f19df4b32c7ed223256c3ee
+
+// simple A..B A..C (unmodified)
+/*
+ 1: $(test_oid t1) = 1: $(test_oid u1) s/5/A/
+ 2: $(test_oid t2) = 2: $(test_oid u2) s/4/A/
+ 3: $(test_oid t3) = 3: $(test_oid u3) s/11/B/
+ 4: $(test_oid t4) = 4: $(test_oid u4) s/12/B/
+*/
+func TestRangeDiffUnmodified(t *testing.T) {
+ actual := cmp("a_b.patch", "a_c.patch")
+ expected := "1: 33c682a = 1: 1668484 chore: add torch and create random tensor\n"
+ if expected != actual {
+ t.Fatalf(fail(expected, actual))
+ }
+}
+
+// trivial reordering
+/*
+ 1: $(test_oid t1) = 1: $(test_oid r1) s/5/A/
+ 3: $(test_oid t3) = 2: $(test_oid r2) s/11/B/
+ 4: $(test_oid t4) = 3: $(test_oid r3) s/12/B/
+ 2: $(test_oid t2) = 4: $(test_oid r4) s/4/A/
+*/
+func TestRangeDiffTrivialReordering(t *testing.T) {
+ actual := cmp("a_b_reorder.patch", "a_c_reorder.patch")
+ expected := `2: 22dde12 = 1: 7dbb94c docs: readme
+1: 33c682a = 2: ad17587 chore: add torch and create random tensor
+`
+ if expected != actual {
+ t.Fatalf(fail(expected, actual))
+ }
+}
+
+// removed commit
+/*
+ 1: $(test_oid t1) = 1: $(test_oid d1) s/5/A/
+ 2: $(test_oid t2) < -: $(test_oid __) s/4/A/
+ 3: $(test_oid t3) = 2: $(test_oid d2) s/11/B/
+ 4: $(test_oid t4) = 3: $(test_oid d3) s/12/B/
+*/
+func TestRangeDiffRemovedCommit(t *testing.T) {
+ actual := cmp("a_b_reorder.patch", "a_c_rm_commit.patch")
+ expected := `1: 33c682a < -: ------- chore: add torch and create random tensor
+2: 22dde12 = 1: 7dbb94c docs: readme
+`
+ if expected != actual {
+ t.Fatalf(fail(expected, actual))
+ }
+}
+
+// added commit
+/*
+ 1: $(test_oid t1) = 1: $(test_oid a1) s/5/A/
+ 2: $(test_oid t2) = 2: $(test_oid a2) s/4/A/
+ -: $(test_oid __) > 3: $(test_oid a3) s/6/A/
+ 3: $(test_oid t3) = 4: $(test_oid a4) s/11/B/
+ 4: $(test_oid t4) = 5: $(test_oid a5) s/12/B/
+*/
+func TestRangeDiffAddedCommit(t *testing.T) {
+ actual := cmp("a_b_reorder.patch", "a_c_added_commit.patch")
+ expected := `1: 33c682a = 1: 33c682a chore: add torch and create random tensor
+2: 22dde12 = 2: 22dde12 docs: readme
+-: ------- > 3: b248060 chore: make tensor 6x6
+`
+ if expected != actual {
+ t.Fatalf(fail(expected, actual))
+ }
+}
+
+// changed commit
+/*
+ 1: $(test_oid t1) = 1: $(test_oid c1) s/5/A/
+ 2: $(test_oid t2) = 2: $(test_oid c2) s/4/A/
+ 3: $(test_oid t3) ! 3: $(test_oid c3) s/11/B/
+ @@ file: A
+ 9
+ 10
+ -11
+ -+B
+ ++BB
+ 12
+ 13
+ 14
+ 4: $(test_oid t4) ! 4: $(test_oid c4) s/12/B/
+ @@ file
+ @@ file: A
+ 9
+ 10
+ - B
+ + BB
+ -12
+ +B
+ 13
+*/
+func TestRangeDiffChangedCommit(t *testing.T) {
+ actual := cmp("a_b_reorder.patch", "a_c_changed_commit.patch")
+ fp, err := fixtures.Fixtures.ReadFile("expected_commit_changed.txt")
+ if err != nil {
+ t.Fatalf("file not found")
+ }
+ expected := string(fp)
+ if expected != actual {
+ t.Fatalf(fail(expected, actual))
+ }
+}
+
+// renamed file
+/*
+ 1: $(test_oid t1) = 1: $(test_oid n1) s/5/A/
+ 2: $(test_oid t2) ! 2: $(test_oid n2) s/4/A/
+ @@ Metadata
+ ZAuthor: Thomas Rast <trast@inf.ethz.ch>
+ Z
+ Z ## Commit message ##
+ - s/4/A/
+ + s/4/A/ + rename file
+ Z
+ - ## file ##
+ + ## file => renamed-file ##
+ Z@@
+ Z 1
+ Z 2
+ 3: $(test_oid t3) ! 3: $(test_oid n3) s/11/B/
+ @@ Metadata
+ Z ## Commit message ##
+ Z s/11/B/
+ Z
+ - ## file ##
+ -@@ file: A
+ + ## renamed-file ##
+ +@@ renamed-file: A
+ Z 8
+ Z 9
+ Z 10
+ 4: $(test_oid t4) ! 4: $(test_oid n4) s/12/B/
+ @@ Metadata
+ Z ## Commit message ##
+ Z s/12/B/
+ Z
+ - ## file ##
+ -@@ file: A
+ + ## renamed-file ##
+ +@@ renamed-file: A
+ Z 9
+ Z 10
+ Z B
+*/
+// func TestRangeDiffRenamedFile(t *testing.T) {}
+
+// file with mode only change
+/*
+ 1: $(test_oid t2) ! 1: $(test_oid o1) s/4/A/
+ @@ Metadata
+ ZAuthor: Thomas Rast <trast@inf.ethz.ch>
+ Z
+ Z ## Commit message ##
+ - s/4/A/
+ + s/4/A/ + add other-file
+ Z
+ Z ## file ##
+ Z@@
+ @@ file
+ Z A
+ Z 6
+ Z 7
+ +
+ + ## other-file (new) ##
+ 2: $(test_oid t3) ! 2: $(test_oid o2) s/11/B/
+ @@ Metadata
+ ZAuthor: Thomas Rast <trast@inf.ethz.ch>
+ Z
+ Z ## Commit message ##
+ - s/11/B/
+ + s/11/B/ + mode change other-file
+ Z
+ Z ## file ##
+ Z@@ file: A
+ @@ file: A
+ Z 12
+ Z 13
+ Z 14
+ +
+ + ## other-file (mode change 100644 => 100755) ##
+ 3: $(test_oid t4) = 3: $(test_oid o3) s/12/B/
+*/
+// func TestRangeDiffFileWithModeOnlyChange(t *testing.T) {}
+
+// file added and later removed
+/*
+ 1: $(test_oid t1) = 1: $(test_oid s1) s/5/A/
+ 2: $(test_oid t2) ! 2: $(test_oid s2) s/4/A/
+ @@ Metadata
+ ZAuthor: Thomas Rast <trast@inf.ethz.ch>
+ Z
+ Z ## Commit message ##
+ - s/4/A/
+ + s/4/A/ + new-file
+ Z
+ Z ## file ##
+ Z@@
+ @@ file
+ Z A
+ Z 6
+ Z 7
+ +
+ + ## new-file (new) ##
+ 3: $(test_oid t3) ! 3: $(test_oid s3) s/11/B/
+ @@ Metadata
+ ZAuthor: Thomas Rast <trast@inf.ethz.ch>
+ Z
+ Z ## Commit message ##
+ - s/11/B/
+ + s/11/B/ + remove file
+ Z
+ Z ## file ##
+ Z@@ file: A
+ @@ file: A
+ Z 12
+ Z 13
+ Z 14
+ +
+ + ## new-file (deleted) ##
+ 4: $(test_oid t4) = 4: $(test_oid s4) s/12/B/
+*/
+// func TestRangeDiffFileAddedThenRemoved(t *testing.T) {}
+
+// changed message
+/*
+ 1: $(test_oid t1) = 1: $(test_oid m1) s/5/A/
+ 2: $(test_oid t2) ! 2: $(test_oid m2) s/4/A/
+ @@ Metadata
+ Z ## Commit message ##
+ Z s/4/A/
+ Z
+ + Also a silly comment here!
+ +
+ Z ## file ##
+ Z@@
+ Z 1
+ 3: $(test_oid t3) = 3: $(test_oid m3) s/11/B/
+ 4: $(test_oid t4) = 4: $(test_oid m4) s/12/B/
+*/
+// func TestRangeDiffChangedMessage(t *testing.T) {}
+3
-3
util.go
#
@@ -109,7 +109,7 @@ func patchToDiff(patch io.Reader) (string, error) {
return str[idx:], nil
}
-func parsePatchset(patchset io.Reader) ([]*Patch, error) {
+func ParsePatchset(patchset io.Reader) ([]*Patch, error) {
patches := []*Patch{}
buf := new(strings.Builder)
_, err := io.Copy(buf, patchset)
@@ -154,6 +154,7 @@ func parsePatchset(patchset io.Reader) ([]*Patch, error) {
ContentSha: contentSha,
RawText: patchStr,
BaseCommitSha: sql.NullString{String: baseCommit},
+ Files: diffFiles,
})
}
@@ -172,12 +173,11 @@ func calcContentSha(diffFiles []*gitdiff.File, header *gitdiff.PatchHeader) stri
authorEmail = header.Author.Email
}
content := fmt.Sprintf(
- "%s\n%s\n%s\n%s\n%s\n",
+ "%s\n%s\n%s\n%s\n",
header.Title,
header.Body,
authorName,
authorEmail,
- header.AuthorDate,
)
for _, diff := range diffFiles {
// we need to ignore diffs with base commit because that depends
+1
-1
util_test.go
#
@@ -15,7 +15,7 @@ func TestParsePatchsetWithCover(t *testing.T) {
if err != nil {
t.Fatalf(err.Error())
}
- actual, err := parsePatchset(file)
+ actual, err := ParsePatchset(file)
if err != nil {
t.Fatalf(err.Error())
}