forked from tangled.org/core
this repo has no description

knotserver: insert change-ids into format-patch output

Changed files
+91 -8
knotserver
types
+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
··· 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
··· 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
··· 1006 1006 Rev1: commit1.Hash.String(), 1007 1007 Rev2: commit2.Hash.String(), 1008 1008 FormatPatch: formatPatch, 1009 + MergeBase: mergeBase.Hash.String(), 1009 1010 Patch: rawPatch, 1010 1011 }) 1011 1012 return
+1
types/repo.go
··· 37 37 Rev1 string `json:"rev1,omitempty"` 38 38 Rev2 string `json:"rev2,omitempty"` 39 39 FormatPatch []patchutil.FormatPatch `json:"format_patch,omitempty"` 40 + MergeBase string `json:"merge_base,omitempty"` 40 41 Patch string `json:"patch,omitempty"` 41 42 } 42 43