commit | author | age
|
5cb5f7
|
1 |
;;; magit-refs.el --- listing references -*- lexical-binding: t -*- |
C |
2 |
|
|
3 |
;; Copyright (C) 2010-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 |
;; This library implements support for listing references in a buffer. |
|
27 |
|
|
28 |
;;; Code: |
|
29 |
|
|
30 |
(eval-when-compile |
|
31 |
(require 'subr-x)) |
|
32 |
|
|
33 |
(require 'magit) |
|
34 |
|
|
35 |
(defvar bookmark-make-record-function) |
|
36 |
|
|
37 |
;;; Options |
|
38 |
|
|
39 |
(defgroup magit-refs nil |
|
40 |
"Inspect and manipulate Git branches and tags." |
|
41 |
:link '(info-link "(magit)References Buffer") |
|
42 |
:group 'magit-modes) |
|
43 |
|
|
44 |
(defcustom magit-refs-mode-hook nil |
|
45 |
"Hook run after entering Magit-Refs mode." |
|
46 |
:package-version '(magit . "2.1.0") |
|
47 |
:group 'magit-refs |
|
48 |
:type 'hook) |
|
49 |
|
|
50 |
(defcustom magit-refs-sections-hook |
|
51 |
'(magit-insert-error-header |
|
52 |
magit-insert-branch-description |
|
53 |
magit-insert-local-branches |
|
54 |
magit-insert-remote-branches |
|
55 |
magit-insert-tags) |
|
56 |
"Hook run to insert sections into a references buffer." |
|
57 |
:package-version '(magit . "2.1.0") |
|
58 |
:group 'magit-refs |
|
59 |
:type 'hook) |
|
60 |
|
|
61 |
(defcustom magit-refs-show-commit-count nil |
|
62 |
"Whether to show commit counts in Magit-Refs mode buffers. |
|
63 |
|
|
64 |
all Show counts for branches and tags. |
|
65 |
branch Show counts for branches only. |
|
66 |
nil Never show counts. |
|
67 |
|
|
68 |
To change the value in an existing buffer use the command |
|
69 |
`magit-refs-show-commit-count'" |
|
70 |
:package-version '(magit . "2.1.0") |
|
71 |
:group 'magit-refs |
|
72 |
:safe (lambda (val) (memq val '(all branch nil))) |
|
73 |
:type '(choice (const all :tag "For branches and tags") |
|
74 |
(const branch :tag "For branches only") |
|
75 |
(const nil :tag "Never"))) |
|
76 |
(put 'magit-refs-show-commit-count 'safe-local-variable 'symbolp) |
|
77 |
(put 'magit-refs-show-commit-count 'permanent-local t) |
|
78 |
|
|
79 |
(defcustom magit-refs-pad-commit-counts nil |
|
80 |
"Whether to pad all counts on all sides in `magit-refs-mode' buffers. |
|
81 |
|
|
82 |
If this is nil, then some commit counts are displayed right next |
|
83 |
to one of the branches that appear next to the count, without any |
|
84 |
space in between. This might look bad if the branch name faces |
|
85 |
look too similar to `magit-dimmed'. |
|
86 |
|
|
87 |
If this is non-nil, then spaces are placed on both sides of all |
|
88 |
commit counts." |
|
89 |
:package-version '(magit . "2.12.0") |
|
90 |
:group 'magit-refs |
|
91 |
:type 'boolean) |
|
92 |
|
|
93 |
(defvar magit-refs-show-push-remote nil |
|
94 |
"Whether to show the push-remotes of local branches. |
|
95 |
Also show the commits that the local branch is ahead and behind |
|
96 |
the push-target. Unfortunately there is a bug in Git that makes |
|
97 |
this useless (the commits ahead and behind the upstream are |
|
98 |
shown), so this isn't enabled yet.") |
|
99 |
|
|
100 |
(defcustom magit-refs-show-remote-prefix nil |
|
101 |
"Whether to show the remote prefix in lists of remote branches. |
|
102 |
|
|
103 |
This is redundant because the name of the remote is already shown |
|
104 |
in the heading preceeding the list of its branches." |
|
105 |
:package-version '(magit . "2.12.0") |
|
106 |
:group 'magit-refs |
|
107 |
:type 'boolean) |
|
108 |
|
|
109 |
(defcustom magit-refs-margin |
|
110 |
(list nil |
|
111 |
(nth 1 magit-log-margin) |
|
112 |
'magit-log-margin-width nil |
|
113 |
(nth 4 magit-log-margin)) |
|
114 |
"Format of the margin in `magit-refs-mode' buffers. |
|
115 |
|
|
116 |
The value has the form (INIT STYLE WIDTH AUTHOR AUTHOR-WIDTH). |
|
117 |
|
|
118 |
If INIT is non-nil, then the margin is shown initially. |
|
119 |
STYLE controls how to format the committer date. It can be one |
|
120 |
of `age' (to show the age of the commit), `age-abbreviated' (to |
|
121 |
abbreviate the time unit to a character), or a string (suitable |
|
122 |
for `format-time-string') to show the actual date. |
|
123 |
WIDTH controls the width of the margin. This exists for forward |
|
124 |
compatibility and currently the value should not be changed. |
|
125 |
AUTHOR controls whether the name of the author is also shown by |
|
126 |
default. |
|
127 |
AUTHOR-WIDTH has to be an integer. When the name of the author |
|
128 |
is shown, then this specifies how much space is used to do so." |
|
129 |
:package-version '(magit . "2.9.0") |
|
130 |
:group 'magit-refs |
|
131 |
:group 'magit-margin |
|
132 |
:safe (lambda (val) (memq val '(all branch nil))) |
|
133 |
:type magit-log-margin--custom-type |
|
134 |
:initialize 'magit-custom-initialize-reset |
|
135 |
:set-after '(magit-log-margin) |
|
136 |
:set (apply-partially #'magit-margin-set-variable 'magit-refs-mode)) |
|
137 |
|
|
138 |
(defcustom magit-refs-margin-for-tags nil |
|
139 |
"Whether to show information about tags in the margin. |
|
140 |
|
|
141 |
This is disabled by default because it is slow if there are many |
|
142 |
tags." |
|
143 |
:package-version '(magit . "2.9.0") |
|
144 |
:group 'magit-refs |
|
145 |
:group 'magit-margin |
|
146 |
:type 'boolean) |
|
147 |
|
|
148 |
(defcustom magit-refs-primary-column-width (cons 16 32) |
|
149 |
"Width of the focus column in `magit-refs-mode' buffers. |
|
150 |
|
|
151 |
The primary column is the column that contains the name of the |
|
152 |
branch that the current row is about. |
|
153 |
|
|
154 |
If this is an integer, then the column is that many columns wide. |
|
155 |
Otherwise it has to be a cons-cell of two integers. The first |
|
156 |
specifies the minimal width, the second the maximal width. In that |
|
157 |
case the actual width is determined using the length of the names |
|
158 |
of the shown local branches. (Remote branches and tags are not |
|
159 |
taken into account when calculating to optimal width.)" |
|
160 |
:package-version '(magit . "2.12.0") |
|
161 |
:group 'magit-refs |
|
162 |
:type '(choice (integer :tag "Constant wide") |
|
163 |
(cons :tag "Wide constrains" |
|
164 |
(integer :tag "Minimum") |
|
165 |
(integer :tag "Maximum")))) |
|
166 |
|
|
167 |
(defcustom magit-refs-focus-column-width 5 |
|
168 |
"Width of the focus column in `magit-refs-mode' buffers. |
|
169 |
|
|
170 |
The focus column is the first column, which marks one |
|
171 |
branch (usually the current branch) as the focused branch using |
|
172 |
\"*\" or \"@\". For each other reference, this column optionally |
|
173 |
shows how many commits it is ahead of the focused branch and \"<\", or |
|
174 |
if it isn't ahead then the commits it is behind and \">\", or if it |
|
175 |
isn't behind either, then a \"=\". |
|
176 |
|
|
177 |
This column may also display only \"*\" or \"@\" for the focused |
|
178 |
branch, in which case this option is ignored. Use \"L v\" to |
|
179 |
change the verbosity of this column." |
|
180 |
:package-version '(magit . "2.12.0") |
|
181 |
:group 'magit-refs |
|
182 |
:type 'integer) |
|
183 |
|
|
184 |
(defcustom magit-refs-filter-alist nil |
|
185 |
"Alist controlling which refs are omitted from `magit-refs-mode' buffers. |
|
186 |
|
|
187 |
All keys are tried in order until one matches. Then its value |
|
188 |
is used and subsequent elements are ignored. If the value is |
|
189 |
non-nil, then the reference is displayed, otherwise it is not. |
|
190 |
If no element matches, then the reference is displayed. |
|
191 |
|
|
192 |
A key can either be a regular expression that the refname has |
|
193 |
to match, or a function that takes the refname as only argument |
|
194 |
and returns a boolean. Contrary to how they are displayed in |
|
195 |
the buffer, for comparison each tag begins with \"tags/\" and |
|
196 |
each remote branch with \"<remote>/\"." |
|
197 |
:package-version '(magit . "2.12.0") |
|
198 |
:group 'magit-refs |
|
199 |
:type '(alist :key-type (choice :tag "Key" regexp function) |
|
200 |
:value-type (boolean :tag "Value" |
|
201 |
:on "show (non-nil)" |
|
202 |
:off "omit (nil)"))) |
|
203 |
|
|
204 |
(defcustom magit-visit-ref-behavior nil |
|
205 |
"Control how `magit-visit-ref' behaves in `magit-refs-mode' buffers. |
|
206 |
|
|
207 |
By default `magit-visit-ref' behaves like `magit-show-commit', |
|
208 |
in all buffers, including `magit-refs-mode' buffers. When the |
|
209 |
type of the section at point is `commit' then \"RET\" is bound to |
|
210 |
`magit-show-commit', and when the type is either `branch' or |
|
211 |
`tag' then it is bound to `magit-visit-ref'. |
|
212 |
|
|
213 |
\"RET\" is one of Magit's most essential keys and at least by |
|
214 |
default it should behave consistently across all of Magit, |
|
215 |
especially because users quickly learn that it does something |
|
216 |
very harmless; it shows more information about the thing at point |
|
217 |
in another buffer. |
|
218 |
|
|
219 |
However \"RET\" used to behave differently in `magit-refs-mode' |
|
220 |
buffers, doing surprising things, some of which cannot really be |
|
221 |
described as \"visit this thing\". If you have grown accustomed |
|
222 |
to such inconsistent, but to you useful, behavior, then you can |
|
223 |
restore that by adding one or more of the below symbols to the |
|
224 |
value of this option. But keep in mind that by doing so you |
|
225 |
don't only introduce inconsistencies, you also lose some |
|
226 |
functionality and might have to resort to `M-x magit-show-commit' |
|
227 |
to get it back. |
|
228 |
|
|
229 |
`magit-visit-ref' looks for these symbols in the order in which |
|
230 |
they are described here. If the presence of a symbol applies to |
|
231 |
the current situation, then the symbols that follow do not affect |
|
232 |
the outcome. |
|
233 |
|
|
234 |
`focus-on-ref' |
|
235 |
|
|
236 |
With a prefix argument update the buffer to show commit counts |
|
237 |
and lists of cherry commits relative to the reference at point |
|
238 |
instead of relative to the current buffer or `HEAD'. |
|
239 |
|
|
240 |
Instead of adding this symbol, consider pressing \"C-u y o RET\". |
|
241 |
|
|
242 |
`create-branch' |
|
243 |
|
|
244 |
If point is on a remote branch, then create a new local branch |
|
245 |
with the same name, use the remote branch as its upstream, and |
|
246 |
then check out the local branch. |
|
247 |
|
|
248 |
Instead of adding this symbol, consider pressing \"b c RET RET\", |
|
249 |
like you would do in other buffers. |
|
250 |
|
|
251 |
`checkout-any' |
|
252 |
|
|
253 |
Check out the reference at point. If that reference is a tag |
|
254 |
or a remote branch, then this results in a detached `HEAD'. |
|
255 |
|
|
256 |
Instead of adding this symbol, consider pressing \"b b RET\", |
|
257 |
like you would do in other buffers. |
|
258 |
|
|
259 |
`checkout-branch' |
|
260 |
|
|
261 |
Check out the local branch at point. |
|
262 |
|
|
263 |
Instead of adding this symbol, consider pressing \"b b RET\", |
|
264 |
like you would do in other buffers." |
|
265 |
:package-version '(magit . "2.9.0") |
|
266 |
:group 'magit-refs |
|
267 |
:group 'magit-commands |
|
268 |
:options '(focus-on-ref create-branch checkout-any checkout-branch) |
|
269 |
:type '(list :convert-widget custom-hook-convert-widget)) |
|
270 |
|
|
271 |
;;; Mode |
|
272 |
|
|
273 |
(defvar magit-refs-mode-map |
|
274 |
(let ((map (make-sparse-keymap))) |
|
275 |
(set-keymap-parent map magit-mode-map) |
|
276 |
(define-key map "\C-y" 'magit-refs-set-show-commit-count) |
|
277 |
(define-key map "L" 'magit-margin-popup) |
|
278 |
map) |
|
279 |
"Keymap for `magit-refs-mode'.") |
|
280 |
|
|
281 |
(define-derived-mode magit-refs-mode magit-mode "Magit Refs" |
|
282 |
"Mode which lists and compares references. |
|
283 |
|
|
284 |
This mode is documented in info node `(magit)References Buffer'. |
|
285 |
|
|
286 |
\\<magit-mode-map>\ |
|
287 |
Type \\[magit-refresh] to refresh the current buffer. |
|
288 |
Type \\[magit-section-toggle] to expand or hide the section at point. |
|
289 |
Type \\[magit-visit-thing] or \\[magit-diff-show-or-scroll-up] \ |
|
290 |
to visit the commit or branch at point. |
|
291 |
|
|
292 |
Type \\[magit-branch-popup] to see available branch commands. |
|
293 |
Type \\[magit-merge-popup] to merge the branch or commit at point. |
|
294 |
Type \\[magit-cherry-pick-popup] to apply the commit at point. |
|
295 |
Type \\[magit-reset] to reset `HEAD' to the commit at point. |
|
296 |
|
|
297 |
\\{magit-refs-mode-map}" |
|
298 |
:group 'magit-refs |
|
299 |
(hack-dir-local-variables-non-file-buffer) |
|
300 |
(setq imenu-create-index-function |
|
301 |
#'magit-imenu--refs-create-index-function) |
|
302 |
(setq-local bookmark-make-record-function |
|
303 |
#'magit-bookmark--refs-make-record)) |
|
304 |
|
|
305 |
(defun magit-refs-refresh-buffer (ref &optional args) |
|
306 |
(setq magit-set-buffer-margin-refresh (not (magit-buffer-margin-p))) |
|
307 |
(unless ref |
|
308 |
(setq ref "HEAD")) |
|
309 |
(unless (magit-rev-verify ref) |
|
310 |
(setq magit-refs-show-commit-count nil)) |
|
311 |
(magit-set-header-line-format |
|
312 |
(format "%s %s" ref (mapconcat #'identity args " "))) |
|
313 |
(magit-insert-section (branchbuf) |
|
314 |
(magit-run-section-hook 'magit-refs-sections-hook)) |
|
315 |
(add-hook 'kill-buffer-hook 'magit-preserve-section-visibility-cache)) |
|
316 |
|
|
317 |
;;; Commands |
|
318 |
|
|
319 |
(defcustom magit-show-refs-arguments nil |
|
320 |
"The arguments used in `magit-refs-mode' buffers." |
|
321 |
:group 'magit-git-arguments |
|
322 |
:group 'magit-refs |
|
323 |
:type '(repeat (string :tag "Argument"))) |
|
324 |
|
|
325 |
(defvar magit-show-refs-popup |
|
326 |
(list |
|
327 |
:variable 'magit-show-refs-arguments |
|
328 |
:man-page "git-branch" |
|
329 |
:switches '((?m "Merged to HEAD" "--merged") |
|
330 |
(?M "Merged to master" "--merged=master") |
|
331 |
(?n "Not merged to HEAD" "--no-merged") |
|
332 |
(?N "Not merged to master" "--no-merged=master")) |
|
333 |
:options '((?c "Contains" "--contains=" magit-read-branch-or-commit) |
|
334 |
(?m "Merged" "--merged=" magit-read-branch-or-commit) |
|
335 |
(?n "Not merged" "--no-merged=" magit-read-branch-or-commit) |
|
336 |
(?s "Sort" "--sort=" magit-read-ref-sort)) |
|
337 |
:actions '((?y "Show refs, comparing them with HEAD" |
|
338 |
magit-show-refs-head) |
|
339 |
(?c "Show refs, comparing them with current branch" |
|
340 |
magit-show-refs-current) |
|
341 |
(?o "Show refs, comparing them with other branch" |
|
342 |
magit-show-refs)) |
|
343 |
:default-action 'magit-show-refs-head |
|
344 |
:max-action-columns 1 |
|
345 |
:use-prefix (lambda () |
|
346 |
(if (derived-mode-p 'magit-refs-mode) |
|
347 |
(if current-prefix-arg 'popup 'default) |
|
348 |
'popup)))) |
|
349 |
|
|
350 |
(magit-define-popup-keys-deferred 'magit-show-refs-popup) |
|
351 |
|
|
352 |
(defun magit-read-ref-sort (prompt initial-input) |
|
353 |
(magit-completing-read prompt |
|
354 |
'("-committerdate" "-authordate" |
|
355 |
"committerdate" "authordate") |
|
356 |
nil nil initial-input)) |
|
357 |
|
|
358 |
(defun magit-show-refs-get-buffer-args () |
|
359 |
(cond ((and magit-use-sticky-arguments |
|
360 |
(derived-mode-p 'magit-refs-mode)) |
|
361 |
(cadr magit-refresh-args)) |
|
362 |
((and (eq magit-use-sticky-arguments t) |
|
363 |
(--when-let (magit-mode-get-buffer 'magit-refs-mode) |
|
364 |
(with-current-buffer it |
|
365 |
(cadr magit-refresh-args))))) |
|
366 |
(t |
|
367 |
(default-value 'magit-show-refs-arguments)))) |
|
368 |
|
|
369 |
(defun magit-show-refs-arguments () |
|
370 |
(if (eq magit-current-popup 'magit-show-refs-popup) |
|
371 |
magit-current-popup-args |
|
372 |
(magit-show-refs-get-buffer-args))) |
|
373 |
|
|
374 |
;;;###autoload |
|
375 |
(defun magit-show-refs-popup (&optional arg) |
|
376 |
"Popup console for `magit-show-refs'." |
|
377 |
(interactive "P") |
|
378 |
(let ((magit-show-refs-arguments (magit-show-refs-get-buffer-args))) |
|
379 |
(magit-invoke-popup 'magit-show-refs-popup nil arg))) |
|
380 |
|
|
381 |
;;;###autoload |
|
382 |
(defun magit-show-refs-head (&optional args) |
|
383 |
"List and compare references in a dedicated buffer. |
|
384 |
Refs are compared with `HEAD'." |
|
385 |
(interactive (list (magit-show-refs-arguments))) |
|
386 |
(magit-show-refs nil args)) |
|
387 |
|
|
388 |
;;;###autoload |
|
389 |
(defun magit-show-refs-current (&optional args) |
|
390 |
"List and compare references in a dedicated buffer. |
|
391 |
Refs are compared with the current branch or `HEAD' if |
|
392 |
it is detached." |
|
393 |
(interactive (list (magit-show-refs-arguments))) |
|
394 |
(magit-show-refs (magit-get-current-branch) args)) |
|
395 |
|
|
396 |
;;;###autoload |
|
397 |
(defun magit-show-refs (&optional ref args) |
|
398 |
"List and compare references in a dedicated buffer. |
|
399 |
Refs are compared with a branch read from the user." |
|
400 |
(interactive (list (magit-read-other-branch "Compare with") |
|
401 |
(magit-show-refs-arguments))) |
|
402 |
(magit-mode-setup #'magit-refs-mode ref args)) |
|
403 |
|
|
404 |
(defun magit-refs-set-show-commit-count () |
|
405 |
"Change for which refs the commit count is shown." |
|
406 |
(interactive) |
|
407 |
(setq-local magit-refs-show-commit-count |
|
408 |
(magit-read-char-case "Show commit counts for " nil |
|
409 |
(?a "[a]ll refs" 'all) |
|
410 |
(?b "[b]ranches only" t) |
|
411 |
(?n "[n]othing" nil))) |
|
412 |
(magit-refresh)) |
|
413 |
|
|
414 |
(defun magit-visit-ref () |
|
415 |
"Visit the reference or revision at point in another buffer. |
|
416 |
If there is no revision at point or with a prefix argument prompt |
|
417 |
for a revision. |
|
418 |
|
|
419 |
This command behaves just like `magit-show-commit', except if |
|
420 |
point is on a reference in a `magit-refs-mode' buffer (a buffer |
|
421 |
listing branches and tags), in which case the behavior may be |
|
422 |
different, but only if you have customized the option |
|
423 |
`magit-visit-ref-behavior' (which see)." |
|
424 |
(interactive) |
|
425 |
(if (and (derived-mode-p 'magit-refs-mode) |
|
426 |
(magit-section-match '(branch tag))) |
|
427 |
(let ((ref (oref (magit-current-section) value))) |
|
428 |
(cond (current-prefix-arg |
|
429 |
(cond ((memq 'focus-on-ref magit-visit-ref-behavior) |
|
430 |
(magit-show-refs ref)) |
|
431 |
(magit-visit-ref-behavior |
|
432 |
;; Don't prompt for commit to visit. |
|
433 |
(let ((current-prefix-arg nil)) |
|
434 |
(call-interactively #'magit-show-commit))))) |
|
435 |
((and (memq 'create-branch magit-visit-ref-behavior) |
|
436 |
(magit-section-match [branch remote])) |
|
437 |
(let ((branch (cdr (magit-split-branch-name ref)))) |
|
438 |
(if (magit-branch-p branch) |
|
439 |
(if (magit-rev-eq branch ref) |
|
440 |
(magit-call-git "checkout" branch) |
|
441 |
(setq branch (propertize branch 'face 'magit-branch-local)) |
|
442 |
(setq ref (propertize ref 'face 'magit-branch-remote)) |
|
443 |
(pcase (prog1 (read-char-choice (format (propertize "\ |
|
444 |
Branch %s already exists. |
|
445 |
[c]heckout %s as-is |
|
446 |
[r]reset %s to %s and checkout %s |
|
447 |
[a]bort " 'face 'minibuffer-prompt) branch branch branch ref branch) |
|
448 |
'(?c ?r ?a)) |
|
449 |
(message "")) ; otherwise prompt sticks |
|
450 |
(?c (magit-call-git "checkout" branch)) |
|
451 |
(?r (magit-call-git "checkout" "-B" branch ref)) |
|
452 |
(?a (user-error "Abort")))) |
|
453 |
(magit-call-git "checkout" "-b" branch ref)) |
|
454 |
(setcar magit-refresh-args branch) |
|
455 |
(magit-refresh))) |
|
456 |
((or (memq 'checkout-any magit-visit-ref-behavior) |
|
457 |
(and (memq 'checkout-branch magit-visit-ref-behavior) |
|
458 |
(magit-section-match [branch local]))) |
|
459 |
(magit-call-git "checkout" ref) |
|
460 |
(setcar magit-refresh-args ref) |
|
461 |
(magit-refresh)) |
|
462 |
(t |
|
463 |
(call-interactively #'magit-show-commit)))) |
|
464 |
(call-interactively #'magit-show-commit))) |
|
465 |
|
|
466 |
;;; Sections |
|
467 |
|
|
468 |
(defvar magit-remote-section-map |
|
469 |
(let ((map (make-sparse-keymap))) |
|
470 |
(define-key map [remap magit-delete-thing] 'magit-remote-remove) |
|
471 |
(define-key map "R" 'magit-remote-rename) |
|
472 |
map) |
|
473 |
"Keymap for `remote' sections.") |
|
474 |
|
|
475 |
(defvar magit-branch-section-map |
|
476 |
(let ((map (make-sparse-keymap))) |
|
477 |
(define-key map [remap magit-visit-thing] 'magit-visit-ref) |
|
478 |
(define-key map [remap magit-delete-thing] 'magit-branch-delete) |
|
479 |
(define-key map "R" 'magit-branch-rename) |
|
480 |
map) |
|
481 |
"Keymap for `branch' sections.") |
|
482 |
|
|
483 |
(defvar magit-tag-section-map |
|
484 |
(let ((map (make-sparse-keymap))) |
|
485 |
(define-key map [remap magit-visit-thing] 'magit-visit-ref) |
|
486 |
(define-key map [remap magit-delete-thing] 'magit-tag-delete) |
|
487 |
map) |
|
488 |
"Keymap for `tag' sections.") |
|
489 |
|
|
490 |
(defun magit-insert-branch-description () |
|
491 |
"Insert header containing the description of the current branch. |
|
492 |
Insert a header line with the name and description of the |
|
493 |
current branch. The description is taken from the Git variable |
|
494 |
`branch.<NAME>.description'; if that is undefined then no header |
|
495 |
line is inserted at all." |
|
496 |
(when-let ((branch (magit-get-current-branch)) |
|
497 |
(desc (magit-get "branch" branch "description")) |
|
498 |
(desc (split-string desc "\n"))) |
|
499 |
(when (equal (car (last desc)) "") |
|
500 |
(setq desc (butlast desc))) |
|
501 |
(magit-insert-section (branchdesc branch t) |
|
502 |
(magit-insert-heading branch ": " (car desc)) |
|
503 |
(when (cdr desc) |
|
504 |
(insert (mapconcat 'identity (cdr desc) "\n")) |
|
505 |
(insert "\n\n"))))) |
|
506 |
|
|
507 |
(defun magit-insert-tags () |
|
508 |
"Insert sections showing all tags." |
|
509 |
(when-let ((tags (magit-git-lines "tag" "--list" "-n" |
|
510 |
(cadr magit-refresh-args)))) |
|
511 |
(let ((_head (magit-rev-parse "HEAD"))) |
|
512 |
(magit-insert-section (tags) |
|
513 |
(magit-insert-heading "Tags:") |
|
514 |
(dolist (tag tags) |
|
515 |
(string-match "^\\([^ \t]+\\)[ \t]+\\([^ \t\n].*\\)?" tag) |
|
516 |
(let ((tag (match-string 1 tag)) |
|
517 |
(msg (match-string 2 tag))) |
|
518 |
(when (magit-refs--insert-refname-p tag) |
|
519 |
(magit-insert-section section (tag tag t) |
|
520 |
(magit-insert-heading |
|
521 |
(magit-refs--format-focus-column tag 'tag) |
|
522 |
(propertize tag 'face 'magit-tag) |
|
523 |
(make-string (max 1 (- magit-refs-primary-column-width |
|
524 |
(length tag))) |
|
525 |
?\s) |
|
526 |
(and msg (magit-log-propertize-keywords nil msg))) |
|
527 |
(when (and magit-refs-margin-for-tags (magit-buffer-margin-p)) |
|
528 |
(magit-refs--format-margin tag)) |
|
529 |
(magit-refs--insert-cherry-commits tag section))))) |
|
530 |
(insert ?\n) |
|
531 |
(magit-make-margin-overlay nil t))))) |
|
532 |
|
|
533 |
(defun magit-insert-remote-branches () |
|
534 |
"Insert sections showing all remote-tracking branches." |
|
535 |
(dolist (remote (magit-list-remotes)) |
|
536 |
(magit-insert-section (remote remote) |
|
537 |
(magit-insert-heading |
|
538 |
(let ((pull (magit-get "remote" remote "url")) |
|
539 |
(push (magit-get "remote" remote "pushurl"))) |
|
540 |
(format (propertize "Remote %s (%s):" 'face 'magit-section-heading) |
|
541 |
(propertize remote 'face 'magit-branch-remote) |
|
542 |
(concat pull (and pull push ", ") push)))) |
|
543 |
(let (head) |
|
544 |
(dolist (line (magit-git-lines "for-each-ref" "--format=\ |
|
545 |
%(symref:short)%00%(refname:short)%00%(refname)%00%(subject)" |
|
546 |
(concat "refs/remotes/" remote) |
|
547 |
(cadr magit-refresh-args))) |
|
548 |
(pcase-let ((`(,head-branch ,branch ,ref ,msg) |
|
549 |
(-replace "" nil (split-string line "\0")))) |
|
550 |
(if head-branch |
|
551 |
(progn (cl-assert (equal branch (concat remote "/HEAD"))) |
|
552 |
(setq head head-branch)) |
|
553 |
(when (magit-refs--insert-refname-p branch) |
|
554 |
(magit-insert-section section (branch branch t) |
|
555 |
(let ((headp (equal branch head)) |
|
556 |
(abbrev (if magit-refs-show-remote-prefix |
|
557 |
branch |
|
558 |
(substring branch (1+ (length remote)))))) |
|
559 |
(magit-insert-heading |
|
560 |
(magit-refs--format-focus-column branch) |
|
561 |
(magit-refs--propertize-branch |
|
562 |
abbrev ref (and headp 'magit-branch-remote-head)) |
|
563 |
(make-string (max 1 (- magit-refs-primary-column-width |
|
564 |
(length abbrev))) |
|
565 |
?\s) |
|
566 |
(and msg (magit-log-propertize-keywords nil msg)))) |
|
567 |
(when (magit-buffer-margin-p) |
|
568 |
(magit-refs--format-margin branch)) |
|
569 |
(magit-refs--insert-cherry-commits branch section))))))) |
|
570 |
(insert ?\n) |
|
571 |
(magit-make-margin-overlay nil t)))) |
|
572 |
|
|
573 |
(defun magit-insert-local-branches () |
|
574 |
"Insert sections showing all local branches." |
|
575 |
(magit-insert-section (local nil) |
|
576 |
(magit-insert-heading "Branches:") |
|
577 |
(dolist (line (magit-refs--format-local-branches)) |
|
578 |
(pcase-let ((`(,branch . ,strings) line)) |
|
579 |
(magit-insert-section section |
|
580 |
((eval (if branch 'branch 'commit)) |
|
581 |
(or branch (magit-rev-parse "HEAD")) |
|
582 |
t) |
|
583 |
(apply #'magit-insert-heading strings) |
|
584 |
(when (magit-buffer-margin-p) |
|
585 |
(magit-refs--format-margin branch)) |
|
586 |
(magit-refs--insert-cherry-commits branch section)))) |
|
587 |
(insert ?\n) |
|
588 |
(magit-make-margin-overlay nil t))) |
|
589 |
|
|
590 |
(defun magit-refs--format-local-branches () |
|
591 |
(let ((lines (-keep 'magit-refs--format-local-branch |
|
592 |
(magit-git-lines |
|
593 |
"for-each-ref" |
|
594 |
(concat "--format=\ |
|
595 |
%(HEAD)%00%(refname:short)%00%(refname)%00\ |
|
596 |
%(upstream:short)%00%(upstream)%00%(upstream:track)%00" |
|
597 |
(if magit-refs-show-push-remote "\ |
|
598 |
%(push:remotename)%00%(push)%00%(push:track)%00%(subject)" |
|
599 |
"%00%00%00%(subject)")) |
|
600 |
"refs/heads" |
|
601 |
(cadr magit-refresh-args))))) |
|
602 |
(unless (magit-get-current-branch) |
|
603 |
(push (magit-refs--format-local-branch |
|
604 |
(concat "*\0\0\0\0\0\0\0\0" (magit-rev-format "%s"))) |
|
605 |
lines)) |
|
606 |
(setq-local magit-refs-primary-column-width |
|
607 |
(let ((def (default-value 'magit-refs-primary-column-width))) |
|
608 |
(if (atom def) |
|
609 |
def |
|
610 |
(pcase-let ((`(,min . ,max) def)) |
|
611 |
(min max (apply #'max min (mapcar #'car lines))))))) |
|
612 |
(mapcar (pcase-lambda (`(,_ ,branch ,focus ,branch-desc ,u:ahead ,p:ahead |
|
613 |
,u:behind ,upstream ,p:behind ,push ,msg)) |
|
614 |
(list branch focus branch-desc u:ahead p:ahead |
|
615 |
(make-string (max 1 (- magit-refs-primary-column-width |
|
616 |
(length (concat branch-desc |
|
617 |
u:ahead |
|
618 |
p:ahead |
|
619 |
u:behind)))) |
|
620 |
?\s) |
|
621 |
u:behind upstream p:behind push |
|
622 |
msg)) |
|
623 |
lines))) |
|
624 |
|
|
625 |
(defun magit-refs--format-local-branch (line) |
|
626 |
(pcase-let ((`(,head ,branch ,ref ,upstream ,u:ref ,u:track |
|
627 |
,push ,p:ref ,p:track ,msg) |
|
628 |
(-replace "" nil (split-string line "\0")))) |
|
629 |
(when (or (not branch) |
|
630 |
(magit-refs--insert-refname-p branch)) |
|
631 |
(let* ((headp (equal head "*")) |
|
632 |
(pushp (and push |
|
633 |
magit-refs-show-push-remote |
|
634 |
(magit-rev-verify p:ref) |
|
635 |
(not (equal p:ref u:ref)))) |
|
636 |
(branch-desc |
|
637 |
(if branch |
|
638 |
(magit-refs--propertize-branch |
|
639 |
branch ref (and headp 'magit-branch-current)) |
|
640 |
(propertize "(detached)" 'face 'font-lock-warning-face))) |
|
641 |
(u:ahead (and u:track |
|
642 |
(string-match "ahead \\([0-9]+\\)" u:track) |
|
643 |
(propertize |
|
644 |
(concat (and magit-refs-pad-commit-counts " ") |
|
645 |
(match-string 1 u:track) |
|
646 |
">") |
|
647 |
'face 'magit-dimmed))) |
|
648 |
(u:behind (and u:track |
|
649 |
(string-match "behind \\([0-9]+\\)" u:track) |
|
650 |
(propertize |
|
651 |
(concat "<" |
|
652 |
(match-string 1 u:track) |
|
653 |
(and magit-refs-pad-commit-counts " ")) |
|
654 |
'face 'magit-dimmed))) |
|
655 |
(p:ahead (and pushp p:track |
|
656 |
(string-match "ahead \\([0-9]+\\)" p:track) |
|
657 |
(propertize |
|
658 |
(concat (match-string 1 p:track) |
|
659 |
">" |
|
660 |
(and magit-refs-pad-commit-counts " ")) |
|
661 |
'face 'magit-branch-remote))) |
|
662 |
(p:behind (and pushp p:track |
|
663 |
(string-match "behind \\([0-9]+\\)" p:track) |
|
664 |
(propertize |
|
665 |
(concat "<" |
|
666 |
(match-string 1 p:track) |
|
667 |
(and magit-refs-pad-commit-counts " ")) |
|
668 |
'face 'magit-dimmed)))) |
|
669 |
(list (1+ (length (concat branch-desc u:ahead p:ahead u:behind))) |
|
670 |
branch |
|
671 |
(magit-refs--format-focus-column branch headp) |
|
672 |
branch-desc u:ahead p:ahead u:behind |
|
673 |
(and upstream |
|
674 |
(concat (if (equal u:track "[gone]") |
|
675 |
(propertize upstream 'face 'error) |
|
676 |
(magit-refs--propertize-branch upstream u:ref)) |
|
677 |
" ")) |
|
678 |
(and pushp |
|
679 |
(concat p:behind |
|
680 |
(propertize push 'face 'magit-branch-remote) |
|
681 |
" ")) |
|
682 |
(and msg (magit-log-propertize-keywords nil msg))))))) |
|
683 |
|
|
684 |
(defun magit-refs--format-focus-column (ref &optional type) |
|
685 |
(let ((focus (car magit-refresh-args)) |
|
686 |
(width (if magit-refs-show-commit-count |
|
687 |
magit-refs-focus-column-width |
|
688 |
1))) |
|
689 |
(format |
|
690 |
(format "%%%ss " width) |
|
691 |
(cond ((or (equal ref focus) |
|
692 |
(and (eq type t) |
|
693 |
(eq focus nil))) |
|
694 |
(propertize (concat (if focus "@" "*") |
|
695 |
(make-string (1- width) ?\s)) |
|
696 |
'face 'magit-section-heading)) |
|
697 |
((if (eq type 'tag) |
|
698 |
(eq magit-refs-show-commit-count 'all) |
|
699 |
magit-refs-show-commit-count) |
|
700 |
(pcase-let ((`(,behind ,ahead) |
|
701 |
(magit-rev-diff-count |
|
702 |
(or (car magit-refresh-args) "HEAD") |
|
703 |
ref))) |
|
704 |
(propertize |
|
705 |
(cond ((> ahead 0) (concat "<" (number-to-string ahead))) |
|
706 |
((> behind 0) (concat (number-to-string behind) ">")) |
|
707 |
(t "=")) |
|
708 |
'face 'magit-dimmed))) |
|
709 |
(t ""))))) |
|
710 |
|
|
711 |
(defun magit-refs--propertize-branch (branch ref &optional head-face) |
|
712 |
(let ((face (cdr (cl-find-if (pcase-lambda (`(,re . ,_)) |
|
713 |
(string-match-p re ref)) |
|
714 |
magit-ref-namespaces)))) |
|
715 |
(propertize branch 'face (if head-face (list face head-face) face)))) |
|
716 |
|
|
717 |
(defun magit-refs--insert-refname-p (refname) |
|
718 |
(--if-let (-first (pcase-lambda (`(,key . ,_)) |
|
719 |
(if (functionp key) |
|
720 |
(funcall key refname) |
|
721 |
(string-match-p key refname))) |
|
722 |
magit-refs-filter-alist) |
|
723 |
(cdr it) |
|
724 |
t)) |
|
725 |
|
|
726 |
(defun magit-refs--insert-cherry-commits (ref section) |
|
727 |
(if (oref section hidden) |
|
728 |
(oset section washer |
|
729 |
(apply-partially #'magit-refs--insert-cherry-commits-1 ref section)) |
|
730 |
(magit-refs--insert-cherry-commits-1 ref section))) |
|
731 |
|
|
732 |
(defun magit-refs--insert-cherry-commits-1 (ref _section) |
|
733 |
(let ((start (point)) |
|
734 |
(magit-insert-section--current nil)) |
|
735 |
(magit-git-wash (apply-partially 'magit-log-wash-log 'cherry) |
|
736 |
"cherry" "-v" (magit-abbrev-arg) |
|
737 |
(or (car magit-refresh-args) "HEAD") |
|
738 |
ref magit-refresh-args) |
|
739 |
(unless (= (point) start) |
|
740 |
(magit-make-margin-overlay nil t)))) |
|
741 |
|
|
742 |
(defun magit-refs--format-margin (commit) |
|
743 |
(save-excursion |
|
744 |
(goto-char (line-beginning-position 0)) |
|
745 |
(let ((line (magit-rev-format "%ct%cN" commit))) |
|
746 |
(magit-log-format-margin commit |
|
747 |
(substring line 10) |
|
748 |
(substring line 0 10))))) |
|
749 |
|
|
750 |
;;; _ |
|
751 |
(provide 'magit-refs) |
|
752 |
;;; magit-refs.el ends here |