return
}
+// Return a "deep copy" clone of r2
+func (r2 *Recipe) Clone() (r *Recipe) {
+ r = &Recipe{}
+ r.XenVersion = r2.XenVersion
+ r.xsa = r2.xsa
+ r.Recipes = make(map[Tree]*TreeRecipe)
+ for t, tr2 := range r2.Recipes {
+ tr := &TreeRecipe{}
+ tr.StableRef = tr2.StableRef
+ tr.Prereqs = append([]int{}, tr2.Prereqs...)
+ tr.Patches = append([]string{}, tr2.Patches...)
+
+ r.Recipes[t] = tr
+ }
+ return r
+}
+
+// Copy only patches bits from r2 into r1, leaving version, stable
+// branch, and prereqs the same. It should already have the
+// appropriate tree recipes.
+func (r *Recipe) CopyPatches(r2 *Recipe) (err error) {
+ for t, tr := range r.Recipes {
+ tr2, prs := r2.Recipes[t]
+ if !prs {
+ err = fmt.Errorf("r2 missing recipe for tree %v!", t)
+ return
+ }
+ tr.Patches = append([]string{}, tr2.Patches...)
+ }
+ return
+}
+
func (r *Recipe) ForEachTree(f func(Tree) error) (err error) {
for t := range r.Recipes {
err = f(t)
return fmt.Errorf("Appling am %s: %v\n", glob, err)
}
}
+
return
}
return 0
}
+
+func dedupPatches(xsa *XSAMeta, from XenVersion, to XenVersion) (err error) {
+ // Make sure both recipes exist before trying anything
+ rTo := xsa.GetRecipe(to)
+ if rTo == nil {
+ err = fmt.Errorf("Internal error: No recipe for version %v\n", to)
+ return
+ }
+
+ rFrom := xsa.GetRecipe(from)
+ if rFrom == nil {
+ err = fmt.Errorf("Internal error: No recipe for version %v\n", from)
+ return
+ }
+
+ // FIXME: make this more generic
+ xr := G.repos.GetRepoNC(TreeXen)
+ if xr == nil {
+ err = fmt.Errorf("Couldn't get tree: %v", err)
+ return
+ }
+
+ // Make a temporary tree with 'to'
+
+ // Use different names between "canonical" and "candidate" branches to make sure
+ // we don't accidentally end up re-using the same tree
+ if err = rTo.ApplyAll("xsa/dedup-canonical"); err != nil {
+ err = fmt.Errorf("Applying base recipe: %v", err)
+ return
+ }
+
+
+ // Get the tree hash
+ _, _, branch := rTo.branchName("xsa/dedup-canonical")
+ canonicalHash, err := xr.TreeRef(branch)
+ if err != nil {
+ err = fmt.Errorf("Getting canonical hash: %v", err)
+ return
+ }
+
+ // Make a temporary tree with 'from'
+ rTemp := rTo.Clone()
+ if err = rTemp.CopyPatches(rFrom); err != nil {
+ err = fmt.Errorf("Copying recipe from version %v: %v",
+ from, err)
+ return
+ }
+
+ if err = rTemp.ApplyAll("xsa/dedup-candidate"); err != nil {
+ fmt.Printf("From patches don't apply, skipping\n")
+ return nil
+ }
+
+ // Get the tree hash
+ _, _, branch = rTo.branchName("xsa/dedup-candidate")
+ candidateHash, err := xr.TreeRef(branch)
+ if err != nil {
+ err = fmt.Errorf("Getting candidate hash: %v", err)
+ return
+ }
+
+ if canonicalHash != candidateHash {
+ fmt.Printf("Hashes don't match (%v, %v), skipping\n",
+ canonicalHash, candidateHash)
+ return
+ }
+
+ fmt.Printf("Hashes match, re-using\n")
+
+ // If they're the same copy the new recipe over
+ if err = rTo.CopyPatches(rTemp); err != nil {
+ err = fmt.Errorf("Copying temporary recipe to permanent one\n")
+ return
+ }
+
+ // FIXME remove the old patches
+
+ xsa.dirty = true
+
+ return
+}
+
+// xsatool NNN dedup-patches
+func MainDedupPatches(xsa *XSAMeta, args []string) (ret int) {
+
+ if !sort.IsSorted(xsa.SupportedVersions) {
+ fmt.Print("Internal error: SupportedVersions not sorted!\n")
+ return 1
+ }
+
+ var from XenVersion
+ for _, to := range xsa.SupportedVersions {
+ fmt.Printf("Checking version %v...", to)
+ if to == XenVersionMaster {
+ from = to
+ continue
+ }
+
+ fmt.Printf("Attempting dedup from %v to %v\n", from, to)
+
+ if err := dedupPatches(xsa, from, to); err != nil {
+ fmt.Printf("Deduping from %v to %v: %v\n",
+ from, to, err)
+ return 1
+ }
+
+ from = to
+ }
+
+ xsa.Save()
+
+ return 0
+}