Update magit
[emacs.git] / .emacs.d / elisp / magit / magit.el
index 121ec9c..71e7b8f 100644 (file)
@@ -1,55 +1,30 @@
 ;;; magit.el --- control Git from Emacs
 
 ;;; magit.el --- control Git from Emacs
 
-;; Copyright (C) 2010 Aaron Culich.
-;; Copyright (C) 2010 Alan Falloon.
-;; Copyright (C) 2008, 2010 Alex Ott.
-;; Copyright (C) 2008, 2009, 2010 Alexey Voinov.
-;; Copyright (C) 2010 Ben Walton.
-;; Copyright (C) 2010 Chris Bernard.
-;; Copyright (C) 2010 Christian Kluge.
-;; Copyright (C) 2008 Daniel Farina.
-;; Copyright (C) 2010 David Abrahams.
-;; Copyright (C) 2009 David Wallin.
-;; Copyright (C) 2009, 2010 Hannu Koivisto.
-;; Copyright (C) 2009 Ian Eure.
-;; Copyright (C) 2009 Jesse Alama.
-;; Copyright (C) 2009 John Wiegley.
-;; Copyright (C) 2010 Leo.
-;; Copyright (C) 2008, 2009 Marcin Bachry.
-;; Copyright (C) 2008, 2009 Marius Vollmer.
-;; Copyright (C) 2010 Mark Hepburn.
-;; Copyright (C) 2010 Moritz Bunkus.
-;; Copyright (C) 2010 Nathan Weizenbaum.
-;; Copyright (C) 2010 Oscar Fuentes.
-;; Copyright (C) 2009 Pavel Holejsovsky.
-;; Copyright (C) 2011-2012 Peter J Weisberg
-;; Copyright (C) 2009, 2010 Phil Jackson.
-;; Copyright (C) 2010 Philip Weaver.
-;; Copyright (C) 2010 Ramkumar Ramachandra.
-;; Copyright (C) 2010 Remco van 't Veer.
-;; Copyright (C) 2009 René Stadler.
-;; Copyright (C) 2010 Robin Green.
-;; Copyright (C) 2010 Roger Crew.
-;; Copyright (C) 2009, 2010, 2011 Rémi Vanicat.
-;; Copyright (C) 2010 Sean Bryant.
-;; Copyright (C) 2009, 2011 Steve Purcell.
-;; Copyright (C) 2010 Timo Juhani Lindfors.
-;; Copyright (C) 2010, 2011 Yann Hodique.
-;; Copyright (C) 2010 Ævar Arnfjörð Bjarmason.
-;; Copyright (C) 2010 Óscar Fuentes.
-
-;; Original Author: Marius Vollmer <marius.vollmer@nokia.com>
-;; Former Maintainer: Phil Jackson <phil@shellarchive.co.uk>
-;; Maintenance Group: https://github.com/organizations/magit/teams/53130
-;;   Currently composed of:
-;;   - Phil Jackson
-;;   - Peter J Weisberg
-;;   - Yann Hodique
-;;   - Rémi Vanicat
-;; Version: 1.2.0
-;; Keywords: tools
-
+;; Copyright (C) 2008-2014  The Magit Project Developers
 ;;
 ;;
+;; For a full list of contributors, see the AUTHORS.md file
+;; at the top-level directory of this distribution and at
+;; https://raw.github.com/magit/magit/master/AUTHORS.md
+
+;; Author: Marius Vollmer <marius.vollmer@gmail.com>
+;; Maintainer: Jonas Bernoulli <jonas@bernoul.li>
+;; Former-Maintainers:
+;;     Nicolas Dudebout  <nicolas.dudebout@gatech.edu>
+;;     Peter J. Weisberg <pj@irregularexpressions.net>
+;;     Phil Jackson      <phil@shellarchive.co.uk>
+;;     Rémi Vanicat      <vanicat@debian.org>
+;;     Yann Hodique      <yann.hodique@gmail.com>
+
+;; Keywords: vc tools
+;; Package: magit
+;; Package-Requires: ((emacs "23.2") (cl-lib "0.3") (git-commit-mode "0.14.0") (git-rebase-mode "0.14.0"))
+
+;; Magit requires at least GNU Emacs 23.2 and Git 1.7.2.5.
+;; These are the versions shipped by Debian oldstable (6.0, Squeeze).
+
+;; Contains code from GNU Emacs <https://www.gnu.org/software/emacs/>,
+;; released under the GNU General Public License version 3 or later.
+
 ;; Magit is free software; you can redistribute it and/or modify it
 ;; under the terms of the GNU General Public License as published by
 ;; the Free Software Foundation; either version 3, or (at your option)
 ;; Magit is free software; you can redistribute it and/or modify it
 ;; under the terms of the GNU General Public License as published by
 ;; the Free Software Foundation; either version 3, or (at your option)
 
 ;;; Code:
 
 
 ;;; Code:
 
-(eval-when-compile (require 'cl))
-(require 'log-edit)
-(require 'easymenu)
-(require 'diff-mode)
-
-;; Silences byte-compiler warnings
-(eval-and-compile
-  (unless (fboundp 'declare-function) (defmacro declare-function (&rest args))))
-
-(eval-when-compile  (require 'view))
-(declare-function view-mode 'view)
-(eval-when-compile (require 'iswitchb))
-(eval-when-compile (require 'ido))
-(eval-when-compile (require 'ediff))
+(defvar magit-version 'undefined
+  "The version of Magit that you're using.
+Use the function by the same name instead of this variable.")
+;; The value is set at the end of this file, using the
+;; function `magit-version' which is also defined there.
 
 
-;; Dummy to be used by the defcustoms when first loading the file.
-(eval-when (load eval)
-  (defalias 'magit-set-variable-and-refresh 'set-default))
+;;;; Dependencies
 
 
-;;; Code:
-(defgroup magit nil
-  "Controlling Git from Emacs."
-  :prefix "magit-"
-  :group 'tools)
+(when (version< emacs-version "23.2")
+  (error "Magit requires at least GNU Emacs 23.2"))
 
 
-(defcustom magit-git-executable "git"
-  "The name of the Git executable."
-  :group 'magit
-  :type 'string)
+;; Users may choose to use `magit-log-edit' instead of the preferred
+;; `git-commit-mode', by simply putting it on the `load-path'.  If
+;; it can be found there then it is loaded at the end of this file.
+(unless (locate-library "magit-log-edit")
+  (require 'git-commit-mode))
 
 
-(defcustom magit-gitk-executable (concat (file-name-directory magit-git-executable)
-                                         "gitk")
-  "The name of the Gitk executable."
-  :group 'magit
-  :type 'string)
+(require 'git-rebase-mode)
 
 
-(defcustom magit-git-standard-options '("--no-pager")
-  "Standard options when running Git."
-  :group 'magit
-  :type '(repeat string))
+(require 'ansi-color)
+(require 'autorevert)
+(require 'cl-lib)
+(require 'diff-mode)
+(require 'easymenu)
+(require 'epa)
+(require 'format-spec)
+(require 'grep)
+(require 'help-mode)
+(require 'ring)
+(require 'server)
+(require 'tramp)
+(require 'view)
+
+(eval-when-compile
+  (require 'dired)
+  (require 'dired-x)
+  (require 'ediff)
+  (require 'eshell)
+  (require 'ido)
+  (require 'iswitchb)
+  (require 'package nil t)
+  (require 'view))
+
+;;;; Declarations
+
+(if (featurep 'vc-git)
+    (defalias 'magit-grep 'vc-git-grep)
+  (defalias 'magit-grep 'lgrep))
+
+(declare-function dired-jump 'dired-x)
+(declare-function dired-uncache 'dired)
+(declare-function ediff-cleanup-mess 'ediff)
+(declare-function eshell-parse-arguments 'eshell)
+(declare-function ido-completing-read 'ido)
+(declare-function iswitchb-read-buffer 'iswitchb)
+(declare-function package-desc-vers 'package)
+(declare-function package-desc-version 'package)
+(declare-function package-version-join 'package)
+(declare-function view-mode 'view)
 
 
-(defcustom magit-repo-dirs nil
-  "Directories containing Git repositories.
-Magit will look into these directories for Git repositories and
-offer them as choices for `magit-status'."
-  :group 'magit
-  :type '(repeat string))
+(defvar git-commit-previous-winconf)
+(defvar magit-commit-buffer-name)
+(defvar magit-custom-options)
+(defvar magit-log-buffer-name)
+(defvar magit-marked-commit)
+(defvar magit-process-buffer-name)
+(defvar magit-reflog-buffer-name)
+(defvar magit-refresh-args)
+(defvar magit-stash-buffer-name)
+(defvar magit-status-buffer-name)
+(defvar magit-this-process)
+(defvar package-alist)
+
+;;;; Compatibility
 
 
-(defcustom magit-repo-dirs-depth 3
-  "The maximum depth to look for Git repos.
-When looking for a Git repository below the directories in `magit-repo-dirs',
-Magit will only descend this many levels deep."
-  :group 'magit
-  :type 'integer)
+(eval-and-compile
 
 
-(defcustom magit-set-upstream-on-push nil
-  "Non-nil means that `magit-push' may use --set-upstream when pushing a branch.
-This only applies if the branch does not have an upstream set yet.
-Setting this to t will ask if --set-upstream should be used.
-Setting it to 'dontask will always use --set-upstream.
-Setting it to 'refuse will refuse to push unless a remote branch has already been set.
+  ;; Added in Emacs 24.3
+  (unless (fboundp 'user-error)
+    (defalias 'user-error 'error))
+
+  ;; Added in Emacs 24.3 (mirrors/emacs@b335efc3).
+  (unless (fboundp 'setq-local)
+    (defmacro setq-local (var val)
+      "Set variable VAR to value VAL in current buffer."
+      (list 'set (list 'make-local-variable (list 'quote var)) val)))
+
+  ;; Added in Emacs 24.3 (mirrors/emacs@b335efc3).
+  (unless (fboundp 'defvar-local)
+    (defmacro defvar-local (var val &optional docstring)
+      "Define VAR as a buffer-local variable with default value VAL.
+Like `defvar' but additionally marks the variable as being automatically
+buffer-local wherever it is set."
+      (declare (debug defvar) (doc-string 3))
+      (list 'progn (list 'defvar var val docstring)
+            (list 'make-variable-buffer-local (list 'quote var)))))
+
+  ;; Added in Emacs 24.1
+  (unless (fboundp 'run-hook-wrapped)
+    (defun run-hook-wrapped-1 (hook fns wrap-function &rest args)
+      (cl-loop for fn in fns
+               if (and (eq fn t)
+                       (local-variable-p hook)
+                       (default-boundp hook)
+                       (apply 'run-hook-wrapped-1 nil
+                              (default-value hook) wrap-function args))
+               return it
+               else if (and (functionp fn) (apply wrap-function fn args))
+               return it))
+
+    (defun run-hook-wrapped  (hook wrap-function &rest args)
+      "Run HOOK, passing each function through WRAP-FUNCTION.
+I.e. instead of calling each function FUN directly with arguments ARGS,
+it calls WRAP-FUNCTION with arguments FUN and ARGS.
+As soon as a call to WRAP-FUNCTION returns non-nil, `run-hook-wrapped'
+aborts and returns that value."
+      (when (boundp hook)
+        (let ((fns (symbol-value hook)))
+          (apply 'run-hook-wrapped-1 hook
+                 (if (functionp fns) (list fns) fns)
+                 wrap-function args)))))
+  )
+
+\f
+;;; Settings
+;;;; Custom Groups
 
 
---set-upstream is supported with git > 1.7.0"
-  :group 'magit
-  :type '(choice (const :tag "Never" nil)
-                 (const :tag "Ask" t)
-                 (const :tag "Refuse" refuse)
-                 (const :tag "Always" dontask)))
+(defgroup magit nil
+  "Controlling Git from Emacs."
+  :group 'tools)
 
 
-(defcustom magit-save-some-buffers t
-  "Non-nil means that \\[magit-status] will save modified buffers before running.
-Setting this to t will ask which buffers to save, setting it to 'dontask will
-save all modified buffers without asking."
-  :group 'magit
-  :type '(choice (const :tag "Never" nil)
-                 (const :tag "Ask" t)
-                 (const :tag "Save without asking" dontask)))
+(defgroup magit-process nil
+  "Git and other external processes used by Magit."
+  :group 'magit)
 
 
-(defcustom magit-save-some-buffers-predicate
-  'magit-save-buffers-predicate-tree-only
-  "A predicate function to decide whether to save a buffer.
+(defgroup magit-modes nil
+  "Modes provided by Magit."
+  :group 'magit)
 
 
-Used by function `magit-save-some-buffers' when the variable of
-the same name is non-nil."
+(defgroup magit-status nil
+  "Inspect and manipulate Git repositories."
+  :group 'magit-modes)
 
 
-  :group 'magit
-  :type '(radio (function-item magit-save-buffers-predicate-tree-only)
-                (function-item magit-save-buffers-predicate-all)
-                (function :tag "Other")))
+(defgroup magit-diff nil
+  "Inspect and manipulate Git diffs."
+  :group 'magit-modes)
 
 
-(defcustom magit-default-tracking-name-function
-  'magit-default-tracking-name-remote-plus-branch
-  "Specifies the function to use to generate default tracking branch names
-when doing a \\[magit-checkout].
+(defgroup magit-commit nil
+  "Inspect and manipulate Git commits."
+  :group 'magit-modes)
 
 
-The default is magit-default-tracking-name-remote-plus-branch,
-which generates a tracking name of the form 'REMOTE-BRANCHNAME'."
-  :group 'magit
-  :type '(radio (function-item magit-default-tracking-name-remote-plus-branch)
-                (function-item magit-default-tracking-name-branch-only)
-                (function :tag "Other")))
+(defgroup magit-log nil
+  "Inspect and manipulate Git history."
+  :group 'magit-modes)
 
 
-(defcustom magit-commit-all-when-nothing-staged 'ask
-  "Determines what \\[magit-log-edit] does when nothing is staged.
-Setting this to nil will make it do nothing, setting it to t will
-arrange things so that the actual commit command will use the \"--all\" option,
-setting it to 'ask will first ask for confirmation whether to do this,
-and setting it to 'ask-stage will cause all changes to be staged,
-after a confirmation."
-  :group 'magit
-  :type '(choice (const :tag "No" nil)
-                 (const :tag "Always" t)
-                 (const :tag "Ask" ask)
-                 (const :tag "Ask to stage everything" ask-stage)))
+(defgroup magit-extensions nil
+  "Extensions to Magit."
+  :group 'magit)
 
 
-(defcustom magit-commit-signoff nil
-  "Add the \"Signed-off-by:\" line when committing."
+(defgroup magit-faces nil
+  "Faces used by Magit."
   :group 'magit
   :group 'magit
-  :type 'boolean)
+  :group 'faces)
+
+(when (featurep 'gitattributes-mode)
+  (custom-add-to-group 'magit-modes 'gitattributes-mode 'custom-group))
+
+(when (featurep 'git-commit-mode)
+  (custom-add-to-group 'magit-modes 'git-commit       'custom-group)
+  (custom-add-to-group 'magit-faces 'git-commit-faces 'custom-group))
+
+(when (featurep 'git-rebase-mode)
+  (custom-add-to-group 'magit-modes 'git-rebase       'custom-group)
+  (custom-add-to-group 'magit-faces 'git-rebase-faces 'custom-group))
+
+(custom-add-to-group 'magit 'vc-follow-symlinks 'custom-variable)
+
+;;;; Custom Options
+;;;;; Processes
+
+(defcustom magit-git-executable
+  (or (and (eq system-type 'windows-nt)
+           ;; On Windows asking for "git" from $PATH might also return
+           ;; a "git.exe" or "git.cmd".  Using "bin/git.exe" directly
+           ;; is faster than using one of the wrappers "cmd/git.exe"
+           ;; or "cmd/git.cmd".  The wrappers are likely to come
+           ;; earlier on $PATH, and so we have to exlicitly use
+           ;; the former.
+           (let ((exe (executable-find "git.exe")))
+             (when exe
+               (let ((alt (directory-file-name (file-name-directory exe))))
+                 (if (and (equal (file-name-nondirectory alt) "cmd")
+                          (setq alt (expand-file-name
+                                     (convert-standard-filename "bin/git.exe")
+                                     (file-name-directory alt)))
+                          (file-executable-p alt))
+                     alt
+                   exe)))))
+      (executable-find "git") "git")
+  "The Git executable used by Magit."
+  :group 'magit-process
+  :type 'string)
 
 
-(defcustom magit-sha1-abbrev-length 7
-  "The number of digits to show when a sha1 is displayed in abbreviated form."
-  :group 'magit
-  :type 'integer)
+(defcustom magit-git-standard-options
+  '("--no-pager" "-c" "core.preloadindex=true")
+  "Standard options when running Git.
+Be careful what you add here, especially if you are using
+tramp to connect to servers with ancient Git versions."
+  :group 'magit-process
+  :type '(repeat string))
 
 
-(defcustom magit-log-cutoff-length 100
-  "The maximum number of commits to show in the log and whazzup buffers."
-  :group 'magit
-  :type 'integer)
+(defcustom magit-gitk-executable
+  (or (and (eq system-type 'windows-nt)
+           (let ((exe (expand-file-name
+                       "gitk" (file-name-nondirectory magit-git-executable))))
+             (and (file-executable-p exe) exe)))
+      (executable-find "gitk") "gitk")
+  "The Gitk executable."
+  :group 'magit-process
+  :set-after '(magit-git-executable)
+  :type 'string)
 
 
-(defcustom magit-log-infinite-length 99999
-  "Number of log used to show as maximum for `magit-log-cutoff-length'."
-  :group 'magit
-  :type 'integer)
+(defcustom magit-emacsclient-executable
+  (ignore-errors
+    (shell-quote-argument
+     (let ((version
+            (format "%s.%s" emacs-major-version emacs-minor-version)))
+       (or (and (eq system-type 'darwin)
+                (let ((exec-path
+                       (list (expand-file-name "bin" invocation-directory))))
+                  (executable-find "emacsclient")))
+           (executable-find (format "emacsclient%s"   version))
+           (executable-find (format "emacsclient-%s"   version))
+           (executable-find (format "emacsclient%s.exe" version))
+           (executable-find (format "emacsclient-%s.exe" version))
+           (executable-find (format "emacsclient%s"   emacs-major-version))
+           (executable-find (format "emacsclient-%s"   emacs-major-version))
+           (executable-find (format "emacsclient%s.exe" emacs-major-version))
+           (executable-find (format "emacsclient-%s.exe" emacs-major-version))
+           (executable-find "emacsclient")
+           (executable-find "emacsclient.exe")))))
+  "The Emacsclient executable.
+
+The default value is the full path to the emacsclient executable
+located in the same directory as the executable of the current
+Emacs instance.  If the emacsclient cannot be located in that
+directory then the first executable found anywhere on the
+`exec-path' is used instead.
+
+If no executable can be located then nil becomes the default
+value, and some important Magit commands will fallback to an
+alternative code path.  However `magit-interactive-rebase'
+will stop working at all."
+  :package-version '(magit . "2.0.0")
+  :group 'magit-process
+  :type '(choice (string :tag "Executable")
+                 (const :tag "Don't use Emacsclient" nil)))
 
 
-(defcustom magit-log-auto-more nil
-  "Insert more log entries automatically when moving past the last entry.
+(defcustom magit-process-connection-type (not (eq system-type 'cygwin))
+  "Connection type used for the git process.
 
 
-Only considered when moving past the last entry with
-`magit-goto-*-section' commands."
-  :group 'magit
-  :type 'boolean)
+If nil, use pipes: this is usually more efficient, and works on Cygwin.
+If t, use ptys: this enables magit to prompt for passphrases when needed."
+  :group 'magit-process
+  :type '(choice (const :tag "pipe" nil)
+                 (const :tag "pty" t)))
 
 (defcustom magit-process-popup-time -1
   "Popup the process buffer if a command takes longer than this many seconds."
 
 (defcustom magit-process-popup-time -1
   "Popup the process buffer if a command takes longer than this many seconds."
-  :group 'magit
+  :group 'magit-process
   :type '(choice (const :tag "Never" -1)
                  (const :tag "Immediately" 0)
                  (integer :tag "After this many seconds")))
 
   :type '(choice (const :tag "Never" -1)
                  (const :tag "Immediately" 0)
                  (integer :tag "After this many seconds")))
 
-(defcustom magit-revert-item-confirm t
-  "Require acknowledgment before reverting an item."
-  :group 'magit
+(defcustom magit-process-log-max 32
+  "Maximum number of sections to keep in a process log buffer.
+When adding a new section would go beyond the limit set here,
+then the older half of the sections are remove.  Sections that
+belong to processes that are still running are never removed."
+  :package-version '(magit . "2.0.0")
+  :group 'magit-process
+  :type 'integer)
+
+(defcustom magit-process-quote-curly-braces
+  (and (eq system-type 'windows-nt)
+       (let ((case-fold-search t))
+         (string-match-p "cygwin" magit-git-executable))
+       t)
+  "Whether curly braces should be quoted when calling git.
+This may be necessary when using Windows.  On all other system
+types this must always be nil.
+
+We are not certain when quoting is needed, but it appears it is
+needed when using Cygwin Git but not when using stand-alone Git.
+The default value is set based on that assumptions.  If this
+turns out to be wrong you can customize this option but please
+also comment on issue #816."
+  :package-version '(magit . "2.0.0")
+  :group 'magit-process
+  :set-after '(magit-git-executable)
   :type 'boolean)
 
   :type 'boolean)
 
-(defcustom magit-log-edit-confirm-cancellation nil
-  "Require acknowledgment before canceling the log edit buffer."
+(defcustom magit-process-yes-or-no-prompt-regexp
+   " [\[(]\\([Yy]\\(?:es\\)?\\)[/|]\\([Nn]o?\\)[\])] ?[?:] ?$"
+  "Regexp matching Yes-or-No prompts of git and its subprocesses."
+  :package-version '(magit . "2.0.0")
+  :group 'magit-process
+  :type 'regexp)
+
+(defcustom magit-process-password-prompt-regexps
+  '("^\\(Enter \\)?[Pp]assphrase\\( for \\(RSA \\)?key '.*'\\)?: ?$"
+    "^\\(Enter \\)?[Pp]assword\\( for '.*'\\)?: ?$"
+    "^.*'s password: ?$"
+    "^Yubikey for .*: ?$")
+  "List of regexps matching password prompts of git and its subprocesses."
+  :package-version '(magit . "2.0.0")
+  :group 'magit-process
+  :type '(repeat (regexp)))
+
+(defcustom magit-process-username-prompt-regexps
+  '("^Username for '.*': ?$")
+  "List of regexps matching username prompts of git and its subprocesses."
+  :package-version '(magit . "2.0.0")
+  :group 'magit-process
+  :type '(repeat (regexp)))
+
+(defconst magit-server-window-type
+  '(choice
+    (const :tag "Use selected window"
+           :match (lambda (widget value)
+                    (not (functionp value)))
+           nil)
+    (function-item :tag "Display in new frame" switch-to-buffer-other-frame)
+    (function-item :tag "Use pop-to-buffer" pop-to-buffer)
+    (function :tag "Other function")))
+
+(defcustom magit-server-window-for-commit 'pop-to-buffer
+  "Function used to select a window for displaying commit message buffers.
+It should take one argument (a buffer) and display and select it.
+A common value is `pop-to-buffer'.  It can also be nil in which
+case the selected window is used."
+  :package-version '(magit . "2.0.0")
+  :group 'magit-process
+  :type magit-server-window-type)
+
+(defcustom magit-server-window-for-rebase server-window
+  "Function used to select a window for displaying interactive rebase buffers.
+It should take one argument (a buffer) and display and select it.
+A common value is `pop-to-buffer'.  It can also be nil in which
+case the selected window is used."
+  :package-version '(magit . "2.0.0")
+  :group 'magit-process
+  :set-after '(server-window)
+  :type magit-server-window-type)
+
+;;;;; Staging
+
+(defcustom magit-stage-all-confirm t
+  "Whether to require confirmation before staging all changes.
+This reduces the risk of accidentally losing the index.  If
+nothing at all is staged yet, then always stage without requiring
+confirmation, because it can be undone without the risk of losing
+a carefully crafted index."
+  :package-version '(magit . "2.0.0")
   :group 'magit
   :type 'boolean)
 
   :group 'magit
   :type 'boolean)
 
-(defcustom magit-remote-ref-format 'branch-then-remote
-  "What format to use for autocompleting refs, in pariticular for remotes.
-
-Autocompletion is used by functions like `magit-checkout',
-`magit-interactive-rebase' and others which offer branch name
-completion.
-
-The value 'name-then-remote means remotes will be of the
-form \"name (remote)\", while the value 'remote-slash-name
-means that they'll be of the form \"remote/name\".  I.e. something that's
-listed as \"remotes/upstream/next\" by \"git branch -l -a\"
-will be \"upstream/next\"."
+(defcustom magit-unstage-all-confirm t
+  "Whether to require confirmation before unstaging all changes.
+This reduces the risk of accidentally losing of the index.  If
+there are no staged changes at all, then always unstage without
+confirmation, because it can be undone without the risk of losing
+a carefully crafted index."
+  :package-version '(magit . "2.0.0")
   :group 'magit
   :group 'magit
-  :type '(choice (const :tag "name (remote)" branch-then-remote)
-                 (const :tag "remote/name" remote-slash-branch)))
-
-(defcustom magit-process-connection-type (not (eq system-type 'cygwin))
-  "Connection type used for the git process.
+  :type 'boolean)
 
 
-If nil, use pipes: this is usually more efficient, and works on Cygwin.
-If t, use ptys: this enables magit to prompt for passphrases when needed."
+(defcustom magit-revert-item-confirm t
+  "Whether to require confirmation before reverting hunks.
+If you disable this, consider enabling `magit-revert-backup'
+instead."
   :group 'magit
   :group 'magit
-  :type '(choice (const :tag "pipe" nil)
-                 (const :tag "pty" t)))
+  :type 'boolean)
 
 
-(defcustom magit-completing-read-function 'magit-builtin-completing-read
-  "Function to be called when requesting input from the user."
+(defcustom magit-revert-backup nil
+  "Whether to backup a hunk before reverting it.
+The hunk is stored in \".git/magit/reverted.diff\" and can be
+applied using `magit-revert-undo'.  Older hunks are available
+in the same directory as numbered backup files and have to be
+applied manually.  Only individual hunks are backed up; when
+a complete file is reverted (which requires confirmation) no
+backup is created."
+  :package-version '(magit . "2.1.0")
   :group 'magit
   :group 'magit
-  :type '(radio (function-item magit-iswitchb-completing-read)
-                (function-item magit-ido-completing-read)
-                (function-item magit-builtin-completing-read)
-                (function :tag "Other")))
+  :type 'boolean)
 
 
-(defcustom magit-create-branch-behaviour 'at-head
-  "Where magit will create a new branch if not supplied a branchname or ref.
+(defcustom magit-save-some-buffers t
+  "Whether certain commands save modified buffers before running.
 
 
-The value 'at-head means a new branch will be created at the tip
-of your current branch, while the value 'at-point means magit
-will try to find a valid reference at point..."
+nil        don't save buffers.
+t          ask which buffers to save.
+`dontask'  save all buffers without asking."
   :group 'magit
   :group 'magit
-  :type '(choice (const :tag "at HEAD" at-head)
-                 (const :tag "at point" at-point)))
+  :type '(choice (const :tag "Never" nil)
+                 (const :tag "Ask" t)
+                 (const :tag "Save without asking" dontask)))
 
 
-(defcustom magit-status-buffer-switch-function 'pop-to-buffer
-  "Function for `magit-status' to use for switching to the status buffer.
+(defcustom magit-save-some-buffers-predicate
+  'magit-save-buffers-predicate-tree-only
+  "A predicate function to decide whether to save a buffer.
 
 
-The function is given one argument, the status buffer."
+Used by function `magit-save-some-buffers' when the variable of
+the same name is non-nil."
   :group 'magit
   :group 'magit
-  :type '(radio (function-item switch-to-buffer)
-                (function-item pop-to-buffer)
+  :type '(radio (function-item magit-save-buffers-predicate-tree-only)
+                (function-item magit-save-buffers-predicate-all)
                 (function :tag "Other")))
 
 (defcustom magit-rewrite-inclusive t
                 (function :tag "Other")))
 
 (defcustom magit-rewrite-inclusive t
@@ -313,23 +494,35 @@ cumbersome to use from the status buffer.
                  (const :tag "Never" nil)
                  (const :tag "Ask"   ask)))
 
                  (const :tag "Never" nil)
                  (const :tag "Ask"   ask)))
 
+;;;;; Highlighting
+
+(defun magit-set-variable-and-refresh (symbol value)
+  "Set SYMBOL to VALUE and call `magit-refresh-all'."
+  (set-default symbol value)
+  ;; If magit isn't fully loaded yet no buffer that might
+  ;; need refreshing can exist and we can take a shortcut.
+  ;; We also don't want everything to repeatedly refresh
+  ;; when evaluating this file.
+  (when (and (featurep 'magit) (not buffer-file-name))
+    (magit-refresh-all)))
+
 (defcustom magit-highlight-whitespace t
 (defcustom magit-highlight-whitespace t
-  "Specifies where to highlight whitespace errors.
+  "Specify where to highlight whitespace errors.
 See `magit-highlight-trailing-whitespace',
 `magit-highlight-indentation'.  The symbol t means in all diffs,
 See `magit-highlight-trailing-whitespace',
 `magit-highlight-indentation'.  The symbol t means in all diffs,
-'status means only in the status buffer, and nil means nowhere."
+`status' means only in the status buffer, and nil means nowhere."
   :group 'magit
   :group 'magit
+  :set 'magit-set-variable-and-refresh
   :type '(choice (const :tag "Always" t)
                  (const :tag "Never" nil)
   :type '(choice (const :tag "Always" t)
                  (const :tag "Never" nil)
-                 (const :tag "In status buffer" status))
-  :set 'magit-set-variable-and-refresh)
+                 (const :tag "In status buffer" status)))
 
 (defcustom magit-highlight-trailing-whitespace t
 
 (defcustom magit-highlight-trailing-whitespace t
-  "Highlight whitespace at the end of a line in diffs.
+  "Whether to highlight whitespace at the end of a line in diffs.
 Used only when `magit-highlight-whitespace' is non-nil."
   :group 'magit
 Used only when `magit-highlight-whitespace' is non-nil."
   :group 'magit
-  :type 'boolean
-  :set 'magit-set-variable-and-refresh)
+  :set 'magit-set-variable-and-refresh
+  :type 'boolean)
 
 (defcustom magit-highlight-indentation nil
   "Highlight the \"wrong\" indentation style.
 
 (defcustom magit-highlight-indentation nil
   "Highlight the \"wrong\" indentation style.
@@ -345,82 +538,665 @@ If the value is `tabs', highlight indentation with tabs.  If the
 value is an integer, highlight indentation with at least that
 many spaces.  Otherwise, highlight neither."
   :group 'magit
 value is an integer, highlight indentation with at least that
 many spaces.  Otherwise, highlight neither."
   :group 'magit
+  :set 'magit-set-variable-and-refresh
   :type `(repeat (cons (string :tag "Directory regexp")
                        (choice (const :tag "Tabs" tabs)
                                (integer :tag "Spaces" :value ,tab-width)
   :type `(repeat (cons (string :tag "Directory regexp")
                        (choice (const :tag "Tabs" tabs)
                                (integer :tag "Spaces" :value ,tab-width)
-                               (const :tag "Neither" nil))))
-  :set 'magit-set-variable-and-refresh)
+                               (const :tag "Neither" nil))))) ;^FIXME
+
+(defcustom magit-item-highlight-face 'magit-item-highlight
+  "The face used to highlight the current section.
+
+By default the highlighting of the current section is done using
+the background color specified by face `magit-item-highlight'.
+
+If you don't want to use the background to do the highlighting,
+this *might* by as easy as customizing that face.  However if you
+are using a theme, which in turn sets the background color of
+that face then, due to limitations in face inheritance when using
+themes, you might be forced to use another face.
+
+Unfortunately it is only possible to override a face attribute,
+set by a theme, but not to drop it entirely.  This means that one
+has to explicitly use the `default' background color, to make it
+appear *as if* the background wasn't used.
+
+One reason you might want to *not* use the background, is that
+doing so forces the use of overlays for parts of diffs and for
+refnames.  Using overlays potentially degrades performance when
+generating large diffs.  Also see option `magit-use-overlays'."
+  :package-version '(magit . "2.0.0")
+  :group 'magit
+  :group 'magit-faces
+  :type '(choice (const magit-item-highlight)
+                 (const bold)
+                 (face  :tag "Other face")
+                 (const :tag "Don't highlight" nil)))
+
+(defcustom magit-use-overlays
+  (not (eq magit-item-highlight-face 'bold))
+  "Whether to use overlays to highlight various diff components.
+
+This has to be non-nil if the current section is highlighted by
+changing the background color.  Otherwise background colors that
+hold semantic meaning, like that of the added and removed lines
+in diffs, as well as section headings, would be shadowed by the
+highlighting.
+
+To select the face used for highlighting customize the option
+`magit-item-highlight-face'.  If you set that to `bold' or some
+other face that does not use the background then you can set this
+option to nil.  Doing so could potentially improve performance
+when generating large diffs."
+  :package-version '(magit . "2.1.0")
+  :group 'magit
+  :group 'magit-faces
+  :set-after '(magit-item-highlight-face)
+  :type 'boolean)
 
 
-(defcustom magit-diff-refine-hunk nil
-  "Show fine (word-granularity) differences within diff hunks.
+(define-obsolete-variable-alias 'magit-diff-use-overlays
+  'magit-use-overlays "2.1.0")
 
 
-There are three possible settings:
+;;;;; Completion
+
+(defcustom magit-completing-read-function 'magit-builtin-completing-read
+  "Function to be called when requesting input from the user."
+  :group 'magit
+  :type '(radio (function-item magit-iswitchb-completing-read)
+                (function-item magit-ido-completing-read)
+                (function-item magit-builtin-completing-read)
+                (function :tag "Other")))
+
+(defcustom magit-remote-ref-format 'remote-slash-branch
+  "How to format refs when autocompleting, in particular for remotes.
+
+Autocompletion is used by functions like `magit-checkout',
+`magit-interactive-rebase' and others which offer branch name
+completion.
+
+`remote-slash-branch'  Format refs as \"remote/branch\".
+`branch-then-remote'   Format refs as \"branch (remote)\"."
+  :package-version '(magit . "2.0.0")
+  :group 'magit
+  :type '(choice (const :tag "branch (remote)" branch-then-remote)
+                 (const :tag "remote/branch" remote-slash-branch)))
+
+(defcustom magit-repo-dirs nil
+  "Directories containing Git repositories.
+Magit will look into these directories for Git repositories and
+offer them as choices for `magit-status'."
+  :group 'magit
+  :type '(repeat string))
 
 
-  nil means to never show fine differences
+(defcustom magit-repo-dirs-depth 3
+  "The maximum depth to look for Git repos.
+When looking for a Git repository below the directories in
+`magit-repo-dirs', Magit will only descend this many levels
+deep."
+  :group 'magit
+  :type 'integer)
 
 
-  t means to only show fine differences for the currently
-  selected diff hunk
+(defcustom magit-default-tracking-name-function
+  'magit-default-tracking-name-remote-plus-branch
+  "Function used to generate default tracking branch names
+when doing a \\[magit-checkout].
 
 
-  `all' means to always show fine differences for all displayed diff hunks"
+The default is `magit-default-tracking-name-remote-plus-branch',
+which generates a tracking name of the form \"REMOTE-BRANCHNAME\"."
   :group 'magit
   :group 'magit
+  :type '(radio (function-item magit-default-tracking-name-remote-plus-branch)
+                (function-item magit-default-tracking-name-branch-only)
+                (function :tag "Other")))
+
+;;;;; Modes
+;;;;;; Common
+
+(defcustom magit-mode-hook nil
+  "Hook run when entering a Magit mode derived mode."
+  :group 'magit-modes
+  :type 'hook)
+
+(defcustom magit-show-xref-buttons '(magit-diff-mode magit-commit-mode)
+  "List of modes whose buffers should contain history buttons.
+Currently only `magit-diff-mode' and `magit-commit-mode' are
+supported."
+  :package-version '(magit . "2.0.0")
+  :group 'magit-modes
+  :type '(repeat (choice (const magit-diff-mode)
+                         (const magit-commit-mode))))
+
+(defcustom magit-show-child-count nil
+  "Whether to append the number of childen to section headings."
+  :package-version '(magit . "2.0.0")
+  :group 'magit-modes
+  :type 'boolean)
+
+(defvar magit-status-line-align-to 9)
+
+(defcustom magit-restore-window-configuration nil
+  "Whether quitting a Magit buffer restores previous window configuration.
+
+Function `magit-mode-display-buffer' is used to display and
+select Magit buffers.  Unless the buffer was already displayed in
+a window of the selected frame it also stores the previous window
+configuration.  If this option is non-nil that configuration will
+later be restored by `magit-mode-quit-window', provided the
+buffer has not since been displayed in another frame.
+
+This works best when only two windows are usually displayed in a
+frame.  If this isn't the case setting this to t might often lead
+to undesirable behaviour.  Also quitting a Magit buffer while
+another Magit buffer that was created earlier is still displayed
+will cause that buffer to be hidden, which might or might not be
+what you want."
+  :package-version '(magit . "2.0.0")
+  :group 'magit-modes
+  :type 'boolean)
+
+(defcustom magit-refs-namespaces
+  '(("^\\(HEAD\\)$"              magit-log-head-label-head nil)
+    ("^refs/tags/\\(.+\\)"       magit-log-head-label-tags nil)
+    ("^refs/heads/\\(.+\\)"      magit-log-head-label-local nil)
+    ("^refs/remotes/\\(.+\\)"    magit-log-head-label-remote nil)
+    ("^refs/bisect/\\(bad\\)"    magit-log-head-label-bisect-bad nil)
+    ("^refs/bisect/\\(skip.*\\)" magit-log-head-label-bisect-skip nil)
+    ("^refs/bisect/\\(good.*\\)" magit-log-head-label-bisect-good nil)
+    ("^refs/wip/\\(.+\\)"        magit-log-head-label-wip nil)
+    ("^refs/patches/\\(.+\\)"    magit-log-head-label-patches nil)
+    ("^\\(bad\\):"               magit-log-head-label-bisect-bad nil)
+    ("^\\(skip\\):"              magit-log-head-label-bisect-skip nil)
+    ("^\\(good\\):"              magit-log-head-label-bisect-good nil)
+    ("\\(.+\\)"                  magit-log-head-label-default nil))
+  "How different refs should be formatted for display.
+
+Each entry controls how a certain type of ref is displayed, and
+has the form (REGEXP FACE FORMATTER).  REGEXP is a regular
+expression used to match full refs.  The first entry whose REGEXP
+matches the reference is used.  The first regexp submatch becomes
+the \"label\" that represents the ref and is propertized with
+font FONT.  If FORMATTER is non-nil it should be a function that
+takes two arguments, the full ref and the face.  It is supposed
+to return a propertized label that represents the ref.
+
+Currently this variable is only used in logs and the branch
+manager but it will be used in more places in the future."
+  :package-version '(magit . "2.0.0")
+  :group 'magit-modes
+  :type '(repeat
+          (list regexp
+                face
+                (choice (const :tag "first submatch is label" nil)
+                        (function :tag "format using function")))))
+
+;;;;;; Status
+
+(defcustom magit-status-sections-hook
+  '(magit-insert-status-local-line
+    magit-insert-status-remote-line
+    magit-insert-status-head-line
+    magit-insert-status-tags-line
+    magit-insert-status-merge-line
+    magit-insert-status-rebase-lines
+    magit-insert-empty-line
+    magit-insert-rebase-sequence
+    magit-insert-bisect-output
+    magit-insert-bisect-rest
+    magit-insert-bisect-log
+    magit-insert-stashes
+    magit-insert-untracked-files
+    magit-insert-pending-commits
+    magit-insert-unstaged-changes
+    magit-insert-staged-changes
+    magit-insert-unpulled-commits
+    magit-insert-unpushed-commits)
+  "Hook run to insert sections into the status buffer.
+
+This option allows reordering the sections and adding sections
+that are by default displayed in other Magit buffers.  Doing the
+latter is currently not recommended because not all functions
+that insert sections have been adapted yet.  Only inserters that
+take no argument can be used and some functions exist that begin
+with the `magit-insert-' prefix but do not insert a section.
+
+Note that there are already plans to improve this and to add
+similar hooks for other Magit modes."
+  :package-version '(magit . "2.0.0")
+  :group 'magit-status
+  :type 'hook)
+
+(defcustom magit-status-buffer-switch-function 'pop-to-buffer
+  "Function for `magit-status' to use for switching to the status buffer.
+
+The function is given one argument, the status buffer."
+  :group 'magit-status
+  :type '(radio (function-item switch-to-buffer)
+                (function-item pop-to-buffer)
+                (function :tag "Other")))
+
+(defcustom magit-status-show-sequence-help t
+  "Whether to show instructions on how to proceed a stopped action.
+When this is non-nil and a commit failed to apply during a merge
+or rebase, then show instructions on how to continue."
+  :package-version '(magit . "2.0.0")
+  :group 'magit-status
+  :type 'boolean)
+
+(defcustom magit-status-tags-line-subject 'head
+  "Whether tag or head is the subject on tags line in status buffer.
+
+This controls how the words \"ahead\" and \"behind\" are used on
+the tags line in the status buffer.  The tags line does not
+actually display complete sentences, but when thinking about when
+to use which term, it helps imagining it did.  This option
+controls whether the tag names should be considered the subjects
+or objects in these sentences.
+
+`tag'   The previous tag is *behind* HEAD by N commits.
+        The next tag is *ahead* of HEAD by N commits.
+`head'  HEAD is *ahead* of the previous tag by N commits.
+        HEAD is *behind* the next tag by N commits.
+
+If the value is `tag' the commit counts are fontified; otherwise
+they are not (due to semantic considerations)."
+  :package-version '(magit . "2.0.0")
+  :group 'magit-status
+  :type '(choice (const :tag "tags are the subjects" tag)
+                 (const :tag "head is the subject" head)))
+
+;;;;;; Diff
+
+(defun magit-set-default-diff-options (symbol value)
+  "Set the default for `magit-diff-options' based on popup value.
+Also set the local value in all Magit buffers and refresh them.
+\n(fn)" ; The arguments are an internal implementation detail.
+  (interactive (list 'magit-diff-options magit-custom-options))
+  (set-default symbol value)
+  (when (and (featurep 'magit) (not buffer-file-name))
+    (dolist (buffer (buffer-list))
+      (when (derived-mode-p 'magit-mode)
+        (with-current-buffer buffer
+          (with-no-warnings
+            (setq-local magit-diff-options value))
+          (magit-mode-refresh-buffer))))))
+
+(defcustom magit-diff-options nil
+  "Git options used to display diffs.
+
+For more information about the options see man:git-diff.
+This variable can be conveniently set in Magit buffers
+using `magit-key-mode-popup-diff-options' (bound to \
+\\<magit-mode-map>\\[magit-key-mode-popup-diff-options]).
+
+Please note that not all of these options are supported by older
+versions of Git, which could become a problem if you use tramp to
+access repositories on a system with such a version.  If you see
+whitespace where you would have expected a diff, this likely is
+the cause, and the only (currently) workaround is to not make the
+problematic option a member of the default value."
+  :package-version '(magit . "2.0.0")
+  :group 'magit-diff
+  :set 'magit-set-default-diff-options
+  :type '(set :greedy t
+              (const :tag
+                     "--minimal              Show smallest possible diff"
+                     "--minimal")
+              (const :tag
+                     "--patience             Use patience diff algorithm"
+                     "--patience")
+              (const :tag
+                     "--histogram            Use histogram diff algorithm"
+                     "--histogram")
+              (const :tag
+                     "--ignore-space-change  Ignore whitespace changes"
+                     "--ignore-space-change")
+              (const :tag
+                     "--ignore-all-space     Ignore all whitespace"
+                     "--ignore-all-space")
+              (const :tag
+                     "--function-context     Show surrounding functions"
+                     "--function-context")))
+
+(put 'magit-diff-options 'permanent-local t)
+
+(defcustom magit-show-diffstat t
+  "Whether to show diffstat in diff and commit buffers."
+  :package-version '(magit . "2.0.0")
+  :group 'magit-diff
+  :group 'magit-commit
+  :type 'boolean)
+
+(defcustom magit-diff-refine-hunk nil
+  "Show fine (word-granularity) differences within diff hunks.
+
+There are three possible settings:
+
+nil    never show fine differences
+t      show fine differences for the selected diff hunk only
+`all'  show fine differences for all displayed diff hunks"
+  :group 'magit-diff
   :type '(choice (const :tag "Never" nil)
                  (const :tag "Selected only" t)
                  (const :tag "All" all))
   :set 'magit-set-variable-and-refresh)
 
   :type '(choice (const :tag "Never" nil)
                  (const :tag "Selected only" t)
                  (const :tag "All" all))
   :set 'magit-set-variable-and-refresh)
 
-(defvar magit-current-indentation nil
-  "Indentation highlight used in the current buffer.
-This is calculated from `magit-highlight-indentation'.")
-(make-variable-buffer-local 'magit-current-indentation)
+;;;;;; Commit
 
 
-(defgroup magit-faces nil
-  "Customize the appearance of Magit."
-  :prefix "magit-"
-  :group 'faces
-  :group 'magit)
+(defcustom magit-commit-ask-to-stage t
+  "Whether to ask to stage everything when committing and nothing is staged."
+  :package-version '(magit . "2.0.0")
+  :group 'magit-commit
+  :type 'boolean)
 
 
-(defface magit-header
-  '((t :inherit header-line))
-  "Face for generic header lines.
+(defcustom magit-commit-extend-override-date nil
+  "Whether using `magit-commit-extend' changes the committer date."
+  :package-version '(magit . "2.0.0")
+  :group 'magit-commit
+  :type 'boolean)
 
 
-Many Magit faces inherit from this one by default."
-  :group 'magit-faces)
+(defcustom magit-commit-reword-override-date nil
+  "Whether using `magit-commit-reword' changes the committer date."
+  :package-version '(magit . "2.0.0")
+  :group 'magit-commit
+  :type 'boolean)
 
 
-(defface magit-section-title
-  '((t :inherit magit-header))
-  "Face for section titles."
-  :group 'magit-faces)
+(defcustom magit-commit-squash-commit nil
+  "Whether to target the marked or current commit when squashing.
+
+When this is nil then the command `magit-commit-fixup' and
+`magit-commit-squash' always require that the user explicitly
+selects a commit.  This is also the case when these commands are
+used with a prefix argument, in which case this option is ignored.
+
+Otherwise this controls which commit to target, either the
+current or marked commit.  Or if both can be used, which should
+be preferred."
+  :package-version '(magit . "2.0.0")
+  :group 'magit-commit
+  :type
+  '(choice
+    (const :tag "Always prompt" nil)
+    (const :tag "Prefer current commit, else use marked" current-or-marked)
+    (const :tag "Prefer marked commit, else use current" marked-or-current)
+    (const :tag "Use current commit, if any" current)
+    (const :tag "Use marked commit, if any" marked)))
+
+(defcustom magit-expand-staged-on-commit nil
+  "Whether to expand staged changes when creating a commit.
+When this is non-nil and the current buffer is the status buffer
+expand the section containing staged changes.  If this is `full'
+always expand all subsections; if it is t subsections that were
+previously hidden remain hidden.
+
+In the event that expanding very large patches takes a long time
+\\<global-map>\\[keyboard-quit] can be used to abort that step.
+This is especially useful when you would normally not look at the
+changes, e.g. because you are committing some binary files."
+  :package-version '(magit . "2.0.0")
+  :group 'magit-commit
+  :type '(choice (const :tag "Expand all subsections" full)
+                 (const :tag "Expand top section" t)
+                 (const :tag "Don't expand" nil)))
+
+;;;;;; Log
 
 
-(defface magit-branch
-  '((t :inherit magit-header))
-  "Face for the current branch."
-  :group 'magit-faces)
+(defcustom magit-log-auto-more nil
+  "Insert more log entries automatically when moving past the last entry.
 
 
-(defface magit-diff-file-header
-  '((t :inherit diff-file-header))
-  "Face for diff file header lines."
-  :group 'magit-faces)
+Only considered when moving past the last entry with
+`magit-goto-*-section' commands."
+  :group 'magit-log
+  :type 'boolean)
 
 
-(defface magit-diff-hunk-header
-  '((t :inherit diff-hunk-header))
-  "Face for diff hunk header lines."
-  :group 'magit-faces)
+(defcustom magit-log-cutoff-length 100
+  "The maximum number of commits to show in the log and whazzup buffers."
+  :group 'magit-log
+  :type 'integer)
 
 
-(defface magit-diff-add
-  '((t :inherit diff-added))
-  "Face for lines in a diff that have been added."
-  :group 'magit-faces)
+(defcustom magit-log-infinite-length 99999
+  "Number of log used to show as maximum for `magit-log-cutoff-length'."
+  :group 'magit-log
+  :type 'integer)
 
 
-(defface magit-diff-none
-  '((t :inherit diff-context))
-  "Face for lines in a diff that are unchanged."
-  :group 'magit-faces)
+(defcustom magit-log-format-graph-function nil
+  "Function used to format graphs in log buffers.
+The function is called with one argument, the propertized graph
+of a single line in as a string.  It has to return the formatted
+string.  This option can also be nil, in which case the graph is
+inserted as is."
+  :package-version '(magit . "2.1.0")
+  :group 'magit-log
+  :type '(choice (const :tag "insert as is" nil)
+                 (function-item magit-log-format-unicode-graph)
+                 function))
+
+(defcustom magit-log-format-unicode-graph-alist
+  '((?/ . ?╱) (?| . ?│) (?\\ . ?╲) (?* . ?◆) (?o . ?◇))
+  "Alist used by `magit-log-format-unicode-graph' to translate chars."
+  :package-version '(magit . "2.1.0")
+  :group 'magit-log
+  :type '(repeat (cons :format "%v\n"
+                       (character :format "replace %v ")
+                       (character :format "with %v"))))
+
+(defcustom magit-log-show-gpg-status nil
+  "Display signature verification information as part of the log."
+  :package-version '(magit . "2.0.0")
+  :group 'magit-log
+  :type 'boolean)
+
+(defcustom magit-log-show-margin t
+  "Whether to use a margin when showing `oneline' logs.
+When non-nil the author name and date are displayed in the margin
+of the log buffer if that contains a `oneline' log.  This can be
+toggled temporarily using the command `magit-log-toggle-margin'."
+  :package-version '(magit . "2.0.0")
+  :group 'magit-log
+  :type 'boolean)
+
+(put 'magit-log-show-margin 'permanent-local t)
+
+(defcustom magit-log-margin-spec '(25 nil magit-duration-spec)
+  "How to format the margin for `oneline' logs.
+
+When the log buffer contains a `oneline' log, then it optionally
+uses the right margin to display the author name and author date.
+This is also supported in the reflog buffer.
+
+Logs that are shown together with other non-log information (e.g.
+in the status buffer) are never accompanied by a margin.  The
+same applies to `long' logs, in this case because that would be
+redundant.
+
+This option controls how that margin is formatted, the other
+option affecting this is `magit-log-show-margin'; if that is nil
+then no margin is displayed at all.  To toggle this temporarily
+use the command `magit-log-show-margin'.
+
+The value has the form (WIDTH CHARACTERP DURATION-SPEC).  The
+width of the margin is controlled using WIDTH, an integer.  When
+CHARACTERP is non-nil time units are shown as single characters,
+otherwise the full name of the unit is displayed.  DURATION-SPEC
+has to be a variable, its value controls which time units are
+used, how many seconds they contain, and what their names are."
+  :package-version '(magit . "2.0.0")
+  :group 'magit-log
+  :type '(list (integer  :tag "Margin width")
+               (choice   :tag "Time unit style"
+                         (const :tag "Character" t)
+                         (const :tag "Word" nil))
+               (variable :tag "Duration spec variable")))
+
+(defcustom magit-duration-spec
+  `((?Y "year"   "years"   ,(round (* 60 60 24 365.2425)))
+    (?M "month"  "months"  ,(round (* 60 60 24 30.436875)))
+    (?w "week"   "weeks"   ,(* 60 60 24 7))
+    (?d "day"    "days"    ,(* 60 60 24))
+    (?h "hour"   "hours"   ,(* 60 60))
+    (?m "minute" "minutes" 60)
+    (?s "second" "seconds" 1))
+  "Units used to display durations in a human format.
+The value is a list of time units, beginning with the longest.
+Each element has the form ((CHAR UNIT UNITS SECONDS)..).  UNIT
+is the time unit, UNITS is the plural of that unit.  CHAR is a
+character that can be used as abbreviation and must be unique
+amoung all elements.  SECONDS is the number of seconds in one
+UNIT.  Also see option `magit-log-margin-spec'."
+  :package-version '(magit . "2.0.0")
+  :group 'magit-log
+  :type '(repeat (list (character :tag "Unit character")
+                       (string    :tag "Unit singular string")
+                       (string    :tag "Unit plural string")
+                       (integer   :tag "Seconds in unit"))))
+
+(defcustom magit-ellipsis #x2026 ; "horizontal ellipsis"
+  "Character appended to abreviated text.
+Currently this is used only in the log margin, but might later
+be used elsewhere too.  Filenames that were abbreviated by Git
+are left as-is."
+  :package-version '(magit . "2.0.0")
+  :group 'magit-log
+  :type 'character)
+
+;;;;;; Others
+
+(defcustom magit-auto-revert-mode-lighter " MRev"
+  "String to display when Magit-Auto-Revert mode is active."
+  :group 'magit-modes)
+
+(define-minor-mode magit-auto-revert-mode
+  "Toggle global Magit-Auto-Revert mode.
+With prefix ARG, enable Magit-Auto-Revert mode if ARG is positive;
+otherwise, disable it.  If called from Lisp, enable the mode if
+ARG is omitted or nil.
+
+Magit-Auto-Revert mode is a global minor mode that, after Magit
+has run a Git command, reverts buffers associated with files that
+have changed on disk and are tracked in the current Git repository."
+  :group 'magit-modes
+  :lighter magit-auto-revert-mode-lighter
+  :global t
+  :init-value t)
+
+(defcustom magit-merge-warn-dirty-worktree t
+  "Whether to issue a warning when attempting to start a merge in a dirty worktree."
+  :package-version '(magit . "2.0.0")
+  :group 'magit-modes
+  :type 'boolean)
+
+(defcustom magit-push-hook '(magit-push-dwim)
+  "Hook run by `magit-push' to actually do the work.
+See `magit-push' and `magit-push-dwim' for more information."
+  :package-version '(magit . "2.0.0")
+  :group 'magit-modes
+  :type 'hook)
+
+(defcustom magit-set-upstream-on-push nil
+  "Whether `magit-push' may set upstream when pushing a branch.
+This only applies if the branch does not have an upstream set yet.
+
+nil        don't use --set-upstream.
+t          ask if --set-upstream should be used.
+`dontask'  always use --set-upstream.
+`refuse'   refuse to push unless a remote branch has already been set."
+  :group 'magit-modes
+  :type '(choice (const :tag "Never" nil)
+                 (const :tag "Ask" t)
+                 (const :tag "Ask if not set" askifnotset)
+                 (const :tag "Refuse" refuse)
+                 (const :tag "Always" dontask)))
+
+(defcustom magit-wazzup-sections-hook
+  '(magit-insert-wazzup-head-line
+    magit-insert-empty-line
+    magit-insert-wazzup-branches)
+  "Hook run to insert sections into the wazzup buffer."
+  :package-version '(magit . "2.0.0")
+  :group 'magit-modes
+  :type 'hook)
+
+(defcustom magit-cherry-sections-hook
+  '(magit-insert-cherry-head-line
+    magit-insert-cherry-upstream-line
+    magit-insert-cherry-help-lines
+    magit-insert-empty-line
+    magit-insert-cherry-commits)
+  "Hook run to insert sections into the cherry buffer."
+  :package-version '(magit . "2.0.0")
+  :group 'magit-modes
+  :type 'hook)
+
+;;;; Custom Faces
+
+(defface magit-section-title
+  '((t :inherit header-line))
+  "Face for section titles."
+  :group 'magit-faces)
+
+(defface magit-branch
+  '((((class color) (background light))
+     :background "Grey85"
+     :foreground "LightSkyBlue4")
+    (((class color) (background dark))
+     :background "Grey13"
+     :foreground "LightSkyBlue1"))
+  "Face for branches."
+  :group 'magit-faces)
+
+(defface magit-tag
+  '((((class color) (background light))
+     :background "LemonChiffon1"
+     :foreground "goldenrod4")
+    (((class color) (background dark))
+     :background "LemonChiffon1"
+     :foreground "goldenrod4"))
+  "Face for tags."
+  :group 'magit-faces)
+
+(defface magit-diff-file-header
+  '((t :inherit diff-file-header))
+  "Face for diff file header lines."
+  :group 'magit-faces)
+
+(defface magit-diff-hunk-header
+  '((t :inherit diff-hunk-header))
+  "Face for diff hunk header lines."
+  :group 'magit-faces)
+
+(defface magit-diff-add
+  '((t :inherit diff-added))
+  "Face for lines in a diff that have been added."
+  :group 'magit-faces)
 
 (defface magit-diff-del
   '((t :inherit diff-removed))
   "Face for lines in a diff that have been deleted."
   :group 'magit-faces)
 
 
 (defface magit-diff-del
   '((t :inherit diff-removed))
   "Face for lines in a diff that have been deleted."
   :group 'magit-faces)
 
+(defface magit-diff-none
+  '((t :inherit diff-context))
+  "Face for lines in a diff that are unchanged."
+  :group 'magit-faces)
+
+(defface magit-diff-merge-current
+  '((t :inherit font-lock-preprocessor-face))
+  "Face for merge conflict marker 'current' line."
+  :group 'magit-faces)
+
+(defface magit-diff-merge-separator
+  '((t :inherit font-lock-preprocessor-face))
+  "Face for merge conflict marker seperator."
+  :group 'magit-faces)
+
+(defface magit-diff-merge-diff3-separator
+  '((t :inherit font-lock-preprocessor-face))
+  "Face for merge conflict marker seperator."
+  :group 'magit-faces)
+
+(defface magit-diff-merge-proposed
+  '((t :inherit font-lock-preprocessor-face))
+  "Face for merge conflict marker 'proposed' line."
+  :group 'magit-faces)
+
 (defface magit-log-graph
   '((((class color) (background light))
      :foreground "grey11")
 (defface magit-log-graph
   '((((class color) (background light))
      :foreground "grey11")
@@ -437,18 +1213,39 @@ Many Magit faces inherit from this one by default."
   "Face for the sha1 element of the log output."
   :group 'magit-faces)
 
   "Face for the sha1 element of the log output."
   :group 'magit-faces)
 
+(defface magit-log-author
+  '((((class color) (background light))
+     :foreground "firebrick")
+    (((class color) (background dark))
+     :foreground "tomato"))
+  "Face for the author element of the log output."
+  :group 'magit-faces)
+
+(defface magit-log-date
+  '((t))
+  "Face for the date element of the log output."
+  :group 'magit-faces)
+
 (defface magit-log-message
   '((t))
   "Face for the message element of the log output."
   :group 'magit-faces)
 
 (defface magit-log-message
   '((t))
   "Face for the message element of the log output."
   :group 'magit-faces)
 
+(defface magit-cherry-unmatched
+  '((t :foreground "cyan"))
+  "Face for unmatched cherry commits.")
+
+(defface magit-cherry-equivalent
+  '((t :foreground "magenta"))
+  "Face for equivalent cherry commits.")
+
 (defface magit-item-highlight
 (defface magit-item-highlight
-  '((t :inherit highlight))
+  '((t :inherit secondary-selection))
   "Face for highlighting the current item."
   :group 'magit-faces)
 
 (defface magit-item-mark
   "Face for highlighting the current item."
   :group 'magit-faces)
 
 (defface magit-item-mark
-  '((t :inherit secondary-selection))
+  '((t :inherit highlight))
   "Face for highlighting marked item."
   :group 'magit-faces)
 
   "Face for highlighting marked item."
   :group 'magit-faces)
 
@@ -464,6 +1261,18 @@ Many Magit faces inherit from this one by default."
   "Face for good bisect refs."
   :group 'magit-faces)
 
   "Face for good bisect refs."
   :group 'magit-faces)
 
+(defface magit-log-head-label-bisect-skip
+  '((((class color) (background light))
+     :box t
+     :background "light goldenrod"
+     :foreground "dark goldenrod")
+    (((class color) (background dark))
+     :box t
+     :background "light goldenrod"
+     :foreground "dark goldenrod"))
+  "Face for skipped bisect refs."
+  :group 'magit-faces)
+
 (defface magit-log-head-label-bisect-bad
   '((((class color) (background light))
      :box t
 (defface magit-log-head-label-bisect-bad
   '((((class color) (background light))
      :box t
@@ -517,35 +1326,96 @@ Many Magit faces inherit from this one by default."
   "Face for highlighting whitespace errors in Magit diffs."
   :group 'magit-faces)
 
   "Face for highlighting whitespace errors in Magit diffs."
   :group 'magit-faces)
 
-(defvar magit-custom-options '()
-  "List of custom options to pass to Git.
-Do not customize this (used in the `magit-key-mode' implementation).")
+(defface magit-log-head-label-local
+  '((((class color) (background light))
+     :box t
+     :background "Grey85"
+     :foreground "LightSkyBlue4")
+    (((class color) (background dark))
+     :box t
+     :background "Grey13"
+     :foreground "LightSkyBlue1"))
+  "Face for local branch head labels shown in log buffer."
+  :group 'magit-faces)
+
+(defface magit-log-head-label-head
+  '((((class color) (background light))
+     :box t
+     :background "Grey70"
+     :foreground "Black")
+    (((class color) (background dark))
+     :box t
+     :background "Grey20"
+     :foreground "White"))
+  "Face for working branch head labels shown in log buffer."
+  :group 'magit-faces)
 
 
-(defvar magit-read-rev-history nil
-  "The history of inputs to `magit-read-rev'.")
+(defface magit-log-head-label-default
+  '((((class color) (background light))
+     :box t
+     :background "Grey50")
+    (((class color) (background dark))
+     :box t
+     :background "Grey50"))
+  "Face for unknown ref labels shown in log buffer."
+  :group 'magit-faces)
 
 
-(defvar magit-buffer-internal nil
-  "Track associated *magit* buffers.
-Do not customize this (used in the `magit-log-edit-mode' implementation
-to switch back to the *magit* buffer associated with a given commit
-operation after commit).")
+(defface magit-log-head-label-wip
+  '((((class color) (background light))
+     :box t
+     :background "Grey95"
+     :foreground "LightSkyBlue3")
+    (((class color) (background dark))
+     :box t
+     :background "Grey07"
+     :foreground "LightSkyBlue4"))
+  "Face for git-wip labels shown in log buffer."
+  :group 'magit-faces)
+
+(defface magit-signature-good
+  '((t :foreground "green"))
+  "Face for good signatures."
+  :group 'magit-faces)
 
 
-(defvar magit-back-navigation-history nil
-  "History items that will be visited by successively going \"back\".")
-(make-variable-buffer-local 'magit-back-navigation-history)
-(put 'magit-back-navigation-history 'permanent-local t)
+(defface magit-signature-bad
+  '((t :foreground "red"))
+  "Face for bad signatures."
+  :group 'magit-faces)
 
 
-(defvar magit-forward-navigation-history nil
-  "History items that will be visited by successively going \"forward\".")
-(make-variable-buffer-local 'magit-forward-navigation-history)
-(put 'magit-forward-navigation-history 'permanent-local t)
+(defface magit-signature-untrusted
+  '((t :foreground "cyan"))
+  "Face for good untrusted signatures."
+  :group 'magit-faces)
 
 
-(defvar magit-omit-untracked-dir-contents nil
-  "When non-nil magit will only list an untracked directory, not its contents.")
+(defface magit-signature-none
+  '((t :inherit magit-log-message))
+  "Face for unsigned commits."
+  :group 'magit-faces)
 
 
-(defvar magit-tmp-buffer-name " *magit-tmp*")
 
 
-(defface magit-log-head-label-local
+(defface magit-log-reflog-label-commit
+  '((((class color) (background light))
+     :box t
+     :background "LemonChiffon1"
+     :foreground "goldenrod4")
+    (((class color) (background dark))
+     :box t
+     :background "LemonChiffon1"
+     :foreground "goldenrod4"))
+  "Face for reflog subject labels shown in reflog buffer."
+  :group 'magit-faces)
+
+(defface magit-log-reflog-label-amend
+  '((t :inherit magit-log-reflog-label-commit))
+  "Face for reflog subject labels shown in reflog buffer."
+  :group 'magit-faces)
+
+(defface magit-log-reflog-label-merge
+  '((t :inherit magit-log-reflog-label-commit))
+  "Face for reflog subject labels shown in reflog buffer."
+  :group 'magit-faces)
+
+(defface magit-log-reflog-label-checkout
   '((((class color) (background light))
      :box t
      :background "Grey85"
   '((((class color) (background light))
      :box t
      :background "Grey85"
@@ -554,19 +1424,89 @@ operation after commit).")
      :box t
      :background "Grey13"
      :foreground "LightSkyBlue1"))
      :box t
      :background "Grey13"
      :foreground "LightSkyBlue1"))
-  "Face for local branch head labels shown in log buffer."
+  "Face for reflog subject labels shown in reflog buffer."
   :group 'magit-faces)
 
   :group 'magit-faces)
 
-(defface magit-log-head-label-default
+(defface magit-log-reflog-label-reset
+  '((((class color) (background light))
+     :box t
+     :background "IndianRed1"
+     :foreground "IndianRed4")
+    (((class color) (background dark))
+     :box t
+     :background "IndianRed1"
+     :foreground "IndianRed4"))
+  "Face for reflog subject labels shown in reflog buffer."
+  :group 'magit-faces)
+
+(defface magit-log-reflog-label-rebase
+  '((((class color) (background light))
+     :box t
+     :background "Grey85"
+     :foreground "OliveDrab4")
+    (((class color) (background dark))
+     :box t
+     :background "Grey11"
+     :foreground "DarkSeaGreen2"))
+  "Face for reflog subject labels shown in reflog buffer."
+  :group 'magit-faces)
+
+(defface magit-log-reflog-label-cherry-pick
+'((((class color) (background light))
+     :box t
+     :background "light green"
+     :foreground "dark olive green")
+    (((class color) (background dark))
+     :box t
+     :background "light green"
+     :foreground "dark olive green"))
+  "Face for reflog subject labels shown in reflog buffer."
+  :group 'magit-faces)
+
+(defface magit-log-reflog-label-remote
   '((((class color) (background light))
      :box t
      :background "Grey50")
     (((class color) (background dark))
      :box t
      :background "Grey50"))
   '((((class color) (background light))
      :box t
      :background "Grey50")
     (((class color) (background dark))
      :box t
      :background "Grey50"))
-  "Face for unknown ref labels shown in log buffer."
+  "Face for reflog subject labels shown in reflog buffer."
+  :group 'magit-faces)
+
+(defface magit-log-reflog-label-other
+  '((((class color) (background light))
+     :box t
+     :background "Grey50")
+    (((class color) (background dark))
+     :box t
+     :background "Grey50"))
+  "Face for reflog subject labels shown in reflog buffer."
+  :group 'magit-faces)
+
+(defface magit-process-ok
+  '((t :inherit magit-section-title
+       :foreground "green"))
+  "Face for zero exit-status."
+  :group 'magit-faces)
+
+(defface magit-process-ng
+  '((t :inherit magit-section-title
+       :foreground "red"))
+  "Face for non-zero exit-status."
   :group 'magit-faces)
 
   :group 'magit-faces)
 
+;;;; Keymaps
+
+;; Not an option to avoid advertising it.
+(defvar magit-rigid-key-bindings nil
+  "Use rigid key bindings instead of thematic key popups.
+If you enable this a lot of functionality is lost.  You most
+likely don't want that.  This variable only has an effect if
+set before loading libary `magit'.")
+
+(when (boundp 'git-commit-mode-map)
+  (define-key git-commit-mode-map (kbd "C-c C-d") 'magit-diff-staged))
+
 (defvar magit-mode-map
   (let ((map (make-keymap)))
     (suppress-keymap map t)
 (defvar magit-mode-map
   (let ((map (make-keymap)))
     (suppress-keymap map t)
@@ -591,32 +1531,54 @@ operation after commit).")
     (define-key map (kbd "M-S") 'magit-show-level-4-all)
     (define-key map (kbd "g") 'magit-refresh)
     (define-key map (kbd "G") 'magit-refresh-all)
     (define-key map (kbd "M-S") 'magit-show-level-4-all)
     (define-key map (kbd "g") 'magit-refresh)
     (define-key map (kbd "G") 'magit-refresh-all)
-    (define-key map (kbd "?") 'magit-describe-item)
-    (define-key map (kbd "!") 'magit-key-mode-popup-running)
+    (define-key map (kbd "?") 'magit-key-mode-popup-dispatch)
     (define-key map (kbd ":") 'magit-git-command)
     (define-key map (kbd "C-x 4 a") 'magit-add-change-log-entry-other-window)
     (define-key map (kbd ":") 'magit-git-command)
     (define-key map (kbd "C-x 4 a") 'magit-add-change-log-entry-other-window)
-    (define-key map (kbd "L") 'magit-add-change-log-entry-no-option)
+    (define-key map (kbd "L") 'magit-add-change-log-entry)
     (define-key map (kbd "RET") 'magit-visit-item)
     (define-key map (kbd "RET") 'magit-visit-item)
+    (define-key map (kbd "C-<return>") 'magit-dired-jump)
     (define-key map (kbd "SPC") 'magit-show-item-or-scroll-up)
     (define-key map (kbd "DEL") 'magit-show-item-or-scroll-down)
     (define-key map (kbd "C-w") 'magit-copy-item-as-kill)
     (define-key map (kbd "SPC") 'magit-show-item-or-scroll-up)
     (define-key map (kbd "DEL") 'magit-show-item-or-scroll-down)
     (define-key map (kbd "C-w") 'magit-copy-item-as-kill)
-    (define-key map (kbd "R") 'magit-rebase-step)
-    (define-key map (kbd "t") 'magit-key-mode-popup-tagging)
-    (define-key map (kbd "r") 'magit-key-mode-popup-rewriting)
-    (define-key map (kbd "P") 'magit-key-mode-popup-pushing)
-    (define-key map (kbd "f") 'magit-key-mode-popup-fetching)
-    (define-key map (kbd "b") 'magit-key-mode-popup-branching)
-    (define-key map (kbd "M") 'magit-key-mode-popup-remoting)
-    (define-key map (kbd "B") 'magit-key-mode-popup-bisecting)
-    (define-key map (kbd "F") 'magit-key-mode-popup-pulling)
-    (define-key map (kbd "l") 'magit-key-mode-popup-logging)
-    (define-key map (kbd "$") 'magit-display-process)
-    (define-key map (kbd "c") 'magit-log-edit)
+    (cond (magit-rigid-key-bindings
+           (define-key map (kbd "c") 'magit-commit)
+           (define-key map (kbd "m") 'magit-merge)
+           (define-key map (kbd "b") 'magit-checkout)
+           (define-key map (kbd "M") 'magit-branch-manager)
+           (define-key map (kbd "r") 'undefined)
+           (define-key map (kbd "f") 'magit-fetch-current)
+           (define-key map (kbd "F") 'magit-pull)
+           (define-key map (kbd "J") 'magit-apply-mailbox)
+           (define-key map (kbd "!") 'magit-git-command-topdir)
+           (define-key map (kbd "P") 'magit-push)
+           (define-key map (kbd "t") 'magit-tag)
+           (define-key map (kbd "l") 'magit-log)
+           (define-key map (kbd "o") 'magit-submodule-update)
+           (define-key map (kbd "B") 'undefined)
+           (define-key map (kbd "z") 'magit-stash))
+          (t
+           (define-key map (kbd "c") 'magit-key-mode-popup-committing)
+           (define-key map (kbd "m") 'magit-key-mode-popup-merging)
+           (define-key map (kbd "b") 'magit-key-mode-popup-branching)
+           (define-key map (kbd "M") 'magit-key-mode-popup-remoting)
+           (define-key map (kbd "r") 'magit-key-mode-popup-rewriting)
+           (define-key map (kbd "f") 'magit-key-mode-popup-fetching)
+           (define-key map (kbd "F") 'magit-key-mode-popup-pulling)
+           (define-key map (kbd "J") 'magit-key-mode-popup-apply-mailbox)
+           (define-key map (kbd "!") 'magit-key-mode-popup-running)
+           (define-key map (kbd "P") 'magit-key-mode-popup-pushing)
+           (define-key map (kbd "t") 'magit-key-mode-popup-tagging)
+           (define-key map (kbd "l") 'magit-key-mode-popup-logging)
+           (define-key map (kbd "o") 'magit-key-mode-popup-submodule)
+           (define-key map (kbd "B") 'magit-key-mode-popup-bisecting)
+           (define-key map (kbd "z") 'magit-key-mode-popup-stashing)))
+    (define-key map (kbd "$") 'magit-process)
     (define-key map (kbd "E") 'magit-interactive-rebase)
     (define-key map (kbd "E") 'magit-interactive-rebase)
+    (define-key map (kbd "R") 'magit-rebase-step)
     (define-key map (kbd "e") 'magit-ediff)
     (define-key map (kbd "w") 'magit-wazzup)
     (define-key map (kbd "e") 'magit-ediff)
     (define-key map (kbd "w") 'magit-wazzup)
-    (define-key map (kbd "q") 'magit-quit-window)
-    (define-key map (kbd "m") 'magit-key-mode-popup-merging)
+    (define-key map (kbd "y") 'magit-cherry)
+    (define-key map (kbd "q") 'magit-mode-quit-window)
     (define-key map (kbd "x") 'magit-reset-head)
     (define-key map (kbd "v") 'magit-revert-item)
     (define-key map (kbd "a") 'magit-apply-item)
     (define-key map (kbd "x") 'magit-reset-head)
     (define-key map (kbd "v") 'magit-revert-item)
     (define-key map (kbd "a") 'magit-apply-item)
@@ -626,383 +1588,643 @@ operation after commit).")
     (define-key map (kbd "-") 'magit-diff-smaller-hunks)
     (define-key map (kbd "+") 'magit-diff-larger-hunks)
     (define-key map (kbd "0") 'magit-diff-default-hunks)
     (define-key map (kbd "-") 'magit-diff-smaller-hunks)
     (define-key map (kbd "+") 'magit-diff-larger-hunks)
     (define-key map (kbd "0") 'magit-diff-default-hunks)
-    (define-key map (kbd "h") 'magit-toggle-diff-refine-hunk)
-    map))
+    (define-key map (kbd "h") 'magit-key-mode-popup-diff-options)
+    (define-key map (kbd "H") 'magit-diff-toggle-refine-hunk)
+    (define-key map (kbd "M-g") 'magit-jump-to-diffstats)
+    (define-key map (kbd "S") 'magit-stage-all)
+    (define-key map (kbd "U") 'magit-unstage-all)
+    (define-key map (kbd "X") 'magit-reset-working-tree)
+    (define-key map (kbd "C-c C-c") 'magit-key-mode-popup-dispatch)
+    (define-key map (kbd "C-c C-e") 'magit-key-mode-popup-dispatch)
+    map)
+  "Parent keymap for all keymaps of modes derived from `magit-mode'.")
 
 (defvar magit-commit-mode-map
   (let ((map (make-sparse-keymap)))
 
 (defvar magit-commit-mode-map
   (let ((map (make-sparse-keymap)))
-    (define-key map (kbd "C-c C-b") 'magit-show-commit-backward)
-    (define-key map (kbd "C-c C-f") 'magit-show-commit-forward)
-    map))
+    (set-keymap-parent map magit-mode-map)
+    (define-key map (kbd "C-c C-b") 'magit-go-backward)
+    (define-key map (kbd "C-c C-f") 'magit-go-forward)
+    (define-key map (kbd "SPC") 'scroll-up)
+    (define-key map (kbd "DEL") 'scroll-down)
+    map)
+  "Keymap for `magit-commit-mode'.")
 
 (defvar magit-status-mode-map
   (let ((map (make-sparse-keymap)))
 
 (defvar magit-status-mode-map
   (let ((map (make-sparse-keymap)))
+    (set-keymap-parent map magit-mode-map)
     (define-key map (kbd "s") 'magit-stage-item)
     (define-key map (kbd "s") 'magit-stage-item)
-    (define-key map (kbd "S") 'magit-stage-all)
     (define-key map (kbd "u") 'magit-unstage-item)
     (define-key map (kbd "u") 'magit-unstage-item)
-    (define-key map (kbd "U") 'magit-unstage-all)
     (define-key map (kbd "i") 'magit-ignore-item)
     (define-key map (kbd "I") 'magit-ignore-item-locally)
     (define-key map (kbd "i") 'magit-ignore-item)
     (define-key map (kbd "I") 'magit-ignore-item-locally)
+    (define-key map (kbd "j") 'magit-section-jump-map)
     (define-key map (kbd ".") 'magit-mark-item)
     (define-key map (kbd "=") 'magit-diff-with-mark)
     (define-key map (kbd "k") 'magit-discard-item)
     (define-key map (kbd ".") 'magit-mark-item)
     (define-key map (kbd "=") 'magit-diff-with-mark)
     (define-key map (kbd "k") 'magit-discard-item)
-    (define-key map (kbd "C") 'magit-add-log)
-    (define-key map (kbd "X") 'magit-reset-working-tree)
-    (define-key map (kbd "z") 'magit-key-mode-popup-stashing)
-    map))
+    (define-key map (kbd "C") 'magit-commit-add-log)
+    map)
+  "Keymap for `magit-status-mode'.")
 
 (eval-after-load 'dired-x
   '(define-key magit-status-mode-map [remap dired-jump] 'magit-dired-jump))
 
 (defvar magit-log-mode-map
   (let ((map (make-sparse-keymap)))
 
 (eval-after-load 'dired-x
   '(define-key magit-status-mode-map [remap dired-jump] 'magit-dired-jump))
 
 (defvar magit-log-mode-map
   (let ((map (make-sparse-keymap)))
+    (set-keymap-parent map magit-mode-map)
     (define-key map (kbd ".") 'magit-mark-item)
     (define-key map (kbd "=") 'magit-diff-with-mark)
     (define-key map (kbd "e") 'magit-log-show-more-entries)
     (define-key map (kbd ".") 'magit-mark-item)
     (define-key map (kbd "=") 'magit-diff-with-mark)
     (define-key map (kbd "e") 'magit-log-show-more-entries)
-    map))
+    (define-key map (kbd "h") 'magit-log-toggle-margin)
+    map)
+  "Keymap for `magit-log-mode'.")
+
+(defvar magit-cherry-mode-map
+  (let ((map (make-sparse-keymap)))
+    (set-keymap-parent map magit-mode-map)
+    map)
+  "Keymap for `magit-cherry-mode'.")
+
+(defvar magit-reflog-mode-map
+  (let ((map (make-sparse-keymap)))
+    (set-keymap-parent map magit-log-mode-map)
+    map)
+  "Keymap for `magit-reflog-mode'.")
+
+(defvar magit-diff-mode-map
+  (let ((map (make-sparse-keymap)))
+    (set-keymap-parent map magit-mode-map)
+    (define-key map (kbd "C-c C-b") 'magit-go-backward)
+    (define-key map (kbd "C-c C-f") 'magit-go-forward)
+    (define-key map (kbd "SPC") 'scroll-up)
+    (define-key map (kbd "DEL") 'scroll-down)
+    map)
+  "Keymap for `magit-diff-mode'.")
 
 (defvar magit-wazzup-mode-map
   (let ((map (make-sparse-keymap)))
 
 (defvar magit-wazzup-mode-map
   (let ((map (make-sparse-keymap)))
+    (set-keymap-parent map magit-mode-map)
     (define-key map (kbd ".") 'magit-mark-item)
     (define-key map (kbd "=") 'magit-diff-with-mark)
     (define-key map (kbd "i") 'magit-ignore-item)
     (define-key map (kbd ".") 'magit-mark-item)
     (define-key map (kbd "=") 'magit-diff-with-mark)
     (define-key map (kbd "i") 'magit-ignore-item)
-    map))
+    map)
+  "Keymap for `magit-wazzup-mode'.")
 
 (defvar magit-branch-manager-mode-map
   (let ((map (make-sparse-keymap)))
 
 (defvar magit-branch-manager-mode-map
   (let ((map (make-sparse-keymap)))
+    (set-keymap-parent map magit-mode-map)
     (define-key map (kbd "c") 'magit-create-branch)
     (define-key map (kbd "a") 'magit-add-remote)
     (define-key map (kbd "c") 'magit-create-branch)
     (define-key map (kbd "a") 'magit-add-remote)
-    (define-key map (kbd "r") 'magit-move-item)
+    (define-key map (kbd "r") 'magit-rename-item)
     (define-key map (kbd "k") 'magit-discard-item)
     (define-key map (kbd "T") 'magit-change-what-branch-tracks)
     (define-key map (kbd "k") 'magit-discard-item)
     (define-key map (kbd "T") 'magit-change-what-branch-tracks)
-    map))
-
-(defvar magit-bug-report-url
-  "http://github.com/magit/magit/issues")
-
-(defconst magit-version "1.2.0"
-  "The version of Magit that you're using.")
-
-(defun magit-bug-report (str)
-  "Asks the user to submit a bug report about the error described in STR."
-;; XXX - should propose more information to be included.
-  (message (concat
-            "Unknown error: %s\n"
-            "Please, with as much information as possible, file a bug at\n"
-            "%s\n"
-            "You are using Magit version %s.")
-           str magit-bug-report-url magit-version))
-
-(defun magit-buffer-switch (buf)
-  (if (string-match "magit" (buffer-name))
-      (switch-to-buffer buf)
-    (pop-to-buffer buf)))
-
-;;; Macros
-
-(defmacro magit-with-refresh (&rest body)
-  (declare (indent 0))
-  `(magit-refresh-wrapper (lambda () ,@body)))
-
-;;; Git features
-
-(defvar magit-have-graph 'unset)
-(defvar magit-have-decorate 'unset)
-(defvar magit-have-abbrev 'unset)
-(make-variable-buffer-local 'magit-have-graph)
-(put 'magit-have-graph 'permanent-local t)
-(make-variable-buffer-local 'magit-have-decorate)
-(put 'magit-have-decorate 'permanent-local t)
-(make-variable-buffer-local 'magit-have-abbrev)
-(put 'magit-have-abbrev 'permanent-local t)
-
-(defun magit-configure-have-graph ()
-  (if (eq magit-have-graph 'unset)
-      (let ((res (magit-git-exit-code "log" "--graph" "--max-count=0")))
-        (setq magit-have-graph (eq res 0)))))
-
-(defun magit-configure-have-decorate ()
-  (if (eq magit-have-decorate 'unset)
-      (let ((res (magit-git-exit-code "log" "--decorate=full" "--max-count=0")))
-        (setq magit-have-decorate (eq res 0)))))
-
-(defun magit-configure-have-abbrev ()
-  (if (eq magit-have-abbrev 'unset)
-      (let ((res (magit-git-exit-code "log" "--no-abbrev-commit" "--max-count=0")))
-        (setq magit-have-abbrev (eq res 0)))))
-
-;;; Compatibilities
-
-(eval-and-compile
-  (defun magit-max-args-internal (function)
-    "Returns the maximum number of arguments accepted by FUNCTION."
-    (if (symbolp function)
-        (setq function (symbol-function function)))
-    (if (subrp function)
-        (let ((max (cdr (subr-arity function))))
-          (if (eq 'many max)
-              most-positive-fixnum
-            max))
-      (if (eq 'macro (car-safe function))
-          (setq function (cdr function)))
-      (let ((arglist (if (byte-code-function-p function)
-                         (aref function 0)
-                       (second function))))
-        (if (memq '&rest arglist)
-            most-positive-fixnum
-          (length (remq '&optional arglist))))))
-
-  (if (functionp 'start-file-process)
-      (defalias 'magit-start-process 'start-file-process)
-    (defalias 'magit-start-process 'start-process))
-
-  (unless (fboundp 'string-match-p)
-    (defun string-match-p (regexp string &optional start)
-      "Same as `string-match' except this function does not
-change the match data."
-      (let ((inhibit-changing-match-data t))
-        (string-match regexp string start))))
-
-  (if (fboundp 'with-silent-modifications)
-      (defalias 'magit-with-silent-modifications 'with-silent-modifications)
-    (defmacro magit-with-silent-modifications (&rest body)
-      "Execute body without changing `buffer-modified-p'. Also, do not
-record undo information."
-      `(set-buffer-modified-p
-        (prog1 (buffer-modified-p)
-          (let ((buffer-undo-list t)
-                before-change-functions
-                after-change-functions)
-            ,@body)))))
-
-  (if (>= (magit-max-args-internal 'delete-directory) 2)
-      (defalias 'magit-delete-directory 'delete-directory)
-    (defun magit-delete-directory (directory &optional recursive)
-      "Deletes a directory named DIRECTORY.  If RECURSIVE is non-nil,
-recursively delete all of DIRECTORY's contents as well.
-
-Does not follow symlinks."
-      (if (or (file-symlink-p directory)
-              (not (file-directory-p directory)))
-          (delete-file directory)
-        (if recursive
-            ;; `directory-files-no-dot-files-regex' borrowed from Emacs 23
-            (dolist (file (directory-files directory 'full "\\([^.]\\|\\.\\([^.]\\|\\..\\)\\).*"))
-              (magit-delete-directory file recursive)))
-        (delete-directory directory)))))
-
-;;; Utilities
-
-(defun magit-set-variable-and-refresh (symbol value)
-  "Set SYMBOL to VALUE and call `magit-refresh-all'"
-  (set-default symbol value)
-  (magit-refresh-all))
-
-(defun magit-iswitchb-completing-read (prompt choices &optional predicate require-match
-                                              initial-input hist def)
-  "iswitchb-based completing-read almost-replacement."
-  (require 'iswitchb)
-  (let ((iswitchb-make-buflist-hook
-         (lambda ()
-           (setq iswitchb-temp-buflist (if (consp (first choices))
-                                           (mapcar #'car choices)
-                                         choices)))))
-    (iswitchb-read-buffer prompt (or initial-input def) require-match)))
-
-(defun magit-ido-completing-read (prompt choices &optional predicate require-match initial-input hist def)
-  "ido-based completing-read almost-replacement."
-  (require 'ido)
-  (let ((selected (ido-completing-read prompt (if (consp (first choices))
-                                                  (mapcar #'car choices)
-                                                choices)
-                                       predicate require-match initial-input hist def)))
-    (if (consp (first choices))
-        (or (cdr (assoc selected choices))
-            selected)
-      selected)))
-
-(defun magit-builtin-completing-read (prompt choices &optional predicate require-match
-                                             initial-input hist def)
-  "Magit wrapper for standard `completing-read' function."
-  (completing-read (if (and def (> (length prompt) 2)
-                            (string-equal ": " (substring prompt -2)))
-                       (format "%s (default %s): " (substring prompt 0 -2) def)
-                     prompt)
-                   choices predicate require-match initial-input hist def))
-
-(defun magit-completing-read (prompt choices &optional predicate require-match
-                                     initial-input hist def)
-  (funcall magit-completing-read-function prompt choices predicate require-match
-           initial-input hist def))
-
-(defun magit-use-region-p ()
-  (if (fboundp 'use-region-p)
-      (use-region-p)
-    (and transient-mark-mode mark-active)))
-
-(defun magit-goto-line (line)
-  "Like `goto-line' but doesn't set the mark."
-  (save-restriction
-    (widen)
-    (goto-char 1)
-    (forward-line (1- line))))
-
-(defun magit-trim-line (str)
-  (if (string= str "")
-      nil
-    (if (equal (elt str (- (length str) 1)) ?\n)
-        (substring str 0 (- (length str) 1))
-      str)))
-
-(defun magit-split-lines (str)
-  (if (string= str "")
-      nil
-    (let ((lines (nreverse (split-string str "\n"))))
-      (if (string= (car lines) "")
-          (setq lines (cdr lines)))
-      (nreverse lines))))
-
-(defun magit-git-insert (args)
-  (insert (magit-git-output args)))
-
-(defun magit-git-output (args)
-  (magit-cmd-output magit-git-executable (append magit-git-standard-options args)))
-
-(defun magit-cmd-insert (cmd args)
-  (insert (magit-cmd-output cmd args)))
-
-(defun magit-cmd-output (cmd args)
-  (let ((cmd-output (with-output-to-string
-                      (with-current-buffer standard-output
-                        (apply #'process-file
-                               cmd
-                               nil (list t nil) nil
-                               args)))))
-    (replace-regexp-in-string "\e\\[.*?m" "" cmd-output)))
-
-(defun magit-git-string (&rest args)
-  (magit-trim-line (magit-git-output args)))
-
-(defun magit-git-lines (&rest args)
-  (magit-split-lines (magit-git-output args)))
-
-(defun magit-git-exit-code (&rest args)
-  (apply #'process-file magit-git-executable nil nil nil
-         (append magit-git-standard-options args)))
-
-(defun magit-file-lines (file)
-  (when (file-exists-p file)
-    (with-temp-buffer
-      (insert-file-contents file)
-      (let ((rev (nreverse (split-string (buffer-string) "\n"))))
-        (nreverse (if (equal (car rev) "")
-                      (cdr rev)
-                    rev))))))
-
-(defun magit-write-file-lines (file lines)
-  (with-temp-buffer
-    (dolist (l lines)
-      (insert l "\n"))
-    (write-file file)))
-
-(defun magit-get (&rest keys)
-  "Return the value of Git config entry specified by KEYS."
-  (magit-git-string "config" (mapconcat 'identity keys ".")))
-
-(defun magit-get-all (&rest keys)
-  "Return all values of the Git config entry specified by KEYS."
-  (magit-git-lines "config" "--get-all" (mapconcat 'identity keys ".")))
-
-(defun magit-get-boolean (&rest keys)
-  "Return the boolean value of Git config entry specified by KEYS."
-  (equal (magit-git-string "config" "--bool" (mapconcat 'identity keys "."))
-         "true"))
+    map)
+  "Keymap for `magit-branch-manager-mode'.")
 
 
-(defun magit-set (val &rest keys)
-  "Set Git config settings specified by KEYS to VAL."
-  (if val
-      (magit-git-string "config" (mapconcat 'identity keys ".") val)
-    (magit-git-string "config" "--unset" (mapconcat 'identity keys "."))))
+(defvar magit-process-mode-map
+  (let ((map (make-sparse-keymap)))
+    (set-keymap-parent map magit-mode-map)
+    map)
+  "Keymap for `magit-process-mode'.")
 
 
-(defun magit-remove-conflicts (alist)
-  (let ((dict (make-hash-table :test 'equal))
-        (result nil))
-    (dolist (a alist)
-      (puthash (car a) (cons (cdr a) (gethash (car a) dict))
-               dict))
-    (maphash (lambda (key value)
-               (if (= (length value) 1)
-                   (push (cons key (car value)) result)
-                 (let ((sub (magit-remove-conflicts
-                             (mapcar (lambda (entry)
-                                       (let ((dir (directory-file-name
-                                                   (substring entry 0 (- (length key))))))
-                                         (cons (concat (file-name-nondirectory dir) "/" key)
-                                               entry)))
-                                     value))))
-                   (setq result (append result sub)))))
-             dict)
-    result))
+(defvar magit-section-jump-map
+  (let ((map (make-sparse-keymap)))
+    (define-key map (kbd "z") 'magit-jump-to-stashes)
+    (define-key map (kbd "n") 'magit-jump-to-untracked)
+    (define-key map (kbd "u") 'magit-jump-to-unstaged)
+    (define-key map (kbd "s") 'magit-jump-to-staged)
+    (define-key map (kbd "f") 'magit-jump-to-unpulled)
+    (define-key map (kbd "p") 'magit-jump-to-unpushed)
+    (define-key map (kbd "r") 'magit-jump-to-pending)
+    map)
+  "Submap for jumping to sections in `magit-status-mode'.")
+(fset 'magit-section-jump-map magit-section-jump-map)
 
 
-(defun magit-git-repo-p (dir)
-  (file-exists-p (expand-file-name ".git" dir)))
+(easy-menu-define magit-mode-menu magit-mode-map
+  "Magit menu"
+  '("Magit"
+    ["Refresh" magit-refresh t]
+    ["Refresh all" magit-refresh-all t]
+    "---"
+    ["Stage" magit-stage-item t]
+    ["Stage all" magit-stage-all t]
+    ["Unstage" magit-unstage-item t]
+    ["Unstage all" magit-unstage-all t]
+    ["Commit" magit-key-mode-popup-committing t]
+    ["Add log entry" magit-commit-add-log t]
+    ["Tag" magit-tag t]
+    "---"
+    ["Diff working tree" magit-diff-working-tree t]
+    ["Diff" magit-diff t]
+    ("Log"
+     ["Short Log" magit-log t]
+     ["Long Log" magit-log-long t]
+     ["Reflog" magit-reflog t]
+     ["Extended..." magit-key-mode-popup-logging t])
+    "---"
+    ["Cherry pick" magit-cherry-pick-item t]
+    ["Apply" magit-apply-item t]
+    ["Revert" magit-revert-item t]
+    "---"
+    ["Ignore" magit-ignore-item t]
+    ["Ignore locally" magit-ignore-item-locally t]
+    ["Discard" magit-discard-item t]
+    ["Reset head" magit-reset-head t]
+    ["Reset working tree" magit-reset-working-tree t]
+    ["Stash" magit-stash t]
+    ["Snapshot" magit-stash-snapshot t]
+    "---"
+    ["Branch..." magit-checkout t]
+    ["Merge" magit-merge t]
+    ["Interactive resolve" magit-interactive-resolve t]
+    ["Rebase" magit-rebase-step t]
+    ("Rewrite"
+     ["Start" magit-rewrite-start t]
+     ["Stop" magit-rewrite-stop t]
+     ["Finish" magit-rewrite-finish t]
+     ["Abort" magit-rewrite-abort t]
+     ["Set used" magit-rewrite-set-used t]
+     ["Set unused" magit-rewrite-set-unused t])
+    "---"
+    ["Push" magit-push t]
+    ["Pull" magit-pull t]
+    ["Remote update" magit-remote-update t]
+    ("Submodule"
+     ["Submodule update" magit-submodule-update t]
+     ["Submodule update and init" magit-submodule-update-init t]
+     ["Submodule init" magit-submodule-init t]
+     ["Submodule sync" magit-submodule-sync t])
+    "---"
+    ("Extensions")
+    "---"
+    ["Display Git output" magit-process t]
+    ["Quit Magit" magit-mode-quit-window t]))
 
 
-(defun magit-git-dir ()
-  "Returns the .git directory for the current repository."
-  (concat (expand-file-name (magit-git-string "rev-parse" "--git-dir")) "/"))
+\f
+;;; Utilities (1)
+;;;; Minibuffer Input
 
 
-(defun magit-no-commit-p ()
-  "Return non-nil if there is no commit in the current git repository."
-  (not (magit-git-string
-        "rev-list" "HEAD" "--max-count=1")))
+(defun magit-iswitchb-completing-read
+  (prompt choices &optional predicate require-match initial-input hist def)
+  "iswitchb-based completing-read almost-replacement."
+  (require 'iswitchb)
+  (let ((iswitchb-make-buflist-hook
+         (lambda ()
+           (setq iswitchb-temp-buflist (if (consp (car choices))
+                                           (mapcar #'car choices)
+                                         choices)))))
+    (iswitchb-read-buffer prompt (or initial-input def) require-match)))
 
 
-(defun magit-list-repos* (dir level)
-  (if (magit-git-repo-p dir)
-      (list dir)
-    (apply #'append
-           (mapcar (lambda (entry)
-                     (unless (or (string= (substring entry -3) "/..")
-                                 (string= (substring entry -2) "/."))
-                       (magit-list-repos* entry (+ level 1))))
-                   (and (file-directory-p dir)
-                        (< level magit-repo-dirs-depth)
-                        (directory-files dir t nil t))))))
+(defun magit-ido-completing-read
+  (prompt choices &optional predicate require-match initial-input hist def)
+  "ido-based completing-read almost-replacement."
+  (require 'ido)
+  (let ((reply (ido-completing-read
+                prompt
+                (if (consp (car choices))
+                    (mapcar #'car choices)
+                  choices)
+                predicate require-match initial-input hist def)))
+    (or (and (consp (car choices))
+             (cdr (assoc reply choices)))
+        reply)))
+
+(defun magit-builtin-completing-read
+  (prompt choices &optional predicate require-match initial-input hist def)
+  "Magit wrapper for standard `completing-read' function."
+  (let ((reply (completing-read
+                (if (and def (> (length prompt) 2)
+                         (string-equal ": " (substring prompt -2)))
+                    (format "%s (default %s): " (substring prompt 0 -2) def)
+                  prompt)
+                choices predicate require-match initial-input hist def)))
+    (if (string= reply "")
+        (if require-match
+            (user-error "Nothing selected")
+          nil)
+      reply)))
+
+(defun magit-completing-read
+  (prompt collection &optional predicate require-match initial-input hist def)
+  "Call function in `magit-completing-read-function' to read user input.
+
+Read `completing-read' documentation for the meaning of the argument."
+  (funcall magit-completing-read-function
+           (concat prompt ": ") collection predicate
+           require-match initial-input hist def))
+
+(defvar magit-gpg-secret-key-hist nil)
+
+(defun magit-read-gpg-secret-key (prompt)
+  (let ((keys (mapcar
+               (lambda (key)
+                 (list (epg-sub-key-id (car (epg-key-sub-key-list key)))
+                       (let ((id-obj (car (epg-key-user-id-list key)))
+                             (id-str nil))
+                         (when id-obj
+                           (setq id-str (epg-user-id-string id-obj))
+                           (if (stringp id-str)
+                               id-str
+                             (epg-decode-dn id-obj))))))
+               (epg-list-keys (epg-make-context epa-protocol) nil t))))
+    (magit-completing-read prompt keys nil t nil 'magit-gpg-secret-key-hist
+                           (car (or magit-gpg-secret-key-hist keys)))))
+
+;;;; Various Utilities
+
+(defmacro magit-bind-match-strings (varlist &rest body)
+  (declare (indent 1))
+  (let ((i 0))
+    `(let ,(mapcar (lambda (var)
+                     (list var (list 'match-string (cl-incf i))))
+                   varlist)
+       ,@body)))
+
+(defun magit-file-line (file)
+  "Return the first line of FILE as a string."
+  (when (file-regular-p file)
+    (with-temp-buffer
+      (insert-file-contents file)
+      (buffer-substring-no-properties (point-min)
+                                      (line-end-position)))))
 
 
-(defun magit-list-repos (dirs)
-  (magit-remove-conflicts
-   (apply #'append
-          (mapcar (lambda (dir)
-                    (mapcar #'(lambda (repo)
-                                (cons (file-name-nondirectory repo)
-                                      repo))
-                            (magit-list-repos* dir 0)))
-                  dirs))))
-
-(defun magit-get-top-dir (cwd)
-  (let ((cwd (expand-file-name (file-truename cwd))))
-    (when (file-directory-p cwd)
-      (let* ((default-directory (file-name-as-directory cwd))
-             (cdup (magit-git-string "rev-parse" "--show-cdup")))
-        (when cdup
-          (file-name-as-directory (expand-file-name cdup cwd)))))))
+(defun magit-file-lines (file &optional keep-empty-lines)
+  "Return a list of strings containing one element per line in FILE.
+Unless optional argument KEEP-EMPTY-LINES is t, trim all empty lines."
+  (when (file-regular-p file)
+    (with-temp-buffer
+      (insert-file-contents file)
+      (split-string (buffer-string) "\n" (not keep-empty-lines)))))
+
+(defvar-local magit-file-name ()
+  "Name of file the buffer shows a different version of.")
+
+(defun magit-buffer-file-name (&optional relative)
+  (let* ((topdir (magit-get-top-dir))
+         (filename (or buffer-file-name
+                       (when (buffer-base-buffer)
+                         (with-current-buffer (buffer-base-buffer)
+                           buffer-file-name))
+                       (when magit-file-name
+                         (expand-file-name magit-file-name topdir)))))
+    (when filename
+      (setq filename (file-truename filename))
+      (if relative
+          (file-relative-name filename topdir)
+        filename))))
+
+(defun magit-format-duration (duration spec width)
+  (cl-destructuring-bind (char unit units weight)
+      (car spec)
+    (let ((cnt (round (/ duration weight 1.0))))
+      (if (or (not (cdr spec))
+              (>= (/ duration weight) 1))
+          (if (= width 1)
+              (format "%3i%c" cnt char)
+            (format (format "%%3i %%-%is" width) cnt
+                    (if (= cnt 1) unit units)))
+        (magit-format-duration duration (cdr spec) width)))))
+
+(defun magit-flatten-onelevel (list)
+  (cl-mapcan (lambda (elt)
+               (cond ((consp elt) (copy-sequence elt))
+                     (elt (list elt))))
+             list))
+
+(defun magit-insert (string face &rest args)
+  (if magit-use-overlays
+      (let ((start (point)))
+        (insert string)
+        (let ((ov (make-overlay start (point) nil t)))
+          (overlay-put ov 'face face)
+          ;; (overlay-put ov 'priority 10)
+          (overlay-put ov 'evaporate t)))
+    (insert (propertize string 'face face)))
+  (apply #'insert args))
+
+(defun magit-put-face-property (start end face)
+  (if magit-use-overlays
+      (let ((ov (make-overlay start end nil t)))
+        (overlay-put ov 'face face)
+        ;; (overlay-put ov 'priority 10)
+        (overlay-put ov 'evaporate t))
+    (put-text-property start end 'face face)))
+
+;;;; Buffer Margins
+
+(defun magit-set-buffer-margin (width enable)
+  (let ((window (get-buffer-window)))
+    (when window
+      (with-selected-window window
+        (set-window-margins nil (car (window-margins)) (if enable width 0))
+        (let ((fn (apply-partially
+                   (lambda (width)
+                     (let ((window (get-buffer-window)))
+                       (when window
+                         (with-selected-window window
+                           (set-window-margins nil (car (window-margins))
+                                               width)))))
+                   width)))
+          (if enable
+              (add-hook  'window-configuration-change-hook fn nil t)
+            (remove-hook 'window-configuration-change-hook fn t)))))))
+
+(defun magit-make-margin-overlay (&rest strings)
+  (let ((o (make-overlay (point) (line-end-position) nil t)))
+    (overlay-put o 'evaporate t)
+    (overlay-put o 'before-string
+                 (propertize "o" 'display
+                             (list '(margin right-margin)
+                                   (apply #'concat strings))))))
+
+(defvar-local magit-log-margin-timeunit-width nil)
+
+(defun magit-log-margin-set-timeunit-width ()
+  (cl-destructuring-bind (width characterp duration-spec)
+      magit-log-margin-spec
+    (setq magit-log-margin-timeunit-width
+          (if characterp
+              1
+            (apply 'max (mapcar (lambda (e)
+                                  (max (length (nth 1 e))
+                                       (length (nth 2 e))))
+                                (symbol-value duration-spec)))))))
+
+;;;; Emacsclient Support
+
+(defmacro magit-with-emacsclient (server-window &rest body)
+  "Arrange for Git to use Emacsclient as \"the git editor\".
+
+Git processes that use \"the editor\" have to be asynchronous.
+The use of this macro ensures that such processes inside BODY use
+Emacsclient as \"the editor\" by setting the environment variable
+$GIT_EDITOR accordingly around calls to Git and starting the
+server if necessary."
+  (declare (indent 1))
+  `(let* ((process-environment process-environment)
+          (magit-process-popup-time -1))
+     ;; Make sure the client is usable.
+     (magit-assert-emacsclient "use `magit-with-emacsclient'")
+     ;; Make sure server-use-tcp's value is valid.
+     (unless (featurep 'make-network-process '(:family local))
+       (setq server-use-tcp t))
+     ;; Make sure the server is running.
+     (unless server-process
+       (when (server-running-p server-name)
+         (setq server-name (format "server%s" (emacs-pid)))
+         (when (server-running-p server-name)
+           (server-force-delete server-name)))
+       (server-start))
+     ;; Tell Git to use the client.
+     (setenv "GIT_EDITOR"
+             (concat magit-emacsclient-executable
+     ;; Tell the client where the server file is.
+                     (and (not server-use-tcp)
+                          (concat " --socket-name="
+                                  (expand-file-name server-name
+                                                    server-socket-dir)))))
+     (when server-use-tcp
+       (setenv "EMACS_SERVER_FILE"
+               (expand-file-name server-name server-auth-dir)))
+     ;; As last resort fallback to a new Emacs instance.
+     (setenv "ALTERNATE_EDITOR"
+             (expand-file-name invocation-name invocation-directory))
+     ;; Git has to be called asynchronously in BODY or we create a
+     ;; dead lock.  By the time Emacsclient is called the dynamic
+     ;; binding is no longer in effect and our primitives don't
+     ;; support callbacks.  Temporarily set the default value and
+     ;; restore the old value using a timer.
+     (let ((window ,server-window))
+       (unless (equal window server-window)
+         (run-at-time "1 sec" nil
+                      (apply-partially (lambda (value)
+                                         (setq server-window value))
+                                       server-window))
+         (setq-default server-window window)))
+     ,@body))
+
+(defun magit-use-emacsclient-p ()
+  (and magit-emacsclient-executable
+       (not (tramp-tramp-file-p default-directory))))
+
+(defun magit-assert-emacsclient (action)
+  (unless magit-emacsclient-executable
+    (user-error "Cannot %s when `magit-emacsclient-executable' is nil" action))
+  (when (tramp-tramp-file-p default-directory)
+    (user-error "Cannot %s when accessing repository using tramp" action)))
+
+;;;; Git Config
+
+(defun magit-get (&rest keys)
+  "Return the value of Git config entry specified by KEYS."
+  (magit-git-string "config" (mapconcat 'identity keys ".")))
+
+(defun magit-get-all (&rest keys)
+  "Return all values of the Git config entry specified by KEYS."
+  (magit-git-lines "config" "--get-all" (mapconcat 'identity keys ".")))
+
+(defun magit-get-boolean (&rest keys)
+  "Return the boolean value of Git config entry specified by KEYS."
+  (magit-git-true "config" "--bool" (mapconcat 'identity keys ".")))
+
+(defun magit-set (val &rest keys)
+  "Set Git config settings specified by KEYS to VAL."
+  (if val
+      (magit-git-string "config" (mapconcat 'identity keys ".") val)
+    (magit-git-string "config" "--unset" (mapconcat 'identity keys "."))))
+
+;;;; Git Low-Level
+
+(defun magit-git-repo-p (dir)
+  (file-exists-p (expand-file-name ".git" dir)))
+
+(defun magit-git-dir (&optional path)
+  "Return absolute path to the GIT_DIR for the current repository.
+If optional PATH is non-nil it has to be a path relative to the
+GIT_DIR and its absolute path is returned"
+  (let ((gitdir (magit-git-string "rev-parse" "--git-dir")))
+    (when gitdir
+      (setq gitdir (file-name-as-directory
+                    (magit-expand-git-file-name gitdir)))
+      (if path
+          (expand-file-name (convert-standard-filename path) gitdir)
+        gitdir))))
+
+(defun magit-no-commit-p ()
+  "Return non-nil if there is no commit in the current git repository."
+  (not (magit-git-string "rev-list" "-1" "HEAD")))
+
+(defun magit-get-top-dir (&optional directory)
+  "Return the top directory for the current repository.
+
+Determine the repository which contains `default-directory' in
+either its work tree or git control directory and return its top
+directory.  If there is no top directory, because the repository
+is bare, return the control directory instead.
+
+If optional DIRECTORY is non-nil then return the top directory of
+the repository that contains that instead.  DIRECTORY has to be
+an existing directory."
+  (setq directory (if directory
+                      (file-name-as-directory
+                       (expand-file-name directory))
+                    default-directory))
+  (unless (file-directory-p directory)
+    (error "%s isn't an existing directory" directory))
+  (let* ((default-directory directory)
+         (top (magit-git-string "rev-parse" "--show-toplevel")))
+    (if top
+        (file-name-as-directory (magit-expand-git-file-name top))
+      (let ((gitdir (magit-git-dir)))
+        (when gitdir
+          (if (magit-bare-repo-p)
+              gitdir
+            (file-name-directory (directory-file-name gitdir))))))))
+
+(defun magit-expand-git-file-name (filename)
+  (when (tramp-tramp-file-p default-directory)
+    (setq filename (file-relative-name filename
+                                       (with-parsed-tramp-file-name
+                                           default-directory nil
+                                         localname))))
+  (expand-file-name filename))
+
+(defun magit-file-relative-name (file)
+  "Return the path of FILE relative to the repository root.
+If FILE isn't inside a Git repository then return nil."
+  (setq file (file-truename file))
+  (let ((topdir (magit-get-top-dir (file-name-directory file))))
+    (and topdir (substring file (length topdir)))))
+
+(defun magit-bare-repo-p ()
+  "Return t if the current repository is bare."
+  (magit-git-true "rev-parse" "--is-bare-repository"))
 
 (defun magit-get-ref (ref)
   (magit-git-string "symbolic-ref" "-q" ref))
 
 (defun magit-get-current-branch ()
 
 (defun magit-get-ref (ref)
   (magit-git-string "symbolic-ref" "-q" ref))
 
 (defun magit-get-current-branch ()
-  (let* ((head (magit-get-ref "HEAD"))
-         (pos (and head (string-match "^refs/heads/" head))))
-    (if pos
-        (substring head 11)
-      nil)))
+  (let ((head (magit-get-ref "HEAD")))
+    (when (and head (string-match "^refs/heads/" head))
+      (substring head 11))))
+
+(defun magit-get-remote/branch (&optional branch verify)
+  "Return the remote-tracking branch of BRANCH used for pulling.
+Return a string of the form \"REMOTE/BRANCH\".
+
+If optional BRANCH is nil return the remote-tracking branch of
+the current branch.  If optional VERIFY is non-nil verify that
+the remote branch exists; else return nil."
+  (save-match-data
+    (let (remote remote-branch remote/branch)
+      (and (or branch (setq branch (magit-get-current-branch)))
+           (setq remote (magit-get "branch" branch "remote"))
+           (setq remote-branch (magit-get "branch" branch "merge"))
+           (string-match "^refs/heads/\\(.+\\)" remote-branch)
+           (setq remote/branch
+                 (concat remote "/" (match-string 1 remote-branch)))
+           (or (not verify)
+               (magit-git-success "rev-parse" "--verify" remote/branch))
+           remote/branch))))
+
+(defun magit-get-tracked-branch (&optional branch qualified pretty)
+  "Return the name of the tracking branch the local branch name BRANCH.
+
+If optional QUALIFIED is non-nil return the full branch path,
+otherwise try to shorten it to a name (which may fail).  If
+optional PRETTY is non-nil additionally format the branch name
+according to option `magit-remote-ref-format'."
+  (unless branch
+    (setq branch (magit-get-current-branch)))
+  (when branch
+    (let ((remote (magit-get "branch" branch "remote"))
+          (merge  (magit-get "branch" branch "merge")))
+      (when (and (not merge)
+                 (not (equal remote ".")))
+        (setq merge branch))
+      (when (and remote merge)
+        (if (string= remote ".")
+            (cond (qualified merge)
+                  ((string-match "^refs/heads/" merge)
+                   (substring merge 11))
+                  ((string-match "^refs/" merge)
+                   merge))
+          (let* ((fetch (mapcar (lambda (f) (split-string f "[+:]" t))
+                                (magit-get-all "remote" remote "fetch")))
+                 (match (cadr (assoc merge fetch))))
+            (unless match
+              (let* ((prefix (nreverse (split-string merge "/")))
+                     (unique (list (car prefix))))
+                (setq prefix (cdr prefix))
+                (setq fetch
+                      (cl-mapcan
+                       (lambda (f)
+                         (cl-destructuring-bind (from to) f
+                           (setq from (nreverse (split-string from "/")))
+                           (when (equal (car from) "*")
+                             (list (list (cdr from) to)))))
+                       fetch))
+                (while (and prefix (not match))
+                  (if (setq match (cadr (assoc prefix fetch)))
+                      (setq match (concat (substring match 0 -1)
+                                          (mapconcat 'identity unique "/")))
+                    (push (car prefix) unique)
+                    (setq prefix (cdr prefix))))))
+            (cond ((not match) nil)
+                  (qualified match)
+                  ((string-match "^refs/remotes/" match)
+                   (if pretty
+                       (magit-format-ref match)
+                     (substring match 13)))
+                  (t match))))))))
+
+(defun magit-get-previous-branch ()
+  "Return the refname of the previously checked out branch.
+Return nil if the previously checked out branch no longer exists."
+  (magit-name-rev (magit-git-string "rev-parse" "--verify" "@{-1}")))
+
+(defun magit-get-current-tag (&optional with-distance-p)
+  "Return the closest tag reachable from \"HEAD\".
+
+If optional WITH-DISTANCE-P is non-nil then return (TAG COMMITS),
+if it is `dirty' return (TAG COMMIT DIRTY). COMMITS is the number
+of commits in \"HEAD\" but not in TAG and DIRTY is t if there are
+uncommitted changes, nil otherwise."
+  (let ((tag (magit-git-string "describe" "--long" "--tags"
+                               (and (eq with-distance-p 'dirty) "--dirty"))))
+    (save-match-data
+      (when tag
+        (string-match
+         "\\(.+\\)-\\(?:0[0-9]*\\|\\([0-9]+\\)\\)-g[0-9a-z]+\\(-dirty\\)?$" tag)
+        (if with-distance-p
+            (list (match-string 1 tag)
+                  (string-to-number (or (match-string 2 tag) "0"))
+                  (and (match-string 3 tag) t))
+          (match-string 1 tag))))))
+
+(defun magit-get-next-tag (&optional with-distance-p)
+  "Return the closest tag from which \"HEAD\" is reachable.
+
+If no such tag can be found or if the distance is 0 (in which
+case it is the current tag, not the next) return nil instead.
+
+If optional WITH-DISTANCE-P is non-nil then return (TAG COMMITS)
+where COMMITS is the number of commits in TAG but not in \"HEAD\"."
+  (let ((rev (magit-git-string "describe" "--contains" "HEAD")))
+    (save-match-data
+      (when (and rev (string-match "^[^^~]+" rev))
+        (let ((tag (match-string 0 rev)))
+          (unless (equal tag (magit-get-current-tag))
+            (if with-distance-p
+                (list tag (car (magit-rev-diff-count tag "HEAD")))
+              tag)))))))
 
 (defun magit-get-remote (branch)
   "Return the name of the remote for BRANCH.
 If branch is nil or it has no remote, but a remote named
 
 (defun magit-get-remote (branch)
   "Return the name of the remote for BRANCH.
 If branch is nil or it has no remote, but a remote named
-\"origin\" exists, return that. Otherwise, return nil."
+\"origin\" exists, return that.  Otherwise, return nil."
   (let ((remote (or (and branch (magit-get "branch" branch "remote"))
                     (and (magit-get "remote" "origin" "url") "origin"))))
   (let ((remote (or (and branch (magit-get "branch" branch "remote"))
                     (and (magit-get "remote" "origin" "url") "origin"))))
-    (if (string= remote "") nil remote)))
+    (unless (string= remote "")
+      remote)))
 
 (defun magit-get-current-remote ()
   "Return the name of the remote for the current branch.
 
 (defun magit-get-current-remote ()
   "Return the name of the remote for the current branch.
@@ -1012,25 +2234,7 @@ Otherwise, return nil."
   (magit-get-remote (magit-get-current-branch)))
 
 (defun magit-ref-exists-p (ref)
   (magit-get-remote (magit-get-current-branch)))
 
 (defun magit-ref-exists-p (ref)
-  (= (magit-git-exit-code "show-ref" "--verify" ref) 0))
-
-(defun magit-read-top-dir (dir)
-  "Ask the user for a Git repository.  The choices offered by
-auto-completion will be the repositories under `magit-repo-dirs'.
-If `magit-repo-dirs' is nil or DIR is non-nill, then
-autocompletion will offer directory names."
-  (if (and (not dir) magit-repo-dirs)
-      (let* ((repos (magit-list-repos magit-repo-dirs))
-             (reply (magit-completing-read "Git repository: " repos)))
-        (file-name-as-directory
-         (or (cdr (assoc reply repos))
-             (if (file-directory-p reply)
-                 (expand-file-name reply)
-               (error "Not a repository or a directory: %s" reply)))))
-    (file-name-as-directory
-     (read-directory-name "Git repository: "
-                          (or (magit-get-top-dir default-directory)
-                              default-directory)))))
+  (magit-git-success "show-ref" "--verify" ref))
 
 (defun magit-rev-parse (ref)
   "Return the SHA hash for REF."
 
 (defun magit-rev-parse (ref)
   "Return the SHA hash for REF."
@@ -1038,15 +2242,26 @@ autocompletion will offer directory names."
 
 (defun magit-ref-ambiguous-p (ref)
   "Return whether or not REF is ambiguous."
 
 (defun magit-ref-ambiguous-p (ref)
   "Return whether or not REF is ambiguous."
-  ;; If REF is ambiguous, rev-parse just prints errors,
-  ;; so magit-git-string returns nil.
+  ;; An ambiguous ref does not cause `git rev-parse --abbrev-ref'
+  ;; to exits with a non-zero status.  But there is nothing on
+  ;; stdout in that case.
   (not (magit-git-string "rev-parse" "--abbrev-ref" ref)))
 
   (not (magit-git-string "rev-parse" "--abbrev-ref" ref)))
 
+(defun magit-rev-diff-count (a b)
+  "Return the commits in A but not B and vice versa.
+Return a list of two integers: (A>B B>A)."
+  (mapcar 'string-to-number
+          (split-string (magit-git-string "rev-list"
+                                          "--count" "--left-right"
+                                          (concat a "..." b))
+                        "\t")))
+
 (defun magit-name-rev (rev &optional no-trim)
   "Return a human-readable name for REV.
 (defun magit-name-rev (rev &optional no-trim)
   "Return a human-readable name for REV.
-Unlike git name-rev, this will remove tags/ and remotes/ prefixes
-if that can be done unambiguously (unless optional arg NO-TRIM is
-non-nil).  In addition, it will filter out revs involving HEAD."
+Unlike `git name-rev', this will remove \"tags/\" and \"remotes/\"
+prefixes if that can be done unambiguously (unless optional arg
+NO-TRIM is non-nil).  In addition, it will filter out revs
+involving HEAD."
   (when rev
     (let ((name (magit-git-string "name-rev" "--no-undefined" "--name-only" rev)))
       ;; There doesn't seem to be a way of filtering HEAD out from name-rev,
   (when rev
     (let ((name (magit-git-string "name-rev" "--no-undefined" "--name-only" rev)))
       ;; There doesn't seem to be a way of filtering HEAD out from name-rev,
@@ -1070,351 +2285,341 @@ non-nil).  In addition, it will filter out revs involving HEAD."
             (setq rev plain-name))))
       rev)))
 
             (setq rev plain-name))))
       rev)))
 
-(defun magit-highlight-line-whitespace ()
-  (when (and magit-highlight-whitespace
-             (or (derived-mode-p 'magit-status-mode)
-                 (not (eq magit-highlight-whitespace 'status))))
-    (if (and magit-highlight-trailing-whitespace
-             (looking-at "^[-+].*?\\([ \t]+\\)$"))
-        (overlay-put (make-overlay (match-beginning 1) (match-end 1))
-                     'face 'magit-whitespace-warning-face))
-    (if (or (and (eq magit-current-indentation 'tabs)
-                 (looking-at "^[-+]\\( *\t[ \t]*\\)"))
-            (and (integerp magit-current-indentation)
-                 (looking-at (format "^[-+]\\([ \t]* \\{%s,\\}[ \t]*\\)"
-                                     magit-current-indentation))))
-        (overlay-put (make-overlay (match-beginning 1) (match-end 1))
-                     'face 'magit-whitespace-warning-face))))
-
-(defun magit-put-line-property (prop val)
-  (put-text-property (line-beginning-position) (line-beginning-position 2)
-                     prop val))
-
-(defun magit-format-commit (commit format)
-  (magit-git-string "log" "--max-count=1"
-                    (concat "--pretty=format:" format)
-                    commit))
-
-(defun magit-current-line ()
-  (buffer-substring-no-properties (line-beginning-position)
-                                  (line-end-position)))
-
-(defun magit-insert-region (beg end buf)
-  (let ((text (buffer-substring-no-properties beg end)))
-    (with-current-buffer buf
-      (insert text))))
-
-(defun magit-insert-current-line (buf)
-  (let ((text (buffer-substring-no-properties
-               (line-beginning-position) (line-beginning-position 2))))
-    (with-current-buffer buf
-      (insert text))))
-
 (defun magit-file-uptodate-p (file)
 (defun magit-file-uptodate-p (file)
-  (eq (magit-git-exit-code "diff" "--quiet" "--" file) 0))
+  (magit-git-success "diff" "--quiet" "--" file))
 
 (defun magit-anything-staged-p ()
 
 (defun magit-anything-staged-p ()
-  (not (eq (magit-git-exit-code "diff" "--quiet" "--cached") 0)))
+  (magit-git-failure "diff" "--quiet" "--cached"))
 
 
-(defun magit-everything-clean-p ()
-  (and (not (magit-anything-staged-p))
-       (eq (magit-git-exit-code "diff" "--quiet") 0)))
+(defun magit-anything-unstaged-p ()
+  (magit-git-failure "diff" "--quiet"))
+
+(defun magit-anything-modified-p ()
+  (or (magit-anything-staged-p)
+      (magit-anything-unstaged-p)))
 
 (defun magit-commit-parents (commit)
   (cdr (split-string (magit-git-string "rev-list" "-1" "--parents" commit))))
 
 
 (defun magit-commit-parents (commit)
   (cdr (split-string (magit-git-string "rev-list" "-1" "--parents" commit))))
 
-;; XXX - let the user choose the parent
-
-(defun magit-choose-parent-id (commit op)
-  (let* ((parents (magit-commit-parents commit)))
-    (if (> (length parents) 1)
-        (error "Can't %s merge commits" op)
-      nil)))
-
-;;; Revisions and ranges
-
-(defvar magit-current-range nil
-  "The range described by the current buffer.
-This is only non-nil in diff and log buffers.
-
-This has three possible (non-nil) forms.  If it's a string REF or
-a singleton list (REF), then the range is from REF to the current
-working directory state (or HEAD in a log buffer).  If it's a
-pair (START . END), then the range is START..END.")
-(make-variable-buffer-local 'magit-current-range)
-
-(defun magit-list-interesting-refs (&optional uninteresting)
-  "Return interesting references as given by `git show-ref'.
-Removes references matching UNINTERESTING from the
-results. UNINTERESTING can be either a function taking a single
-argument or a list of strings used as regexps."
-  (let ((refs ()))
-    (dolist (line (magit-git-lines "show-ref"))
-      (if (string-match "[^ ]+ +\\(.*\\)" line)
-          (let ((ref (match-string 1 line)))
-            (cond ((and (functionp uninteresting)
-                        (funcall uninteresting ref)))
-                  ((and (not (functionp uninteresting))
-                        (loop for i in uninteresting thereis (string-match i ref))))
-                  (t
-                   (let ((fmt-ref (magit-format-ref ref)))
-                     (when fmt-ref
-                       (push (cons fmt-ref
-                                   (replace-regexp-in-string "^refs/heads/"
-                                                             "" ref))
-                             refs))))))))
-    (nreverse refs)))
+(defun magit-assert-one-parent (commit command)
+  (when (> (length (magit-commit-parents commit)) 1)
+    (user-error "Cannot %s a merge commit" command)))
+
+(defun magit-decode-git-path (path)
+  (if (eq (aref path 0) ?\")
+      (string-as-multibyte (read path))
+    path))
+
+(defun magit-abbrev-length ()
+  (string-to-number (or (magit-get "core.abbrev") "7")))
+
+(defun magit-abbrev-arg ()
+  (format "--abbrev=%d" (magit-abbrev-length)))
+
+;;;; Git Revisions
+
+(defvar magit-uninteresting-refs
+  '("^refs/stash$"
+    "^refs/remotes/[^/]+/HEAD$"
+    "^refs/remotes/[^/]+/top-bases$"
+    "^refs/top-bases$"))
+
+(cl-defun magit-list-interesting-refs (&optional uninteresting
+                                                 (refs nil srefs))
+  (cl-loop for ref in (if srefs
+                          refs
+                        (mapcar (lambda (l)
+                                  (cadr (split-string l " ")))
+                                (magit-git-lines "show-ref")))
+           with label
+           unless (or (cl-loop for i in
+                               (cl-typecase uninteresting
+                                 (null magit-uninteresting-refs)
+                                 (list uninteresting)
+                                 (string (cons (format "^refs/heads/%s$"
+                                                       uninteresting)
+                                               magit-uninteresting-refs)))
+                               thereis (string-match i ref))
+                      (not (setq label (magit-format-ref ref))))
+           collect (cons label ref)))
 
 (defun magit-format-ref (ref)
 
 (defun magit-format-ref (ref)
-  "Convert fully-specified ref REF into its displayable form
-according to `magit-remote-ref-format'"
-  (cond
-   ((null ref)
-    nil)
-   ((string-match "refs/heads/\\(.*\\)" ref)
-    (match-string 1 ref))
-   ((string-match "refs/tags/\\(.*\\)" ref)
-    (format (if (eq magit-remote-ref-format 'branch-then-remote)
-                "%s (tag)"
-              "%s")
-            (match-string 1 ref)))
-   ((string-match "refs/remotes/\\([^/]+\\)/\\(.+\\)" ref)
-    (if (eq magit-remote-ref-format 'branch-then-remote)
-        (format "%s (%s)"
-                (match-string 2 ref)
-                (match-string 1 ref))
-      (format "%s/%s"
-              (match-string 1 ref)
-              (match-string 2 ref))))))
-
-(defun magit-tree-contents (treeish)
-  "Returns a list of all files under TREEISH.  TREEISH can be a tree,
-a commit, or any reference to one of those."
-  (let ((return-value nil))
-    (with-temp-buffer
-      (magit-git-insert (list "ls-tree" "-r" treeish))
-      (if (eql 0 (buffer-size))
-          (error "%s is not a commit or tree." treeish))
-      (goto-char (point-min))
-      (while (search-forward-regexp "\t\\(.*\\)" nil 'noerror)
-        (push (match-string 1) return-value)))
-    return-value))
-
-(defvar magit-uninteresting-refs '("refs/remotes/\\([^/]+\\)/HEAD$" "refs/stash"))
-
-(defun magit-read-file-from-rev (revision)
-  (magit-completing-read (format "Retrieve file from %s: " revision)
-                         (magit-tree-contents revision)
-                         nil
-                         'require-match
-                         nil
-                         'magit-read-file-hist
-                         (if buffer-file-name
-                             (let ((topdir-length (length (magit-get-top-dir default-directory))))
-                               (substring (buffer-file-name) topdir-length)))))
-
-(defun magit-read-rev (prompt &optional default uninteresting)
-  (let* ((interesting-refs (magit-list-interesting-refs
-                            (or uninteresting magit-uninteresting-refs)))
-         (reply (magit-completing-read (concat prompt ": ") interesting-refs
-                                       nil nil nil 'magit-read-rev-history default))
-         (rev (or (cdr (assoc reply interesting-refs)) reply)))
-    (if (string= rev "")
-        nil
-      rev)))
+  (cond ((string-match "refs/heads/\\(.*\\)" ref)
+         (match-string 1 ref))
+        ((string-match "refs/tags/\\(.*\\)" ref)
+         (format (if (eq magit-remote-ref-format 'branch-then-remote)
+                     "%s (tag)"
+                   "%s")
+                 (match-string 1 ref)))
+        ((string-match "refs/remotes/\\([^/]+\\)/\\(.+\\)" ref)
+         (if (eq magit-remote-ref-format 'branch-then-remote)
+             (format "%s (%s)"
+                     (match-string 2 ref)
+                     (match-string 1 ref))
+           (substring ref 13)))
+        (t ref)))
+
+(defvar magit-read-file-hist nil)
+
+(defun magit-read-file-from-rev (revision &optional default)
+  (unless revision
+    (setq revision "HEAD"))
+  (let ((default-directory (magit-get-top-dir)))
+    (magit-completing-read
+     (format "Retrieve file from %s" revision)
+     (magit-git-lines "ls-tree" "-r" "-t" "--name-only" revision)
+     nil 'require-match
+     nil 'magit-read-file-hist
+     (or default (magit-buffer-file-name t)))))
+
+(defun magit-read-file-trace (ignored)
+  (let ((file  (magit-read-file-from-rev "HEAD"))
+        (trace (read-string "Trace: ")))
+    (if (string-match
+         "^\\(/.+/\\|:[^:]+\\|[0-9]+,[-+]?[0-9]+\\)\\(:\\)?$" trace)
+        (concat trace (or (match-string 2 trace) ":") file)
+      (user-error "Trace is invalid, see man git-log"))))
 
 
-(defun magit-read-rev-range (op &optional def-beg def-end)
-  (let ((beg (magit-read-rev (format "%s start" op)
-                             def-beg)))
-    (if (not beg)
-        nil
-      (save-match-data
-        (if (string-match "^\\(.+\\)\\.\\.\\(.+\\)$" beg)
-            (cons (match-string 1 beg) (match-string 2 beg))
-          (let ((end (magit-read-rev (format "%s end" op) def-end)))
-            (cons beg end)))))))
-
-(defun magit-rev-to-git (rev)
-  (or rev
-      (error "No revision specified"))
-  (if (string= rev ".")
-      (magit-marked-commit)
+(defvar magit-read-rev-history nil
+  "The history of inputs to `magit-read-rev' and `magit-read-tag'.")
+
+(defun magit-read-tag (prompt &optional require-match)
+  (magit-completing-read prompt (magit-git-lines "tag") nil
+                         require-match nil 'magit-read-rev-history))
+
+(defun magit-read-rev (prompt &optional default uninteresting noselection)
+  (let* ((interesting-refs
+          (mapcar (lambda (elt)
+                    (setcdr elt (replace-regexp-in-string
+                                 "^refs/heads/" "" (cdr elt)))
+                    elt)
+                  (magit-list-interesting-refs uninteresting)))
+         (reply (magit-completing-read prompt interesting-refs nil nil nil
+                                       'magit-read-rev-history default))
+         (rev (or (cdr (assoc reply interesting-refs)) reply)))
+    (when (equal rev ".")
+      (setq rev magit-marked-commit))
+    (unless (or rev noselection)
+      (user-error "No rev selected"))
     rev))
 
     rev))
 
-(defun magit-rev-range-to-git (range)
-  (or range
-      (error "No revision range specified"))
-  (if (stringp range)
-      range
-    (if (cdr range)
-        (format "%s..%s"
-                (magit-rev-to-git (car range))
-                (magit-rev-to-git (cdr range)))
-      (format "%s" (magit-rev-to-git (car range))))))
-
-(defun magit-rev-describe (rev)
-  (or rev
-      (error "No revision specified"))
-  (if (string= rev ".")
-      "mark"
-    (magit-name-rev rev)))
-
-(defun magit-rev-range-describe (range things)
-  (or range
-      (error "No revision range specified"))
-  (if (stringp range)
-      (format "%s in %s" things range)
-    (if (cdr range)
-        (format "%s from %s to %s" things
-                (magit-rev-describe (car range))
-                (magit-rev-describe (cdr range)))
-      (format "%s at %s" things (magit-rev-describe (car range))))))
-
-(defun magit-default-rev (&optional no-trim)
-  (or (magit-name-rev (magit-commit-at-point t) no-trim)
-      (let ((branch (magit-guess-branch)))
-        (if branch
-            (if (string-match "^refs/\\(.*\\)" branch)
-                (match-string 1 branch)
-              branch)))))
-
-(defun magit-read-remote (&optional prompt def)
-  "Read the name of a remote.
-PROMPT is used as the prompt, and defaults to \"Remote\".
-DEF is the default value."
-  (let* ((prompt (or prompt "Remote"))
-         (def (or def (magit-guess-remote)))
-         (remotes (magit-git-lines "remote"))
-
-         (reply (magit-completing-read (concat prompt ": ") remotes
-                                       nil nil nil nil def)))
-    (if (string= reply "") nil reply)))
-
-(defun magit-read-remote-branch (remote &optional prompt default)
-  (let* ((prompt (or prompt (format "Remote branch (in %s)" remote)))
-         (branches (delete nil
-                           (mapcar
-                            (lambda (b)
-                              (and (not (string-match " -> " b))
-                                   (string-match (format "^ *%s/\\(.*\\)$"
-                                                         (regexp-quote remote)) b)
-                                   (match-string 1 b)))
-                            (magit-git-lines "branch" "-r"))))
-         (reply (magit-completing-read (concat prompt ": ") branches
-                                       nil nil nil nil default)))
-    (if (string= reply "") nil reply)))
-
-;;; Sections
-
-;; A buffer in magit-mode is organized into hierarchical sections.
-;; These sections are used for navigation and for hiding parts of the
-;; buffer.
-;;
-;; Most sections also represent the objects that Magit works with,
-;; such as files, diffs, hunks, commits, etc.  The 'type' of a section
-;; identifies what kind of object it represents (if any), and the
-;; parent and grand-parent, etc provide the context.
-
-(defstruct magit-section
-  parent title beginning end children hidden type info
-  needs-refresh-on-show)
-
-(defvar magit-top-section nil
-  "The top section of the current buffer.")
-(make-variable-buffer-local 'magit-top-section)
-(put 'magit-top-section 'permanent-local t)
-
-(defvar magit-old-top-section nil)
-
-(defvar magit-section-hidden-default nil)
-
-(defun magit-new-section (title type)
-  "Create a new section with title TITLE and type TYPE in current buffer.
-
-If not `magit-top-section' exist, the new section will be the new top-section
-otherwise, the new-section will be a child of the current top-section.
-
-If TYPE is nil, the section won't be highlighted."
-  (let* ((s (make-magit-section :parent magit-top-section
-                                :title title
-                                :type type
-                                :hidden magit-section-hidden-default))
-         (old (and magit-old-top-section
-                   (magit-find-section (magit-section-path s)
-                                       magit-old-top-section))))
-    (if magit-top-section
-        (push s (magit-section-children magit-top-section))
-      (setq magit-top-section s))
-    (if old
-        (setf (magit-section-hidden s) (magit-section-hidden old)))
-    s))
-
-(defun magit-cancel-section (section)
-  "Delete the section SECTION."
-  (delete-region (magit-section-beginning section)
-                 (magit-section-end section))
-  (let ((parent (magit-section-parent section)))
-    (if parent
-        (setf (magit-section-children parent)
-              (delq section (magit-section-children parent)))
-      (setq magit-top-section nil))))
+(defun magit-read-rev-with-default (prompt)
+  (magit-read-rev prompt
+                  (let ((branch (or (magit-guess-branch) "HEAD")))
+                    (when branch
+                      (if (string-match "^refs/\\(.*\\)" branch)
+                          (match-string 1 branch)
+                        branch)))))
 
 
-(defmacro magit-with-section (title type &rest body)
-  "Create a new section of title TITLE and type TYPE and evaluate BODY there.
+(defun magit-read-rev-range (op &optional def-beg def-end)
+  (let ((beg (magit-read-rev (format "%s range or start" op) def-beg)))
+    (save-match-data
+      (if (string-match "^\\(.+\\)\\.\\.\\(.+\\)$" beg)
+          (cons (match-string 1 beg) (match-string 2 beg))
+        (let ((end (magit-read-rev (format "%s end" op) def-end nil t)))
+          (if end (cons beg end) beg))))))
+
+(defun magit-read-stash (prompt)
+  (let ((n (read-number prompt 0))
+        (l (1- (length (magit-git-lines "stash" "list")))))
+    (if (> n l)
+        (user-error "No stash older than stash@{%i}" l)
+      (format "stash@{%i}" n))))
+
+(defun magit-read-remote (prompt &optional default require-match)
+  (magit-completing-read prompt (magit-git-lines "remote")
+                         nil require-match nil nil
+                         (or default (magit-guess-remote))))
+
+(defun magit-read-remote-branch (prompt remote &optional default)
+  (let ((branch (magit-completing-read
+                 prompt
+                 (cl-mapcan
+                  (lambda (b)
+                    (and (not (string-match " -> " b))
+                         (string-match (format "^ *%s/\\(.*\\)$"
+                                               (regexp-quote remote)) b)
+                         (list (match-string 1 b))))
+                  (magit-git-lines "branch" "-r"))
+                 nil nil nil nil default)))
+    (unless (string= branch "")
+      branch)))
 
 
-Sections created inside BODY will become children of the new
-section. BODY must leave point at the end of the created section.
+(defun magit-format-ref-label (ref)
+  (cl-destructuring-bind (re face fn)
+      (cl-find-if (lambda (ns)
+                    (string-match (car ns) ref))
+                  magit-refs-namespaces)
+    (if fn
+        (funcall fn ref face)
+      (propertize (or (match-string 1 ref) ref) 'face face))))
+
+(defun magit-format-ref-labels (string)
+  (save-match-data
+    (mapconcat 'magit-format-ref-label
+               (mapcar 'cdr
+                       (magit-list-interesting-refs
+                        nil (split-string string "\\(tag: \\|[(), ]\\)" t)))
+               " ")))
+
+(defun magit-insert-ref-labels (string)
+  (save-match-data
+    (dolist (ref (split-string string "\\(tag: \\|[(), ]\\)" t) " ")
+      (cl-destructuring-bind (re face fn)
+          (cl-find-if (lambda (elt) (string-match (car elt) ref))
+                      magit-refs-namespaces)
+        (if fn
+            (let ((text (funcall fn ref face)))
+              (magit-insert text (get-text-property 1 'face text) ?\s))
+        (magit-insert (or (match-string 1 ref) ref) face ?\s))))))
+
+(defun magit-format-rev-summary (rev)
+  (let ((s (magit-git-string "log" "-1"
+                             (concat "--pretty=format:%h %s") rev)))
+    (when s
+      (string-match " " s)
+      (put-text-property 0 (match-beginning 0) 'face 'magit-log-sha1 s)
+      s)))
+
+;;; Magit Api
+;;;; Section Api
+;;;;; Section Core
+
+(cl-defstruct magit-section
+  type info
+  beginning content-beginning end
+  hidden needs-refresh-on-show highlight
+  diff-status diff-file2 diff-range
+  process
+  parent children)
+
+(defvar-local magit-root-section nil
+  "The root section in the current buffer.
+All other sections are descendants of this section.  The value
+of this variable is set by `magit-with-section' and you should
+never modify it.")
+(put 'magit-root-section 'permanent-local t)
+
+;;;;; Section Creation
+
+(defvar magit-with-section--parent nil
+  "For use by `magit-with-section' only.")
+
+(defvar magit-with-section--oldroot nil
+  "For use by `magit-with-section' only.")
+
+(defmacro magit-with-section (arglist &rest body)
+  "\n\n(fn (NAME TYPE &optional INFO HEADING NOHIGHLIGHT COLLAPSE) &rest ARGS)"
+  (declare (indent 1) (debug ((form form &optional form form form) body)))
+  (let ((s (car arglist)))
+    `(let ((,s (make-magit-section
+                :type ',(nth 1 arglist)
+                :info  ,(nth 2 arglist)
+                :highlight (not ,(nth 4 arglist))
+                :beginning (point-marker)
+                :content-beginning (point-marker)
+                :parent magit-with-section--parent)))
+       (setf (magit-section-hidden ,s)
+             (let ((old (and magit-with-section--oldroot
+                             (magit-find-section (magit-section-path ,s)
+                                                 magit-with-section--oldroot))))
+               (if old
+                   (magit-section-hidden old)
+                 ,(nth 5 arglist))))
+       (let ((magit-with-section--parent ,s)
+             (magit-with-section--oldroot
+              (or magit-with-section--oldroot
+                  (unless magit-with-section--parent
+                    (prog1 magit-root-section
+                      (setq magit-root-section ,s))))))
+         ,@body)
+       (when ,s
+         (set-marker-insertion-type (magit-section-content-beginning ,s) t)
+         (let ((heading ,(nth 3 arglist)))
+           (when heading
+             (save-excursion
+               (goto-char (magit-section-beginning ,s))
+               (insert
+                (if (string-match-p "\n$" heading)
+                    (substring heading 0 -1)
+                 (propertize
+                  (let (c)
+                    (if (and magit-show-child-count
+                              (string-match-p ":$" heading)
+                             (> (setq c (length (magit-section-children ,s))) 0))
+                        (format "%s (%s):" (substring heading 0 -1) c)
+                      heading))
+                  'face 'magit-section-title)))
+               (insert "\n"))))
+         (set-marker-insertion-type (magit-section-beginning ,s) t)
+         (goto-char (max (point) ; smaller if there is no content
+                         (magit-section-content-beginning ,s)))
+         (setf (magit-section-end ,s) (point-marker))
+         (save-excursion
+           (goto-char (magit-section-beginning ,s))
+           (let ((end (magit-section-end ,s)))
+             (while (< (point) end)
+               (let ((next (or (next-single-property-change
+                                (point) 'magit-section)
+                               end)))
+                 (unless (get-text-property (point) 'magit-section)
+                   (put-text-property (point) next 'magit-section ,s))
+                 (goto-char next)))))
+         (if (eq ,s magit-root-section)
+             (magit-section-set-hidden magit-root-section nil)
+           (setf (magit-section-children (magit-section-parent ,s))
+                 (nconc (magit-section-children (magit-section-parent ,s))
+                        (list ,s)))))
+       ,s)))
 
 
-If TYPE is nil, the section won't be highlighted."
+(defmacro magit-cmd-insert-section (arglist washer program &rest args)
+  "\n\n(fn (TYPE &optional HEADING) WASHER PROGRAM &rest ARGS)"
   (declare (indent 2))
   (declare (indent 2))
-  (let ((s (make-symbol "*section*")))
-    `(let* ((,s (magit-new-section ,title ,type))
-            (magit-top-section ,s))
-       (setf (magit-section-beginning ,s) (point))
-       ,@body
-       (setf (magit-section-end ,s) (point))
-       (setf (magit-section-children ,s)
-             (nreverse (magit-section-children ,s)))
-       ,s)))
+  `(magit-with-section (section ,(car arglist)
+                                ',(car arglist)
+                                ,(cadr arglist) t)
+     (apply #'process-file ,program nil (list t nil) nil
+            (magit-flatten-onelevel (list ,@args)))
+     (unless (eq (char-before) ?\n)
+       (insert "\n"))
+     (save-restriction
+       (narrow-to-region (magit-section-content-beginning section) (point))
+       (goto-char (point-min))
+       (funcall ,washer)
+       (goto-char (point-max)))
+     (let ((parent   (magit-section-parent section))
+           (head-beg (magit-section-beginning section))
+           (body-beg (magit-section-content-beginning section)))
+       (if (= (point) body-beg)
+           (if (not parent)
+               (insert "(empty)\n")
+             (delete-region head-beg body-beg)
+             (setq section nil))
+         (insert "\n")))))
+
+(defmacro magit-git-insert-section (arglist washer &rest args)
+  "\n\n(fn (TYPE &optional HEADING) WASHER &rest ARGS)"
+  (declare (indent 2))
+  `(magit-cmd-insert-section ,arglist
+       ,washer
+     magit-git-executable
+     magit-git-standard-options ,@args))
 
 
-(defun magit-set-section (title type start end)
-  "Create a new section of title TITLE and type TYPE with specified start and
-end positions."
-  (let ((section (magit-new-section title type)))
-    (setf (magit-section-beginning section) start)
-    (setf (magit-section-end section) end)
-    section))
-
-(defun magit-set-section-info (info &optional section)
-  (setf (magit-section-info (or section magit-top-section)) info))
-
-(defun magit-set-section-needs-refresh-on-show (flag &optional section)
-  (setf (magit-section-needs-refresh-on-show
-         (or section magit-top-section))
-        flag))
-
-(defmacro magit-create-buffer-sections (&rest body)
-  "Empty current buffer of text and Magit's sections, and then evaluate BODY."
-  (declare (indent 0))
-  `(let ((inhibit-read-only t))
-     (erase-buffer)
-     (let ((magit-old-top-section magit-top-section))
-       (setq magit-top-section nil)
-       ,@body
-       (when (null magit-top-section)
-         (magit-with-section 'top nil
-           (insert "(empty)\n")))
-       (magit-propertize-section magit-top-section)
-       (magit-section-set-hidden magit-top-section
-                                 (magit-section-hidden magit-top-section)))))
-
-(defun magit-propertize-section (section)
-  "Add text-property needed for SECTION."
-  (put-text-property (magit-section-beginning section)
-                     (magit-section-end section)
-                     'magit-section section)
-  (dolist (s (magit-section-children section))
-    (magit-propertize-section s)))
+(defmacro magit-insert-line-section (arglist line)
+  "\n\n(fn (TYPE &optional INFO) line)"
+  (declare (indent 1))
+  (let ((l (cl-gensym "line")))
+    `(let ((,l (concat ,line "\n")))
+       (when (string-match "^\\([^:]+\\):\\( \\)" ,l)
+         (setq ,l (replace-match
+                   (make-string (max 1 (- magit-status-line-align-to
+                                          (length (match-string 1 ,l))))
+                                ?\s)
+                   t t ,l 2)))
+       (magit-with-section (section ,(car arglist) ',(car arglist) ,l t)
+         (setf (magit-section-info section) ,(cadr arglist))))))
+
+;;;;; Section Searching
 
 (defun magit-find-section (path top)
   "Find the section at the path PATH in subsection of section TOP."
 
 (defun magit-find-section (path top)
   "Find the section at the path PATH in subsection of section TOP."
@@ -1422,21 +2627,21 @@ end positions."
       top
     (let ((secs (magit-section-children top)))
       (while (and secs (not (equal (car path)
       top
     (let ((secs (magit-section-children top)))
       (while (and secs (not (equal (car path)
-                                   (magit-section-title (car secs)))))
+                                   (magit-section-info (car secs)))))
         (setq secs (cdr secs)))
         (setq secs (cdr secs)))
-      (and (car secs)
-           (magit-find-section (cdr path) (car secs))))))
+      (when (car secs)
+        (magit-find-section (cdr path) (car secs))))))
 
 (defun magit-section-path (section)
   "Return the path of SECTION."
 
 (defun magit-section-path (section)
   "Return the path of SECTION."
-  (if (not (magit-section-parent section))
-      '()
-    (append (magit-section-path (magit-section-parent section))
-            (list (magit-section-title section)))))
+  (let ((parent (magit-section-parent section)))
+    (when parent
+      (append (magit-section-path parent)
+              (list (magit-section-info section))))))
 
 (defun magit-find-section-after (pos)
   "Find the first section that begins after POS."
 
 (defun magit-find-section-after (pos)
   "Find the first section that begins after POS."
-  (magit-find-section-after* pos (list magit-top-section)))
+  (magit-find-section-after* pos (list magit-root-section)))
 
 (defun magit-find-section-after* (pos secs)
   "Find the first section that begins after POS in the list SECS
 
 (defun magit-find-section-after* (pos secs)
   "Find the first section that begins after POS in the list SECS
@@ -1452,13 +2657,15 @@ end positions."
 (defun magit-find-section-before (pos)
   "Return the last section that begins before POS."
   (let ((section (magit-find-section-at pos)))
 (defun magit-find-section-before (pos)
   "Return the last section that begins before POS."
   (let ((section (magit-find-section-at pos)))
-    (do* ((current (or (magit-section-parent section)
-                       section)
-                   next)
-          (next (if (not (magit-section-hidden current))
-                    (magit-find-section-before* pos (magit-section-children current)))
-                (if (not (magit-section-hidden current))
-                    (magit-find-section-before* pos (magit-section-children current)))))
+    (cl-do* ((current (or (magit-section-parent section)
+                          section)
+                      next)
+             (next (unless (magit-section-hidden current)
+                     (magit-find-section-before*
+                      pos (magit-section-children current)))
+                   (unless (magit-section-hidden current)
+                     (magit-find-section-before*
+                      pos (magit-section-children current)))))
         ((null next) current))))
 
 (defun magit-find-section-before* (pos secs)
         ((null next) current))))
 
 (defun magit-find-section-before* (pos secs)
@@ -1477,62 +2684,9 @@ end positions."
 (defun magit-find-section-at (pos)
   "Return the Magit section at POS."
   (or (get-text-property pos 'magit-section)
 (defun magit-find-section-at (pos)
   "Return the Magit section at POS."
   (or (get-text-property pos 'magit-section)
-      magit-top-section))
-
-(defun magit-insert-section (section-title-and-type
-                             buffer-title washer cmd &rest args)
-  "Run CMD and put its result in a new section.
-
-SECTION-TITLE-AND-TYPE is either a string that is the title of the section
-or (TITLE . TYPE) where TITLE is the title of the section and TYPE is its type.
-
-If there is no type, or if type is nil, the section won't be highlighted.
-
-BUFFER-TITLE is the inserted title of the section
-
-WASHER is a function that will be run after CMD.
-The buffer will be narrowed to the inserted text.
-It should add sectioning as needed for Magit interaction.
-
-CMD is an external command that will be run with ARGS as arguments."
-  (let* ((body-beg nil)
-         (section-title (if (consp section-title-and-type)
-                            (car section-title-and-type)
-                          section-title-and-type))
-         (section-type (if (consp section-title-and-type)
-                           (cdr section-title-and-type)
-                         nil))
-         (section
-          (magit-with-section section-title section-type
-            (if buffer-title
-                (insert (propertize buffer-title 'face 'magit-section-title)
-                        "\n"))
-            (setq body-beg (point))
-            (magit-cmd-insert cmd args)
-            (if (not (eq (char-before) ?\n))
-                (insert "\n"))
-            (if washer
-                (save-restriction
-                  (narrow-to-region body-beg (point))
-                  (goto-char (point-min))
-                  (funcall washer)
-                  (goto-char (point-max)))))))
-    (if (= body-beg (point))
-        (magit-cancel-section section)
-      (insert "\n"))
-    section))
-
-(defun magit-git-section (section-title-and-type
-                          buffer-title washer &rest args)
-  "Run git and put its result in a new section.
-
-see `magit-insert-section' for meaning of the arguments"
-  (apply #'magit-insert-section
-         section-title-and-type
-         buffer-title
-         washer
-         magit-git-executable
-         (append magit-git-standard-options args)))
+      magit-root-section))
+
+;;;;; Section Jumping
 
 (defun magit-goto-next-section ()
   "Go to the next section."
 
 (defun magit-goto-next-section ()
   "Go to the next section."
@@ -1559,27 +2713,26 @@ see `magit-insert-section' for meaning of the arguments"
 (defun magit-goto-next-sibling-section ()
   "Go to the next sibling section."
   (interactive)
 (defun magit-goto-next-sibling-section ()
   "Go to the next sibling section."
   (interactive)
-  (let* ((initial (point))
-         (section (magit-current-section))
-         (end (- (magit-section-end section) 1))
-         (parent (magit-section-parent section))
-         (siblings (magit-section-children parent))
-         (next-sibling (magit-find-section-after* end siblings)))
-    (if next-sibling
-        (magit-goto-section next-sibling)
+  (let* ((section (magit-current-section))
+         (parent  (magit-section-parent section))
+         (next    (and parent (magit-find-section-after*
+                               (1- (magit-section-end section))
+                               (magit-section-children parent)))))
+    (if next
+        (magit-goto-section next)
       (magit-goto-next-section))))
 
 (defun magit-goto-previous-sibling-section ()
   "Go to the previous sibling section."
   (interactive)
   (let* ((section (magit-current-section))
       (magit-goto-next-section))))
 
 (defun magit-goto-previous-sibling-section ()
   "Go to the previous sibling section."
   (interactive)
   (let* ((section (magit-current-section))
-         (beginning (magit-section-beginning section))
-         (parent (magit-section-parent section))
-         (siblings (magit-section-children parent))
-         (previous-sibling (magit-find-section-before* beginning siblings)))
-    (if previous-sibling
-        (magit-goto-section previous-sibling)
-      (magit-goto-parent-section))))
+         (parent  (magit-section-parent section))
+         (prev    (and parent (magit-find-section-before*
+                               (magit-section-beginning section)
+                               (magit-section-children parent)))))
+    (if prev
+        (magit-goto-section prev)
+      (magit-goto-previous-section))))
 
 (defun magit-goto-section (section)
   (goto-char (magit-section-beginning section))
 
 (defun magit-goto-section (section)
   (goto-char (magit-section-beginning section))
@@ -1590,25 +2743,151 @@ see `magit-insert-section' for meaning of the arguments"
     (forward-line -1)
     (magit-goto-next-section))
    ((and (eq (magit-section-type section) 'commit)
     (forward-line -1)
     (magit-goto-next-section))
    ((and (eq (magit-section-type section) 'commit)
-         (derived-mode-p 'magit-log-mode))
-    (magit-show-commit section))))
+         (derived-mode-p 'magit-log-mode)
+         (or (eq (car magit-refresh-args) 'oneline)
+             (get-buffer-window magit-commit-buffer-name)))
+    (magit-show-commit (magit-section-info section) t))))
 
 (defun magit-goto-section-at-path (path)
   "Go to the section described by PATH."
 
 (defun magit-goto-section-at-path (path)
   "Go to the section described by PATH."
-  (let ((sec (magit-find-section path magit-top-section)))
+  (let ((sec (magit-find-section path magit-root-section)))
     (if sec
         (goto-char (magit-section-beginning sec))
       (message "No such section"))))
 
     (if sec
         (goto-char (magit-section-beginning sec))
       (message "No such section"))))
 
-(defun magit-for-all-sections (func &optional top)
-  "Run FUNC on TOP and recursively on all its children.
+(defmacro magit-define-section-jumper (sym title)
+  "Define an interactive function to go to section SYM.
+TITLE is the displayed title of the section."
+  (let ((fun (intern (format "magit-jump-to-%s" sym))))
+    `(progn
+       (defun ,fun (&optional expand) ,(format "\
+Jump to section '%s'.
+With a prefix argument also expand it." title)
+         (interactive "P")
+         (if (magit-goto-section-at-path '(,sym))
+             (when expand
+               (with-local-quit
+                 (if (eq magit-expand-staged-on-commit 'full)
+                     (magit-show-level 4 nil)
+                   (magit-expand-section)))
+               (recenter 0))
+           (message ,(format "Section '%s' wasn't found" title))))
+       (put ',fun 'definition-name ',sym))))
 
 
-Default value for TOP is `magit-top-section'"
-  (let ((section (or top magit-top-section)))
-    (when section
-      (funcall func section)
-      (dolist (c (magit-section-children section))
-        (magit-for-all-sections func c)))))
+(magit-define-section-jumper stashes   "Stashes")
+(magit-define-section-jumper untracked "Untracked files")
+(magit-define-section-jumper unstaged  "Unstaged changes")
+(magit-define-section-jumper staged    "Staged changes")
+(magit-define-section-jumper unpulled  "Unpulled commits")
+(magit-define-section-jumper unpushed  "Unpushed commits")
+(magit-define-section-jumper diffstats "Diffstats")
+
+;;;;; Section Hooks
+
+(defun magit-add-section-hook (hook function &optional at append local)
+  "Add to the value of section hook HOOK the function FUNCTION.
+
+Add FUNCTION at the beginning of the hook list unless optional
+APPEND is non-nil, in which case FUNCTION is added at the end.
+If FUNCTION already is a member then move it to the new location.
+
+If optional AT is non-nil and a member of the hook list, then add
+FUNCTION next to that instead.  Add before or after AT depending
+on APPEND.  If only FUNCTION is a member of the list, then leave
+it where ever it already is.
+
+If optional LOCAL is non-nil, then modify the hook's buffer-local
+value rather than its global value.  This makes the hook local by
+copying the default value.  That copy is then modified.
+
+HOOK should be a symbol.  If HOOK is void, it is first set to nil.
+HOOK's value must not be a single hook function.  FUNCTION should
+be a function that takes no arguments and inserts one or multiple
+sections at point, moving point forward.  FUNCTION may choose not
+to insert its section(s), when doing so would not make sense.  It
+should not be abused for other side-effects.  To remove FUNCTION
+again use `remove-hook'."
+  (or (boundp hook) (set hook nil))
+  (or (default-boundp hook) (set-default hook nil))
+  (let ((value (if local
+                   (if (local-variable-p hook)
+                       (symbol-value hook)
+                     (unless (local-variable-if-set-p hook)
+                       (make-local-variable hook))
+                     (copy-sequence (default-value hook)))
+                 (default-value hook))))
+    (if at
+        (when (setq at (member at value))
+          (setq value (delq function value))
+          (if append
+              (push function (cdr at))
+            (push (car at) (cdr at))
+            (setcar at function)))
+      (setq value (delq function value)))
+    (unless (member function value)
+      (setq value (if append
+                      (append value (list function))
+                    (cons function value))))
+    (if local
+        (set hook value)
+      (set-default hook value))))
+
+;;;;; Section Utilities
+
+(defun magit-map-sections (function section)
+  "Apply FUNCTION to SECTION and recursively its subsections."
+  (funcall function section)
+  (mapc (apply-partially 'magit-map-sections function)
+        (magit-section-children section)))
+
+(defun magit-wash-sequence (function)
+  "Repeatedly call FUNCTION until it returns nil or eob is reached.
+FUNCTION has to move point forward or return nil."
+  (while (and (not (eobp)) (funcall function))))
+
+(defun magit-section-parent-info (section)
+  (setq section (magit-section-parent section))
+  (when section (magit-section-info   section)))
+
+(defun magit-section-siblings (section &optional direction)
+  (let ((parent (magit-section-parent section)))
+    (when parent
+      (let ((siblings (magit-section-children parent)))
+        (cl-ecase direction
+          (prev (member section (reverse siblings)))
+          (next (member section siblings))
+          (nil  siblings))))))
+
+(defun magit-section-region-siblings (&optional key)
+  (let ((beg (magit-find-section-at (region-beginning)))
+        (end (magit-find-section-at (region-end))))
+    (if (eq beg end)
+        (list (if key (funcall key beg) beg))
+      (goto-char (region-end))
+      (when (bolp)
+        (setq end (magit-find-section-at (1- (point)))))
+      (while (> (length (magit-section-path beg))
+                (length (magit-section-path end)))
+        (setq beg (magit-section-parent beg)))
+      (while (> (length (magit-section-path end))
+                (length (magit-section-path beg)))
+        (setq end (magit-section-parent end)))
+      (let* ((parent   (magit-section-parent beg))
+             (siblings (magit-section-children parent)))
+        (if (eq parent (magit-section-parent end))
+            (mapcar (or key #'identity)
+                    (cl-intersection (memq beg siblings)
+                                     (memq end (reverse siblings))))
+          (user-error "Ambitious cross-section region"))))))
+
+(defun magit-diff-section-for-diffstat (section)
+  (let ((file (magit-section-info section)))
+    (cl-find-if (lambda (s)
+                  (and (eq (magit-section-type s) 'diff)
+                       (string-equal (magit-section-info s) file)))
+                (magit-section-children magit-root-section))))
+
+;;;;; Section Visibility
 
 (defun magit-section-set-hidden (section hidden)
   "Hide SECTION if HIDDEN is not nil, show it otherwise."
 
 (defun magit-section-set-hidden (section hidden)
   "Hide SECTION if HIDDEN is not nil, show it otherwise."
@@ -1622,11 +2901,11 @@ Default value for TOP is `magit-top-section'"
                  (forward-line)
                  (point)))
           (end (magit-section-end section)))
                  (forward-line)
                  (point)))
           (end (magit-section-end section)))
-      (if (< beg end)
-          (put-text-property beg end 'invisible hidden)))
-    (if (not hidden)
-        (dolist (c (magit-section-children section))
-          (magit-section-set-hidden c (magit-section-hidden c))))))
+      (when (< beg end)
+        (put-text-property beg end 'invisible hidden)))
+    (unless hidden
+      (dolist (c (magit-section-children section))
+        (magit-section-set-hidden c (magit-section-hidden c))))))
 
 (defun magit-section-any-hidden (section)
   "Return true if SECTION or any of its children is hidden."
 
 (defun magit-section-any-hidden (section)
   "Return true if SECTION or any of its children is hidden."
@@ -1662,8 +2941,9 @@ Default value for TOP is `magit-top-section'"
 (defun magit-section-hideshow (flag-or-func)
   "Show or hide current section depending on FLAG-OR-FUNC.
 
 (defun magit-section-hideshow (flag-or-func)
   "Show or hide current section depending on FLAG-OR-FUNC.
 
-If FLAG-OR-FUNC is a function, it will be ran on current section
-IF FLAG-OR-FUNC is a Boolean value, the section will be hidden if its true, shown otherwise"
+If FLAG-OR-FUNC is a function, it will be ran on current section.
+IF FLAG-OR-FUNC is a boolean, the section will be hidden if it is
+true, shown otherwise."
   (let ((section (magit-current-section)))
     (when (magit-section-parent section)
       (goto-char (magit-section-beginning section))
   (let ((section (magit-current-section)))
     (when (magit-section-parent section)
       (goto-char (magit-section-beginning section))
@@ -1694,7 +2974,7 @@ IF FLAG-OR-FUNC is a Boolean value, the section will be hidden if its true, show
 (defun magit-toggle-file-section ()
   "Like `magit-toggle-section' but toggle at file granularity."
   (interactive)
 (defun magit-toggle-file-section ()
   "Like `magit-toggle-section' but toggle at file granularity."
   (interactive)
-  (when (eq 'hunk (first (magit-section-context-type (magit-current-section))))
+  (when (eq (magit-section-type (magit-current-section)) 'hunk)
     (magit-goto-parent-section))
   (magit-toggle-section))
 
     (magit-goto-parent-section))
   (magit-toggle-section))
 
@@ -1726,15 +3006,16 @@ Expanded: everything is shown."
    (lambda (s)
      (cond ((magit-section-hidden s)
             (magit-section-collapse s))
    (lambda (s)
      (cond ((magit-section-hidden s)
             (magit-section-collapse s))
-           ((notany #'magit-section-hidden (magit-section-children s))
+           ((with-no-warnings
+              (cl-notany #'magit-section-hidden (magit-section-children s)))
             (magit-section-set-hidden s t))
            (t
             (magit-section-expand s))))))
 
             (magit-section-set-hidden s t))
            (t
             (magit-section-expand s))))))
 
-(defun magit-section-lineage (s)
-  "Return list of parent, grand-parents... for section S."
-  (when s
-    (cons s (magit-section-lineage (magit-section-parent s)))))
+(defun magit-section-lineage (section)
+  "Return list of parent, grand-parents... for SECTION."
+  (when section
+    (cons section (magit-section-lineage (magit-section-parent section)))))
 
 (defun magit-section-show-level (section level threshold path)
   (magit-section-set-hidden section (>= level threshold))
 
 (defun magit-section-show-level (section level threshold path)
   (magit-section-set-hidden section (>= level threshold))
@@ -1747,16 +3028,15 @@ Expanded: everything is shown."
 
 (defun magit-show-level (level all)
   "Show section whose level is less than LEVEL, hide the others.
 
 (defun magit-show-level (level all)
   "Show section whose level is less than LEVEL, hide the others.
-If ALL is non nil, do this in all sections,
-otherwise do it only on ancestors and descendants of current section."
-  (magit-with-refresh
-    (if all
-        (magit-section-show-level magit-top-section 0 level nil)
-      (let ((path (reverse (magit-section-lineage (magit-current-section)))))
-        (magit-section-show-level (car path) 0 level (cdr path))))))
+If ALL is non nil, do this in all sections, otherwise do it only
+on ancestors and descendants of current section."
+  (if all
+      (magit-section-show-level magit-root-section 0 level nil)
+    (let ((path (reverse (magit-section-lineage (magit-current-section)))))
+      (magit-section-show-level (car path) 0 level (cdr path)))))
 
 (defun magit-show-only-files ()
 
 (defun magit-show-only-files ()
-  "Show section that are files, but not there subsection.
+  "Show section that are files, but not their subsection.
 
 Do this in on ancestors and descendants of current section."
   (interactive)
 
 Do this in on ancestors and descendants of current section."
   (interactive)
@@ -1765,8 +3045,7 @@ Do this in on ancestors and descendants of current section."
     (call-interactively 'magit-show-level-1)))
 
 (defun magit-show-only-files-all ()
     (call-interactively 'magit-show-level-1)))
 
 (defun magit-show-only-files-all ()
-  "Show section that are files, but not there subsection.
-
+  "Show section that are files, but not their subsection.
 Do this for all sections"
   (interactive)
   (if (derived-mode-p 'magit-status-mode)
 Do this for all sections"
   (interactive)
   (if (derived-mode-p 'magit-status-mode)
@@ -1777,7 +3056,8 @@ Do this for all sections"
   "Define an interactive function to show function of level LEVEL.
 
 If ALL is non nil, this function will affect all section,
   "Define an interactive function to show function of level LEVEL.
 
 If ALL is non nil, this function will affect all section,
-otherwise it will affect only ancestors and descendants of current section."
+otherwise it will affect only ancestors and descendants of
+current section."
   (let ((fun (intern (format "magit-show-level-%s%s"
                              level (if all "-all" ""))))
         (doc (format "Show sections on level %s." level)))
   (let ((fun (intern (format "magit-show-level-%s%s"
                              level (if all "-all" ""))))
         (doc (format "Show sections on level %s." level)))
@@ -1788,539 +3068,867 @@ otherwise it will affect only ancestors and descendants of current section."
 
 (defmacro magit-define-level-shower (level)
   "Define two interactive function to show function of level LEVEL.
 
 (defmacro magit-define-level-shower (level)
   "Define two interactive function to show function of level LEVEL.
-one for all, one for current lineage."
+One for all, one for current lineage."
   `(progn
      (magit-define-level-shower-1 ,level nil)
      (magit-define-level-shower-1 ,level t)))
 
   `(progn
      (magit-define-level-shower-1 ,level nil)
      (magit-define-level-shower-1 ,level t)))
 
-(defmacro magit-define-section-jumper (sym title)
-  "Define an interactive function to go to section SYM.
-
-TITLE is the displayed title of the section."
-  (let ((fun (intern (format "magit-jump-to-%s" sym)))
-        (doc (format "Jump to section `%s'." title)))
-    `(progn
-       (defun ,fun ()
-         ,doc
-         (interactive)
-         (magit-goto-section-at-path '(,sym)))
-       (put ',fun 'definition-name ',sym))))
+(magit-define-level-shower 1)
+(magit-define-level-shower 2)
+(magit-define-level-shower 3)
+(magit-define-level-shower 4)
 
 
-(defmacro magit-define-inserter (sym arglist &rest body)
-  (declare (indent defun))
-  (let ((fun (intern (format "magit-insert-%s" sym)))
-        (before (intern (format "magit-before-insert-%s-hook" sym)))
-        (after (intern (format "magit-after-insert-%s-hook" sym)))
-        (doc (format "Insert items for `%s'." sym)))
-    `(progn
-       (defvar ,before nil)
-       (defvar ,after nil)
-       (defun ,fun ,arglist
-         ,doc
-         (run-hooks ',before)
-         ,@body
-         (run-hooks ',after))
-       (put ',before 'definition-name ',sym)
-       (put ',after 'definition-name ',sym)
-       (put ',fun 'definition-name ',sym))))
+;;;;; Section Highlighting
 
 
-(defvar magit-highlighted-section nil)
-
-(defun magit-refine-section (section)
-  "Apply temporary refinements to the display of SECTION.
-Refinements can be undone with `magit-unrefine-section'."
-  (let ((type (and section (magit-section-type section))))
-    (cond ((and (eq type 'hunk)
-                magit-diff-refine-hunk
-                (not (eq magit-diff-refine-hunk 'all)))
-           ;; Refine the current hunk to show fine details, using
-           ;; diff-mode machinery.
-           (save-excursion
-             (goto-char (magit-section-beginning magit-highlighted-section))
-             (diff-refine-hunk))))))
-
-(defun magit-unrefine-section (section)
-  "Remove refinements to the display of SECTION done by `magit-refine-section'."
-  (let ((type (and section (magit-section-type section))))
-    (cond ((and (eq type 'hunk)
-                magit-diff-refine-hunk
-                (not (eq magit-diff-refine-hunk 'all)))
-           ;; XXX this should be in some diff-mode function, like
-           ;; `diff-unrefine-hunk'
-           (remove-overlays (magit-section-beginning section)
-                            (magit-section-end section)
-                            'diff-mode 'fine)))))
-
-(defvar magit-highlight-overlay nil)
+(defvar-local magit-highlighted-section nil)
+(defvar-local magit-highlight-overlay nil)
 
 (defun magit-highlight-section ()
 
 (defun magit-highlight-section ()
-  "Highlight current section if it has a type."
-  (let ((section (magit-current-section)))
-    (when (not (eq section magit-highlighted-section))
-      (when magit-highlighted-section
-        ;; remove any refinement from previous hunk
-        (magit-unrefine-section magit-highlighted-section))
+  "Highlight current section.
+If its HIGHLIGHT slot is nil, then don't highlight it."
+  (let ((section (magit-current-section))
+        (refinep (lambda ()
+                   (and magit-highlighted-section
+                        (eq magit-diff-refine-hunk t)
+                        (eq (magit-section-type magit-highlighted-section)
+                            'hunk)))))
+    (unless (eq section magit-highlighted-section)
+      (when (funcall refinep)
+        (magit-diff-unrefine-hunk magit-highlighted-section))
       (setq magit-highlighted-section section)
       (setq magit-highlighted-section section)
-      (if (not magit-highlight-overlay)
-          (let ((ov (make-overlay 1 1)))
-            (overlay-put ov 'face 'magit-item-highlight)
-            (setq magit-highlight-overlay ov)))
-      (if (and section (magit-section-type section))
-          (progn
-            (magit-refine-section section)
-            (move-overlay magit-highlight-overlay
-                          (magit-section-beginning section)
-                          (magit-section-end section)
-                          (current-buffer)))
-        (delete-overlay magit-highlight-overlay)))))
+      (unless magit-highlight-overlay
+        (overlay-put (setq magit-highlight-overlay (make-overlay 1 1))
+                     'face magit-item-highlight-face))
+      (cond ((and section (magit-section-highlight section))
+             (when (funcall refinep)
+               (magit-diff-refine-hunk section))
+             (move-overlay magit-highlight-overlay
+                           (magit-section-beginning section)
+                           (magit-section-end section)
+                           (current-buffer)))
+            (t
+             (delete-overlay magit-highlight-overlay))))))
+
+;;;;; Section Actions
 
 (defun magit-section-context-type (section)
 
 (defun magit-section-context-type (section)
-  (if (null section)
-      '()
-    (let ((c (or (magit-section-type section)
-                 (if (symbolp (magit-section-title section))
-                     (magit-section-title section)))))
-      (if c
-          (cons c (magit-section-context-type
-                   (magit-section-parent section)))
-        '()))))
-
-(defun magit-prefix-p (prefix list)
-  "Returns non-nil if PREFIX is a prefix of LIST.  PREFIX and LIST should both be
-lists.
-
-If the car of PREFIX is the symbol '*, then return non-nil if the cdr of PREFIX
-is a sublist of LIST (as if '* matched zero or more arbitrary elements of LIST)"
-  ;;; Very schemish...
-  (or (null prefix)
-      (if (eq (car prefix) '*)
-          (or (magit-prefix-p (cdr prefix) list)
-              (and (not (null list))
-                   (magit-prefix-p prefix (cdr list))))
-        (and (not (null list))
-             (equal (car prefix) (car list))
-             (magit-prefix-p (cdr prefix) (cdr list))))))
-
-(defmacro magit-section-case (head &rest clauses)
-  "Make different action depending of current section.
-
-HEAD is (SECTION INFO &optional OPNAME),
-  SECTION will be bind to the current section,
-  INFO will be bind to the info's of the current section,
-  OPNAME is a string that will be used to describe current action,
-
-CLAUSES is a list of CLAUSE, each clause is (SECTION-TYPE &BODY)
-where SECTION-TYPE describe section where BODY will be run.
-
-This returns non-nil if some section matches. If the
-corresponding body return a non-nil value, it is returned,
-otherwise it returns t.
-
-If no section matches, this returns nil if no OPNAME was given
-and throws an error otherwise."
-  (declare (indent 1))
-  (let ((section (car head))
-        (info (cadr head))
-        (type (make-symbol "*type*"))
-        (context (make-symbol "*context*"))
-        (opname (caddr head)))
-    `(let* ((,section (magit-current-section))
-            (,info (and ,section (magit-section-info ,section)))
-            (,type (and ,section (magit-section-type ,section)))
-            (,context (magit-section-context-type ,section)))
-       (cond ,@(mapcar (lambda (clause)
-                         (if (eq (car clause) t)
-                             `(t (or (progn ,@(cdr clause))
-                                     t))
-                           (let ((prefix (reverse (car clause)))
-                                 (body (cdr clause)))
-                             `((magit-prefix-p ',prefix ,context)
-                               (or (progn ,@body)
-                                   t)))))
-                       clauses)
-             ,@(when opname
-                 `(((run-hook-with-args-until-success
-                     ',(intern (format "magit-%s-action-hook" opname))))
-                   ((not ,type)
-                    (error "Nothing to %s here" ,opname))
-                   (t
-                    (error "Can't %s a %s"
-                           ,opname
-                           (or (get ,type 'magit-description)
-                               ,type)))))))))
+  (cons (magit-section-type section)
+        (let ((parent (magit-section-parent section)))
+          (when parent
+            (magit-section-context-type parent)))))
+
+(defun magit-section-match-1 (l1 l2)
+  (or (null l1)
+      (if (eq (car l1) '*)
+          (or (magit-section-match-1 (cdr l1) l2)
+              (and l2
+                   (magit-section-match-1 l1 (cdr l2))))
+        (and l2
+             (equal (car l1) (car l2))
+             (magit-section-match-1 (cdr l1) (cdr l2))))))
+
+(defun magit-section-match (condition &optional section)
+  (unless section
+    (setq section (magit-current-section)))
+  (cond ((eq condition t) t)
+        ((not section)  nil)
+        ((listp condition)
+         (cl-find t condition :test
+                  (lambda (_ condition)
+                    (magit-section-match condition section))))
+        (t
+         (magit-section-match-1 (if (symbolp condition)
+                                    (list condition)
+                                  (append condition nil))
+                                (magit-section-context-type section)))))
 
 
-(defmacro magit-section-action (head &rest clauses)
+(defmacro magit-section-case (slots &rest clauses)
   (declare (indent 1))
   (declare (indent 1))
-  `(magit-with-refresh
-     (magit-section-case ,head ,@clauses)))
-
-(defmacro magit-add-action (head &rest clauses)
-  "Add additional actions to a pre-existing operator.
-The syntax is identical to `magit-section-case', except that
-OPNAME is mandatory and specifies the operation to which to add
-the actions."
-  (declare (indent 1))
-  (let ((section (car head))
-        (info (cadr head))
-        (type (caddr head)))
-    `(add-hook ',(intern (format "magit-%s-action-hook" type))
-               (lambda ()
-                 ,(macroexpand
-                   ;; Don't pass in the opname so we don't recursively
-                   ;; run the hook again, and so we don't throw an
-                   ;; error if no action matches.
-                   `(magit-section-case (,section ,info)
-                      ,@clauses))))))
-
-(defun magit-wash-sequence (func)
-  "Run FUNC until end of buffer is reached.
-
-FUNC should leave point at the end of the modified region"
-  (while (and (not (eobp))
-              (funcall func))))
-
-(defmacro magit-define-command (sym arglist &rest body)
-  "Macro to define a magit command.
-It will define the magit-SYM function having ARGLIST as argument.
-It will also define the magit-SYM-command-hook variable.
-
-The defined function will call the function in the hook in
-order until one return non nil. If they all return nil then body will be called.
-
-It is used to define hookable magit command: command defined by this
-function can be enriched by magit extension like magit-topgit and magit-svn"
-  (declare (indent defun)
-           (debug (&define name lambda-list
-                           [&optional stringp]        ; Match the doc string, if present.
-                           [&optional ("interactive" interactive)]
-                           def-body)))
-  (let ((fun (intern (format "magit-%s" sym)))
-        (hook (intern (format "magit-%s-command-hook" sym)))
-        (doc (format "Command for `%s'." sym))
-        (inter nil)
-        (instr body))
-    (when (stringp (car body))
-      (setq doc (car body)
-            instr (cdr body)))
-    (let ((form (car instr)))
-      (when (eq (car form) 'interactive)
-        (setq inter form
-              instr (cdr instr))))
-    `(progn
-       (defvar ,hook nil)
-       (defun ,fun ,arglist
-         ,doc
-         ,inter
-         (or (run-hook-with-args-until-success
-              ',hook ,@(remq '&optional (remq '&rest arglist)))
-             ,@instr))
-       (put ',fun 'definition-name ',sym)
-       (put ',hook 'definition-name ',sym))))
-
-;;; Running commands
-
-(defun magit-set-mode-line-process (str)
-  (let ((pr (if str (concat " " str) "")))
-    (save-excursion
-      (magit-for-all-buffers (lambda ()
-                               (setq mode-line-process pr))))))
-
-(defun magit-process-indicator-from-command (comps)
-  (if (magit-prefix-p (cons magit-git-executable magit-git-standard-options)
-                      comps)
-      (setq comps (nthcdr (+ (length magit-git-standard-options) 1) comps)))
-  (cond ((or (null (cdr comps))
-             (not (member (car comps) '("remote"))))
-         (car comps))
-        (t
-         (concat (car comps) " " (cadr comps)))))
+  `(let* ((it (magit-current-section))
+          ,@(mapcar
+             (lambda (slot)
+               `(,slot
+                 (and it (,(intern (format "magit-section-%s" slot)) it))))
+             slots))
+     (cond ,@(mapcar (lambda (clause)
+                       `((magit-section-match ',(car clause) it)
+                         ,@(cdr clause)))
+                     clauses))))
+
+(defconst magit-section-action-success
+  (make-symbol "magit-section-action-success"))
+
+(defmacro magit-section-action (opname slots &rest clauses)
+  (declare (indent 2) (debug (sexp &rest (sexp body))))
+  (let ((value (cl-gensym "value")))
+    `(let ((,value
+            (or (run-hook-wrapped
+                 ',(intern (format "magit-%s-hook" opname))
+                 (lambda (fn section)
+                   (when (magit-section-match
+                          (or (get fn 'magit-section-action-context)
+                              (error "%s undefined for %s"
+                                     'magit-section-action-context fn))
+                          section)
+                     (funcall fn (magit-section-info section))))
+                 (magit-current-section))
+                (magit-section-case ,slots
+                  ,@clauses
+                  (t (user-error
+                      (if (magit-current-section)
+                          ,(format "Cannot %s this section" opname)
+                        ,(format "Nothing to %s here" opname))))))))
+       (unless (eq ,value magit-section-action-success)
+         ,value))))
+
+;;;; Process Api
+;;;;; Process Commands
+
+(defun magit-process ()
+  "Display Magit process buffer."
+  (interactive)
+  (let ((buf (magit-process-buffer)))
+    (if (buffer-live-p buf)
+        (pop-to-buffer buf)
+      (user-error "Process buffer doesn't exist"))))
+
+(defun magit-process-kill ()
+  "Kill the process at point."
+  (interactive)
+  (magit-section-case (info)
+    (process (if (eq (process-status info) 'run)
+                 (when (yes-or-no-p "Kill this process? ")
+                   (kill-process info))
+               (user-error "Process isn't running")))))
+
+(defvar magit-git-command-history nil)
+
+;;;###autoload
+(defun magit-git-command (args directory)
+  "Execute a Git subcommand asynchronously, displaying the output.
+With a prefix argument run Git in the root of the current
+repository.  Non-interactively run Git in DIRECTORY with ARGS."
+  (interactive (magit-git-command-read-args))
+  (require 'eshell)
+  (magit-mode-display-buffer (magit-process-buffer nil t)
+                             'magit-process-mode 'pop-to-buffer)
+  (goto-char (point-max))
+  (let ((default-directory directory))
+    (magit-run-git-async
+     (with-temp-buffer
+       (insert args)
+       (mapcar 'eval (eshell-parse-arguments (point-min)
+                                             (point-max)))))))
+
+(defun magit-git-command-topdir (args directory)
+  "Execute a Git subcommand asynchronously, displaying the output.
+Run Git in the root of the current repository.
+\n(fn)" ; arguments are for internal use
+  (interactive (magit-git-command-read-args t))
+  (magit-git-command args directory))
+
+(defun magit-git-command-read-args (&optional root)
+  (let ((dir (if (or root current-prefix-arg)
+                 (or (magit-get-top-dir)
+                     (user-error "Not inside a Git repository"))
+               default-directory)))
+    (list (read-string (format "Git subcommand (in %s): "
+                               (abbreviate-file-name dir))
+                       nil 'magit-git-command-history)
+          dir)))
+
+;;;;; Process Mode
+
+(define-derived-mode magit-process-mode magit-mode "Magit Process"
+  "Mode for looking at git process output.")
 
 
-(defvar magit-process nil)
-(defvar magit-process-client-buffer nil)
 (defvar magit-process-buffer-name "*magit-process*"
 (defvar magit-process-buffer-name "*magit-process*"
-  "Buffer name for running git commands.")
-
-(defun magit-run* (cmd-and-args
-                   &optional logline noerase noerror nowait input)
-  (if (and magit-process
-           (get-buffer magit-process-buffer-name))
-      (error "Git is already running"))
-  (let ((cmd (car cmd-and-args))
-        (args (cdr cmd-and-args))
-        (dir default-directory)
-        (buf (get-buffer-create magit-process-buffer-name))
-        (successp nil))
-    (magit-set-mode-line-process
-     (magit-process-indicator-from-command cmd-and-args))
-    (setq magit-process-client-buffer (current-buffer))
-    (with-current-buffer buf
-      (view-mode 1)
-      (set (make-local-variable 'view-no-disable-on-exit) t)
-      (setq view-exit-action
-            (lambda (buffer)
-              (with-current-buffer buffer
-                (bury-buffer))))
-      (setq buffer-read-only t)
-      (let ((inhibit-read-only t))
-        (setq default-directory dir)
-        (if noerase
-            (goto-char (point-max))
-          (erase-buffer))
-        (insert "$ " (or logline
-                         (mapconcat 'identity cmd-and-args " "))
-                "\n")
-        (cond (nowait
-               (setq magit-process
-                     (let ((process-connection-type magit-process-connection-type))
-                       (apply 'magit-start-process cmd buf cmd args)))
-               (set-process-sentinel magit-process 'magit-process-sentinel)
-               (set-process-filter magit-process 'magit-process-filter)
-               (when input
-                 (with-current-buffer input
-                   (process-send-region magit-process
-                                        (point-min) (point-max)))
-                 (process-send-eof magit-process)
-                 (sit-for 0.1 t))
-               (cond ((= magit-process-popup-time 0)
-                      (pop-to-buffer (process-buffer magit-process)))
-                     ((> magit-process-popup-time 0)
-                      (run-with-timer
-                       magit-process-popup-time nil
-                       (function
-                        (lambda (buf)
-                          (with-current-buffer buf
-                            (when magit-process
-                              (display-buffer (process-buffer magit-process))
-                              (goto-char (point-max))))))
-                       (current-buffer))))
-               (setq successp t))
-              (input
-               (with-current-buffer input
-                 (setq default-directory dir)
-                 (setq magit-process
-                       ;; Don't use a pty, because it would set icrnl
-                       ;; which would modify the input (issue #20).
-                       (let ((process-connection-type nil))
-                         (apply 'magit-start-process cmd buf cmd args)))
-                 (set-process-filter magit-process 'magit-process-filter)
-                 (process-send-region magit-process
-                                      (point-min) (point-max))
-                 (process-send-eof magit-process)
-                 (while (equal (process-status magit-process) 'run)
-                   (sit-for 0.1 t))
-                 (setq successp
-                       (equal (process-exit-status magit-process) 0))
-                 (setq magit-process nil))
-               (magit-set-mode-line-process nil)
-               (magit-need-refresh magit-process-client-buffer))
-              (t
-               (setq successp
-                     (equal (apply 'process-file cmd nil buf nil args) 0))
-               (magit-set-mode-line-process nil)
-               (magit-need-refresh magit-process-client-buffer))))
-      (or successp
-          noerror
-          (error
-           "%s ... [Hit %s or see buffer %s for details]"
-           (or (with-current-buffer (get-buffer magit-process-buffer-name)
-                 (when (re-search-backward
-                        (concat "^error: \\(.*\\)" paragraph-separate) nil t)
-                   (match-string 1)))
-               "Git failed")
-           (with-current-buffer magit-process-client-buffer
-             (key-description (car (where-is-internal
-                                    'magit-display-process))))
-           magit-process-buffer-name))
-      successp)))
-
-(autoload 'dired-uncache "dired")
-(defun magit-process-sentinel (process event)
-  (let ((msg (format "%s %s." (process-name process) (substring event 0 -1)))
-        (successp (string-match "^finished" event))
-        (key (with-current-buffer magit-process-client-buffer
-               (key-description (car (where-is-internal
-                                      'magit-display-process))))))
-    (with-current-buffer (process-buffer process)
-      (let ((inhibit-read-only t))
-        (goto-char (point-max))
-        (insert msg "\n")
-        (message (if successp msg
-                   (format "%s Hit %s or see buffer %s for details."
-                           msg key (current-buffer)))))
-      (unless (memq (process-status process) '(run open))
-        (dired-uncache default-directory)))
-    (setq magit-process nil)
-    (magit-set-mode-line-process nil)
-    (magit-refresh-buffer magit-process-client-buffer)))
-
-(defun magit-password (proc string)
-  "Checks if git/ssh asks for a password and ask the user for it."
-  (let (ask)
-    (cond ((or (string-match "^Enter passphrase for key '\\\(.*\\\)': $" string)
-               (string-match "^\\\(.*\\\)'s password:" string)
-               (string-match "^Password for '\\\(.*\\\)':" string))
-           (setq ask (format "Password for '%s': " (match-string 1 string))))
-          ((string-match "^[pP]assword:" string)
-           (setq ask "Password:")))
-    (when ask
-      (process-send-string proc (concat (read-passwd ask nil) "\n")))))
-
-(defun magit-username (proc string)
-  "Checks if git asks for a username and ask the user for it."
-  (when (string-match "^Username for '\\\(.*\\\)':" string)
-    (process-send-string proc
-                         (concat
-                          (read-string (format "Username for '%s': "
-                                               (match-string 1 string))
-                                       nil nil (user-login-name))
-                          "\n"))))
+  "Name of buffer where output of processes is put.")
+
+(defun magit-process-buffer (&optional topdir create)
+  (or (magit-mode-get-buffer magit-process-buffer-name
+                             'magit-process-mode topdir)
+      (with-current-buffer (magit-mode-get-buffer-create
+                            magit-process-buffer-name
+                            'magit-process-mode topdir)
+        (magit-process-mode)
+        (let* ((inhibit-read-only t)
+               (s (magit-with-section (section processbuf nil nil t)
+                    (insert "\n"))))
+          (set-marker-insertion-type (magit-section-beginning s) nil)
+          (set-marker-insertion-type (magit-section-content-beginning s) nil)
+          (current-buffer)))))
+
+;;;;; Synchronous Processes
 
 
-(defun magit-process-filter (proc string)
-  (save-current-buffer
-    (set-buffer (process-buffer proc))
-    (let ((inhibit-read-only t))
-      (magit-username proc string)
-      (magit-password proc string)
-      (goto-char (process-mark proc))
-      ;; Find last ^M in string.  If one was found, ignore everything
-      ;; before it and delete the current line.
-      (let ((ret-pos (length string)))
-        (while (and (>= (setq ret-pos (1- ret-pos)) 0)
-                    (/= ?\r (aref string ret-pos))))
-        (cond ((>= ret-pos 0)
-               (goto-char (line-beginning-position))
-               (delete-region (point) (line-end-position))
-               (insert (substring string (+ ret-pos 1))))
-              (t
-               (insert string))))
-      (set-marker (process-mark proc) (point)))))
+(defun magit-git-exit-code (&rest args)
+  "Execute Git with ARGS, returning its exit code."
+  (apply #'process-file magit-git-executable nil nil nil
+         (append magit-git-standard-options
+                 (magit-flatten-onelevel args))))
+
+(defun magit-git-success (&rest args)
+  "Execute Git with ARGS, returning t if its exit code is 0."
+  (= (apply #'magit-git-exit-code args) 0))
+
+(defun magit-git-failure (&rest args)
+  "Execute Git with ARGS, returning t if its exit code is 1."
+  (= (apply #'magit-git-exit-code args) 1))
 
 
-(defun magit-run (cmd &rest args)
-  (magit-with-refresh
-    (magit-run* (cons cmd args))))
+(defun magit-git-string (&rest args)
+  "Execute Git with ARGS, returning the first line of its output.
+If there is no output return nil.  If the output begins with a
+newline return an empty string."
+  (with-temp-buffer
+    (apply #'process-file magit-git-executable nil (list t nil) nil
+           (append magit-git-standard-options
+                   (magit-flatten-onelevel args)))
+    (unless (= (point-min) (point-max))
+      (goto-char (point-min))
+      (buffer-substring-no-properties
+       (line-beginning-position)
+       (line-end-position)))))
+
+(defun magit-git-true (&rest args)
+  "Execute Git with ARGS, returning t if it prints \"true\".
+Return t if the first (and usually only) output line is the
+string \"true\", otherwise return nil."
+  (equal (apply #'magit-git-string args) "true"))
+
+(defun magit-git-false (&rest args)
+  "Execute Git with ARGS, returning t if it prints \"false\".
+Return t if the first (and usually only) output line is the
+string \"false\", otherwise return nil."
+  (equal (apply #'magit-git-string args) "false"))
+
+(defun magit-git-insert (&rest args)
+  "Execute Git with ARGS, inserting its output at point."
+  (apply #'process-file magit-git-executable nil (list t nil) nil
+         (append magit-git-standard-options
+                 (magit-flatten-onelevel args))))
+
+(defun magit-git-lines (&rest args)
+  "Execute Git with ARGS, returning its output as a list of lines.
+Empty lines anywhere in the output are omitted."
+  (with-temp-buffer
+    (apply #'process-file magit-git-executable nil (list t nil) nil
+           (append magit-git-standard-options
+                   (magit-flatten-onelevel args)))
+    (split-string (buffer-string) "\n" 'omit-nulls)))
 
 (defun magit-run-git (&rest args)
 
 (defun magit-run-git (&rest args)
-  (magit-with-refresh
-    (magit-run* (append (cons magit-git-executable
-                              magit-git-standard-options)
-                        args))))
+  "Call Git synchronously in a separate process, and refresh.
 
 
-(defun magit-run-git-with-input (input &rest args)
-  (magit-with-refresh
-    (magit-run* (append (cons magit-git-executable
-                              magit-git-standard-options)
-                        args)
-                nil nil nil nil input)))
+The arguments ARGS specify command line arguments.  The first
+level of ARGS is flattened, so each member of ARGS has to be a
+string or a list of strings.
 
 
-(defun magit-run-git-async (&rest args)
-  (message "Running %s %s" magit-git-executable (mapconcat 'identity args " "))
-  (magit-run* (append (cons magit-git-executable
-                            magit-git-standard-options)
-                      args)
-              nil nil nil t))
+Option `magit-git-executable' specifies which Git executable is
+used.  The arguments in option `magit-git-standard-options' are
+prepended to ARGS.
 
 
-(defun magit-run-async-with-input (input cmd &rest args)
-  (magit-run* (cons cmd args) nil nil nil t input))
+After Git returns, the current buffer (if it is a Magit buffer)
+as well as the current repository's status buffer are refreshed.
+Unmodified buffers visiting files that are tracked in the current
+repository are reverted if `magit-auto-revert-mode' is active.
 
 
-(defun magit-display-process ()
-  "Display output from most recent git command."
-  (interactive)
-  (unless (get-buffer magit-process-buffer-name)
-    (error "No Git commands have run"))
-  (display-buffer magit-process-buffer-name))
+Process output goes into a new section in a buffer specified by
+variable `magit-process-buffer-name'."
+  (apply #'magit-call-git (magit-process-quote-arguments args))
+  (magit-refresh))
 
 
-;;; Mode
+(defun magit-call-git (&rest args)
+  "Call Git synchronously in a separate process.
 
 
-;; We define individual functions (instead of using lambda etc) so
-;; that the online help can show something meaningful.
+The arguments ARGS specify command line arguments.  The first
+level of ARGS is flattened, so each member of ARGS has to be a
+string or a list of strings.
 
 
-(magit-define-section-jumper untracked "Untracked files")
-(magit-define-section-jumper unstaged  "Unstaged changes")
-(magit-define-section-jumper staged    "Staged changes")
-(magit-define-section-jumper unpushed  "Unpushed commits")
+Option `magit-git-executable' specifies which Git executable is
+used.  The arguments in option `magit-git-standard-options' are
+prepended to ARGS.
 
 
-(magit-define-level-shower 1)
-(magit-define-level-shower 2)
-(magit-define-level-shower 3)
-(magit-define-level-shower 4)
+Process output goes into a new section in a buffer specified by
+variable `magit-process-buffer-name'."
+  (apply #'magit-call-process magit-git-executable
+         (append magit-git-standard-options args)))
 
 
-(easy-menu-define magit-mode-menu magit-mode-map
-  "Magit menu"
-  '("Magit"
-    ["Refresh" magit-refresh t]
-    ["Refresh all" magit-refresh-all t]
-    "---"
-    ["Stage" magit-stage-item t]
-    ["Stage all" magit-stage-all t]
-    ["Unstage" magit-unstage-item t]
-    ["Unstage all" magit-unstage-all t]
-    ["Commit" magit-log-edit t]
-    ["Add log entry" magit-add-log t]
-    ["Tag" magit-tag t]
-    ["Annotated tag" magit-annotated-tag t]
-    "---"
-    ["Diff working tree" magit-diff-working-tree t]
-    ["Diff" magit-diff t]
-    ("Log"
-     ["Short Log" magit-log t]
-     ["Long Log" magit-log-long t]
-     ["Reflog" magit-reflog t]
-     ["Extended..." magit-key-mode-popup-logging t])
-    "---"
-    ["Cherry pick" magit-cherry-pick-item t]
-    ["Apply" magit-apply-item t]
-    ["Revert" magit-revert-item t]
-    "---"
-    ["Ignore" magit-ignore-item t]
-    ["Ignore locally" magit-ignore-item-locally t]
-    ["Discard" magit-discard-item t]
-    ["Reset head" magit-reset-head t]
-    ["Reset working tree" magit-reset-working-tree t]
-    ["Stash" magit-stash t]
-    ["Snapshot" magit-stash-snapshot t]
-    "---"
-    ["Branch..." magit-checkout t]
-    ["Merge" magit-manual-merge t]
-    ["Interactive resolve" magit-interactive-resolve-item t]
-    ["Rebase" magit-rebase-step t]
-    ("Rewrite"
-     ["Start" magit-rewrite-start t]
-     ["Stop" magit-rewrite-stop t]
-     ["Finish" magit-rewrite-finish t]
-     ["Abort" magit-rewrite-abort t]
-     ["Set used" magit-rewrite-set-used t]
-     ["Set unused" magit-rewrite-set-unused t])
-    "---"
-    ["Push" magit-push t]
-    ["Pull" magit-pull t]
-    ["Remote update" magit-remote-update t]
-    ("Submodule"
-     ["Submodule update" magit-submodule-update t]
-     ["Submodule update and init" magit-submodule-update-init t]
-     ["Submodule init" magit-submodule-init t]
-     ["Submodule sync" magit-submodule-sync t])
-    "---"
-    ("Extensions")
-    "---"
-    ["Display Git output" magit-display-process t]
-    ["Quit Magit" magit-quit-window t]))
+(defun magit-call-process (program &rest args)
+  "Call PROGRAM synchronously in a separate process.
+
+The arguments ARGS specify command line arguments.  The first
+level of ARGS is flattened, so each member of ARGS has to be a
+string or a list of strings.
+
+Process output goes into a new section in a buffer specified by
+variable `magit-process-buffer-name'."
+  (setq args (magit-flatten-onelevel args))
+  (cl-destructuring-bind (process-buf . section)
+      (magit-process-setup program args)
+    (magit-process-finish
+     (let ((inhibit-read-only t))
+       (apply #'process-file program nil process-buf nil args))
+     process-buf (current-buffer) default-directory section)))
+
+(defun magit-run-git-with-input (input &rest args)
+  "Call Git in a separate process.
+
+The first argument, INPUT, has to be a buffer or the name of an
+existing buffer.  The content of that buffer is used as the
+process' standard input.
+
+The remaining arguments, ARGS, specify command line arguments.
+The first level of ARGS is flattened, so each member of ARGS has
+to be a string or a list of strings.
+
+Option `magit-git-executable' specifies which Git executable is
+used.  The arguments in option `magit-git-standard-options' are
+prepended to ARGS.
+
+After Git returns, the current buffer (if it is a Magit buffer)
+as well as the current repository's status buffer are refreshed.
+Unmodified buffers visiting files that are tracked in the current
+repository are reverted if `magit-auto-revert-mode' is active.
+
+This function actually starts a asynchronous process, but it then
+waits for that process to return."
+  (apply #'magit-start-git input args)
+  (magit-process-wait)
+  (magit-refresh))
+
+(defun magit-run-git-with-logfile (file &rest args)
+  "Call Git in a separate process and log its output.
+And log the output to FILE.  This function might have a
+short halflive.  See `magit-run-git' for more information."
+  (apply #'magit-start-git nil args)
+  (process-put magit-this-process 'logfile file)
+  (set-process-filter magit-this-process 'magit-process-logfile-filter)
+  (magit-process-wait)
+  (magit-refresh))
+
+;;;;; Asynchronous Processes
+
+(defvar magit-this-process nil)
+
+(defun magit-run-git-async (&rest args)
+  "Start Git, prepare for refresh, and return the process object.
+
+If optional argument INPUT is non-nil, it has to be a buffer or
+the name of an existing buffer.  The content of that buffer is
+used as the process' standard input.
+
+The remaining arguments, ARGS, specify command line arguments.
+The first level of ARGS is flattened, so each member of ARGS has
+to be a string or a list of strings.
+
+Display the command line arguments in the echo area.
+
+After Git returns some buffers are refreshed: the buffer that was
+current when `magit-start-process' was called (if it is a Magit
+buffer and still alive), as well as the respective Magit status
+buffer.  Unmodified buffers visiting files that are tracked in
+the current repository are reverted if `magit-auto-revert-mode'
+is active.
+
+See `magit-start-process' for more information."
+  (message "Running %s %s" magit-git-executable
+           (mapconcat 'identity (magit-flatten-onelevel args) " "))
+  (apply #'magit-start-git nil args))
+
+(defun magit-start-git (&optional input &rest args)
+  "Start Git, prepare for refresh, and return the process object.
+
+If optional argument INPUT is non-nil, it has to be a buffer or
+the name of an existing buffer.  The buffer content becomes the
+processes standard input.
+
+The remaining arguments, ARGS, specify command line arguments.
+The first level of ARGS is flattened, so each member of ARGS has
+to be a string or a list of strings.
+
+After Git returns some buffers are refreshed: the buffer that was
+current when `magit-start-process' was called (if it is a Magit
+buffer and still alive), as well as the respective Magit status
+buffer.  Unmodified buffers visiting files that are tracked in
+the current repository are reverted if `magit-auto-revert-mode'
+is active.
+
+See `magit-start-process' for more information."
+  (apply #'magit-start-process magit-git-executable input
+         (append magit-git-standard-options
+                 (magit-process-quote-arguments args))))
+
+(defun magit-start-process (program &optional input &rest args)
+  "Start PROGRAM, prepare for refresh, and return the process object.
+
+If optional argument INPUT is non-nil, it has to be a buffer or
+the name of an existing buffer.  The buffer content becomes the
+processes standard input.
+
+The remaining arguments, ARGS, specify command line arguments.
+The first level of ARGS is flattened, so each member of ARGS has
+to be a string or a list of strings.
+
+The process is started using `start-file-process' and then setup
+to use the sentinel `magit-process-sentinel' and the filter
+`magit-process-filter'.  Information required by these functions
+is stored in the process object.  When this function returns the
+process has not started to run yet so it is possible to override
+the sentinel and filter.
+
+After the process returns, `magit-process-sentinel' refreshes the
+buffer that was current when `magit-start-process' was called (if
+it is a Magit buffer and still alive), as well as the respective
+Magit status buffer.  Unmodified buffers visiting files that are
+tracked in the current repository are reverted if
+`magit-auto-revert-mode' is active."
+  (setq args (magit-flatten-onelevel args))
+  (cl-destructuring-bind (process-buf . section)
+      (magit-process-setup program args)
+    (let* ((process-connection-type
+            ;; Don't use a pty, because it would set icrnl
+            ;; which would modify the input (issue #20).
+            (and (not input) magit-process-connection-type))
+           (process (apply 'start-file-process
+                           (file-name-nondirectory program)
+                           process-buf program args)))
+      (set-process-sentinel process #'magit-process-sentinel)
+      (set-process-filter   process #'magit-process-filter)
+      (set-process-buffer   process process-buf)
+      (process-put process 'section section)
+      (process-put process 'command-buf (current-buffer))
+      (process-put process 'default-dir default-directory)
+      (setf (magit-section-process section) process)
+      (with-current-buffer process-buf
+        (set-marker (process-mark process) (point)))
+      (when input
+        (with-current-buffer input
+          (process-send-region process (point-min) (point-max))
+          (process-send-eof    process)))
+      (setq magit-this-process process)
+      (setf (magit-section-info section) process)
+      (magit-process-display-buffer process)
+      process)))
+
+;;;;; Process Internals
+
+(defun magit-process-setup (program args)
+  (magit-process-set-mode-line program args)
+  (let ((buf (magit-process-buffer)))
+    (if  buf
+        (magit-process-truncate-log buf)
+      (setq buf (magit-process-buffer nil t)))
+    (with-current-buffer buf
+      (goto-char (1- (point-max)))
+      (let* ((inhibit-read-only t)
+             (magit-with-section--parent magit-root-section)
+             ;; Kids, don't do this ^^^^ at home.
+             (s (magit-with-section
+                    (section process nil
+                             (mapconcat 'identity (cons program args) " "))
+                  (insert "\n"))))
+        (set-marker-insertion-type (magit-section-content-beginning s) nil)
+        (insert "\n")
+        (backward-char 2)
+        (cons (current-buffer) s)))))
+
+(defun magit-process-truncate-log (buffer)
+  (with-current-buffer buffer
+    (let* ((head nil)
+           (tail (magit-section-children magit-root-section))
+           (count (length tail)))
+      (when (> (1+ count) magit-process-log-max)
+        (while (and (cdr tail)
+                    (> count (/ magit-process-log-max 2)))
+          (let* ((inhibit-read-only t)
+                 (section (car tail))
+                 (process (magit-section-process section)))
+            (cond ((not process))
+                  ((memq (process-status process) '(exit signal))
+                   (delete-region (magit-section-beginning section)
+                                  (1+ (magit-section-end section)))
+                   (cl-decf count))
+                  (t
+                   (push section head))))
+          (pop tail))
+        (setf (magit-section-children magit-root-section)
+              (nconc (reverse head) tail))))))
+
+(defun magit-process-sentinel (process event)
+  "Default sentinel used by `magit-start-process'."
+  (when (memq (process-status process) '(exit signal))
+    (setq event (substring event 0 -1))
+    (magit-process-unset-mode-line)
+    (when (string-match "^finished" event)
+      (message (concat (capitalize (process-name process)) " finished")))
+    (magit-process-finish process)
+    (when (eq process magit-this-process)
+      (setq magit-this-process nil))
+    (magit-refresh (and (buffer-live-p (process-get process 'command-buf))
+                        (process-get process 'command-buf)))))
+
+(defun magit-process-filter (proc string)
+  "Default filter used by `magit-start-process'."
+  (with-current-buffer (process-buffer proc)
+    (let ((inhibit-read-only t))
+      (magit-process-yes-or-no-prompt proc string)
+      (magit-process-username-prompt  proc string)
+      (magit-process-password-prompt  proc string)
+      (goto-char (process-mark proc))
+      (setq string (propertize string 'invisible
+                               (magit-section-hidden
+                                (process-get proc 'section))))
+      ;; Find last ^M in string.  If one was found, ignore everything
+      ;; before it and delete the current line.
+      (let ((ret-pos (length string)))
+        (while (and (>= (setq ret-pos (1- ret-pos)) 0)
+                    (/= ?\r (aref string ret-pos))))
+        (cond ((>= ret-pos 0)
+               (goto-char (line-beginning-position))
+               (delete-region (point) (line-end-position))
+               (insert-and-inherit (substring string (+ ret-pos 1))))
+              (t
+               (insert-and-inherit string))))
+      (set-marker (process-mark proc) (point)))))
+
+(defun magit-process-logfile-filter (process string)
+  "Special filter used by `magit-run-git-with-logfile'."
+  (magit-process-filter process string)
+  (let ((file (process-get process 'logfile)))
+    (with-temp-file file
+      (when (file-exists-p file)
+        (insert-file-contents file)
+        (goto-char (point-max)))
+      (insert string)
+      (write-region (point-min) (point-max) file))))
+
+(defun magit-process-yes-or-no-prompt (proc string)
+  "Forward yes-or-no prompts to the user."
+  (let ((beg (string-match magit-process-yes-or-no-prompt-regexp string))
+        (max-mini-window-height 30))
+    (when beg
+      (process-send-string
+       proc
+       (downcase
+        (concat (match-string (if (yes-or-no-p (substring string 0 beg)) 1 2)
+                              string)
+                "\n"))))))
+
+(defun magit-process-password-prompt (proc string)
+  "Forward password prompts to the user."
+  (let ((prompt (magit-process-match-prompt
+                 magit-process-password-prompt-regexps string)))
+    (when prompt
+      (process-send-string proc (concat (read-passwd prompt) "\n")))))
+
+(defun magit-process-username-prompt (proc string)
+  "Forward username prompts to the user."
+  (let ((prompt (magit-process-match-prompt
+                 magit-process-username-prompt-regexps string)))
+    (when prompt
+      (process-send-string proc
+                           (concat (read-string prompt nil nil
+                                                (user-login-name))
+                                   "\n")))))
+
+(defun magit-process-match-prompt (prompts string)
+  (when (cl-find-if (lambda (regex)
+                      (string-match regex string))
+                    prompts)
+    (let ((prompt (match-string 0 string)))
+      (cond ((string-match ": $" prompt) prompt)
+            ((string-match ":$"  prompt) (concat prompt " "))
+            (t                           (concat prompt ": "))))))
+
+(defun magit-process-wait ()
+  (while (and magit-this-process
+              (eq (process-status magit-this-process) 'run))
+    (sit-for 0.1 t)))
+
+(defun magit-process-set-mode-line (program args)
+  (when (equal program magit-git-executable)
+    (setq args (nthcdr (1+ (length magit-git-standard-options)) args)))
+  (magit-map-magit-buffers
+   (apply-partially (lambda (s)
+                      (setq mode-line-process s))
+                    (concat " " program
+                            (and args (concat " " (car args)))))))
+
+(defun magit-process-unset-mode-line ()
+  (magit-map-magit-buffers (lambda () (setq mode-line-process nil))))
+
+(defvar magit-process-error-message-re
+  (concat "^\\(?:error\\|fatal\\|git\\): \\(.*\\)" paragraph-separate))
+
+(defun magit-process-finish (arg &optional process-buf command-buf
+                                 default-dir section)
+  (unless (integerp arg)
+    (setq process-buf (process-buffer arg)
+          command-buf (process-get arg 'command-buf)
+          default-dir (process-get arg 'default-dir)
+          section     (process-get arg 'section)
+          arg         (process-exit-status arg)))
+  (when (featurep 'dired)
+    (dired-uncache default-dir))
+  (when (buffer-live-p process-buf)
+    (with-current-buffer process-buf
+      (let ((inhibit-read-only t)
+            (mark (magit-section-beginning section)))
+        (set-marker-insertion-type mark nil)
+        (goto-char mark)
+        (insert (propertize (format "%3s " arg)
+                            'magit-section section
+                            'face (if (= arg 0)
+                                      'magit-process-ok
+                                    'magit-process-ng))))))
+  (unless (= arg 0)
+    (message ; `error' would prevent refresh
+     "%s ... [%s buffer %s for details]"
+     (or (and (buffer-live-p process-buf)
+              (with-current-buffer process-buf
+                (save-excursion
+                  (goto-char (magit-section-end section))
+                  (when (re-search-backward
+                         magit-process-error-message-re nil
+                         (magit-section-content-beginning section))
+                    (match-string 1)))))
+         "Git failed")
+     (let ((key (and (buffer-live-p command-buf)
+                     (with-current-buffer command-buf
+                       (car (where-is-internal
+                             'magit-process-display-buffer))))))
+       (if key (format "Hit %s to see" (key-description key)) "See"))
+     (buffer-name process-buf)))
+  arg)
+
+(defun magit-process-display-buffer (process)
+  (when (process-live-p process)
+    (let ((buf (process-buffer process)))
+      (cond ((not (buffer-live-p buf)))
+            ((= magit-process-popup-time 0)
+             (pop-to-buffer buf))
+            ((> magit-process-popup-time 0)
+             (run-with-timer magit-process-popup-time nil
+                             (lambda (p)
+                               (when (eq (process-status p) 'run)
+                                 (let ((buf (process-buffer p)))
+                                   (when (buffer-live-p buf)
+                                     (pop-to-buffer buf)))))
+                             process))))))
+
+(defun magit-process-quote-arguments (args)
+  "Quote each argument in list ARGS as an argument to Git.
+Except when `magit-process-quote-curly-braces' is non-nil ARGS is
+returned unchanged.  This is required to works around strangeness
+of the Windows \"Powershell\"."
+  (if magit-process-quote-curly-braces
+      (mapcar (apply-partially 'replace-regexp-in-string
+                               "{\\([0-9]+\\)}" "\\\\{\\1\\\\}")
+              (magit-flatten-onelevel args))
+    args))
+
+;;;; Mode Api
+;;;;; Mode Foundation
+
+(define-derived-mode magit-mode special-mode "Magit"
+  "Parent major mode from which Magit major modes inherit.
 
 
-(defvar magit-mode-hook nil "Hook run by `magit-mode'.")
+Please see the manual for a complete description of Magit.
 
 
-(put 'magit-mode 'mode-class 'special)
+\\{magit-mode-map}"
+  (buffer-disable-undo)
+  (setq truncate-lines t)
+  (add-hook 'pre-command-hook  #'magit-remember-point nil t)
+  (add-hook 'post-command-hook #'magit-correct-point-after-command t t)
+  (add-hook 'post-command-hook #'magit-highlight-section t t)
+  ;; Emacs' normal method of showing trailing whitespace gives weird
+  ;; results when `magit-whitespace-warning-face' is different from
+  ;; `trailing-whitespace'.
+  (when (and magit-highlight-whitespace
+             magit-highlight-trailing-whitespace)
+    (setq show-trailing-whitespace nil)))
 
 
-(defvar magit-refresh-function nil)
-(make-variable-buffer-local 'magit-refresh-function)
+(defvar-local magit-refresh-function nil)
 (put 'magit-refresh-function 'permanent-local t)
 
 (put 'magit-refresh-function 'permanent-local t)
 
-(defvar magit-refresh-args nil)
-(make-variable-buffer-local 'magit-refresh-args)
+(defvar-local magit-refresh-args nil)
 (put 'magit-refresh-args 'permanent-local t)
 
 (put 'magit-refresh-args 'permanent-local t)
 
-(defvar last-point)
+(defmacro magit-mode-setup
+  (buffer switch-func mode refresh-func &rest refresh-args)
+  "Display and select BUFFER, turn on MODE, and refresh a first time.
+Display BUFFER using `magit-mode-display-buffer', then turn on
+MODE in BUFFER, set the local value of `magit-refresh-function'
+to REFRESH-FUNC and that of `magit-refresh-args' to REFRESH-ARGS
+and finally \"refresh\" a first time.  All arguments are
+evaluated before switching to BUFFER."
+  (let ((mode-symb (cl-gensym "mode-symb"))
+        (toplevel  (cl-gensym "toplevel"))
+        (init-args (cl-gensym "init-args"))
+        (buf-symb  (cl-gensym "buf-symb")))
+    `(let* ((,mode-symb ,mode)
+            (,toplevel  (magit-get-top-dir))
+            (,init-args (list ,mode-symb ,refresh-func ,@refresh-args))
+            (,buf-symb  (magit-mode-display-buffer
+                         ,buffer ,mode-symb ,switch-func)))
+       (if ,toplevel
+           (with-current-buffer ,buf-symb
+             (apply #'magit-mode-init ,toplevel ,init-args))
+         (user-error "Not inside a Git repository")))))
+
+(defun magit-mode-init (dir mode refresh-func &rest refresh-args)
+  "Turn on MODE and refresh in the current buffer.
+Turn on MODE, set the local value of `magit-refresh-function' to
+REFRESH-FUNC and that of `magit-refresh-args' to REFRESH-ARGS and
+finally \"refresh\" a first time.
+
+Also see `magit-mode-setup', a more convenient variant."
+  (cl-case mode
+    (magit-commit-mode
+     (magit-setup-xref (cons #'magit-show-commit refresh-args))
+     (goto-char (point-min)))
+    (magit-diff-mode
+     (magit-setup-xref (cons #'magit-diff refresh-args))
+     (goto-char (point-min))))
+  (setq default-directory dir
+        magit-refresh-function refresh-func
+        magit-refresh-args refresh-args)
+  (funcall mode)
+  (magit-mode-refresh-buffer))
+
+(defvar-local magit-previous-window-configuration nil)
+(put 'magit-previous-window-configuration 'permanent-local t)
+
+(defun magit-mode-display-buffer (buffer mode &optional switch-function)
+  "Display BUFFER in some window and select it.
+BUFFER may be a buffer or a string, the name of a buffer.  Return
+the buffer.
+
+Unless BUFFER is already displayed in the selected frame store the
+previous window configuration as a buffer local value, so that it
+can later be restored by `magit-mode-quit-window'.
+
+Then display and select BUFFER using SWITCH-FUNCTION.  If that is
+nil either use `pop-to-buffer' if the current buffer's major mode
+derives from Magit mode; or else use `switch-to-buffer'.
+
+This is only intended for buffers whose major modes derive from
+Magit mode."
+  (cond ((stringp buffer)
+         (setq buffer (magit-mode-get-buffer-create buffer mode)))
+        ((not (bufferp buffer))
+         (signal 'wrong-type-argument (list 'bufferp nil))))
+  (unless (get-buffer-window buffer (selected-frame))
+    (with-current-buffer (get-buffer-create buffer)
+      (setq magit-previous-window-configuration
+            (current-window-configuration))))
+  (funcall (or switch-function
+               (if (derived-mode-p 'magit-mode)
+                   'switch-to-buffer
+                 'pop-to-buffer))
+           buffer)
+  buffer)
+
+(defun magit-mode-get-buffer (format mode &optional topdir create)
+  (if (not (string-match-p "%[Tt]" format))
+      (funcall (if create #'get-buffer-create #'get-buffer) format)
+    (unless topdir
+      (setq topdir (magit-get-top-dir)))
+    (let ((name (format-spec
+                 format `((?T . ,topdir)
+                          (?t . ,(file-name-nondirectory
+                                  (directory-file-name topdir)))))))
+      (or (cl-find-if
+           (lambda (buf)
+             (with-current-buffer buf
+               (and (or (not mode) (eq major-mode mode))
+                    (equal (expand-file-name default-directory) topdir)
+                    (string-match-p (format "^%s\\(?:<[0-9]+>\\)?$"
+                                            (regexp-quote name))
+                                    (buffer-name)))))
+           (buffer-list))
+          (and create (generate-new-buffer name))))))
+
+(defun magit-mode-get-buffer-create (format mode &optional topdir)
+  (magit-mode-get-buffer format mode topdir t))
+
+(cl-defun magit-mode-refresh-buffer (&optional (buffer (current-buffer)))
+  (with-current-buffer buffer
+    (when magit-refresh-function
+      (let* ((old-line (line-number-at-pos))
+             (old-point (point))
+             (old-window (selected-window))
+             (old-window-start (window-start))
+             (old-section (magit-current-section))
+             (old-path (and old-section
+                            (magit-section-path (magit-current-section)))))
+        (beginning-of-line)
+        (let ((inhibit-read-only t)
+              (section-line (and old-section
+                                 (count-lines
+                                  (magit-section-beginning old-section)
+                                  (point))))
+              (line-char (- old-point (point))))
+          (erase-buffer)
+          (apply magit-refresh-function
+                 magit-refresh-args)
+          (let ((s (and old-path (magit-find-section old-path magit-root-section))))
+            (cond (s
+                   (goto-char (magit-section-beginning s))
+                   (forward-line section-line)
+                   (forward-char line-char))
+                  (t
+                   (save-restriction
+                     (widen)
+                     (goto-char (point-min))
+                     (forward-line (1- old-line)))))))
+        (when (fboundp 'unrecord-window-buffer)
+          (unrecord-window-buffer old-window buffer))
+        (dolist (w (get-buffer-window-list buffer nil t))
+          (set-window-point w (point))
+          (set-window-start w old-window-start t))
+        (magit-highlight-section)
+        (magit-refresh-marked-commits-in-buffer)))))
+
+(defun magit-mode-quit-window (&optional kill-buffer)
+  "Bury the current buffer and delete its window.
+With a prefix argument, kill the buffer instead.
+
+If `magit-restore-window-configuration' is non-nil and the last
+configuration stored by `magit-mode-display-buffer' originates
+from the selected frame then restore it after burrying/killing
+the buffer.  Finally reset the window configuration to nil."
+  (interactive "P")
+  (let ((winconf magit-previous-window-configuration)
+        (buffer (current-buffer))
+        (frame (selected-frame)))
+    (quit-window kill-buffer (selected-window))
+    (when winconf
+      (when (and magit-restore-window-configuration
+                 (equal frame (window-configuration-frame winconf)))
+        (set-window-configuration winconf)
+        (when (buffer-live-p buffer)
+          (with-current-buffer buffer
+            (setq magit-previous-window-configuration nil)))))
+    (run-hook-with-args 'magit-mode-quit-window-hook buffer)))
+
+;;;;; Mode Utilities
+
+(defun magit-map-magit-buffers (func &optional dir)
+  (dolist (buf (buffer-list))
+    (with-current-buffer buf
+      (when (and (derived-mode-p 'magit-mode)
+                 (or (null dir)
+                     (equal default-directory dir)))
+        (funcall func)))))
+
+;;;;; (section kludges)
+
+(defvar-local magit-last-point nil)
+(put 'magit-last-point 'permanent-local t)
 
 (defun magit-remember-point ()
 
 (defun magit-remember-point ()
-  (setq last-point (point)))
+  (setq magit-last-point (point)))
 
 (defun magit-invisible-region-end (pos)
   (while (and (not (= pos (point-max))) (invisible-p pos))
 
 (defun magit-invisible-region-end (pos)
   (while (and (not (= pos (point-max))) (invisible-p pos))
@@ -2343,205 +3951,120 @@ When point has to be moved out of an invisible region, it can be
 moved to its end or its beginning.  We usually move it to its
 end, except when that would move point back to where it was
 before the last command."
 moved to its end or its beginning.  We usually move it to its
 end, except when that would move point back to where it was
 before the last command."
-  (if (invisible-p (point))
-      (let ((end (magit-invisible-region-end (point))))
-        (goto-char (if (= end last-point)
-                       (magit-invisible-region-start (point))
-                     end))))
+  (when (invisible-p (point))
+    (let ((end (magit-invisible-region-end (point))))
+      (goto-char (if (= end magit-last-point)
+                     (magit-invisible-region-start (point))
+                   end))))
   (setq disable-point-adjustment t))
 
   (setq disable-point-adjustment t))
 
-(defun magit-post-command-hook ()
-  (magit-correct-point-after-command)
-  (magit-highlight-section))
-
-(defun magit-mode ()
-  "Review the status of a git repository and act on it.
-
-Please see the manual for a complete description of Magit.
-
-\\{magit-mode-map}"
-  (kill-all-local-variables)
-  (buffer-disable-undo)
-  (setq buffer-read-only t
-        truncate-lines t
-        major-mode 'magit-mode
-        mode-name "Magit"
-        mode-line-process "")
-  (add-hook 'pre-command-hook #'magit-remember-point nil t)
-  (add-hook 'post-command-hook #'magit-post-command-hook t t)
-  (use-local-map magit-mode-map)
-  (setq magit-current-indentation (magit-indentation-for default-directory))
-  ;; Emacs' normal method of showing trailing whitespace gives weird
-  ;; results when `magit-whitespace-warning-face' is different from
-  ;; `trailing-whitespace'.
-  (if (and magit-highlight-whitespace magit-highlight-trailing-whitespace)
-      (setq show-trailing-whitespace nil))
-  (run-mode-hooks 'magit-mode-hook))
-
-(defun magit-mode-init (dir submode refresh-func &rest refresh-args)
-  (setq default-directory dir
-        magit-refresh-function refresh-func
-        magit-refresh-args refresh-args)
-  (funcall submode)
-  (magit-refresh-buffer))
-
-(defun magit-indentation-for (dir)
-  (let (result)
-    (dolist (pair magit-highlight-indentation)
-      (if (string-match-p (car pair) dir)
-          (setq result (cdr pair))))
-    result))
-
-(defun magit-find-buffer (submode &optional dir)
-  (let ((topdir (magit-get-top-dir (or dir default-directory))))
-    (dolist (buf (buffer-list))
-      (if (with-current-buffer buf
-            (and (eq major-mode submode)
-                 default-directory
-                 (equal (expand-file-name default-directory) topdir)))
-          (return buf)))))
+;;;;; Buffer History
 
 
-(defun magit-find-status-buffer (&optional dir)
-  (magit-find-buffer 'magit-status-mode dir))
+(defun magit-go-backward ()
+  "Move backward in current buffer's history."
+  (interactive)
+  (if help-xref-stack
+      (help-xref-go-back (current-buffer))
+    (user-error "No previous entry in buffer's history")))
 
 
-(defun magit-for-all-buffers (func &optional dir)
-  (dolist (buf (buffer-list))
-    (with-current-buffer buf
-      (if (and (derived-mode-p 'magit-mode)
-               (or (null dir)
-                   (equal default-directory dir)))
-          (funcall func)))))
-
-(defun magit-refresh-buffer (&optional buffer)
-  (with-current-buffer (or buffer (current-buffer))
-    (let* ((old-line (line-number-at-pos))
-           (old-point (point))
-           (old-section (magit-current-section))
-           (old-path (and old-section
-                          (magit-section-path (magit-current-section)))))
-      (beginning-of-line)
-      (let ((section-line (and old-section
-                               (count-lines
-                                (magit-section-beginning old-section)
-                                (point))))
-            (line-char (- old-point (point))))
-        (if magit-refresh-function
-            (apply magit-refresh-function
-                   magit-refresh-args))
-        (magit-refresh-marked-commits-in-buffer)
-        (let ((s (and old-path (magit-find-section old-path magit-top-section))))
-          (cond (s
-                 (goto-char (magit-section-beginning s))
-                 (forward-line section-line)
-                 (forward-char line-char))
-                (t
-                 (magit-goto-line old-line)))
-          (dolist (w (get-buffer-window-list (current-buffer)))
-            (set-window-point w (point)))
-          (magit-highlight-section))))))
-
-(defun magit-string-has-prefix-p (string prefix)
-  (eq (compare-strings string nil (length prefix) prefix nil nil) t))
-
-(defun magit-revert-buffers (dir &optional ignore-modtime)
-  (dolist (buffer (buffer-list))
-    (when (and buffer
-               (buffer-file-name buffer)
-               ;; don't revert indirect buffers, as the parent will be reverted
-               (not (buffer-base-buffer buffer))
-               (magit-string-has-prefix-p (buffer-file-name buffer) dir)
-               (file-readable-p (buffer-file-name buffer))
-               (or ignore-modtime (not (verify-visited-file-modtime buffer)))
-               (not (buffer-modified-p buffer)))
-      (with-current-buffer buffer
-        (condition-case var
-            (revert-buffer t t nil)
-          (error (let ((signal-data (cadr var)))
-                   (cond (t (magit-bug-report signal-data))))))))))
-
-(defun magit-update-vc-modeline (dir)
-  "Update the modeline for buffers representable by magit."
-  (dolist (buffer (buffer-list))
-    (when (and buffer
-               (buffer-file-name buffer)
-               (magit-string-has-prefix-p (buffer-file-name buffer) dir))
-      (with-current-buffer buffer
-        (condition-case var
-            (vc-find-file-hook)
-          (error (let ((signal-data (cadr var)))
-                   (cond (t (magit-bug-report signal-data))))))))))
-
-(defvar magit-refresh-needing-buffers nil)
-(defvar magit-refresh-pending nil)
-
-(defun magit-refresh-wrapper (func)
-  (if magit-refresh-pending
-      (funcall func)
-    (let* ((dir default-directory)
-           (status-buffer (magit-find-status-buffer dir))
-           (magit-refresh-needing-buffers nil)
-           (magit-refresh-pending t))
-      (unwind-protect
-          (funcall func)
-        (when magit-refresh-needing-buffers
-          (magit-revert-buffers dir)
-          (dolist (b (adjoin status-buffer
-                             magit-refresh-needing-buffers))
-            (magit-refresh-buffer b)))))))
-
-(defun magit-need-refresh (&optional buffer)
-  "Mark BUFFER as needing to be refreshed.  If BUFFER is nil, use the
-current buffer."
-  (pushnew (or buffer (current-buffer)) magit-refresh-needing-buffers :test 'eq))
-
-(defun magit-refresh ()
-  "Refresh current buffer to match repository state.
-Also revert every unmodified buffer visiting files
-in the corresponding directory."
+(defun magit-go-forward ()
+  "Move forward in current buffer's history."
   (interactive)
   (interactive)
-  (magit-with-refresh
-    (magit-need-refresh)))
+  (if help-xref-forward-stack
+      (help-xref-go-forward (current-buffer))
+    (user-error "No next entry in buffer's history")))
+
+(defun magit-xref-insert-buttons ()
+  (when (and (or (eq magit-show-xref-buttons t)
+                 (apply 'derived-mode-p magit-show-xref-buttons))
+            (or help-xref-stack help-xref-forward-stack))
+    (insert "\n")
+    (when help-xref-stack
+      (magit-xref-insert-button help-back-label
+                                'magit-xref-backward))
+    (when help-xref-forward-stack
+      (when help-xref-stack
+       (insert " "))
+      (magit-xref-insert-button help-forward-label
+                                'magit-xref-forward))))
+
+(defun magit-xref-insert-button (label type)
+  (magit-with-section (section button label)
+    (insert-text-button label 'type type
+                        'help-args (list (current-buffer)))))
+
+(define-button-type 'magit-xref-backward
+  :supertype 'help-back
+  'mouse-face magit-item-highlight-face
+  'help-echo (purecopy "mouse-2, RET: go back to previous history entry"))
+
+(define-button-type 'magit-xref-forward
+  :supertype 'help-forward
+  'mouse-face magit-item-highlight-face
+  'help-echo (purecopy "mouse-2, RET: go back to next history entry"))
+
+(defun magit-setup-xref (item)
+  (when help-xref-stack-item
+    (push (cons (point) help-xref-stack-item) help-xref-stack)
+    (setq help-xref-forward-stack nil))
+  (when (called-interactively-p 'interactive)
+    (let ((tail (nthcdr 10 help-xref-stack)))
+      (if tail (setcdr tail nil))))
+  (setq help-xref-stack-item item))
+
+;;;;; Refresh Machinery
+
+(defun magit-refresh (&optional buffer)
+  "Refresh some buffers belonging to the current repository.
+
+Refresh the current buffer if its major mode derives from
+`magit-mode', and refresh the corresponding status buffer.
+If the global `magit-auto-revert-mode' is turned on, then
+also revert all unmodified buffers that visit files being
+tracked in the current repository."
+  (interactive (list (current-buffer)))
+  (unless buffer
+    (setq buffer (current-buffer)))
+  (with-current-buffer buffer
+    (when (derived-mode-p 'magit-mode)
+      (magit-mode-refresh-buffer buffer))
+    (let (status)
+      (when (and (not (eq major-mode 'magit-status-mode))
+                 (setq status (magit-mode-get-buffer
+                               magit-status-buffer-name
+                               'magit-status-mode)))
+        (magit-mode-refresh-buffer status))))
+  (when magit-auto-revert-mode
+    (magit-revert-buffers)))
 
 (defun magit-refresh-all ()
 
 (defun magit-refresh-all ()
-  "Refresh all magit buffers to match respective repository states.
-Also revert every unmodified buffer visiting files
-in the corresponding directories."
-  (interactive)
-  (magit-for-all-buffers #'magit-refresh-buffer default-directory))
-
-;;; Untracked files
-
-(defun magit-wash-untracked-file ()
-  (if (looking-at "^? \\(.*\\)$")
-      (let ((file (match-string-no-properties 1)))
-        (delete-region (point) (+ (line-end-position) 1))
-        (magit-with-section file 'file
-          (magit-set-section-info file)
-          (insert "\t" file "\n"))
-        t)
-    nil))
-
-(defun magit-wash-untracked-files ()
-  ;; Setting magit-old-top-section to nil speeds up washing: no time
-  ;; is wasted looking up the old visibility, which doesn't matter for
-  ;; untracked files.
-  ;;
-  ;; XXX - speed this up in a more general way.
-  ;;
-  (let ((magit-old-top-section nil))
-    (magit-wash-sequence #'magit-wash-untracked-file)))
+  "Refresh all buffers belonging to the current repository.
 
 
-(defun magit-insert-untracked-files ()
-  (unless (string= (magit-get "status" "showUntrackedFiles") "no")
-    (apply 'magit-git-section
-           `(untracked
-             "Untracked files:"
-             magit-wash-untracked-files
-             "ls-files" "--others" "-t" "--exclude-standard"
-             ,@(when magit-omit-untracked-dir-contents
-                 '("--directory"))))))
+Refresh all Magit buffers belonging to the current repository.
+If the global `magit-auto-revert-mode' is turned on, then also
+revert all unmodified buffers that visit files being tracked in
+the current repository."
+  (interactive)
+  (magit-map-magit-buffers #'magit-mode-refresh-buffer default-directory)
+  (magit-revert-buffers))
 
 
-;;; Diffs and Hunks
+(defun magit-revert-buffers ()
+  (let ((topdir (magit-get-top-dir)))
+    (when topdir
+      (let ((gitdir  (magit-git-dir))
+            (tracked (magit-git-lines "ls-tree" "-r" "--name-only" "HEAD")))
+        (dolist (buf (buffer-list))
+          (with-current-buffer buf
+            (let ((file (buffer-file-name)))
+              (and file (string-prefix-p topdir file)
+                   (not (string-prefix-p gitdir file))
+                   (member (file-relative-name file topdir) tracked)
+                   (let ((auto-revert-mode t))
+                     (auto-revert-handler)
+                     (run-hooks 'magit-revert-buffer-hook))))))))))
+
+;;; (misplaced)
+;;;; Diff Options
 
 (defvar magit-diff-context-lines 3)
 
 
 (defvar magit-diff-context-lines 3)
 
@@ -2562,1030 +4085,567 @@ in the corresponding directories."
 
 (defun magit-diff-default-hunks ()
   "Reset context for diff hunks to the default size."
 
 (defun magit-diff-default-hunks ()
   "Reset context for diff hunks to the default size."
-  (interactive "")
+  (interactive)
   (setq magit-diff-context-lines 3)
   (magit-refresh))
 
   (setq magit-diff-context-lines 3)
   (magit-refresh))
 
-(defun magit-toggle-diff-refine-hunk (&optional other)
-  (interactive "P")
-  "Turn diff-hunk refining on or off.
+(defun magit-set-diff-options ()
+  "Set local `magit-diff-options' based on popup state.
+And refresh the current Magit buffer."
+  (interactive)
+  (setq-local magit-diff-options magit-custom-options)
+  (magit-refresh))
 
 
-If hunk refining is currently on, then hunk refining is turned off.
-If hunk refining is off, then hunk refining is turned on, in
-`selected' mode (only the currently selected hunk is refined).
+;; `magit-set-default-diff-options' is defined in "Options"/"Setters".
 
 
-With a prefix argument, the \"third choice\" is used instead:
-If hunk refining is currently on, then refining is kept on, but
-the refining mode (`selected' or `all') is switched.
-If hunk refining is off, then hunk refining is turned on, in
-`all' mode (all hunks refined).
+(defun magit-save-default-diff-options ()
+  "Set and save the default for `magit-diff-options' based on popup value.
+Also set the local value in all Magit buffers and refresh them."
+  (interactive)
+  (customize-save-variable 'magit-diff-options magit-custom-options))
 
 
-Customize `magit-diff-refine-hunk' to change the default mode."
-  (let* ((old magit-diff-refine-hunk)
-         (new
-          (if other
-              (if (eq old 'all) t 'all)
-            (not old))))
+(defun magit-reset-diff-options ()
+  "Reset local `magit-diff-options' to default value.
+And refresh the current Magit buffer."
+  (interactive)
+  (setq-local magit-diff-options (default-value 'magit-diff-options))
+  (magit-refresh))
 
 
-    ;; remove any old refining in currently highlighted section
-    (when (and magit-highlighted-section old (not (eq old 'all)))
-      (magit-unrefine-section magit-highlighted-section))
+;;;; Raw Diff Washing
 
 
-    ;; set variable to new value locally
-    (set (make-local-variable 'magit-diff-refine-hunk) new)
+(defun magit-insert-diff (section file status)
+  (let ((beg (point)))
+    (apply 'magit-git-insert "-c" "diff.submodule=short" "diff"
+           `(,(magit-diff-U-arg) ,@magit-diff-options "--" ,file))
+    (unless (eq (char-before) ?\n)
+      (insert "\n"))
+    (save-restriction
+      (narrow-to-region beg (point))
+      (goto-char beg)
+      (magit-wash-diff-section section)
+      (goto-char (point-max)))))
+
+(defun magit-wash-raw-diffs (&optional staged)
+  (let (previous)
+    (magit-wash-sequence
+     (lambda ()
+       (setq previous (magit-wash-raw-diff previous staged))))))
+
+(defun magit-wash-raw-diff (previous staged)
+  (when (looking-at
+         ":\\([0-7]+\\) \\([0-7]+\\) [0-9a-f]+ [0-9a-f]+ \\(.\\)[0-9]*\t\\([^\t\n]+\\)$")
+    (let ((file (magit-decode-git-path (match-string-no-properties 4)))
+          (status (cl-ecase (string-to-char (match-string-no-properties 3))
+                    (?A 'new)
+                    (?C 'copy)
+                    (?D 'deleted)
+                    (?M 'modified)
+                    (?T 'typechange)
+                    (?U 'unmerged)
+                    (?X 'unknown))))
+      (delete-region (point) (1+ (line-end-position)))
+      (unless (or ;; Unmerged files get two entries; we ignore the second.
+                  (equal file previous)
+                  ;; Ignore staged, unmerged files.
+                  (and staged (eq status 'unmerged)))
+        (magit-with-section (section diff file nil nil
+                                     (not (derived-mode-p
+                                           'magit-diff-mode
+                                           'magit-commit-mode)))
+          (if (not (magit-section-hidden section))
+              (magit-insert-diff section file status)
+            (setf (magit-section-diff-status section) status)
+            (setf (magit-section-needs-refresh-on-show section) t)
+            (magit-insert-diff-title status file nil))))
+      file)))
+
+;;; Modes (1)
+;;;; Commit Mode
+;;;;; Commit Core
 
 
-    ;; if now highlighting in "selected only" mode, turn refining back
-    ;; on in the current section
-    (when (and magit-highlighted-section new (not (eq new 'all)))
-      (magit-refine-section magit-highlighted-section))
+(define-derived-mode magit-commit-mode magit-mode "Magit"
+  "Mode for looking at a git commit.
+
+\\<magit-commit-mode-map>Type `\\[magit-visit-item]` to visit the changed file, \
+`\\[magit-toggle-section]` to hide or show a hunk,
+`\\[magit-diff-larger-hunks]` and `\\[magit-diff-smaller-hunks]` to change the \
+size of the hunks.
+Type `\\[magit-apply-item]` to apply a change to your worktree and \
+`\\[magit-revert-item]` to reverse it.
+
+\\{magit-commit-mode-map}
+Unless shadowed by the mode specific bindings above, bindings
+from the parent keymap `magit-mode-map' are also available."
+  :group 'magit)
 
 
-    ;; `all' mode being turned on or off needs a complete refresh
-    (when (or (eq old 'all) (eq new 'all))
-      (magit-refresh))))
+(defvar magit-commit-buffer-name "*magit-commit*"
+  "Name of buffer used to display a commit.")
 
 
-(defun magit-diff-line-file ()
-  (cond ((looking-at "^diff --git ./\\(.*\\) ./\\(.*\\)$")
-         (match-string-no-properties 2))
-        ((looking-at "^diff --cc +\\(.*\\)$")
-         (match-string-no-properties 1))
-        (t
-         nil)))
+;;;###autoload
+(defun magit-show-commit (commit &optional noselect)
+  "Show information about COMMIT."
+  (interactive (list (magit-read-rev-with-default
+                      "Show commit (hash or ref)")))
+  (when (magit-git-failure "cat-file" "commit" commit)
+    (user-error "%s is not a commit" commit))
+  (magit-mode-setup magit-commit-buffer-name
+                    (if noselect 'display-buffer 'pop-to-buffer)
+                    #'magit-commit-mode
+                    #'magit-refresh-commit-buffer
+                    commit))
 
 
-(defun magit-wash-diffs ()
-  (magit-wash-sequence #'magit-wash-diff-or-other-file))
+(defun magit-show-item-or-scroll-up ()
+  "Update commit or status buffer for item at point.
 
 
-(defun magit-wash-diff-or-other-file ()
-  (or (magit-wash-diff)
-      (magit-wash-other-file)))
+Either show the commit or stash at point in another buffer,
+or if that buffer is already displayed in the current frame
+and contains information about that commit or stash, then
+instead scroll the buffer up.  If there is no commit or
+stash at point, then prompt for a commit."
+  (interactive)
+  (magit-show-item-or-scroll 'scroll-up))
 
 
-(defun magit-wash-other-file ()
-  (if (looking-at "^? \\(.*\\)$")
-      (let ((file (match-string-no-properties 1)))
-        (delete-region (point) (+ (line-end-position) 1))
-        (magit-with-section file 'file
-          (magit-set-section-info file)
-          (insert "\tNew      " file "\n"))
-        t)
-    nil))
+(defun magit-show-item-or-scroll-down ()
+  "Update commit or status buffer for item at point.
 
 
-(defvar magit-hide-diffs nil)
+Either show the commit or stash at point in another buffer,
+or if that buffer is already displayed in the current frame
+and contains information about that commit or stash, then
+instead scroll the buffer down.  If there is no commit or
+stash at point, then prompt for a commit."
+  (interactive)
+  (magit-show-item-or-scroll 'scroll-down))
+
+(defun magit-show-item-or-scroll (fn)
+  (let (rev cmd buf win)
+    (magit-section-case (info)
+      (commit (setq rev info
+                    cmd 'magit-show-commit
+                    buf magit-commit-buffer-name))
+      (stash  (setq rev info
+                    cmd 'magit-diff-stash
+                    buf magit-stash-buffer-name)))
+    (if rev
+        (if (and (setq buf (get-buffer buf))
+                 (setq win (get-buffer-window buf))
+                 (with-current-buffer buf
+                   (equal rev (car magit-refresh-args))))
+            (with-selected-window win
+              (condition-case err
+                  (funcall fn)
+                (error
+                 (goto-char (cl-case fn
+                              (scroll-up   (point-min))
+                              (scroll-down (point-max)))))))
+          (funcall cmd rev t))
+      (call-interactively 'magit-show-commit))))
 
 
-(defvar magit-indentation-level 1)
+(defun magit-refresh-commit-buffer (commit)
+  (magit-git-insert-section (commitbuf nil)
+      #'magit-wash-commit
+    "log" "-1" "--decorate=full"
+    "--pretty=medium" (magit-diff-U-arg)
+    "--cc" "-p" (and magit-show-diffstat "--stat")
+    magit-diff-options commit))
 
 
-(defun magit-insert-diff-title (status file file2)
-  (let ((status-text (case status
-                       ((unmerged)
-                        (format "Unmerged   %s" file))
-                       ((new)
-                        (format "New        %s" file))
-                       ((deleted)
-                        (format "Deleted    %s" file))
-                       ((renamed)
-                        (format "Renamed    %s   (from %s)"
-                                file file2))
-                       ((modified)
-                        (format "Modified   %s" file))
-                       ((typechange)
-                        (format "Typechange %s" file))
-                       (t
-                        (format "?          %s" file)))))
-    (insert (make-string magit-indentation-level ?\t) status-text "\n")))
+;;;;; Commit Washing
 
 
-(defvar magit-current-diff-range nil
-  "Used internally when setting up magit diff sections.")
+(defun magit-wash-commit ()
+  (looking-at "^commit \\([a-z0-9]+\\)\\(?: \\(.+\\)\\)?$")
+  (let ((rev  (match-string 1))
+        (refs (match-string 2)))
+    (delete-region (point) (1+ (line-end-position)))
+    (magit-with-section
+        (section headers 'headers
+         (concat (propertize rev 'face 'magit-log-sha1)
+                 (and refs (concat " "(magit-format-ref-labels refs)))
+                 "\n"))
+      (while (re-search-forward "^\\([a-z]+\\): +\\(.+\\)$" nil t)
+        (when (string-match-p (match-string 1) "Merge")
+          (let ((revs (match-string 2)))
+            (delete-region (match-beginning 2) (match-end 2))
+            (dolist (rev (split-string revs))
+              (magit-insert-commit-button rev)
+              (insert ?\s)))))
+      (forward-line)))
+  (forward-line)
+  (let ((bound (save-excursion
+                 (when (re-search-forward "^diff" nil t)
+                   (copy-marker (match-beginning 0)))))
+        (summary (buffer-substring-no-properties
+                  (point) (line-end-position))))
+    (delete-region (point) (1+ (line-end-position)))
+    (magit-with-section (section message 'message (concat summary "\n"))
+      (cond ((re-search-forward "^---" bound t)
+             (goto-char (match-beginning 0))
+             (delete-region (match-beginning 0) (match-end 0)))
+            ((re-search-forward "^.[^ ]" bound t)
+             (goto-char (1- (match-beginning 0)))))))
+  (forward-line)
+  (when magit-show-diffstat
+    (magit-wash-diffstats))
+  (forward-line)
+  (magit-wash-diffs))
+
+(defun magit-insert-commit-button (hash)
+  (magit-with-section (section commit hash)
+    (insert-text-button hash
+                        'help-echo "Visit commit"
+                        'action (lambda (button)
+                                  (save-excursion
+                                    (goto-char button)
+                                    (magit-visit-item)))
+                        'follow-link t
+                        'mouse-face magit-item-highlight-face
+                        'face 'magit-log-sha1)))
+
+;;;; Status Mode
 
 
-(defun magit-wash-typechange-section (file)
-  (magit-set-section-info (list 'typechange file))
-  (let ((first-start (point-marker))
-        (second-start (progn (forward-line 1)
-                             (search-forward-regexp "^diff")
-                             (beginning-of-line)
-                             (point-marker))))
-    (let ((magit-indentation-level (+ magit-indentation-level 1)))
-      (save-restriction
-        (narrow-to-region first-start second-start)
-        (goto-char (point-min))
-        (magit-with-section file 'diff
-          (magit-wash-diff-section)))
-      (save-restriction
-        (narrow-to-region second-start (point-max))
-        (goto-char (point-min))
-        (magit-with-section file 'diff
-          (magit-wash-diff-section))))))
-
-(defun magit-wash-diff-section ()
-  (cond ((looking-at "^\\* Unmerged path \\(.*\\)")
-         (let ((file (match-string-no-properties 1)))
-           (delete-region (point) (line-end-position))
-           (insert "\tUnmerged " file "\n")
-           (magit-set-section-info (list 'unmerged file nil))
-           t))
-        ((looking-at "^diff")
-         (let ((file (magit-diff-line-file))
-               (end (save-excursion
-                      (forward-line) ;; skip over "diff" line
-                      (if (search-forward-regexp "^diff\\|^@@" nil t)
-                          (goto-char (match-beginning 0))
-                        (goto-char (point-max)))
-                      (point-marker))))
-           (let* ((status (cond
-                           ((looking-at "^diff --cc")
-                            'unmerged)
-                           ((save-excursion
-                              (search-forward-regexp "^new file" end t))
-                            'new)
-                           ((save-excursion
-                              (search-forward-regexp "^deleted" end t))
-                            'deleted)
-                           ((save-excursion
-                              (search-forward-regexp "^rename" end t))
-                            'renamed)
-                           (t
-                            'modified)))
-                  (file2 (cond
-                          ((save-excursion
-                             (search-forward-regexp "^rename from \\(.*\\)"
-                                                    end t))
-                           (match-string-no-properties 1)))))
-             (magit-set-section-info (list status
-                                           file
-                                           (or file2 file)
-                                           magit-current-diff-range))
-             (magit-insert-diff-title status file file2)
-             (when (search-forward-regexp "\\(--- \\(.*\\)\n\\+\\+\\+ \\(.*\\)\n\\)" () t)
-               (when (match-string 1)
-                 (add-text-properties (match-beginning 1) (match-end 1)
-                                      '(face magit-diff-hunk-header))
-                 (add-text-properties (match-beginning 2) (match-end 2)
-                                      '(face magit-diff-file-header))
-                 (add-text-properties (match-beginning 3) (match-end 3)
-                                      '(face magit-diff-file-header))))
-             (goto-char end)
-             (let ((magit-section-hidden-default nil))
-               (magit-wash-sequence #'magit-wash-hunk))))
-         t)
-        (t
-         nil)))
+(define-derived-mode magit-status-mode magit-mode "Magit"
+  "Mode for looking at git status.
 
 
-(defun magit-wash-diff ()
-  (let ((magit-section-hidden-default magit-hide-diffs))
-    (magit-with-section (magit-current-line) 'diff
-      (magit-wash-diff-section))))
+\\<magit-status-mode-map>Type `\\[magit-stage-item]` to stage (add) an item, \
+`\\[magit-unstage-item]` to unstage it.
+Type `\\[magit-key-mode-popup-committing]` to have a popup to commit, type \
+`\\[magit-key-mode-popup-dispatch]` to see others
+available popup.
+Type `\\[magit-visit-item]` to visit something, and \
+`\\[magit-toggle-section]` to show or hide section.
 
 
-(defun magit-diff-item-kind (diff)
-  (car (magit-section-info diff)))
+More information can be found in Info node `(magit)Status'
 
 
-(defun magit-diff-item-file (diff)
-  (cadr (magit-section-info diff)))
+Other key binding:
+\\{magit-status-mode-map}"
+  :group 'magit)
 
 
-(defun magit-diff-item-file2 (diff)
-  (caddr (magit-section-info diff)))
+(defvar magit-status-buffer-name "*magit: %t*"
+  "Name of buffer used to display a repository's status.")
 
 
-(defun magit-diff-item-range (diff)
-  (nth 3 (magit-section-info diff)))
+;;;###autoload
+(defun magit-status (dir &optional switch-function)
+  "Open a Magit status buffer for the Git repository containing DIR.
+If DIR is not within a Git repository, offer to create a Git
+repository in DIR.
 
 
-(defun magit-wash-hunk ()
-  (cond ((looking-at "\\(^@+\\)[^@]*@+.*")
-         (let ((n-columns (1- (length (match-string 1))))
-               (head (match-string 0))
-               (hunk-start-pos (point)))
-           (magit-with-section head 'hunk
-             (add-text-properties (match-beginning 0) (match-end 0)
-                                  '(face magit-diff-hunk-header))
-             (forward-line)
-             (while (not (or (eobp)
-                             (looking-at "^diff\\|^@@")))
-               (magit-highlight-line-whitespace)
-               (let ((prefix (buffer-substring-no-properties
-                              (point) (min (+ (point) n-columns) (point-max)))))
-                 (cond ((string-match "\\+" prefix)
-                        (magit-put-line-property 'face 'magit-diff-add))
-                       ((string-match "-" prefix)
-                        (magit-put-line-property 'face 'magit-diff-del))
-                       (t
-                        (magit-put-line-property 'face 'magit-diff-none))))
-               (forward-line)))
+Interactively, a prefix argument means to ask the user which Git
+repository to use even if `default-directory' is under Git
+control.  Two prefix arguments means to ignore `magit-repo-dirs'
+when asking for user input.
+
+Depending on option `magit-status-buffer-switch-function' the
+status buffer is shown in another window (the default) or the
+current window.  Non-interactively optional SWITCH-FUNCTION
+can be used to override this."
+  (interactive (list (if current-prefix-arg
+                         (magit-read-top-dir
+                          (> (prefix-numeric-value current-prefix-arg)
+                             4))
+                       (or (magit-get-top-dir)
+                           (magit-read-top-dir nil)))))
+  (magit-save-some-buffers)
+  (let ((topdir (magit-get-top-dir dir)))
+    (when (or topdir
+              (and (yes-or-no-p
+                    (format "There is no Git repository in %s.  Create one? "
+                            dir))
+                   (magit-init dir)
+                   (setq topdir (magit-get-top-dir dir))))
+      (let ((default-directory topdir))
+        (magit-mode-setup magit-status-buffer-name
+                          (or switch-function
+                              magit-status-buffer-switch-function)
+                          #'magit-status-mode
+                          #'magit-refresh-status)))))
 
 
-           (when (eq magit-diff-refine-hunk 'all)
-             (save-excursion
-               (goto-char hunk-start-pos)
-               (diff-refine-hunk))))
-         t)
-        (t
-         nil)))
-
-(defvar magit-diff-options nil)
-
-(defun magit-insert-diff (file status)
-  (let ((cmd magit-git-executable)
-        (args (append (list "diff")
-                      (list (magit-diff-U-arg))
-                      magit-diff-options
-                      (list "--" file))))
-    (let ((p (point)))
-      (magit-git-insert args)
-      (if (not (eq (char-before) ?\n))
-          (insert "\n"))
-      (save-restriction
-        (narrow-to-region p (point))
-        (goto-char p)
-        (cond
-         ((eq status 'typechange)
-          (magit-insert-diff-title status file file)
-          (magit-wash-typechange-section file))
-         (t
-          (magit-wash-diff-section)))
-        (goto-char (point-max))))))
-
-(defvar magit-last-raw-diff nil)
-(defvar magit-ignore-unmerged-raw-diffs nil)
-
-(defun magit-wash-raw-diffs ()
-  (let ((magit-last-raw-diff nil))
-    (magit-wash-sequence #'magit-wash-raw-diff)))
-
-(defun magit-wash-raw-diff ()
-  (if (looking-at
-       ":\\([0-7]+\\) \\([0-7]+\\) [0-9a-f]+ [0-9a-f]+ \\(.\\)[0-9]*\t\\([^\t\n]+\\)$")
-      (let ((old-perm (match-string-no-properties 1))
-            (new-perm (match-string-no-properties 2))
-            (status (case (string-to-char (match-string-no-properties 3))
-                      (?A 'new)
-                      (?D 'deleted)
-                      (?M 'modified)
-                      (?U 'unmerged)
-                      (?T 'typechange)
-                      (t     nil)))
-            (file (match-string-no-properties 4)))
-        ;; If this is for the same file as the last diff, ignore it.
-        ;; Unmerged files seem to get two entries.
-        ;; We also ignore unmerged files when told so.
-        (if (or (equal file magit-last-raw-diff)
-                (and magit-ignore-unmerged-raw-diffs (eq status 'unmerged)))
-            (delete-region (point) (+ (line-end-position) 1))
-          (setq magit-last-raw-diff file)
-          ;; The 'diff' section that is created here will not work with
-          ;; magit-insert-diff-item-patch etc when we leave it empty.
-          ;; Luckily, raw diffs are only produced for staged and
-          ;; unstaged changes, and we never call
-          ;; magit-insert-diff-item-patch on them.  This is a bit
-          ;; brittle, of course.
-          (let ((magit-section-hidden-default magit-hide-diffs))
-            (magit-with-section file 'diff
-              (delete-region (point) (+ (line-end-position) 1))
-              (if (not (magit-section-hidden magit-top-section))
-                  (magit-insert-diff file status)
-                (magit-set-section-info (list status file nil))
-                (magit-set-section-needs-refresh-on-show t)
-                (magit-insert-diff-title status file nil)))))
-        t)
-    nil))
-
-(defun magit-hunk-item-diff (hunk)
-  (let ((diff (magit-section-parent hunk)))
-    (or (eq (magit-section-type diff) 'diff)
-        (error "Huh?  Parent of hunk not a diff"))
-    diff))
+(defun magit-refresh-status ()
+  (magit-git-exit-code "update-index" "--refresh")
+  (magit-with-section (section status 'status nil t)
+    (run-hooks 'magit-status-sections-hook))
+  (run-hooks 'magit-refresh-status-hook))
 
 
-(defun magit-diff-item-insert-header (diff buf)
-  (let ((beg (save-excursion
-               (goto-char (magit-section-beginning diff))
-               (forward-line)
-               (point)))
-        (end (if (magit-section-children diff)
-                 (magit-section-beginning (car (magit-section-children diff)))
-               (magit-section-end diff))))
-    (magit-insert-region beg end buf)))
+;;; Sections
+;;;; Real Sections
+
+(defun magit-insert-stashes ()
+  (let ((stashes (magit-git-lines "stash" "list")))
+    (when stashes
+      (magit-with-section (section stashes 'stashes "Stashes:" t)
+        (dolist (stash stashes)
+          (string-match "^\\(stash@{\\([0-9]+\\)}\\): \\(.+\\)$" stash)
+          (let ((stash (match-string 1 stash))
+                (number (match-string 2 stash))
+                (message (match-string 3 stash)))
+            (magit-with-section (section stash stash)
+              (insert number ": " message "\n"))))
+        (insert "\n")))))
 
 
-(defun magit-insert-diff-item-patch (diff buf)
-  (let ((beg (save-excursion
-               (goto-char (magit-section-beginning diff))
-               (forward-line)
-               (point)))
-        (end (magit-section-end diff)))
-    (magit-insert-region beg end buf)))
+(defun magit-insert-untracked-files ()
+  (magit-with-section (section untracked 'untracked "Untracked files:" t)
+    (let ((files (cl-mapcan
+                  (lambda (f)
+                    (when (eq (aref f 0) ??) (list f)))
+                  (magit-git-lines
+                   "status" "--porcelain"))))
+      (if (not files)
+          (setq section nil)
+        (dolist (file files)
+          (setq file (magit-decode-git-path (substring file 3)))
+          (magit-with-section (section file file)
+            (insert "\t" file "\n")))
+        (insert "\n")))))
+
+(defun magit-insert-pending-commits ()
+  (let* ((info (magit-read-rewrite-info))
+         (pending (cdr (assq 'pending info))))
+    (when pending
+      (magit-with-section (section pending 'pending "Pending commits:" t)
+        (dolist (p pending)
+          (let* ((commit (car p))
+                 (properties (cdr p))
+                 (used (plist-get properties 'used)))
+            (magit-with-section (section commit commit)
+              (insert (magit-git-string
+                       "log" "-1"
+                       (if used
+                           "--pretty=format:. %s"
+                         "--pretty=format:* %s")
+                       commit "--")
+                      "\n")))))
+      (insert "\n"))))
 
 
-(defun magit-insert-hunk-item-patch (hunk buf)
-  (magit-diff-item-insert-header (magit-hunk-item-diff hunk) buf)
-  (magit-insert-region (magit-section-beginning hunk) (magit-section-end hunk)
-                       buf))
+(defun magit-insert-unstaged-changes ()
+  (let ((magit-current-diff-range (cons 'index 'working))
+        (magit-diff-options (copy-sequence magit-diff-options)))
+    (magit-git-insert-section (unstaged "Unstaged changes:")
+        #'magit-wash-raw-diffs
+      "diff-files")))
+
+(defun magit-insert-staged-changes ()
+  (let ((no-commit (not (magit-git-success "log" "-1" "HEAD"))))
+    (when (or no-commit (magit-anything-staged-p))
+      (let ((magit-current-diff-range (cons "HEAD" 'index))
+            (base (if no-commit
+                      (magit-git-string "mktree")
+                    "HEAD"))
+            (magit-diff-options (append '("--cached") magit-diff-options)))
+        (magit-git-insert-section (staged "Staged changes:")
+            (apply-partially #'magit-wash-raw-diffs t)
+          "diff-index" "--cached" base)))))
+
+(defun magit-insert-unpulled-or-recent-commits ()
+  (let ((tracked (magit-get-tracked-branch nil t)))
+    (if (and tracked
+             (not (equal (magit-git-string "rev-parse" "HEAD")
+                         (magit-git-string "rev-parse" tracked))))
+        (magit-insert-unpulled-commits)
+      (magit-git-insert-section (recent "Recent commits:")
+          (apply-partially 'magit-wash-log 'unique)
+        "log" "--format=format:%h %s" "-n" "10"))))
+
+(defun magit-insert-unpulled-commits ()
+  (let ((tracked (magit-get-tracked-branch nil t)))
+    (when tracked
+      (magit-git-insert-section (unpulled "Unpulled commits:")
+          (apply-partially 'magit-wash-log 'unique)
+        "log" "--format=format:%h %s" (concat "HEAD.." tracked)))))
+
+(defun magit-insert-unpushed-commits ()
+  (let ((tracked (magit-get-tracked-branch nil t)))
+    (when tracked
+      (magit-git-insert-section (unpushed "Unpushed commits:")
+          (apply-partially 'magit-wash-log 'unique)
+        "log" "--format=format:%h %s" (concat tracked "..HEAD")))))
+
+(defun magit-insert-unpulled-cherries ()
+  (let ((tracked (magit-get-tracked-branch nil t)))
+    (when tracked
+      (magit-git-insert-section (unpulled "Unpulled commits:")
+          (apply-partially 'magit-wash-log 'cherry)
+        "cherry" "-v" (magit-abbrev-arg) (magit-get-current-branch) tracked))))
+
+(defun magit-insert-unpushed-cherries ()
+  (let ((tracked (magit-get-tracked-branch nil t)))
+    (when tracked
+      (magit-git-insert-section (unpushed "Unpushed commits:")
+          (apply-partially 'magit-wash-log 'cherry)
+        "cherry" "-v" (magit-abbrev-arg) tracked))))
+
+;;;; Line Sections
+
+(defun magit-insert-empty-line ()
+  (insert "\n"))
+
+(defun magit-insert-status-local-line ()
+  (let ((branch (or (magit-get-current-branch) "(detached)")))
+    (magit-insert-line-section (branch branch)
+      (concat "Local: "
+              (propertize branch 'face 'magit-branch)
+              " " (abbreviate-file-name default-directory)))))
+
+(defun magit-insert-status-remote-line ()
+  (let* ((branch  (magit-get-current-branch))
+         (tracked (magit-get-tracked-branch branch)))
+    (when tracked
+      (magit-insert-line-section (branch tracked)
+        (concat "Remote: "
+                (and (magit-get-boolean "branch" branch "rebase") "onto ")
+                (magit-format-tracked-line tracked branch))))))
+
+(defun magit-format-tracked-line (tracked branch)
+  (when tracked
+    (setq tracked (propertize tracked 'face 'magit-branch))
+    (let ((remote (magit-get "branch" branch "remote")))
+      (concat (if (string= "." remote)
+                  (concat "branch " tracked)
+                (when (string-match (concat "^" remote) tracked)
+                  (setq tracked (substring tracked (1+ (length remote)))))
+                (concat tracked " @ " remote
+                        " (" (magit-get "remote" remote "url") ")"))))))
+
+(defun magit-insert-status-head-line ()
+  (let ((hash (magit-git-string "rev-parse" "--verify" "HEAD")))
+    (if hash
+        (magit-insert-line-section (commit hash)
+          (concat "Head: " (magit-format-rev-summary "HEAD")))
+      (magit-insert-line-section (no-commit)
+        "Head: nothing committed yet"))))
+
+(defun magit-insert-status-tags-line ()
+  (let* ((current-tag (magit-get-current-tag t))
+         (next-tag (magit-get-next-tag t))
+         (both-tags (and current-tag next-tag t))
+         (tag-subject (eq magit-status-tags-line-subject 'tag)))
+    (when (or current-tag next-tag)
+      (magit-insert-line-section (line)
+        (concat
+         (if both-tags "Tags: " "Tag: ")
+         (and current-tag (apply 'magit-format-status-tag-sentence
+                                 tag-subject current-tag))
+         (and both-tags ", ")
+         (and next-tag (apply 'magit-format-status-tag-sentence
+                              (not tag-subject) next-tag)))))))
+
+(defun magit-format-status-tag-sentence (behindp tag cnt &rest ignored)
+  (concat (propertize tag 'face 'magit-tag)
+          (and (> cnt 0)
+               (concat (if (eq magit-status-tags-line-subject 'tag)
+                           (concat " (" (propertize (format "%s" cnt)
+                                                    'face 'magit-branch))
+                         (format " (%i" cnt))
+                       " " (if behindp "behind" "ahead") ")"))))
+
+;;;; Progress Sections
+
+(defun magit-insert-status-merge-line ()
+  (let ((heads (magit-file-lines (magit-git-dir "MERGE_HEAD"))))
+    (when heads
+      (magit-insert-line-section (line)
+        (concat
+         "Merging: "
+         (mapconcat 'identity (mapcar 'magit-name-rev heads) ", ")
+         (and magit-status-show-sequence-help
+              "; Resolve conflicts, or press \"m A\" to Abort"))))))
+
+(defun magit-insert-status-rebase-lines ()
+  (let ((rebase (magit-rebase-info)))
+    (when rebase
+      (magit-insert-line-section (line)
+        (concat (if (nth 4 rebase) "Applying" "Rebasing")
+               (apply 'format ": onto %s (%s of %s)" rebase)
+               (and magit-status-show-sequence-help
+                    "; Press \"R\" to Abort, Skip, or Continue")))
+      (when (and (null (nth 4 rebase)) (nth 3 rebase))
+        (magit-insert-line-section (line)
+          (concat "Stopped: "
+                  (magit-format-rev-summary (nth 3 rebase))))))))
+
+(defun magit-insert-rebase-sequence ()
+  (let ((f (magit-git-dir "rebase-merge/git-rebase-todo")))
+    (when (file-exists-p f)
+      (magit-with-section (section rebase-todo 'rebase-todo "Rebasing:" t)
+        (cl-loop
+         for line in (magit-file-lines f)
+         when (string-match
+               "^\\(pick\\|reword\\|edit\\|squash\\|fixup\\) \\([^ ]+\\) \\(.*\\)$"
+               line)
+         do (let ((cmd  (match-string 1 line))
+                  (hash (match-string 2 line))
+                  (msg  (match-string 3 line)))
+              (magit-with-section (section commit hash)
+                (insert cmd " ")
+                (insert (propertize
+                         (magit-git-string "rev-parse" "--short" hash)
+                         'face 'magit-log-sha1))
+                (insert " " msg "\n"))))
+        (insert "\n")))))
+
+(defun magit-insert-bisect-output ()
+  (when (magit-bisecting-p)
+    (let ((lines
+           (or (magit-file-lines (magit-git-dir "BISECT_CMD_OUTPUT"))
+               (list "Bisecting: (no saved bisect output)"
+                     "It appears you have invoked `git bisect' from a shell."
+                     "There is nothing wrong with that, we just cannot display"
+                     "anything useful here.  Consult the shell output instead.")))
+          (done-re "^[a-z0-9]\\{40\\} is the first bad commit$"))
+      (magit-with-section
+          (section bisect-output 'bisect-output
+                   (propertize
+                    (or (and (string-match done-re (car lines)) (pop lines))
+                        (cl-find-if (apply-partially 'string-match done-re)
+                                    lines)
+                        (pop lines))
+                    'face 'magit-section-title)
+                   t t)
+        (dolist (line lines)
+          (insert line "\n"))))
+    (insert "\n")))
+
+(defun magit-insert-bisect-rest ()
+  (when (magit-bisecting-p)
+    (magit-git-insert-section (bisect-view "Bisect Rest:")
+        (apply-partially 'magit-wash-log 'bisect-vis)
+      "bisect" "visualize" "git" "log"
+      "--pretty=format:%h%d %s" "--decorate=full")))
+
+(defun magit-insert-bisect-log ()
+  (when (magit-bisecting-p)
+    (magit-git-insert-section (bisect-log "Bisect Log:")
+        #'magit-wash-bisect-log
+      "bisect" "log")))
+
+(defun magit-wash-bisect-log ()
+  (let (beg)
+    (while (progn (setq beg (point-marker))
+                  (re-search-forward "^\\(git bisect [^\n]+\n\\)" nil t))
+      (let ((heading (match-string-no-properties 1)))
+        (delete-region (match-beginning 0) (match-end 0))
+        (save-restriction
+          (narrow-to-region beg (point))
+          (goto-char (point-min))
+          (magit-with-section (section bisect-log 'bisect-log heading nil t)
+            (magit-wash-sequence
+             (apply-partially 'magit-wash-log-line 'bisect-log
+                              (magit-abbrev-length)))))))
+    (when (re-search-forward
+           "# first bad commit: \\[\\([a-z0-9]\\{40\\}\\)\\] [^\n]+\n" nil t)
+      (let ((hash (match-string-no-properties 1)))
+        (delete-region (match-beginning 0) (match-end 0))
+        (magit-with-section
+            (section 'bisect-log 'bisect-log
+                     (concat hash " is the first bad commit\n")))))))
 
 
-(defun magit-insert-hunk-item-region-patch (hunk reverse beg end buf)
-  (magit-diff-item-insert-header (magit-hunk-item-diff hunk) buf)
-  (save-excursion
-    (goto-char (magit-section-beginning hunk))
-    (magit-insert-current-line buf)
-    (forward-line)
-    (let ((copy-op (if reverse "+" "-")))
-      (while (< (point) (magit-section-end hunk))
-        (if (and (<= beg (point)) (< (point) end))
-            (magit-insert-current-line buf)
-          (cond ((looking-at " ")
-                 (magit-insert-current-line buf))
-                ((looking-at copy-op)
-                 (let ((text (buffer-substring-no-properties
-                              (+ (point) 1) (line-beginning-position 2))))
-                   (with-current-buffer buf
-                     (insert " " text))))))
-        (forward-line))))
-  (with-current-buffer buf
-    (diff-fixup-modifs (point-min) (point-max))))
+(defun magit-bisecting-p ()
+  (file-exists-p (magit-git-dir "BISECT_LOG")))
 
 
-(defun magit-hunk-item-is-conflict-p (hunk)
-  ;;; XXX - Using the title is a bit too clever...
-  (string-match "^diff --cc"
-                (magit-section-title (magit-hunk-item-diff hunk))))
-
-(defun magit-hunk-item-target-line (hunk)
-  (save-excursion
-    (beginning-of-line)
-    (let ((line (line-number-at-pos)))
-      (goto-char (magit-section-beginning hunk))
-      (if (not (looking-at "@@+ .* \\+\\([0-9]+\\)\\(,[0-9]+\\)? @@+"))
-          (error "Hunk header not found"))
-      (let ((target (string-to-number (match-string 1))))
-        (forward-line)
-        (while (< (line-number-at-pos) line)
-          ;; XXX - deal with combined diffs
-          (if (not (looking-at "-"))
-              (setq target (+ target 1)))
-          (forward-line))
-        target))))
-
-(defun magit-show (commit filename &optional select prefix)
-  "Returns a buffer containing the contents of the file FILENAME, as stored in
-COMMIT.  COMMIT may be one of the following:
-
-- A string with the name of a commit, such as \"head\" or \"dae86e\".  See 'git
-  help revisions' for syntax.
-- The symbol 'index, indicating that you want the version in Git's index or
-  staging area.
-- The symbol 'working, indicating that you want the version in the working
-  directory.  In this case you'll get a buffer visiting the file.  If there's
-  already a buffer visiting that file, you'll get that one.
-
-When called interactively or when SELECT is non-nil, make the buffer active,
-either in another window or (with a prefix argument) in the current window."
-  (interactive (let* ((revision (magit-read-rev "Retrieve file from revision"))
-                      (filename (magit-read-file-from-rev revision)))
-                 (list revision filename t current-prefix-arg)))
-  (if (eq commit 'working)
-      (find-file-noselect filename)
-    (let ((buffer (create-file-buffer (format "%s.%s" filename (replace-regexp-in-string ".*/" "" (prin1-to-string commit t))))))
-      (cond
-       ((eq commit 'index)
-        (let ((checkout-string (magit-git-string "checkout-index"
-                                                 "--temp"
-                                                 filename)))
-          (string-match "^\\(.*\\)\t" checkout-string)
-          (with-current-buffer buffer
-            (let ((tmpname (match-string 1 checkout-string)))
-              (magit-with-silent-modifications
-               (insert-file-contents tmpname nil nil nil t))
-              (delete-file tmpname)))))
-       (t
-        (with-current-buffer buffer
-          (magit-with-silent-modifications
-           (magit-git-insert (list "cat-file" "-p"
-                                   (concat commit ":" filename)))))))
-      (with-current-buffer buffer
-        (let ((buffer-file-name filename))
-          (normal-mode))
-        (goto-char (point-min)))
-      (if select
-          (if prefix
-              (switch-to-buffer buffer)
-            (switch-to-buffer-other-window buffer))
-        buffer))))
-
-(defmacro with-magit-tmp-buffer (var &rest body)
-  (declare (indent 1)
-           (debug (symbolp &rest form)))
-  `(let ((,var (generate-new-buffer magit-tmp-buffer-name)))
-     (unwind-protect
-         (progn ,@body)
-       (kill-buffer ,var))))
-
-(defun magit-apply-diff-item (diff &rest args)
-  (when (zerop magit-diff-context-lines)
-    (setq args (cons "--unidiff-zero" args)))
-  (with-magit-tmp-buffer tmp
-    (magit-insert-diff-item-patch diff tmp)
-    (apply #'magit-run-git-with-input tmp
-           "apply" (append args (list "-")))))
-
-(defun magit-apply-hunk-item* (hunk reverse &rest args)
-  "Apply single hunk or part of a hunk to the index or working file.
-
-This function is the core of magit's stage, unstage, apply, and
-revert operations.  HUNK (or the portion of it selected by the
-region) will be applied to either the index, if \"--cached\" is a
-member of ARGS, or to the working file otherwise."
-  (let ((zero-context (zerop magit-diff-context-lines))
-        (use-region (magit-use-region-p)))
-    (when zero-context
-      (setq args (cons "--unidiff-zero" args)))
-    (when reverse
-      (setq args (cons "--reverse" args)))
-    (when (and use-region zero-context)
-      (error (concat "Not enough context to partially apply hunk.  "
-                     "Use `+' to increase context.")))
-    (with-magit-tmp-buffer tmp
-      (if use-region
-          (magit-insert-hunk-item-region-patch
-           hunk reverse (region-beginning) (region-end) tmp)
-        (magit-insert-hunk-item-patch hunk tmp))
-      (apply #'magit-run-git-with-input tmp
-             "apply" (append args (list "-"))))))
-
-(defun magit-apply-hunk-item (hunk &rest args)
-  (apply #'magit-apply-hunk-item* hunk nil args))
-
-(defun magit-apply-hunk-item-reverse (hunk &rest args)
-  (apply #'magit-apply-hunk-item* hunk t args))
-
-(magit-define-inserter unstaged-changes (title)
-  (let ((magit-hide-diffs t)
-        (magit-current-diff-range (cons 'index 'working)))
-    (let ((magit-diff-options (append '() magit-diff-options)))
-      (magit-git-section 'unstaged title 'magit-wash-raw-diffs
-                         "diff-files"))))
-
-(magit-define-inserter staged-changes (staged no-commit)
-  (let ((magit-current-diff-range (cons "HEAD" 'index)))
-    (when staged
-      (let ((magit-hide-diffs t)
-            (base (if no-commit
-                      (magit-git-string "mktree")
-                    "HEAD")))
-        (let ((magit-diff-options (append '("--cached") magit-diff-options))
-              (magit-ignore-unmerged-raw-diffs t))
-          (magit-git-section 'staged "Staged changes:" 'magit-wash-raw-diffs
-                             "diff-index" "--cached"
-                             base))))))
-
-;;; Logs and Commits
-
-                                        ; Note: making this a plain defcustom would probably let users break
-                                        ; the parser too easily
-(defvar magit-git-log-options
-  (list
-   "--pretty=format:* %h %s"
-   (format "--abbrev=%s" magit-sha1-abbrev-length)))
-                                        ; --decorate=full otherwise some ref prefixes are stripped
-                                        ;  '("--pretty=format:* %H%d %s" "--decorate=full"))
-
-;;
-;; Regexps for parsing ref names
-;;
-;; see the `git-check-ref-format' manpage for details
-
-(defconst magit-ref-nonchars "\000-\037\177 ~^:?*[\\"
-  "Characters specifically disallowed from appearing in Git symbolic refs.
-
-Evaluate (man \"git-check-ref-format\") for details")
-
-(defconst magit-ref-nonslash-re
-  (concat "\\(?:"
-          ;; "no slash-separated component can begin with a dot ." (rule 1)
-          "[^" magit-ref-nonchars "./]"
-          ;; "cannot have two consecutive dots ..  anywhere." (rule 3)
-          "\\.?"
-          "\\)*")
-  "Regexp that matches the non-slash parts of a ref name.
-
-Evaluate (man \"git-check-ref-format\") for details")
-
-(defconst magit-refname-re
-  (concat
-   "\\(?:HEAD\\|"
-
-   "\\(?:tag: \\)?"
-
-   ;; optional non-slash sequence at the beginning
-   magit-ref-nonslash-re
-
-   ;; any number of slash-prefixed sequences
-   "\\(?:"
-   "/"
-   magit-ref-nonslash-re
-   "\\)*"
-
-   "/" ;; "must contain at least one /." (rule 2)
-   magit-ref-nonslash-re
-
-   ;; "cannot end with a slash / nor a dot .." (rule 5)
-   "[^" magit-ref-nonchars "./]"
-
-   "\\)"
-   )
-  "Regexp that matches a git symbolic reference name.
-
-Evaluate (man \"git-check-ref-format\") for details")
-
-(defconst magit-log-oneline-re
-  (concat
-   "^\\([_\\*|/ -.]+\\)?"                          ; graph   (1)
-   "\\(?:"
-   "\\([0-9a-fA-F]+\\)"                            ; sha1    (2)
-   "\\(?:"                                         ; refs    (3)
-   " "
-   "\\("
-   "("
-   magit-refname-re "\\(?:, " magit-refname-re "\\)*"
-   ")"
-   "\\)"
-   "\\)?"
-   "\\)?"
-   " ?\\(.*\\)$"                                   ; msg     (4)
-   ))
-
-(defconst magit-log-longline-re
-  (concat
-   ;; use \0 delimiter (from -z option) to identify commits. this prevents
-   ;; commit messages containing lines like "commit 00000" from polluting the
-   ;; display
-   "\\(?:\\(?:\\`\\|\0\\)"
-   "\\([_\\*|/ -.]+\\)?"                           ; graph   (1)
-   "commit "
-   "\\([0-9a-fA-F]+\\)"                            ; sha1    (2)
-   "\\(?:"                                         ; refs    (3)
-   " "
-   "\\("
-   "("
-   magit-refname-re "\\(?:, " magit-refname-re "\\)*"
-   ")"
-   "\\)"
-   "\\)?"
-   "$\\)?"
-   " ?\\(.*\\)$"                                   ; msg     (4)
-   ))
-
-(defvar magit-present-log-line-function 'magit-present-log-line
-  "The function to use when generating a log line.
-It takes four args: CHART, SHA1, REFS and MESSAGE.  The function
-must return a string which will represent the log line.")
-
-(defun magit-log-get-bisect-state-color (suffix)
-  (if (string= suffix "bad")
-      (list suffix 'magit-log-head-label-bisect-bad)
-    (list suffix 'magit-log-head-label-bisect-good)))
-
-(defun magit-log-get-patches-color (suffix)
-  (list (and (string-match ".+/\\(.+\\)" suffix)
-             (match-string 1 suffix))
-        'magit-log-head-label-patches))
-
-(defvar magit-log-remotes-color-hook nil)
-
-(defun magit-log-get-remotes-color (suffix)
-  (or
-   (run-hook-with-args-until-success
-    'magit-log-remotes-color-hook suffix)
-   (list suffix 'magit-log-head-label-remote)))
-
-(defvar magit-refs-namespaces
-  '(("tags" . magit-log-head-label-tags)
-    ("remotes" magit-log-get-remotes-color)
-    ("heads" . magit-log-head-label-local)
-    ("patches" magit-log-get-patches-color)
-    ("bisect" magit-log-get-bisect-state-color)))
-
-(defun magit-ref-get-label-color (r)
-  (let ((uninteresting (loop for re in magit-uninteresting-refs
-                             thereis (string-match re r))))
-    (if uninteresting (list nil nil)
-      (let* ((ref-re "\\(?:tag: \\)?refs/\\(?:\\([^/]+\\)/\\)?\\(.+\\)")
-             (label (and (string-match ref-re r)
-                         (match-string 2 r)))
-             (res (let ((colorizer
-                         (cdr (assoc (match-string 1 r)
-                                     magit-refs-namespaces))))
-                    (cond ((null colorizer)
-                           (list r 'magit-log-head-label-default))
-                          ((symbolp colorizer)
-                           (list label colorizer))
-                          ((listp colorizer)
-                           (funcall (car colorizer)
-                                    (match-string 2 r)))
-                          (t
-                           (list r 'magit-log-head-label-default))))))
-        res))))
-
-(defun magit-present-log-line (graph sha1 refs message)
-  "The default log line generator."
-  (let ((string-refs
-         (when refs
-           (let ((colored-labels
-                  (delete nil
-                          (mapcar (lambda (r)
-                                    (destructuring-bind (label face)
-                                        (magit-ref-get-label-color r)
-                                      (and label
-                                           (propertize label 'face face))))
-                                  refs))))
-             (concat
-              (mapconcat 'identity colored-labels " ")
-              " ")))))
-
-    (concat
-     (if sha1
-         (propertize sha1 'face 'magit-log-sha1)
-       (insert-char ? magit-sha1-abbrev-length))
-     " "
-     (when graph
-       (propertize graph 'face 'magit-log-graph))
-     string-refs
-     (when message
-       (propertize message 'face 'magit-log-message)))))
-
-(defvar magit-log-count ()
-  "Internal var used to count the number of logs actually added in a buffer.")
-
-(defmacro magit-create-log-buffer-sections (&rest body)
-  "Empty current buffer of text and magit's section, and then evaluate BODY.
-
-if the number of logs inserted in the buffer is `magit-log-cutoff-length'
-insert a line to tell how to insert more of them"
-  (declare (indent 0))
-  `(let ((magit-log-count 0) (inhibit-read-only t))
-     (magit-create-buffer-sections
-       (magit-with-section 'log nil
-         ,@body
-         (if (= magit-log-count magit-log-cutoff-length)
-             (magit-with-section "longer"  'longer
-               (insert "type \"e\" to show more logs\n")))))))
-
-(defun magit-wash-log-line (style)
-  (beginning-of-line)
-  (let ((line-re (cond ((eq style 'long) magit-log-longline-re)
-                       (t magit-log-oneline-re))))
-    (cond
-     ((looking-at line-re)
-      (let ((chart (match-string 1))
-            (sha1 (match-string 2))
-            (msg (match-string 4))
-            (refs (when (match-string 3)
-                    (delq nil
-                          (mapcar
-                           (lambda (s)
-                             (and (not
-                                   (or (string= s "tag:")
-                                       (string= s "HEAD"))) ; as of 1.6.6
-                                  s))
-                           (split-string (match-string 3) "[(), ]" t))))))
-        (delete-region (point-at-bol) (point-at-eol))
-        (insert (funcall magit-present-log-line-function chart sha1 refs msg))
-        (goto-char (point-at-bol))
-        (if sha1
-            (magit-with-section sha1 'commit
-              (when magit-log-count (setq magit-log-count (1+ magit-log-count)))
-              (magit-set-section-info sha1)
-              (forward-line))
-          (forward-line))))
-     (t
-      (forward-line)))
-    t))
-
-(defun magit-wash-log (&optional style)
-  (let ((magit-old-top-section nil))
-    (magit-wash-sequence (apply-partially 'magit-wash-log-line style))))
-
-(defvar magit-currently-shown-commit nil)
-
-(defun magit-wash-commit ()
-  (let ((magit-current-diff-range))
-    (when (looking-at "^commit \\([0-9a-fA-F]\\{40\\}\\)")
-      (setq magit-current-diff-range (match-string 1))
-      (add-text-properties (match-beginning 1) (match-end 1)
-                           '(face magit-log-sha1)))
-    (cond
-     ((search-forward-regexp "^Merge: \\([0-9a-fA-F]+\\) \\([0-9a-fA-F]+\\)$" nil t)
-      (setq magit-current-diff-range (cons (cons (match-string 1)
-                                                 (match-string 2))
-                                           magit-current-diff-range))
-      (let ((first (magit-set-section nil 'commit (match-beginning 1) (match-end 1)))
-            (second (magit-set-section nil 'commit (match-beginning 2) (match-end 2))))
-        (magit-set-section-info (match-string 1) first)
-        (magit-set-section-info (match-string 2) second))
-      (make-commit-button (match-beginning 1) (match-end 1))
-      (make-commit-button (match-beginning 2) (match-end 2)))
-     (t
-      (setq magit-current-diff-range (cons (concat magit-current-diff-range "^")
-                                           magit-current-diff-range))))
-    (search-forward-regexp "^$")
-    (while (and
-            (search-forward-regexp "\\(\\b[0-9a-fA-F]\\{4,40\\}\\b\\)\\|\\(^diff\\)" nil 'noerror)
-            (not (match-string 2)))
-      (let ((sha1 (match-string 1))
-            (start (match-beginning 1))
-            (end (match-end 1)))
-        (when (string-equal "commit" (magit-git-string "cat-file" "-t" sha1))
-          (make-commit-button start end)
-          (let ((section (magit-set-section sha1 'commit start end)))
-            (magit-set-section-info sha1 section)))))
-    (beginning-of-line)
-    (when (looking-at "^diff")
-      (magit-wash-diffs))
-    (goto-char (point-max))
-    (insert "\n")
-    (if magit-back-navigation-history
-        (magit-with-section "[back]" 'button
-          (insert-text-button "[back]"
-                              'help-echo "Previous commit"
-                              'action 'magit-show-commit-backward
-                              'follow-link t
-                              'mouse-face 'magit-item-highlight)))
-    (insert " ")
-    (if magit-forward-navigation-history
-        (magit-with-section "[forward]" 'button
-          (insert-text-button "[forward]"
-                              'help-echo "Next commit"
-                              'action 'magit-show-commit-forward
-                              'follow-link t
-                              'mouse-face 'magit-item-highlight)))))
-
-(defun make-commit-button (start end)
-  (make-text-button start end
-                    'help-echo "Visit commit"
-                    'action (lambda (button)
-                              (save-excursion
-                                (goto-char button)
-                                (magit-visit-item)))
-                    'follow-link t
-                    'mouse-face 'magit-item-highlight
-                    'face 'magit-log-sha1))
-
-(defun magit-refresh-commit-buffer (commit)
-  (magit-configure-have-abbrev)
-  (magit-configure-have-decorate)
-  (magit-create-buffer-sections
-    (apply #'magit-git-section nil nil
-           'magit-wash-commit
-           "log"
-           "--max-count=1"
-           "--pretty=medium"
-           `(,@(if magit-have-abbrev (list "--no-abbrev-commit"))
-             ,@(if magit-have-decorate (list "--decorate=full"))
-             "--cc"
-             "-p" ,commit))))
-
-(define-derived-mode magit-commit-mode magit-mode "Magit"
-  "Mode to view a git commit.
-
-\\{magit-commit-mode-map}"
-  :group 'magit)
-
-(defvar magit-commit-buffer-name "*magit-commit*"
-  "Buffer name for displaying commit log messages.")
-
-(defun magit-show-commit (commit &optional scroll inhibit-history select)
-  "Show information about a commit in the buffer named by
-`magit-commit-buffer-name'.  COMMIT can be any valid name for a commit
-in the current Git repository.
-
-When called interactively or when SELECT is non-nil, switch to
-the commit buffer using `pop-to-buffer'.
-
-Unless INHIBIT-HISTORY is non-nil, the commit currently shown
-will be pushed onto `magit-back-navigation-history' and
-`magit-forward-navigation-history' will be cleared.
-
-Noninteractively, if the commit is already displayed and SCROLL
-is provided, call SCROLL's function definition in the commit
-window.  (`scroll-up' and `scroll-down' are typically passed in
-for this argument.)"
-  (interactive (list (magit-read-rev "Show commit (hash or ref)")
-                     nil nil t))
-  (when (magit-section-p commit)
-    (setq commit (magit-section-info commit)))
-  (unless (eql 0 (magit-git-exit-code "cat-file" "commit" commit))
-    (error "%s is not a commit" commit))
-  (let ((dir default-directory)
-        (buf (get-buffer-create magit-commit-buffer-name)))
-    (cond
-     ((and (equal magit-currently-shown-commit commit)
-           ;; if it's empty then the buffer was killed
-           (with-current-buffer buf
-             (> (length (buffer-string)) 1)))
-      (let ((win (get-buffer-window buf)))
-        (cond ((not win)
-               (display-buffer buf))
-              (scroll
-               (with-selected-window win
-                 (funcall scroll))))))
-     (commit
-      (display-buffer buf)
-      (with-current-buffer buf
-        (unless inhibit-history
-          (push (cons default-directory magit-currently-shown-commit)
-                magit-back-navigation-history)
-          (setq magit-forward-navigation-history nil))
-        (setq magit-currently-shown-commit commit)
-        (goto-char (point-min))
-        (magit-mode-init dir 'magit-commit-mode
-                         #'magit-refresh-commit-buffer commit))))
-    (if select
-        (pop-to-buffer buf))))
-
-(defun magit-show-commit-backward (&optional ignored)
-  ;; Ignore argument passed by push-button
-  "Show the commit at the head of `magit-back-navigation-history in
-`magit-commit-buffer-name`."
-  (interactive)
-  (with-current-buffer magit-commit-buffer-name
-    (unless magit-back-navigation-history
-      (error "No previous commit."))
-    (let ((histitem (pop magit-back-navigation-history)))
-      (push (cons default-directory magit-currently-shown-commit)
-            magit-forward-navigation-history)
-      (setq default-directory (car histitem))
-      (magit-show-commit (cdr histitem) nil 'inhibit-history))))
-
-(defun magit-show-commit-forward (&optional ignored)
-  ;; Ignore argument passed by push-button
-  "Show the commit at the head of `magit-forward-navigation-history in
-`magit-commit-buffer-name`."
-  (interactive)
-  (with-current-buffer magit-commit-buffer-name
-    (unless magit-forward-navigation-history
-      (error "No next commit."))
-    (let ((histitem (pop magit-forward-navigation-history)))
-      (push (cons default-directory magit-currently-shown-commit)
-            magit-back-navigation-history)
-      (setq default-directory (car histitem))
-      (magit-show-commit (cdr histitem) nil 'inhibit-history))))
-
-(defvar magit-marked-commit nil)
-
-(defvar magit-mark-overlay nil)
-(make-variable-buffer-local 'magit-mark-overlay)
-(put 'magit-mark-overlay 'permanent-local t)
-
-(defun magit-refresh-marked-commits ()
-  (magit-for-all-buffers #'magit-refresh-marked-commits-in-buffer))
-
-(defun magit-refresh-marked-commits-in-buffer ()
-  (if (not magit-mark-overlay)
-      (let ((ov (make-overlay 1 1)))
-        (overlay-put ov 'face 'magit-item-mark)
-        (setq magit-mark-overlay ov)))
-  (delete-overlay magit-mark-overlay)
-  (magit-for-all-sections
-   (lambda (section)
-     (when (and (eq (magit-section-type section) 'commit)
-                (equal (magit-section-info section)
-                       magit-marked-commit))
-       (move-overlay magit-mark-overlay
-                     (magit-section-beginning section)
-                     (magit-section-end section)
-                     (current-buffer))))))
-
-(defun magit-set-marked-commit (commit)
-  (setq magit-marked-commit commit)
-  (magit-refresh-marked-commits))
-
-(defun magit-marked-commit ()
-  (or magit-marked-commit
-      (error "No commit marked")))
-
-(defun magit-remote-branch-name (remote branch)
-  "Get the name of the branch BRANCH on remote REMOTE"
-  (if (string= remote ".")
-      branch
-    (concat remote "/" branch)))
-
-(magit-define-inserter unpulled-commits (remote branch)
-  (when remote
-    (apply #'magit-git-section
-           'unpulled "Unpulled commits:" 'magit-wash-log "log"
-           (append magit-git-log-options
-                   (list
-                    (format "HEAD..%s" (magit-remote-branch-name remote branch)))))))
-
-(magit-define-inserter unpushed-commits (remote branch)
-  (when remote
-    (apply #'magit-git-section
-           'unpushed "Unpushed commits:" 'magit-wash-log "log"
-           (append magit-git-log-options
-                   (list
-                    (format "%s..HEAD" (magit-remote-branch-name remote branch)))))))
-
-(defun magit-remote-branch-for (local-branch &optional fully-qualified-name)
-  "Guess the remote branch name that LOCAL-BRANCH is tracking.
-Gives a fully qualified name (e.g., refs/remotes/origin/master) if
-FULLY-QUALIFIED-NAME is non-nil."
-  (let ((merge (magit-get "branch" local-branch "merge")))
-    (save-match-data
-      (if (and merge (string-match "^refs/heads/\\(.+\\)" merge))
-          (concat (if fully-qualified-name
-                      (let ((remote-name (magit-get "branch" local-branch "remote")))
-                        (if (string= "." remote-name)
-                            "refs/heads/"
-                          (concat "refs/remotes/" remote-name "/"))))
-                  (match-string 1 merge))))))
-
-;;; Status
-
-(defvar magit-remote-string-hook nil)
-
-(defun magit-remote-string (remote remote-branch remote-rebase)
-  (cond
-   ((string= "." remote)
-    (concat
-     (when remote-rebase "onto ")
-     "branch "
-     (propertize remote-branch 'face 'magit-branch)))
-   (remote
-    (concat
-     (when remote-rebase "onto ")
-     (propertize remote-branch 'face 'magit-branch)
-     " @ "
-     remote
-     " ("
-     (magit-get "remote" remote "url")
-     ")"))
-   (t
-    (run-hook-with-args-until-success 'magit-remote-string-hook))))
-
-(declare-function magit--bisect-info-for-status "magit-bisect" (branch))
-
-(defun magit-refresh-status ()
-  (magit-create-buffer-sections
-    (magit-with-section 'status nil
-      (let* ((branch (magit-get-current-branch))
-             (remote (and branch (magit-get "branch" branch "remote")))
-             (remote-rebase (and branch (magit-get-boolean "branch" branch "rebase")))
-             (remote-branch (or (and branch (magit-remote-branch-for branch)) branch))
-             (remote-string (magit-remote-string remote remote-branch remote-rebase))
-             (head (magit-git-string
-                    "log"
-                    "--max-count=1"
-                    "--abbrev-commit"
-                    (format "--abbrev=%s" magit-sha1-abbrev-length)
-                    "--pretty=oneline"))
-             (no-commit (not head)))
-        (when remote-string
-          (insert "Remote:   " remote-string "\n"))
-        (insert (format "Local:    %s %s\n"
-                        (propertize (magit--bisect-info-for-status branch)
-                                    'face 'magit-branch)
-                        (abbreviate-file-name default-directory)))
-        (insert (format "Head:     %s\n"
-                        (if no-commit "nothing commited (yet)" head)))
-        (let ((merge-heads (magit-file-lines (concat (magit-git-dir)
-                                                     "MERGE_HEAD"))))
-          (if merge-heads
-              (insert (format "Merging:   %s\n"
-                              (mapconcat 'identity
-                                         (mapcar 'magit-name-rev merge-heads)
-                                         ", ")))))
-        (let ((rebase (magit-rebase-info)))
-          (if rebase
-              (insert (apply 'format "Rebasing: onto %s (%s of %s); Press \"R\" to Abort, Skip, or Continue\n" rebase))))
-        (insert "\n")
-        (magit-git-exit-code "update-index" "--refresh")
-        (magit-insert-stashes)
-        (magit-insert-untracked-files)
-        (magit-insert-pending-changes)
-        (magit-insert-pending-commits)
-        (magit-insert-unpulled-commits remote remote-branch)
-        (let ((staged (or no-commit (magit-anything-staged-p))))
-          (magit-insert-unstaged-changes
-           (if staged "Unstaged changes:" "Changes:"))
-          (magit-insert-staged-changes staged no-commit))
-        (magit-insert-unpushed-commits remote remote-branch)
-        (run-hooks 'magit-refresh-status-hook)))))
-
-(defun magit-init (dir)
-  "Initialize git repository in the DIR directory."
-  (interactive (list (read-directory-name "Directory for Git repository: ")))
-  (let* ((dir (file-name-as-directory (expand-file-name dir)))
-         (topdir (magit-get-top-dir dir)))
-    (when (or (not topdir)
-              (yes-or-no-p
-               (format
-                (if (string-equal topdir dir)
-                    "There is already a Git repository in %s. Reinitialize? "
-                  "There is a Git repository in %s. Create another in %s? ")
-                topdir dir)))
-      (unless (file-directory-p dir)
-        (and (y-or-n-p (format "Directory %s does not exists.  Create it? " dir))
-             (make-directory dir)))
-      (let ((default-directory dir))
-        (magit-run* (list magit-git-executable "init"))))))
-
-(define-derived-mode magit-status-mode magit-mode "Magit"
-  "Mode for looking at git status.
-
-\\{magit-status-mode-map}"
-  :group 'magit)
+;;; Utilities (2)
+;;;; Save Buffers
 
 (defvar magit-default-directory nil)
 
 
 (defvar magit-default-directory nil)
 
-(defun magit-save-some-buffers (&optional msg pred)
+(defun magit-save-some-buffers (&optional msg pred topdir)
   "Save some buffers if variable `magit-save-some-buffers' is non-nil.
   "Save some buffers if variable `magit-save-some-buffers' is non-nil.
-If variable `magit-save-some-buffers' is set to 'dontask then
+If variable `magit-save-some-buffers' is set to `dontask' then
 don't ask the user before saving the buffers, just go ahead and
 do it.
 
 don't ask the user before saving the buffers, just go ahead and
 do it.
 
@@ -3599,7 +4659,7 @@ If PRED is a zero-argument function, it indicates for each buffer whether
 to consider it or not when called with that buffer current."
   (interactive)
   (let ((predicate-function (or pred magit-save-some-buffers-predicate))
 to consider it or not when called with that buffer current."
   (interactive)
   (let ((predicate-function (or pred magit-save-some-buffers-predicate))
-        (magit-default-directory default-directory))
+        (magit-default-directory (or topdir default-directory)))
     (if magit-save-some-buffers
         (save-some-buffers
          (eq magit-save-some-buffers 'dontask)
     (if magit-save-some-buffers
         (save-some-buffers
          (eq magit-save-some-buffers 'dontask)
@@ -3608,163 +4668,518 @@ to consider it or not when called with that buffer current."
         (message msg)))))
 
 (defun magit-save-buffers-predicate-all ()
         (message msg)))))
 
 (defun magit-save-buffers-predicate-all ()
-  "Prompt to save all buffers with unsaved changes"
+  "Prompt to save all buffers with unsaved changes."
   t)
 
 (defun magit-save-buffers-predicate-tree-only ()
   t)
 
 (defun magit-save-buffers-predicate-tree-only ()
-  "Only prompt to save buffers which are within the current git project (as
-  determined by the dir passed to `magit-status'."
+  "Only prompt to save buffers which are within the current git project.
+As determined by the directory passed to `magit-status'."
   (and buffer-file-name
        (string= (magit-get-top-dir magit-default-directory)
                 (magit-get-top-dir (file-name-directory buffer-file-name)))))
 
   (and buffer-file-name
        (string= (magit-get-top-dir magit-default-directory)
                 (magit-get-top-dir (file-name-directory buffer-file-name)))))
 
-;;;###autoload
-(defun magit-status (dir)
-  "Open a Magit status buffer for the Git repository containing
-DIR.  If DIR is not within a Git repository, offer to create a
-Git repository in DIR.
+;;; Porcelain
+;;;; Apply
+;;;;; Apply Commands
+;;;;;; Apply
 
 
-Interactively, a prefix argument means to ask the user which Git
-repository to use even if `default-directory' is under Git control.
-Two prefix arguments means to ignore `magit-repo-dirs' when asking for
-user input."
-  (interactive (list (if current-prefix-arg
-                         (magit-read-top-dir
-                          (> (prefix-numeric-value current-prefix-arg)
-                             4))
-                       (or (magit-get-top-dir default-directory)
-                           (magit-read-top-dir nil)))))
-  (magit-save-some-buffers)
-  (let ((topdir (magit-get-top-dir dir)))
-    (unless topdir
-      (when (y-or-n-p (format "There is no Git repository in %S.  Create one? "
-                              dir))
-        (magit-init dir)
-        (setq topdir (magit-get-top-dir dir))))
-    (when topdir
-      (let ((buf (or (magit-find-status-buffer topdir)
-                     (generate-new-buffer
-                      (concat "*magit: "
-                              (file-name-nondirectory
-                               (directory-file-name topdir)) "*")))))
-        (funcall magit-status-buffer-switch-function buf)
-        (magit-mode-init topdir 'magit-status-mode #'magit-refresh-status)))))
-
-(magit-define-command automatic-merge (revision)
-  "Merge REVISION into the current 'HEAD'; commit unless merge fails.
-\('git merge REVISION')."
-  (interactive (list (magit-read-rev "Merge" (magit-guess-branch))))
-  (if revision
-      (magit-run-git "merge" (magit-rev-to-git revision))))
-
-(magit-define-command manual-merge (revision)
-  "Merge REVISION into the current 'HEAD'; commit unless merge fails.
-\('git merge REVISION')."
-  (interactive (list (magit-read-rev "Merge" (magit-guess-branch))))
-  (when revision
-    (apply 'magit-run-git
-           "merge" "--no-commit"
-           (magit-rev-to-git revision)
-           magit-custom-options)
-    (when (file-exists-p ".git/MERGE_MSG")
-        (magit-log-edit))))
+(defun magit-apply-item ()
+  "Apply the item at point to the current working tree."
+  (interactive)
+  (magit-section-action apply (info)
+    (([* unstaged] [* staged])
+     (user-error "Change is already in your working tree"))
+    (hunk   (magit-apply-hunk-item it))
+    (diff   (magit-apply-diff-item it))
+    (stash  (magit-stash-apply info))
+    (commit (magit-apply-commit info))))
 
 
-;;; Staging and Unstaging
+;;;;;; Stage
 
 
-(defun magit-stage-item (&optional ask)
+(defun magit-stage-item (&optional file)
   "Add the item at point to the staging area.
   "Add the item at point to the staging area.
-If ASK is set, ask for the file name rather than picking the one
-at point."
+With a prefix argument, prompt for a file to be staged instead."
+  (interactive
+   (when current-prefix-arg
+     (list (file-relative-name (read-file-name "File to stage: " nil nil t)
+                               (magit-get-top-dir)))))
+  (if file
+      (magit-run-git "add" file)
+    (magit-section-action stage (info)
+      ([file untracked]
+       (magit-run-git
+        (cond
+         ((use-region-p)
+          (cons "add" (magit-section-region-siblings #'magit-section-info)))
+         ((and (string-match-p "/$" info)
+               (file-exists-p (expand-file-name ".git" info)))
+          (let ((repo (read-string
+                       "Add submodule tracking remote repo (empty to abort): "
+                       (let ((default-directory
+                               (file-name-as-directory
+                                (expand-file-name info default-directory))))
+                         (magit-get "remote.origin.url")))))
+            (if (equal repo "")
+                (user-error "Abort")
+              (list "submodule" "add" repo (substring info 0 -1)))))
+         (t
+          (list "add" info)))))
+      (untracked
+       (magit-run-git "add" "--" (magit-git-lines "ls-files" "--other"
+                                                  "--exclude-standard")))
+      ([hunk diff unstaged]
+       (magit-apply-hunk-item it "--cached"))
+      ([diff unstaged]
+       (magit-run-git "add" "-u"
+                      (if (use-region-p)
+                          (magit-section-region-siblings #'magit-section-info)
+                        info)))
+      (unstaged
+       (magit-stage-all))
+      ([* staged]
+       (user-error "Already staged"))
+      (hunk (user-error "Can't stage this hunk"))
+      (diff (user-error "Can't stage this diff")))))
+
+;;;###autoload
+(defun magit-stage-all (&optional including-untracked)
+  "Add all remaining changes in tracked files to staging area.
+With a prefix argument, add remaining untracked files as well.
+\('git add [-u] .')."
   (interactive "P")
   (interactive "P")
-  (if ask
-      (magit-run-git "add" (read-file-name "File to stage: "))
-    (magit-section-action (item info "stage")
-      ((untracked file)
-       (magit-run-git "add" info))
-      ((untracked)
-       (apply #'magit-run-git "add" "--"
-              (magit-git-lines "ls-files" "--other" "--exclude-standard")))
-      ((unstaged diff hunk)
-       (if (magit-hunk-item-is-conflict-p item)
-           (error (concat "Can't stage individual resolution hunks.  "
-                          "Please stage the whole file.")))
-       (magit-apply-hunk-item item "--cached"))
-      ((unstaged diff)
-       (magit-run-git "add" "-u" (magit-diff-item-file item)))
-      ((staged *)
-       (error "Already staged"))
-      ((diff diff)
-       (save-excursion
-         (magit-goto-parent-section)
-         (magit-stage-item)))
-      ((diff diff hunk)
-       (save-excursion
-         (magit-goto-parent-section)
-         (magit-goto-parent-section)
-         (magit-stage-item)))
-      ((hunk)
-       (error "Can't stage this hunk"))
-      ((diff)
-       (error "Can't stage this diff")))))
+  (when (or (not magit-stage-all-confirm)
+            (not (magit-anything-staged-p))
+            (yes-or-no-p "Stage all changes? "))
+    (if including-untracked
+        (magit-run-git "add" ".")
+      (magit-run-git "add" "-u" "."))))
+
+;;;;;; Unstage
 
 (defun magit-unstage-item ()
   "Remove the item at point from the staging area."
   (interactive)
 
 (defun magit-unstage-item ()
   "Remove the item at point from the staging area."
   (interactive)
-  (magit-section-action (item info "unstage")
-    ((staged diff hunk)
-     (magit-apply-hunk-item-reverse item "--cached"))
-    ((staged diff)
-     (if (eq (car info) 'unmerged)
-         (error "Can't unstage an unmerged file.  Resolve it first"))
-     (if (magit-no-commit-p)
-         (magit-run-git "rm" "--cached" "--" (magit-diff-item-file item))
-       (magit-run-git "reset" "-q" "HEAD" "--" (magit-diff-item-file item))))
-    ((unstaged *)
-     (error "Already unstaged"))
-    ((diff diff)
-     (save-excursion
-       (magit-goto-parent-section)
-       (magit-unstage-item)))
-    ((diff diff hunk)
-     (save-excursion
-       (magit-goto-parent-section)
-       (magit-goto-parent-section)
-       (magit-unstage-item)))
-    ((hunk)
-     (error "Can't unstage this hunk"))
-    ((diff)
-     (error "Can't unstage this diff"))))
-
-(defun magit-stage-all (&optional also-untracked-p)
-  "Add all remaining changes in tracked files to staging area.
-With prefix argument, add remaining untracked files as well.
-\('git add -u .' or 'git add .', respectively)."
-  (interactive "P")
-  (if also-untracked-p
-      (magit-run-git "add" ".")
-    (magit-run-git "add" "-u" ".")))
+  (magit-section-action unstage (info)
+    ([hunk diff staged]
+     (magit-apply-hunk-item it "--reverse" "--cached"))
+    ([diff staged]
+     (when (eq info 'unmerged)
+       (user-error "Can't unstage an unmerged file.  Resolve it first"))
+     (let ((files (if (use-region-p)
+                      (magit-section-region-siblings #'magit-section-info)
+                    (list info))))
+       (if (magit-no-commit-p)
+           (magit-run-git "rm" "--cached" "--" files)
+         (magit-run-git "reset" "-q" "HEAD" "--" files))))
+    (staged
+     (magit-unstage-all))
+    ([* unstaged]
+     (user-error "Already unstaged"))
+    (hunk (user-error "Can't unstage this hunk"))
+    (diff (user-error "Can't unstage this diff"))))
 
 
+;;;###autoload
 (defun magit-unstage-all ()
   "Remove all changes from staging area.
 \('git reset --mixed HEAD')."
   (interactive)
 (defun magit-unstage-all ()
   "Remove all changes from staging area.
 \('git reset --mixed HEAD')."
   (interactive)
-  (magit-run-git "reset" "HEAD"))
+  (when (or (not magit-unstage-all-confirm)
+            (and (not (magit-anything-unstaged-p))
+                 (not (magit-git-lines "ls-files" "--others" "-t"
+                                       "--exclude-standard")))
+            (yes-or-no-p "Unstage all changes? "))
+    (magit-run-git "reset" "HEAD" "--")))
+
+;;;;;; Discard
+
+(defun magit-discard-item ()
+  "Remove the change introduced by the item at point."
+  (interactive)
+  (magit-section-action discard (info parent-info)
+    ([file untracked]
+     (when (yes-or-no-p (format "Delete %s? " info))
+       (if (and (file-directory-p info)
+                (not (file-symlink-p info)))
+           (delete-directory info 'recursive)
+         (delete-file info))
+       (magit-refresh)))
+    (untracked
+     (when (yes-or-no-p "Delete all untracked files and directories? ")
+       (magit-run-git "clean" "-df")))
+    ([hunk diff unstaged]
+     (when (yes-or-no-p (if (use-region-p)
+                            "Discard changes in region? "
+                          "Discard hunk? "))
+       (magit-apply-hunk-item it "--reverse")))
+    ([hunk diff staged]
+     (if (magit-file-uptodate-p parent-info)
+         (when (yes-or-no-p (if (use-region-p)
+                                "Discard changes in region? "
+                              "Discard hunk? "))
+           (magit-apply-hunk-item it "--reverse" "--index"))
+       (user-error "Can't discard this hunk.  Please unstage it first")))
+    ([diff unstaged]
+     (magit-discard-diff it nil))
+    ([diff staged]
+     (if (magit-file-uptodate-p (magit-section-info it))
+         (magit-discard-diff it t)
+       (user-error "Can't discard staged changes to this file.  \
+Please unstage it first")))
+    (hunk   (user-error "Can't discard this hunk"))
+    (diff   (user-error "Can't discard this diff"))
+    (stash  (when (yes-or-no-p "Discard stash? ")
+              (magit-stash-drop info)))
+    (branch (when (yes-or-no-p
+                   (if current-prefix-arg
+                       (concat "Force delete branch [" info "]? ")
+                     (concat "Delete branch [" info "]? ")))
+              (magit-delete-branch info current-prefix-arg)))
+    (remote (when (yes-or-no-p "Remove remote? ")
+              (magit-remove-remote info)))))
+
+;;;;;; Revert
+
+(defun magit-revert-item ()
+  "Revert the item at point.
+The change introduced by the item is reversed in the current
+working tree."
+  (interactive)
+  (magit-section-action revert (info)
+    ([* unstaged] (magit-discard-item))
+    (commit (when (or (not magit-revert-item-confirm)
+                      (yes-or-no-p "Revert this commit? "))
+              (magit-revert-commit info)))
+    (diff   (when (or (not magit-revert-item-confirm)
+                      (yes-or-no-p "Revert this diff? "))
+              (magit-apply-diff-item it "--reverse")))
+    (hunk   (when (or (not magit-revert-item-confirm)
+                      (yes-or-no-p "Revert this hunk? "))
+              (magit-apply-hunk-item it "--reverse")))))
+
+(defun magit-revert-commit (commit)
+  (magit-assert-one-parent commit "revert")
+  (magit-run-git "revert" "--no-commit" commit))
+
+(defconst magit-revert-backup-file "magit/reverted.diff")
+
+(defun magit-revert-undo ()
+  "Re-apply the previously reverted hunk.
+Also see option `magit-revert-backup'."
+  (interactive)
+  (let ((file (magit-git-dir magit-revert-backup-file)))
+    (if (file-readable-p file)
+        (magit-run-git "apply" file)
+      (user-error "No backups exist"))
+    (magit-refresh)))
+
+;;;;; Apply Core
+
+(defun magit-discard-diff (diff stagedp)
+  (let ((file (magit-section-info diff)))
+    (cl-case (magit-section-diff-status diff)
+      (deleted
+       (when (yes-or-no-p (format "Resurrect %s? " file))
+         (when stagedp
+           (magit-run-git "reset" "-q" "--" file))
+         (magit-run-git "checkout" "--" file)))
+      (new
+       (when (yes-or-no-p (format "Delete %s? " file))
+         (magit-run-git "rm" "-f" "--" file)))
+      (t
+       (when (yes-or-no-p (format "Discard changes to %s? " file))
+         (if stagedp
+             (magit-run-git "checkout" "HEAD" "--" file)
+           (magit-run-git "checkout" "--" file)))))))
+
+(defun magit-apply-commit (commit)
+  (magit-assert-one-parent commit "cherry-pick")
+  (magit-run-git "cherry-pick" "--no-commit" commit))
+
+(defun magit-apply-diff-item (diff &rest args)
+  (when (zerop magit-diff-context-lines)
+    (setq args (cons "--unidiff-zero" args)))
+  (let ((buf (generate-new-buffer " *magit-input*")))
+    (unwind-protect
+        (progn (magit-insert-diff-item-patch diff buf)
+               (magit-run-git-with-input
+                buf "apply" args "--ignore-space-change" "-"))
+      (kill-buffer buf))))
+
+(defun magit-apply-hunk-item (hunk &rest args)
+  "Apply single hunk or part of a hunk to the index or working file.
+
+This function is the core of magit's stage, unstage, apply, and
+revert operations.  HUNK (or the portion of it selected by the
+region) will be applied to either the index, if \"--cached\" is a
+member of ARGS, or to the working file otherwise."
+  (when (string-match "^diff --cc" (magit-section-parent-info hunk))
+    (user-error (concat "Cannot un-/stage individual resolution hunks.  "
+                        "Please stage the whole file.")))
+  (let ((use-region (use-region-p)))
+    (when (zerop magit-diff-context-lines)
+      (setq args (cons "--unidiff-zero" args))
+      (when use-region
+        (user-error (concat "Not enough context to partially apply hunk.  "
+                            "Use `+' to increase context."))))
+    (let ((buf (generate-new-buffer " *magit-input*")))
+      (unwind-protect
+          (progn (if use-region
+                     (magit-insert-hunk-item-region-patch
+                      hunk (member "--reverse" args)
+                      (region-beginning) (region-end) buf)
+                   (magit-insert-hunk-item-patch hunk buf))
+                 (magit-revert-backup buf args)
+                 (magit-run-git-with-input
+                  buf "apply" args "--ignore-space-change" "-"))
+        (kill-buffer buf)))))
+
+(defun magit-insert-diff-item-patch (diff buf)
+  (magit-insert-region (magit-section-content-beginning diff)
+                       (magit-section-end diff)
+                       buf))
+
+(defun magit-insert-hunk-item-patch (hunk buf)
+  (magit-diff-item-insert-header (magit-section-parent hunk) buf)
+  (magit-insert-region (magit-section-beginning hunk)
+                       (magit-section-end hunk)
+                       buf))
+
+(defun magit-insert-hunk-item-region-patch (hunk reverse beg end buf)
+  (magit-diff-item-insert-header (magit-section-parent hunk) buf)
+  (save-excursion
+    (goto-char (magit-section-beginning hunk))
+    (magit-insert-current-line buf)
+    (forward-line)
+    (let ((copy-op (if reverse "+" "-")))
+      (while (< (point) (magit-section-end hunk))
+        (cond ((and (<= beg (point)) (< (point) end))
+               (magit-insert-current-line buf))
+              ((looking-at " ")
+               (magit-insert-current-line buf))
+              ((looking-at copy-op)
+               (let ((text (buffer-substring-no-properties
+                            (+ (point) 1) (line-beginning-position 2))))
+                 (with-current-buffer buf
+                   (insert " " text)))))
+        (forward-line))))
+  (with-current-buffer buf
+    (diff-fixup-modifs (point-min) (point-max))))
+
+(defun magit-diff-item-insert-header (diff buf)
+  (magit-insert-region (magit-section-content-beginning diff)
+                       (if (magit-section-children diff)
+                           (magit-section-beginning
+                            (car (magit-section-children diff)))
+                         (magit-section-end diff))
+                       buf))
+
+(defun magit-insert-region (beg end buf)
+  (let ((text (buffer-substring-no-properties beg end)))
+    (with-current-buffer buf
+      (insert text))))
+
+(defun magit-insert-current-line (buf)
+  (let ((text (buffer-substring-no-properties
+               (line-beginning-position) (line-beginning-position 2))))
+    (with-current-buffer buf
+      (insert text))))
+
+(defun magit-revert-backup (buffer args)
+  (when (and magit-revert-backup (member "--reverse" args))
+    (with-current-buffer buffer
+      (let ((buffer-file-name (magit-git-dir magit-revert-backup-file))
+            (make-backup-files t)
+            (backup-directory-alist nil)
+            (version-control t)
+            (kept-old-versions 0)
+            (kept-new-versions 10))
+        (make-directory (file-name-directory buffer-file-name) t)
+        (save-buffer 16)))))
+
+;;;; Visit
+
+(defun magit-visit-item (&optional other-window)
+  "Visit current item.
+With a prefix argument, visit in other window."
+  (interactive "P")
+  (magit-section-action visit (info parent-info)
+    ((diff diffstat [file untracked])
+     (magit-visit-file-item info other-window))
+    (hunk   (magit-visit-file-item parent-info other-window
+                                   (magit-hunk-item-target-line it)
+                                   (current-column)))
+    (commit (magit-show-commit info))
+    (stash  (magit-diff-stash info))
+    (branch (magit-checkout info))))
+
+(defun magit-visit-file-item (file &optional other-window line column)
+  (unless file
+    (user-error "Can't get pathname for this file"))
+  (unless (file-exists-p file)
+    (user-error "Can't visit deleted file: %s" file))
+  (if (file-directory-p file)
+      (progn
+        (setq file (file-name-as-directory (expand-file-name file)))
+        (if (equal (magit-get-top-dir (file-name-directory file))
+                   (magit-get-top-dir))
+            (magit-dired-jump other-window)
+          (magit-status file (if other-window
+                                 'pop-to-buffer
+                               'switch-to-buffer))))
+    (if other-window
+        (find-file-other-window file)
+      (find-file file))
+    (when line
+      (goto-char (point-min))
+      (forward-line (1- line))
+      (when (> column 0)
+        (move-to-column (1- column))))))
+
+(defun magit-hunk-item-target-line (hunk)
+  (save-excursion
+    (beginning-of-line)
+    (let ((line (line-number-at-pos)))
+      (goto-char (magit-section-beginning hunk))
+      (unless (looking-at "@@+ .* \\+\\([0-9]+\\)\\(,[0-9]+\\)? @@+")
+        (user-error "Hunk header not found"))
+      (let ((target (string-to-number (match-string 1))))
+        (forward-line)
+        (while (< (line-number-at-pos) line)
+          ;; XXX - deal with combined diffs
+          (unless (looking-at "-")
+            (setq target (+ target 1)))
+          (forward-line))
+        target))))
+
+;;;###autoload
+(defun magit-dired-jump (&optional other-window)
+  "Visit current item in dired.
+With a prefix argument, visit in other window."
+  (interactive "P")
+  (require 'dired-x)
+  (dired-jump other-window
+              (file-truename
+               (magit-section-action dired-jump (info parent-info)
+                 ([file untracked] info)
+                 ((diff diffstat) info)
+                 (hunk parent-info)
+                 (t default-directory)))))
+
+(defvar-local magit-file-log-file nil)
+(defvar-local magit-show-current-version nil)
+
+;;;###autoload
+(defun magit-show (rev file &optional switch-function)
+  "Display and select a buffer containing FILE as stored in REV.
+
+Insert the contents of FILE as stored in the revision REV into a
+buffer.  Then select the buffer using `pop-to-buffer' or with a
+prefix argument using `switch-to-buffer'.  Non-interactivity use
+SWITCH-FUNCTION to switch to the buffer, if that is nil simply
+return the buffer, without displaying it."
+  ;; REV may also be one of the symbols `index' or `working' but
+  ;; that is only intended for use by `magit-ediff'.
+  (interactive
+   (let (rev file section)
+     (magit-section-case (info parent)
+       (commit (setq file magit-file-log-file rev info))
+       (hunk   (setq section parent))
+       (diff   (setq section it)))
+     (if section
+         (setq rev  (car (magit-diff-range section))
+               file (magit-section-info section))
+       (unless rev
+         (setq rev (magit-get-current-branch))))
+     (setq rev  (magit-read-rev "Retrieve file from revision" rev)
+           file (cl-case rev
+                  (working (read-file-name "Find file: "))
+                  (index   (magit-read-file-from-rev "HEAD" file))
+                  (t       (magit-read-file-from-rev rev file))))
+     (list rev file (if current-prefix-arg
+                        'switch-to-buffer
+                      'pop-to-buffer))))
+  (let (buffer)
+    (if (eq rev 'working)
+        (setq buffer (find-file-noselect file))
+      (let ((name (format "%s.%s" file
+                          (if (symbolp rev)
+                              (format "@{%s}" rev)
+                            (replace-regexp-in-string "/" ":" rev)))))
+        (setq buffer (get-buffer name))
+        (when buffer
+          (with-current-buffer buffer
+            (if (and (equal file magit-file-name)
+                     (equal rev  magit-show-current-version))
+                (let ((inhibit-read-only t))
+                  (erase-buffer))
+              (setq buffer nil))))
+        (with-current-buffer
+            (or buffer (setq buffer (create-file-buffer name)))
+          (setq buffer-read-only t)
+          (with-silent-modifications
+            (if (eq rev 'index)
+                (let ((temp (car (split-string
+                                  (magit-git-string "checkout-index"
+                                                    "--temp" file)
+                                  "\t")))
+                      (inhibit-read-only t))
+                  (insert-file-contents temp nil nil nil t)
+                  (delete-file temp))
+              (magit-git-insert "cat-file" "-p" (concat rev ":" file))))
+          (let ((buffer-file-name (expand-file-name file (magit-get-top-dir))))
+            (normal-mode t))
+          (setq magit-file-name file)
+          (setq magit-show-current-version rev)
+          (goto-char (point-min)))))
+    (when switch-function
+      (with-current-buffer buffer
+        (funcall switch-function (current-buffer))))
+    buffer))
+
+;;;; Act
+;;;;; Merging
+
+;;;###autoload
+(defun magit-merge (revision &optional do-commit)
+  "Merge REVISION into the current 'HEAD', leaving changes uncommitted.
+With a prefix argument, skip editing the log message and commit.
+\('git merge [--no-commit] REVISION')."
+  (interactive (list (magit-read-rev "Merge"
+                                     (or (magit-guess-branch)
+                                         (magit-get-previous-branch)))
+                     current-prefix-arg))
+  (when (or (not (magit-anything-modified-p))
+            (not magit-merge-warn-dirty-worktree)
+            (yes-or-no-p
+             "Running merge in a dirty worktree could cause data loss.  Continue?"))
+    (magit-run-git "merge" revision magit-custom-options
+                   (unless do-commit "--no-commit"))
+    (when (file-exists-p ".git/MERGE_MSG")
+      (let ((magit-custom-options nil))
+        (magit-commit)))))
+
+;;;###autoload
+(defun magit-merge-abort ()
+  "Abort the current merge operation."
+  (interactive)
+  (if (file-exists-p (magit-git-dir "MERGE_HEAD"))
+      (when (yes-or-no-p "Abort merge? ")
+        (magit-run-git-async "merge" "--abort"))
+    (user-error "No merge in progress")))
 
 
-;;; Branches
+;;;;; Branching
 
 
-(defun escape-branch-name (branch)
-  "Escapes branch names to remove problematic characters."
+(defun magit-escape-branch-name (branch)
+  "Escape branch name BRANCH to remove problematic characters."
   (replace-regexp-in-string "[/]" "-" branch))
 
   (replace-regexp-in-string "[/]" "-" branch))
 
-(defun magit-default-tracking-name-remote-plus-branch
-  (remote branch)
+(defun magit-default-tracking-name-remote-plus-branch (remote branch)
   "Use the remote name plus a hyphen plus the escaped branch name for tracking branches."
   "Use the remote name plus a hyphen plus the escaped branch name for tracking branches."
-  (concat remote "-" (escape-branch-name branch)))
+  (concat remote "-" (magit-escape-branch-name branch)))
 
 
-(defun magit-default-tracking-name-branch-only
-  (remote branch)
+(defun magit-default-tracking-name-branch-only (remote branch)
   "Use just the escaped branch name for tracking branches."
   "Use just the escaped branch name for tracking branches."
-  (escape-branch-name branch))
+  (magit-escape-branch-name branch))
 
 (defun magit-get-tracking-name (remote branch)
   "Given a REMOTE and a BRANCH name, ask the user for a local
 
 (defun magit-get-tracking-name (remote branch)
   "Given a REMOTE and a BRANCH name, ask the user for a local
@@ -3772,231 +5187,246 @@ tracking brach name suggesting a sensible default."
   (when (yes-or-no-p
          (format "Create local tracking branch for %s? " branch))
     (let* ((default-name
   (when (yes-or-no-p
          (format "Create local tracking branch for %s? " branch))
     (let* ((default-name
-             (funcall magit-default-tracking-name-function remote branch))
-           (chosen-name (read-string (format "Call local branch (%s): " default-name)
-                                     nil
-                                     nil
-                                     default-name)))
+            (funcall magit-default-tracking-name-function remote branch))
+           (chosen-name
+            (read-string (format "Call local branch (%s): " default-name)
+                         nil nil default-name)))
       (when (magit-ref-exists-p (concat "refs/heads/" chosen-name))
       (when (magit-ref-exists-p (concat "refs/heads/" chosen-name))
-        (error "'%s' already exists." chosen-name))
+        (user-error "'%s' already exists" chosen-name))
       chosen-name)))
 
 (defun magit-maybe-create-local-tracking-branch (rev)
   "Depending on the users wishes, create a tracking branch for
       chosen-name)))
 
 (defun magit-maybe-create-local-tracking-branch (rev)
   "Depending on the users wishes, create a tracking branch for
-rev... maybe."
-  (if (string-match "^\\(?:refs/\\)?remotes/\\([^/]+\\)/\\(.+\\)" rev)
-      (let* ((remote (match-string 1 rev))
-             (branch (match-string 2 rev))
-             (tracker-name (magit-get-tracking-name remote branch)))
-        (when tracker-name
-          (magit-run-git "checkout" "-b" tracker-name rev)
-          t))
-    nil))
-
-(magit-define-command checkout (revision)
+REV... maybe."
+  (when (string-match "^\\(?:refs/\\)?remotes/\\([^/]+\\)/\\(.+\\)" rev)
+    (let* ((remote (match-string 1 rev))
+           (branch (match-string 2 rev))
+           (tracker-name (magit-get-tracking-name remote branch)))
+      (when tracker-name
+        (magit-run-git "checkout" "-b" tracker-name rev)
+        t))))
+
+;;;###autoload
+(defun magit-checkout (revision)
   "Switch 'HEAD' to REVISION and update working tree.
 Fails if working tree or staging area contain uncommitted changes.
 If REVISION is a remote branch, offer to create a local tracking branch.
 \('git checkout [-b] REVISION')."
   (interactive
    (list (let ((current-branch (magit-get-current-branch))
   "Switch 'HEAD' to REVISION and update working tree.
 Fails if working tree or staging area contain uncommitted changes.
 If REVISION is a remote branch, offer to create a local tracking branch.
 \('git checkout [-b] REVISION')."
   (interactive
    (list (let ((current-branch (magit-get-current-branch))
-               (default (magit-default-rev)))
-           (magit-read-rev "Switch to"
+               (default (or (magit-guess-branch)
+                            (magit-get-previous-branch))))
+           (magit-read-rev (format "Switch from '%s' to" current-branch)
                            (unless (string= current-branch default)
                              default)
                            (unless (string= current-branch default)
                              default)
-                           (if current-branch
-                               (cons (concat "refs/heads/" current-branch "$")
-                                     magit-uninteresting-refs)
-                             magit-uninteresting-refs)))))
-  (if revision
-      (when (not (magit-maybe-create-local-tracking-branch revision))
+                           current-branch))))
+  (or (magit-maybe-create-local-tracking-branch revision)
+      (progn
         (magit-save-some-buffers)
         (magit-save-some-buffers)
-        (magit-run-git "checkout" (magit-rev-to-git revision))
-        (magit-update-vc-modeline default-directory))))
-
-(defun magit-read-create-branch-args ()
-  (let* ((cur-branch (magit-get-current-branch))
-         (cur-point (magit-default-rev))
-         (branch (read-string "Create branch: "))
-         (parent (magit-read-rev "Parent"
-                                 (cond
-                                  ((eq magit-create-branch-behaviour 'at-point) cur-point)
-                                  ((eq magit-create-branch-behaviour 'at-head) cur-branch)
-                                  (t cur-branch)))))
-    (list branch parent)))
-
-(magit-define-command create-branch (branch parent)
+  &n