summaryrefslogtreecommitdiffstats
path: root/emacs.d/lisp/magit/magit-bisect.el
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--emacs.d/lisp/magit/magit-bisect.el196
1 files changed, 196 insertions, 0 deletions
diff --git a/emacs.d/lisp/magit/magit-bisect.el b/emacs.d/lisp/magit/magit-bisect.el
new file mode 100644
index 0000000..fbbfa95
--- /dev/null
+++ b/emacs.d/lisp/magit/magit-bisect.el
@@ -0,0 +1,196 @@
+(require 'magit)
+
+(defvar magit--bisect-last-pos)
+(defvar magit--bisect-tmp-file)
+(defvar magit--bisect-info nil)
+(make-variable-buffer-local 'magit--bisect-info)
+(put 'magit--bisect-info 'permanent-local t)
+
+(defun magit--bisecting-p (&optional required-status)
+ "Return t if a bisect session is running.
+If REQUIRED-STATUS is not nil then the current status must also
+match REQUIRED-STATUS."
+ (and (file-exists-p (concat (magit-get-top-dir default-directory)
+ ".git/BISECT_LOG"))
+ (or (not required-status)
+ (eq (plist-get (magit--bisect-info) :status)
+ required-status))))
+
+(defun magit--bisect-info ()
+ (with-current-buffer (magit-find-status-buffer)
+ (or (if (local-variable-p 'magit--bisect-info) magit--bisect-info)
+ (list :status (if (magit--bisecting-p) 'running 'not-running)))))
+
+(defun magit--bisect-cmd (&rest args)
+ "Run `git bisect ...' and update the status buffer"
+ (with-current-buffer (magit-find-status-buffer)
+ (let* ((output (apply 'magit-git-lines (append '("bisect") args)))
+ (cmd (car args))
+ (first-line (car output)))
+ (save-match-data
+ (setq magit--bisect-info
+ (cond ((string= cmd "reset")
+ (list :status 'not-running))
+ ;; Bisecting: 78 revisions left to test after this (roughly 6 steps)
+ ((string-match "^Bisecting:\\s-+\\([0-9]+\\).+roughly\\s-+\\([0-9]+\\)" first-line)
+ (list :status 'running
+ :revs (match-string 1 first-line)
+ :steps (match-string 2 first-line)))
+ ;; e2596955d9253a80aec9071c18079705597fa102 is the first bad commit
+ ((string-match "^\\([a-f0-9]+\\)\\s-.*first bad commit" first-line)
+ (list :status 'finished
+ :bad (match-string 1 first-line)))
+ (t
+ (list :status 'error)))))))
+ (magit-refresh))
+
+(defun magit--bisect-info-for-status (branch)
+ "Return bisect info suitable for display in the status buffer"
+ (let* ((info (magit--bisect-info))
+ (status (plist-get info :status)))
+ (cond ((eq status 'not-running)
+ (or branch "(detached)"))
+ ((eq status 'running)
+ (format "(bisecting; %s revisions & %s steps left)"
+ (or (plist-get info :revs) "unknown number of")
+ (or (plist-get info :steps) "unknown number of")))
+ ((eq status 'finished)
+ (format "(bisected: first bad revision is %s)" (plist-get info :bad)))
+ (t
+ "(bisecting; unknown error occured)"))))
+
+(defun magit-bisect-start ()
+ "Start a bisect session"
+ (interactive)
+ (if (magit--bisecting-p)
+ (error "Already bisecting"))
+ (let ((bad (magit-read-rev "Start bisect with known bad revision" "HEAD"))
+ (good (magit-read-rev "Good revision" (magit-default-rev))))
+ (magit--bisect-cmd "start" bad good)))
+
+(defun magit-bisect-reset ()
+ "Quit a bisect session"
+ (interactive)
+ (unless (magit--bisecting-p)
+ (error "Not bisecting"))
+ (magit--bisect-cmd "reset"))
+
+(defun magit-bisect-good ()
+ "Tell git that the current revision is good during a bisect session"
+ (interactive)
+ (unless (magit--bisecting-p 'running)
+ (error "Not bisecting"))
+ (magit--bisect-cmd "good"))
+
+(defun magit-bisect-bad ()
+ "Tell git that the current revision is bad during a bisect session"
+ (interactive)
+ (unless (magit--bisecting-p 'running)
+ (error "Not bisecting"))
+ (magit--bisect-cmd "bad"))
+
+(defun magit-bisect-skip ()
+ "Tell git to skip the current revision during a bisect session."
+ (interactive)
+ (unless (magit--bisecting-p 'running)
+ (error "Not bisecting"))
+ (magit--bisect-cmd "skip"))
+
+(defun magit-bisect-log ()
+ "Show the bisect log"
+ (interactive)
+ (unless (magit--bisecting-p)
+ (error "Not bisecting"))
+ (magit-run-git "bisect" "log")
+ (magit-display-process))
+
+(defun magit-bisect-visualize ()
+ "Show the remaining suspects with gitk"
+ (interactive)
+ (unless (magit--bisecting-p)
+ (error "Not bisecting"))
+ (magit-run-git "bisect" "visualize")
+ (unless (getenv "DISPLAY")
+ (magit-display-process)))
+
+(easy-mmode-defmap magit-bisect-minibuffer-local-map
+ '(("\C-i" . comint-dynamic-complete-filename))
+ "Keymap for minibuffer prompting of rebase command."
+ :inherit minibuffer-local-map)
+
+(defvar magit-bisect-mode-history nil
+ "Previously run bisect commands.")
+
+(defun magit-bisect-run (command)
+ "Bisect automatically by running commands after each step"
+ (interactive
+ (list
+ (read-from-minibuffer "Run command (like this): "
+ ""
+ magit-bisect-minibuffer-local-map
+ nil
+ 'magit-bisect-mode-history)))
+ (unless (magit--bisecting-p)
+ (error "Not bisecting"))
+ (let ((file (make-temp-file "magit-bisect-run"))
+ buffer)
+ (with-temp-buffer
+ (insert "#!/bin/sh\n" command "\n")
+ (write-region (point-min) (point-max) file))
+ (chmod file #o755)
+ (magit-run-git-async "bisect" "run" file)
+ (magit-display-process)
+ (setq buffer (get-buffer magit-process-buffer-name))
+ (with-current-buffer buffer
+ (set (make-local-variable 'magit--bisect-last-pos) 0)
+ (set (make-local-variable 'magit--bisect-tmp-file) file))
+ (set-process-filter (get-buffer-process buffer) 'magit--bisect-run-filter)
+ (set-process-sentinel (get-buffer-process buffer) 'magit--bisect-run-sentinel)))
+
+(defun magit--bisect-run-filter (process output)
+ (with-current-buffer (process-buffer process)
+ (save-match-data
+ (let ((inhibit-read-only t)
+ line new-info)
+ (insert output)
+ (goto-char magit--bisect-last-pos)
+ (beginning-of-line)
+ (while (< (point) (point-max))
+ (cond ( ;; Bisecting: 78 revisions left to test after this (roughly 6 steps)
+ (looking-at "^Bisecting:\\s-+\\([0-9]+\\).+roughly\\s-+\\([0-9]+\\)")
+ (setq new-info (list :status 'running
+ :revs (match-string 1)
+ :steps (match-string 2))))
+ ( ;; e2596955d9253a80aec9071c18079705597fa102 is the first bad commit
+ (looking-at "^\\([a-f0-9]+\\)\\s-.*first bad commit")
+ (setq new-info (list :status 'finished
+ :bad (match-string 1)))))
+ (forward-line 1))
+ (goto-char (point-max))
+ (setq magit--bisect-last-pos (point))
+ (if new-info
+ (with-current-buffer (magit-find-status-buffer)
+ (setq magit--bisect-info new-info)
+ (magit--bisect-update-status-buffer)))))))
+
+(defun magit--bisect-run-sentinel (process event)
+ (if (string-match-p "^finish" event)
+ (with-current-buffer (process-buffer process)
+ (delete-file magit--bisect-tmp-file)))
+ (magit-process-sentinel process event))
+
+(defun magit--bisect-update-status-buffer ()
+ (with-current-buffer (magit-find-status-buffer)
+ (save-excursion
+ (save-match-data
+ (let ((inhibit-read-only t))
+ (goto-char (point-min))
+ (when (search-forward-regexp "Local:" nil t)
+ (beginning-of-line)
+ (kill-line)
+ (insert (format "Local: %s %s"
+ (propertize (magit--bisect-info-for-status (magit-get-current-branch))
+ 'face 'magit-branch)
+ (abbreviate-file-name default-directory)))))))))
+
+(provide 'magit-bisect)