Codebase list golang-github-vbatts-go-mtree / d8d43cd
compare: add CompareSame() I have a use case where I'd like to know the files that are the same in the tree, as well as the differences. I could do this with a separate walk and excluding the paths that were different, but since mtree is already doing all of this for me, it makes sense to include it here. I've added a new function so that the behavior stays the same for existing users of Compare(), since I assume mostly this will be slower given that most files stay the same. I'd be happy to merge it into one, though. Signed-off-by: Tycho Andersen <tycho@tycho.ws> Tycho Andersen 5 years ago
1 changed file(s) with 139 addition(s) and 114 deletion(s). Raw diff Collapse all Expand all
2828 // have different values (or have not been set in one of the
2929 // manifests).
3030 Modified DifferenceType = "modified"
31
32 // Same represents the case where two files are the same. These are
33 // only generated from CompareSame().
34 Same DifferenceType = "same"
3135
3236 // ErrorDifference represents an attempted update to the values of
3337 // a keyword that failed
304308 name: name,
305309 old: diff.Old.Value(),
306310 new: diff.New.Value(),
311 })
312 }
313 }
314 }
315
316 return results, nil
317 }
318
319 // compare is the actual workhorse for Compare() and CompareSame()
320 func compare(oldDh, newDh *DirectoryHierarchy, keys []Keyword, same bool) ([]InodeDelta, error) {
321 // Represents the new and old states for an entry.
322 type stateT struct {
323 Old *Entry
324 New *Entry
325 }
326
327 // To deal with different orderings of the entries, use a path-keyed
328 // map to make sure we don't start comparing unrelated entries.
329 diffs := map[string]*stateT{}
330
331 // First, iterate over the old hierarchy. If nil, pretend it's empty.
332 if oldDh != nil {
333 for _, e := range oldDh.Entries {
334 if e.Type == RelativeType || e.Type == FullType {
335 path, err := e.Path()
336 if err != nil {
337 return nil, err
338 }
339 //fmt.Printf("new: %q\n", path)
340
341 // Cannot take &kv because it's the iterator.
342 cEntry := new(Entry)
343 *cEntry = e
344
345 _, ok := diffs[path]
346 if !ok {
347 diffs[path] = &stateT{}
348 }
349 diffs[path].Old = cEntry
350 }
351 }
352 }
353
354 // Then, iterate over the new hierarchy. If nil, pretend it's empty.
355 if newDh != nil {
356 for _, e := range newDh.Entries {
357 if e.Type == RelativeType || e.Type == FullType {
358 path, err := e.Path()
359 if err != nil {
360 return nil, err
361 }
362 //fmt.Printf("old: %q\n", path)
363
364 // Cannot take &kv because it's the iterator.
365 cEntry := new(Entry)
366 *cEntry = e
367
368 _, ok := diffs[path]
369 if !ok {
370 diffs[path] = &stateT{}
371 }
372 diffs[path].New = cEntry
373 }
374 }
375 }
376
377 // Now we compute the diff.
378 var results []InodeDelta
379 for path, diff := range diffs {
380 // Invalid
381 if diff.Old == nil && diff.New == nil {
382 return nil, fmt.Errorf("invalid state: both old and new are nil: path=%s", path)
383 }
384
385 switch {
386 // Missing
387 case diff.New == nil:
388 results = append(results, InodeDelta{
389 diff: Missing,
390 path: path,
391 old: *diff.Old,
392 })
393
394 // Extra
395 case diff.Old == nil:
396 results = append(results, InodeDelta{
397 diff: Extra,
398 path: path,
399 new: *diff.New,
400 })
401
402 // Modified
403 default:
404 changed, err := compareEntry(*diff.Old, *diff.New)
405 if err != nil {
406 return nil, fmt.Errorf("comparison failed %s: %s", path, err)
407 }
408
409 // Now remove "changed" entries that don't match the keys.
410 if keys != nil {
411 var filterChanged []KeyDelta
412 for _, keyDiff := range changed {
413 if InKeywordSlice(keyDiff.name.Prefix(), keys) {
414 filterChanged = append(filterChanged, keyDiff)
415 }
416 }
417 changed = filterChanged
418 }
419
420 // Check if there were any actual changes.
421 if len(changed) > 0 {
422 results = append(results, InodeDelta{
423 diff: Modified,
424 path: path,
425 old: *diff.Old,
426 new: *diff.New,
427 keys: changed,
428 })
429 } else if same {
430 // this means that nothing changed, i.e. that
431 // the files are the same.
432 results = append(results, InodeDelta{
433 diff: Same,
434 path: path,
435 old: *diff.Old,
436 new: *diff.New,
437 keys: changed,
307438 })
308439 }
309440 }
331462 // NB: The order of the parameters matters (old, new) because Extra and
332463 // Missing are considered as different discrepancy types.
333464 func Compare(oldDh, newDh *DirectoryHierarchy, keys []Keyword) ([]InodeDelta, error) {
334 // Represents the new and old states for an entry.
335 type stateT struct {
336 Old *Entry
337 New *Entry
338 }
339
340 // To deal with different orderings of the entries, use a path-keyed
341 // map to make sure we don't start comparing unrelated entries.
342 diffs := map[string]*stateT{}
343
344 // First, iterate over the old hierarchy. If nil, pretend it's empty.
345 if oldDh != nil {
346 for _, e := range oldDh.Entries {
347 if e.Type == RelativeType || e.Type == FullType {
348 path, err := e.Path()
349 if err != nil {
350 return nil, err
351 }
352 //fmt.Printf("new: %q\n", path)
353
354 // Cannot take &kv because it's the iterator.
355 cEntry := new(Entry)
356 *cEntry = e
357
358 _, ok := diffs[path]
359 if !ok {
360 diffs[path] = &stateT{}
361 }
362 diffs[path].Old = cEntry
363 }
364 }
365 }
366
367 // Then, iterate over the new hierarchy. If nil, pretend it's empty.
368 if newDh != nil {
369 for _, e := range newDh.Entries {
370 if e.Type == RelativeType || e.Type == FullType {
371 path, err := e.Path()
372 if err != nil {
373 return nil, err
374 }
375 //fmt.Printf("old: %q\n", path)
376
377 // Cannot take &kv because it's the iterator.
378 cEntry := new(Entry)
379 *cEntry = e
380
381 _, ok := diffs[path]
382 if !ok {
383 diffs[path] = &stateT{}
384 }
385 diffs[path].New = cEntry
386 }
387 }
388 }
389
390 // Now we compute the diff.
391 var results []InodeDelta
392 for path, diff := range diffs {
393 // Invalid
394 if diff.Old == nil && diff.New == nil {
395 return nil, fmt.Errorf("invalid state: both old and new are nil: path=%s", path)
396 }
397
398 switch {
399 // Missing
400 case diff.New == nil:
401 results = append(results, InodeDelta{
402 diff: Missing,
403 path: path,
404 old: *diff.Old,
405 })
406
407 // Extra
408 case diff.Old == nil:
409 results = append(results, InodeDelta{
410 diff: Extra,
411 path: path,
412 new: *diff.New,
413 })
414
415 // Modified
416 default:
417 changed, err := compareEntry(*diff.Old, *diff.New)
418 if err != nil {
419 return nil, fmt.Errorf("comparison failed %s: %s", path, err)
420 }
421
422 // Now remove "changed" entries that don't match the keys.
423 if keys != nil {
424 var filterChanged []KeyDelta
425 for _, keyDiff := range changed {
426 if InKeywordSlice(keyDiff.name.Prefix(), keys) {
427 filterChanged = append(filterChanged, keyDiff)
428 }
429 }
430 changed = filterChanged
431 }
432
433 // Check if there were any actual changes.
434 if len(changed) > 0 {
435 results = append(results, InodeDelta{
436 diff: Modified,
437 path: path,
438 old: *diff.Old,
439 new: *diff.New,
440 keys: changed,
441 })
442 }
443 }
444 }
445
446 return results, nil
447 }
465 return compare(oldDh, newDh, keys, false)
466 }
467
468 // CompareSame is the same as Compare, except it also includes the entries
469 // that are the same with a Same DifferenceType.
470 func CompareSame(oldDh, newDh *DirectoryHierarchy, keys []Keyword) ([]InodeDelta, error) {
471 return compare(oldDh, newDh, keys, true)
472 }