stgit.el: Add command for showing diff for a range of patches
[stgit] / contrib / stgit.el
index f617e6d..73c8e4b 100644 (file)
@@ -940,6 +940,7 @@ file for (applied) copies and renames."
             ("c" .        stgit-diff-combined)
             ("m" .        stgit-find-file-merge)
             ("o" .        stgit-diff-ours)
             ("c" .        stgit-diff-combined)
             ("m" .        stgit-find-file-merge)
             ("o" .        stgit-diff-ours)
+            ("r" .        stgit-diff-range)
             ("t" .        stgit-diff-theirs)))
     (suppress-keymap toggle-map)
     (mapc (lambda (arg) (define-key toggle-map (car arg) (cdr arg)))
             ("t" .        stgit-diff-theirs)))
     (suppress-keymap toggle-map)
     (mapc (lambda (arg) (define-key toggle-map (car arg) (cdr arg)))
@@ -1077,6 +1078,8 @@ file for (applied) copies and renames."
         "-"
         ["Show diff" stgit-diff
          :active (get-text-property (point) 'entry-type)]
         "-"
         ["Show diff" stgit-diff
          :active (get-text-property (point) 'entry-type)]
+        ["Show diff for range of applied patches" stgit-diff-range
+         :active (= (length stgit-marked-patches) 1)]
         ("Merge"
          :active (stgit-git-index-unmerged-p)
          ["Combined diff" stgit-diff-combined
         ("Merge"
          :active (stgit-git-index-unmerged-p)
          ["Combined diff" stgit-diff-combined
@@ -1191,6 +1194,7 @@ Display commands:
 
 Commands for diffs:
 \\[stgit-diff] Show diff of patch or file
 
 Commands for diffs:
 \\[stgit-diff] Show diff of patch or file
+\\[stgit-diff-range]   Show diff for range of patches
 \\[stgit-diff-base]    Show diff against the merge base
 \\[stgit-diff-ours]    Show diff against our branch
 \\[stgit-diff-theirs]  Show diff against their branch
 \\[stgit-diff-base]    Show diff against the merge base
 \\[stgit-diff-ours]    Show diff against our branch
 \\[stgit-diff-theirs]  Show diff against their branch
@@ -1718,16 +1722,17 @@ If PATCHSYM is a keyword, returns PATCHSYM unmodified."
        (error "Cannot find commit id for %s" patchsym))
       (match-string 1 result))))
 
        (error "Cannot find commit id for %s" patchsym))
       (match-string 1 result))))
 
+(defun stgit-whitespace-diff-arg (arg)
+  (when (numberp arg)
+    (cond ((> arg 4) "--ignore-all-space")
+          ((> arg 1) "--ignore-space-change"))))
+
 (defun stgit-show-patch (unmerged-stage ignore-whitespace)
   "Show the patch on the current line.
 
 UNMERGED-STAGE is the argument to `git-diff' that that selects
 which stage to diff against in the case of unmerged files."
 (defun stgit-show-patch (unmerged-stage ignore-whitespace)
   "Show the patch on the current line.
 
 UNMERGED-STAGE is the argument to `git-diff' that that selects
 which stage to diff against in the case of unmerged files."
-  (let ((space-arg (when (numberp ignore-whitespace)
-                     (cond ((> ignore-whitespace 4)
-                            "--ignore-all-space")
-                           ((> ignore-whitespace 1)
-                            "--ignore-space-change"))))
+  (let ((space-arg (stgit-whitespace-diff-arg ignore-whitespace))
         (patch-name (stgit-patch-name-at-point t)))
     (stgit-capture-output "*StGit patch*"
       (case (get-text-property (point) 'entry-type)
         (patch-name (stgit-patch-name-at-point t)))
     (stgit-capture-output "*StGit patch*"
       (case (get-text-property (point) 'entry-type)
@@ -1803,6 +1808,35 @@ greater than four (e.g., \\[universal-argument] \
                    "--cc"
                    "show a combined diff")
 
                    "--cc"
                    "show a combined diff")
 
+(defun stgit-diff-range (&optional ignore-whitespace)
+  "Show diff for the range of patches between point and the marked patch.
+
+With a prefix argument, ignore whitespace. With a prefix argument
+greater than four (e.g., \\[universal-argument] \
+\\[universal-argument] \\[stgit-diff-range]), ignore all whitespace."
+  (interactive "p")
+  (stgit-assert-mode)
+  (unless (= (length stgit-marked-patches) 1)
+    (error "Need exactly one patch marked"))
+  (let* ((patches (stgit-sort-patches (cons (stgit-patch-name-at-point t t)
+                                            stgit-marked-patches)
+                                      t))
+         (first-patch (car patches))
+         (second-patch (if (cdr patches) (cadr patches) first-patch))
+         (whitespace-arg (stgit-whitespace-diff-arg ignore-whitespace))
+         (applied (stgit-applied-patchsyms t)))
+    (unless (and (memq first-patch applied) (memq second-patch applied))
+      (error "Can only show diff range for applied patches"))
+    (stgit-capture-output (format "*StGit diff %s..%s*"
+                                  first-patch second-patch)
+      (apply 'stgit-run-git (append '("diff" "--patch-with-stat")
+                                    (and whitespace-arg (list whitespace-arg))
+                                    (list (format "%s^" (stgit-id first-patch))
+                                          (stgit-id second-patch))))
+      (with-current-buffer standard-output
+        (goto-char (point-min))
+        (diff-mode)))))
+
 (defun stgit-move-change-to-index (file &optional force)
   "Copies the work tree state of FILE to index, using git add or git rm.
 
 (defun stgit-move-change-to-index (file &optional force)
   "Copies the work tree state of FILE to index, using git add or git rm.
 
@@ -2021,11 +2055,12 @@ patches."
                 (t (setq result :bottom)))))
       result)))
 
                 (t (setq result :bottom)))))
       result)))
 
-(defun stgit-sort-patches (patchsyms)
+(defun stgit-sort-patches (patchsyms &optional allow-duplicates)
   "Returns the list of patches in PATCHSYMS sorted according to
 their position in the patch series, bottommost first.
 
   "Returns the list of patches in PATCHSYMS sorted according to
 their position in the patch series, bottommost first.
 
-PATCHSYMS must not contain duplicate entries."
+PATCHSYMS must not contain duplicate entries, unless
+ALLOW-DUPLICATES is not nil."
   (let (sorted-patchsyms
         (series (with-output-to-string
                   (with-current-buffer standard-output
   (let (sorted-patchsyms
         (series (with-output-to-string
                   (with-current-buffer standard-output
@@ -2038,8 +2073,9 @@ PATCHSYMS must not contain duplicate entries."
       (setq start (match-end 0)))
     (setq sorted-patchsyms (nreverse sorted-patchsyms))
 
       (setq start (match-end 0)))
     (setq sorted-patchsyms (nreverse sorted-patchsyms))
 
-    (unless (= (length patchsyms) (length sorted-patchsyms))
-      (error "Internal error"))
+    (unless allow-duplicates
+      (unless (= (length patchsyms) (length sorted-patchsyms))
+        (error "Internal error")))
 
     sorted-patchsyms))
 
 
     sorted-patchsyms))