fix name in function
[magit.git] / magit.el
blob02dad42a9d981f602ee55e8777aae343dd79d2bf
1 ;;; Magit -- control Git from Emacs.
3 ;; Copyright (C) 2008 Marius Vollmer
4 ;; Copyright (C) 2008 Linh Dang
5 ;;
6 ;; Magit is free software; you can redistribute it and/or modify it
7 ;; under the terms of the GNU General Public License as published by
8 ;; the Free Software Foundation; either version 3, or (at your option)
9 ;; any later version.
11 ;; Magit is distributed in the hope that it will be useful, but WITHOUT
12 ;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 ;; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
14 ;; License for more details.
16 ;; You should have received a copy of the GNU General Public License
17 ;; along with Magit. If not, see <http://www.gnu.org/licenses/>.
19 ;;; Commentary
21 ;; Invoking the magit-status function will show a buffer with the
22 ;; status of the current git repository and its working tree. That
23 ;; buffer offers key bindings for manipulating the status in simple
24 ;; ways.
26 ;; The status buffer mainly shows the difference between the working
27 ;; tree and the index, and the difference between the index and the
28 ;; current HEAD. You can add individual hunks from the working tree
29 ;; to the index, and you can commit the index.
31 ;; See the Magit User Manual for more information.
33 ;;; TODO
35 ;; - Showing tags
36 ;; - Amending commits other than HEAD.
37 ;; - Visiting from staged hunks doesn't always work since the line
38 ;; numbers don't refer to the working tree. Fix that somehow.
39 ;; - Get current defun from removed lines in a diff
40 ;; - Equivalent of git-wtf, http://git-wt-commit.rubyforge.org/#git-wtf
41 ;; - 'Subsetting', only looking at a subset of all files.
43 (require 'cl)
44 (require 'parse-time)
45 (require 'log-edit)
46 (require 'easymenu)
47 (require 'diff-mode)
49 (defgroup magit nil
50 "Controlling Git from Emacs."
51 :prefix "magit-"
52 :group 'tools)
54 (defcustom magit-collapse-threshold 50
55 "Sections with more lines than this are collapsed automatically."
56 :group 'magit
57 :type '(integer))
59 (defface magit-header
60 '((t))
61 "Face for generic header lines.
63 Many Magit faces inherit from this one by default."
64 :group 'magit)
66 (defface magit-section-title
67 '((t :weight bold :inherit magit-header))
68 "Face for section titles."
69 :group 'magit)
71 (defface magit-branch
72 '((t :weight bold :inherit magit-header))
73 "Face for the current branch."
74 :group 'magit)
76 (defface magit-diff-file-header
77 '((t :inherit magit-header))
78 "Face for diff file header lines."
79 :group 'magit)
81 (defface magit-diff-hunk-header
82 '((t :slant italic :inherit magit-header))
83 "Face for diff hunk header lines."
84 :group 'magit)
86 (defface magit-diff-add
87 '((((class color) (background light))
88 :foreground "blue1")
89 (((class color) (background dark))
90 :foreground "white"))
91 "Face for lines in a diff that have been added."
92 :group 'magit)
94 (defface magit-diff-none
95 '((t))
96 "Face for lines in a diff that are unchanged."
97 :group 'magit)
99 (defface magit-diff-del
100 '((((class color) (background light))
101 :foreground "red")
102 (((class color) (background dark))
103 :foreground "OrangeRed"))
104 "Face for lines in a diff that have been deleted."
105 :group 'magit)
107 (defface magit-item-highlight
108 '((((class color) (background light))
109 :background "gray95")
110 (((class color) (background dark))
111 :background "dim gray"))
112 "Face for highlighting the current item."
113 :group 'magit)
115 (defface magit-log-tag-label
116 '((((class color) (background light))
117 :background "LightGoldenRod")
118 (((class color) (background dark))
119 :background "DarkGoldenRod"))
120 "Face for git tag labels shown in log buffer."
121 :group 'magit)
123 (defface magit-log-head-label
124 '((((class color) (background light))
125 :background "spring green")
126 (((class color) (background dark))
127 :background "DarkGreen"))
128 "Face for branch head labels shown in log buffer."
129 :group 'magit)
131 ;;; Macros
133 (defmacro magit-with-refresh (&rest body)
134 (declare (indent 0))
135 `(magit-refresh-wrapper (lambda () ,@body)))
137 ;;; Utilities
139 (defun magit-use-region-p ()
140 (if (fboundp 'use-region-p)
141 (use-region-p)
142 (and transient-mark-mode mark-active)))
144 (defun magit-goto-line (line)
145 ;; Like goto-line but doesn't set the mark.
146 (save-restriction
147 (widen)
148 (goto-char 1)
149 (forward-line (1- line))))
151 (defun magit-shell (cmd &rest args)
152 (let ((str (shell-command-to-string
153 (apply 'format cmd (mapcar #'magit-escape-for-shell args)))))
154 (if (string= str "")
156 (if (equal (elt str (- (length str) 1)) ?\n)
157 (substring str 0 (- (length str) 1))
158 str))))
160 (defun magit-shell-lines (cmd &rest args)
161 (let ((str (shell-command-to-string
162 (apply 'format cmd (mapcar #'magit-escape-for-shell args)))))
163 (if (string= str "")
165 (let ((lines (nreverse (split-string str "\n"))))
166 (if (string= (car lines) "")
167 (setq lines (cdr lines)))
168 (nreverse lines)))))
170 (defun magit-shell-exit-code (cmd &rest args)
171 (call-process shell-file-name nil nil nil
172 shell-command-switch
173 (apply 'format cmd (mapcar #'magit-escape-for-shell args))))
175 (defun magit-file-lines (file)
176 (if (file-exists-p file)
177 (magit-shell-lines "cat %s" file)
178 nil))
180 (defun magit-concat-with-delim (delim seqs)
181 (cond ((null seqs)
182 nil)
183 ((null (cdr seqs))
184 (car seqs))
186 (concat (car seqs) delim (magit-concat-with-delim delim (cdr seqs))))))
188 (defun magit-get (&rest keys)
189 (magit-shell "git config %s" (magit-concat-with-delim "." keys)))
191 (defun magit-set (val &rest keys)
192 (if val
193 (magit-shell "git config %s %s" (magit-concat-with-delim "." keys) val)
194 (magit-shell "git config --unset %s" (magit-concat-with-delim "." keys))))
196 (defun magit-get-top-dir (cwd)
197 (let* ((cwd (expand-file-name cwd))
198 (magit-dir (magit-shell
199 "cd %s && git rev-parse --git-dir 2>/dev/null"
200 cwd)))
201 (if magit-dir
202 (file-name-as-directory (or (file-name-directory magit-dir) cwd))
203 nil)))
205 (defun magit-get-ref (ref)
206 (magit-shell "git symbolic-ref -q %s" ref))
208 (defun magit-get-current-branch ()
209 (let* ((head (magit-get-ref "HEAD"))
210 (pos (and head (string-match "^refs/heads/" head))))
211 (if pos
212 (substring head 11)
213 nil)))
215 (defun magit-read-top-dir ()
216 (magit-get-top-dir
217 (read-directory-name "Git repository: "
218 (magit-get-top-dir default-directory))))
220 (defun magit-name-rev (rev)
221 (and rev
222 (let ((name (magit-shell "git name-rev --name-only %s" rev)))
223 (if (or (not name) (string= name "undefined"))
225 name))))
227 (defun magit-put-line-property (prop val)
228 (put-text-property (line-beginning-position) (line-beginning-position 2)
229 prop val))
231 (defun magit-escape-for-shell (str)
232 (concat "'" (replace-regexp-in-string "'" "'\\''" str) "'"))
234 (defun magit-format-commit (commit format)
235 (magit-shell "git log --max-count=1 --pretty=format:%s %s"
236 format
237 commit))
239 (defun magit-current-line ()
240 (buffer-substring-no-properties (line-beginning-position)
241 (line-end-position)))
243 (defun magit-insert-region (beg end buf)
244 (let ((text (buffer-substring-no-properties beg end)))
245 (with-current-buffer buf
246 (insert text))))
248 (defun magit-insert-current-line (buf)
249 (let ((text (buffer-substring-no-properties
250 (line-beginning-position) (line-beginning-position 2))))
251 (with-current-buffer buf
252 (insert text))))
254 (defun magit-file-uptodate-p (file)
255 (eq (magit-shell-exit-code "git diff --quiet -- %s" file) 0))
257 (defun magit-anything-staged-p ()
258 (not (eq (magit-shell-exit-code "git diff --quiet --cached") 0)))
260 (defun magit-everything-clean-p ()
261 (and (not (magit-anything-staged-p))
262 (eq (magit-shell-exit-code "git diff --quiet") 0)))
264 (defun magit-commit-parents (commit)
265 (cdr (magit-shell-lines "git rev-list -1 --parents %s | tr ' ' '\n'"
266 commit)))
268 ;; XXX - let the user choose the parent
270 (defun magit-choose-parent (commit op)
271 (let* ((parents (magit-commit-parents commit)))
272 (if (> (length parents) 1)
273 (error "Can't %s merge commits." op)
274 (car parents))))
276 (defun magit-choose-parent-id (commit op)
277 (let* ((parents (magit-commit-parents commit)))
278 (if (> (length parents) 1)
279 (error "Can't %s merge commits." op)
280 nil)))
282 ;;; Revisions and ranges
284 (defun magit-list-interesting-revisions ()
285 (append (magit-shell-lines "git branch -a | cut -c3-")
286 (magit-shell-lines "git tag")))
288 (defun magit-read-rev (prompt &optional def)
289 (let* ((prompt (if def
290 (format "%s (default %s): " prompt def)
291 (format "%s: " prompt)))
292 (rev (completing-read prompt (magit-list-interesting-revisions)
293 nil nil nil nil def)))
294 (if (string= rev "")
296 rev)))
298 (defun magit-read-rev-range (op &optional def-beg def-end)
299 (if current-prefix-arg
300 (read-string (format "%s range: " op))
301 (let ((beg (magit-read-rev (format "%s start" op)
302 def-beg)))
303 (if (not beg)
305 (let ((end (magit-read-rev (format "%s end" op) def-end)))
306 (cons beg end))))))
308 (defun magit-rev-to-git (rev)
309 (or rev
310 (error "No revision specified"))
311 (if (string= rev ".")
312 (magit-marked-commit)
313 rev))
315 (defun magit-rev-range-to-git (range)
316 (or range
317 (error "No revision range specified"))
318 (if (stringp range)
319 range
320 (if (cdr range)
321 (format "%s..%s"
322 (magit-rev-to-git (car range))
323 (magit-rev-to-git (cdr range)))
324 (format "%s" (magit-rev-to-git (car range))))))
326 (defun magit-rev-describe (rev)
327 (or rev
328 (error "No revision specified"))
329 (if (string= rev ".")
330 "mark"
331 (magit-name-rev rev)))
333 (defun magit-rev-range-describe (range things)
334 (or range
335 (error "No revision range specified"))
336 (if (stringp range)
337 (format "%s in %s" things range)
338 (if (cdr range)
339 (format "%s from %s to %s" things
340 (magit-rev-describe (car range))
341 (magit-rev-describe (cdr range)))
342 (format "%s at %s" things (magit-rev-describe (car range))))))
344 (defun magit-default-rev ()
345 (magit-name-rev (magit-commit-at-point t)))
347 ;;; Sections
349 ;; A buffer in magit-mode is organized into hierarchical sections.
350 ;; These sections are used for navigation and for hiding parts of the
351 ;; buffer.
353 ;; Most sections also represent the objects that Magit works with,
354 ;; such as files, diffs, hunks, commits, etc. The 'type' of a section
355 ;; identifies what kind of object it represents (if any), and the
356 ;; parent and grand-parent, etc provide the context.
358 (defstruct magit-section
359 parent title beginning end children hidden type info)
361 (defvar magit-top-section nil)
362 (make-variable-buffer-local 'magit-top-section)
363 (put 'magit-top-section 'permanent-local t)
365 (defvar magit-old-top-section nil)
367 (defun magit-new-section (title &rest args)
368 (let* ((s (apply #'make-magit-section
369 :parent magit-top-section
370 :title title
371 args))
372 (old (and magit-old-top-section
373 (magit-find-section (magit-section-path s)
374 magit-old-top-section))))
375 (if magit-top-section
376 (setf (magit-section-children magit-top-section)
377 (cons s (magit-section-children magit-top-section)))
378 (setq magit-top-section s))
379 (if old
380 (setf (magit-section-hidden s) (magit-section-hidden old)))
383 (defun magit-cancel-section (section)
384 (delete-region (magit-section-beginning section)
385 (magit-section-end section))
386 (let ((parent (magit-section-parent section)))
387 (if parent
388 (setf (magit-section-children parent)
389 (delq section (magit-section-children parent)))
390 (setq magit-top-section nil))))
392 (defmacro magit-with-section (title args &rest body)
393 (declare (indent 2))
394 (let ((s (gensym)))
395 `(let* ((,s (magit-new-section ,title ,@(if (keywordp (car args))
396 args
397 `(:type ,args))))
398 (magit-top-section ,s))
399 (setf (magit-section-beginning ,s) (point))
400 ,@body
401 (setf (magit-section-end ,s) (point))
402 (setf (magit-section-children ,s)
403 (nreverse (magit-section-children ,s)))
404 ,s)))
406 (defun magit-set-section-info (info &optional section)
407 (setf (magit-section-info (or section magit-top-section)) info))
409 (defmacro magit-create-buffer-sections (&rest body)
410 (declare (indent 0))
411 `(let ((inhibit-read-only t))
412 (erase-buffer)
413 (let ((magit-old-top-section magit-top-section))
414 (setq magit-top-section nil)
415 ,@body
416 (when (null magit-top-section)
417 (magit-with-section 'top nil
418 (insert "(empty)\n")))
419 (magit-propertize-section magit-top-section)
420 (magit-section-set-hidden magit-top-section
421 (magit-section-hidden magit-top-section)))))
423 (defun magit-propertize-section (section)
424 (put-text-property (magit-section-beginning section)
425 (magit-section-end section)
426 'magit-section section)
427 (dolist (s (magit-section-children section))
428 (magit-propertize-section s)))
430 (defun magit-find-section (path top)
431 (if (null path)
433 (let ((sec (find-if (lambda (s) (equal (car path)
434 (magit-section-title s)))
435 (magit-section-children top))))
436 (if sec
437 (magit-find-section (cdr path) sec)
438 nil))))
440 (defun magit-section-path (section)
441 (if (not (magit-section-parent section))
443 (append (magit-section-path (magit-section-parent section))
444 (list (magit-section-title section)))))
446 (defun magit-find-section-at (pos secs)
447 (while (and secs
448 (not (and (<= (magit-section-beginning (car secs)) pos)
449 (< pos (magit-section-end (car secs))))))
450 (setq secs (cdr secs)))
451 (if secs
452 (or (magit-find-section-at pos (magit-section-children (car secs)))
453 (car secs))
454 nil))
456 (defun magit-find-section-after (pos secs)
457 (while (and secs
458 (not (> (magit-section-beginning (car secs)) pos)))
459 (setq secs (cdr secs)))
460 (car secs))
462 (defun magit-find-section-before (pos secs)
463 (let ((prev nil))
464 (while (and secs
465 (not (> (magit-section-beginning (car secs)) pos)))
466 (setq prev (car secs))
467 (setq secs (cdr secs)))
468 prev))
470 (defun magit-current-section ()
471 (or (get-text-property (point) 'magit-section)
472 magit-top-section))
474 (defun magit-insert-section (type title washer threshold cmd &rest args)
475 (let* ((body-beg nil)
476 (section
477 (magit-with-section type nil
478 (if title
479 (insert (propertize title 'face 'magit-section-title) "\n"))
480 (setq body-beg (point))
481 (apply 'call-process cmd nil t nil args)
482 (if washer
483 (save-restriction
484 (narrow-to-region body-beg (point))
485 (goto-char (point-min))
486 (funcall washer)
487 (goto-char (point-max)))))))
488 (if (= body-beg (point))
489 (magit-cancel-section section)
490 (insert "\n"))))
492 (defun magit-next-section (section)
493 (let ((parent (magit-section-parent section)))
494 (if parent
495 (let ((next (cadr (memq section
496 (magit-section-children parent)))))
497 (or next
498 (magit-next-section parent))))))
500 (defun magit-goto-next-section ()
501 (interactive)
502 (let* ((section (magit-current-section))
503 (next (or (and (not (magit-section-hidden section))
504 (magit-section-children section)
505 (magit-find-section-after (point)
506 (magit-section-children
507 section)))
508 (magit-next-section section))))
509 (if next
510 (progn
511 (goto-char (magit-section-beginning next))
512 (if (memq magit-submode '(log reflog))
513 (magit-show-commit next)))
514 (message "No next section"))))
516 (defun magit-prev-section (section)
517 (let ((parent (magit-section-parent section)))
518 (if parent
519 (let ((prev (cadr (memq section
520 (reverse (magit-section-children parent))))))
521 (cond (prev
522 (while (and (not (magit-section-hidden prev))
523 (magit-section-children prev))
524 (setq prev (car (reverse (magit-section-children prev)))))
525 prev)
527 parent))))))
529 (defun magit-goto-previous-section ()
530 (interactive)
531 (let ((section (magit-current-section)))
532 (cond ((= (point) (magit-section-beginning section))
533 (let ((prev (magit-prev-section (magit-current-section))))
534 (if prev
535 (progn
536 (if (memq magit-submode '(log reflog))
537 (magit-show-commit (or prev section)))
538 (goto-char (magit-section-beginning prev)))
539 (message "No previous section"))))
541 (let ((prev (magit-find-section-before (point)
542 (magit-section-children
543 section))))
544 (if (memq magit-submode '(log reflog))
545 (magit-show-commit (or prev section)))
546 (goto-char (magit-section-beginning (or prev section))))))))
548 (defun magit-goto-section (path)
549 (let ((sec (magit-find-section path magit-top-section)))
550 (if sec
551 (goto-char (magit-section-beginning sec))
552 (message "No such section"))))
554 (defun magit-for-all-sections (func &optional top)
555 (let ((section (or top magit-top-section)))
556 (when section
557 (funcall func section)
558 (dolist (c (magit-section-children section))
559 (magit-for-all-sections func c)))))
561 (defun magit-section-set-hidden (section hidden)
562 (setf (magit-section-hidden section) hidden)
563 (let ((inhibit-read-only t)
564 (beg (save-excursion
565 (goto-char (magit-section-beginning section))
566 (forward-line)
567 (point)))
568 (end (magit-section-end section)))
569 (put-text-property beg end 'invisible hidden)
570 (put-text-property beg end 'rear-nonsticky t))
571 (if (not hidden)
572 (dolist (c (magit-section-children section))
573 (magit-section-set-hidden c (magit-section-hidden c)))))
575 (defun magit-section-any-hidden (section)
576 (or (magit-section-hidden section)
577 (some #'magit-section-any-hidden (magit-section-children section))))
579 (defun magit-section-collapse (section)
580 (dolist (c (magit-section-children section))
581 (setf (magit-section-hidden c) t))
582 (magit-section-set-hidden section nil))
584 (defun magit-section-expand (section)
585 (dolist (c (magit-section-children section))
586 (setf (magit-section-hidden c) nil))
587 (magit-section-set-hidden section nil))
589 (defun magit-section-expand-all-aux (section)
590 (dolist (c (magit-section-children section))
591 (setf (magit-section-hidden c) nil)
592 (magit-section-expand-all-aux c)))
594 (defun magit-section-expand-all (section)
595 (magit-section-expand-all-aux section)
596 (magit-section-set-hidden section nil))
598 (defun magit-section-hideshow (flag-or-func)
599 (let ((section (magit-current-section)))
600 (cond ((magit-section-parent section)
601 (goto-char (magit-section-beginning section))
602 (if (functionp flag-or-func)
603 (funcall flag-or-func section)
604 (magit-section-set-hidden section flag-or-func))))))
606 (defun magit-show-section ()
607 (interactive)
608 (magit-section-hideshow nil))
610 (defun magit-hide-section ()
611 (interactive)
612 (magit-section-hideshow t))
614 (defun magit-collapse-section ()
615 (interactive)
616 (magit-section-hideshow #'magit-section-collapse))
618 (defun magit-expand-section ()
619 (interactive)
620 (magit-section-hideshow #'magit-section-expand))
622 (defun magit-toggle-section ()
623 (interactive)
624 (magit-section-hideshow
625 (lambda (s)
626 (magit-section-set-hidden s (not (magit-section-hidden s))))))
628 (defun magit-expand-collapse-section ()
629 (interactive)
630 (magit-section-hideshow
631 (lambda (s)
632 (cond ((magit-section-any-hidden s)
633 (magit-section-expand-all s))
635 (magit-section-collapse s))))))
637 (defun magit-cycle-section ()
638 (interactive)
639 (magit-section-hideshow
640 (lambda (s)
641 (cond ((magit-section-hidden s)
642 (magit-section-collapse s))
643 ((notany #'magit-section-hidden (magit-section-children s))
644 (magit-section-set-hidden s t))
646 (magit-section-expand s))))))
648 (defmacro magit-define-section-jumper (sym title)
649 (let ((fun (intern (format "magit-jump-to-%s" sym)))
650 (doc (format "Jump to section `%s'." title)))
651 `(defun ,fun ()
652 (interactive)
653 (magit-goto-section '(,sym)))))
655 (defvar magit-highlight-overlay nil)
657 (defvar magit-highlighted-section nil)
659 (defun magit-highlight-section ()
660 (let ((section (magit-current-section)))
661 (when (not (eq section magit-highlighted-section))
662 (setq magit-highlighted-section section)
663 (if (not magit-highlight-overlay)
664 (let ((ov (make-overlay 1 1)))
665 (overlay-put ov 'face 'magit-item-highlight)
666 (setq magit-highlight-overlay ov)))
667 (if (and section (magit-section-type section))
668 (move-overlay magit-highlight-overlay
669 (magit-section-beginning section)
670 (magit-section-end section)
671 (current-buffer))
672 (delete-overlay magit-highlight-overlay)))))
674 (defun magit-section-context-type (section)
675 (if (null section)
677 (let ((c (or (magit-section-type section)
678 (if (symbolp (magit-section-title section))
679 (magit-section-title section)))))
680 (if c
681 (cons c (magit-section-context-type
682 (magit-section-parent section)))
683 '()))))
685 (defun magit-prefix-p (prefix list)
686 ;;; Very schemish...
687 (or (null prefix)
688 (if (eq (car prefix) '*)
689 (or (magit-prefix-p (cdr prefix) list)
690 (and (not (null list))
691 (magit-prefix-p prefix (cdr list))))
692 (and (not (null list))
693 (eq (car prefix) (car list))
694 (magit-prefix-p (cdr prefix) (cdr list))))))
696 (defmacro magit-section-case (head &rest clauses)
697 (declare (indent 1))
698 (let ((section (car head))
699 (info (cadr head))
700 (type (gensym))
701 (context (gensym))
702 (opname (caddr head)))
703 `(let* ((,section (magit-current-section))
704 (,info (magit-section-info ,section))
705 (,type (magit-section-type ,section))
706 (,context (magit-section-context-type ,section)))
707 (cond ,@(mapcar (lambda (clause)
708 (let ((prefix (reverse (car clause)))
709 (body (cdr clause)))
710 `((magit-prefix-p ',prefix ,context)
711 ,@body)))
712 clauses)
713 ,@(if opname
714 `(((not ,type)
715 (error "Nothing to %s here." ,opname))
717 (error "Can't %s a %s."
718 ,opname
719 (or (get ,type 'magit-description)
720 ,type)))))))))
722 (defmacro magit-section-action (head &rest clauses)
723 (declare (indent 1))
724 `(magit-with-refresh
725 (magit-section-case ,head ,@clauses)))
727 (defun magit-wash-sequence (func)
728 (while (and (not (eobp))
729 (funcall func))))
731 ;;; Running commands
733 (defun magit-set-mode-line-process (str)
734 (let ((pr (if str (concat " " str) "")))
735 (save-excursion
736 (magit-for-all-buffers (lambda ()
737 (setq mode-line-process pr))))))
739 (defun magit-process-indicator-from-command (cmd args)
740 (cond ((or (null args)
741 (not (equal cmd "git")))
742 cmd)
743 ((or (null (cdr args))
744 (not (member (car args) '("remote"))))
745 (car args))
747 (concat (car args) " " (cadr args)))))
749 (defvar magit-process nil)
750 (defvar magit-process-client-buffer nil)
752 (defun magit-run* (cmd-and-args
753 &optional logline noerase noerror nowait input)
754 (let ((cmd (car cmd-and-args))
755 (args (cdr cmd-and-args))
756 (dir default-directory)
757 (buf (get-buffer-create "*magit-process*"))
758 (successp nil))
759 (or (not magit-process)
760 (error "Git is already running."))
761 (magit-set-mode-line-process
762 (magit-process-indicator-from-command
763 (car cmd-and-args) (cdr cmd-and-args)))
764 (setq magit-process-client-buffer (current-buffer))
765 (save-excursion
766 (set-buffer buf)
767 (setq default-directory dir)
768 (if noerase
769 (goto-char (point-max))
770 (erase-buffer))
771 (insert "$ " (or logline
772 (magit-concat-with-delim " " cmd-and-args))
773 "\n")
774 (cond (nowait
775 (setq magit-process
776 (apply 'start-process "git" buf cmd args))
777 (set-process-sentinel magit-process 'magit-process-sentinel)
778 (set-process-filter magit-process 'magit-process-filter)
779 (setq successp t))
780 (input
781 (with-current-buffer input
782 (setq default-directory dir)
783 (setq successp
784 (equal (apply 'call-process-region (point-min) (point-max)
785 cmd nil buf nil args) 0)))
786 (magit-set-mode-line-process nil)
787 (magit-need-refresh magit-process-client-buffer))
789 (setq successp
790 (equal (apply 'call-process cmd nil buf nil args) 0))
791 (magit-set-mode-line-process nil)
792 (magit-need-refresh magit-process-client-buffer))))
793 (or successp
794 noerror
795 (error "Git failed."))
796 successp))
798 (defun magit-process-sentinel (process event)
799 (let ((msg (format "Git %s." (substring event 0 -1)))
800 (successp (string-match "^finished" event)))
801 (with-current-buffer (process-buffer process)
802 (insert msg "\n")
803 (message msg))
804 (setq magit-process nil)
805 (magit-set-mode-line-process nil)
806 (magit-refresh-buffer magit-process-client-buffer)))
808 (defun magit-process-filter (proc string)
809 (save-excursion
810 (set-buffer (process-buffer proc))
811 (goto-char (process-mark proc))
812 ;; Find last ^M in string. If one was found, ignore everything
813 ;; before it and delete the current line.
814 (let ((ret-pos (position ?\r string :from-end t)))
815 (cond (ret-pos
816 (goto-char (line-beginning-position))
817 (delete-region (point) (line-end-position))
818 (insert (substring string (+ ret-pos 1))))
820 (insert string))))
821 (set-marker (process-mark proc) (point))))
823 (defun magit-run (cmd &rest args)
824 (magit-with-refresh
825 (magit-run* (cons cmd args))))
827 (defun magit-run-with-input (input cmd &rest args)
828 (magit-with-refresh
829 (magit-run* (cons cmd args) nil nil nil nil input)))
831 (defun magit-run-shell (fmt &rest args)
832 (let ((cmd (apply #'format fmt (mapcar #'magit-escape-for-shell args))))
833 (magit-with-refresh
834 (magit-run* (list shell-file-name shell-command-switch cmd)
835 cmd))))
837 (defun magit-run-async (cmd &rest args)
838 (magit-run* (cons cmd args) nil nil nil t))
840 (defun magit-display-process ()
841 (interactive)
842 (display-buffer "*magit-process*"))
844 ;;; Mode
846 ;; We define individual functions (instead of using lambda etc) so
847 ;; that the online help can show something meaningful.
849 (magit-define-section-jumper untracked "Untracked files")
850 (magit-define-section-jumper unstaged "Unstaged changes")
851 (magit-define-section-jumper staged "Staged changes")
852 (magit-define-section-jumper unpushed "Unpushed commits")
854 (defvar magit-mode-map
855 (let ((map (make-keymap)))
856 (suppress-keymap map t)
857 (define-key map (kbd "n") 'magit-goto-next-section)
858 (define-key map (kbd "p") 'magit-goto-previous-section)
859 (define-key map (kbd "TAB") 'magit-toggle-section)
860 (define-key map (kbd "<backtab>") 'magit-expand-collapse-section)
861 (define-key map (kbd "1") 'magit-jump-to-untracked)
862 (define-key map (kbd "2") 'magit-jump-to-unstaged)
863 (define-key map (kbd "3") 'magit-jump-to-staged)
864 (define-key map (kbd "4") 'magit-jump-to-unpushed)
865 (define-key map (kbd "g") 'magit-refresh)
866 (define-key map (kbd "G") 'magit-refresh-all)
867 (define-key map (kbd "s") 'magit-stage-item)
868 (define-key map (kbd "S") 'magit-stage-all)
869 (define-key map (kbd "u") 'magit-unstage-item)
870 (define-key map (kbd "U") 'magit-unstage-all)
871 (define-key map (kbd "i") 'magit-ignore-item)
872 (define-key map (kbd "I") 'magit-ignore-item-locally)
873 (define-key map (kbd "i") 'magit-ignore-item)
874 (define-key map (kbd "?") 'magit-describe-item)
875 (define-key map (kbd ".") 'magit-mark-item)
876 (define-key map (kbd "=") 'magit-diff-with-mark)
877 (define-key map (kbd "l") 'magit-log-head)
878 (define-key map (kbd "L") 'magit-log)
879 (define-key map (kbd "h") 'magit-reflog-head)
880 (define-key map (kbd "H") 'magit-reflog)
881 (define-key map (kbd "d") 'magit-diff-working-tree)
882 (define-key map (kbd "D") 'magit-diff)
883 (define-key map (kbd "a") 'magit-apply-item)
884 (define-key map (kbd "A") 'magit-cherry-pick-item)
885 (define-key map (kbd "v") 'magit-revert-item)
886 (define-key map (kbd "x") 'magit-reset-head)
887 (define-key map (kbd "X") 'magit-reset-working-tree)
888 (define-key map (kbd "k") 'magit-discard-item)
889 (define-key map (kbd "RET") 'magit-visit-item)
890 (define-key map (kbd "SPC") 'magit-show-item-or-scroll-up)
891 (define-key map (kbd "DEL") 'magit-show-item-or-scroll-down)
892 (define-key map (kbd "C-w") 'magit-copy-item-as-kill)
893 (define-key map (kbd "b") 'magit-checkout)
894 (define-key map (kbd "B") 'magit-create-branch)
895 (define-key map (kbd "m") 'magit-manual-merge)
896 (define-key map (kbd "M") 'magit-automatic-merge)
897 (define-key map (kbd "N r") 'magit-svn-rebase)
898 (define-key map (kbd "N c") 'magit-svn-dcommit)
899 (define-key map (kbd "R") 'magit-rebase-step)
900 (define-key map (kbd "r s") 'magit-rewrite-start)
901 (define-key map (kbd "r t") 'magit-rewrite-stop)
902 (define-key map (kbd "r a") 'magit-rewrite-abort)
903 (define-key map (kbd "r f") 'magit-rewrite-finish)
904 (define-key map (kbd "r *") 'magit-rewrite-set-unused)
905 (define-key map (kbd "r .") 'magit-rewrite-set-used)
906 (define-key map (kbd "P") 'magit-push)
907 (define-key map (kbd "f") 'magit-remote-update)
908 (define-key map (kbd "F") 'magit-pull)
909 (define-key map (kbd "c") 'magit-log-edit)
910 (define-key map (kbd "C") 'magit-add-log)
911 (define-key map (kbd "t") 'magit-tag)
912 (define-key map (kbd "T") 'magit-annotated-tag)
913 (define-key map (kbd "z") 'magit-stash)
914 (define-key map (kbd "$") 'magit-display-process)
915 (define-key map (kbd "q") 'quit-window)
916 map))
918 (easy-menu-define magit-mode-menu magit-mode-map
919 "Magit menu"
920 '("Magit"
921 ["Refresh" magit-refresh t]
922 ["Refresh all" magit-refresh-all t]
923 "---"
924 ["Stage" magit-stage-item t]
925 ["Stage all" magit-stage-all t]
926 ["Unstage" magit-unstage-item t]
927 ["Unstage all" magit-unstage-all t]
928 ["Commit" magit-log-edit t]
929 ["Add log entry" magit-add-log t]
930 ["Tag" magit-tag t]
931 ["Annotated tag" magit-annotated-tag t]
932 "---"
933 ["Diff working tree" magit-diff-working-tree t]
934 ["Diff" magit-diff t]
935 ["Log head" magit-log-head t]
936 ["Log" magit-log t]
937 ["Reflog head" magit-reflog-head t]
938 ["Reflog" magit-reflog t]
939 "---"
940 ["Cherry pick" magit-cherry-pick-item t]
941 ["Apply" magit-apply-item t]
942 ["Revert" magit-revert-item t]
943 "---"
944 ["Ignore" magit-ignore-item t]
945 ["Ignore locally" magit-ignore-item-locally t]
946 ["Discard" magit-discard-item t]
947 ["Reset head" magit-reset-head t]
948 ["Reset working tree" magit-reset-working-tree t]
949 ["Stash" magit-stash t]
950 "---"
951 ["Switch branch" magit-checkout t]
952 ["Create branch" magit-create-branch t]
953 ["Merge" magit-automatic-merge t]
954 ["Merge (no commit)" magit-manual-merge t]
955 ["Rebase" magit-rebase-step t]
956 ("Git SVN"
957 ["Rebase" magit-svn-rebase (magit-svn-enabled)]
958 ["Commit" magit-svn-dcommit (magit-svn-enabled)]
960 ("Rewrite"
961 ["Start" magit-rewrite-start t]
962 ["Stop" magit-rewrite-stop t]
963 ["Finish" magit-rewrite-finish t]
964 ["Abort" magit-rewrite-abort t]
965 ["Set used" magit-rewrite-set-used t]
966 ["Set unused" magit-rewrite-set-unused t])
967 "---"
968 ["Push" magit-push t]
969 ["Pull" magit-pull t]
970 ["Remote update" magit-remote-update t]
971 "---"
972 ["Display Git output" magit-display-process t]
973 ["Quit Magit" quit-window t]))
975 (defvar magit-mode-hook nil)
977 (put 'magit-mode 'mode-class 'special)
979 (defvar magit-submode nil)
980 (make-variable-buffer-local 'magit-submode)
981 (put 'magit-submode 'permanent-local t)
983 (defvar magit-refresh-function nil)
984 (make-variable-buffer-local 'magit-refresh-function)
985 (put 'magit-refresh-function 'permanent-local t)
987 (defvar magit-refresh-args nil)
988 (make-variable-buffer-local 'magit-refresh-args)
989 (put 'magit-refresh-args 'permanent-local t)
991 (defun magit-mode ()
992 "Review the status of a git repository and act on it.
994 Please see the manual for a complete description of Magit.
996 \\{magit-mode-map}"
997 (kill-all-local-variables)
998 (setq buffer-read-only t)
999 (setq major-mode 'magit-mode
1000 mode-name "Magit"
1001 mode-line-process ""
1002 truncate-lines t)
1003 (add-hook 'post-command-hook #'magit-highlight-section nil t)
1004 (use-local-map magit-mode-map)
1005 (run-mode-hooks 'magit-mode-hook))
1007 (defun magit-mode-init (dir submode refresh-func &rest refresh-args)
1008 (setq default-directory dir
1009 magit-submode submode
1010 magit-refresh-function refresh-func
1011 magit-refresh-args refresh-args)
1012 (magit-mode)
1013 (magit-refresh-buffer))
1015 (defun magit-find-buffer (submode &optional dir)
1016 (let ((topdir (magit-get-top-dir (or dir default-directory))))
1017 (dolist (buf (buffer-list))
1018 (if (save-excursion
1019 (set-buffer buf)
1020 (and (equal default-directory topdir)
1021 (eq major-mode 'magit-mode)
1022 (eq magit-submode submode)))
1023 (return buf)))))
1025 (defun magit-find-status-buffer (&optional dir)
1026 (magit-find-buffer 'status dir))
1028 (defun magit-for-all-buffers (func &optional dir)
1029 (dolist (buf (buffer-list))
1030 (save-excursion
1031 (set-buffer buf)
1032 (if (and (eq major-mode 'magit-mode)
1033 (or (null dir)
1034 (equal default-directory dir)))
1035 (funcall func)))))
1037 (defun magit-refresh-buffer (&optional buffer)
1038 (with-current-buffer (or buffer (current-buffer))
1039 (let* ((old-line (line-number-at-pos))
1040 (old-section (magit-current-section))
1041 (old-path (and old-section
1042 (magit-section-path (magit-current-section))))
1043 (section-line (and old-section
1044 (count-lines
1045 (magit-section-beginning old-section)
1046 (point)))))
1047 (if magit-refresh-function
1048 (apply magit-refresh-function
1049 magit-refresh-args))
1050 (magit-refresh-marked-commits-in-buffer)
1051 (let ((s (and old-path (magit-find-section old-path magit-top-section))))
1052 (cond (s
1053 (goto-char (magit-section-beginning s))
1054 (forward-line section-line))
1056 (magit-goto-line old-line)))
1057 (dolist (w (get-buffer-window-list (current-buffer)))
1058 (set-window-point w (point)))
1059 (magit-highlight-section)))))
1061 (defun magit-revert-buffers ()
1062 (dolist (buffer (buffer-list))
1063 (when (and buffer
1064 (not (verify-visited-file-modtime buffer))
1065 (not (buffer-modified-p buffer)))
1066 (with-current-buffer buffer
1067 (ignore-errors
1068 (revert-buffer t t t))))))
1070 (defvar magit-refresh-needing-buffers nil)
1071 (defvar magit-refresh-pending nil)
1073 (defun magit-refresh-wrapper (func)
1074 (if magit-refresh-pending
1075 (funcall func)
1076 (let ((status-buffer (magit-find-buffer 'status default-directory))
1077 (magit-refresh-needing-buffers nil)
1078 (magit-refresh-pending t))
1079 (unwind-protect
1080 (funcall func)
1081 (when magit-refresh-needing-buffers
1082 (magit-revert-buffers)
1083 (dolist (b (adjoin status-buffer
1084 magit-refresh-needing-buffers))
1085 (magit-refresh-buffer b)))))))
1087 (defun magit-need-refresh (&optional buffer)
1088 (let ((buffer (or buffer (current-buffer))))
1089 (when (not (memq buffer magit-refresh-needing-buffers))
1090 (setq magit-refresh-needing-buffers
1091 (cons buffer magit-refresh-needing-buffers)))))
1093 (defun magit-refresh ()
1094 (interactive)
1095 (magit-with-refresh
1096 (magit-need-refresh)))
1098 (defun magit-refresh-all ()
1099 (interactive)
1100 (magit-for-all-buffers #'magit-refresh-buffer default-directory))
1102 ;;; Untracked files
1104 (defun magit-wash-untracked-file ()
1105 (if (looking-at "^? \\(.*\\)$")
1106 (let ((file (match-string-no-properties 1)))
1107 (delete-region (point) (+ (line-end-position) 1))
1108 (magit-with-section file 'file
1109 (magit-set-section-info file)
1110 (insert "\t" file "\n"))
1112 nil))
1114 (defun magit-wash-untracked-files ()
1115 (magit-wash-sequence #'magit-wash-untracked-file))
1117 (defun magit-insert-untracked-files ()
1118 (magit-insert-section 'untracked "Untracked files:"
1119 'magit-wash-untracked-files
1120 magit-collapse-threshold
1121 "git" "ls-files" "-t" "--others" "--exclude-standard"))
1123 ;;; Diffs and Hunks
1125 (defun magit-diff-line-file ()
1126 (cond ((looking-at "^diff --git a/\\(.*\\) b/\\(.*\\)$")
1127 (match-string-no-properties 2))
1128 ((looking-at "^diff --cc +\\(.*\\)$")
1129 (match-string-no-properties 1))
1131 nil)))
1133 (defun magit-wash-diffs ()
1134 (magit-wash-sequence #'magit-wash-diff-or-other-file))
1136 (defun magit-wash-diff-or-other-file ()
1137 (or (magit-wash-diff)
1138 (magit-wash-other-file)))
1140 (defun magit-wash-other-file ()
1141 (if (looking-at "^? \\(.*\\)$")
1142 (let ((file (match-string-no-properties 1)))
1143 (delete-region (point) (+ (line-end-position) 1))
1144 (magit-with-section file 'file
1145 (magit-set-section-info file)
1146 (insert "\tNew " file "\n"))
1148 nil))
1150 (defvar magit-hide-diffs nil)
1152 (defun magit-wash-diff ()
1153 (cond ((looking-at "^diff")
1154 (magit-with-section
1155 (magit-current-line)
1156 (:type 'diff :hidden magit-hide-diffs)
1157 (let ((file (magit-diff-line-file))
1158 (end (save-excursion
1159 (forward-line) ;; skip over "diff" line
1160 (if (search-forward-regexp "^diff\\|^@@" nil t)
1161 (goto-char (match-beginning 0))
1162 (goto-char (point-max)))
1163 (point-marker))))
1164 (let* ((status (cond
1165 ((looking-at "^diff --cc")
1166 'unmerged)
1167 ((save-excursion
1168 (search-forward-regexp "^new" end t))
1169 'new)
1170 ((save-excursion
1171 (search-forward-regexp "^deleted" end t))
1172 'deleted)
1173 ((save-excursion
1174 (search-forward-regexp "^rename" end t))
1175 'renamed)
1177 'modified)))
1178 (file2 (cond
1179 ((save-excursion
1180 (search-forward-regexp "^rename from \\(.*\\)"
1181 end t))
1182 (match-string-no-properties 1))))
1183 (status-text (case status
1184 ((unmerged)
1185 (format "Unmerged %s" file))
1186 ((new)
1187 (format "New %s" file))
1188 ((deleted)
1189 (format "Deleted %s" file))
1190 ((renamed)
1191 (format "Renamed %s (from %s)"
1192 file file2))
1193 ((modified)
1194 (format "Modified %s" file))
1196 (format "? %s" file)))))
1197 (magit-set-section-info (list status file file2))
1198 (insert "\t" status-text "\n")
1199 (goto-char end)
1200 (magit-wash-sequence #'magit-wash-hunk)
1201 t))))
1203 nil)))
1205 (defun magit-diff-item-kind (diff)
1206 (car (magit-section-info diff)))
1208 (defun magit-diff-item-file (diff)
1209 (cadr (magit-section-info diff)))
1211 (defun magit-diff-item-file2 (diff)
1212 (caddr (magit-section-info diff)))
1214 (defun magit-wash-hunk ()
1215 (cond ((looking-at "\\(^@+\\)[^@]*@+")
1216 (let ((n-files (length (match-string 1)))
1217 (head (match-string 0)))
1218 (magit-with-section head 'hunk
1219 (magit-put-line-property 'face 'magit-diff-hunk-header)
1220 (forward-line)
1221 (while (not (or (eobp)
1222 (looking-at "^diff\\|^@@")))
1223 (let ((prefix (buffer-substring-no-properties
1224 (point) (min (+ (point) n-files) (point-max)))))
1225 (cond ((string-match "\\+" prefix)
1226 (magit-put-line-property 'face 'magit-diff-add))
1227 ((string-match "-" prefix)
1228 (magit-put-line-property 'face 'magit-diff-del))
1230 (magit-put-line-property 'face 'magit-diff-none))))
1231 (forward-line))))
1234 nil)))
1236 (defun magit-hunk-item-diff (hunk)
1237 (let ((diff (magit-section-parent hunk)))
1238 (or (eq (magit-section-type diff) 'diff)
1239 (error "Huh? Parent of hunk not a diff."))
1240 diff))
1242 (defun magit-diff-item-insert-header (diff buf)
1243 (let ((beg (save-excursion
1244 (goto-char (magit-section-beginning diff))
1245 (forward-line)
1246 (point)))
1247 (end (if (magit-section-children diff)
1248 (magit-section-beginning (car (magit-section-children diff)))
1249 (magit-section-end diff))))
1250 (magit-insert-region beg end buf)))
1252 (defun magit-insert-diff-item-patch (diff buf)
1253 (let ((beg (save-excursion
1254 (goto-char (magit-section-beginning diff))
1255 (forward-line)
1256 (point)))
1257 (end (magit-section-end diff)))
1258 (magit-insert-region beg end buf)))
1260 (defun magit-insert-hunk-item-patch (hunk buf)
1261 (magit-diff-item-insert-header (magit-hunk-item-diff hunk) buf)
1262 (magit-insert-region (magit-section-beginning hunk) (magit-section-end hunk)
1263 buf))
1265 (defun magit-insert-hunk-item-region-patch (hunk beg end buf)
1266 (magit-diff-item-insert-header (magit-hunk-item-diff hunk) buf)
1267 (save-excursion
1268 (goto-char (magit-section-beginning hunk))
1269 (magit-insert-current-line buf)
1270 (forward-line)
1271 (while (< (point) (magit-section-end hunk))
1272 (if (and (<= beg (point)) (< (point) end))
1273 (magit-insert-current-line buf)
1274 (cond ((looking-at " ")
1275 (magit-insert-current-line buf))
1276 ((looking-at "-")
1277 (let ((text (buffer-substring-no-properties
1278 (+ (point) 1) (line-beginning-position 2))))
1279 (with-current-buffer buf
1280 (insert " " text))))))
1281 (forward-line)))
1282 (with-current-buffer buf
1283 (diff-fixup-modifs (point-min) (point-max))))
1285 (defun magit-hunk-item-is-conflict-p (hunk)
1286 ;;; XXX - Using the title is a bit too clever...
1287 (string-match "^diff --cc"
1288 (magit-section-title (magit-hunk-item-diff hunk))))
1290 (defun magit-hunk-item-target-line (hunk)
1291 (save-excursion
1292 (beginning-of-line)
1293 (let ((line (line-number-at-pos)))
1294 (if (looking-at "-")
1295 (error "Can't visit removed lines."))
1296 (goto-char (magit-section-beginning hunk))
1297 (if (not (looking-at "@@+ .* \\+\\([0-9]+\\),[0-9]+ @@+"))
1298 (error "Hunk header not found."))
1299 (let ((target (parse-integer (match-string 1))))
1300 (forward-line)
1301 (while (< (line-number-at-pos) line)
1302 ;; XXX - deal with combined diffs
1303 (if (not (looking-at "-"))
1304 (setq target (+ target 1)))
1305 (forward-line))
1306 target))))
1308 (defun magit-apply-diff-item (diff &rest args)
1309 (with-current-buffer (get-buffer-create "*magit-tmp*")
1310 (erase-buffer))
1311 (magit-insert-diff-item-patch diff "*magit-tmp*")
1312 (apply #'magit-run "git" "apply" (append args (list "-"))))
1314 (defun magit-apply-hunk-item (hunk &rest args)
1315 (let ((tmp (get-buffer-create "*magit-tmp*")))
1316 (with-current-buffer tmp
1317 (erase-buffer))
1318 (if (magit-use-region-p)
1319 (magit-insert-hunk-item-region-patch
1320 hunk (region-beginning) (region-end) tmp)
1321 (magit-insert-hunk-item-patch hunk tmp))
1322 (apply #'magit-run-with-input tmp
1323 "git" "apply" (append args (list "-")))))
1325 (defun magit-insert-unstaged-changes (title)
1326 (let ((magit-hide-diffs t))
1327 (magit-insert-section 'unstaged title 'magit-wash-diffs
1328 magit-collapse-threshold
1329 "git" "diff")))
1331 (defun magit-insert-staged-changes ()
1332 (let ((magit-hide-diffs t))
1333 (magit-insert-section 'staged "Staged changes:" 'magit-wash-diffs
1334 magit-collapse-threshold
1335 "git" "diff" "--cached")))
1337 ;;; Logs and Commits
1339 (defun magit-parse-log-ref (refname)
1340 "Return shortened and propertized version of full REFNAME, like
1341 \"refs/remotes/origin/master\"."
1342 (let ((face 'magit-log-head-label))
1343 (cond ((string-match "^\\(tag: +\\)?refs/tags/\\(.+\\)" refname)
1344 (setq refname (match-string 2 refname)
1345 face 'magit-log-tag-label))
1346 ((string-match "^refs/remotes/\\(.+\\)" refname)
1347 (setq refname (match-string 1 refname)))
1348 ((string-match "[^/]+$" refname)
1349 (setq refname (match-string 0 refname))))
1350 (propertize refname 'face face)))
1352 (defun magit-parse-log-refs (refstring)
1353 "Parse REFSTRING annotation from `git log --decorate'
1354 output (for example: \"refs/remotes/origin/master,
1355 refs/heads/master\") and return prettified string for displaying
1356 in log buffer."
1357 (mapconcat 'identity
1358 (mapcar 'magit-parse-log-ref
1359 (remove-if (lambda (refname)
1360 (string-match "/HEAD$" refname))
1361 (reverse (split-string refstring ", *" t))))
1362 " - "))
1364 (defun magit-wash-log-line ()
1365 (if (search-forward-regexp "[0-9a-fA-F]\\{40\\}" (line-end-position) t)
1366 (let ((commit (match-string-no-properties 0)))
1367 (delete-region (match-beginning 0) (match-end 0))
1368 (goto-char (match-beginning 0))
1369 (fixup-whitespace)
1370 (goto-char (line-beginning-position))
1371 (when (search-forward-regexp "^[|*\\/ ]+\\((\\(tag:.+?\\|refs/.+?\\))\\)"
1372 (line-end-position) t)
1373 (let ((refstring (match-string-no-properties 2)))
1374 (delete-region (match-beginning 1) (match-end 1))
1375 (insert (magit-parse-log-refs refstring)))
1376 (goto-char (line-beginning-position)))
1377 (magit-with-section commit 'commit
1378 (magit-set-section-info commit)
1379 (forward-line)))
1380 (forward-line))
1383 (defun magit-wash-log ()
1384 (let ((magit-old-top-section nil))
1385 (magit-wash-sequence #'magit-wash-log-line)))
1387 (defvar magit-currently-shown-commit nil)
1389 (defun magit-wash-commit ()
1390 (cond ((search-forward-regexp "^diff" nil t)
1391 (goto-char (match-beginning 0))
1392 (magit-wash-diffs))))
1394 (defun magit-refresh-commit-buffer (commit)
1395 (magit-create-buffer-sections
1396 (magit-insert-section 'commitbuf nil
1397 'magit-wash-commit nil
1398 "git" "log" "--max-count=1" "--cc" "-p" commit)))
1400 (defun magit-show-commit (commit &optional scroll)
1401 (when (magit-section-p commit)
1402 (setq commit (magit-section-info commit)))
1403 (let ((dir default-directory)
1404 (buf (get-buffer-create "*magit-commit*")))
1405 (cond ((equal magit-currently-shown-commit commit)
1406 (let ((win (get-buffer-window buf)))
1407 (cond ((not win)
1408 (display-buffer buf))
1409 (scroll
1410 (with-selected-window win
1411 (funcall scroll))))))
1413 (setq magit-currently-shown-commit commit)
1414 (display-buffer buf)
1415 (with-current-buffer buf
1416 (set-buffer buf)
1417 (goto-char (point-min))
1418 (magit-mode-init dir 'commit
1419 #'magit-refresh-commit-buffer commit))))))
1421 (defvar magit-marked-commit nil)
1423 (defun magit-refresh-marked-commits ()
1424 (magit-for-all-buffers #'magit-refresh-marked-commits-in-buffer))
1426 (defun magit-refresh-marked-commits-in-buffer ()
1427 (let ((inhibit-read-only t))
1428 (magit-for-all-sections
1429 (lambda (section)
1430 (if (not (magit-section-p section))
1431 (message "%s" section))
1432 (when (and (eq (magit-section-type section) 'commit)
1433 (equal (magit-section-info section)
1434 magit-marked-commit))
1435 (put-text-property (magit-section-beginning section)
1436 (magit-section-end section)
1437 'face '(:foreground "red")))))))
1439 (defun magit-set-marked-commit (commit)
1440 (setq magit-marked-commit commit)
1441 (magit-refresh-marked-commits))
1443 (defun magit-marked-commit ()
1444 (or magit-marked-commit
1445 (error "Not commit marked")))
1447 (defun magit-insert-unpulled-commits (remote branch)
1448 (magit-insert-section 'unpulled
1449 "Unpulled commits:" 'magit-wash-log
1451 "git" "log" "--pretty=format:* %H %s"
1452 (format "HEAD..%s/%s" remote branch)))
1454 (defun magit-insert-unpushed-commits (remote branch)
1455 (magit-insert-section 'unpushed
1456 "Unpushed commits:" 'magit-wash-log
1458 "git" "log" "--pretty=format:* %H %s"
1459 (format "%s/%s..HEAD" remote branch)))
1461 ;;; Status
1463 (defun magit-refresh-status ()
1464 (magit-create-buffer-sections
1465 (magit-with-section 'status nil
1466 (let* ((branch (magit-get-current-branch))
1467 (remote (and branch (magit-get "branch" branch "remote"))))
1468 (if remote
1469 (insert (format "Remote: %s %s\n"
1470 remote (magit-get "remote" remote "url"))))
1471 (insert (format "Local: %s %s\n"
1472 (propertize (or branch "(detached)")
1473 'face 'magit-branch)
1474 (abbreviate-file-name default-directory)))
1475 (insert
1476 (format
1477 "Head: %s\n"
1478 (magit-shell
1479 "git log --max-count=1 --abbrev-commit --pretty=oneline")))
1480 (let ((merge-heads (magit-file-lines ".git/MERGE_HEAD")))
1481 (if merge-heads
1482 (insert (format "Merging: %s\n"
1483 (magit-concat-with-delim
1484 ", "
1485 (mapcar 'magit-name-rev merge-heads))))))
1486 (let ((rebase (magit-rebase-info)))
1487 (if rebase
1488 (insert (apply 'format "Rebasing: %s (%s of %s)\n" rebase))))
1489 (insert "\n")
1490 (magit-insert-untracked-files)
1491 (magit-insert-stashes)
1492 (magit-insert-pending-changes)
1493 (magit-insert-pending-commits)
1494 (when remote
1495 (magit-insert-unpulled-commits remote branch))
1496 (let ((staged (magit-anything-staged-p)))
1497 (magit-insert-unstaged-changes
1498 (if staged "Unstaged changes:" "Changes:"))
1499 (if staged
1500 (magit-insert-staged-changes)))
1501 (when remote
1502 (magit-insert-unpushed-commits remote branch))))))
1504 (defun magit-status (dir)
1505 (interactive (list (magit-read-top-dir)))
1506 (save-some-buffers)
1507 (let* ((topdir (magit-get-top-dir dir))
1508 (buf (or (magit-find-buffer 'status topdir)
1509 (switch-to-buffer
1510 (generate-new-buffer
1511 (concat "*magit: "
1512 (file-name-nondirectory
1513 (directory-file-name topdir)) "*"))))))
1514 (switch-to-buffer buf)
1515 (magit-mode-init topdir 'status #'magit-refresh-status)))
1517 ;;; Staging and Unstaging
1519 (defun magit-stage-item ()
1520 "Add the item at point to the staging area."
1521 (interactive)
1522 (magit-section-action (item info "stage")
1523 ((untracked file)
1524 (magit-run "git" "add" info))
1525 ((unstaged diff hunk)
1526 (if (magit-hunk-item-is-conflict-p item)
1527 (error (concat "Can't stage individual resolution hunks. "
1528 "Please stage the whole file.")))
1529 (magit-apply-hunk-item item "--cached"))
1530 ((unstaged diff)
1531 (magit-run "git" "add" "-u" (magit-diff-item-file item)))
1532 ((staged *)
1533 (error "Already staged"))
1534 ((hunk)
1535 (error "Can't stage this hunk"))
1536 ((diff)
1537 (error "Can't stage this diff"))))
1539 (defun magit-unstage-item ()
1540 "Remove the item at point from the staging area."
1541 (interactive)
1542 (magit-section-action (item info "unstage")
1543 ((staged diff hunk)
1544 (magit-apply-hunk-item item "--cached" "--reverse"))
1545 ((staged diff)
1546 (magit-run "git" "reset" "-q" "HEAD" "--" (magit-diff-item-file item)))
1547 ((unstaged *)
1548 (error "Already unstaged"))
1549 ((hunk)
1550 (error "Can't unstage this hunk"))
1551 ((diff)
1552 (error "Can't unstage this diff"))))
1554 (defun magit-stage-all ()
1555 (interactive)
1556 (magit-run "git" "add" "-u" "."))
1558 (defun magit-unstage-all ()
1559 (interactive)
1560 (magit-run "git" "reset" "HEAD"))
1562 ;;; Branches
1564 (defun magit-checkout (rev)
1565 (interactive (list (magit-read-rev "Switch to" (magit-default-rev))))
1566 (if rev
1567 (magit-run "git" "checkout" (magit-rev-to-git rev))))
1569 (defun magit-read-create-branch-args ()
1570 (let* ((cur-branch (magit-get-current-branch))
1571 (branch (read-string "Create branch: "))
1572 (parent (magit-read-rev "Parent" cur-branch)))
1573 (list branch parent)))
1575 (defun magit-create-branch (branch parent)
1576 (interactive (magit-read-create-branch-args))
1577 (if (and branch (not (string= branch ""))
1578 parent)
1579 (magit-run "git" "checkout" "-b"
1580 branch
1581 (magit-rev-to-git parent))))
1583 ;;; Merging
1585 (defun magit-manual-merge (rev)
1586 (interactive (list (magit-read-rev "Manually merge")))
1587 (if rev
1588 (magit-run "git" "merge" "--no-ff" "--no-commit"
1589 (magit-rev-to-git rev))))
1591 (defun magit-automatic-merge (rev)
1592 (interactive (list (magit-read-rev "Merge")))
1593 (if rev
1594 (magit-run "git" "merge" (magit-rev-to-git rev))))
1596 ;;; Rebasing
1598 (defun magit-rebase-info ()
1599 (cond ((file-exists-p ".dotest")
1600 (list (magit-name-rev (car (magit-file-lines ".dotest/onto")))
1601 (car (magit-file-lines ".dotest/next"))
1602 (car (magit-file-lines ".dotest/last"))))
1603 ((file-exists-p ".git/.dotest-merge")
1604 (list (car (magit-file-lines ".git/.dotest-merge/onto_name"))
1605 (car (magit-file-lines ".git/.dotest-merge/msgnum"))
1606 (car (magit-file-lines ".git/.dotest-merge/end"))))
1608 nil)))
1610 (defun magit-rebase-step ()
1611 (interactive)
1612 (let ((info (magit-rebase-info)))
1613 (if (not info)
1614 (let ((rev (magit-read-rev "Rebase to")))
1615 (if rev
1616 (magit-run "git" "rebase" (magit-rev-to-git rev))))
1617 (let ((cursor-in-echo-area t)
1618 (message-log-max nil))
1619 (message "Rebase in progress. Abort, Skip, or Continue? ")
1620 (let ((reply (read-event)))
1621 (case reply
1622 ((?A ?a)
1623 (magit-run "git" "rebase" "--abort"))
1624 ((?S ?s)
1625 (magit-run "git" "rebase" "--skip"))
1626 ((?C ?c)
1627 (magit-run "git" "rebase" "--continue"))))))))
1629 ;; git svn commands
1631 (defun magit-svn-rebase ()
1632 (interactive)
1633 (magit-run "git" "svn" "rebase"))
1635 (defun magit-svn-dcommit ()
1636 (interactive)
1637 (magit-run "git" "svn" "dcommit"))
1639 (defun magit-svn-enabled ()
1640 (not (null (find "git-svn" (magit-list-interesting-revisions) :test 'equal))))
1642 ;;; Resetting
1644 (defun magit-reset-head (rev)
1645 (interactive (list (magit-read-rev "Reset head to"
1646 (or (magit-default-rev)
1647 "HEAD^"))))
1648 (if rev
1649 (magit-run "git" "reset" "--soft" (magit-rev-to-git rev))))
1651 (defun magit-reset-working-tree ()
1652 (interactive)
1653 (if (yes-or-no-p "Discard all uncommitted changes? ")
1654 (magit-run "git" "reset" "--hard")))
1656 ;;; Rewriting
1658 (defun magit-read-rewrite-info ()
1659 (when (file-exists-p ".git/magit-rewrite-info")
1660 (with-temp-buffer
1661 (insert-file-contents ".git/magit-rewrite-info")
1662 (goto-char (point-min))
1663 (read (current-buffer)))))
1665 (defun magit-write-rewrite-info (info)
1666 (with-temp-file ".git/magit-rewrite-info"
1667 (prin1 info (current-buffer))
1668 (princ "\n" (current-buffer))))
1670 (defun magit-insert-pending-commits ()
1671 (let* ((info (magit-read-rewrite-info))
1672 (pending (cdr (assq 'pending info))))
1673 (when pending
1674 (magit-with-section 'pending nil
1675 (insert (propertize "Pending commits:\n"
1676 'face 'magit-section-title))
1677 (dolist (p pending)
1678 (let* ((commit (car p))
1679 (properties (cdr p))
1680 (used (plist-get properties 'used)))
1681 (magit-with-section commit 'commit
1682 (magit-set-section-info commit)
1683 (insert (magit-shell
1684 "git log --max-count=1 --pretty=format:%s %s --"
1685 (if used ". %s" "* %s")
1686 commit)
1687 "\n")))))
1688 (insert "\n"))))
1690 (defun magit-rewrite-set-commit-property (commit prop value)
1691 (let* ((info (magit-read-rewrite-info))
1692 (pending (cdr (assq 'pending info)))
1693 (p (assoc commit pending)))
1694 (when p
1695 (setf (cdr p) (plist-put (cdr p) prop value))
1696 (magit-write-rewrite-info info)
1697 (magit-need-refresh))))
1699 (defun magit-rewrite-set-used ()
1700 (interactive)
1701 (magit-section-action (item info)
1702 ((pending commit)
1703 (magit-rewrite-set-commit-property info 'used t))))
1705 (defun magit-rewrite-set-unused ()
1706 (interactive)
1707 (magit-section-action (item info)
1708 ((pending commit)
1709 (magit-rewrite-set-commit-property info 'used nil))))
1711 (defun magit-insert-pending-changes ()
1712 (let* ((info (magit-read-rewrite-info))
1713 (orig (cadr (assq 'orig info))))
1714 (when orig
1715 (let ((magit-hide-diffs t))
1716 (magit-insert-section 'pending-changes
1717 "Pending changes"
1718 'magit-wash-diffs nil
1719 "git" "diff" "-R" orig)))))
1721 (defun magit-rewrite-start (from &optional onto)
1722 (interactive (list (magit-read-rev "Rewrite from" (magit-default-rev))))
1723 (or (magit-everything-clean-p)
1724 (error "You have uncommitted changes."))
1725 (or (not (magit-read-rewrite-info))
1726 (error "Rewrite in progress."))
1727 (let* ((orig (magit-shell "git rev-parse HEAD"))
1728 (base (magit-shell "git rev-parse %s^" from))
1729 (pending (magit-shell-lines "git rev-list %s.." base)))
1730 (magit-write-rewrite-info `((orig ,orig)
1731 (pending ,@(mapcar #'list pending))))
1732 (magit-run "git" "reset" "--hard" base)))
1734 (defun magit-rewrite-stop (&optional noconfirm)
1735 (interactive)
1736 (let* ((info (magit-read-rewrite-info)))
1737 (or info
1738 (error "No rewrite in progress."))
1739 (when (or noconfirm
1740 (yes-or-no-p "Stop rewrite? "))
1741 (magit-write-rewrite-info nil)
1742 (magit-need-refresh))))
1744 (defun magit-rewrite-abort ()
1745 (interactive)
1746 (let* ((info (magit-read-rewrite-info))
1747 (orig (cadr (assq 'orig info))))
1748 (or info
1749 (error "No rewrite in progress."))
1750 (or (magit-everything-clean-p)
1751 (error "You have uncommitted changes."))
1752 (when (yes-or-no-p "Abort rewrite? ")
1753 (magit-write-rewrite-info nil)
1754 (magit-run "git" "reset" "--hard" orig))))
1756 (defun magit-rewrite-finish ()
1757 (interactive)
1758 (magit-with-refresh
1759 (magit-rewrite-finish-step t)))
1761 (defun magit-rewrite-finish-step (first-p)
1762 (let ((info (magit-read-rewrite-info)))
1763 (or info
1764 (error "No rewrite in progress."))
1765 (let* ((pending (cdr (assq 'pending info)))
1766 (first-unused (find-if (lambda (p)
1767 (not (plist-get (cdr p) 'used)))
1768 pending
1769 :from-end t))
1770 (commit (car first-unused)))
1771 (cond ((not first-unused)
1772 (magit-rewrite-stop t))
1773 ((magit-cherry-pick-commit commit (not first-p))
1774 (magit-rewrite-set-commit-property commit 'used t)
1775 (magit-rewrite-finish-step nil))))))
1777 ;;; Updating, pull, and push
1780 (defun magit-list-remotes ()
1781 "retrieve list of remotes, registered in current repository"
1782 (magit-shell-lines "git remote show"))
1784 (defun magit-read-remote (prompt &optional def)
1785 "reads name of remote repository from mini-buffer"
1786 (let* ((prompt (if def
1787 (format "%s (default %s): " prompt def)
1788 (format "%s: " prompt)))
1789 (remote (completing-read prompt (magit-list-remotes)
1790 nil nil nil nil def)))
1791 (if (string= remote "")
1793 remote)))
1795 (defun magit-get-remote-refspec (remote)
1796 "obtain refspec for given remote"
1797 (let ((refspec (magit-get "remote" remote "fetch")))
1798 (if (string= refspec "")
1800 refspec)))
1802 (defun magit-remote-update (remote)
1803 "Check for updates in remote repository"
1804 (interactive (list (magit-read-remote "Pull from:" "origin")))
1805 (when remote
1806 (let ((refspec (magit-get-remote-refspec remote)))
1807 (when refspec
1808 (magit-run-async "git" "fetch" remote refspec)))))
1810 (defun magit-pull (remote)
1811 "Pull changes from remote repository"
1812 (interactive (list (magit-read-remote "Pull from:" "origin")))
1813 (when remote
1814 (let ((refspec (magit-get-remote-refspec remote)))
1815 (when refspec
1816 (magit-run-async "git" "pull" remote refspec "-v")))))
1818 (defun magit-push (remote)
1819 "Push changes to remote repository"
1820 (interactive (list (magit-read-remote "Pull to:" "origin")))
1821 (when remote
1822 (magit-run-async "git" "push" "-v" remote)))
1824 ;;; Log edit mode
1826 (defvar magit-log-edit-map
1827 (let ((map (make-sparse-keymap)))
1828 (define-key map (kbd "C-c C-c") 'magit-log-edit-commit)
1829 (define-key map (kbd "C-c C-a") 'magit-log-edit-toggle-amending)
1830 (define-key map (kbd "M-p") 'log-edit-previous-comment)
1831 (define-key map (kbd "M-n") 'log-edit-next-comment)
1832 map))
1834 (defvar magit-pre-log-edit-window-configuration nil)
1836 (defun magit-log-fill-paragraph (&optional justify)
1837 "Fill the paragraph, but preserve open parentheses at beginning of lines.
1838 Prefix arg means justify as well."
1839 (interactive "P")
1840 ;; Add lines starting with a left paren or an asterisk.
1841 (let ((paragraph-start (concat paragraph-start "\\|*\\|(")))
1842 (let ((end (progn (forward-paragraph) (point)))
1843 (beg (progn (backward-paragraph) (point)))
1844 (adaptive-fill-mode nil))
1845 (fill-region beg end justify)
1846 t)))
1848 (define-derived-mode magit-log-edit-mode text-mode "Magit Log Edit"
1849 (set (make-local-variable 'fill-paragraph-function)
1850 'magit-log-fill-paragraph)
1851 (use-local-map magit-log-edit-map))
1853 (defun magit-log-edit-cleanup ()
1854 (save-excursion
1855 (goto-char (point-min))
1856 (flush-lines "^#")
1857 (goto-char (point-min))
1858 (if (re-search-forward "[ \t\n]*\\'" nil t)
1859 (replace-match "\n" nil nil))))
1861 (defun magit-log-edit-append (str)
1862 (save-excursion
1863 (set-buffer (get-buffer-create "*magit-log-edit*"))
1864 (goto-char (point-max))
1865 (insert str "\n")))
1867 (defconst magit-log-header-end "-- End of Magit header --\n")
1869 (defun magit-log-edit-get-fields ()
1870 (let ((buf (get-buffer "*magit-log-edit*"))
1871 (result nil))
1872 (if buf
1873 (save-excursion
1874 (set-buffer buf)
1875 (goto-char (point-min))
1876 (while (looking-at "^\\([A-Za-z0-9]+\\): *\\(.*\\)$")
1877 (setq result (acons (intern (downcase (match-string 1)))
1878 (match-string 2)
1879 result))
1880 (forward-line))
1881 (if (not (looking-at (regexp-quote magit-log-header-end)))
1882 (setq result nil))))
1883 (nreverse result)))
1885 (defun magit-log-edit-set-fields (fields)
1886 (let ((buf (get-buffer-create "*magit-log-edit*")))
1887 (save-excursion
1888 (set-buffer buf)
1889 (goto-char (point-min))
1890 (if (search-forward-regexp (format "^\\([A-Za-z0-9]+:.*\n\\)+%s"
1891 (regexp-quote magit-log-header-end))
1892 nil t)
1893 (delete-region (match-beginning 0) (match-end 0)))
1894 (goto-char (point-min))
1895 (when fields
1896 (while fields
1897 (insert (capitalize (symbol-name (caar fields))) ": "
1898 (cdar fields) "\n")
1899 (setq fields (cdr fields)))
1900 (insert magit-log-header-end)))))
1902 (defun magit-log-edit-set-field (name value)
1903 (let* ((fields (magit-log-edit-get-fields))
1904 (cell (assq name fields)))
1905 (cond (cell
1906 (if value
1907 (rplacd cell value)
1908 (setq fields (delq cell fields))))
1910 (if value
1911 (setq fields (append fields (list (cons name value)))))))
1912 (magit-log-edit-set-fields fields)))
1914 (defun magit-log-edit-setup-author-env (author)
1915 (cond (author
1916 ;; XXX - this is a bit strict, probably.
1917 (or (string-match "\\(.*\\) <\\(.*\\)>, \\(.*\\)" author)
1918 (error "Can't parse author string."))
1919 ;; Shucks, setenv destroys the match data.
1920 (let ((name (match-string 1 author))
1921 (email (match-string 2 author))
1922 (date (match-string 3 author)))
1923 (setenv "GIT_AUTHOR_NAME" name)
1924 (setenv "GIT_AUTHOR_EMAIL" email)
1925 (setenv "GIT_AUTHOR_DATE" date)))
1927 (setenv "GIT_AUTHOR_NAME")
1928 (setenv "GIT_AUTHOR_EMAIL")
1929 (setenv "GIT_AUTHOR_DATE"))))
1931 (defun magit-log-edit-push-to-comment-ring (comment)
1932 (when (or (ring-empty-p log-edit-comment-ring)
1933 (not (equal comment (ring-ref log-edit-comment-ring 0))))
1934 (ring-insert log-edit-comment-ring comment)))
1936 (defun magit-log-edit-commit ()
1937 (interactive)
1938 (let* ((fields (magit-log-edit-get-fields))
1939 (amend (equal (cdr (assq 'amend fields)) "yes"))
1940 (tag (cdr (assq 'tag fields)))
1941 (author (cdr (assq 'author fields))))
1942 (magit-log-edit-push-to-comment-ring (buffer-string))
1943 (magit-log-edit-setup-author-env author)
1944 (magit-log-edit-set-fields nil)
1945 (magit-log-edit-cleanup)
1946 (if (= (buffer-size) 0)
1947 (insert "(Empty description)\n"))
1948 (let ((commit-buf (current-buffer)))
1949 (with-current-buffer (magit-find-buffer 'status default-directory)
1950 (cond (tag
1951 (magit-run-with-input commit-buf
1952 "git" "tag" tag "-a" "-F" "-"))
1954 (apply #'magit-run-with-input commit-buf
1955 "git" "commit" "-F" "-"
1956 (append (if (not (magit-anything-staged-p))
1957 '("--all") '())
1958 (if amend '("--amend") '())))))))
1959 (erase-buffer)
1960 (bury-buffer)
1961 (when magit-pre-log-edit-window-configuration
1962 (set-window-configuration magit-pre-log-edit-window-configuration)
1963 (setq magit-pre-log-edit-window-configuration nil))))
1965 (defun magit-log-edit-toggle-amending ()
1966 (interactive)
1967 (let* ((fields (magit-log-edit-get-fields))
1968 (cell (assq 'amend fields)))
1969 (if cell
1970 (rplacd cell (if (equal (cdr cell) "yes") "no" "yes"))
1971 (setq fields (acons 'amend "yes" fields))
1972 (magit-log-edit-append
1973 (magit-format-commit "HEAD" "%s%n%n%b")))
1974 (magit-log-edit-set-fields fields)))
1976 (defun magit-pop-to-log-edit (operation)
1977 (let ((dir default-directory)
1978 (buf (get-buffer-create "*magit-log-edit*")))
1979 (setq magit-pre-log-edit-window-configuration
1980 (current-window-configuration))
1981 (pop-to-buffer buf)
1982 (setq default-directory dir)
1983 (magit-log-edit-mode)
1984 (message "Type C-c C-c to %s." operation)))
1986 (defun magit-log-edit ()
1987 (interactive)
1988 (magit-log-edit-set-field 'tag nil)
1989 (magit-pop-to-log-edit "commit"))
1991 (defun magit-add-log ()
1992 (interactive)
1993 (let ((section (magit-current-section)))
1994 (let ((fun (if (eq (magit-section-type section) 'hunk)
1995 (save-window-excursion
1996 (save-excursion
1997 (magit-visit-item)
1998 (add-log-current-defun)))
1999 nil))
2000 (file (magit-diff-item-file
2001 (cond ((eq (magit-section-type section) 'hunk)
2002 (magit-hunk-item-diff section))
2003 ((eq (magit-section-type section) 'diff)
2004 section)
2006 (error "No change at point"))))))
2007 (magit-log-edit)
2008 (goto-char (point-min))
2009 (cond ((not (search-forward-regexp (format "^\\* %s" (regexp-quote file))
2010 nil t))
2011 ;; No entry for file, create it.
2012 (goto-char (point-max))
2013 (insert (format "\n* %s" file))
2014 (if fun
2015 (insert (format " (%s)" fun)))
2016 (insert ": "))
2017 (fun
2018 ;; found entry for file, look for fun
2019 (let ((limit (or (save-excursion
2020 (and (search-forward-regexp "^\\* " nil t)
2021 (match-beginning 0)))
2022 (point-max))))
2023 (cond ((search-forward-regexp (format "(.*\\<%s\\>.*):"
2024 (regexp-quote fun))
2025 limit t)
2026 ;; found it, goto end of current entry
2027 (if (search-forward-regexp "^(" limit t)
2028 (backward-char 2)
2029 (goto-char limit)))
2031 ;; not found, insert new entry
2032 (goto-char limit)
2033 (if (bolp)
2034 (open-line 1)
2035 (newline))
2036 (insert (format "(%s): " fun))))))))))
2038 ;;; Tags
2040 (defun magit-tag (name)
2041 (interactive "sNew tag name: ")
2042 (magit-run "git" "tag" name))
2044 (defun magit-annotated-tag (name)
2045 (interactive "sNew tag name: ")
2046 (magit-log-edit-set-field 'tag name)
2047 (magit-pop-to-log-edit "tag"))
2049 ;;; Stashing
2051 (defun magit-wash-stash ()
2052 (if (search-forward-regexp "stash@{\\(.*\\)}" (line-end-position) t)
2053 (let ((stash (match-string-no-properties 0))
2054 (name (match-string-no-properties 1)))
2055 (delete-region (match-beginning 0) (match-end 0))
2056 (goto-char (match-beginning 0))
2057 (fixup-whitespace)
2058 (goto-char (line-beginning-position))
2059 (insert name)
2060 (goto-char (line-beginning-position))
2061 (magit-with-section stash 'stash
2062 (magit-set-section-info stash)
2063 (forward-line)))
2064 (forward-line))
2067 (defun magit-wash-stashes ()
2068 (let ((magit-old-top-section nil))
2069 (magit-wash-sequence #'magit-wash-stash)))
2071 (defun magit-insert-stashes ()
2072 (magit-insert-section 'stashes
2073 "Stashes:" 'magit-wash-stashes
2075 "git" "stash" "list"))
2077 (defun magit-stash (description)
2078 (interactive "sStash description: ")
2079 (magit-run "git" "stash" "save" description))
2081 ;;; Commits
2083 (defun magit-commit-at-point (&optional nil-ok-p)
2084 (let* ((section (magit-current-section))
2085 (commit (and (eq (magit-section-type section) 'commit)
2086 (magit-section-info section))))
2087 (if nil-ok-p
2088 commit
2089 (or commit
2090 (error "No commit at point.")))))
2092 (defun magit-apply-commit (commit)
2093 (let ((parent (magit-choose-parent commit "apply")))
2094 (magit-log-edit-append
2095 (magit-format-commit commit "%s%n%n%b"))
2096 (magit-log-edit-set-field
2097 'author
2098 (magit-format-commit commit "%an <%ae>, %ai"))
2099 (magit-run-shell "git diff %s %s | git apply -" parent commit)))
2101 (defun magit-cherry-pick-commit (commit &optional noerase)
2102 (let ((parent-id (magit-choose-parent-id commit "cherry-pick")))
2103 (magit-run* `("git" "cherry-pick"
2104 ,@(if parent-id
2105 (list "-m" (number-to-string parent-id)))
2106 ,commit)
2107 nil noerase)))
2109 (defun magit-apply-item ()
2110 (interactive)
2111 (magit-section-action (item info "apply")
2112 ((pending commit)
2113 (magit-apply-commit info)
2114 (magit-rewrite-set-commit-property info 'used t))
2115 ((commit)
2116 (magit-apply-commit info))
2117 ((unstaged *)
2118 (error "Change is already in your working tree"))
2119 ((staged *)
2120 (error "Change is already in your working tree"))
2121 ((hunk)
2122 (magit-apply-hunk-item item))
2123 ((diff)
2124 (magit-apply-diff-item item))
2125 ((stash)
2126 (magit-run "git" "stash" "apply" info))))
2128 (defun magit-cherry-pick-item ()
2129 (interactive)
2130 (magit-section-action (item info "cherry-pick")
2131 ((pending commit)
2132 (magit-cherry-pick-commit info)
2133 (magit-rewrite-set-commit-property info 'used t))
2134 ((commit)
2135 (magit-cherry-pick-commit info))
2136 ((stash)
2137 (magit-run "git" "stash" "pop" info))))
2139 (defun magit-revert-commit (commit)
2140 (let ((parent (magit-choose-parent commit "revert")))
2141 (magit-log-edit-append
2142 (magit-format-commit commit "Reverting \"%s\""))
2143 (magit-run-shell "git diff %s %s | git apply --reverse -" parent commit)))
2145 (defun magit-revert-item ()
2146 (interactive)
2147 (magit-section-action (item info "revert")
2148 ((pending commit)
2149 (magit-revert-commit info)
2150 (magit-rewrite-set-commit-property info 'used nil))
2151 ((commit)
2152 (magit-revert-commit info))
2153 ((hunk)
2154 (magit-apply-hunk-item item "--reverse"))
2155 ((diff)
2156 (magit-apply-diff-item item "--reverse"))))
2158 (defvar magit-have-graph 'unset)
2159 (defvar magit-have-decorate 'unset)
2161 (defun magit-configure-have-graph ()
2162 (if (eq magit-have-graph 'unset)
2163 (let ((res (magit-shell-exit-code "git log --graph --max-count=0")))
2164 (message "result %s" res)
2165 (setq magit-have-graph (eq res 0)))))
2167 (defun magit-configure-have-decorate ()
2168 (if (eq magit-have-decorate 'unset)
2169 (let ((res (magit-shell-exit-code "git log --decorate --max-count=0")))
2170 (setq magit-have-decorate (eq res 0)))))
2172 (defun magit-refresh-log-buffer (range args)
2173 (magit-configure-have-graph)
2174 (magit-configure-have-decorate)
2175 (magit-create-buffer-sections
2176 (apply #'magit-insert-section 'log
2177 (magit-rev-range-describe range "Commits")
2178 'magit-wash-log nil
2179 `("git" "log" "--max-count=1000" "--pretty=oneline"
2180 ,@(if magit-have-decorate (list "--decorate"))
2181 ,@(if magit-have-graph (list "--graph"))
2182 ,args))))
2184 (defun magit-log (range)
2185 (interactive (list (magit-read-rev-range "Log" (magit-get-current-branch))))
2186 (if range
2187 (let* ((topdir (magit-get-top-dir default-directory))
2188 (args (magit-rev-range-to-git range)))
2189 (switch-to-buffer "*magit-log*")
2190 (magit-mode-init topdir 'log #'magit-refresh-log-buffer range args))))
2192 (defun magit-log-head ()
2193 (interactive)
2194 (magit-log "HEAD"))
2196 ;;; Reflog
2198 (defun magit-refresh-reflog-buffer (head args)
2199 (magit-create-buffer-sections
2200 (magit-insert-section 'reflog
2201 (format "Local history of head %s" head)
2202 'magit-wash-log nil
2203 "git" "log" "--walk-reflogs"
2204 "--max-count=1000"
2205 "--pretty=oneline" args)))
2207 (defun magit-reflog (head)
2208 (interactive (list (magit-read-rev "Reflog of" "HEAD")))
2209 (if head
2210 (let* ((topdir (magit-get-top-dir default-directory))
2211 (args (magit-rev-to-git head)))
2212 (switch-to-buffer "*magit-reflog*")
2213 (magit-mode-init topdir 'reflog
2214 #'magit-refresh-reflog-buffer head args))))
2216 (defun magit-reflog-head ()
2217 (interactive)
2218 (magit-reflog "HEAD"))
2220 ;;; Diffing
2222 (defun magit-refresh-diff-buffer (range args)
2223 (magit-create-buffer-sections
2224 (magit-insert-section 'diffbuf
2225 (magit-rev-range-describe range "Changes")
2226 'magit-wash-diffs nil
2227 "git" "diff" args)))
2229 (defun magit-diff (range)
2230 (interactive (list (magit-read-rev-range "Diff")))
2231 (if range
2232 (let* ((dir default-directory)
2233 (args (magit-rev-range-to-git range))
2234 (buf (get-buffer-create "*magit-diff*")))
2235 (display-buffer buf)
2236 (save-excursion
2237 (set-buffer buf)
2238 (magit-mode-init dir 'diff #'magit-refresh-diff-buffer range args)))))
2240 (defun magit-diff-working-tree (rev)
2241 (interactive (list (magit-read-rev "Diff with (default HEAD)")))
2242 (magit-diff (or rev "HEAD")))
2244 (defun magit-diff-with-mark ()
2245 (interactive)
2246 (magit-diff (cons (magit-marked-commit)
2247 (magit-commit-at-point))))
2249 ;;; Miscellaneous
2251 (defun magit-ignore-file (file edit local)
2252 (let ((ignore-file (if local ".git/info/exclude" ".gitignore")))
2253 (if edit
2254 (setq file (read-string "File to ignore: " file)))
2255 (append-to-file (concat "/" file "\n") nil ignore-file)
2256 (magit-need-refresh)))
2258 (defun magit-ignore-item ()
2259 (interactive)
2260 (magit-section-action (item info "ignore")
2261 ((untracked file)
2262 (magit-ignore-file info current-prefix-arg nil))))
2264 (defun magit-ignore-item-locally ()
2265 (interactive)
2266 (magit-section-action (item info "ignore")
2267 ((untracked file)
2268 (magit-ignore-file info current-prefix-arg t))))
2270 (defun magit-discard-diff (diff)
2271 (let ((kind (magit-diff-item-kind diff))
2272 (file (magit-diff-item-file diff)))
2273 (cond ((eq kind 'deleted)
2274 (when (yes-or-no-p (format "Resurrect %s? " file))
2275 (magit-shell "git reset -q -- %s" file)
2276 (magit-run "git" "checkout" "--" file)))
2277 ((eq kind 'new)
2278 (if (yes-or-no-p (format "Delete %s? " file))
2279 (magit-run "git" "rm" "-f" "--" file)))
2281 (if (yes-or-no-p (format "Discard changes to %s? " file))
2282 (magit-run "git" "checkout" "--" file))))))
2284 (defun magit-discard-item ()
2285 (interactive)
2286 (magit-section-action (item info "discard")
2287 ((untracked file)
2288 (if (yes-or-no-p (format "Delete %s? " info))
2289 (magit-run "rm" info)))
2290 ((unstaged diff hunk)
2291 (when (yes-or-no-p "Discard hunk? ")
2292 (magit-apply-hunk-item item "--reverse")))
2293 ((staged diff hunk)
2294 (if (magit-file-uptodate-p (magit-diff-item-file
2295 (magit-hunk-item-diff item)))
2296 (when (yes-or-no-p "Discard hunk? ")
2297 (magit-apply-hunk-item item "--reverse" "--index"))
2298 (error "Can't discard this hunk. Please unstage it first.")))
2299 ((unstaged diff)
2300 (magit-discard-diff item))
2301 ((staged diff)
2302 (magit-discard-diff item))
2303 ((hunk)
2304 (error "Can't discard this hunk"))
2305 ((diff)
2306 (error "Can't discard this diff"))
2307 ((stash)
2308 (when (yes-or-no-p "Discard stash? ")
2309 (magit-run "git" "stash" "drop" info)))))
2311 (defun magit-visit-item ()
2312 (interactive)
2313 (magit-section-action (item info "visit")
2314 ((untracked file)
2315 (find-file info))
2316 ((diff)
2317 (find-file (magit-diff-item-file item)))
2318 ((hunk)
2319 (let ((file (magit-diff-item-file (magit-hunk-item-diff item)))
2320 (line (magit-hunk-item-target-line item)))
2321 (find-file file)
2322 (goto-line line)))
2323 ((commit)
2324 (magit-show-commit info)
2325 (pop-to-buffer "*magit-commit*"))))
2327 (defun magit-show-item-or-scroll-up ()
2328 (interactive)
2329 (magit-section-action (item info)
2330 ((commit)
2331 (magit-show-commit info #'scroll-up))))
2333 (defun magit-show-item-or-scroll-down ()
2334 (interactive)
2335 (magit-section-action (item info)
2336 ((commit)
2337 (magit-show-commit info #'scroll-down))))
2339 (defun magit-mark-item ()
2340 (interactive)
2341 (magit-section-action (item info "mark")
2342 ((commit)
2343 (magit-set-marked-commit info))))
2345 (defun magit-describe-item ()
2346 (interactive)
2347 (let ((section (magit-current-section)))
2348 (message "Section: %s %s-%s %S %S"
2349 (magit-section-type section)
2350 (magit-section-beginning section)
2351 (magit-section-end section)
2352 (magit-section-title section)
2353 (magit-section-info section))))
2355 (defun magit-copy-item-as-kill ()
2356 "Copy sha1 of commit at point into kill ring."
2357 (interactive)
2358 (magit-section-action (item info "copy")
2359 ((commit)
2360 (kill-new info)
2361 (message "%s" info))))
2363 (provide 'magit)