mirror of
https://github.com/dolthub/dolt.git
synced 2026-01-27 18:59:23 -06:00
221
cmd/noms-diff/diff/diff.go
Normal file
221
cmd/noms-diff/diff/diff.go
Normal file
@@ -0,0 +1,221 @@
|
||||
// Copyright 2016 The Noms Authors. All rights reserved.
|
||||
// Licensed under the Apache License, version 2.0:
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
package diff
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
|
||||
"github.com/attic-labs/noms/go/d"
|
||||
"github.com/attic-labs/noms/go/types"
|
||||
)
|
||||
|
||||
const (
|
||||
addPrefix = "+ "
|
||||
subPrefix = "- "
|
||||
)
|
||||
|
||||
func isPrimitiveOrRef(v1 types.Value) bool {
|
||||
kind := v1.Type().Kind()
|
||||
return types.IsPrimitiveKind(kind) || kind == types.RefKind
|
||||
}
|
||||
|
||||
func canCompare(v1, v2 types.Value) bool {
|
||||
return !isPrimitiveOrRef(v1) && v1.Type().Kind() == v2.Type().Kind()
|
||||
}
|
||||
|
||||
func Diff(w io.Writer, v1, v2 types.Value) (err error) {
|
||||
dq := NewDiffQueue()
|
||||
di := diffInfo{path: types.NewPath().AddField("/"), v1: v1, v2: v2}
|
||||
dq.PushBack(di)
|
||||
|
||||
err = d.Try(func() {
|
||||
for di, ok := dq.PopFront(); ok; di, ok = dq.PopFront() {
|
||||
p, key, v1, v2 := di.path, di.key, di.v1, di.v2
|
||||
|
||||
v1.Type().Kind()
|
||||
if v1 == nil && v2 != nil {
|
||||
line(w, addPrefix, key, v2)
|
||||
}
|
||||
if v1 != nil && v2 == nil {
|
||||
line(w, subPrefix, key, v1)
|
||||
}
|
||||
if !v1.Equals(v2) {
|
||||
if !canCompare(v1, v2) {
|
||||
line(w, subPrefix, key, v1)
|
||||
line(w, addPrefix, key, v2)
|
||||
} else {
|
||||
switch v1.Type().Kind() {
|
||||
case types.ListKind:
|
||||
diffLists(dq, w, p, v1.(types.List), v2.(types.List))
|
||||
case types.MapKind:
|
||||
diffMaps(dq, w, p, v1.(types.Map), v2.(types.Map))
|
||||
case types.SetKind:
|
||||
diffSets(dq, w, p, v1.(types.Set), v2.(types.Set))
|
||||
case types.StructKind:
|
||||
diffStructs(dq, w, p, v1.(types.Struct), v2.(types.Struct))
|
||||
default:
|
||||
panic("Unrecognized type in diff function")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
func diffLists(dq *diffQueue, w io.Writer, p types.Path, v1, v2 types.List) {
|
||||
wroteHeader := false
|
||||
splices, _ := v2.Diff(v1)
|
||||
for _, splice := range splices {
|
||||
if splice.SpRemoved == splice.SpAdded {
|
||||
for i := uint64(0); i < splice.SpRemoved; i++ {
|
||||
lastEl := v1.Get(splice.SpAt + i)
|
||||
newEl := v2.Get(splice.SpFrom + i)
|
||||
if canCompare(lastEl, newEl) {
|
||||
idx := types.Number(splice.SpAt + i)
|
||||
p1 := p.AddIndex(idx)
|
||||
dq.PushBack(diffInfo{p1, idx, lastEl, newEl})
|
||||
} else {
|
||||
wroteHeader = writeHeader(w, wroteHeader, p)
|
||||
line(w, subPrefix, nil, v1.Get(splice.SpAt+i))
|
||||
line(w, addPrefix, nil, v2.Get(splice.SpFrom+i))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for i := uint64(0); i < splice.SpRemoved; i++ {
|
||||
wroteHeader = writeHeader(w, wroteHeader, p)
|
||||
line(w, subPrefix, nil, v1.Get(splice.SpAt+i))
|
||||
}
|
||||
for i := uint64(0); i < splice.SpAdded; i++ {
|
||||
wroteHeader = writeHeader(w, wroteHeader, p)
|
||||
line(w, addPrefix, nil, v2.Get(splice.SpFrom+i))
|
||||
}
|
||||
}
|
||||
}
|
||||
writeFooter(w, wroteHeader)
|
||||
}
|
||||
|
||||
func diffMaps(dq *diffQueue, w io.Writer, p types.Path, v1, v2 types.Map) {
|
||||
wroteHeader := false
|
||||
|
||||
added, removed, modified := v2.Diff(v1)
|
||||
for _, k := range added {
|
||||
wroteHeader = writeHeader(w, wroteHeader, p)
|
||||
line(w, addPrefix, k, v2.Get(k))
|
||||
}
|
||||
for _, k := range removed {
|
||||
wroteHeader = writeHeader(w, wroteHeader, p)
|
||||
line(w, subPrefix, k, v1.Get(k))
|
||||
}
|
||||
for _, k := range modified {
|
||||
c1, c2 := v1.Get(k), v2.Get(k)
|
||||
if canCompare(c1, c2) {
|
||||
buf := bytes.NewBuffer(nil)
|
||||
d.Exp.NoError(types.WriteEncodedValueWithTags(buf, k))
|
||||
p1 := p.AddField(buf.String())
|
||||
dq.PushBack(diffInfo{path: p1, key: k, v1: c1, v2: c2})
|
||||
} else {
|
||||
wroteHeader = writeHeader(w, wroteHeader, p)
|
||||
line(w, subPrefix, k, v1.Get(k))
|
||||
line(w, addPrefix, k, v2.Get(k))
|
||||
}
|
||||
}
|
||||
writeFooter(w, wroteHeader)
|
||||
}
|
||||
|
||||
func diffStructs(dq *diffQueue, w io.Writer, p types.Path, v1, v2 types.Struct) {
|
||||
changed := types.StructDiff(v1, v2)
|
||||
wroteHeader := false
|
||||
for _, field := range changed {
|
||||
f1 := v1.Get(field)
|
||||
f2 := v2.Get(field)
|
||||
if canCompare(f1, f2) {
|
||||
p1 := p.AddField(field)
|
||||
dq.PushBack(diffInfo{path: p1, key: types.NewString(field), v1: f1, v2: f2})
|
||||
} else {
|
||||
wroteHeader = writeHeader(w, wroteHeader, p)
|
||||
line(w, subPrefix, types.NewString(field), f1)
|
||||
line(w, addPrefix, types.NewString(field), f2)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func diffSets(dq *diffQueue, w io.Writer, p types.Path, v1, v2 types.Set) {
|
||||
wroteHeader := false
|
||||
added, removed := v2.Diff(v1)
|
||||
if len(added) == 1 && len(removed) == 1 && canCompare(added[0], removed[0]) {
|
||||
p1 := p.AddField(added[0].Hash().String())
|
||||
dq.PushBack(diffInfo{path: p1, key: types.NewString(""), v1: removed[0], v2: added[0]})
|
||||
} else {
|
||||
for _, value := range removed {
|
||||
wroteHeader = writeHeader(w, wroteHeader, p)
|
||||
line(w, subPrefix, nil, value)
|
||||
}
|
||||
for _, value := range added {
|
||||
wroteHeader = writeHeader(w, wroteHeader, p)
|
||||
line(w, addPrefix, nil, value)
|
||||
}
|
||||
}
|
||||
writeFooter(w, wroteHeader)
|
||||
return
|
||||
}
|
||||
|
||||
type prefixWriter struct {
|
||||
w io.Writer
|
||||
prefix []byte
|
||||
}
|
||||
|
||||
// todo: Not sure if we want to use a writer to do this for the longterm but, if so, we can
|
||||
// probably do better than writing byte by byte
|
||||
func (pw prefixWriter) Write(bytes []byte) (n int, err error) {
|
||||
for i, b := range bytes {
|
||||
_, err = pw.w.Write([]byte{b})
|
||||
if err != nil {
|
||||
return i, err
|
||||
}
|
||||
if b == '\n' {
|
||||
_, err := pw.w.Write(pw.prefix)
|
||||
if err != nil {
|
||||
return i, err
|
||||
}
|
||||
}
|
||||
}
|
||||
return len(bytes), nil
|
||||
}
|
||||
|
||||
func line(w io.Writer, start string, key, v2 types.Value) {
|
||||
var err error
|
||||
pw := prefixWriter{w: w, prefix: []byte(start)}
|
||||
_, err = w.Write([]byte(start))
|
||||
d.Exp.NoError(err)
|
||||
if key != nil {
|
||||
d.Exp.NoError(types.WriteEncodedValueWithTags(pw, key))
|
||||
_, err = w.Write([]byte(": "))
|
||||
d.Exp.NoError(err)
|
||||
}
|
||||
d.Exp.NoError(types.WriteEncodedValueWithTags(pw, v2))
|
||||
_, err = w.Write([]byte("\n"))
|
||||
d.Exp.NoError(err)
|
||||
}
|
||||
|
||||
func writeHeader(w io.Writer, wroteHeader bool, p types.Path) bool {
|
||||
var err error
|
||||
if !wroteHeader {
|
||||
_, err = w.Write([]byte(p.String()))
|
||||
d.Exp.NoError(err)
|
||||
_, err = w.Write([]byte(" {\n"))
|
||||
d.Exp.NoError(err)
|
||||
wroteHeader = true
|
||||
}
|
||||
return wroteHeader
|
||||
}
|
||||
|
||||
func writeFooter(w io.Writer, wroteHeader bool) {
|
||||
if wroteHeader {
|
||||
_, err := w.Write([]byte(" }\n"))
|
||||
d.Exp.NoError(err)
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,10 @@
|
||||
package main
|
||||
// Copyright 2016 The Noms Authors. All rights reserved.
|
||||
// Licensed under the Apache License, version 2.0:
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
package diff
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/attic-labs/noms/go/types"
|
||||
@@ -18,8 +21,6 @@ var (
|
||||
mm3 = createMap("m1", "m-one", "v2", "m-two", "m3", "m-three", "m4", aa1)
|
||||
mm3x = createMap("m1", "m-one", "v2", "m-two", "m3", "m-three-diff", "m4", aa1x)
|
||||
mm4 = createMap("n1", "n-one", "n2", "n-two", "n3", "n-three", "n4", aa1)
|
||||
|
||||
startPath = types.NewPath().AddField("/")
|
||||
)
|
||||
|
||||
func valToTypesValue(v interface{}) types.Value {
|
||||
@@ -73,26 +74,26 @@ func TestNomsMapdiff(t *testing.T) {
|
||||
|
||||
m1 := createMap("map-1", mm1, "map-2", mm2, "map-3", mm3, "map-4", mm4)
|
||||
m2 := createMap("map-1", mm1, "map-2", mm2, "map-3", mm3x, "map-4", mm4)
|
||||
diffQ.PushBack(diffInfo{path: startPath, key: nil, v1: m1, v2: m2})
|
||||
buf := util.NewBuffer(nil)
|
||||
diff(buf)
|
||||
Diff(buf, m1, m2)
|
||||
assert.Equal(expected, buf.String())
|
||||
}
|
||||
|
||||
func TestNomsSetDiff(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
expected := "./.sha1-c26be7ea6e815f747c1552fe402a773ad466be88 {\n- \"m3\": \"m-three\"\n+ \"m3\": \"m-three-diff\"\n }\n./.sha1-c26be7ea6e815f747c1552fe402a773ad466be88.\"m4\" {\n- \"a1\": \"a-one\"\n+ \"a1\": \"a-one-diff\"\n }\n"
|
||||
|
||||
expected := "./ {\n- \"five\"\n+ \"five-diff\"\n }\n"
|
||||
s1 := createSet("one", "three", "five", "seven", "nine")
|
||||
s2 := createSet("one", "three", "five-diff", "seven", "nine")
|
||||
diffQ.PushBack(diffInfo{path: startPath, key: nil, v1: s1, v2: s2})
|
||||
diff(os.Stdout)
|
||||
buf := util.NewBuffer(nil)
|
||||
Diff(buf, s1, s2)
|
||||
assert.Equal(expected, buf.String())
|
||||
|
||||
expected = "./.sha1-c26be7ea6e815f747c1552fe402a773ad466be88 {\n- \"m3\": \"m-three\"\n+ \"m3\": \"m-three-diff\"\n }\n./.sha1-c26be7ea6e815f747c1552fe402a773ad466be88.\"m4\" {\n- \"a1\": \"a-one\"\n+ \"a1\": \"a-one-diff\"\n }\n"
|
||||
s1 = createSet(mm1, mm2, mm3, mm4)
|
||||
s2 = createSet(mm1, mm2, mm3x, mm4)
|
||||
diffQ.PushBack(diffInfo{path: startPath, key: nil, v1: s1, v2: s2})
|
||||
buf := util.NewBuffer(nil)
|
||||
diff(buf)
|
||||
buf = util.NewBuffer(nil)
|
||||
Diff(buf, s1, s2)
|
||||
assert.Equal(expected, buf.String())
|
||||
}
|
||||
|
||||
@@ -112,30 +113,32 @@ func TestNomsStructDiff(t *testing.T) {
|
||||
m1 := createMap("one", 1, "two", 2, "three", s1, "four", "four")
|
||||
m2 := createMap("one", 1, "two", 2, "three", s2, "four", "four-diff")
|
||||
|
||||
diffQ.PushBack(diffInfo{path: startPath, key: nil, v1: m1, v2: m2})
|
||||
buf := util.NewBuffer(nil)
|
||||
diff(buf)
|
||||
Diff(buf, m1, m2)
|
||||
assert.Equal(expected, buf.String())
|
||||
}
|
||||
|
||||
func TestNomsListDiff(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
expected := "./[2] {\n- \"m3\": \"m-three\"\n+ \"m3\": \"m-three-diff\"\n }\n./[2].\"m4\" {\n- \"a1\": \"a-one\"\n+ \"a1\": \"a-one-diff\"\n }\n"
|
||||
|
||||
expected := "./ {\n- 2\n+ 22\n- 44\n }\n"
|
||||
l1 := createList(1, 2, 3, 4, 44, 5, 6)
|
||||
l2 := createList(1, 22, 3, 4, 5, 6)
|
||||
diffQ.PushBack(diffInfo{path: startPath, key: nil, v1: l1, v2: l2})
|
||||
diff(os.Stdout)
|
||||
buf := util.NewBuffer(nil)
|
||||
Diff(buf, l1, l2)
|
||||
assert.Equal(expected, buf.String())
|
||||
|
||||
expected = "./ {\n+ \"seven\"\n }\n"
|
||||
l1 = createList("one", "two", "three", "four", "five", "six")
|
||||
l2 = createList("one", "two", "three", "four", "five", "six", "seven")
|
||||
diffQ.PushBack(diffInfo{path: startPath, key: nil, v1: l1, v2: l2})
|
||||
diff(os.Stdout)
|
||||
buf = util.NewBuffer(nil)
|
||||
Diff(buf, l1, l2)
|
||||
assert.Equal(expected, buf.String())
|
||||
|
||||
expected = "./[2] {\n- \"m3\": \"m-three\"\n+ \"m3\": \"m-three-diff\"\n }\n./[2].\"m4\" {\n- \"a1\": \"a-one\"\n+ \"a1\": \"a-one-diff\"\n }\n"
|
||||
l1 = createList(mm1, mm2, mm3, mm4)
|
||||
l2 = createList(mm1, mm2, mm3x, mm4)
|
||||
diffQ.PushBack(diffInfo{path: startPath, key: nil, v1: l1, v2: l2})
|
||||
buf := util.NewBuffer(nil)
|
||||
diff(buf)
|
||||
buf = util.NewBuffer(nil)
|
||||
Diff(buf, l1, l2)
|
||||
assert.Equal(expected, buf.String())
|
||||
}
|
||||
@@ -1,4 +1,8 @@
|
||||
package main
|
||||
// Copyright 2016 The Noms Authors. All rights reserved.
|
||||
// Licensed under the Apache License, version 2.0:
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
package diff
|
||||
|
||||
import (
|
||||
"container/list"
|
||||
@@ -1,4 +1,8 @@
|
||||
package main
|
||||
// Copyright 2016 The Noms Authors. All rights reserved.
|
||||
// Licensed under the Apache License, version 2.0:
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
package diff
|
||||
|
||||
import (
|
||||
"testing"
|
||||
@@ -1,14 +1,16 @@
|
||||
// Copyright 2016 The Noms Authors. All rights reserved.
|
||||
// Licensed under the Apache License, version 2.0:
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/attic-labs/noms/go/types"
|
||||
"github.com/attic-labs/noms/cmd/noms-diff/diff"
|
||||
"github.com/attic-labs/noms/go/util/outputpager"
|
||||
"github.com/attic-labs/noms/samples/go/flags"
|
||||
"github.com/attic-labs/noms/samples/go/util"
|
||||
@@ -21,7 +23,6 @@ const (
|
||||
|
||||
var (
|
||||
showHelp = flag.Bool("help", false, "show help text")
|
||||
diffQ = NewDiffQueue()
|
||||
)
|
||||
|
||||
func main() {
|
||||
@@ -55,17 +56,9 @@ func main() {
|
||||
util.CheckError(err)
|
||||
defer db2.Close()
|
||||
|
||||
di := diffInfo{
|
||||
path: types.NewPath().AddField("/"),
|
||||
key: nil,
|
||||
v1: value1,
|
||||
v2: value2,
|
||||
}
|
||||
diffQ.PushBack(di)
|
||||
|
||||
waitChan := outputpager.PageOutput(!*outputpager.NoPager)
|
||||
|
||||
diff(os.Stdout)
|
||||
diff.Diff(os.Stdout, value1, value2)
|
||||
fmt.Fprintf(os.Stdout, "\n")
|
||||
|
||||
if waitChan != nil {
|
||||
@@ -73,190 +66,3 @@ func main() {
|
||||
<-waitChan
|
||||
}
|
||||
}
|
||||
|
||||
func isPrimitiveOrRef(v1 types.Value) bool {
|
||||
kind := v1.Type().Kind()
|
||||
return types.IsPrimitiveKind(kind) || kind == types.RefKind
|
||||
}
|
||||
|
||||
func canCompare(v1, v2 types.Value) bool {
|
||||
return !isPrimitiveOrRef(v1) && v1.Type().Kind() == v2.Type().Kind()
|
||||
}
|
||||
|
||||
func diff(w io.Writer) {
|
||||
for di, ok := diffQ.PopFront(); ok; di, ok = diffQ.PopFront() {
|
||||
p, key, v1, v2 := di.path, di.key, di.v1, di.v2
|
||||
|
||||
v1.Type().Kind()
|
||||
if v1 == nil && v2 != nil {
|
||||
line(w, addPrefix, key, v2)
|
||||
}
|
||||
if v1 != nil && v2 == nil {
|
||||
line(w, subPrefix, key, v1)
|
||||
}
|
||||
if !v1.Equals(v2) {
|
||||
if !canCompare(v1, v2) {
|
||||
line(w, subPrefix, key, v1)
|
||||
line(w, addPrefix, key, v2)
|
||||
} else {
|
||||
switch v1.Type().Kind() {
|
||||
case types.ListKind:
|
||||
diffLists(w, p, v1.(types.List), v2.(types.List))
|
||||
case types.MapKind:
|
||||
diffMaps(w, p, v1.(types.Map), v2.(types.Map))
|
||||
case types.SetKind:
|
||||
diffSets(w, p, v1.(types.Set), v2.(types.Set))
|
||||
case types.StructKind:
|
||||
diffStructs(w, p, v1.(types.Struct), v2.(types.Struct))
|
||||
default:
|
||||
panic("Unrecognized type in diff function")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func diffLists(w io.Writer, p types.Path, v1, v2 types.List) {
|
||||
wroteHeader := false
|
||||
splices, _ := v2.Diff(v1)
|
||||
for _, splice := range splices {
|
||||
if splice.SpRemoved == splice.SpAdded {
|
||||
for i := uint64(0); i < splice.SpRemoved; i++ {
|
||||
lastEl := v1.Get(splice.SpAt + i)
|
||||
newEl := v2.Get(splice.SpFrom + i)
|
||||
if canCompare(lastEl, newEl) {
|
||||
idx := types.Number(splice.SpAt + i)
|
||||
p1 := p.AddIndex(idx)
|
||||
diffQ.PushBack(diffInfo{p1, idx, lastEl, newEl})
|
||||
} else {
|
||||
wroteHeader = writeHeader(w, wroteHeader, p)
|
||||
line(w, subPrefix, nil, v1.Get(splice.SpAt+i))
|
||||
line(w, addPrefix, nil, v2.Get(splice.SpFrom+i))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for i := uint64(0); i < splice.SpRemoved; i++ {
|
||||
wroteHeader = writeHeader(w, wroteHeader, p)
|
||||
line(w, subPrefix, nil, v1.Get(splice.SpAt+i))
|
||||
}
|
||||
for i := uint64(0); i < splice.SpAdded; i++ {
|
||||
wroteHeader = writeHeader(w, wroteHeader, p)
|
||||
line(w, addPrefix, nil, v2.Get(splice.SpFrom+i))
|
||||
}
|
||||
}
|
||||
}
|
||||
writeFooter(w, wroteHeader)
|
||||
}
|
||||
|
||||
func diffMaps(w io.Writer, p types.Path, v1, v2 types.Map) {
|
||||
wroteHeader := false
|
||||
|
||||
added, removed, modified := v2.Diff(v1)
|
||||
for _, k := range added {
|
||||
wroteHeader = writeHeader(w, wroteHeader, p)
|
||||
line(w, addPrefix, k, v2.Get(k))
|
||||
}
|
||||
for _, k := range removed {
|
||||
wroteHeader = writeHeader(w, wroteHeader, p)
|
||||
line(w, subPrefix, k, v1.Get(k))
|
||||
}
|
||||
for _, k := range modified {
|
||||
c1, c2 := v1.Get(k), v2.Get(k)
|
||||
if canCompare(c1, c2) {
|
||||
buf := bytes.NewBuffer(nil)
|
||||
types.WriteEncodedValueWithTags(buf, k)
|
||||
p1 := p.AddField(buf.String())
|
||||
diffQ.PushBack(diffInfo{path: p1, key: k, v1: c1, v2: c2})
|
||||
} else {
|
||||
wroteHeader = writeHeader(w, wroteHeader, p)
|
||||
line(w, subPrefix, k, v1.Get(k))
|
||||
line(w, addPrefix, k, v2.Get(k))
|
||||
}
|
||||
}
|
||||
writeFooter(w, wroteHeader)
|
||||
}
|
||||
|
||||
func diffStructs(w io.Writer, p types.Path, v1, v2 types.Struct) {
|
||||
changed := types.StructDiff(v1, v2)
|
||||
wroteHeader := false
|
||||
for _, field := range changed {
|
||||
f1 := v1.Get(field)
|
||||
f2 := v2.Get(field)
|
||||
if canCompare(f1, f2) {
|
||||
p1 := p.AddField(field)
|
||||
diffQ.PushBack(diffInfo{path: p1, key: types.NewString(field), v1: f1, v2: f2})
|
||||
} else {
|
||||
wroteHeader = writeHeader(w, wroteHeader, p)
|
||||
line(w, subPrefix, types.NewString(field), f1)
|
||||
line(w, addPrefix, types.NewString(field), f2)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func diffSets(w io.Writer, p types.Path, v1, v2 types.Set) {
|
||||
wroteHeader := false
|
||||
added, removed := v2.Diff(v1)
|
||||
if len(added) == 1 && len(removed) == 1 && canCompare(added[0], removed[0]) {
|
||||
p1 := p.AddField(added[0].Hash().String())
|
||||
diffQ.PushBack(diffInfo{path: p1, key: types.NewString(""), v1: removed[0], v2: added[0]})
|
||||
} else {
|
||||
for _, value := range removed {
|
||||
wroteHeader = writeHeader(w, wroteHeader, p)
|
||||
line(w, subPrefix, nil, value)
|
||||
}
|
||||
for _, value := range added {
|
||||
wroteHeader = writeHeader(w, wroteHeader, p)
|
||||
line(w, addPrefix, nil, value)
|
||||
}
|
||||
}
|
||||
writeFooter(w, wroteHeader)
|
||||
}
|
||||
|
||||
type prefixWriter struct {
|
||||
w io.Writer
|
||||
prefix []byte
|
||||
}
|
||||
|
||||
// todo: Not sure if we want to use a writer to do this for the longterm but, if so, we can
|
||||
// probably do better than writing byte by byte
|
||||
func (pw prefixWriter) Write(bytes []byte) (n int, err error) {
|
||||
for i, b := range bytes {
|
||||
_, err = pw.w.Write([]byte{b})
|
||||
if err != nil {
|
||||
return i, err
|
||||
}
|
||||
if b == '\n' {
|
||||
_, err := pw.w.Write(pw.prefix)
|
||||
if err != nil {
|
||||
return i, err
|
||||
}
|
||||
}
|
||||
}
|
||||
return len(bytes), nil
|
||||
}
|
||||
|
||||
func line(w io.Writer, start string, key, v2 types.Value) {
|
||||
pw := prefixWriter{w: w, prefix: []byte(start)}
|
||||
w.Write([]byte(start))
|
||||
if key != nil {
|
||||
types.WriteEncodedValueWithTags(pw, key)
|
||||
w.Write([]byte(": "))
|
||||
}
|
||||
types.WriteEncodedValueWithTags(pw, v2)
|
||||
w.Write([]byte("\n"))
|
||||
}
|
||||
|
||||
func writeHeader(w io.Writer, wroteHeader bool, p types.Path) bool {
|
||||
if !wroteHeader {
|
||||
w.Write([]byte(p.String()))
|
||||
w.Write([]byte(" {\n"))
|
||||
wroteHeader = true
|
||||
}
|
||||
return wroteHeader
|
||||
}
|
||||
|
||||
func writeFooter(w io.Writer, wroteHeader bool) {
|
||||
if wroteHeader {
|
||||
w.Write([]byte(" }\n"))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ func (iter *CommitIterator) Next() (LogNode, bool) {
|
||||
// Now that the branchlist has been adusted, check to see if there are branches with common
|
||||
// ancestors that will be folded together on this commit's graph.
|
||||
foldedCols := iter.branches.HighestBranchIndexes()
|
||||
return LogNode{
|
||||
node := LogNode{
|
||||
cr: br.cr,
|
||||
commit: br.commit,
|
||||
startingColCount: startingColCount,
|
||||
@@ -73,7 +73,8 @@ func (iter *CommitIterator) Next() (LogNode, bool) {
|
||||
newCols: newCols,
|
||||
foldedCols: foldedCols,
|
||||
lastCommit: iter.branches.IsEmpty(),
|
||||
}, true
|
||||
}
|
||||
return node, true
|
||||
}
|
||||
|
||||
type LogNode struct {
|
||||
@@ -88,7 +89,7 @@ type LogNode struct {
|
||||
}
|
||||
|
||||
func (n LogNode) String() string {
|
||||
return fmt.Sprintf("cr: %s, startingColCount: %d, endingColCount: %d, col: %d, newCols: %v, foldedCols: %v, expanding: %t, shrunk: %t, shrinking: %t", n.cr.TargetHash(), n.startingColCount, n.endingColCount, n.col, n.newCols, n.foldedCols, n.Expanding(), n.Shrunk(), n.Shrinking())
|
||||
return fmt.Sprintf("cr: %s(%d), startingColCount: %d, endingColCount: %d, col: %d, newCols: %v, foldedCols: %v, expanding: %t, shrunk: %t, shrinking: %t", n.cr.TargetHash().String()[0:9], n.cr.Height(), n.startingColCount, n.endingColCount, n.col, n.newCols, n.foldedCols, n.Expanding(), n.Shrunk(), n.Shrinking())
|
||||
}
|
||||
|
||||
// True if this commit's graph will expand to show an additional branch
|
||||
@@ -112,7 +113,7 @@ type branch struct {
|
||||
}
|
||||
|
||||
func (b branch) String() string {
|
||||
return b.cr.TargetHash().String()
|
||||
return fmt.Sprintf("%s(%d)", b.cr.TargetHash().String()[0:9], b.cr.Height())
|
||||
}
|
||||
|
||||
type branchList []branch
|
||||
|
||||
63
cmd/noms-log/max_line_writer.go
Normal file
63
cmd/noms-log/max_line_writer.go
Normal file
@@ -0,0 +1,63 @@
|
||||
// Copyright 2016 The Noms Authors. All rights reserved.
|
||||
// Licensed under the Apache License, version 2.0:
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"io"
|
||||
)
|
||||
|
||||
var (
|
||||
maxLinesError = MaxLinesError{"Maximum number of lines written"}
|
||||
)
|
||||
|
||||
type MaxLinesError struct {
|
||||
msg string
|
||||
}
|
||||
|
||||
func (e MaxLinesError) Error() string { return e.msg }
|
||||
|
||||
type maxLineWriter struct {
|
||||
numLines int
|
||||
maxLines int
|
||||
node LogNode
|
||||
dest io.Writer
|
||||
needsPrefix bool
|
||||
}
|
||||
|
||||
func (w *maxLineWriter) writeMax(data []byte, enforceMax bool) (byteCnt int, err error) {
|
||||
for _, b := range data {
|
||||
if w.needsPrefix {
|
||||
w.needsPrefix = false
|
||||
w.numLines++
|
||||
if *showGraph {
|
||||
s := genGraph(w.node, w.numLines)
|
||||
_, err = w.dest.Write([]byte(s))
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
byteCnt++
|
||||
if enforceMax && w.maxLines >= 0 && w.numLines >= w.maxLines {
|
||||
err = maxLinesError
|
||||
return
|
||||
}
|
||||
// TODO: This is not technically correct due to utf-8, but ... meh.
|
||||
w.needsPrefix = b == byte('\n')
|
||||
_, err = w.dest.Write(data[byteCnt-1 : byteCnt])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (w *maxLineWriter) Write(data []byte) (byteCnt int, err error) {
|
||||
return w.writeMax(data, true)
|
||||
}
|
||||
|
||||
func (w *maxLineWriter) forceWrite(data []byte) (byteCnt int, err error) {
|
||||
return w.writeMax(data, false)
|
||||
}
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/attic-labs/noms/cmd/noms-diff/diff"
|
||||
"github.com/attic-labs/noms/go/datas"
|
||||
"github.com/attic-labs/noms/go/types"
|
||||
"github.com/attic-labs/noms/go/util/outputpager"
|
||||
@@ -22,10 +23,9 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
color, maxLines, maxCommits *int
|
||||
showHelp, showGraph *bool
|
||||
useColor = false
|
||||
maxLinesError = errors.New("Maximum number of lines written")
|
||||
color, maxLines, maxCommits *int
|
||||
showHelp, showGraph, showValue *bool
|
||||
useColor = false
|
||||
)
|
||||
|
||||
func main() {
|
||||
@@ -34,6 +34,7 @@ func main() {
|
||||
maxCommits = flag.Int("n", 0, "max number of commits to display (0 for all commits)")
|
||||
showHelp = flag.Bool("help", false, "show help text")
|
||||
showGraph = flag.Bool("graph", false, "show ascii-based commit hierarcy on left side of output")
|
||||
showValue = flag.Bool("show-value", false, "show commit value rather than diff information -- this is temporary")
|
||||
|
||||
flag.Usage = func() {
|
||||
fmt.Fprintln(os.Stderr, "Displays the history of a Noms dataset\n")
|
||||
@@ -76,7 +77,7 @@ func main() {
|
||||
*maxCommits = math.MaxInt32
|
||||
}
|
||||
for ln, ok := iter.Next(); ok && displayed < *maxCommits; ln, ok = iter.Next() {
|
||||
if printCommit(ln) != nil {
|
||||
if printCommit(ln, database) != nil {
|
||||
break
|
||||
}
|
||||
displayed++
|
||||
@@ -90,7 +91,7 @@ func main() {
|
||||
|
||||
// Prints the information for one commit in the log, including ascii graph on left side of commits if
|
||||
// -graph arg is true.
|
||||
func printCommit(node LogNode) (err error) {
|
||||
func printCommit(node LogNode, db datas.Database) (err error) {
|
||||
lineno := 0
|
||||
doColor := func(s string) string { return s }
|
||||
if useColor {
|
||||
@@ -113,12 +114,13 @@ func printCommit(node LogNode) (err error) {
|
||||
}
|
||||
if *maxLines != 0 {
|
||||
var n int
|
||||
n, err = writeCommitLines(node, *maxLines, lineno, os.Stdout)
|
||||
if *showValue {
|
||||
n, err = writeCommitLines(node, *maxLines, lineno, os.Stdout)
|
||||
} else {
|
||||
n, err = writeDiffLines(node, db, *maxLines, lineno, os.Stdout)
|
||||
}
|
||||
lineno += n
|
||||
}
|
||||
if !node.lastCommit {
|
||||
fmt.Printf("%s\n", genGraph(node, lineno))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -175,65 +177,44 @@ func genGraph(node LogNode, lineno int) string {
|
||||
return string(buf)
|
||||
}
|
||||
|
||||
type maxLineWriter struct {
|
||||
numLines int
|
||||
maxLines int
|
||||
node LogNode
|
||||
dest io.Writer
|
||||
first bool
|
||||
}
|
||||
|
||||
func (w *maxLineWriter) Write(data []byte) (n int, err error) {
|
||||
doGraph := func() error {
|
||||
var err error
|
||||
w.numLines++
|
||||
if *showGraph {
|
||||
s := genGraph(w.node, w.numLines)
|
||||
_, err = w.dest.Write([]byte(s))
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
if w.first && len(data) > 0 {
|
||||
w.first = false
|
||||
err = doGraph()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
for _, b := range data {
|
||||
n++
|
||||
if w.numLines == w.maxLines {
|
||||
err = maxLinesError
|
||||
return
|
||||
}
|
||||
// TODO: This is not technically correct due to utf-8, but ... meh.
|
||||
newLine := b == byte('\n')
|
||||
_, err = w.dest.Write(data[n-1 : n])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if newLine {
|
||||
err = doGraph()
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func writeCommitLines(node LogNode, maxLines, lineno int, w io.Writer) (int, error) {
|
||||
out := &maxLineWriter{numLines: lineno, maxLines: maxLines, node: node, dest: w, first: true}
|
||||
err := types.WriteEncodedValueWithTags(out, node.commit.Get(datas.ValueField))
|
||||
if err == maxLinesError {
|
||||
fmt.Fprint(w, "...")
|
||||
out.numLines++
|
||||
func writeCommitLines(node LogNode, maxLines, lineno int, w io.Writer) (lineCnt int, err error) {
|
||||
mlw := &maxLineWriter{numLines: lineno, maxLines: maxLines, node: node, dest: w, needsPrefix: true}
|
||||
err = types.WriteEncodedValueWithTags(mlw, node.commit.Get(datas.ValueField))
|
||||
if err != nil {
|
||||
mlw.forceWrite([]byte("..."))
|
||||
mlw.numLines++
|
||||
err = nil
|
||||
}
|
||||
fmt.Fprintln(w)
|
||||
return out.numLines, err
|
||||
mlw.forceWrite([]byte("\n"))
|
||||
if !node.lastCommit {
|
||||
mlw.forceWrite([]byte("\n"))
|
||||
}
|
||||
return mlw.numLines, err
|
||||
}
|
||||
|
||||
func writeDiffLines(node LogNode, db datas.Database, maxLines, lineno int, w io.Writer) (lineCnt int, err error) {
|
||||
mlw := &maxLineWriter{numLines: lineno, maxLines: maxLines, node: node, dest: w, needsPrefix: true}
|
||||
parents := node.commit.Get(datas.ParentsField).(types.Set)
|
||||
var parent types.Value = nil
|
||||
if parents.Len() > 0 {
|
||||
parent = parents.First()
|
||||
}
|
||||
if parent == nil {
|
||||
_, err = fmt.Fprint(mlw, "\n")
|
||||
return 1, err
|
||||
}
|
||||
|
||||
parentCommit := parent.(types.Ref).TargetValue(db).(types.Struct)
|
||||
err = diff.Diff(mlw, parentCommit.Get(datas.ValueField), node.commit.Get(datas.ValueField))
|
||||
if err != nil {
|
||||
mlw.forceWrite([]byte("...\n"))
|
||||
mlw.numLines++
|
||||
err = nil
|
||||
}
|
||||
if !node.lastCommit {
|
||||
mlw.forceWrite([]byte("\n"))
|
||||
}
|
||||
return mlw.numLines, err
|
||||
}
|
||||
|
||||
func shouldUseColor() bool {
|
||||
|
||||
@@ -47,7 +47,6 @@ func testCommitInResults(s *nomsShowTestSuite, spec string, i int) {
|
||||
ds, err = ds.Commit(types.Number(i))
|
||||
s.NoError(err)
|
||||
commit := ds.Head()
|
||||
fmt.Printf("commit hash: %s, type: %s\n", commit.Hash(), commit.Type().Name())
|
||||
ds.Database().Close()
|
||||
s.Contains(s.Run(main, []string{spec}), commit.Hash().String())
|
||||
}
|
||||
@@ -168,7 +167,8 @@ func (s *nomsShowTestSuite) TestNomsGraph1() {
|
||||
s.NoError(err)
|
||||
|
||||
b1.Database().Close()
|
||||
s.Equal(graphRes1, s.Run(main, []string{"-graph", test_util.CreateValueSpecString("ldb", s.LdbDir, "b1")}))
|
||||
s.Equal(graphRes1, s.Run(main, []string{"-graph", "-show-value=true", test_util.CreateValueSpecString("ldb", s.LdbDir, "b1")}))
|
||||
s.Equal(diffRes1, s.Run(main, []string{"-graph", "-show-value=false", test_util.CreateValueSpecString("ldb", s.LdbDir, "b1")}))
|
||||
}
|
||||
|
||||
func (s *nomsShowTestSuite) TestNomsGraph2() {
|
||||
@@ -198,7 +198,8 @@ func (s *nomsShowTestSuite) TestNomsGraph2() {
|
||||
s.NoError(err)
|
||||
|
||||
db.Close()
|
||||
s.Equal(graphRes2, s.Run(main, []string{"-graph", test_util.CreateValueSpecString("ldb", s.LdbDir, "ba")}))
|
||||
s.Equal(graphRes2, s.Run(main, []string{"-graph", "-show-value=true", test_util.CreateValueSpecString("ldb", s.LdbDir, "ba")}))
|
||||
s.Equal(diffRes2, s.Run(main, []string{"-graph", "-show-value=false", test_util.CreateValueSpecString("ldb", s.LdbDir, "ba")}))
|
||||
}
|
||||
|
||||
func (s *nomsShowTestSuite) TestNomsGraph3() {
|
||||
@@ -238,7 +239,8 @@ func (s *nomsShowTestSuite) TestNomsGraph3() {
|
||||
s.NoError(err)
|
||||
|
||||
db.Close()
|
||||
s.Equal(graphRes3, s.Run(main, []string{"-graph", test_util.CreateValueSpecString("ldb", s.LdbDir, "w")}))
|
||||
s.Equal(graphRes3, s.Run(main, []string{"-graph", "-show-value=true", test_util.CreateValueSpecString("ldb", s.LdbDir, "w")}))
|
||||
s.Equal(diffRes3, s.Run(main, []string{"-graph", "-show-value=false", test_util.CreateValueSpecString("ldb", s.LdbDir, "w")}))
|
||||
}
|
||||
|
||||
func (s *nomsShowTestSuite) TestTruncation() {
|
||||
@@ -267,9 +269,14 @@ func (s *nomsShowTestSuite) TestTruncation() {
|
||||
db.Close()
|
||||
|
||||
dsSpec := test_util.CreateValueSpecString("ldb", s.LdbDir, "truncate")
|
||||
s.Equal(truncRes1, s.Run(main, []string{"-graph", dsSpec}))
|
||||
s.Equal(truncRes2, s.Run(main, []string{"-graph", "-max-lines=-1", dsSpec}))
|
||||
s.Equal(truncRes3, s.Run(main, []string{"-graph", "-max-lines=0", dsSpec}))
|
||||
s.Equal(truncRes1, s.Run(main, []string{"-graph", "-show-value=true", dsSpec}))
|
||||
s.Equal(diffTrunc1, s.Run(main, []string{"-graph", "-show-value=false", dsSpec}))
|
||||
|
||||
s.Equal(truncRes2, s.Run(main, []string{"-graph", "-show-value=true", "-max-lines=-1", dsSpec}))
|
||||
s.Equal(diffTrunc2, s.Run(main, []string{"-graph", "-show-value=false", "-max-lines=-1", dsSpec}))
|
||||
|
||||
s.Equal(truncRes3, s.Run(main, []string{"-graph", "-show-value=true", "-max-lines=0", dsSpec}))
|
||||
s.Equal(diffTrunc3, s.Run(main, []string{"-graph", "-show-value=false", "-max-lines=0", dsSpec}))
|
||||
}
|
||||
|
||||
func TestBranchlistSplice(t *testing.T) {
|
||||
@@ -297,14 +304,20 @@ func TestBranchlistSplice(t *testing.T) {
|
||||
|
||||
const (
|
||||
graphRes1 = "* sha1-0b0a92f5515b5778194d07d8011b7c18bf9be178\n| Parent: sha1-14491727789ebd03ca0ddd5e3a1307ca3e2651dc\n| \"7\"\n| \n* sha1-14491727789ebd03ca0ddd5e3a1307ca3e2651dc\n| Parent: sha1-b5a8493d7be003673216bdc0ed275fbbacbb9b08\n| \"6\"\n| \n* sha1-b5a8493d7be003673216bdc0ed275fbbacbb9b08\n| Parent: sha1-2b837c2e2dd0e6ef015e4742bd969f9fa1641f38\n| \"5\"\n| \n* sha1-2b837c2e2dd0e6ef015e4742bd969f9fa1641f38\n|\\ Merge: sha1-1f622d4006278a63cc77e95c6efcaf83f46606c4 sha1-eca65988a4ade585b8502d8f9fc37a5a5da94ab2\n| | \"4\"\n| | \n| * sha1-eca65988a4ade585b8502d8f9fc37a5a5da94ab2\n| | Parent: sha1-890cf3b1d49228ace9d691a627131a2872e42eb4\n| | \"3.7\"\n| | \n| * sha1-890cf3b1d49228ace9d691a627131a2872e42eb4\n| |\\ Merge: sha1-4f72aeacef670bc6884a97de5a363b0674ccd008 sha1-fa78bedf601fc6d49ebf70865944dfccf5b5a133\n| | | \"3.5\"\n| | | \n| | * sha1-fa78bedf601fc6d49ebf70865944dfccf5b5a133\n| | | Parent: sha1-72f530e2ba8ba3168de8da8d15271cd856ac5c2b\n| | | \"3.1.7\"\n| | | \n| | * sha1-72f530e2ba8ba3168de8da8d15271cd856ac5c2b\n| | | Parent: sha1-dda04a898b2048558b4238a4f111364ff5c92ab8\n| | | \"3.1.5\"\n| | | \n* | | sha1-1f622d4006278a63cc77e95c6efcaf83f46606c4\n| | | Parent: sha1-e1583426c4201c7602b4aa1ae835915666d933f0\n| | | \"3.6\"\n| | | \n| | * sha1-dda04a898b2048558b4238a4f111364ff5c92ab8\n| | | Parent: sha1-4f72aeacef670bc6884a97de5a363b0674ccd008\n| | | \"3.1.3\"\n| | | \n* | | sha1-e1583426c4201c7602b4aa1ae835915666d933f0\n| |/ Parent: sha1-27290eda714ce5d47df05e8f77a0986647887e32\n| | \"3.2\"\n| | \n| * sha1-4f72aeacef670bc6884a97de5a363b0674ccd008\n|/ Parent: sha1-27290eda714ce5d47df05e8f77a0986647887e32\n| \"3.1\"\n| \n* sha1-27290eda714ce5d47df05e8f77a0986647887e32\n| Parent: sha1-624532f5d8a5839344ccfc465957a975e1962b6d\n| \"3\"\n| \n* sha1-624532f5d8a5839344ccfc465957a975e1962b6d\n| Parent: sha1-611d5d868352d4d6ae9b778d6627b81f769cdef5\n| \"2\"\n| \n* sha1-611d5d868352d4d6ae9b778d6627b81f769cdef5\n| Parent: None\n| \"1\"\n"
|
||||
diffRes1 = "* sha1-0b0a92f5515b5778194d07d8011b7c18bf9be178\n| Parent: sha1-14491727789ebd03ca0ddd5e3a1307ca3e2651dc\n| - \"6\"\n| + \"7\"\n| \n* sha1-14491727789ebd03ca0ddd5e3a1307ca3e2651dc\n| Parent: sha1-b5a8493d7be003673216bdc0ed275fbbacbb9b08\n| - \"5\"\n| + \"6\"\n| \n* sha1-b5a8493d7be003673216bdc0ed275fbbacbb9b08\n| Parent: sha1-2b837c2e2dd0e6ef015e4742bd969f9fa1641f38\n| - \"4\"\n| + \"5\"\n| \n* sha1-2b837c2e2dd0e6ef015e4742bd969f9fa1641f38\n|\\ Merge: sha1-1f622d4006278a63cc77e95c6efcaf83f46606c4 sha1-eca65988a4ade585b8502d8f9fc37a5a5da94ab2\n| | - \"3.6\"\n| | + \"4\"\n| | \n| * sha1-eca65988a4ade585b8502d8f9fc37a5a5da94ab2\n| | Parent: sha1-890cf3b1d49228ace9d691a627131a2872e42eb4\n| | - \"3.5\"\n| | + \"3.7\"\n| | \n| * sha1-890cf3b1d49228ace9d691a627131a2872e42eb4\n| |\\ Merge: sha1-4f72aeacef670bc6884a97de5a363b0674ccd008 sha1-fa78bedf601fc6d49ebf70865944dfccf5b5a133\n| | | - \"3.1\"\n| | | + \"3.5\"\n| | | \n| | * sha1-fa78bedf601fc6d49ebf70865944dfccf5b5a133\n| | | Parent: sha1-72f530e2ba8ba3168de8da8d15271cd856ac5c2b\n| | | - \"3.1.5\"\n| | | + \"3.1.7\"\n| | | \n| | * sha1-72f530e2ba8ba3168de8da8d15271cd856ac5c2b\n| | | Parent: sha1-dda04a898b2048558b4238a4f111364ff5c92ab8\n| | | - \"3.1.3\"\n| | | + \"3.1.5\"\n| | | \n* | | sha1-1f622d4006278a63cc77e95c6efcaf83f46606c4\n| | | Parent: sha1-e1583426c4201c7602b4aa1ae835915666d933f0\n| | | - \"3.2\"\n| | | + \"3.6\"\n| | | \n| | * sha1-dda04a898b2048558b4238a4f111364ff5c92ab8\n| | | Parent: sha1-4f72aeacef670bc6884a97de5a363b0674ccd008\n| | | - \"3.1\"\n| | | + \"3.1.3\"\n| | | \n* | | sha1-e1583426c4201c7602b4aa1ae835915666d933f0\n| |/ Parent: sha1-27290eda714ce5d47df05e8f77a0986647887e32\n| | - \"3\"\n| | + \"3.2\"\n| | \n| * sha1-4f72aeacef670bc6884a97de5a363b0674ccd008\n|/ Parent: sha1-27290eda714ce5d47df05e8f77a0986647887e32\n| - \"3\"\n| + \"3.1\"\n| \n* sha1-27290eda714ce5d47df05e8f77a0986647887e32\n| Parent: sha1-624532f5d8a5839344ccfc465957a975e1962b6d\n| - \"2\"\n| + \"3\"\n| \n* sha1-624532f5d8a5839344ccfc465957a975e1962b6d\n| Parent: sha1-611d5d868352d4d6ae9b778d6627b81f769cdef5\n| - \"1\"\n| + \"2\"\n| \n* sha1-611d5d868352d4d6ae9b778d6627b81f769cdef5\n| Parent: None\n| \n"
|
||||
|
||||
graphRes2 = "* sha1-65c7e849861df97129cbd49352e52eef6dad3b11\n|\\ Merge: sha1-4578a4c35f09b6fe85608a95145a36f4c701d030 sha1-aecf8991bf5af5bb0b725ff2bdeb260426508ac5\n| | \"101\"\n| | \n* | sha1-4578a4c35f09b6fe85608a95145a36f4c701d030\n|\\ \\ Merge: sha1-611d5d868352d4d6ae9b778d6627b81f769cdef5 sha1-d9cf6dcbf03a014c28c80a9baa34525b4f9095c8\n| | | \"11\"\n| | | \n* | sha1-611d5d868352d4d6ae9b778d6627b81f769cdef5\n| | Parent: None\n| | \"1\"\n| | \n* sha1-d9cf6dcbf03a014c28c80a9baa34525b4f9095c8\n| Parent: None\n| \"10\"\n| \n* sha1-aecf8991bf5af5bb0b725ff2bdeb260426508ac5\n| Parent: None\n| \"100\"\n"
|
||||
diffRes2 = "* sha1-65c7e849861df97129cbd49352e52eef6dad3b11\n|\\ Merge: sha1-4578a4c35f09b6fe85608a95145a36f4c701d030 sha1-aecf8991bf5af5bb0b725ff2bdeb260426508ac5\n| | - \"11\"\n| | + \"101\"\n| | \n* | sha1-4578a4c35f09b6fe85608a95145a36f4c701d030\n|\\ \\ Merge: sha1-611d5d868352d4d6ae9b778d6627b81f769cdef5 sha1-d9cf6dcbf03a014c28c80a9baa34525b4f9095c8\n| | | - \"1\"\n| | | + \"11\"\n| | | \n* | sha1-611d5d868352d4d6ae9b778d6627b81f769cdef5\n| | Parent: None\n| | \n* sha1-d9cf6dcbf03a014c28c80a9baa34525b4f9095c8\n| Parent: None\n| \n* sha1-aecf8991bf5af5bb0b725ff2bdeb260426508ac5\n| Parent: None\n| \n"
|
||||
|
||||
graphRes3 = "* sha1-a859b0936cc42f073c03a27eb820f29a57f025f3\n|\\ Merge: sha1-d996bef5350a0316da794ae1f262dfcdcd4b2e8c sha1-65c99ad8e4db74b2cb3bf00bff806a4d46a220eb\n| | \"2222-wz\"\n| | \n* | sha1-d996bef5350a0316da794ae1f262dfcdcd4b2e8c\n|\\ \\ Merge: sha1-da122844f28f7a814fb99a4950ff673ea7e3611c sha1-ba3e907b42eaba15bf01752b012c92f8e5821536\n| | | \"222-wy\"\n| | | \n| * | sha1-ba3e907b42eaba15bf01752b012c92f8e5821536\n| |\\ \\ Merge: sha1-8af6c17c296c36f2b5adf19b02a83918a61088d1 sha1-624532f5d8a5839344ccfc465957a975e1962b6d\n| | | | \"22-wx\"\n| | | | \n* | | | sha1-da122844f28f7a814fb99a4950ff673ea7e3611c\n| | | | Parent: sha1-624532f5d8a5839344ccfc465957a975e1962b6d\n| | | | \"200-y\"\n| | | | \n| * | | sha1-8af6c17c296c36f2b5adf19b02a83918a61088d1\n| | | | Parent: sha1-624532f5d8a5839344ccfc465957a975e1962b6d\n| | | | \"20-x\"\n| | | | \n| | | * sha1-65c99ad8e4db74b2cb3bf00bff806a4d46a220eb\n|/ / / Parent: sha1-624532f5d8a5839344ccfc465957a975e1962b6d\n| \"2000-z\"\n| \n* sha1-624532f5d8a5839344ccfc465957a975e1962b6d\n| Parent: sha1-611d5d868352d4d6ae9b778d6627b81f769cdef5\n| \"2\"\n| \n* sha1-611d5d868352d4d6ae9b778d6627b81f769cdef5\n| Parent: None\n| \"1\"\n"
|
||||
diffRes3 = "* sha1-a859b0936cc42f073c03a27eb820f29a57f025f3\n|\\ Merge: sha1-d996bef5350a0316da794ae1f262dfcdcd4b2e8c sha1-65c99ad8e4db74b2cb3bf00bff806a4d46a220eb\n| | - \"222-wy\"\n| | + \"2222-wz\"\n| | \n* | sha1-d996bef5350a0316da794ae1f262dfcdcd4b2e8c\n|\\ \\ Merge: sha1-da122844f28f7a814fb99a4950ff673ea7e3611c sha1-ba3e907b42eaba15bf01752b012c92f8e5821536\n| | | - \"200-y\"\n| | | + \"222-wy\"\n| | | \n| * | sha1-ba3e907b42eaba15bf01752b012c92f8e5821536\n| |\\ \\ Merge: sha1-8af6c17c296c36f2b5adf19b02a83918a61088d1 sha1-624532f5d8a5839344ccfc465957a975e1962b6d\n| | | | - \"20-x\"\n| | | | + \"22-wx\"\n| | | | \n* | | | sha1-da122844f28f7a814fb99a4950ff673ea7e3611c\n| | | | Parent: sha1-624532f5d8a5839344ccfc465957a975e1962b6d\n| | | | - \"2\"\n| | | | + \"200-y\"\n| | | | \n| * | | sha1-8af6c17c296c36f2b5adf19b02a83918a61088d1\n| | | | Parent: sha1-624532f5d8a5839344ccfc465957a975e1962b6d\n| | | | - \"2\"\n| | | | + \"20-x\"\n| | | | \n| | | * sha1-65c99ad8e4db74b2cb3bf00bff806a4d46a220eb\n|/ / / Parent: sha1-624532f5d8a5839344ccfc465957a975e1962b6d\n| - \"2\"\n| + \"2000-z\"\n| \n* sha1-624532f5d8a5839344ccfc465957a975e1962b6d\n| Parent: sha1-611d5d868352d4d6ae9b778d6627b81f769cdef5\n| - \"1\"\n| + \"2\"\n| \n* sha1-611d5d868352d4d6ae9b778d6627b81f769cdef5\n| Parent: None\n| \n"
|
||||
|
||||
truncRes1 = "* sha1-39d1f600887364b2e4832fe80f6853b0966a9e6c\n| Parent: sha1-185ad8e966cba1a70b7f6cf19cd7bc5a7983c3d2\n| List<String>([\n| \"one\",\n| \"two\",\n| \"three\",\n| \"four\",\n| \"five\",\n| \"six\",\n| \"seven\",\n| ...\n| \n* sha1-185ad8e966cba1a70b7f6cf19cd7bc5a7983c3d2\n| Parent: None\n| \"the first line\"\n"
|
||||
truncRes1 = "* sha1-39d1f600887364b2e4832fe80f6853b0966a9e6c\n| Parent: sha1-185ad8e966cba1a70b7f6cf19cd7bc5a7983c3d2\n| List<String>([\n| \"one\",\n| \"two\",\n| \"three\",\n| \"four\",\n| \"five\",\n| \"six\",\n| \"seven\",\n| ...\n| \n* sha1-185ad8e966cba1a70b7f6cf19cd7bc5a7983c3d2\n| Parent: None\n| \"the first line\"\n"
|
||||
diffTrunc1 = "* sha1-39d1f600887364b2e4832fe80f6853b0966a9e6c\n| Parent: sha1-185ad8e966cba1a70b7f6cf19cd7bc5a7983c3d2\n| - \"the first line\"\n| + List<String>([\n| + \"one\",\n| + \"two\",\n| + \"three\",\n| + \"four\",\n| + \"five\",\n| + \"six\",\n| ...\n| \n* sha1-185ad8e966cba1a70b7f6cf19cd7bc5a7983c3d2\n| Parent: None\n| \n"
|
||||
|
||||
truncRes2 = "* sha1-39d1f600887364b2e4832fe80f6853b0966a9e6c\n| Parent: sha1-185ad8e966cba1a70b7f6cf19cd7bc5a7983c3d2\n| List<String>([\n| \"one\",\n| \"two\",\n| \"three\",\n| \"four\",\n| \"five\",\n| \"six\",\n| \"seven\",\n| \"eight\",\n| \"nine\",\n| \"ten\",\n| \"eleven\",\n| ])\n| \n* sha1-185ad8e966cba1a70b7f6cf19cd7bc5a7983c3d2\n| Parent: None\n| \"the first line\"\n"
|
||||
truncRes2 = "* sha1-39d1f600887364b2e4832fe80f6853b0966a9e6c\n| Parent: sha1-185ad8e966cba1a70b7f6cf19cd7bc5a7983c3d2\n| List<String>([\n| \"one\",\n| \"two\",\n| \"three\",\n| \"four\",\n| \"five\",\n| \"six\",\n| \"seven\",\n| \"eight\",\n| \"nine\",\n| \"ten\",\n| \"eleven\",\n| ])\n| \n* sha1-185ad8e966cba1a70b7f6cf19cd7bc5a7983c3d2\n| Parent: None\n| \"the first line\"\n"
|
||||
diffTrunc2 = "* sha1-39d1f600887364b2e4832fe80f6853b0966a9e6c\n| Parent: sha1-185ad8e966cba1a70b7f6cf19cd7bc5a7983c3d2\n| - \"the first line\"\n| + List<String>([\n| + \"one\",\n| + \"two\",\n| + \"three\",\n| + \"four\",\n| + \"five\",\n| + \"six\",\n| + \"seven\",\n| + \"eight\",\n| + \"nine\",\n| + \"ten\",\n| + \"eleven\",\n| + ])\n| \n* sha1-185ad8e966cba1a70b7f6cf19cd7bc5a7983c3d2\n| Parent: None\n| \n"
|
||||
|
||||
truncRes3 = "* sha1-39d1f600887364b2e4832fe80f6853b0966a9e6c\n| Parent: sha1-185ad8e966cba1a70b7f6cf19cd7bc5a7983c3d2\n| \n* sha1-185ad8e966cba1a70b7f6cf19cd7bc5a7983c3d2\n| Parent: None\n"
|
||||
truncRes3 = "* sha1-39d1f600887364b2e4832fe80f6853b0966a9e6c\n| Parent: sha1-185ad8e966cba1a70b7f6cf19cd7bc5a7983c3d2\n* sha1-185ad8e966cba1a70b7f6cf19cd7bc5a7983c3d2\n| Parent: None\n"
|
||||
diffTrunc3 = "* sha1-39d1f600887364b2e4832fe80f6853b0966a9e6c\n| Parent: sha1-185ad8e966cba1a70b7f6cf19cd7bc5a7983c3d2\n* sha1-185ad8e966cba1a70b7f6cf19cd7bc5a7983c3d2\n| Parent: None\n"
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user