From 6a61ff58c159a8b46408b99825c3c2ff420a20e0 Mon Sep 17 00:00:00 2001 From: Kyle Meyer Date: Fri, 5 Jun 2020 19:46:43 -0400 Subject: Extract reusable git-am logic from -b4 The end goal is to turn piem-am into a command that can work with patches extracted from the current buffer. Pull out the logic that can be reused for this. --- piem-b4.el | 125 +----------------------------------------------------------- piem.el | 126 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 128 insertions(+), 123 deletions(-) diff --git a/piem-b4.el b/piem-b4.el index f8908f7..a5530e3 100644 --- a/piem-b4.el +++ b/piem-b4.el @@ -30,7 +30,6 @@ (require 'mail-extr) (require 'message) (require 'piem) -(require 'subr-x) (require 'transient) @@ -45,108 +44,9 @@ "Which b4 executable to use." :type 'string) - - -(defcustom piem-b4-default-branch-function - #'piem-b4-name-branch-who-what-v - "Function that generates the default branch on completion. - -The function is called with one argument, a plist that contains -the following information about the patch series: - - :date - :from - :subject - The date, sender, and subject of the cover letter, if any, or of - the first patch otherwise. - :base-commit - The reported base commit of the patch, if any." - :type 'boolean) - ;;;; Internals -(defun piem-b4--series-info (cover patches) - "Collect information for a patch series. -COVER is an mbox with the cover letter, and PATCHES is an -am-ready mbox. If the series does not have a cover letter (e.g., -a one-patch series), COVER may be nil." - (with-temp-buffer - (insert-file-contents (or cover patches)) - (let ((info (save-restriction - (message-narrow-to-headers) - (list :date (message-fetch-field "date") - :from (message-fetch-field "from") - :subject (message-fetch-field "subject"))))) - (when (re-search-forward (rx line-start "base-commit: " - (group (>= 40 hex-digit)) - line-end) - nil t) - (plist-put info :base-commit (match-string 1))) - info))) - -(defun piem-b4--shorten-subject (subject) - (let ((words - (mapcar #'downcase - (split-string - (thread-last subject - (replace-regexp-in-string - (rx (any "'\"")) - "") - (replace-regexp-in-string - (rx string-start (zero-or-more space) "[" - (zero-or-more (not (any "]" "\n"))) - "PATCH" - (zero-or-more (not (any "]" "\n"))) - "]" (one-or-more space)) - "")) - "\\W+" t))) - (ignore-these (list "a" "an" "the")) - (num-words 0) - (num-chars 0) - kept) - (catch 'stop - (dolist (word words) - (when (not (member word ignore-these)) - (cl-incf num-words) - (cl-incf num-chars (length word)) - (push word kept) - (when (or (> num-words 5) - (> num-chars 20)) - (throw 'stop nil))))) - (mapconcat #'identity (reverse kept) "-"))) - -(defun piem-b4-name-branch-who-what-v (info) - "Construct a branch name like \"km/b4-short-subj__v3\". - -In the above example, \"k\" and \"m\" are the first letters of -the first and second \"words\" in the \"From:\" field. -\"b4-short-subj\" is a stripped down, truncated variant of -\"Subject:\". And \"v3\" is the version of the patch series, as -indicated in the subject. - -INFO is a plist with properties documented -in `piem-b4-default-branch-function'." - (when-let ((sender (car (mail-extract-address-components - (plist-get info :from)))) - (subject (plist-get info :subject))) - (let* ((subnames (split-string sender)) - (initials (mapconcat - (lambda (subname) - (and subname - (downcase (substring subname nil 1)))) - (list (car subnames) (cadr subnames)) - "")) - (version (and (string-match - (rx "[" (zero-or-more (not (any "[" "\n"))) - "PATCH " - (zero-or-one (group "v" (one-or-more digit)))) - subject) - (match-string 1 subject)))) - (concat initials "/" - (piem-b4--shorten-subject subject) - (and version (concat "__" version)))))) - ;; In many cases, we don't really need b4 to download the mbox for us, ;; as we already have our own mbox to URL mapping. Perhaps we should ;; default to using that, but it should still be an option to use b4 @@ -221,30 +121,9 @@ in `piem-b4-default-branch-function'." (read-directory-name "Git repository: "))) (`(,cover ,mbox-file) (piem-b4--get-am-files mid coderepo args)) - (info (piem-b4--series-info cover mbox-file)) + (info (piem-series-info cover mbox-file)) (default-directory coderepo)) - ;; TODO: Optionally do more through Magit. - (let ((new-branch (read-string - "New branch (empty for detached): " - (funcall piem-b4-default-branch-function info))) - (base (completing-read - "Base commit: " - (let ((cands (and piem-use-magit - (fboundp 'magit-list-local-branch-names) - (magit-list-local-branch-names))) - (base (plist-get info :base-commit))) - (if base (cons base cands) cands))))) - (apply #'piem-process-call nil piem-git-executable "checkout" - (append (if (string-empty-p new-branch) - (list "--detach") - (list "-b" new-branch)) - (list base)))) - (piem-process-call nil piem-git-executable "am" "--scissors" - mbox-file) - (if (and piem-use-magit - (fboundp 'magit-status-setup-buffer)) - (magit-status-setup-buffer) - (dired ".")))) + (piem-am mbox-file info coderepo))) (define-infix-argument piem-b4-am:--outdir () :description "Output directory" diff --git a/piem.el b/piem.el index 6218e50..b764616 100644 --- a/piem.el +++ b/piem.el @@ -139,6 +139,22 @@ intended to be used by libraries implementing a function for "Whether to use Magit where possible." :type 'boolean) +(defcustom piem-default-branch-function + #'piem-name-branch-who-what-v + "Function that generates the default branch on completion. + +The function is called with one argument, a plist that contains +the following information about the patch series: + + :date + :from + :subject + The date, sender, and subject of the cover letter, if any, or of + the first patch otherwise. + :base-commit + The reported base commit of the patch, if any." + :type 'boolean) + ;;;; Subprocess handling @@ -223,6 +239,116 @@ intended to be used by libraries implementing a function for "Return the current buffer's message ID." (run-hook-with-args-until-success 'piem-get-mid-functions)) + +;;;; Patch handling + +(defun piem-series-info (cover patches) + "Collect information for a patch series. +COVER is an mbox with the cover letter, and PATCHES is an +am-ready mbox. If the series does not have a cover letter (e.g., +a one-patch series), COVER may be nil." + (with-temp-buffer + (insert-file-contents (or cover patches)) + (let ((info (save-restriction + (message-narrow-to-headers) + (list :date (message-fetch-field "date") + :from (message-fetch-field "from") + :subject (message-fetch-field "subject"))))) + (when (re-search-forward (rx line-start "base-commit: " + (group (>= 40 hex-digit)) + line-end) + nil t) + (plist-put info :base-commit (match-string 1))) + info))) + +(defun piem--shorten-subject (subject) + (let ((words + (mapcar #'downcase + (split-string + (thread-last subject + (replace-regexp-in-string + (rx (any "'\"")) + "") + (replace-regexp-in-string + (rx string-start (zero-or-more space) "[" + (zero-or-more (not (any "]" "\n"))) + "PATCH" + (zero-or-more (not (any "]" "\n"))) + "]" (one-or-more space)) + "")) + "\\W+" t))) + (ignore-these (list "a" "an" "the")) + (num-words 0) + (num-chars 0) + kept) + (catch 'stop + (dolist (word words) + (when (not (member word ignore-these)) + (cl-incf num-words) + (cl-incf num-chars (length word)) + (push word kept) + (when (or (> num-words 5) + (> num-chars 20)) + (throw 'stop nil))))) + (mapconcat #'identity (reverse kept) "-"))) + +(defun piem-name-branch-who-what-v (info) + "Construct a branch name like \"km/b4-short-subj__v3\". + +In the above example, \"k\" and \"m\" are the first letters of +the first and second \"words\" in the \"From:\" field. +\"b4-short-subj\" is a stripped down, truncated variant of +\"Subject:\". And \"v3\" is the version of the patch series, as +indicated in the subject. + +INFO is a plist with properties documented +in `piem-default-branch-function'." + (when-let ((sender (car (mail-extract-address-components + (plist-get info :from)))) + (subject (plist-get info :subject))) + (let* ((subnames (split-string sender)) + (initials (mapconcat + (lambda (subname) + (and subname + (downcase (substring subname nil 1)))) + (list (car subnames) (cadr subnames)) + "")) + (version (and (string-match + (rx "[" (zero-or-more (not (any "[" "\n"))) + "PATCH " + (zero-or-one (group "v" (one-or-more digit)))) + subject) + (match-string 1 subject)))) + (concat initials "/" + (piem--shorten-subject subject) + (and version (concat "__" version)))))) + +(defun piem-am (mbox &optional info coderepo) + (let* ((default-directory (or coderepo default-directory))) + ;; TODO: Optionally do more through Magit. + (let ((new-branch (read-string + "New branch (empty for detached): " + (funcall piem-default-branch-function info))) + (base (completing-read + "Base commit: " + (let ((cands (and piem-use-magit + (fboundp 'magit-list-local-branch-names) + (magit-list-local-branch-names))) + (base (plist-get info :base-commit))) + (if base (cons base cands) cands))))) + (apply #'piem-process-call nil piem-git-executable "checkout" + (append (if (string-empty-p new-branch) + (list "--detach") + (list "-b" new-branch)) + (list base)))) + (piem-process-call nil piem-git-executable "am" "--scissors" mbox) + (if (and piem-use-magit + (fboundp 'magit-status-setup-buffer)) + (magit-status-setup-buffer) + (dired ".")))) + + + (defun piem-please () "How I wish I could intersect my emails, feeds, and repos") -- cgit v1.2.3