From: George Dunlap Date: Mon, 20 Nov 2017 11:40:22 +0000 (+0000) Subject: xsa: Patch de-duping, first take X-Git-Url: http://xenbits.xensource.com/gitweb?a=commitdiff_plain;h=1ee141b407feccd39f1b5e22224b97f63ac232d2;p=people%2Fgdunlap%2Fxsatool xsa: Patch de-duping, first take First take of patch deduping: Run a separate command which, for each version of an XSA: - Makes a branch with the current recipe - Takes the hash of that tree - Tries to make a branch with a potential "de-dupped" recipe - If that works, compare the hash with the previous hash If the dedupped recipe applies, and the hashes match, change to using the de-dupped recipe. Otherwise, use the current recipe. This will need to be reworked somewhat to support automatic de-dupping during patch creation, but get a working copy in the tree. Signed-off-by: George Dunlap --- diff --git a/git.go b/git.go index 7437770..3e09963 100644 --- a/git.go +++ b/git.go @@ -161,6 +161,8 @@ func (r Repo) VerifyRef(ref string) (ok bool) { return err == nil } +// FIXME Should this use ^{commit} or something like it (to avoid, say, +// getting the ref of tag objects rather refs of the committish objects)? func (r Repo) ParseRef(ref string) (string, error) { out, err := r.gitCmd("rev-parse", "--verify", ref) if err != nil { diff --git a/main.go b/main.go index a7056be..ba59bfc 100644 --- a/main.go +++ b/main.go @@ -138,6 +138,8 @@ func XsaMain(args []string) int { case "set-patches": main = MainSetPatches checkXSARepos = false + case "dedup-patches": + main = MainDedupPatches default: fmt.Printf("Unknown command: %s\n", cmd) return 1 diff --git a/recipe.go b/recipe.go index 87e9198..b29f963 100644 --- a/recipe.go +++ b/recipe.go @@ -57,6 +57,38 @@ func cp(src, dst string) (err error) { 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) @@ -356,6 +388,7 @@ func (r *Recipe) ApplyPatches(prefix string) (err error) { return fmt.Errorf("Appling am %s: %v\n", glob, err) } } + return } diff --git a/xsa.go b/xsa.go index 04ab88f..0e55968 100644 --- a/xsa.go +++ b/xsa.go @@ -347,3 +347,116 @@ func MainSetPatches(xsa *XSAMeta, args []string) (ret int) { 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 +}