diff options
author | thing1 <thing1@seacrossedlovers.xyz> | 2025-04-01 20:27:39 +0100 |
---|---|---|
committer | thing1 <thing1@seacrossedlovers.xyz> | 2025-04-01 20:27:39 +0100 |
commit | d3a5ddb4189ef7c04df0cc47a0f9642b23292d2d (patch) | |
tree | 14264483e4d2e6481abc74feea6d9cbcae3666d1 /elpa/magit-4.3.1/magit-bisect.el | |
parent | dabaff03992c102c395314629f63ce93a2c1bd3a (diff) |
added magit and other general configs
Diffstat (limited to 'elpa/magit-4.3.1/magit-bisect.el')
-rw-r--r-- | elpa/magit-4.3.1/magit-bisect.el | 318 |
1 files changed, 318 insertions, 0 deletions
diff --git a/elpa/magit-4.3.1/magit-bisect.el b/elpa/magit-4.3.1/magit-bisect.el new file mode 100644 index 0000000..3d64daa --- /dev/null +++ b/elpa/magit-4.3.1/magit-bisect.el @@ -0,0 +1,318 @@ +;;; magit-bisect.el --- Bisect support for Magit -*- lexical-binding:t -*- + +;; Copyright (C) 2008-2025 The Magit Project Contributors + +;; Author: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev> +;; Maintainer: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev> + +;; SPDX-License-Identifier: GPL-3.0-or-later + +;; Magit is free software: you can redistribute it and/or modify it +;; under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. +;; +;; Magit is distributed in the hope that it will be useful, but WITHOUT +;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +;; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +;; License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with Magit. If not, see <https://www.gnu.org/licenses/>. + +;;; Commentary: + +;; Use a binary search to find the commit that introduced a bug. + +;;; Code: + +(require 'magit) + +;;; Options + +(defcustom magit-bisect-show-graph t + "Whether to use `--graph' in the log showing commits yet to be bisected." + :package-version '(magit . "2.8.0") + :group 'magit-status + :type 'boolean) + +(defface magit-bisect-good + '((t :foreground "DarkOliveGreen")) + "Face for good bisect revisions." + :group 'magit-faces) + +(defface magit-bisect-skip + '((t :foreground "DarkGoldenrod")) + "Face for skipped bisect revisions." + :group 'magit-faces) + +(defface magit-bisect-bad + '((t :foreground "IndianRed4")) + "Face for bad bisect revisions." + :group 'magit-faces) + +;;; Commands + +;;;###autoload (autoload 'magit-bisect "magit-bisect" nil t) +(transient-define-prefix magit-bisect () + "Narrow in on the commit that introduced a bug." + :man-page "git-bisect" + [:class transient-subgroups + :if-not magit-bisect-in-progress-p + ["Arguments" + ("-n" "Don't checkout commits" "--no-checkout") + ("-p" "Follow only first parent of a merge" "--first-parent" + :if (lambda () (magit-git-version>= "2.29"))) + (magit-bisect:--term-old :level 6) + (magit-bisect:--term-new :level 6)] + ["Actions" + ("B" "Start" magit-bisect-start) + ("s" "Start script" magit-bisect-run)]] + ["Actions" + :if magit-bisect-in-progress-p + ("B" "Bad" magit-bisect-bad) + ("g" "Good" magit-bisect-good) + ("m" "Mark" magit-bisect-mark :level 6) + ("k" "Skip" magit-bisect-skip) + ("r" "Reset" magit-bisect-reset) + ("s" "Run script" magit-bisect-run)]) + +(transient-define-argument magit-bisect:--term-old () + :description "Old/good term" + :class 'transient-option + :key "=o" + :argument "--term-old=") + +(transient-define-argument magit-bisect:--term-new () + :description "New/bad term" + :class 'transient-option + :key "=n" + :argument "--term-new=") + +;;;###autoload +(defun magit-bisect-start (bad good args) + "Start a bisect session. + +Bisecting a bug means to find the commit that introduced it. +This command starts such a bisect session by asking for a known +good and a known bad commit. To move the session forward use the +other actions from the bisect transient command (\ +\\<magit-status-mode-map>\\[magit-bisect])." + (interactive (if (magit-bisect-in-progress-p) + (user-error "Already bisecting") + (magit-bisect-start-read-args))) + (magit-bisect-start--assert bad good args) + (magit-repository-local-set 'bisect--first-parent + (transient-arg-value "--first-parent" args)) + (magit-git-bisect "start" (list args bad good) t)) + +(defun magit-bisect-start-read-args () + (let* ((args (transient-args 'magit-bisect)) + (bad (magit-read-branch-or-commit + (format "Start bisect with %s revision" + (or (transient-arg-value "--term-new=" args) + "bad"))))) + (list bad + (magit-read-other-branch-or-commit + (format "%s revision" (or (transient-arg-value "--term-old=" args) + "Good")) + bad) + args))) + +(defun magit-bisect-start--assert (bad good args) + (unless (magit-rev-ancestor-p good bad) + (user-error + "The %s revision (%s) has to be an ancestor of the %s one (%s)" + (or (transient-arg-value "--term-old=" args) "good") + good + (or (transient-arg-value "--term-new=" args) "bad") + bad)) + (when (magit-anything-modified-p) + (user-error "Cannot bisect with uncommitted changes"))) + +;;;###autoload +(defun magit-bisect-reset () + "After bisecting, cleanup bisection state and return to original `HEAD'." + (interactive) + (magit-confirm 'reset-bisect) + (magit-run-git "bisect" "reset") + (magit-repository-local-delete 'bisect--first-parent) + (ignore-errors + (delete-file (expand-file-name "BISECT_CMD_OUTPUT" (magit-gitdir))))) + +;;;###autoload +(defun magit-bisect-good () + "While bisecting, mark the current commit as good. +Use this after you have asserted that the commit does not contain +the bug in question." + (interactive) + (magit-git-bisect (or (cadr (magit-bisect-terms)) + (user-error "Not bisecting")))) + +;;;###autoload +(defun magit-bisect-bad () + "While bisecting, mark the current commit as bad. +Use this after you have asserted that the commit does contain the +bug in question." + (interactive) + (magit-git-bisect (or (car (magit-bisect-terms)) + (user-error "Not bisecting")))) + +;;;###autoload +(defun magit-bisect-mark () + "While bisecting, mark the current commit with a bisect term. +During a bisect using alternate terms, commits can still be +marked with `magit-bisect-good' and `magit-bisect-bad', as those +commands map to the correct term (\"good\" to --term-old's value +and \"bad\" to --term-new's). However, in some cases, it can be +difficult to keep that mapping straight in your head; this +command provides an interface that exposes the underlying terms." + (interactive) + (magit-git-bisect + (pcase-let ((`(,term-new ,term-old) (or (magit-bisect-terms) + (user-error "Not bisecting")))) + (pcase (read-char-choice + (format "Mark HEAD as %s ([n]ew) or %s ([o]ld)" + term-new term-old) + (list ?n ?o)) + (?n term-new) + (?o term-old))))) + +;;;###autoload +(defun magit-bisect-skip () + "While bisecting, skip the current commit. +Use this if for some reason the current commit is not a good one +to test. This command lets Git choose a different one." + (interactive) + (magit-git-bisect "skip")) + +;;;###autoload +(defun magit-bisect-run (cmdline &optional bad good args) + "Bisect automatically by running commands after each step. + +Unlike `git bisect run' this can be used before bisecting has +begun. In that case it behaves like `git bisect start; git +bisect run'." + (interactive (let ((args (and (not (magit-bisect-in-progress-p)) + (magit-bisect-start-read-args)))) + (cons (read-shell-command "Bisect shell command: ") args))) + (when (and bad good) + (magit-bisect-start--assert bad good args) + ;; Avoid `magit-git-bisect' because it's asynchronous, but the + ;; next `git bisect run' call requires the bisect to be started. + (magit-with-toplevel + (magit-process-git + (list :file (expand-file-name "BISECT_CMD_OUTPUT" (magit-gitdir))) + "bisect" "start" bad good args) + (magit-refresh))) + (with-connection-local-variables + (magit-git-bisect "run" (list shell-file-name + shell-command-switch cmdline)))) + +(defun magit-git-bisect (subcommand &optional args no-assert) + (unless (or no-assert (magit-bisect-in-progress-p)) + (user-error "Not bisecting")) + (message "Bisecting...") + (magit-with-toplevel + (magit-run-git-async "bisect" subcommand args)) + (set-process-sentinel + magit-this-process + (lambda (process event) + (when (memq (process-status process) '(exit signal)) + (if (> (process-exit-status process) 0) + (magit-process-sentinel process event) + (process-put process 'inhibit-refresh t) + (magit-process-sentinel process event) + (when (buffer-live-p (process-buffer process)) + (with-current-buffer (process-buffer process) + (when-let* ((section (magit-section-at)) + (output (buffer-substring-no-properties + (oref section content) + (oref section end)))) + (with-temp-file + (expand-file-name "BISECT_CMD_OUTPUT" (magit-gitdir)) + (insert output))))) + (magit-refresh)) + (message "Bisecting...done"))))) + +;;; Sections + +(defun magit-bisect-in-progress-p () + (file-exists-p (expand-file-name "BISECT_LOG" (magit-gitdir)))) + +(defun magit-bisect-terms () + (magit-file-lines (expand-file-name "BISECT_TERMS" (magit-gitdir)))) + +(defun magit-insert-bisect-output () + "While bisecting, insert section with output from `git bisect'." + (when (magit-bisect-in-progress-p) + (let* ((lines + (or (magit-file-lines + (expand-file-name "BISECT_CMD_OUTPUT" (magit-gitdir))) + (list "Bisecting: (no saved bisect output)" + "It appears you have invoked `git bisect' from a shell." + "There is nothing wrong with that, we just cannot display" + "anything useful here. Consult the shell output instead."))) + (done-re "^\\([a-z0-9]\\{40,\\}\\) is the first bad commit$") + (bad-line (or (and (string-match done-re (car lines)) + (pop lines)) + (seq-find (##string-match done-re %) lines)))) + (magit-insert-section ((eval (if bad-line 'commit 'bisect-output)) + (and bad-line (match-string 1 bad-line))) + (magit-insert-heading + (propertize (or bad-line (pop lines)) + 'font-lock-face 'magit-section-heading)) + (dolist (line lines) + (insert line "\n")))) + (insert "\n"))) + +(defun magit-insert-bisect-rest () + "While bisecting, insert section visualizing the bisect state." + (when (magit-bisect-in-progress-p) + (magit-insert-section (bisect-view) + (magit-insert-heading t "Bisect Rest") + (magit-git-wash (apply-partially #'magit-log-wash-log 'bisect-vis) + "bisect" "visualize" "git" "log" + "--format=%h%x00%D%x00%s" "--decorate=full" + (and magit-bisect-show-graph "--graph") + (and (magit-repository-local-get 'bisect--first-parent) + "--first-parent"))))) + +(defun magit-insert-bisect-log () + "While bisecting, insert section logging bisect progress." + (when (magit-bisect-in-progress-p) + (magit-insert-section (bisect-log) + (magit-insert-heading t "Bisect Log") + (magit-git-wash #'magit-wash-bisect-log "bisect" "log") + (insert ?\n)))) + +(defun magit-wash-bisect-log (_args) + (let (beg) + (while (progn (setq beg (point-marker)) + (re-search-forward + "^\\(\\(?:git bisect\\|# status:\\) [^\n]+\n\\)" nil t)) + (if (string-prefix-p "# status:" (match-string 1)) + (magit-delete-match) + (magit-bind-match-strings (heading) nil + (magit-delete-match) + (save-restriction + (narrow-to-region beg (point)) + (goto-char (point-min)) + (magit-insert-section (bisect-item heading t) + (magit-insert-heading + (propertize heading 'font-lock-face + 'magit-section-secondary-heading)) + (magit-wash-sequence + (apply-partially #'magit-log-wash-rev 'bisect-log + (magit-abbrev-length))) + (insert ?\n)))))) + (when (re-search-forward + "# first bad commit: \\[\\([a-z0-9]\\{40,\\}\\)\\] [^\n]+\n" nil t) + (magit-bind-match-strings (hash) nil + (magit-delete-match) + (magit-insert-section (bisect-item) + (insert hash " is the first bad commit\n")))))) + +;;; _ +(provide 'magit-bisect) +;;; magit-bisect.el ends here |