-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmerge.go
More file actions
104 lines (100 loc) · 2.98 KB
/
merge.go
File metadata and controls
104 lines (100 loc) · 2.98 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
package diff
import "strings"
func dropWhile(d []Item, fn func(Item) bool) []Item {
for i, x := range d {
if !fn(x) {
return d[i:]
}
}
return nil
}
// Merge takes a slice of original lines, as well as any number of other
// "versions". A Patience diff from the original is computed for each
// version, the changesets are merged together, and the resulting lines are
// returned.
//
// The algorithm used for merging the resulting diffs, is:
// while there are non-empty diffs,
// if the first element in each diff is Unchanged, remove a line from the
// front of the original set of lines, and append it to the result lines,
// removing the Unchanged first element from each diff
// else,
// if a diff begins with an insertion,
// while the diff's first item is an insertion, remove it and add
// the line to a temporary block
// join the temporary block with newlines, and if the last line in
// the results does not have the temporary block as a prefix,
// append it to the results
// else if a diff begins with a deletion,
// if the first line of the original set of lines matches the deletion,
// remove it
// for each other non-empty diff,
// remove the first element if it is Unchanged and matches the deletion
func Merge(a []string, others ...[]string) []string {
diffs := make([][]Item, len(others))
for i, l := range others {
diffs[i] = Patience(a, l)
}
merged := make([]string, 0)
// Continue until all diffs have been exhausted
for empty := false; !empty; {
changed := false
empty = true
for i, d := range diffs {
if len(d) == 0 {
continue
}
empty = false
switch d[0].Type {
case Insertion:
changed = true
// accumulate blocks of added lines
toInsert := make([]string, 0)
diffs[i] = dropWhile(d, func(c Item) (drop bool) {
if drop = (c.Type == Insertion); drop {
toInsert = append(toInsert, c.Text)
}
return
})
// skip lines that were just added (completely or partially)
// by another diff
joined := strings.Join(toInsert, "\n")
if len(merged) == 0 || !strings.HasPrefix(merged[len(merged)-1], joined) {
merged = append(merged, joined)
}
case Deletion:
changed = true
diffs[i] = dropWhile(d, func(c Item) (drop bool) {
if drop = (c.Type == Deletion); drop {
if len(a) > 0 && a[0] == c.Text {
a = a[1:]
}
// delete lines from other diffs
for j := 0; j < len(diffs); j++ {
if i == j {
continue
}
diffs[j] = dropWhile(diffs[j], func(x Item) bool {
return x.Type == Unchanged && x.Text == c.Text
})
}
}
return
})
}
}
if !changed {
// "pop" the unchanged line from all diffs
for i, d := range diffs {
if len(d) > 0 {
diffs[i] = d[1:]
}
}
if len(a) > 0 {
merged = append(merged, a[0])
a = a[1:]
}
}
}
return merged
}