aboutsummaryrefslogtreecommitdiffstats
path: root/lib/pama/apply.go
diff options
context:
space:
mode:
authorKoni Marti <koni.marti@gmail.com>2023-11-24 16:03:02 +0100
committerRobin Jarry <robin@jarry.cc>2023-12-30 15:42:09 +0100
commitfdd9f7991aa50bd99d21c178a2816fc075eead6b (patch)
tree737207d7dcb5b626f57d59e7b0b247d73a352f28 /lib/pama/apply.go
parentf98382d1dfc8970d3006fcc2175dd514bf7e07d0 (diff)
downloadaerc-fdd9f7991aa50bd99d21c178a2816fc075eead6b.tar.gz
patch/apply: add apply sub-cmd
Add the :patch apply command to apply a patch set and create a corresponding tag. The tag command argument can be completed based on the subject lines of the selected messages. Add a test for the completion proposal. Signed-off-by: Koni Marti <koni.marti@gmail.com> Acked-by: Robin Jarry <robin@jarry.cc>
Diffstat (limited to 'lib/pama/apply.go')
-rw-r--r--lib/pama/apply.go137
1 files changed, 137 insertions, 0 deletions
diff --git a/lib/pama/apply.go b/lib/pama/apply.go
new file mode 100644
index 00000000..15875818
--- /dev/null
+++ b/lib/pama/apply.go
@@ -0,0 +1,137 @@
+package pama
+
+import (
+ "encoding/base64"
+ "fmt"
+ "math/rand"
+ "strings"
+
+ "git.sr.ht/~rjarry/aerc/lib/pama/models"
+ "git.sr.ht/~rjarry/aerc/log"
+)
+
+func (m PatchManager) CurrentProject() (p models.Project, err error) {
+ store := m.store()
+ name, err := store.CurrentName()
+ if name == "" || err != nil {
+ log.Errorf("failed to get current name: %v", storeErr(err))
+ err = fmt.Errorf("No current project set. " +
+ "Run :patch init first")
+ return
+ }
+ names, err := store.Names()
+ if err != nil {
+ err = storeErr(err)
+ return
+ }
+ notFound := true
+ for _, s := range names {
+ if s == name {
+ notFound = !notFound
+ break
+ }
+ }
+ if notFound {
+ err = fmt.Errorf("Project '%s' does not exist anymore. "+
+ "Run :patch init or :patch switch", name)
+ return
+ }
+ p, err = store.Current()
+ if err != nil {
+ err = storeErr(err)
+ }
+ return
+}
+
+func (m PatchManager) CurrentPatches() ([]string, error) {
+ c, err := m.CurrentProject()
+ if err != nil {
+ return nil, err
+ }
+ return models.Commits(c.Commits).Tags(), nil
+}
+
+func (m PatchManager) Head(p models.Project) (string, error) {
+ rc, err := m.rc(p.RevctrlID, p.Root)
+ if err != nil {
+ return "", revErr(err)
+ }
+ return rc.Head()
+}
+
+func (m PatchManager) Clean(p models.Project) bool {
+ rc, err := m.rc(p.RevctrlID, p.Root)
+ if err != nil {
+ log.Errorf("could not get revctl: %v", revErr(err))
+ return false
+ }
+ return rc.Clean()
+}
+
+func (m PatchManager) ApplyCmd(p models.Project) (string, error) {
+ rc, err := m.rc(p.RevctrlID, p.Root)
+ if err != nil {
+ return "", revErr(err)
+ }
+ return rc.ApplyCmd(), nil
+}
+
+func generateTag(n int) (string, error) {
+ b := make([]byte, n)
+ _, err := rand.Read(b)
+ if err != nil {
+ return "", err
+ }
+ return base64.RawURLEncoding.EncodeToString(b), nil
+}
+
+func makeUnique(s string) string {
+ tag, err := generateTag(4)
+ if err != nil {
+ return fmt.Sprintf("%s_%d", s, rand.Uint32())
+ }
+ return fmt.Sprintf("%s_%s", s, tag)
+}
+
+// ApplyUpdate is called after the commits have been applied with the
+// ApplyCmd(). It will determine the additional commits from the commitID (last
+// HEAD position), assign the patch tag to those commits and store them in
+// project p.
+func (m PatchManager) ApplyUpdate(p models.Project, patch, commitID string,
+ kv map[string]string,
+) (models.Project, error) {
+ rc, err := m.rc(p.RevctrlID, p.Root)
+ if err != nil {
+ return p, revErr(err)
+ }
+
+ commitIDs, err := rc.History(commitID)
+ if err != nil {
+ return p, revErr(err)
+ }
+ if len(commitIDs) == 0 {
+ return p, fmt.Errorf("no commits found for patch %s", patch)
+ }
+
+ if models.Commits(p.Commits).HasTag(patch) {
+ log.Warnf("Patch name '%s' already exists", patch)
+ patch = makeUnique(patch)
+ log.Warnf("Creating new name: '%s'", patch)
+ }
+
+ for _, c := range commitIDs {
+ nc := models.NewCommit(rc, c, patch)
+ for msgid, subj := range kv {
+ if nc.Subject == "" {
+ continue
+ }
+ if strings.Contains(subj, nc.Subject) {
+ nc.MessageId = msgid
+ }
+ }
+ p.Commits = append(p.Commits, nc)
+ }
+
+ err = m.store().StoreProject(p, true)
+ return p, storeErr(err)
+}