diff --git a/debian/list.go b/debian/list.go index 487ec27a..59277796 100644 --- a/debian/list.go +++ b/debian/list.go @@ -473,3 +473,66 @@ func (l *PackageRefList) Diff(r *PackageRefList, packageCollection *PackageColle return } + +// Merge merges reflist r into current reflist. Merge replaces matching packages (by architecture/name) +// with reference from r. +func (l *PackageRefList) Merge(r *PackageRefList) (result *PackageRefList) { + // pointer to left and right reflists + il, ir := 0, 0 + // length of reflists + ll, lr := l.Len(), r.Len() + + result = &PackageRefList{} + result.Refs = make([][]byte, 0, ll+lr) + + // until we reached end of both lists + for il < ll || ir < lr { + // if we've exhausted left list, pull the rest from the right + if il == ll { + result.Refs = append(result.Refs, r.Refs[ir:]...) + break + } + // if we've exhausted right list, pull the rest from the left + if ir == lr { + result.Refs = append(result.Refs, l.Refs[il:]...) + break + } + + // refs on both sides are present, load them + rl, rr := l.Refs[il], r.Refs[ir] + // compare refs + rel := bytes.Compare(rl, rr) + + if rel == 0 { + // refs are identical, so are packages, advance pointer + result.Refs = append(result.Refs, l.Refs[il]) + il++ + ir++ + } else { + partsL := bytes.Split(rl, []byte(" ")) + archL, nameL := partsL[0][1:], partsL[1] + + partsR := bytes.Split(rr, []byte(" ")) + archR, nameR := partsR[0][1:], partsR[1] + + if bytes.Compare(archL, archR) == 0 && bytes.Compare(nameL, nameR) == 0 { + // override with package from the right + result.Refs = append(result.Refs, r.Refs[ir]) + il++ + ir++ + } else { + // otherwise append smallest of two + if rel < 0 { + result.Refs = append(result.Refs, l.Refs[il]) + il++ + } else { + result.Refs = append(result.Refs, r.Refs[ir]) + ir++ + } + } + + } + } + + return +} diff --git a/debian/list_test.go b/debian/list_test.go index 8a7b63da..62e51791 100644 --- a/debian/list_test.go +++ b/debian/list_test.go @@ -394,3 +394,56 @@ func (s *PackageListSuite) TestDiff(c *C) { c.Check(diffBA[3].Left, IsNil) } + +func (s *PackageListSuite) TestMerge(c *C) { + db, _ := database.OpenDB(c.MkDir()) + coll := NewPackageCollection(db) + + packages := []*Package{ + &Package{Name: "lib", Version: "1.0", Architecture: "i386"}, //0 + &Package{Name: "dpkg", Version: "1.7", Architecture: "i386"}, //1 + &Package{Name: "data", Version: "1.1~bp1", Architecture: "all"}, //2 + &Package{Name: "app", Version: "1.1~bp1", Architecture: "i386"}, //3 + &Package{Name: "app", Version: "1.1~bp2", Architecture: "i386"}, //4 + &Package{Name: "app", Version: "1.1~bp2", Architecture: "amd64"}, //5 + &Package{Name: "dpkg", Version: "1.0", Architecture: "i386"}, //6 + &Package{Name: "xyz", Version: "1.0", Architecture: "sparc"}, //7 + } + + for _, p := range packages { + coll.Update(p) + } + + listA := NewPackageList() + listA.Add(packages[0]) + listA.Add(packages[1]) + listA.Add(packages[2]) + listA.Add(packages[3]) + listA.Add(packages[7]) + + listB := NewPackageList() + listB.Add(packages[0]) + listB.Add(packages[2]) + listB.Add(packages[4]) + listB.Add(packages[5]) + listB.Add(packages[6]) + + reflistA := NewPackageRefListFromPackageList(listA) + reflistB := NewPackageRefListFromPackageList(listB) + + mergeAB := reflistA.Merge(reflistB) + mergeBA := reflistB.Merge(reflistA) + + toStrSlice := func(reflist *PackageRefList) (result []string) { + result = make([]string, reflist.Len()) + for i, r := range reflist.Refs { + result[i] = string(r) + } + return + } + + c.Check(toStrSlice(mergeAB), DeepEquals, + []string{"Pall data 1.1~bp1", "Pamd64 app 1.1~bp2", "Pi386 app 1.1~bp2", "Pi386 dpkg 1.0", "Pi386 lib 1.0", "Psparc xyz 1.0"}) + c.Check(toStrSlice(mergeBA), DeepEquals, + []string{"Pall data 1.1~bp1", "Pamd64 app 1.1~bp2", "Pi386 app 1.1~bp1", "Pi386 dpkg 1.7", "Pi386 lib 1.0", "Psparc xyz 1.0"}) +}