]> xenbits.xensource.com Git - people/gdunlap/xsatool/commitdiff
xsa: Patch de-duping, first take
authorGeorge Dunlap <george.dunlap@citrix.com>
Mon, 20 Nov 2017 11:40:22 +0000 (11:40 +0000)
committerGeorge Dunlap <george.dunlap@citrix.com>
Mon, 20 Nov 2017 11:40:22 +0000 (11:40 +0000)
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 <george.dunlap@citrix.com>
git.go
main.go
recipe.go
xsa.go

diff --git a/git.go b/git.go
index 74377707e1d800dc520bd7ede86d6f981c880567..3e0996376ce77dbada1c5746588cb78c60673898 100644 (file)
--- 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 a7056be8be1398becfc8db77e70342b956c15582..ba59bfcdd224371280137d124988ad658490975a 100644 (file)
--- 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
index 87e91981dba681890523ab8aa614b8a6c159a0de..b29f9631a71228356cd74c05ce06580de0a764fb 100644 (file)
--- 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 04ab88f3cc6b7d15afb896cc4b7b7ce638eb7c0e..0e55968118f91acddf38fe8d586ad8bea87a5e0b 100644 (file)
--- 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
+}