mirror of https://github.com/Chizi123/.emacs.d.git

Chizi123
2018-11-18 21067e7cbe6d7a0f65ff5c317a96b5c337b0b3d8
commit | author | age
5cb5f7 1 ;;; magit-bisect.el --- bisect support for Magit  -*- lexical-binding: t -*-
C 2
3 ;; Copyright (C) 2011-2018  The Magit Project Contributors
4 ;;
5 ;; You should have received a copy of the AUTHORS.md file which
6 ;; lists all contributors.  If not, see http://magit.vc/authors.
7
8 ;; Author: Jonas Bernoulli <jonas@bernoul.li>
9 ;; Maintainer: Jonas Bernoulli <jonas@bernoul.li>
10
11 ;; Magit is free software; you can redistribute it and/or modify it
12 ;; under the terms of the GNU General Public License as published by
13 ;; the Free Software Foundation; either version 3, or (at your option)
14 ;; any later version.
15 ;;
16 ;; Magit is distributed in the hope that it will be useful, but WITHOUT
17 ;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
18 ;; or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
19 ;; License for more details.
20 ;;
21 ;; You should have received a copy of the GNU General Public License
22 ;; along with Magit.  If not, see http://www.gnu.org/licenses.
23
24 ;;; Commentary:
25
26 ;; Use a binary search to find the commit that introduced a bug.
27
28 ;;; Code:
29
30 (require 'magit)
31
32 ;;; Options
33
34 (defcustom magit-bisect-show-graph t
35   "Whether to use `--graph' in the log showing commits yet to be bisected."
36   :package-version '(magit . "2.8.0")
37   :group 'magit-status
38   :type 'boolean)
39
40 (defface magit-bisect-good
41   '((t :foreground "DarkOliveGreen"))
42   "Face for good bisect revisions."
43   :group 'magit-faces)
44
45 (defface magit-bisect-skip
46   '((t :foreground "DarkGoldenrod"))
47   "Face for skipped bisect revisions."
48   :group 'magit-faces)
49
50 (defface magit-bisect-bad
51   '((t :foreground "IndianRed4"))
52   "Face for bad bisect revisions."
53   :group 'magit-faces)
54
55 ;;; Commands
56
57 ;;;###autoload (autoload 'magit-bisect-popup "magit-bisect" nil t)
58 (magit-define-popup magit-bisect-popup
59   "Popup console for bisect commands."
60   :man-page "git-bisect"
61   :actions            '((?B "Start"        magit-bisect-start)
62                         (?s "Start script" magit-bisect-run))
63   :sequence-actions   '((?b "Bad"          magit-bisect-bad)
64                         (?g "Good"         magit-bisect-good)
65                         (?k "Skip"         magit-bisect-skip)
66                         (?r "Reset"        magit-bisect-reset)
67                         (?s "Run script"   magit-bisect-run))
68   :sequence-predicate 'magit-bisect-in-progress-p)
69
70 ;;;###autoload
71 (defun magit-bisect-start (bad good)
72   "Start a bisect session.
73
74 Bisecting a bug means to find the commit that introduced it.
75 This command starts such a bisect session by asking for a know
76 good and a bad commit.  To move the session forward use the
77 other actions from the bisect popup (\
78 \\<magit-status-mode-map>\\[magit-bisect-popup])."
79   (interactive (if (magit-bisect-in-progress-p)
80                    (user-error "Already bisecting")
81                  (magit-bisect-start-read-args)))
82   (unless (magit-rev-ancestor-p good bad)
83     (user-error
84      "The good revision (%s) has to be an ancestor of the bad one (%s)"
85      good bad))
86   (when (magit-anything-modified-p)
87     (user-error "Cannot bisect with uncommitted changes"))
88   (magit-git-bisect "start" (list bad good) t))
89
90 (defun magit-bisect-start-read-args ()
91   (let  ((b (magit-read-branch-or-commit "Start bisect with bad revision")))
92     (list b (magit-read-other-branch-or-commit "Good revision" b))))
93
94 ;;;###autoload
95 (defun magit-bisect-reset ()
96   "After bisecting, cleanup bisection state and return to original `HEAD'."
97   (interactive)
98   (magit-confirm 'reset-bisect)
99   (magit-run-git "bisect" "reset")
100   (ignore-errors (delete-file (magit-git-dir "BISECT_CMD_OUTPUT"))))
101
102 ;;;###autoload
103 (defun magit-bisect-good ()
104   "While bisecting, mark the current commit as good.
105 Use this after you have asserted that the commit does not contain
106 the bug in question."
107   (interactive)
108   (magit-git-bisect "good"))
109
110 ;;;###autoload
111 (defun magit-bisect-bad ()
112   "While bisecting, mark the current commit as bad.
113 Use this after you have asserted that the commit does contain the
114 bug in question."
115   (interactive)
116   (magit-git-bisect "bad"))
117
118 ;;;###autoload
119 (defun magit-bisect-skip ()
120   "While bisecting, skip the current commit.
121 Use this if for some reason the current commit is not a good one
122 to test.  This command lets Git choose a different one."
123   (interactive)
124   (magit-git-bisect "skip"))
125
126 ;;;###autoload
127 (defun magit-bisect-run (cmdline &optional bad good)
128   "Bisect automatically by running commands after each step.
129
130 Unlike `git bisect run' this can be used before bisecting has
131 begun.  In that case it behaves like `git bisect start; git
132 bisect run'."
133   (interactive (let ((args (and (not (magit-bisect-in-progress-p))
134                                 (magit-bisect-start-read-args))))
135                  (cons (read-shell-command "Bisect shell command: ") args)))
136   (when (and bad good)
137     (magit-bisect-start bad good))
138   (magit-git-bisect "run" (list shell-file-name shell-command-switch cmdline)))
139
140 (defun magit-git-bisect (subcommand &optional args no-assert)
141   (unless (or no-assert (magit-bisect-in-progress-p))
142     (user-error "Not bisecting"))
143   (magit-with-toplevel
144     (magit-run-git-with-logfile
145      (magit-git-dir "BISECT_CMD_OUTPUT") "bisect" subcommand args)))
146
147 ;;; Sections
148
149 (defun magit-bisect-in-progress-p ()
150   (file-exists-p (magit-git-dir "BISECT_LOG")))
151
152 (defun magit-insert-bisect-output ()
153   "While bisecting, insert section with output from `git bisect'."
154   (when (magit-bisect-in-progress-p)
155     (let* ((lines
156             (or (magit-file-lines (magit-git-dir "BISECT_CMD_OUTPUT"))
157                 (list "Bisecting: (no saved bisect output)"
158                       "It appears you have invoked `git bisect' from a shell."
159                       "There is nothing wrong with that, we just cannot display"
160                       "anything useful here.  Consult the shell output instead.")))
161            (done-re "^\\([a-z0-9]\\{40\\}\\) is the first bad commit$")
162            (bad-line (or (and (string-match done-re (car lines))
163                               (pop lines))
164                          (--first (string-match done-re it) lines))))
165       (magit-insert-section ((eval (if bad-line 'commit 'bisect-output))
166                              (and bad-line (match-string 1 bad-line)))
167         (magit-insert-heading
168           (propertize (or bad-line (pop lines))
169                       'face 'magit-section-heading))
170         (dolist (line lines)
171           (insert line "\n"))))
172     (insert "\n")))
173
174 (defun magit-insert-bisect-rest ()
175   "While bisecting, insert section visualizing the bisect state."
176   (when (magit-bisect-in-progress-p)
177     (magit-insert-section (bisect-view)
178       (magit-insert-heading "Bisect Rest:")
179       (magit-git-wash (apply-partially 'magit-log-wash-log 'bisect-vis)
180         "bisect" "visualize" "git" "log"
181         "--format=%h%d%x00%s" "--decorate=full"
182         (and magit-bisect-show-graph "--graph")))))
183
184 (defun magit-insert-bisect-log ()
185   "While bisecting, insert section logging bisect progress."
186   (when (magit-bisect-in-progress-p)
187     (magit-insert-section (bisect-log)
188       (magit-insert-heading "Bisect Log:")
189       (magit-git-wash #'magit-wash-bisect-log "bisect" "log")
190       (insert ?\n))))
191
192 (defun magit-wash-bisect-log (_args)
193   (let (beg)
194     (while (progn (setq beg (point-marker))
195                   (re-search-forward "^\\(git bisect [^\n]+\n\\)" nil t))
196       (magit-bind-match-strings (heading) nil
197         (magit-delete-match)
198         (save-restriction
199           (narrow-to-region beg (point))
200           (goto-char (point-min))
201           (magit-insert-section (bisect-item heading t)
202             (insert (propertize heading 'face 'magit-section-secondary-heading))
203             (magit-insert-heading)
204             (magit-wash-sequence
205              (apply-partially 'magit-log-wash-rev 'bisect-log
206                               (magit-abbrev-length)))
207             (insert ?\n)))))
208     (when (re-search-forward
209            "# first bad commit: \\[\\([a-z0-9]\\{40\\}\\)\\] [^\n]+\n" nil t)
210       (magit-bind-match-strings (hash) nil
211         (magit-delete-match)
212         (magit-insert-section (bisect-item)
213           (insert hash " is the first bad commit\n"))))))
214
215 ;;; _
216 (provide 'magit-bisect)
217 ;;; magit-bisect.el ends here