+1
-1
flake.nix
+1
-1
flake.nix
···
49
49
inherit (gitignore.lib) gitignoreSource;
50
50
in {
51
51
overlays.default = final: prev: let
52
-
goModHash = "sha256-zcfTNo7QsiihzLa4qHEX8uGGtbcmBn8TlSm0YHBRNw8=";
52
+
goModHash = "sha256-TwlPge7vhVGmtNvYkHFFnZjJs2DWPUwPhCSBTCUYCtc=";
53
53
buildCmdPackage = name:
54
54
final.buildGoModule {
55
55
pname = name;
+3
-1
go.mod
+3
-1
go.mod
···
16
16
github.com/dustin/go-humanize v1.0.1
17
17
github.com/gliderlabs/ssh v0.3.8
18
18
github.com/go-chi/chi/v5 v5.2.0
19
-
github.com/go-git/go-git/v5 v5.14.0
19
+
github.com/go-git/go-git/v5 v5.0.0-00010101000000-000000000000
20
20
github.com/google/uuid v1.6.0
21
21
github.com/gorilla/sessions v1.4.0
22
22
github.com/haileyok/atproto-oauth-golang v0.0.2
···
123
123
replace github.com/sergi/go-diff => github.com/sergi/go-diff v1.1.0
124
124
125
125
replace github.com/go-git/go-git/v5 => /home/op/code/go-git
126
+
127
+
replace github.com/bluekeyes/go-gitdiff => /home/op/code/go-gitdiff
126
128
127
129
// from bluesky-social/indigo
128
130
replace github.com/gocql/gocql => github.com/scylladb/gocql v1.14.4
+85
-6
knotserver/git/diff.go
+85
-6
knotserver/git/diff.go
···
6
6
"log"
7
7
"os"
8
8
"os/exec"
9
+
"slices"
9
10
"strings"
10
11
11
12
"github.com/bluekeyes/go-gitdiff/gitdiff"
···
126
127
127
128
// FormatPatch generates a git-format-patch output between two commits,
128
129
// and returns the raw format-patch series, a parsed FormatPatch and an error.
129
-
func (g *GitRepo) FormatPatch(base, commit2 *object.Commit) (string, []patchutil.FormatPatch, error) {
130
+
func (g *GitRepo) formatSinglePatch(base, commit2 plumbing.Hash, extraArgs ...string) (string, *patchutil.FormatPatch, error) {
130
131
var stdout bytes.Buffer
131
-
cmd := exec.Command(
132
-
"git",
132
+
133
+
args := []string{
133
134
"-C",
134
135
g.path,
135
136
"format-patch",
136
-
fmt.Sprintf("%s..%s", base.Hash.String(), commit2.Hash.String()),
137
+
fmt.Sprintf("%s..%s", base.String(), commit2.String()),
137
138
"--stdout",
138
-
)
139
+
}
140
+
args = append(args, extraArgs...)
141
+
142
+
cmd := exec.Command("git", args...)
139
143
cmd.Stdout = &stdout
140
144
cmd.Stderr = os.Stderr
141
145
err := cmd.Run()
···
148
152
return "", nil, err
149
153
}
150
154
151
-
return stdout.String(), formatPatch, nil
155
+
if len(formatPatch) > 1 {
156
+
return "", nil, fmt.Errorf("running format-patch on single commit produced more than on patch")
157
+
}
158
+
159
+
return stdout.String(), &formatPatch[0], nil
152
160
}
153
161
154
162
func (g *GitRepo) MergeBase(commit1, commit2 *object.Commit) (*object.Commit, error) {
···
187
195
188
196
return commit, nil
189
197
}
198
+
199
+
func (g *GitRepo) commitsBetween(newCommit, oldCommit *object.Commit) ([]*object.Commit, error) {
200
+
var commits []*object.Commit
201
+
current := newCommit
202
+
203
+
for {
204
+
if current.Hash == oldCommit.Hash {
205
+
break
206
+
}
207
+
208
+
commits = append(commits, current)
209
+
210
+
if len(current.ParentHashes) == 0 {
211
+
return nil, fmt.Errorf("old commit %s not found in history of new commit %s", oldCommit.Hash, newCommit.Hash)
212
+
}
213
+
214
+
parent, err := current.Parents().Next()
215
+
if err != nil {
216
+
return nil, fmt.Errorf("error getting parent: %w", err)
217
+
}
218
+
219
+
current = parent
220
+
}
221
+
222
+
return commits, nil
223
+
}
224
+
225
+
func (g *GitRepo) FormatPatch(base, commit2 *object.Commit) (string, []patchutil.FormatPatch, error) {
226
+
// get list of commits between commir2 and base
227
+
commits, err := g.commitsBetween(commit2, base)
228
+
if err != nil {
229
+
return "", nil, fmt.Errorf("failed to get commits: %w", err)
230
+
}
231
+
232
+
// reverse the list so we start from the oldest one and go up to the most recent one
233
+
slices.Reverse(commits)
234
+
235
+
var allPatchesContent strings.Builder
236
+
var allPatches []patchutil.FormatPatch
237
+
238
+
for _, commit := range commits {
239
+
changeId := ""
240
+
if val, ok := commit.ExtraHeaders["change-id"]; ok {
241
+
changeId = string(val)
242
+
}
243
+
244
+
var parentHash plumbing.Hash
245
+
if len(commit.ParentHashes) > 0 {
246
+
parentHash = commit.ParentHashes[0]
247
+
} else {
248
+
parentHash = plumbing.NewHash("4b825dc642cb6eb9a060e54bf8d69288fbee4904") // git empty tree hash
249
+
}
250
+
251
+
var additionalArgs []string
252
+
if changeId != "" {
253
+
additionalArgs = append(additionalArgs, "--add-header", fmt.Sprintf("Change-Id: %s", changeId))
254
+
}
255
+
256
+
stdout, patch, err := g.formatSinglePatch(parentHash, commit.Hash, additionalArgs...)
257
+
if err != nil {
258
+
return "", nil, fmt.Errorf("failed to format patch for commit %s: %w", commit.Hash.String(), err)
259
+
}
260
+
261
+
allPatchesContent.WriteString(stdout)
262
+
allPatchesContent.WriteString("\n")
263
+
264
+
allPatches = append(allPatches, *patch)
265
+
}
266
+
267
+
return allPatchesContent.String(), allPatches, nil
268
+
}
+1
knotserver/routes.go
+1
knotserver/routes.go