Skip to content


Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

197 lines (177 sloc) 7.695 kb
(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
(and (file-exists-p (concat (magit-get-top-dir default-directory)
(or (not required-status)
(eq (plist-get (magit--bisect-info) :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)))
(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)))
(list :status 'error)))))))
(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)))
"(bisecting; unknown error occured)"))))
(defun magit-bisect-start ()
"Start a bisect session"
(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"
(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"
(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"
(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."
(unless (magit--bisecting-p 'running)
(error "Not bisecting"))
(magit--bisect-cmd "skip"))
(defun magit-bisect-log ()
"Show the bisect log"
(unless (magit--bisecting-p)
(error "Not bisecting"))
(magit-run-git "bisect" "log")
(defun magit-bisect-visualize ()
"Show the remaining suspects with gitk"
(unless (magit--bisecting-p)
(error "Not bisecting"))
(magit-run-git "bisect" "visualize")
(unless (getenv "DISPLAY")
(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"
(read-from-minibuffer "Run command (like this): "
(unless (magit--bisecting-p)
(error "Not bisecting"))
(let ((file (make-temp-file "magit-bisect-run"))
(insert "#!/bin/sh\n" command "\n")
(write-region (point-min) (point-max) file))
(chmod file #o755)
(magit-run-git-async "bisect" "run" file)
(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)
(let ((inhibit-read-only t)
line new-info)
(insert output)
(goto-char magit--bisect-last-pos)
(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)
(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)
(let ((inhibit-read-only t))
(goto-char (point-min))
(when (search-forward-regexp "Local:" nil t)
(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)
Jump to Line
Something went wrong with that request. Please try again.