Add a super quick indeterminite progress meter to noms-merge (#2488)

This commit is contained in:
Aaron Boodman
2016-09-01 10:24:30 -07:00
committed by GitHub
parent 01bdeab025
commit 42fd80be2e
5 changed files with 46 additions and 19 deletions
+24 -13
View File
@@ -40,7 +40,7 @@ func newMergeConflict(format string, args ...interface{}) *ErrMergeConflict {
//
// All other modifications are allowed.
// ThreeWay() works on types.Map, types.Set, and types.Struct.
func ThreeWay(a, b, parent types.Value, vwr types.ValueReadWriter) (merged types.Value, err error) {
func ThreeWay(a, b, parent types.Value, vwr types.ValueReadWriter, progress chan struct{}) (merged types.Value, err error) {
if a == nil && b == nil {
return parent, nil
} else if a == nil {
@@ -51,7 +51,7 @@ func ThreeWay(a, b, parent types.Value, vwr types.ValueReadWriter) (merged types
return parent, newMergeConflict("Cannot merge %s with %s.", a.Type().Describe(), b.Type().Describe())
}
return threeWayMerge(a, b, parent, vwr)
return threeWayMerge(a, b, parent, vwr, progress)
}
// a and b cannot be merged if they are of different NomsKind, or if at least one of the two is nil, or if either is a Noms primitive.
@@ -63,7 +63,15 @@ func unmergeable(a, b types.Value) bool {
return true
}
func threeWayMerge(a, b, parent types.Value, vwr types.ValueReadWriter) (merged types.Value, err error) {
func updateProgress(progress chan struct{}) {
// TODO: Eventually we'll want more information than a single bit :).
if progress != nil {
progress <- struct{}{}
}
}
func threeWayMerge(a, b, parent types.Value, vwr types.ValueReadWriter, progress chan struct{}) (merged types.Value, err error) {
defer updateProgress(progress)
d.PanicIfTrue(a == nil || b == nil, "Merge candidates cannont be nil: a = %v, b = %v", a, b)
newTypeConflict := func() *ErrMergeConflict {
pDescription := "<nil>"
@@ -81,12 +89,12 @@ func threeWayMerge(a, b, parent types.Value, vwr types.ValueReadWriter) (merged
case types.MapKind:
if aMap, bMap, pMap, ok := mapAssert(a, b, parent); ok {
return threeWayMapMerge(aMap, bMap, pMap, vwr)
return threeWayMapMerge(aMap, bMap, pMap, vwr, progress)
}
case types.RefKind:
if aValue, bValue, pValue, ok := refAssert(a, b, parent, vwr); ok {
merged, err := threeWayMerge(aValue, bValue, pValue, vwr)
merged, err := threeWayMerge(aValue, bValue, pValue, vwr, progress)
if err != nil {
return parent, err
}
@@ -95,12 +103,12 @@ func threeWayMerge(a, b, parent types.Value, vwr types.ValueReadWriter) (merged
case types.SetKind:
if aSet, bSet, pSet, ok := setAssert(a, b, parent); ok {
return threeWaySetMerge(aSet, bSet, pSet, vwr)
return threeWaySetMerge(aSet, bSet, pSet, vwr, progress)
}
case types.StructKind:
if aStruct, bStruct, pStruct, ok := structAssert(a, b, parent); ok {
return threeWayStructMerge(aStruct, bStruct, pStruct, vwr)
return threeWayStructMerge(aStruct, bStruct, pStruct, vwr, progress)
}
default:
@@ -110,7 +118,7 @@ func threeWayMerge(a, b, parent types.Value, vwr types.ValueReadWriter) (merged
return parent, newTypeConflict()
}
func threeWayMapMerge(a, b, parent types.Map, vwr types.ValueReadWriter) (merged types.Value, err error) {
func threeWayMapMerge(a, b, parent types.Map, vwr types.ValueReadWriter, progress chan struct{}) (merged types.Value, err error) {
aDiff := func(change chan<- types.ValueChanged, stop <-chan struct{}) {
a.Diff(parent, change, stop)
}
@@ -118,6 +126,7 @@ func threeWayMapMerge(a, b, parent types.Map, vwr types.ValueReadWriter) (merged
b.Diff(parent, change, stop)
}
apply := func(target types.Value, change types.ValueChanged, newVal types.Value) types.Value {
defer updateProgress(progress)
switch change.ChangeType {
case types.DiffChangeAdded, types.DiffChangeModified:
return target.(types.Map).Set(change.V, newVal)
@@ -127,10 +136,10 @@ func threeWayMapMerge(a, b, parent types.Map, vwr types.ValueReadWriter) (merged
panic("Not Reached")
}
}
return threeWayOrderedSequenceMerge(parent, aDiff, bDiff, a.Get, b.Get, parent.Get, apply, vwr)
return threeWayOrderedSequenceMerge(parent, aDiff, bDiff, a.Get, b.Get, parent.Get, apply, vwr, progress)
}
func threeWaySetMerge(a, b, parent types.Set, vwr types.ValueReadWriter) (merged types.Value, err error) {
func threeWaySetMerge(a, b, parent types.Set, vwr types.ValueReadWriter, progress chan struct{}) (merged types.Value, err error) {
aDiff := func(change chan<- types.ValueChanged, stop <-chan struct{}) {
a.Diff(parent, change, stop)
}
@@ -141,6 +150,7 @@ func threeWaySetMerge(a, b, parent types.Set, vwr types.ValueReadWriter) (merged
return v
}
apply := func(target types.Value, change types.ValueChanged, ignored types.Value) types.Value {
defer updateProgress(progress)
switch change.ChangeType {
case types.DiffChangeAdded, types.DiffChangeModified:
return target.(types.Set).Insert(change.V)
@@ -150,10 +160,10 @@ func threeWaySetMerge(a, b, parent types.Set, vwr types.ValueReadWriter) (merged
panic("Not Reached")
}
}
return threeWayOrderedSequenceMerge(parent, aDiff, bDiff, getSelf, getSelf, getSelf, apply, vwr)
return threeWayOrderedSequenceMerge(parent, aDiff, bDiff, getSelf, getSelf, getSelf, apply, vwr, progress)
}
func threeWayStructMerge(a, b, parent types.Struct, vwr types.ValueReadWriter) (merged types.Value, err error) {
func threeWayStructMerge(a, b, parent types.Struct, vwr types.ValueReadWriter, progress chan struct{}) (merged types.Value, err error) {
aDiff := func(change chan<- types.ValueChanged, stop <-chan struct{}) {
a.Diff(parent, change, stop)
}
@@ -172,6 +182,7 @@ func threeWayStructMerge(a, b, parent types.Struct, vwr types.ValueReadWriter) (
}
apply := func(target types.Value, change types.ValueChanged, newVal types.Value) types.Value {
defer updateProgress(progress)
// Right now, this always iterates over all fields to create a new Struct, because there's no API for adding/removing a field from an existing struct type.
if f, ok := change.V.(types.String); ok {
field := string(f)
@@ -189,7 +200,7 @@ func threeWayStructMerge(a, b, parent types.Struct, vwr types.ValueReadWriter) (
}
panic(fmt.Errorf("Bad key type in diff: %s", change.V.Type().Describe()))
}
return threeWayOrderedSequenceMerge(parent, aDiff, bDiff, makeGetFunc(a), makeGetFunc(b), makeGetFunc(parent), apply, vwr)
return threeWayOrderedSequenceMerge(parent, aDiff, bDiff, makeGetFunc(a), makeGetFunc(b), makeGetFunc(parent), apply, vwr, progress)
}
func listAssert(a, b, parent types.Value) (aList, bList, pList types.List, ok bool) {
+2 -2
View File
@@ -15,7 +15,7 @@ type diffFunc func(chan<- types.ValueChanged, <-chan struct{})
type applyFunc func(types.Value, types.ValueChanged, types.Value) types.Value
type getFunc func(types.Value) types.Value
func threeWayOrderedSequenceMerge(parent types.Value, aDiff, bDiff diffFunc, aGet, bGet, pGet getFunc, apply applyFunc, vwr types.ValueReadWriter) (merged types.Value, err error) {
func threeWayOrderedSequenceMerge(parent types.Value, aDiff, bDiff diffFunc, aGet, bGet, pGet getFunc, apply applyFunc, vwr types.ValueReadWriter, progress chan struct{}) (merged types.Value, err error) {
aChangeChan, bChangeChan := make(chan types.ValueChanged), make(chan types.ValueChanged)
aStopChan, bStopChan := make(chan struct{}, 1), make(chan struct{}, 1)
@@ -75,7 +75,7 @@ func threeWayOrderedSequenceMerge(parent types.Value, aDiff, bDiff diffFunc, aGe
return parent, newMergeConflict("Conflict:\n%s = %s\nvs\n%s = %s", describeChange(aChange), types.EncodedValue(aValue), describeChange(bChange), types.EncodedValue(bValue))
}
// TODO: Add concurrency.
mergedValue, err := threeWayMerge(aValue, bValue, pGet(aChange.V), vwr)
mergedValue, err := threeWayMerge(aValue, bValue, pGet(aChange.V), vwr, progress)
if err != nil {
return parent, err
}
+2 -2
View File
@@ -20,7 +20,7 @@ type ThreeWayMergeSuite struct {
}
func (s *ThreeWayMergeSuite) tryThreeWayMerge(a, b, p, exp seq, vs types.ValueReadWriter) {
merged, err := ThreeWay(s.create(a), s.create(b), s.create(p), vs)
merged, err := ThreeWay(s.create(a), s.create(b), s.create(p), vs, nil)
if s.NoError(err) {
expected := s.create(exp)
s.True(expected.Equals(merged), "%s != %s", types.EncodedValue(expected), types.EncodedValue(merged))
@@ -28,7 +28,7 @@ func (s *ThreeWayMergeSuite) tryThreeWayMerge(a, b, p, exp seq, vs types.ValueRe
}
func (s *ThreeWayMergeSuite) tryThreeWayConflict(a, b, p types.Value, contained string) {
m, err := ThreeWay(a, b, p, nil)
m, err := ThreeWay(a, b, p, nil, nil)
if s.Error(err) {
s.Contains(err.Error(), contained)
return
+17 -1
View File
@@ -14,6 +14,7 @@ import (
"github.com/attic-labs/noms/go/merge"
"github.com/attic-labs/noms/go/spec"
"github.com/attic-labs/noms/go/types"
"github.com/attic-labs/noms/go/util/status"
flag "github.com/juju/gnuflag"
)
@@ -29,6 +30,7 @@ func main() {
func nomsMerge() error {
outDSStr := flag.String("out-ds-name", "", "output dataset to write to - if empty, defaults to <right-ds-name>")
parentStr := flag.String("parent", "", "common ancestor of <left-ds-name> and <right-ds-name> (currently required; soon to be optional)")
quiet := flag.Bool("quiet", false, "silence progress output")
flag.Usage = usage
return d.Unwrap(d.Try(func() {
@@ -66,11 +68,25 @@ func nomsMerge() error {
outDS = makeDS(*outDSStr)
}
merged, err := merge.ThreeWay(left, right, parent, db)
pc := make(chan struct{}, 128)
go func() {
count := 0
for _ = range pc {
if !*quiet {
count++
status.Printf("Applied %d changes...", count)
}
}
}()
merged, err := merge.ThreeWay(left, right, parent, db, pc)
d.PanicIfError(err)
_, err = outDS.Commit(merged, dataset.CommitOptions{Parents: types.NewSet(leftDS.HeadRef(), rightDS.HeadRef())})
d.PanicIfError(err)
if !*quiet {
status.Printf("Done")
status.Done()
}
}))
}
+1 -1
View File
@@ -64,7 +64,7 @@ func (s *testSuite) TestWin() {
})
var mainErr error
stdout, stderr := s.Run(func() { mainErr = nomsMerge() }, []string{"--parent=" + p, s.LdbDir, l, r})
stdout, stderr := s.Run(func() { mainErr = nomsMerge() }, []string{"--quiet=true", "--parent=" + p, s.LdbDir, l, r})
if s.NoError(mainErr, "%s", mainErr) {
s.Equal("", stdout)
s.Equal("", stderr)