Mirror of @tangled.org/core. Running on a Raspberry Pi Zero 2 (Please be gentle).

knotserver: use common ancestor to calculate diffs

the existing diff calculation approach directly compares branches, this forces linear history for merge. this patch updates the diff logic to use the common ancestor commit to calculate diffs.

```
P Q
o---o topic
/
/
o---o---o---o master
A X Y
```

when diffing topic and master directly with difftree (Q and Y), the commits introduced in master since the creation of the branch, namely, X and Y, are considered "removed" from the generated patch. however, when diffing with the common ancestor A, only changes introduced by P and Q are included in the produced patch.

this calculation can be retired in favor of format-patch soon enough (i think).

authored by oppi.li and committed by

Tangled 70a9bc2d 6c27a747

+46 -13
+24 -12
knotserver/git/diff.go
··· 86 86 return &nd, nil 87 87 } 88 88 89 - func (g *GitRepo) DiffTree(rev1, rev2 string) (*types.DiffTree, error) { 90 - commit1, err := g.resolveRevision(rev1) 91 - if err != nil { 92 - return nil, fmt.Errorf("Invalid revision: %s", rev1) 93 - } 94 - 95 - commit2, err := g.resolveRevision(rev2) 96 - if err != nil { 97 - return nil, fmt.Errorf("Invalid revision: %s", rev2) 98 - } 99 - 89 + func (g *GitRepo) DiffTree(commit1, commit2 *object.Commit) (*types.DiffTree, error) { 100 90 tree1, err := commit1.Tree() 101 91 if err != nil { 102 92 return nil, err ··· 120 130 }, nil 121 131 } 122 132 123 - func (g *GitRepo) resolveRevision(revStr string) (*object.Commit, error) { 133 + func (g *GitRepo) MergeBase(commit1, commit2 *object.Commit) (*object.Commit, error) { 134 + isAncestor, err := commit1.IsAncestor(commit2) 135 + if err != nil { 136 + return nil, err 137 + } 138 + 139 + if isAncestor { 140 + return commit1, nil 141 + } 142 + 143 + mergeBase, err := commit1.MergeBase(commit2) 144 + if err != nil { 145 + return nil, err 146 + } 147 + 148 + if len(mergeBase) == 0 { 149 + return nil, fmt.Errorf("failed to find a merge-base") 150 + } 151 + 152 + return mergeBase[0], nil 153 + } 154 + 155 + func (g *GitRepo) ResolveRevision(revStr string) (*object.Commit, error) { 124 156 rev, err := g.r.ResolveRevision(plumbing.Revision(revStr)) 125 157 if err != nil { 126 158 return nil, fmt.Errorf("resolving revision %s: %w", revStr, err)
+22 -1
knotserver/routes.go
··· 775 775 return 776 776 } 777 777 778 - difftree, err := gr.DiffTree(rev1, rev2) 778 + commit1, err := gr.ResolveRevision(rev1) 779 + if err != nil { 780 + l.Error("error resolving revision 1", "msg", err.Error()) 781 + writeError(w, fmt.Sprintf("error resolving revision %s", rev1), http.StatusBadRequest) 782 + return 783 + } 784 + 785 + commit2, err := gr.ResolveRevision(rev2) 786 + if err != nil { 787 + l.Error("error resolving revision 2", "msg", err.Error()) 788 + writeError(w, fmt.Sprintf("error resolving revision %s", rev2), http.StatusBadRequest) 789 + return 790 + } 791 + 792 + mergeBase, err := gr.MergeBase(commit1, commit2) 793 + if err != nil { 794 + l.Error("failed to find merge-base", "msg", err.Error()) 795 + writeError(w, "failed to calculate diff", http.StatusBadRequest) 796 + return 797 + } 798 + 799 + difftree, err := gr.DiffTree(mergeBase, commit2) 779 800 if err != nil { 780 801 l.Error("error comparing revisions", "msg", err.Error()) 781 802 writeError(w, "error comparing revisions", http.StatusBadRequest)