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

Chizi123
2018-11-18 21067e7cbe6d7a0f65ff5c317a96b5c337b0b3d8
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