aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2026-05-26 19:28:05 -0500
committerCraig Jennings <c@cjennings.net>2026-05-26 19:28:05 -0500
commit269ed3c2b1a94ed8ebad5fdce110efb671aa70f6 (patch)
treebc954739ec3437b9938ecb7f8cd3d70627de093d
parent80eca723d4340d0d67a709e6aef01acb935c706b (diff)
downloadpearl-269ed3c2b1a94ed8ebad5fdce110efb671aa70f6.tar.gz
pearl-269ed3c2b1a94ed8ebad5fdce110efb671aa70f6.zip
fix(refresh): merge keeps any locally-edited ticket
The merge refresh protected a subtree from being overwritten only when its description body had changed, since pearl--subtree-dirty-p is description-only. A local edit to the title, state, priority, assignee, labels, or a comment looked untouched and got re-rendered away. I added pearl--subtree-has-local-edits-p, which wraps the comprehensive per-field detector the save path already uses, and pointed both merge protection sites at it. A merge now keeps any ticket you've touched in any field. The same gain flows to pearl-refresh-current-view.
-rw-r--r--pearl.el17
-rw-r--r--tests/test-pearl-merge.el17
2 files changed, 30 insertions, 4 deletions
diff --git a/pearl.el b/pearl.el
index 369608d..79c2ebd 100644
--- a/pearl.el
+++ b/pearl.el
@@ -4020,12 +4020,21 @@ reported. A clean file does nothing."
(and pearl--cache-viewer
(plist-get pearl--cache-viewer :id))))))))
+(defun pearl--subtree-has-local-edits-p ()
+ "Non-nil if the issue subtree at point has any unpushed local edit.
+Broader than `pearl--subtree-dirty-p', which inspects only the description:
+this also covers a changed title, state, priority, assignee, labels, or comment.
+A merge refresh uses it so it keeps a ticket you have touched in any field, not
+just ones whose description changed."
+ (pearl--issue-has-dirty-fields-p (pearl--issue-dirty-fields)))
+
(defun pearl--merge-issues-into-buffer (issues)
"Merge normalized ISSUES into the current buffer by `LINEAR-ID'.
Same-source refresh semantics: an existing issue still in ISSUES is re-rendered
in place; an issue new to ISSUES is appended after the last one; an issue no
-longer in ISSUES is dropped. A subtree whose body has unpushed edits (see
-`pearl--subtree-dirty-p') is never overwritten and never dropped -- it is kept
+longer in ISSUES is dropped. A subtree with any unpushed local edit (see
+`pearl--subtree-has-local-edits-p' -- title, state, priority, assignee, labels,
+description, or a comment) is never overwritten and never dropped; it is kept
and counted, so a refresh can't lose un-synced work. Returns a plist of counts
\(:updated :added :dropped :skipped)."
(let ((existing (pearl--issue-subtree-markers))
@@ -4037,7 +4046,7 @@ and counted, so a refresh can't lose un-synced work. Returns a plist of counts
(when marker
(save-excursion
(goto-char marker)
- (if (pearl--subtree-dirty-p)
+ (if (pearl--subtree-has-local-edits-p)
(setq skipped (1+ skipped))
(pearl--replace-issue-subtree-at-point issue)
(setq updated (1+ updated)))))))
@@ -4046,7 +4055,7 @@ and counted, so a refresh can't lose un-synced work. Returns a plist of counts
(unless (member (car cell) fetched-ids)
(save-excursion
(goto-char (cdr cell))
- (if (pearl--subtree-dirty-p)
+ (if (pearl--subtree-has-local-edits-p)
(setq skipped (1+ skipped))
(org-back-to-heading t)
(delete-region (point) (progn (org-end-of-subtree t t) (point)))
diff --git a/tests/test-pearl-merge.el b/tests/test-pearl-merge.el
index 7cb2a34..122e5fa 100644
--- a/tests/test-pearl-merge.el
+++ b/tests/test-pearl-merge.el
@@ -142,6 +142,23 @@
(goto-char (point-min))
(should-not (re-search-forward "Alpha Renamed" nil t)))))
+(ert-deftest test-pearl-merge-keeps-title-edited-issue ()
+ "A subtree with an unpushed title edit (not a description edit) is kept too.
+Merge must protect any locally-edited field, not only the description."
+ (test-pearl-merge--in-org
+ (test-pearl-merge--buffer (test-pearl-merge--issue "a" "Alpha" "Desc Alpha."))
+ (goto-char (point-min))
+ (re-search-forward "Alpha") ; the title word in the heading
+ (insert " EDITED") ; dirty the title only; body untouched
+ (let ((counts (pearl--merge-issues-into-buffer
+ (list (test-pearl-merge--issue "a" "Alpha Renamed" "Remote desc.")))))
+ (should (= 1 (plist-get counts :skipped)))
+ (should (= 0 (plist-get counts :updated)))
+ (goto-char (point-min))
+ (should (re-search-forward "Alpha EDITED" nil t)) ; local title edit kept
+ (goto-char (point-min))
+ (should-not (re-search-forward "Alpha Renamed" nil t))))) ; remote did not land
+
(ert-deftest test-pearl-merge-keeps-dirty-issue-absent-from-result ()
"A dirty issue gone from the result is kept rather than dropped (no data loss)."
(test-pearl-merge--in-org