emacs-elpa-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[elpa] elpa-admin 97ebbd5: * elpa-admin.el (elpaa--prune-old-tarballs):


From: Stefan Monnier
Subject: [elpa] elpa-admin 97ebbd5: * elpa-admin.el (elpaa--prune-old-tarballs): "(Re)move" the non-kept files
Date: Sun, 17 Jan 2021 17:30:07 -0500 (EST)

branch: elpa-admin
commit 97ebbd584febdae2de2313bf81ffecee322ec5df
Author: Stefan Monnier <monnier@iro.umontreal.ca>
Commit: Stefan Monnier <monnier@iro.umontreal.ca>

    * elpa-admin.el (elpaa--prune-old-tarballs): "(Re)move" the non-kept files
    
    Remove `vers` argument.
    
    (elpaa--keep-old): Tune further and add comments.
---
 elpa-admin.el | 162 ++++++++++++++++++++++++++++++++++++++--------------------
 1 file changed, 108 insertions(+), 54 deletions(-)

diff --git a/elpa-admin.el b/elpa-admin.el
index 2fd9042..90236d9 100644
--- a/elpa-admin.el
+++ b/elpa-admin.el
@@ -320,70 +320,124 @@ Do it without leaving the current branch."
 
 (defconst elpaa--keep-max 20)
 
-(defun elpaa--keep-old (vers oldtarballs n)
-  (cl-assert (and (integerp n) (> n 0)))
-  (cl-assert (not (assoc vers oldtarballs)))
-  (if (not (nthcdr n oldtarballs))
-      ;; We can keep them all.
-      oldtarballs
-    (let ((buckets ())
-          (buckets2 ())
-          (kept ()))
-      (dolist (oldtarball oldtarballs)
-        (let* ((tvers (car oldtarball))
-               (common-prefix (try-completion "" (list vers tvers)))
-               (len (length (if (stringp common-prefix) common-prefix vers))))
-          (push oldtarball (alist-get len buckets))
-          (push oldtarball
-                (alist-get (substring tvers 0 (min (length tvers) (1+ len)))
-                           buckets2 nil nil #'equal))))
-      (when (<= (length buckets2) n)
-        (setq buckets buckets2))
-      (while
-          (let ((bucket-size (/ n (length buckets)))
-                repeat)
-            (dolist (bucket buckets)
-              (when (<= (1- (length bucket)) bucket-size)
-                (setq kept (nconc (cdr bucket) kept))
-                (setq n (- n (1- (length bucket))))
-                (setq buckets (delq bucket buckets))
-                (setq repeat t)))
-            repeat))
-      (let ((bucket-size (/ n (length buckets))))
-        (dolist (bucket buckets)
-          (setq bucket (sort (cdr bucket)
+(defun elpaa--keep-old (oldtarballs n)
+  "Select N tarballs to keep among those in OLDTARBALLS."
+  ;; It's not clear which ones to select.  My main goal here was to try and 
keep
+  ;; more of the last releases than of the old releases, and also to favor the
+  ;; last release in a given line, so for example for Emacs releases, we might
+  ;; prefer to keep: 24.5 24.4 24.3 24.2 24.1 23.4 22.3 21.3 20.4
+  ;; rather than   : 24.3 24.1 23.3 23.2 23.1 21.1 20.3 20.2 20.1
+  ;; Also, we want this to work for "any" release numbering scheme, including
+  ;; the pseudo release numbers YYYYMMMDD used for snapshots.
+  ;;
+  ;; I'm not very satisfied with the code below:
+  ;; - It was tested mostly on sets where N is significantly smaller than the
+  ;;   input set size, whereas in practice it'll probably mostly be used with
+  ;;   N being 20 and OLDTARBALLS containing 21 elements, so... we'll see.
+  ;; - I don't think this algorithm enjoys any kind of "stability" property
+  ;;   such as a guarantee that if you first select 50 elements and then you
+  ;;   select 20 elements out of that you get the same result as if you
+  ;;   directly selected 20 elements from the original set.
+  (cl-assert (natnump n))
+  (cond
+   ((< n 1) nil)
+   ((not (nthcdr n oldtarballs)) oldtarballs) ;; We can keep them all.
+   (t
+    (setq oldtarballs (nreverse
+                       (sort (copy-sequence oldtarballs)
                              (lambda (t1 t2)
-                               (version<= (car t1) (car t2)))))
-          (let ((last (last bucket)))
-            (push (car last) kept)
-            (cond
-             ;; If there's only room for 2 elements, keep the first and
-             ;; the last.
-             ((and (cdr bucket) (= bucket-size 2))
-              (push (car bucket) kept))
-             ((> bucket-size 2)
-              (setq kept (nconc (elpaa--keep-old (caar last)
-                                                 (butlast bucket)
-                                                 (1- bucket-size))
-                                kept)))))))
-      kept)))
-
-(defun elpaa--prune-old-tarballs (vers tarball oldtarballs destdir)
+                               (version<= (car t1) (car t2))))))
+    (cond
+     ((< n 2)
+      ;; If we have to pick one, keep the latest.
+      (list (car oldtarballs)))
+     ((< n 3)
+      ;; If there's only room for 2 elements, keep the first and the last.
+      (cons (car oldtarballs) (last oldtarballs)))
+     (t
+      ;; The general idea here is to split the input into buckets
+      ;; which represent a kind of "logarithm of distance to the latest"
+      ;; and then we pick the same number of elements from each bucket
+      ;; (the log(distance) is actually taken to be the length of the common
+      ;; prefix between the two versions).
+      (let* ((latest (pop oldtarballs))
+             (vers (car latest))
+             (buckets ())
+             (kept (list latest)))
+        (dolist (oldtarball oldtarballs)
+          (let* ((tvers (car oldtarball))
+                 (common-prefix (try-completion "" (list vers tvers))))
+            (push oldtarball (alist-get (length common-prefix) buckets))))
+
+        ;; Make sure there are fewer buckets than target elements.
+        (while (> (length buckets) (- n (length kept)))
+          ;; (message "Too many buckets (%s/%s): Merging...."
+          ;;          (length buckets) (- n (length kept)))
+          (let ((target-size (1+ (/ (length oldtarballs) n)))
+                (new t))
+            (dolist (bucket (prog1 buckets (setq buckets nil)))
+              (if (or new (> (length bucket) target-size))
+                  (progn (push bucket buckets) (setq new nil))
+                (setq new t)
+                (setf (cdar buckets) (nconc (cdr bucket) (cdar buckets)))))))
+
+        ;; "Spread" some buckets: for a two-level release numbering scheme,
+        ;; we might end up with 2 buckets: one with the latest minor releases
+        ;; and the other with everything else.  When we recurse on the
+        ;; "everything else", the same will tend to happen again, and overall
+        ;; this tends to select too many "recent minor releases" in favor of
+        ;; keeping older major releases.
+        ;; We try to compensate here by splitting "furtherest" buckets into
+        ;; smaller buckets based on the first char that differs between their
+        ;; release number.
+        (setq buckets (sort buckets (lambda (b1 b2) (<= (car b1) (car b2)))))
+        (while
+            (let* ((bucket (car buckets))
+                   (len (length (try-completion "" bucket)))
+                   (newbuckets ()))
+              (dolist (oldtarball (cdr bucket))
+                (let ((tvers (car oldtarball)))
+                  (push oldtarball
+                        (alist-get (substring tvers 0
+                                              (min (length tvers) (1+ len)))
+                                   newbuckets nil nil #'equal))))
+              (when (< (+ (length newbuckets) (length (cdr buckets)))
+                       (- n (length kept)))
+                ;; (message "Spreading one bucket")
+                (setq buckets (nconc (cdr buckets)
+                                     (mapcar (lambda (b)
+                                               (cons (length (car b)) (cdr b)))
+                                             newbuckets)))
+                t)))
+        ;; Finally, evenly select elements from every bucket.
+        (setq buckets (sort buckets (lambda (b1 b2) (<= (length b1) (length 
b2)))))
+        (while buckets
+          (let ((bucket-size (/ (- n (length kept)) (length buckets)))
+                (bucket (cdr (pop buckets))))
+            (setq kept (nconc (elpaa--keep-old bucket
+                                               bucket-size)
+                              kept))))
+        kept))))))
+
+(defun elpaa--prune-old-tarballs (tarball oldtarballs destdir)
   ;; Make sure we don't count ourselves among the "old" tarballs.
   (let ((self (rassoc (file-name-nondirectory tarball) oldtarballs)))
     (when self
       (setq oldtarballs (delq self oldtarballs))))
   (when (nthcdr elpaa--keep-max oldtarballs)
-    (let* ((keep (elpaa--keep-old vers oldtarballs elpaa--keep-max))
+    (let* ((keep (elpaa--keep-old oldtarballs elpaa--keep-max))
            (skeep (nreverse (sort keep
-                                  (lambda (t1 t2)
-                                    (version<= (car t1) (car t2)))))))
+                                  (lambda (t1 t2) (version<= (car t1) (car 
t2)))))))
       (message "Keeping: %s" (mapcar #'cdr skeep))
       (dolist (oldtarball oldtarballs)
         (unless (memq oldtarball keep)
           (cl-assert (not (equal (cdr oldtarball)
                                  (file-name-nondirectory tarball))))
-          (message "Deleting %s" (cdr oldtarball))))
+          (message "Deleting %s" (cdr oldtarball))
+          (let ((oldd (expand-file-name "old" destdir)))
+            (make-directory oldd t)
+            (rename-file (expand-file-name (cdr oldtarball) destdir)
+                         (expand-file-name (cdr oldtarball) oldd)))))
       (setq oldtarballs skeep)))
   (dolist (oldtarball oldtarballs)
     ;; Compress oldtarballs.
@@ -478,7 +532,7 @@ Return non-nil if a new tarball was created."
               (when (file-symlink-p link) (delete-file link))
               (make-symbolic-link (file-name-nondirectory tarball) link))
             (setq oldtarballs
-                  (elpaa--prune-old-tarballs vers tarball oldtarballs destdir))
+                  (elpaa--prune-old-tarballs tarball oldtarballs destdir))
             (let* ((default-directory (expand-file-name destdir)))
               ;; Apparently this also creates the <pkg>-readme.txt file.
               (elpaa--html-make-pkg pkgdesc pkg-spec
@@ -1040,7 +1094,7 @@ Rename DIR/ to PKG-VERS/, and return the descriptor."
       (unless (< (length files) (if (zerop (length latest)) 1 2))
         (insert (format "<h2>Old versions</h2><table>\n"))
         (dolist (file
-                 (sort files (lambda (f1 f2) (version< (car f2) (car f1)))))
+                 (sort files (lambda (f1 f2) (version<= (car f2) (car f1)))))
           (unless (equal (pop file) latest)
             (let ((attrs (file-attributes file)))
               (insert (format "<tr><td><a 
href=%S>%s</a></td><td>%s</td><td>%s</td>\n"



reply via email to

[Prev in Thread] Current Thread [Next in Thread]